@malloydata/malloy-filter 0.0.237-dev250221201621

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +81 -0
  2. package/SAMPLES.md +381 -0
  3. package/SERIALIZE_SAMPLES.md +300 -0
  4. package/dist/a_simple_parser.d.ts +1 -0
  5. package/dist/a_simple_parser.js +20 -0
  6. package/dist/a_simple_parser.js.map +1 -0
  7. package/dist/a_simple_serializer.d.ts +1 -0
  8. package/dist/a_simple_serializer.js +31 -0
  9. package/dist/a_simple_serializer.js.map +1 -0
  10. package/dist/base_parser.d.ts +13 -0
  11. package/dist/base_parser.js +33 -0
  12. package/dist/base_parser.js.map +1 -0
  13. package/dist/base_serializer.d.ts +6 -0
  14. package/dist/base_serializer.js +11 -0
  15. package/dist/base_serializer.js.map +1 -0
  16. package/dist/boolean_parser.d.ts +7 -0
  17. package/dist/boolean_parser.js +59 -0
  18. package/dist/boolean_parser.js.map +1 -0
  19. package/dist/boolean_serializer.d.ts +8 -0
  20. package/dist/boolean_serializer.js +31 -0
  21. package/dist/boolean_serializer.js.map +1 -0
  22. package/dist/clause_types.d.ts +70 -0
  23. package/dist/clause_types.js +3 -0
  24. package/dist/clause_types.js.map +1 -0
  25. package/dist/date_parser.d.ts +22 -0
  26. package/dist/date_parser.js +315 -0
  27. package/dist/date_parser.js.map +1 -0
  28. package/dist/date_serializer.d.ts +10 -0
  29. package/dist/date_serializer.js +100 -0
  30. package/dist/date_serializer.js.map +1 -0
  31. package/dist/filter_parser.d.ts +12 -0
  32. package/dist/filter_parser.js +66 -0
  33. package/dist/filter_parser.js.map +1 -0
  34. package/dist/filter_serializer.d.ts +13 -0
  35. package/dist/filter_serializer.js +43 -0
  36. package/dist/filter_serializer.js.map +1 -0
  37. package/dist/filter_types.d.ts +10 -0
  38. package/dist/filter_types.js +3 -0
  39. package/dist/filter_types.js.map +1 -0
  40. package/dist/generate_samples.d.ts +1 -0
  41. package/dist/generate_samples.js +344 -0
  42. package/dist/generate_samples.js.map +1 -0
  43. package/dist/number_parser.d.ts +20 -0
  44. package/dist/number_parser.js +275 -0
  45. package/dist/number_parser.js.map +1 -0
  46. package/dist/number_serializer.d.ts +11 -0
  47. package/dist/number_serializer.js +76 -0
  48. package/dist/number_serializer.js.map +1 -0
  49. package/dist/string_parser.d.ts +18 -0
  50. package/dist/string_parser.js +198 -0
  51. package/dist/string_parser.js.map +1 -0
  52. package/dist/string_serializer.d.ts +11 -0
  53. package/dist/string_serializer.js +77 -0
  54. package/dist/string_serializer.js.map +1 -0
  55. package/dist/token_types.d.ts +7 -0
  56. package/dist/token_types.js +3 -0
  57. package/dist/token_types.js.map +1 -0
  58. package/dist/tokenizer.d.ts +52 -0
  59. package/dist/tokenizer.js +263 -0
  60. package/dist/tokenizer.js.map +1 -0
  61. package/dist/tokenizer.spec.d.ts +1 -0
  62. package/dist/tokenizer.spec.js +255 -0
  63. package/dist/tokenizer.spec.js.map +1 -0
  64. package/jest.config.js +3 -0
  65. package/package.json +21 -0
  66. package/src/DEVELOPING.md +26 -0
  67. package/src/a_simple_parser.ts +22 -0
  68. package/src/a_simple_serializer.ts +40 -0
  69. package/src/base_parser.ts +45 -0
  70. package/src/base_serializer.ts +9 -0
  71. package/src/boolean_parser.ts +60 -0
  72. package/src/boolean_serializer.ts +32 -0
  73. package/src/clause_types.ts +160 -0
  74. package/src/date_parser.ts +413 -0
  75. package/src/date_serializer.ts +114 -0
  76. package/src/filter_parser.ts +68 -0
  77. package/src/filter_serializer.ts +49 -0
  78. package/src/filter_types.ts +12 -0
  79. package/src/generate_samples.ts +387 -0
  80. package/src/number_parser.ts +308 -0
  81. package/src/number_serializer.ts +96 -0
  82. package/src/string_parser.ts +193 -0
  83. package/src/string_serializer.ts +87 -0
  84. package/src/token_types.ts +7 -0
  85. package/src/tokenizer.spec.ts +273 -0
  86. package/src/tokenizer.ts +320 -0
  87. package/tsconfig.json +14 -0
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Malloy Filter Expressions
2
+
3
+ This repository contains the parser and serializer for Malloy filter expressions. Filter expressions give Malloy users a powerful, yet simple and intuitive, way to specify complex filtering conditions.
4
+
5
+ The library contains no external dependencies, and is designed to produce a lightweight data structure that can be embedded with minimal overhead into products interfacing with Malloy. Instructions for building and using the library are below.
6
+
7
+ The WN for filter expressions is [TBD].
8
+
9
+ ## Building
10
+
11
+ ```bash
12
+ npm run build
13
+ ```
14
+
15
+ ## Using
16
+
17
+ To use the parser, simply import `FilterParser` and go.
18
+
19
+ Example:
20
+
21
+ ```code
22
+ import { FilterParser } from './filter_parser'
23
+
24
+ let str = 'CAT,DOG';
25
+ let response = new FilterParser(str, 'string').parse();
26
+ console.log(str, '\n', ...response.clauses, '\n');
27
+
28
+ str = '-5.5, 10, 2.3e7';
29
+ response = new FilterParser(str, 'number').parse();
30
+ console.log(str, '\n', ...response.clauses, '\n');
31
+
32
+ str = 'null, false';
33
+ response = new FilterParser(str, 'boolean').parse();
34
+ console.log(str, '\n', ...response.clauses, '\n');
35
+
36
+ str = 'after 2025-10-05';
37
+ response = new FilterParser(str, 'date').parse();
38
+ console.log(str, '\n', ...response.clauses, '\n');
39
+ ```
40
+
41
+ Output:
42
+
43
+ ```code
44
+ CAT,DOG
45
+ { operator: '=', values: [ 'CAT', 'DOG' ] }
46
+
47
+ -5.5, 10, 2.3e7
48
+ { operator: '=', values: [ -5.5, 10, 23000000 ] }
49
+
50
+ null, false
51
+ { operator: 'NULL' } { operator: 'FALSE' }
52
+
53
+ after 2025-10-05
54
+ { operator: 'DATE', date: '2025-10-05', prefix: 'AFTER' }
55
+ ```
56
+
57
+ Likewise, to use the serializer, simply import `FilterSerializer`.
58
+
59
+ ## Parsers
60
+
61
+ Each filter type is handled by a different parser (strings, numbers, dates and times, etc).
62
+
63
+ ### Number Parser
64
+
65
+ The number parser `number_parser.ts` supports the operations highlighted on the [Samples](SAMPLES.md#numbers) page.
66
+
67
+ ### String Parser
68
+
69
+ The string parser `string_parser.ts` supports the operations highlighted on the [Samples](SAMPLES.md#strings) page.
70
+
71
+ ### Boolean Parser
72
+
73
+ The boolean parser `boolean_parser.ts` supports the operations highlighted on the [Samples](SAMPLES.md#booleans) page.
74
+
75
+ ### Date and Time Parser
76
+
77
+ The date and time parser `date_parser.ts` supports the operations highlighted on the [Samples](SAMPLES.md#dates-and-times) page.
78
+
79
+ ## Serializers
80
+
81
+ Each parser has a complementary serializer that converts the parser output (`Clause[]`) back to a string. See examples of the round trip from string to Clause to string on the [Serialization Samples](SERIALIZE_SAMPLES.md) page.
package/SAMPLES.md ADDED
@@ -0,0 +1,381 @@
1
+ # Parsers
2
+
3
+ Each filter type is handled by a different parser (strings, numbers, dates and times, etc). Sample outputs from each parser follow...
4
+
5
+ ## Numbers
6
+
7
+ The number parser `number_parser.ts` supports the "numeric" expressions shown below. `Input` is the input string and `Output` shows the output of `number_parser`:
8
+
9
+ ```code
10
+ Input: 5
11
+ Output: { operator: '=', values: [ 5 ] }
12
+
13
+ Input: !=5
14
+ Output: { operator: '!=', values: [ 5 ] }
15
+
16
+ Input: 1, 3, null , 7
17
+ Output: { operator: '=', values: [ 1, 3, null, 7 ] }
18
+
19
+ Input: <1, >=100
20
+ Output: { operator: '<', values: [ 1 ] } { operator: '>=', values: [ 100 ] }
21
+
22
+ Input: >=1
23
+ Output: { operator: '>=', values: [ 1 ] }
24
+
25
+ Input: <= 10
26
+ Output: { operator: '<=', values: [ 10 ] }
27
+
28
+ Input: NULL
29
+ Output: { operator: '=', values: [ null ] }
30
+
31
+ Input: -NULL
32
+ Output: { operator: '!=', values: [ null ] }
33
+
34
+ Input: (1, 7)
35
+ Output: {
36
+ operator: 'range',
37
+ startOperator: '>',
38
+ startValue: 1,
39
+ endOperator: '<',
40
+ endValue: 7
41
+ }
42
+
43
+ Input: [-5, 90]
44
+ Output: {
45
+ operator: 'range',
46
+ startOperator: '>=',
47
+ startValue: -5,
48
+ endOperator: '<=',
49
+ endValue: 90
50
+ }
51
+
52
+ Input: != ( 12, 20 ]
53
+ Output: {
54
+ operator: 'range',
55
+ startOperator: '<=',
56
+ startValue: 12,
57
+ endOperator: '>',
58
+ endValue: 20
59
+ }
60
+
61
+ Input: [.12e-20, 20.0e3)
62
+ Output: {
63
+ operator: 'range',
64
+ startOperator: '>=',
65
+ startValue: 1.2e-21,
66
+ endOperator: '<',
67
+ endValue: 20000
68
+ }
69
+
70
+ Input: [0,9],[20,29]
71
+ Output: {
72
+ operator: 'range',
73
+ startOperator: '>=',
74
+ startValue: 0,
75
+ endOperator: '<=',
76
+ endValue: 9
77
+ } {
78
+ operator: 'range',
79
+ startOperator: '>=',
80
+ startValue: 20,
81
+ endOperator: '<=',
82
+ endValue: 29
83
+ }
84
+
85
+ Input: [0,10], 20, NULL, ( 72, 82 ]
86
+ Output: {
87
+ operator: 'range',
88
+ startOperator: '>=',
89
+ startValue: 0,
90
+ endOperator: '<=',
91
+ endValue: 10
92
+ } { operator: '=', values: [ 20, null ] } {
93
+ operator: 'range',
94
+ startOperator: '>',
95
+ startValue: 72,
96
+ endOperator: '<=',
97
+ endValue: 82
98
+ }
99
+
100
+ Input: , notanumber,, "null", apple pear orange, nulle, nnull, >=,
101
+ Errors: { message: 'Invalid expression', startIndex: 2, endIndex: 12 } { message: 'Invalid expression', startIndex: 15, endIndex: 21 } { message: 'Invalid expression', startIndex: 23, endIndex: 28 } { message: 'Invalid expression', startIndex: 29, endIndex: 33 } { message: 'Invalid expression', startIndex: 34, endIndex: 40 } { message: 'Invalid expression', startIndex: 42, endIndex: 47 } { message:
102
+ 'Invalid expression', startIndex: 49, endIndex: 54 } { message: 'Invalid expression', startIndex: 56, endIndex: 58 }
103
+
104
+ Input: [cat, 100], <cat
105
+ Errors: { message: 'Invalid number', startIndex: 1, endIndex: 4 } { message: 'Invalid expression', startIndex: 12, endIndex: 13 } { message: 'Invalid expression', startIndex: 13, endIndex: 16 }
106
+
107
+ Input: -5.5 to 10
108
+ Output: { operator: '=', values: [ -5.5, 10 ] }
109
+ Errors: { message: 'Invalid expression', startIndex: 5, endIndex: 7 }
110
+ ```
111
+
112
+ ## Strings
113
+
114
+ The string parser `string_parser.ts` supports the "string expressions" shown below. `Input` is the input string and `Output` shows the output of `string_parser`. Note that **several examples below are illogical**, and are intended to stress-test the parser.
115
+
116
+ ```code
117
+ Input: CAT, DOG,mouse
118
+ Output: { operator: '=', values: [ 'CAT', 'DOG', 'mouse' ] }
119
+
120
+ Input: -CAT,-DOG , -mouse
121
+ Output: { operator: '!=', values: [ 'CAT', 'DOG', 'mouse' ] }
122
+
123
+ Input: CAT,-"DOG",m o u s e
124
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ '"DOG"' ] } { operator: '=', values: [ 'm o u s e' ] }
125
+
126
+ Input: -CAT,-DOG,mouse, bird, zebra, -horse, -goat
127
+ Output: { operator: '!=', values: [ 'CAT', 'DOG' ] } { operator: '=', values: [ 'mouse', 'bird', 'zebra' ] } { operator: '!=', values: [ 'horse', 'goat' ] }
128
+
129
+ Input: Missing ,NULL
130
+ Output: { operator: '=', values: [ 'Missing', null ] }
131
+
132
+ Input: CAT%, D%OG, %ous%, %ira_f%, %_oat,
133
+ Output: { operator: 'starts', values: [ 'CAT' ] } { operator: '~', values: [ 'D%OG' ] } { operator: 'contains', values: [ 'ous' ] } { operator: '~', values: [ '%ira_f%', '%_oat' ] }
134
+ Errors: { message: 'Invalid expression', startIndex: 34, endIndex: 35 }
135
+
136
+ Input: -CAT%,-D%OG,-%mouse,-%zebra%
137
+ Output: { operator: 'notStarts', values: [ 'CAT' ] } { operator: '!~', values: [ 'D%OG' ] } { operator: 'notEnds', values: [ 'mouse' ] } { operator: 'notContains', values: [ 'zebra' ] }
138
+
139
+ Input: CAT%,-CATALOG
140
+ Output: { operator: 'starts', values: [ 'CAT' ] } { operator: '!=', values: [ 'CATALOG' ] }
141
+
142
+ Input: %,_,%%,%a%
143
+ Output: { operator: '~', values: [ '%', '_', '%%' ] } { operator: 'contains', values: [ 'a' ] }
144
+
145
+ Input: %\_X
146
+ Output: { operator: 'ends', values: [ '_X' ] }
147
+
148
+ Input: _\_X
149
+ Output: { operator: '~', values: [ '_\\_X' ] }
150
+
151
+ Input: _CAT,D_G,mouse_
152
+ Output: { operator: '~', values: [ '_CAT', 'D_G', 'mouse_' ] }
153
+
154
+ Input: \_CAT,D\%G,\mouse
155
+ Output: { operator: '=', values: [ '_CAT', 'D%G', 'mouse' ] }
156
+
157
+ Input: CAT,-NULL
158
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ null ] }
159
+
160
+ Input: CAT,-"NULL"
161
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ '"NULL"' ] }
162
+
163
+ Input: CAT,NULL
164
+ Output: { operator: '=', values: [ 'CAT', null ] }
165
+
166
+ Input: EMPTY
167
+ Output: { operator: 'EMPTY', values: [ null ] }
168
+
169
+ Input: -EMPTY
170
+ Output: { operator: 'NOTEMPTY', values: [ null ] }
171
+
172
+ Input: CAT,-EMPTY
173
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: 'NOTEMPTY', values: [ null ] }
174
+
175
+ Input: "CAT,DOG',mo`use,zeb'''ra,g"""t,g\"ir\`af\'e
176
+ Output: {
177
+ operator: '=',
178
+ values: [ '"CAT', "DOG'", 'mo`use', "zeb'''ra", 'g"""t', 'g"ir`af\'e' ]
179
+ }
180
+
181
+ Input: CAT\,DOG
182
+ Output: { operator: '=', values: [ 'CAT,DOG' ] }
183
+
184
+ Input: CAT,DOG,-, -
185
+ Output: { operator: '=', values: [ 'CAT', 'DOG', '-', '-' ] }
186
+
187
+ Input: --CAT,DOG,\
188
+ Output: { operator: '!=', values: [ '-CAT' ] } { operator: '=', values: [ 'DOG' ] }
189
+ Errors: { message: 'Invalid expression', startIndex: 10, endIndex: 11 }
190
+
191
+ Input: CAT\ DOG
192
+ Output: { operator: '=', values: [ 'CAT DOG' ] }
193
+
194
+ Input: _\_CAT
195
+ Output: { operator: '~', values: [ '_\\_CAT' ] }
196
+
197
+ Input: \NULL
198
+ Output: { operator: '=', values: [ 'NULL' ] }
199
+
200
+ Input: \-NULL
201
+ Output: { operator: '=', values: [ '-NULL' ] }
202
+
203
+ Input: -N\ULL
204
+ Output: { operator: '!=', values: [ 'NULL' ] }
205
+
206
+ Input: CA--,D-G
207
+ Output: { operator: '=', values: [ 'CA--', 'D-G' ] }
208
+
209
+ Input: hello world, foo="bar baz" , qux=quux
210
+ Output: {
211
+ operator: '=',
212
+ values: [ 'hello world', 'foo="bar baz"', 'qux=quux' ]
213
+ }
214
+
215
+ Input: one ,Null , Empty,E M P T Y Y,EEmpty, emptIEs
216
+ Output: { operator: '=', values: [ 'one', null ] } { operator: 'EMPTY', values: [ null ] } { operator: '=', values: [ 'E M P T Y Y', 'EEmpty', 'emptIEs' ] }
217
+
218
+ Input:
219
+ ```
220
+
221
+ ## Booleans
222
+
223
+ The boolean parser `boolean_parser.ts` supports the "truthy" expressions shown below. `Input` is the input string and `Output` shows the output of `boolean_parser`:
224
+
225
+ ```code
226
+ Input: true
227
+ Output: { operator: 'TRUE' }
228
+
229
+ Input: FALSE
230
+ Output: { operator: 'FALSE' }
231
+
232
+ Input: null
233
+ Output: { operator: 'NULL' }
234
+
235
+ Input: -NULL
236
+ Output: { operator: 'NOTNULL' }
237
+
238
+ Input: True , faLSE,NULl,-null
239
+ Output: { operator: 'TRUE' } { operator: 'FALSE' } { operator: 'NULL' } { operator: 'NOTNULL' }
240
+
241
+ Input: -'null'
242
+ Errors: { message: "Invalid token -'null'", startIndex: 0, endIndex: 7 }
243
+
244
+ Input: 10
245
+ Errors: { message: 'Invalid token 10', startIndex: 0, endIndex: 2 }
246
+
247
+ Input: nnull
248
+ Errors: { message: 'Invalid token nnull', startIndex: 0, endIndex: 5 }
249
+
250
+ Input: truee
251
+ Errors: { message: 'Invalid token truee', startIndex: 1, endIndex: 6 }
252
+ ```
253
+
254
+ ## Dates and Times
255
+
256
+ The date and time parser `date_parser.ts` supports the date and time expressions shown below. `Input` is the input string and `Output` shows the output of `date_parser`. Note that, like with strings above, we include several invalid examples to highlight error responses:
257
+
258
+ ```code
259
+ Input: this month
260
+ Output: { operator: 'THIS', unit: 'MONTH' }
261
+
262
+ Input: 3 days
263
+ Output: { operator: 'TIMEBLOCK', value: '3', unit: 'DAYS' }
264
+
265
+ Input: 3 days ago
266
+ Output: { operator: 'AGO', value: '3', unit: 'DAYS' }
267
+
268
+ Input: 3 months ago for 2 days
269
+ Output: {
270
+ start: { operator: 'AGO', value: '3', unit: 'MONTHS' },
271
+ operator: 'FOR',
272
+ end: { operator: 'TIMEBLOCK', value: '2', unit: 'DAYS' }
273
+ }
274
+
275
+ Input: after 2025 seconds
276
+ Output: {
277
+ operator: 'TIMEBLOCK',
278
+ value: '2025',
279
+ unit: 'SECONDS',
280
+ prefix: 'AFTER'
281
+ }
282
+
283
+ Input: 2025 weeks ago
284
+ Output: { operator: 'AGO', value: '2025', unit: 'WEEKS' }
285
+
286
+ Input: before 3 days ago
287
+ Output: { operator: 'AGO', value: '3', unit: 'DAYS', prefix: 'BEFORE' }
288
+
289
+ Input: before 2025-08-30 08:30:20
290
+ Output: {
291
+ operator: 'DATETIME',
292
+ date: '2025-08-30',
293
+ time: '08:30:20',
294
+ prefix: 'BEFORE'
295
+ }
296
+
297
+ Input: after 2025-10-05
298
+ Output: { operator: 'DATE', date: '2025-10-05', prefix: 'AFTER' }
299
+
300
+ Input: 2025-08-30 12:00:00 to 2025-09-18 14:00:00
301
+ Output: {
302
+ start: { operator: 'DATETIME', date: '2025-08-30', time: '12:00:00' },
303
+ operator: 'TO',
304
+ end: { operator: 'DATETIME', date: '2025-09-18', time: '14:00:00' }
305
+ }
306
+
307
+ Input: this year
308
+ Output: { operator: 'THIS', unit: 'YEAR' }
309
+
310
+ Input: next tuesday
311
+ Output: { operator: 'NEXT', unit: 'TUESDAY' }
312
+
313
+ Input: 7 years from now
314
+ Output: { operator: 'FROMNOW', value: '7', unit: 'YEARS' }
315
+
316
+ Input: 2025-01-01 12:00:00 for 3 days
317
+ Output: {
318
+ start: { operator: 'DATETIME', date: '2025-01-01', time: '12:00:00' },
319
+ operator: 'FOR',
320
+ end: { operator: 'TIMEBLOCK', value: '3', unit: 'DAYS' }
321
+ }
322
+
323
+ Input: 2020-08-12
324
+ Output: { operator: 'DATE', date: '2020-08-12' }
325
+
326
+ Input: 2020-08
327
+ Output: { operator: 'DATE', date: '2020-08' }
328
+
329
+ Input: today
330
+ Output: { operator: 'TODAY' }
331
+
332
+ Input: yesterday
333
+ Output: { operator: 'YESTERDAY' }
334
+
335
+ Input: tomorrow
336
+ Output: { operator: 'TOMORROW' }
337
+
338
+ Input: TODay,Yesterday, TOMORROW , ,TODay ,,
339
+ Output: { operator: 'TODAY' } { operator: 'YESTERDAY' } { operator: 'TOMORROW' } { operator: 'TODAY' }
340
+
341
+ Input: 2010 to 2011, 2015 to 2016 , 2018, 2020
342
+ Output: {
343
+ start: { operator: 'DATE', date: '2010' },
344
+ operator: 'TO',
345
+ end: { operator: 'DATE', date: '2011' }
346
+ } {
347
+ start: { operator: 'DATE', date: '2015' },
348
+ operator: 'TO',
349
+ end: { operator: 'DATE', date: '2016' }
350
+ } { operator: 'DATE', date: '2018' } { operator: 'DATE', date: '2020' }
351
+
352
+ Input: next week
353
+ Output: { operator: 'NEXT', unit: 'WEEK' }
354
+
355
+ Input: now
356
+ Output: { operator: 'NOW' }
357
+
358
+ Input: now to next month
359
+ Output: {
360
+ start: { operator: 'NOW' },
361
+ operator: 'TO',
362
+ end: { operator: 'NEXT', unit: 'MONTH' }
363
+ }
364
+
365
+ Input: yyesterday
366
+ Errors: { message: 'Invalid token yyesterday', startIndex: 1, endIndex: 11 }
367
+
368
+ Input: before
369
+
370
+ Input: for
371
+ Errors: { message: 'Invalid token FOR', startIndex: 0, endIndex: 3 }
372
+
373
+ Input: 7
374
+ Errors: { message: 'Invalid token 7', startIndex: 0, endIndex: 1 }
375
+
376
+ Input: from now
377
+ Output: { operator: 'NOW' }
378
+ Errors: { message: 'Invalid token FROM', startIndex: 0, endIndex: 4 }
379
+
380
+ Input:
381
+ ```