import * as test from 'tape'; import Lexer, { LexerError, LexerResult } from '../lexer'; import Token, { TokenTypes as TT } from '../token'; const scan = (source: string): LexerResult => { return new Lexer(source).scan(); }; const EOF = (line: number = 1) => new Token(TT.EOF, 'eof', line); test('scans punctuation', (t) => { t.plan(2); const result: LexerResult = scan('(),'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens.map((t) => t.type), [ TT.LPAREN, TT.RPAREN, TT.COMMA, TT.EOF, ]); } }); test('scans property', (t) => { t.plan(2); const result: LexerResult = scan(':property'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [new Token(TT.PROPERTY, 'property', 1), EOF()]); } }); test('scans identifier', (t) => { t.plan(2); const result: LexerResult = scan('$identifier'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.IDENTIFIER, 'identifier', 1), EOF(), ]); } }); test('scans function name', (t) => { t.plan(2); const result: LexerResult = scan('@function_name'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.FUNCTION_NAME, 'function_name', 1), EOF(), ]); } }); test('scans literal', (t) => { t.plan(2); const result: LexerResult = scan('literal'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [new Token(TT.LITERAL, 'literal', 1), EOF()]); } }); test('scans literal followed by parens', (t) => { t.plan(2); const result: LexerResult = scan('literal(literal)literal ('); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LITERAL, 'literal', 1), new Token(TT.LPAREN, '(', 1), new Token(TT.LITERAL, 'literal', 1), new Token(TT.RPAREN, ')', 1), new Token(TT.LITERAL, 'literal', 1), new Token(TT.LPAREN, '(', 1), EOF(), ]); } }); test('scans literal followed by property', (t) => { t.plan(2); const result: LexerResult = scan('literal :property'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LITERAL, 'literal', 1), new Token(TT.PROPERTY, 'property', 1), EOF(), ]); } }); test('scans simple expression', (t) => { t.plan(2); const result: LexerResult = scan('(div :color blue)'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LPAREN, '(', 1), new Token(TT.LITERAL, 'div', 1), new Token(TT.PROPERTY, 'color', 1), new Token(TT.LITERAL, 'blue', 1), new Token(TT.RPAREN, ')', 1), EOF(), ]); } }); test('keeps track of line numbers', (t) => { t.plan(2); const result: LexerResult = scan('line1\nline2'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LITERAL, 'line1', 1), new Token(TT.LITERAL, 'line2', 2), EOF(2), ]); } }); test('scans literal followed by identifier', (t) => { t.plan(2); const result: LexerResult = scan('literal $identifier'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LITERAL, 'literal', 1), new Token(TT.IDENTIFIER, 'identifier', 1), EOF(), ]); } }); test('allow dashes', (t) => { t.plan(2); const result: LexerResult = scan( 'literal-dash $identifier-dash @function-dash' ); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LITERAL, 'literal-dash', 1), new Token(TT.IDENTIFIER, 'identifier-dash', 1), new Token(TT.FUNCTION_NAME, 'function-dash', 1), EOF(), ]); } }); test('disallow whitespace in function arguments', (t) => { t.plan(2); const result: LexerResult = scan('(@rgba 0 0 0 0)'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LPAREN, '(', 1), new Token(TT.FUNCTION_NAME, 'rgba', 1), new Token(TT.LITERAL, '0', 1), new Token(TT.LITERAL, '0', 1), new Token(TT.LITERAL, '0', 1), new Token(TT.LITERAL, '0', 1), new Token(TT.RPAREN, ')', 1), EOF(), ]); } }); test('ignores comments', (t) => { t.plan(2); const result: LexerResult = scan('; div with blue text\n(div :color blue)'); t.false(result instanceof LexerError); if (!(result instanceof LexerError)) { t.deepEqual(result.tokens, [ new Token(TT.LPAREN, '(', 2), new Token(TT.LITERAL, 'div', 2), new Token(TT.PROPERTY, 'color', 2), new Token(TT.LITERAL, 'blue', 2), new Token(TT.RPAREN, ')', 2), EOF(2), ]); } });