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

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 (60) hide show
  1. package/SAMPLES.md +336 -116
  2. package/SERIALIZE_SAMPLES.md +268 -64
  3. package/dist/a_simple_parser.js +6 -0
  4. package/dist/a_simple_parser.js.map +1 -1
  5. package/dist/base_parser.js +6 -0
  6. package/dist/base_parser.js.map +1 -1
  7. package/dist/boolean_parser.js +28 -13
  8. package/dist/boolean_parser.js.map +1 -1
  9. package/dist/boolean_serializer.js +12 -6
  10. package/dist/boolean_serializer.js.map +1 -1
  11. package/dist/clause_types.d.ts +20 -15
  12. package/dist/clause_types.js +6 -0
  13. package/dist/clause_types.js.map +1 -1
  14. package/dist/date_parser.d.ts +1 -0
  15. package/dist/date_parser.js +174 -116
  16. package/dist/date_parser.js.map +1 -1
  17. package/dist/date_serializer.js +26 -20
  18. package/dist/date_serializer.js.map +1 -1
  19. package/dist/date_types.d.ts +21 -21
  20. package/dist/date_types.js +6 -0
  21. package/dist/date_types.js.map +1 -1
  22. package/dist/generate_samples.js +41 -26
  23. package/dist/generate_samples.js.map +1 -1
  24. package/dist/index.d.ts +10 -0
  25. package/dist/index.js +42 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/number_parser.js +43 -25
  28. package/dist/number_parser.js.map +1 -1
  29. package/dist/number_serializer.js +10 -4
  30. package/dist/number_serializer.js.map +1 -1
  31. package/dist/string_parser.d.ts +0 -1
  32. package/dist/string_parser.js +47 -79
  33. package/dist/string_parser.js.map +1 -1
  34. package/dist/string_serializer.d.ts +1 -0
  35. package/dist/string_serializer.js +49 -33
  36. package/dist/string_serializer.js.map +1 -1
  37. package/dist/token_types.js +6 -0
  38. package/dist/token_types.js.map +1 -1
  39. package/dist/tokenizer.js +9 -3
  40. package/dist/tokenizer.js.map +1 -1
  41. package/dist/tokenizer.spec.js +13 -7
  42. package/dist/tokenizer.spec.js.map +1 -1
  43. package/package.json +2 -2
  44. package/src/a_simple_parser.ts +7 -0
  45. package/src/base_parser.ts +7 -0
  46. package/src/boolean_parser.ts +30 -18
  47. package/src/boolean_serializer.ts +13 -6
  48. package/src/clause_types.ts +36 -31
  49. package/src/date_parser.ts +177 -115
  50. package/src/date_serializer.ts +27 -20
  51. package/src/date_types.ts +42 -34
  52. package/src/generate_samples.ts +42 -26
  53. package/src/index.ts +17 -0
  54. package/src/number_parser.ts +45 -26
  55. package/src/number_serializer.ts +11 -4
  56. package/src/string_parser.ts +51 -79
  57. package/src/string_serializer.ts +65 -39
  58. package/src/token_types.ts +7 -0
  59. package/src/tokenizer.spec.ts +14 -7
  60. package/src/tokenizer.ts +10 -3
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
1
8
  import {SpecialToken, Tokenizer, TokenizerParams} from './tokenizer';
2
9
  import {
3
10
  DateTimeUnit,
@@ -21,9 +28,9 @@ import {
21
28
  } from './date_types';
22
29
  import {BaseParser} from './base_parser';
23
30
  import {Token} from './token_types';
24
- import {FilterError} from './clause_types';
31
+ import {FilterLog} from './clause_types';
25
32
 
26
- type DatePrefix = 'BEFORE' | 'AFTER';
33
+ type DatePrefix = 'before' | 'after';
27
34
 
28
35
  export class DateParser extends BaseParser {
29
36
  private static readonly yearRegex: RegExp = /[%_]/;
@@ -35,38 +42,49 @@ export class DateParser extends BaseParser {
35
42
 
36
43
  private tokenize(): void {
37
44
  const specialSubstrings: SpecialToken[] = [{type: ',', value: ','}];
45
+ // Do not reorder.
38
46
  const specialWords: SpecialToken[] = [
39
47
  {
40
- type: 'UNITOFTIME',
48
+ type: 'unitoftime',
41
49
  value: /^(second|minute|hour|day|week|month|quarter|year)s?$/i,
42
50
  ignoreCase: true,
43
51
  },
44
52
  {
45
- type: 'DAYOFWEEK',
53
+ type: 'dayofweek',
46
54
  value: /^(monday|tuesday|wednesday|thursday|friday|saturday|sunday)$/i,
47
55
  ignoreCase: true,
48
56
  },
49
- {type: 'DATE', value: /^\d\d\d\d-\d\d-\d\d$/},
50
- {type: 'DATE', value: /^\d\d\d\d-\d\d$/},
51
- {type: 'TIME', value: /^\d\d:\d\d:\d\d\.\d+$/},
52
- {type: 'TIME', value: /^\d\d:\d\d:\d\d$/},
53
- {type: 'TIME', value: /^\d\d:\d\d$/},
54
- {type: 'NOTNULL', value: '-null', ignoreCase: true},
55
- {type: 'NULL', value: 'null', ignoreCase: true},
56
- {type: 'PREFIX', value: /^(before|after)/i, ignoreCase: true},
57
- {type: 'TODAY', value: 'today', ignoreCase: true},
58
- {type: 'YESTERDAY', value: 'yesterday', ignoreCase: true},
59
- {type: 'TOMORROW', value: 'tomorrow', ignoreCase: true},
60
- {type: 'NOW', value: 'now', ignoreCase: true},
61
- {type: 'THIS', value: 'this', ignoreCase: true},
62
- {type: 'LAST', value: 'last', ignoreCase: true},
63
- {type: 'NEXT', value: 'next', ignoreCase: true},
64
- {type: 'AGO', value: 'ago', ignoreCase: true},
65
- {type: 'FROM', value: 'from', ignoreCase: true},
66
- {type: 'FOR', value: 'for', ignoreCase: true},
67
- {type: 'TO', value: 'to', ignoreCase: true},
68
- {type: 'YEAR', value: /^\d\d\d\d$/}, // Years are ambiguous, and require special handling.
69
- {type: 'NUMBER', value: /^[\d.]+/, ignoreCase: true},
57
+ {type: 'date', value: /^\d{4}-\d{2}-\d{2}T\d\d$/},
58
+ {type: 'date', value: /^\d{4}-\d{2}-\d{2}$/},
59
+ {type: 'date', value: /^\d{4}-\d{2}$/},
60
+ {type: 'date', value: /^\d{4}-[Qq][1234]$/},
61
+ {
62
+ type: 'date',
63
+ value: /^\d{4}-\d{2}-\d{2}T\d\d:\d\d:\d\d\[[a-zA-Z_/]*\]$/,
64
+ },
65
+ {type: 'date', value: /^\d{4}-\d{2}-\d{2}T\d\d:\d\d:\d\d[.,]\d+$/},
66
+ {type: 'date', value: /^\d{4}-\d{2}-\d{2}T\d\d:\d\d:\d\d$/},
67
+ {type: 'date', value: /^\d{4}-\d{2}-\d{2}T\d\d:\d\d$/},
68
+ {type: 'time', value: /^\d\d:\d\d:\d\d\[[a-zA-Z_/]*\]$/},
69
+ {type: 'time', value: /^\d\d:\d\d:\d\d[.,]\d+$/},
70
+ {type: 'time', value: /^\d\d:\d\d:\d\d$/},
71
+ {type: 'time', value: /^\d\d:\d\d$/},
72
+ {type: 'not_null', value: '-null', ignoreCase: true},
73
+ {type: 'null', value: 'null', ignoreCase: true},
74
+ {type: 'prefix', value: /^(before|after)/i, ignoreCase: true},
75
+ {type: 'today', value: 'today', ignoreCase: true},
76
+ {type: 'yesterday', value: 'yesterday', ignoreCase: true},
77
+ {type: 'tomorrow', value: 'tomorrow', ignoreCase: true},
78
+ {type: 'now', value: 'now', ignoreCase: true},
79
+ {type: 'this', value: 'this', ignoreCase: true},
80
+ {type: 'last', value: 'last', ignoreCase: true},
81
+ {type: 'next', value: 'next', ignoreCase: true},
82
+ {type: 'ago', value: 'ago', ignoreCase: true},
83
+ {type: 'from', value: 'from', ignoreCase: true},
84
+ {type: 'for', value: 'for', ignoreCase: true},
85
+ {type: 'to', value: 'to', ignoreCase: true},
86
+ {type: 'year', value: /^\d\d\d\d$/}, // Years are ambiguous, and require special handling.
87
+ {type: 'number', value: /^[\d.]+/, ignoreCase: true},
70
88
  ];
71
89
  const params: TokenizerParams = {
72
90
  trimWordWhitespace: true,
@@ -77,37 +95,62 @@ export class DateParser extends BaseParser {
77
95
 
78
96
  const tokenizer = new Tokenizer(this.inputString, params);
79
97
  this.tokens = tokenizer.parse();
80
- // console.log('Tokens before moments ', ...this.tokens);
98
+ this.tokens = this.mergeDateTimeTokens();
81
99
  this.tokens = this.mergeMomentTokens();
82
100
  }
83
101
 
102
+ private mergeDateTimeTokens(): Token[] {
103
+ const output: Token[] = [];
104
+ this.index = 0;
105
+ let previous: Token | undefined = undefined;
106
+ while (this.index < this.tokens.length) {
107
+ const token = this.tokens[this.index];
108
+ if (
109
+ previous &&
110
+ previous.type === 'date' &&
111
+ previous.value.length >= 10 &&
112
+ (token.type === 'time' ||
113
+ (token.type === 'number' && token.value.length === 2))
114
+ ) {
115
+ previous.value = previous.value + ' ' + token.value;
116
+ previous.endIndex = token.endIndex;
117
+ previous = undefined;
118
+ } else {
119
+ previous = token;
120
+ output.push(token);
121
+ }
122
+ this.index++;
123
+ }
124
+ return output;
125
+ }
126
+
84
127
  private mergeMomentTokens(): Token[] {
85
128
  const output: Token[] = [];
86
129
  this.index = 0;
87
130
  while (this.index < this.tokens.length) {
131
+ // Do not reorder.
88
132
  if (
89
- this.matchAndMerge('LAST|UNITOFTIME', output) ||
90
- this.matchAndMerge('LAST|DAYOFWEEK', output) ||
91
- this.matchAndMerge('LAST|NUMBER|UNITOFTIME', output) ||
92
- this.matchAndMerge('LAST|YEAR|UNITOFTIME', output) ||
93
- this.matchAndMerge('THIS|UNITOFTIME', output) ||
94
- this.matchAndMerge('NEXT|UNITOFTIME', output) ||
95
- this.matchAndMerge('NEXT|DAYOFWEEK', output) ||
96
- this.matchAndMerge('NEXT|NUMBER|UNITOFTIME', output) ||
97
- this.matchAndMerge('NEXT|YEAR|UNITOFTIME', output) ||
98
- this.matchAndMerge('NUMBER|UNITOFTIME|AGO', output) ||
99
- this.matchAndMerge('YEAR|UNITOFTIME|AGO', output) ||
100
- this.matchAndMerge('NUMBER|UNITOFTIME|FROM|NOW', output) ||
101
- this.matchAndMerge('YEAR|UNITOFTIME|FROM|NOW', output) ||
102
- this.matchAndMerge('NUMBER|UNITOFTIME', output) ||
103
- this.matchAndMerge('YEAR|UNITOFTIME', output) ||
104
- this.matchAndMerge('DATE|TIME', output) ||
105
- this.matchAndMerge('TODAY', output) ||
106
- this.matchAndMerge('YESTERDAY', output) ||
107
- this.matchAndMerge('TOMORROW', output) ||
108
- this.matchAndMerge('DATE', output) ||
109
- this.matchAndMerge('YEAR', output) ||
110
- this.matchAndMerge('NOW', output)
133
+ this.matchAndMerge('last|unitoftime', output) ||
134
+ this.matchAndMerge('last|dayofweek', output) ||
135
+ this.matchAndMerge('last|number|unitoftime', output) ||
136
+ this.matchAndMerge('last|year|unitoftime', output) ||
137
+ this.matchAndMerge('this|unitoftime', output) ||
138
+ this.matchAndMerge('next|unitoftime', output) ||
139
+ this.matchAndMerge('next|dayofweek', output) ||
140
+ this.matchAndMerge('next|number|unitoftime', output) ||
141
+ this.matchAndMerge('next|year|unitoftime', output) ||
142
+ this.matchAndMerge('number|unitoftime|ago', output) ||
143
+ this.matchAndMerge('year|unitoftime|ago', output) ||
144
+ this.matchAndMerge('number|unitoftime|from|now', output) ||
145
+ this.matchAndMerge('year|unitoftime|from|now', output) ||
146
+ this.matchAndMerge('number|unitoftime', output) ||
147
+ this.matchAndMerge('year|unitoftime', output) ||
148
+ this.matchAndMerge('today', output) ||
149
+ this.matchAndMerge('yesterday', output) ||
150
+ this.matchAndMerge('tomorrow', output) ||
151
+ this.matchAndMerge('date', output) ||
152
+ this.matchAndMerge('year', output) ||
153
+ this.matchAndMerge('now', output)
111
154
  ) {
112
155
  continue;
113
156
  } else {
@@ -139,25 +182,34 @@ export class DateParser extends BaseParser {
139
182
  this.tokenize();
140
183
  let prefix: DatePrefix | undefined = undefined;
141
184
  const clauses: DateClause[] = [];
142
- const errors: FilterError[] = [];
185
+ const logs: FilterLog[] = [];
143
186
  this.index = 0;
144
187
  while (this.index < this.tokens.length) {
145
188
  const token = this.getNext();
146
189
  if (token.type === ',') {
147
190
  if (prefix) {
148
- errors.push({
191
+ logs.push({
192
+ severity: 'error',
149
193
  message: 'Invalid ' + prefix,
150
194
  startIndex: token.startIndex,
151
195
  endIndex: token.endIndex,
152
196
  });
197
+ } else if (this.index > 0 && this.tokens[this.index - 1].type === ',') {
198
+ logs.push({
199
+ severity: 'warn',
200
+ message: 'Empty clause',
201
+ startIndex: token.startIndex,
202
+ endIndex: token.endIndex,
203
+ });
153
204
  }
154
205
  this.index++;
155
- } else if (token.type === 'PREFIX') {
206
+ } else if (token.type === 'prefix') {
156
207
  prefix = token.value as DatePrefix;
157
208
  this.index++;
158
209
  } else if (this.handleRange(clauses)) {
159
210
  if (prefix) {
160
- errors.push({
211
+ logs.push({
212
+ severity: 'error',
161
213
  message: 'Invalid ' + prefix,
162
214
  startIndex: token.startIndex,
163
215
  endIndex: token.endIndex,
@@ -166,8 +218,13 @@ export class DateParser extends BaseParser {
166
218
  }
167
219
  } else if (this.handleMerged(prefix, clauses)) {
168
220
  prefix = undefined;
221
+ } else if (token.type === 'null' || token.type === 'not_null') {
222
+ prefix = undefined;
223
+ clauses.push({operator: token.type});
224
+ this.index++;
169
225
  } else {
170
- errors.push({
226
+ logs.push({
227
+ severity: 'error',
171
228
  message:
172
229
  'Invalid token ' + token.value ||
173
230
  (token.values ? token.values.join(' ') : ''),
@@ -178,7 +235,7 @@ export class DateParser extends BaseParser {
178
235
  this.index++;
179
236
  }
180
237
  }
181
- return {clauses, errors};
238
+ return {clauses, logs};
182
239
  }
183
240
 
184
241
  private static createMomentClause(
@@ -186,11 +243,11 @@ export class DateParser extends BaseParser {
186
243
  moment: DateMoment
187
244
  ): DateClause {
188
245
  if (!prefix) {
189
- return {operator: 'ON', moment}; // DateOnClause
190
- } else if (prefix === 'BEFORE') {
191
- return {operator: 'BEFORE', moment}; // DateBeforeClause
246
+ return {operator: 'on', moment}; // DateOnClause
247
+ } else if (prefix === 'before') {
248
+ return {operator: 'before', moment}; // DateBeforeClause
192
249
  } else {
193
- return {operator: 'AFTER', moment}; // DateAfterClause
250
+ return {operator: 'after', moment}; // DateAfterClause
194
251
  }
195
252
  }
196
253
 
@@ -204,7 +261,7 @@ export class DateParser extends BaseParser {
204
261
  const unit: DateTimeUnit | DateWeekday = tokens[1].value as
205
262
  | DateTimeUnit
206
263
  | DateWeekday;
207
- const moment: IntervalMoment = {type: 'INTERVAL', kind, unit};
264
+ const moment: IntervalMoment = {type: 'interval', kind, unit};
208
265
  return DateParser.createMomentClause(prefix, moment);
209
266
  }
210
267
 
@@ -217,12 +274,12 @@ export class DateParser extends BaseParser {
217
274
  const amount = Number(tokens[0].value);
218
275
  const unit: DateTimeUnit = tokens[1].value as DateTimeUnit;
219
276
  const direction: DateMomentOffsetFromNowDirection =
220
- tokens[2].type === 'AGO' ? 'AGO' : 'FROMNOW';
277
+ tokens[2].type === 'ago' ? 'ago' : 'from_now';
221
278
  if (!DateParser.isValidNumber(amount)) {
222
279
  return undefined;
223
280
  }
224
281
  const moment: OffsetMoment = {
225
- type: 'OFFSET_FROM_NOW',
282
+ type: 'offset_from_now',
226
283
  direction,
227
284
  amount,
228
285
  unit,
@@ -238,11 +295,11 @@ export class DateParser extends BaseParser {
238
295
  const amount = Number(tokens[0].value);
239
296
  const unit: DateTimeUnit = tokens[1].value as DateTimeUnit;
240
297
  const direction: DateMomentSpanFromNowDirection =
241
- tokens[2].type === 'LAST' ? 'LAST' : 'NEXT';
298
+ tokens[2].type === 'last' ? 'last' : 'next';
242
299
  if (!DateParser.isValidNumber(amount)) {
243
300
  return undefined;
244
301
  }
245
- const moment: SpanMoment = {type: 'SPAN_FROM_NOW', direction, amount, unit};
302
+ const moment: SpanMoment = {type: 'span_from_now', direction, amount, unit};
246
303
  return DateParser.createMomentClause(prefix, moment);
247
304
  }
248
305
 
@@ -254,7 +311,7 @@ export class DateParser extends BaseParser {
254
311
  if (prefix) {
255
312
  return undefined; // before 7 hours is ambiguous, not allowed.
256
313
  }
257
- const operator = 'DURATION';
314
+ const operator = 'duration';
258
315
  const amount = Number(tokens[0].value);
259
316
  const unit: DateTimeUnit = tokens[1].value as DateTimeUnit;
260
317
  if (!DateParser.isValidNumber(amount)) {
@@ -269,19 +326,27 @@ export class DateParser extends BaseParser {
269
326
  prefix: DatePrefix | undefined,
270
327
  tokens: Token[]
271
328
  ): DateClause {
272
- let unit: DateTimeUnit = 'YEAR';
273
- let date = tokens[0].value;
274
- if (tokens.length === 2) {
275
- const timeStr = tokens[1].value;
276
- date += ' ' + timeStr;
277
- if (timeStr.length > 5) unit = 'SECOND';
278
- else unit = 'MINUTE';
279
- } else if (date.length > 7) {
280
- unit = 'DAY';
281
- } else if (date.length > 4) {
282
- unit = 'MONTH';
329
+ const dateStr = tokens[0].value;
330
+ const matcher: RegExpExecArray | null = /^\d{4}-\d{2}-\d{2}[Tt ](.+)$/.exec(
331
+ dateStr
332
+ );
333
+ const timeStr = matcher ? matcher[1] : '';
334
+
335
+ let unit: DateTimeUnit = 'year';
336
+ if (timeStr.length > 8) {
337
+ unit = 'instant';
338
+ } else if (timeStr.length > 5) {
339
+ unit = 'second';
340
+ } else if (timeStr.length > 2) {
341
+ unit = 'minute';
342
+ } else if (timeStr.length === 2) {
343
+ unit = 'hour';
344
+ } else if (dateStr.length > 7) {
345
+ unit = 'day';
346
+ } else if (dateStr.length > 4) {
347
+ unit = /[qQ]/.test(dateStr) ? 'quarter' : 'month';
283
348
  }
284
- const moment: AbsoluteMoment = {type: 'ABSOLUTE', date, unit};
349
+ const moment: AbsoluteMoment = {type: 'absolute', date: dateStr, unit};
285
350
  return DateParser.createMomentClause(prefix, moment);
286
351
  }
287
352
 
@@ -290,19 +355,19 @@ export class DateParser extends BaseParser {
290
355
  prefix: DatePrefix | undefined,
291
356
  tokens: Token[]
292
357
  ): DateClause {
293
- let momentName: DateMomentName = 'NOW';
358
+ let momentName: DateMomentName = 'now';
294
359
  switch (tokens[0].type) {
295
- case 'TODAY':
296
- momentName = 'TODAY';
360
+ case 'today':
361
+ momentName = 'today';
297
362
  break;
298
- case 'YESTERDAY':
299
- momentName = 'YESTERDAY';
363
+ case 'yesterday':
364
+ momentName = 'yesterday';
300
365
  break;
301
- case 'TOMORROW':
302
- momentName = 'TOMORROW';
366
+ case 'tomorrow':
367
+ momentName = 'tomorrow';
303
368
  break;
304
369
  }
305
- const moment: NamedMoment = {type: 'NAMED', name: momentName};
370
+ const moment: NamedMoment = {type: 'named', name: momentName};
306
371
  return DateParser.createMomentClause(prefix, moment);
307
372
  }
308
373
 
@@ -315,8 +380,8 @@ export class DateParser extends BaseParser {
315
380
  return undefined;
316
381
  }
317
382
  if (
318
- token.type === 'MERGE:NUMBER|UNITOFTIME' ||
319
- token.type === 'MERGE:YEAR|UNITOFTIME'
383
+ token.type === 'MERGE:number|unitoftime' ||
384
+ token.type === 'MERGE:year|unitoftime'
320
385
  ) {
321
386
  const value = Number(token.values[0].value);
322
387
  if (!DateParser.isValidNumber(value)) {
@@ -334,33 +399,32 @@ export class DateParser extends BaseParser {
334
399
  ): DateClause | undefined {
335
400
  const tokens: Token[] = token.values || [];
336
401
  switch (token.type) {
337
- case 'MERGE:LAST|UNITOFTIME':
338
- case 'MERGE:LAST|DAYOFWEEK':
339
- case 'MERGE:THIS|UNITOFTIME':
340
- case 'MERGE:NEXT|UNITOFTIME':
341
- case 'MERGE:NEXT|DAYOFWEEK':
402
+ case 'MERGE:last|unitoftime':
403
+ case 'MERGE:last|dayofweek':
404
+ case 'MERGE:this|unitoftime':
405
+ case 'MERGE:next|unitoftime':
406
+ case 'MERGE:next|dayofweek':
342
407
  return this.createIntervalMoment(prefix, tokens);
343
- case 'MERGE:LAST|NUMBER|UNITOFTIME':
344
- case 'MERGE:LAST|YEAR|UNITOFTIME':
345
- case 'MERGE:NEXT|NUMBER|UNITOFTIME':
346
- case 'MERGE:NEXT|YEAR|UNITOFTIME':
408
+ case 'MERGE:last|number|unitoftime':
409
+ case 'MERGE:last|year|unitoftime':
410
+ case 'MERGE:next|number|unitoftime':
411
+ case 'MERGE:next|year|unitoftime':
347
412
  return this.createSpanMoment(prefix, tokens);
348
- case 'MERGE:NUMBER|UNITOFTIME|AGO':
349
- case 'MERGE:YEAR|UNITOFTIME|AGO':
350
- case 'MERGE:NUMBER|UNITOFTIME|FROM|NOW':
351
- case 'MERGE:YEAR|UNITOFTIME|FROM|NOW':
413
+ case 'MERGE:number|unitoftime|ago':
414
+ case 'MERGE:year|unitoftime|ago':
415
+ case 'MERGE:number|unitoftime|from|now':
416
+ case 'MERGE:year|unitoftime|from|now':
352
417
  return this.createOffsetMoment(prefix, tokens);
353
- case 'MERGE:NUMBER|UNITOFTIME':
354
- case 'MERGE:YEAR|UNITOFTIME':
418
+ case 'MERGE:number|unitoftime':
419
+ case 'MERGE:year|unitoftime':
355
420
  return this.createDateDuration(prefix, tokens);
356
- case 'MERGE:DATE|TIME':
357
- case 'MERGE:DATE':
358
- case 'MERGE:YEAR':
421
+ case 'MERGE:date':
422
+ case 'MERGE:year':
359
423
  return this.createAbsoluteMoment(prefix, tokens);
360
- case 'MERGE:NOW':
361
- case 'MERGE:TODAY':
362
- case 'MERGE:YESTERDAY':
363
- case 'MERGE:TOMORROW':
424
+ case 'MERGE:now':
425
+ case 'MERGE:today':
426
+ case 'MERGE:yesterday':
427
+ case 'MERGE:tomorrow':
364
428
  return this.createNamedMoment(prefix, tokens);
365
429
  default:
366
430
  return undefined;
@@ -383,8 +447,8 @@ export class DateParser extends BaseParser {
383
447
  private handleRange(clauses: DateClause[]): boolean {
384
448
  if (
385
449
  this.isMatchingToken(this.index, 'MERGE', false) &&
386
- (this.isMatchingToken(this.index + 1, 'TO', true) ||
387
- this.isMatchingToken(this.index + 1, 'FOR', true)) &&
450
+ (this.isMatchingToken(this.index + 1, 'to', true) ||
451
+ this.isMatchingToken(this.index + 1, 'for', true)) &&
388
452
  this.isMatchingToken(this.index + 2, 'MERGE', false)
389
453
  ) {
390
454
  const startToken = this.tokens[this.index];
@@ -398,7 +462,7 @@ export class DateParser extends BaseParser {
398
462
  if (startClause === undefined || !('moment' in startClause)) {
399
463
  return false;
400
464
  }
401
- if (operator === 'TO') {
465
+ if (operator === 'to') {
402
466
  const endClause = DateParser.createClauseFromMerged(
403
467
  undefined,
404
468
  endToken
@@ -407,7 +471,7 @@ export class DateParser extends BaseParser {
407
471
  return false;
408
472
  }
409
473
  const clause: DateBetweenClause = {
410
- operator: 'TO_RANGE',
474
+ operator: 'to_range',
411
475
  from: startClause.moment,
412
476
  to: endClause.moment,
413
477
  };
@@ -418,7 +482,7 @@ export class DateParser extends BaseParser {
418
482
  return false;
419
483
  }
420
484
  const clause: DateForClause = {
421
- operator: 'FOR_RANGE',
485
+ operator: 'for_range',
422
486
  from: startClause.moment,
423
487
  duration: endDuration,
424
488
  };
@@ -442,8 +506,6 @@ export class DateParser extends BaseParser {
442
506
  }
443
507
  clauses.push(clause);
444
508
  return true;
445
- } else if (token.type === 'NULL' || token.type === 'NOTNULL') {
446
- clauses.push({operator: token.type});
447
509
  }
448
510
  return false;
449
511
  }
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
1
8
  import {
2
9
  DateMoment,
3
10
  DateBetweenClause,
@@ -16,16 +23,16 @@ export class DateSerializer {
16
23
  }
17
24
 
18
25
  private static dateMomentToString(moment: DateMoment): string {
19
- if (moment.type === 'ABSOLUTE') {
26
+ if (moment.type === 'absolute') {
20
27
  return moment.date;
21
- } else if (moment.type === 'INTERVAL') {
28
+ } else if (moment.type === 'interval') {
22
29
  return moment.kind + ' ' + moment.unit;
23
- } else if (moment.type === 'NAMED') {
30
+ } else if (moment.type === 'named') {
24
31
  return moment.name;
25
- } else if (moment.type === 'OFFSET_FROM_NOW') {
26
- const direction = moment.direction === 'FROMNOW' ? 'FROM NOW' : 'AGO';
32
+ } else if (moment.type === 'offset_from_now') {
33
+ const direction = moment.direction === 'from_now' ? 'from now' : 'ago';
27
34
  return moment.amount + ' ' + moment.unit + ' ' + direction;
28
- } else if (moment.type === 'SPAN_FROM_NOW') {
35
+ } else if (moment.type === 'span_from_now') {
29
36
  return moment.direction + ' ' + moment.amount + ' ' + moment.unit;
30
37
  } else {
31
38
  throw new Error('moment type not recognized ' + JSON.stringify(moment));
@@ -35,7 +42,7 @@ export class DateSerializer {
35
42
  private static goDateBetweenClause(clause: DateBetweenClause): string {
36
43
  return (
37
44
  DateSerializer.dateMomentToString(clause.from) +
38
- ' TO ' +
45
+ ' to ' +
39
46
  DateSerializer.dateMomentToString(clause.to)
40
47
  );
41
48
  }
@@ -43,7 +50,7 @@ export class DateSerializer {
43
50
  private static goDateForClause(clause: DateForClause): string {
44
51
  return (
45
52
  DateSerializer.dateMomentToString(clause.from) +
46
- ' FOR ' +
53
+ ' for ' +
47
54
  clause.duration.amount +
48
55
  ' ' +
49
56
  clause.duration.unit
@@ -54,21 +61,21 @@ export class DateSerializer {
54
61
  if (!('operator' in clause)) {
55
62
  throw new Error('Invalid date clause ' + JSON.stringify(clause));
56
63
  }
57
- if (clause.operator === 'TO_RANGE') {
64
+ if (clause.operator === 'to_range') {
58
65
  return DateSerializer.goDateBetweenClause(clause);
59
- } else if (clause.operator === 'FOR_RANGE') {
66
+ } else if (clause.operator === 'for_range') {
60
67
  return DateSerializer.goDateForClause(clause);
61
- } else if (clause.operator === 'BEFORE') {
62
- return 'BEFORE ' + DateSerializer.dateMomentToString(clause.moment);
63
- } else if (clause.operator === 'AFTER') {
64
- return 'AFTER ' + DateSerializer.dateMomentToString(clause.moment);
65
- } else if (clause.operator === 'ON') {
68
+ } else if (clause.operator === 'before') {
69
+ return 'before ' + DateSerializer.dateMomentToString(clause.moment);
70
+ } else if (clause.operator === 'after') {
71
+ return 'after ' + DateSerializer.dateMomentToString(clause.moment);
72
+ } else if (clause.operator === 'on') {
66
73
  return DateSerializer.dateMomentToString(clause.moment);
67
- } else if (clause.operator === 'NULL') {
68
- return 'NULL';
69
- } else if (clause.operator === 'NOTNULL') {
70
- return '-NULL';
71
- } else if (clause.operator === 'DURATION') {
74
+ } else if (clause.operator === 'null') {
75
+ return 'null';
76
+ } else if (clause.operator === 'not_null') {
77
+ return '-null';
78
+ } else if (clause.operator === 'duration') {
72
79
  return clause.duration.amount + ' ' + clause.duration.unit;
73
80
  } else {
74
81
  throw new Error('Clause type not recognized ' + JSON.stringify(clause));