A stylesheet language written in TypeScript that compiles to CSS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lexer.test.ts 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import * as test from 'tape';
  2. import Lexer, { LexerError, LexerResult } from '../lexer';
  3. import Token, { TokenTypes as TT } from '../token';
  4. const scan = (source: string): LexerResult => {
  5. return new Lexer(source).scan();
  6. };
  7. const EOF = (line: number = 1) => new Token(TT.EOF, 'eof', line);
  8. test('scans punctuation', (t) => {
  9. t.plan(2);
  10. const result: LexerResult = scan('(),');
  11. t.false(result instanceof LexerError);
  12. if (!(result instanceof LexerError)) {
  13. t.deepEqual(result.tokens.map((t) => t.type), [
  14. TT.LPAREN,
  15. TT.RPAREN,
  16. TT.COMMA,
  17. TT.EOF,
  18. ]);
  19. }
  20. });
  21. test('scans property', (t) => {
  22. t.plan(2);
  23. const result: LexerResult = scan(':property');
  24. t.false(result instanceof LexerError);
  25. if (!(result instanceof LexerError)) {
  26. t.deepEqual(result.tokens, [new Token(TT.PROPERTY, 'property', 1), EOF()]);
  27. }
  28. });
  29. test('scans identifier', (t) => {
  30. t.plan(2);
  31. const result: LexerResult = scan('$identifier');
  32. t.false(result instanceof LexerError);
  33. if (!(result instanceof LexerError)) {
  34. t.deepEqual(result.tokens, [
  35. new Token(TT.IDENTIFIER, 'identifier', 1),
  36. EOF(),
  37. ]);
  38. }
  39. });
  40. test('scans function name', (t) => {
  41. t.plan(2);
  42. const result: LexerResult = scan('@function_name');
  43. t.false(result instanceof LexerError);
  44. if (!(result instanceof LexerError)) {
  45. t.deepEqual(result.tokens, [
  46. new Token(TT.FUNCTION_NAME, 'function_name', 1),
  47. EOF(),
  48. ]);
  49. }
  50. });
  51. test('scans literal', (t) => {
  52. t.plan(2);
  53. const result: LexerResult = scan('literal');
  54. t.false(result instanceof LexerError);
  55. if (!(result instanceof LexerError)) {
  56. t.deepEqual(result.tokens, [new Token(TT.LITERAL, 'literal', 1), EOF()]);
  57. }
  58. });
  59. test('scans literal followed by parens', (t) => {
  60. t.plan(2);
  61. const result: LexerResult = scan('literal(literal)literal (');
  62. t.false(result instanceof LexerError);
  63. if (!(result instanceof LexerError)) {
  64. t.deepEqual(result.tokens, [
  65. new Token(TT.LITERAL, 'literal', 1),
  66. new Token(TT.LPAREN, '(', 1),
  67. new Token(TT.LITERAL, 'literal', 1),
  68. new Token(TT.RPAREN, ')', 1),
  69. new Token(TT.LITERAL, 'literal', 1),
  70. new Token(TT.LPAREN, '(', 1),
  71. EOF(),
  72. ]);
  73. }
  74. });
  75. test('scans literal followed by property', (t) => {
  76. t.plan(2);
  77. const result: LexerResult = scan('literal :property');
  78. t.false(result instanceof LexerError);
  79. if (!(result instanceof LexerError)) {
  80. t.deepEqual(result.tokens, [
  81. new Token(TT.LITERAL, 'literal', 1),
  82. new Token(TT.PROPERTY, 'property', 1),
  83. EOF(),
  84. ]);
  85. }
  86. });
  87. test('scans simple expression', (t) => {
  88. t.plan(2);
  89. const result: LexerResult = scan('(div :color blue)');
  90. t.false(result instanceof LexerError);
  91. if (!(result instanceof LexerError)) {
  92. t.deepEqual(result.tokens, [
  93. new Token(TT.LPAREN, '(', 1),
  94. new Token(TT.LITERAL, 'div', 1),
  95. new Token(TT.PROPERTY, 'color', 1),
  96. new Token(TT.LITERAL, 'blue', 1),
  97. new Token(TT.RPAREN, ')', 1),
  98. EOF(),
  99. ]);
  100. }
  101. });
  102. test('keeps track of line numbers', (t) => {
  103. t.plan(2);
  104. const result: LexerResult = scan('line1\nline2');
  105. t.false(result instanceof LexerError);
  106. if (!(result instanceof LexerError)) {
  107. t.deepEqual(result.tokens, [
  108. new Token(TT.LITERAL, 'line1', 1),
  109. new Token(TT.LITERAL, 'line2', 2),
  110. EOF(2),
  111. ]);
  112. }
  113. });
  114. test('scans literal followed by identifier', (t) => {
  115. t.plan(2);
  116. const result: LexerResult = scan('literal $identifier');
  117. t.false(result instanceof LexerError);
  118. if (!(result instanceof LexerError)) {
  119. t.deepEqual(result.tokens, [
  120. new Token(TT.LITERAL, 'literal', 1),
  121. new Token(TT.IDENTIFIER, 'identifier', 1),
  122. EOF(),
  123. ]);
  124. }
  125. });
  126. test('allow dashes', (t) => {
  127. t.plan(2);
  128. const result: LexerResult = scan(
  129. 'literal-dash $identifier-dash @function-dash'
  130. );
  131. t.false(result instanceof LexerError);
  132. if (!(result instanceof LexerError)) {
  133. t.deepEqual(result.tokens, [
  134. new Token(TT.LITERAL, 'literal-dash', 1),
  135. new Token(TT.IDENTIFIER, 'identifier-dash', 1),
  136. new Token(TT.FUNCTION_NAME, 'function-dash', 1),
  137. EOF(),
  138. ]);
  139. }
  140. });
  141. test('disallow whitespace in function arguments', (t) => {
  142. t.plan(2);
  143. const result: LexerResult = scan('(@rgba 0 0 0 0)');
  144. t.false(result instanceof LexerError);
  145. if (!(result instanceof LexerError)) {
  146. t.deepEqual(result.tokens, [
  147. new Token(TT.LPAREN, '(', 1),
  148. new Token(TT.FUNCTION_NAME, 'rgba', 1),
  149. new Token(TT.LITERAL, '0', 1),
  150. new Token(TT.LITERAL, '0', 1),
  151. new Token(TT.LITERAL, '0', 1),
  152. new Token(TT.LITERAL, '0', 1),
  153. new Token(TT.RPAREN, ')', 1),
  154. EOF(),
  155. ]);
  156. }
  157. });
  158. test('ignores comments', (t) => {
  159. t.plan(2);
  160. const result: LexerResult = scan('; div with blue text\n(div :color blue)');
  161. t.false(result instanceof LexerError);
  162. if (!(result instanceof LexerError)) {
  163. t.deepEqual(result.tokens, [
  164. new Token(TT.LPAREN, '(', 2),
  165. new Token(TT.LITERAL, 'div', 2),
  166. new Token(TT.PROPERTY, 'color', 2),
  167. new Token(TT.LITERAL, 'blue', 2),
  168. new Token(TT.RPAREN, ')', 2),
  169. EOF(2),
  170. ]);
  171. }
  172. });