lex_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package js // import "github.com/tdewolff/parse/js"
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "testing"
  7. "github.com/tdewolff/test"
  8. )
  9. type TTs []TokenType
  10. func TestTokens(t *testing.T) {
  11. var tokenTests = []struct {
  12. js string
  13. expected []TokenType
  14. }{
  15. {" \t\v\f\u00A0\uFEFF\u2000", TTs{}}, // WhitespaceToken
  16. {"\n\r\r\n\u2028\u2029", TTs{LineTerminatorToken}},
  17. {"5.2 .04 0x0F 5e99", TTs{NumericToken, NumericToken, NumericToken, NumericToken}},
  18. {"a = 'string'", TTs{IdentifierToken, PunctuatorToken, StringToken}},
  19. {"/*comment*/ //comment", TTs{SingleLineCommentToken, SingleLineCommentToken}},
  20. {"{ } ( ) [ ]", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  21. {". ; , < > <=", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  22. {">= == != === !==", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  23. {"+ - * % ++ --", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  24. {"<< >> >>> & | ^", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  25. {"! ~ && || ? :", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  26. {"= += -= *= %= <<=", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  27. {">>= >>>= &= |= ^= =>", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  28. {"a = /.*/g;", TTs{IdentifierToken, PunctuatorToken, RegexpToken, PunctuatorToken}},
  29. {"/*co\nm\u2028m/*ent*/ //co//mment\u2029//comment", TTs{MultiLineCommentToken, SingleLineCommentToken, LineTerminatorToken, SingleLineCommentToken}},
  30. {"<!-", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  31. {"1<!--2\n", TTs{NumericToken, SingleLineCommentToken, LineTerminatorToken}},
  32. {"x=y-->10\n", TTs{IdentifierToken, PunctuatorToken, IdentifierToken, PunctuatorToken, PunctuatorToken, NumericToken, LineTerminatorToken}},
  33. {" /*comment*/ -->nothing\n", TTs{SingleLineCommentToken, SingleLineCommentToken, LineTerminatorToken}},
  34. {"1 /*comment\nmultiline*/ -->nothing\n", TTs{NumericToken, MultiLineCommentToken, SingleLineCommentToken, LineTerminatorToken}},
  35. {"$ _\u200C \\u2000 \u200C", TTs{IdentifierToken, IdentifierToken, IdentifierToken, UnknownToken}},
  36. {">>>=>>>>=", TTs{PunctuatorToken, PunctuatorToken, PunctuatorToken}},
  37. {"1/", TTs{NumericToken, PunctuatorToken}},
  38. {"1/=", TTs{NumericToken, PunctuatorToken}},
  39. {"010xF", TTs{NumericToken, NumericToken, IdentifierToken}},
  40. {"50e+-0", TTs{NumericToken, IdentifierToken, PunctuatorToken, PunctuatorToken, NumericToken}},
  41. {"'str\\i\\'ng'", TTs{StringToken}},
  42. {"'str\\\\'abc", TTs{StringToken, IdentifierToken}},
  43. {"'str\\\ni\\\\u00A0ng'", TTs{StringToken}},
  44. {"a = /[a-z/]/g", TTs{IdentifierToken, PunctuatorToken, RegexpToken}},
  45. {"a=/=/g1", TTs{IdentifierToken, PunctuatorToken, RegexpToken}},
  46. {"a = /'\\\\/\n", TTs{IdentifierToken, PunctuatorToken, RegexpToken, LineTerminatorToken}},
  47. {"a=/\\//g1", TTs{IdentifierToken, PunctuatorToken, RegexpToken}},
  48. {"new RegExp(a + /\\d{1,2}/.source)", TTs{IdentifierToken, IdentifierToken, PunctuatorToken, IdentifierToken, PunctuatorToken, RegexpToken, PunctuatorToken, IdentifierToken, PunctuatorToken}},
  49. {"0b0101 0o0707 0b17", TTs{NumericToken, NumericToken, NumericToken, NumericToken}},
  50. {"`template`", TTs{TemplateToken}},
  51. {"`a${x+y}b`", TTs{TemplateToken, IdentifierToken, PunctuatorToken, IdentifierToken, TemplateToken}},
  52. {"`temp\nlate`", TTs{TemplateToken}},
  53. {"`outer${{x: 10}}bar${ raw`nested${2}endnest` }end`", TTs{TemplateToken, PunctuatorToken, IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, TemplateToken, IdentifierToken, TemplateToken, NumericToken, TemplateToken, TemplateToken}},
  54. // early endings
  55. {"'string", TTs{StringToken}},
  56. {"'\n '\u2028", TTs{UnknownToken, LineTerminatorToken, UnknownToken, LineTerminatorToken}},
  57. {"'str\\\U00100000ing\\0'", TTs{StringToken}},
  58. {"'strin\\00g'", TTs{StringToken}},
  59. {"/*comment", TTs{SingleLineCommentToken}},
  60. {"a=/regexp", TTs{IdentifierToken, PunctuatorToken, RegexpToken}},
  61. {"\\u002", TTs{UnknownToken, IdentifierToken}},
  62. // coverage
  63. {"Ø a〉", TTs{IdentifierToken, IdentifierToken, UnknownToken}},
  64. {"0xg 0.f", TTs{NumericToken, IdentifierToken, NumericToken, PunctuatorToken, IdentifierToken}},
  65. {"0bg 0og", TTs{NumericToken, IdentifierToken, NumericToken, IdentifierToken}},
  66. {"\u00A0\uFEFF\u2000", TTs{}},
  67. {"\u2028\u2029", TTs{LineTerminatorToken}},
  68. {"\\u0029ident", TTs{IdentifierToken}},
  69. {"\\u{0029FEF}ident", TTs{IdentifierToken}},
  70. {"\\u{}", TTs{UnknownToken, IdentifierToken, PunctuatorToken, PunctuatorToken}},
  71. {"\\ugident", TTs{UnknownToken, IdentifierToken}},
  72. {"'str\u2028ing'", TTs{UnknownToken, IdentifierToken, LineTerminatorToken, IdentifierToken, StringToken}},
  73. {"a=/\\\n", TTs{IdentifierToken, PunctuatorToken, PunctuatorToken, UnknownToken, LineTerminatorToken}},
  74. {"a=/x/\u200C\u3009", TTs{IdentifierToken, PunctuatorToken, RegexpToken, UnknownToken}},
  75. {"a=/x\n", TTs{IdentifierToken, PunctuatorToken, PunctuatorToken, IdentifierToken, LineTerminatorToken}},
  76. {"return /abc/;", TTs{IdentifierToken, RegexpToken, PunctuatorToken}},
  77. {"yield /abc/;", TTs{IdentifierToken, RegexpToken, PunctuatorToken}},
  78. {"a/b/g", TTs{IdentifierToken, PunctuatorToken, IdentifierToken, PunctuatorToken, IdentifierToken}},
  79. {"{}/1/g", TTs{PunctuatorToken, PunctuatorToken, RegexpToken}},
  80. {"i(0)/1/g", TTs{IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, PunctuatorToken, NumericToken, PunctuatorToken, IdentifierToken}},
  81. {"if(0)/1/g", TTs{IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, RegexpToken}},
  82. {"a.if(0)/1/g", TTs{IdentifierToken, PunctuatorToken, IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, PunctuatorToken, NumericToken, PunctuatorToken, IdentifierToken}},
  83. {"while(0)/1/g", TTs{IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, RegexpToken}},
  84. {"for(;;)/1/g", TTs{IdentifierToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, RegexpToken}},
  85. {"with(0)/1/g", TTs{IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, RegexpToken}},
  86. {"this/1/g", TTs{IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, IdentifierToken}},
  87. {"case /1/g:", TTs{IdentifierToken, RegexpToken, PunctuatorToken}},
  88. {"function f(){}/1/g", TTs{IdentifierToken, IdentifierToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, PunctuatorToken, RegexpToken}},
  89. {"this.return/1/g", TTs{IdentifierToken, PunctuatorToken, IdentifierToken, PunctuatorToken, NumericToken, PunctuatorToken, IdentifierToken}},
  90. {"(a+b)/1/g", TTs{PunctuatorToken, IdentifierToken, PunctuatorToken, IdentifierToken, PunctuatorToken, PunctuatorToken, NumericToken, PunctuatorToken, IdentifierToken}},
  91. {"`\\``", TTs{TemplateToken}},
  92. {"`\\${ 1 }`", TTs{TemplateToken}},
  93. {"`\\\r\n`", TTs{TemplateToken}},
  94. // go fuzz
  95. {"`", TTs{UnknownToken}},
  96. }
  97. for _, tt := range tokenTests {
  98. t.Run(tt.js, func(t *testing.T) {
  99. l := NewLexer(bytes.NewBufferString(tt.js))
  100. i := 0
  101. j := 0
  102. for {
  103. token, _ := l.Next()
  104. j++
  105. if token == ErrorToken {
  106. test.T(t, l.Err(), io.EOF)
  107. test.T(t, i, len(tt.expected), "when error occurred we must be at the end")
  108. break
  109. } else if token == WhitespaceToken {
  110. continue
  111. }
  112. if i < len(tt.expected) {
  113. if token != tt.expected[i] {
  114. test.String(t, token.String(), tt.expected[i].String(), "token types must match")
  115. break
  116. }
  117. } else {
  118. test.Fail(t, "index", i, "must not exceed expected token types size", len(tt.expected))
  119. break
  120. }
  121. i++
  122. }
  123. })
  124. }
  125. test.T(t, WhitespaceToken.String(), "Whitespace")
  126. test.T(t, TokenType(100).String(), "Invalid(100)")
  127. }
  128. ////////////////////////////////////////////////////////////////
  129. func ExampleNewLexer() {
  130. l := NewLexer(bytes.NewBufferString("var x = 'lorem ipsum';"))
  131. out := ""
  132. for {
  133. tt, data := l.Next()
  134. if tt == ErrorToken {
  135. break
  136. }
  137. out += string(data)
  138. }
  139. fmt.Println(out)
  140. // Output: var x = 'lorem ipsum';
  141. }