lexer_test.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package jmespath
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/stretchr/testify/assert"
  6. )
  7. var lexingTests = []struct {
  8. expression string
  9. expected []token
  10. }{
  11. {"*", []token{{tStar, "*", 0, 1}}},
  12. {".", []token{{tDot, ".", 0, 1}}},
  13. {"[?", []token{{tFilter, "[?", 0, 2}}},
  14. {"[]", []token{{tFlatten, "[]", 0, 2}}},
  15. {"(", []token{{tLparen, "(", 0, 1}}},
  16. {")", []token{{tRparen, ")", 0, 1}}},
  17. {"[", []token{{tLbracket, "[", 0, 1}}},
  18. {"]", []token{{tRbracket, "]", 0, 1}}},
  19. {"{", []token{{tLbrace, "{", 0, 1}}},
  20. {"}", []token{{tRbrace, "}", 0, 1}}},
  21. {"||", []token{{tOr, "||", 0, 2}}},
  22. {"|", []token{{tPipe, "|", 0, 1}}},
  23. {"29", []token{{tNumber, "29", 0, 2}}},
  24. {"2", []token{{tNumber, "2", 0, 1}}},
  25. {"0", []token{{tNumber, "0", 0, 1}}},
  26. {"-20", []token{{tNumber, "-20", 0, 3}}},
  27. {"foo", []token{{tUnquotedIdentifier, "foo", 0, 3}}},
  28. {`"bar"`, []token{{tQuotedIdentifier, "bar", 0, 3}}},
  29. // Escaping the delimiter
  30. {`"bar\"baz"`, []token{{tQuotedIdentifier, `bar"baz`, 0, 7}}},
  31. {",", []token{{tComma, ",", 0, 1}}},
  32. {":", []token{{tColon, ":", 0, 1}}},
  33. {"<", []token{{tLT, "<", 0, 1}}},
  34. {"<=", []token{{tLTE, "<=", 0, 2}}},
  35. {">", []token{{tGT, ">", 0, 1}}},
  36. {">=", []token{{tGTE, ">=", 0, 2}}},
  37. {"==", []token{{tEQ, "==", 0, 2}}},
  38. {"!=", []token{{tNE, "!=", 0, 2}}},
  39. {"`[0, 1, 2]`", []token{{tJSONLiteral, "[0, 1, 2]", 1, 9}}},
  40. {"'foo'", []token{{tStringLiteral, "foo", 1, 3}}},
  41. {"'a'", []token{{tStringLiteral, "a", 1, 1}}},
  42. {`'foo\'bar'`, []token{{tStringLiteral, "foo'bar", 1, 7}}},
  43. {"@", []token{{tCurrent, "@", 0, 1}}},
  44. {"&", []token{{tExpref, "&", 0, 1}}},
  45. // Quoted identifier unicode escape sequences
  46. {`"\u2713"`, []token{{tQuotedIdentifier, "✓", 0, 3}}},
  47. {`"\\"`, []token{{tQuotedIdentifier, `\`, 0, 1}}},
  48. {"`\"foo\"`", []token{{tJSONLiteral, "\"foo\"", 1, 5}}},
  49. // Combinations of tokens.
  50. {"foo.bar", []token{
  51. {tUnquotedIdentifier, "foo", 0, 3},
  52. {tDot, ".", 3, 1},
  53. {tUnquotedIdentifier, "bar", 4, 3},
  54. }},
  55. {"foo[0]", []token{
  56. {tUnquotedIdentifier, "foo", 0, 3},
  57. {tLbracket, "[", 3, 1},
  58. {tNumber, "0", 4, 1},
  59. {tRbracket, "]", 5, 1},
  60. }},
  61. {"foo[?a<b]", []token{
  62. {tUnquotedIdentifier, "foo", 0, 3},
  63. {tFilter, "[?", 3, 2},
  64. {tUnquotedIdentifier, "a", 5, 1},
  65. {tLT, "<", 6, 1},
  66. {tUnquotedIdentifier, "b", 7, 1},
  67. {tRbracket, "]", 8, 1},
  68. }},
  69. }
  70. func TestCanLexTokens(t *testing.T) {
  71. assert := assert.New(t)
  72. lexer := NewLexer()
  73. for _, tt := range lexingTests {
  74. tokens, err := lexer.tokenize(tt.expression)
  75. if assert.Nil(err) {
  76. errMsg := fmt.Sprintf("Mismatch expected number of tokens: (expected: %s, actual: %s)",
  77. tt.expected, tokens)
  78. tt.expected = append(tt.expected, token{tEOF, "", len(tt.expression), 0})
  79. if assert.Equal(len(tt.expected), len(tokens), errMsg) {
  80. for i, token := range tokens {
  81. expected := tt.expected[i]
  82. assert.Equal(expected, token, "Token not equal")
  83. }
  84. }
  85. }
  86. }
  87. }
  88. var lexingErrorTests = []struct {
  89. expression string
  90. msg string
  91. }{
  92. {"'foo", "Missing closing single quote"},
  93. {"[?foo==bar?]", "Unknown char '?'"},
  94. }
  95. func TestLexingErrors(t *testing.T) {
  96. assert := assert.New(t)
  97. lexer := NewLexer()
  98. for _, tt := range lexingErrorTests {
  99. _, err := lexer.tokenize(tt.expression)
  100. assert.NotNil(err, fmt.Sprintf("Expected lexing error: %s", tt.msg))
  101. }
  102. }
  103. var exprIdentifier = "abcdefghijklmnopqrstuvwxyz"
  104. var exprSubexpr = "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz"
  105. var deeplyNested50 = "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40.j39.j38.j37.j36.j35.j34.j33.j32.j31.j30.j29.j28.j27.j26.j25.j24.j23.j22.j21.j20.j19.j18.j17.j16.j15.j14.j13.j12.j11.j10.j9.j8.j7.j6.j5.j4.j3.j2.j1.j0"
  106. var deeplyNested50Pipe = "j49|j48|j47|j46|j45|j44|j43|j42|j41|j40|j39|j38|j37|j36|j35|j34|j33|j32|j31|j30|j29|j28|j27|j26|j25|j24|j23|j22|j21|j20|j19|j18|j17|j16|j15|j14|j13|j12|j11|j10|j9|j8|j7|j6|j5|j4|j3|j2|j1|j0"
  107. var deeplyNested50Index = "[49][48][47][46][45][44][43][42][41][40][39][38][37][36][35][34][33][32][31][30][29][28][27][26][25][24][23][22][21][20][19][18][17][16][15][14][13][12][11][10][9][8][7][6][5][4][3][2][1][0]"
  108. var deepProjection104 = "a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*]"
  109. var exprQuotedIdentifier = `"abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz"`
  110. var quotedIdentifierEscapes = `"\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t"`
  111. var rawStringLiteral = `'abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz'`
  112. func BenchmarkLexIdentifier(b *testing.B) {
  113. runLexBenchmark(b, exprIdentifier)
  114. }
  115. func BenchmarkLexSubexpression(b *testing.B) {
  116. runLexBenchmark(b, exprSubexpr)
  117. }
  118. func BenchmarkLexDeeplyNested50(b *testing.B) {
  119. runLexBenchmark(b, deeplyNested50)
  120. }
  121. func BenchmarkLexDeepNested50Pipe(b *testing.B) {
  122. runLexBenchmark(b, deeplyNested50Pipe)
  123. }
  124. func BenchmarkLexDeepNested50Index(b *testing.B) {
  125. runLexBenchmark(b, deeplyNested50Index)
  126. }
  127. func BenchmarkLexQuotedIdentifier(b *testing.B) {
  128. runLexBenchmark(b, exprQuotedIdentifier)
  129. }
  130. func BenchmarkLexQuotedIdentifierEscapes(b *testing.B) {
  131. runLexBenchmark(b, quotedIdentifierEscapes)
  132. }
  133. func BenchmarkLexRawStringLiteral(b *testing.B) {
  134. runLexBenchmark(b, rawStringLiteral)
  135. }
  136. func BenchmarkLexDeepProjection104(b *testing.B) {
  137. runLexBenchmark(b, deepProjection104)
  138. }
  139. func runLexBenchmark(b *testing.B, expression string) {
  140. lexer := NewLexer()
  141. for i := 0; i < b.N; i++ {
  142. lexer.tokenize(expression)
  143. }
  144. }