@malloydata/malloy-filter 0.0.237-dev250222205057 → 0.0.237-dev250224203840

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.
package/README.md CHANGED
@@ -19,42 +19,52 @@ To use the parser, simply import `FilterParser` and go.
19
19
  Example:
20
20
 
21
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');
22
+ import {BooleanParser} from './boolean_parser';
23
+ import {StringParser} from './string_parser';
24
+ import {NumberParser} from './number_parser';
25
+ import {DateParser} from './date_parser';
26
+
27
+ function aSimpleParser() {
28
+ let str = 'CAT,DOG';
29
+ const stringResponse = new StringParser(str).parse();
30
+ console.log(str, '\n', ...stringResponse.clauses, '\n');
31
+
32
+ str = '-5.5, 10, 2.3e7';
33
+ const numberResponse = new NumberParser(str).parse();
34
+ console.log(str, '\n', ...numberResponse.clauses, '\n');
35
+
36
+ str = 'null, false';
37
+ const booleanResponse = new BooleanParser(str).parse();
38
+ console.log(str, '\n', ...booleanResponse.clauses, '\n');
39
+
40
+ str = 'after 2025-10-05';
41
+ const dateResponse = new DateParser(str).parse();
42
+ console.log(str, '\n', ...dateResponse.clauses, '\n');
43
+ }
44
+
45
+ aSimpleParser();
39
46
  ```
40
47
 
41
48
  Output:
42
49
 
43
50
  ```code
44
- CAT,DOG
51
+ CAT,DOG
45
52
  { operator: '=', values: [ 'CAT', 'DOG' ] }
46
53
 
47
54
  -5.5, 10, 2.3e7
48
55
  { operator: '=', values: [ -5.5, 10, 23000000 ] }
49
56
 
50
57
  null, false
51
- { operator: 'NULL' } { operator: 'FALSE' }
58
+ { operator: 'NULL' } { operator: 'FALSEORNULL' }
52
59
 
53
60
  after 2025-10-05
54
- { operator: 'DATE', date: '2025-10-05', prefix: 'AFTER' }
61
+ {
62
+ operator: 'AFTER',
63
+ moment: { type: 'ABSOLUTE', date: '2025-10-05', unit: 'DAY' }
64
+ }
55
65
  ```
56
66
 
57
- Likewise, to use the serializer, simply import `FilterSerializer`.
67
+ Likewise, to use the serializers, simply import the `*Serializer` classes.
58
68
 
59
69
  ## Parsers
60
70
 
@@ -78,4 +88,4 @@ The date and time parser `date_parser.ts` supports the operations highlighted on
78
88
 
79
89
  ## Serializers
80
90
 
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.
91
+ Each parser has a complementary serializer that converts the parser output (`xxxClause[]`) 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 CHANGED
@@ -1,11 +1,11 @@
1
1
  # Parsers
2
2
 
3
- Each filter type is handled by a different parser (strings, numbers, dates and times, etc). Sample outputs from each parser follow...
3
+ Each filter type is handled by a different parser (strings, numbers, dates and times, etc).
4
+ Sample outputs from each parser follow...
4
5
 
6
+ -------------------------------------------------------------------------
5
7
  ## Numbers
6
8
 
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
9
  ```code
10
10
  Input: 5
11
11
  Output: { operator: '=', values: [ 5 ] }
@@ -13,8 +13,8 @@ Output: { operator: '=', values: [ 5 ] }
13
13
  Input: !=5
14
14
  Output: { operator: '!=', values: [ 5 ] }
15
15
 
16
- Input: 1, 3, null , 7
17
- Output: { operator: '=', values: [ 1, 3, null, 7 ] }
16
+ Input: 1, 3, 5, null
17
+ Output: { operator: '=', values: [ 1, 3, 5 ] } { operator: 'NULL' }
18
18
 
19
19
  Input: <1, >=100
20
20
  Output: { operator: '<', values: [ 1 ] } { operator: '>=', values: [ 100 ] }
@@ -26,10 +26,10 @@ Input: <= 10
26
26
  Output: { operator: '<=', values: [ 10 ] }
27
27
 
28
28
  Input: NULL
29
- Output: { operator: '=', values: [ null ] }
29
+ Output: { operator: 'NULL' }
30
30
 
31
31
  Input: -NULL
32
- Output: { operator: '!=', values: [ null ] }
32
+ Output: { operator: 'NOTNULL' }
33
33
 
34
34
  Input: (1, 7)
35
35
  Output: {
@@ -89,7 +89,7 @@ Output: {
89
89
  startValue: 0,
90
90
  endOperator: '<=',
91
91
  endValue: 10
92
- } { operator: '=', values: [ 20, null ] } {
92
+ } { operator: '=', values: [ 20 ] } { operator: 'NULL' } {
93
93
  operator: 'range',
94
94
  startOperator: '>',
95
95
  startValue: 72,
@@ -98,8 +98,7 @@ Output: {
98
98
  }
99
99
 
100
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 }
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: 'Invalid expression', startIndex: 49, endIndex: 54 } { message: 'Invalid expression', startIndex: 56, endIndex: 58 }
103
102
 
104
103
  Input: [cat, 100], <cat
105
104
  Errors: { message: 'Invalid number', startIndex: 1, endIndex: 4 } { message: 'Invalid expression', startIndex: 12, endIndex: 13 } { message: 'Invalid expression', startIndex: 13, endIndex: 16 }
@@ -107,12 +106,12 @@ Errors: { message: 'Invalid number', startIndex: 1, endIndex: 4 } { message: 'I
107
106
  Input: -5.5 to 10
108
107
  Output: { operator: '=', values: [ -5.5, 10 ] }
109
108
  Errors: { message: 'Invalid expression', startIndex: 5, endIndex: 7 }
109
+
110
110
  ```
111
111
 
112
+ -------------------------------------------------------------------------
112
113
  ## Strings
113
114
 
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
115
  ```code
117
116
  Input: CAT, DOG,mouse
118
117
  Output: { operator: '=', values: [ 'CAT', 'DOG', 'mouse' ] }
@@ -121,13 +120,14 @@ Input: -CAT,-DOG , -mouse
121
120
  Output: { operator: '!=', values: [ 'CAT', 'DOG', 'mouse' ] }
122
121
 
123
122
  Input: CAT,-"DOG",m o u s e
124
- Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ '"DOG"' ] } { operator: '=', values: [ 'm o u s e' ] }
123
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ '"DOG"' ] } { operator: '=', values: [ 'm o u s e' ] }
124
+ Quotes: DOUBLE
125
125
 
126
126
  Input: -CAT,-DOG,mouse, bird, zebra, -horse, -goat
127
127
  Output: { operator: '!=', values: [ 'CAT', 'DOG' ] } { operator: '=', values: [ 'mouse', 'bird', 'zebra' ] } { operator: '!=', values: [ 'horse', 'goat' ] }
128
128
 
129
129
  Input: Missing ,NULL
130
- Output: { operator: '=', values: [ 'Missing', null ] }
130
+ Output: { operator: '=', values: [ 'Missing' ] } { operator: 'NULL' }
131
131
 
132
132
  Input: CAT%, D%OG, %ous%, %ira_f%, %_oat,
133
133
  Output: { operator: 'starts', values: [ 'CAT' ] } { operator: '~', values: [ 'D%OG' ] } { operator: 'contains', values: [ 'ous' ] } { operator: '~', values: [ '%ira_f%', '%_oat' ] }
@@ -155,28 +155,30 @@ Input: \_CAT,D\%G,\mouse
155
155
  Output: { operator: '=', values: [ '_CAT', 'D%G', 'mouse' ] }
156
156
 
157
157
  Input: CAT,-NULL
158
- Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ null ] }
158
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: 'NOTNULL' }
159
159
 
160
160
  Input: CAT,-"NULL"
161
161
  Output: { operator: '=', values: [ 'CAT' ] } { operator: '!=', values: [ '"NULL"' ] }
162
+ Quotes: DOUBLE
162
163
 
163
164
  Input: CAT,NULL
164
- Output: { operator: '=', values: [ 'CAT', null ] }
165
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: 'NULL' }
165
166
 
166
167
  Input: EMPTY
167
- Output: { operator: 'EMPTY', values: [ null ] }
168
+ Output: { operator: 'EMPTY' }
168
169
 
169
170
  Input: -EMPTY
170
- Output: { operator: 'NOTEMPTY', values: [ null ] }
171
+ Output: { operator: 'NOTEMPTY' }
171
172
 
172
173
  Input: CAT,-EMPTY
173
- Output: { operator: '=', values: [ 'CAT' ] } { operator: 'NOTEMPTY', values: [ null ] }
174
+ Output: { operator: '=', values: [ 'CAT' ] } { operator: 'NOTEMPTY' }
174
175
 
175
176
  Input: "CAT,DOG',mo`use,zeb'''ra,g"""t,g\"ir\`af\'e
176
177
  Output: {
177
178
  operator: '=',
178
179
  values: [ '"CAT', "DOG'", 'mo`use', "zeb'''ra", 'g"""t', 'g"ir`af\'e' ]
179
180
  }
181
+ Quotes: DOUBLE SINGLE BACKTICK TRIPLESINGLE TRIPLEDOUBLE ESCAPEDDOUBLE ESCAPEDBACKTICK ESCAPEDSINGLE
180
182
 
181
183
  Input: CAT\,DOG
182
184
  Output: { operator: '=', values: [ 'CAT,DOG' ] }
@@ -211,22 +213,26 @@ Output: {
211
213
  operator: '=',
212
214
  values: [ 'hello world', 'foo="bar baz"', 'qux=quux' ]
213
215
  }
216
+ Quotes: DOUBLE
214
217
 
215
218
  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' ] }
219
+ Output: { operator: '=', values: [ 'one' ] } { operator: 'NULL' } { operator: 'EMPTY' } { operator: '=', values: [ 'E M P T Y Y', 'EEmpty', 'emptIEs' ] }
217
220
 
218
221
  Input:
222
+
219
223
  ```
220
224
 
225
+ -------------------------------------------------------------------------
221
226
  ## Booleans
222
227
 
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
228
  ```code
226
229
  Input: true
227
230
  Output: { operator: 'TRUE' }
228
231
 
229
232
  Input: FALSE
233
+ Output: { operator: 'FALSEORNULL' }
234
+
235
+ Input: =false
230
236
  Output: { operator: 'FALSE' }
231
237
 
232
238
  Input: null
@@ -235,8 +241,8 @@ Output: { operator: 'NULL' }
235
241
  Input: -NULL
236
242
  Output: { operator: 'NOTNULL' }
237
243
 
238
- Input: True , faLSE,NULl,-null
239
- Output: { operator: 'TRUE' } { operator: 'FALSE' } { operator: 'NULL' } { operator: 'NOTNULL' }
244
+ Input: True , faLSE,=false,NULl,-null
245
+ Output: { operator: 'TRUE' } { operator: 'FALSEORNULL' } { operator: 'FALSE' } { operator: 'NULL' } { operator: 'NOTNULL' }
240
246
 
241
247
  Input: -'null'
242
248
  Errors: { message: "Invalid token -'null'", startIndex: 0, endIndex: 7 }
@@ -249,119 +255,181 @@ Errors: { message: 'Invalid token nnull', startIndex: 0, endIndex: 5 }
249
255
 
250
256
  Input: truee
251
257
  Errors: { message: 'Invalid token truee', startIndex: 1, endIndex: 6 }
258
+
252
259
  ```
253
260
 
261
+ -------------------------------------------------------------------------
254
262
  ## Dates and Times
255
263
 
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
264
  ```code
259
265
  Input: this month
260
- Output: { operator: 'THIS', unit: 'MONTH' }
266
+ Output: {
267
+ operator: 'ON',
268
+ moment: { type: 'INTERVAL', kind: 'THIS', unit: 'MONTH' }
269
+ }
261
270
 
262
271
  Input: 3 days
263
- Output: { operator: 'TIMEBLOCK', value: '3', unit: 'DAYS' }
272
+ Output: { operator: 'DURATION', duration: { amount: 3, unit: 'DAYS' } }
264
273
 
265
274
  Input: 3 days ago
266
- Output: { operator: 'AGO', value: '3', unit: 'DAYS' }
267
-
268
- Input: 3 months ago for 2 days
269
275
  Output: {
270
- start: { operator: 'AGO', value: '3', unit: 'MONTHS' },
271
- operator: 'FOR',
272
- end: { operator: 'TIMEBLOCK', value: '2', unit: 'DAYS' }
276
+ operator: 'ON',
277
+ moment: {
278
+ type: 'OFFSET_FROM_NOW',
279
+ direction: 'AGO',
280
+ amount: 3,
281
+ unit: 'DAYS'
282
+ }
273
283
  }
274
284
 
275
- Input: after 2025 seconds
285
+ Input: 3 months ago for 2 days
276
286
  Output: {
277
- operator: 'TIMEBLOCK',
278
- value: '2025',
279
- unit: 'SECONDS',
280
- prefix: 'AFTER'
287
+ operator: 'FOR_RANGE',
288
+ from: {
289
+ type: 'OFFSET_FROM_NOW',
290
+ direction: 'AGO',
291
+ amount: 3,
292
+ unit: 'MONTHS'
293
+ },
294
+ duration: { amount: 2, unit: 'DAYS' }
281
295
  }
282
296
 
283
297
  Input: 2025 weeks ago
284
- Output: { operator: 'AGO', value: '2025', unit: 'WEEKS' }
298
+ Output: {
299
+ operator: 'ON',
300
+ moment: {
301
+ type: 'OFFSET_FROM_NOW',
302
+ direction: 'AGO',
303
+ amount: 2025,
304
+ unit: 'WEEKS'
305
+ }
306
+ }
285
307
 
286
308
  Input: before 3 days ago
287
- Output: { operator: 'AGO', value: '3', unit: 'DAYS', prefix: 'BEFORE' }
309
+ Output: {
310
+ operator: 'BEFORE',
311
+ moment: {
312
+ type: 'OFFSET_FROM_NOW',
313
+ direction: 'AGO',
314
+ amount: 3,
315
+ unit: 'DAYS'
316
+ }
317
+ }
288
318
 
289
319
  Input: before 2025-08-30 08:30:20
290
320
  Output: {
291
- operator: 'DATETIME',
292
- date: '2025-08-30',
293
- time: '08:30:20',
294
- prefix: 'BEFORE'
321
+ operator: 'BEFORE',
322
+ moment: { type: 'ABSOLUTE', date: '2025-08-30 08:30:20', unit: 'SECOND' }
295
323
  }
296
324
 
297
325
  Input: after 2025-10-05
298
- Output: { operator: 'DATE', date: '2025-10-05', prefix: 'AFTER' }
326
+ Output: {
327
+ operator: 'AFTER',
328
+ moment: { type: 'ABSOLUTE', date: '2025-10-05', unit: 'DAY' }
329
+ }
299
330
 
300
- Input: 2025-08-30 12:00:00 to 2025-09-18 14:00:00
331
+ Input: 2025-08-30 12:00 to 2025-09-18 14:30
301
332
  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' }
333
+ operator: 'TO_RANGE',
334
+ from: { type: 'ABSOLUTE', date: '2025-08-30 12:00', unit: 'MINUTE' },
335
+ to: { type: 'ABSOLUTE', date: '2025-09-18 14:30', unit: 'MINUTE' }
305
336
  }
306
337
 
307
338
  Input: this year
308
- Output: { operator: 'THIS', unit: 'YEAR' }
339
+ Output: {
340
+ operator: 'ON',
341
+ moment: { type: 'INTERVAL', kind: 'THIS', unit: 'YEAR' }
342
+ }
309
343
 
310
344
  Input: next tuesday
311
- Output: { operator: 'NEXT', unit: 'TUESDAY' }
345
+ Output: {
346
+ operator: 'ON',
347
+ moment: { type: 'INTERVAL', kind: 'NEXT', unit: 'TUESDAY' }
348
+ }
312
349
 
313
350
  Input: 7 years from now
314
- Output: { operator: 'FROMNOW', value: '7', unit: 'YEARS' }
351
+ Output: {
352
+ operator: 'ON',
353
+ moment: {
354
+ type: 'OFFSET_FROM_NOW',
355
+ direction: 'FROMNOW',
356
+ amount: 7,
357
+ unit: 'YEARS'
358
+ }
359
+ }
315
360
 
316
361
  Input: 2025-01-01 12:00:00 for 3 days
317
362
  Output: {
318
- start: { operator: 'DATETIME', date: '2025-01-01', time: '12:00:00' },
319
- operator: 'FOR',
320
- end: { operator: 'TIMEBLOCK', value: '3', unit: 'DAYS' }
363
+ operator: 'FOR_RANGE',
364
+ from: { type: 'ABSOLUTE', date: '2025-01-01 12:00:00', unit: 'SECOND' },
365
+ duration: { amount: 3, unit: 'DAYS' }
321
366
  }
322
367
 
323
368
  Input: 2020-08-12
324
- Output: { operator: 'DATE', date: '2020-08-12' }
369
+ Output: {
370
+ operator: 'ON',
371
+ moment: { type: 'ABSOLUTE', date: '2020-08-12', unit: 'DAY' }
372
+ }
325
373
 
326
374
  Input: 2020-08
327
- Output: { operator: 'DATE', date: '2020-08' }
375
+ Output: {
376
+ operator: 'ON',
377
+ moment: { type: 'ABSOLUTE', date: '2020-08', unit: 'MONTH' }
378
+ }
328
379
 
329
380
  Input: today
330
- Output: { operator: 'TODAY' }
381
+ Output: { operator: 'ON', moment: { type: 'NAMED', name: 'TODAY' } }
331
382
 
332
383
  Input: yesterday
333
- Output: { operator: 'YESTERDAY' }
384
+ Output: { operator: 'ON', moment: { type: 'NAMED', name: 'YESTERDAY' } }
334
385
 
335
386
  Input: tomorrow
336
- Output: { operator: 'TOMORROW' }
387
+ Output: { operator: 'ON', moment: { type: 'NAMED', name: 'TOMORROW' } }
337
388
 
338
389
  Input: TODay,Yesterday, TOMORROW , ,TODay ,,
339
- Output: { operator: 'TODAY' } { operator: 'YESTERDAY' } { operator: 'TOMORROW' } { operator: 'TODAY' }
390
+ Output: { operator: 'ON', moment: { type: 'NAMED', name: 'TODAY' } } { operator: 'ON', moment: { type: 'NAMED', name: 'YESTERDAY' } } { operator: 'ON', moment: { type: 'NAMED', name: 'TOMORROW' } } { operator: 'ON', moment: { type: 'NAMED', name: 'TODAY' } }
340
391
 
341
392
  Input: 2010 to 2011, 2015 to 2016 , 2018, 2020
342
393
  Output: {
343
- start: { operator: 'DATE', date: '2010' },
344
- operator: 'TO',
345
- end: { operator: 'DATE', date: '2011' }
394
+ operator: 'TO_RANGE',
395
+ from: { type: 'ABSOLUTE', date: '2010', unit: 'YEAR' },
396
+ to: { type: 'ABSOLUTE', date: '2011', unit: 'YEAR' }
397
+ } {
398
+ operator: 'TO_RANGE',
399
+ from: { type: 'ABSOLUTE', date: '2015', unit: 'YEAR' },
400
+ to: { type: 'ABSOLUTE', date: '2016', unit: 'YEAR' }
346
401
  } {
347
- start: { operator: 'DATE', date: '2015' },
348
- operator: 'TO',
349
- end: { operator: 'DATE', date: '2016' }
350
- } { operator: 'DATE', date: '2018' } { operator: 'DATE', date: '2020' }
402
+ operator: 'ON',
403
+ moment: { type: 'ABSOLUTE', date: '2018', unit: 'YEAR' }
404
+ } {
405
+ operator: 'ON',
406
+ moment: { type: 'ABSOLUTE', date: '2020', unit: 'YEAR' }
407
+ }
351
408
 
352
409
  Input: next week
353
- Output: { operator: 'NEXT', unit: 'WEEK' }
410
+ Output: {
411
+ operator: 'ON',
412
+ moment: { type: 'INTERVAL', kind: 'NEXT', unit: 'WEEK' }
413
+ }
354
414
 
355
415
  Input: now
356
- Output: { operator: 'NOW' }
416
+ Output: { operator: 'ON', moment: { type: 'NAMED', name: 'NOW' } }
357
417
 
358
418
  Input: now to next month
359
419
  Output: {
360
- start: { operator: 'NOW' },
361
- operator: 'TO',
362
- end: { operator: 'NEXT', unit: 'MONTH' }
420
+ operator: 'TO_RANGE',
421
+ from: { type: 'NAMED', name: 'NOW' },
422
+ to: { type: 'INTERVAL', kind: 'NEXT', unit: 'MONTH' }
363
423
  }
364
424
 
425
+ Input: null
426
+ Output: { operator: 'NULL' }
427
+ Errors: { message: 'Invalid token NULL', startIndex: 0, endIndex: 4 }
428
+
429
+ Input: -null,
430
+ Output: { operator: 'NOTNULL' }
431
+ Errors: { message: 'Invalid token -NULL', startIndex: 0, endIndex: 5 }
432
+
365
433
  Input: yyesterday
366
434
  Errors: { message: 'Invalid token yyesterday', startIndex: 1, endIndex: 11 }
367
435
 
@@ -374,8 +442,19 @@ Input: 7
374
442
  Errors: { message: 'Invalid token 7', startIndex: 0, endIndex: 1 }
375
443
 
376
444
  Input: from now
377
- Output: { operator: 'NOW' }
445
+ Output: { operator: 'ON', moment: { type: 'NAMED', name: 'NOW' } }
378
446
  Errors: { message: 'Invalid token FROM', startIndex: 0, endIndex: 4 }
379
447
 
448
+ Input: 2025-12-25 12:32:
449
+ Output: {
450
+ operator: 'ON',
451
+ moment: { type: 'ABSOLUTE', date: '2025-12-25', unit: 'DAY' }
452
+ }
453
+ Errors: { message: 'Invalid token 12:32:', startIndex: 11, endIndex: 17 }
454
+
455
+ Input: after 2025 seconds
456
+ Errors: { message: 'Invalid token ', startIndex: 6, endIndex: 18 }
457
+
380
458
  Input:
459
+
381
460
  ```
@@ -1,16 +1,15 @@
1
1
  # Serializers
2
2
 
3
- Each parser has a complementary serializer that converts the structured clause list back to string format. Below are round-trip samples: `string` to `Clause[]` back to `string`.
4
-
5
- Round-trip Examples:
3
+ Each parser has a complementary serializer that converts the structured clause list back
4
+ to string format. Below are round-trip samples: `string` to `Clause[]` back to `string`.
5
+ Round-trip Examples:
6
6
 
7
7
  ```code
8
8
  Input > parse > Clause[] > serialize > Output
9
9
  string string
10
10
  ```
11
11
 
12
- ---
13
-
12
+ -------------------------------------------------------------------------
14
13
  ## Number Serializer
15
14
 
16
15
  ```code
@@ -20,8 +19,8 @@ Output: 5
20
19
  Input: !=5
21
20
  Output: !=5
22
21
 
23
- Input: 1, 3, null , 7
24
- Output: 1, 3, NULL, 7
22
+ Input: 1, 3, 5, null
23
+ Output: 1, 3, 5, NULL
25
24
 
26
25
  Input: <1, >=100
27
26
  Output: <1, >=100
@@ -57,8 +56,7 @@ Input: [0,10], 20, NULL, ( 72, 82 ]
57
56
  Output: [0, 10], 20, NULL, (72, 82]
58
57
 
59
58
  Input: , notanumber,, "null", apple pear orange, nulle, nnull, >=,
60
- 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:
61
- 'Invalid expression', startIndex: 49, endIndex: 54 } { message: 'Invalid expression', startIndex: 56, endIndex: 58 }
59
+ 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: 'Invalid expression', startIndex: 49, endIndex: 54 } { message: 'Invalid expression', startIndex: 56, endIndex: 58 }
62
60
 
63
61
  Input: [cat, 100], <cat
64
62
  Errors: { message: 'Invalid number', startIndex: 1, endIndex: 4 } { message: 'Invalid expression', startIndex: 12, endIndex: 13 } { message: 'Invalid expression', startIndex: 13, endIndex: 16 }
@@ -68,8 +66,7 @@ Output: -5.5, 10
68
66
  Errors: { message: 'Invalid expression', startIndex: 5, endIndex: 7 }
69
67
  ```
70
68
 
71
- ---
72
-
69
+ -------------------------------------------------------------------------
73
70
  ## String Serializer
74
71
 
75
72
  ```code
@@ -169,10 +166,10 @@ Input: one ,Null , Empty,E M P T Y Y,EEmpty, emptIEs
169
166
  Output: one, NULL, EMPTY, E M P T Y Y, EEmpty, emptIEs
170
167
 
171
168
  Input:
172
- ```
173
169
 
174
- ---
170
+ ```
175
171
 
172
+ -------------------------------------------------------------------------
176
173
  ## Boolean Serializer
177
174
 
178
175
  ```code
@@ -182,14 +179,17 @@ Output: TRUE
182
179
  Input: FALSE
183
180
  Output: FALSE
184
181
 
182
+ Input: =false
183
+ Output: =FALSE
184
+
185
185
  Input: null
186
186
  Output: NULL
187
187
 
188
188
  Input: -NULL
189
189
  Output: -NULL
190
190
 
191
- Input: True , faLSE,NULl,-null
192
- Output: TRUE, FALSE, NULL, -NULL
191
+ Input: True , faLSE,=false,NULl,-null
192
+ Output: TRUE, FALSE, =FALSE, NULL, -NULL
193
193
 
194
194
  Input: -'null'
195
195
  Errors: { message: "Invalid token -'null'", startIndex: 0, endIndex: 7 }
@@ -202,11 +202,11 @@ Errors: { message: 'Invalid token nnull', startIndex: 0, endIndex: 5 }
202
202
 
203
203
  Input: truee
204
204
  Errors: { message: 'Invalid token truee', startIndex: 1, endIndex: 6 }
205
- ```
206
205
 
207
- ---
206
+ ```
208
207
 
209
- ## Date and Time Serializer
208
+ -------------------------------------------------------------------------
209
+ ## Date Serializer
210
210
 
211
211
  ```code
212
212
  Input: this month
@@ -221,9 +221,6 @@ Output: 3 DAYS AGO
221
221
  Input: 3 months ago for 2 days
222
222
  Output: 3 MONTHS AGO FOR 2 DAYS
223
223
 
224
- Input: after 2025 seconds
225
- Output: AFTER 2025 SECONDS
226
-
227
224
  Input: 2025 weeks ago
228
225
  Output: 2025 WEEKS AGO
229
226
 
@@ -236,8 +233,8 @@ Output: BEFORE 2025-08-30 08:30:20
236
233
  Input: after 2025-10-05
237
234
  Output: AFTER 2025-10-05
238
235
 
239
- Input: 2025-08-30 12:00:00 to 2025-09-18 14:00:00
240
- Output: 2025-08-30 12:00:00 TO 2025-09-18 14:00:00
236
+ Input: 2025-08-30 12:00 to 2025-09-18 14:30
237
+ Output: 2025-08-30 12:00 TO 2025-09-18 14:30
241
238
 
242
239
  Input: this year
243
240
  Output: THIS YEAR
@@ -281,6 +278,14 @@ Output: NOW
281
278
  Input: now to next month
282
279
  Output: NOW TO NEXT MONTH
283
280
 
281
+ Input: null
282
+ Output: NULL
283
+ Errors: { message: 'Invalid token NULL', startIndex: 0, endIndex: 4 }
284
+
285
+ Input: -null,
286
+ Output: -NULL
287
+ Errors: { message: 'Invalid token -NULL', startIndex: 0, endIndex: 5 }
288
+
284
289
  Input: yyesterday
285
290
  Errors: { message: 'Invalid token yyesterday', startIndex: 1, endIndex: 11 }
286
291
 
@@ -296,5 +301,13 @@ Input: from now
296
301
  Output: NOW
297
302
  Errors: { message: 'Invalid token FROM', startIndex: 0, endIndex: 4 }
298
303
 
304
+ Input: 2025-12-25 12:32:
305
+ Output: 2025-12-25
306
+ Errors: { message: 'Invalid token 12:32:', startIndex: 11, endIndex: 17 }
307
+
308
+ Input: after 2025 seconds
309
+ Errors: { message: 'Invalid token ', startIndex: 6, endIndex: 18 }
310
+
299
311
  Input:
300
- ```
312
+
313
+ ```