@malloydata/malloy-filter 0.0.350 → 0.0.351

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 (46) hide show
  1. package/README.md +3 -3
  2. package/dist/clause_utils.d.ts +7 -62
  3. package/dist/clause_utils.js +0 -14
  4. package/dist/clause_utils.js.map +1 -1
  5. package/dist/filter_interface.d.ts +5 -5
  6. package/dist/filter_interface.js +13 -5
  7. package/dist/filter_interface.js.map +1 -1
  8. package/dist/lib/fexpr_number_parser.d.ts +9 -26
  9. package/dist/lib/fexpr_number_parser.js +1476 -83
  10. package/dist/lib/fexpr_number_parser.js.map +1 -1
  11. package/dist/lib/fexpr_string_parser.d.ts +9 -26
  12. package/dist/lib/fexpr_string_parser.js +709 -50
  13. package/dist/lib/fexpr_string_parser.js.map +1 -1
  14. package/dist/lib/ftemporal_parser.d.ts +9 -26
  15. package/dist/lib/ftemporal_parser.js +4095 -163
  16. package/dist/lib/ftemporal_parser.js.map +1 -1
  17. package/dist/number_filter_expression.js +3 -41
  18. package/dist/number_filter_expression.js.map +1 -1
  19. package/dist/{nearley_parse.d.ts → peggy_parse.d.ts} +1 -2
  20. package/dist/peggy_parse.js +40 -0
  21. package/dist/peggy_parse.js.map +1 -0
  22. package/dist/string_filter_expression.js +3 -46
  23. package/dist/string_filter_expression.js.map +1 -1
  24. package/dist/temporal_filter_expression.js +3 -41
  25. package/dist/temporal_filter_expression.js.map +1 -1
  26. package/femto-config.motly +19 -0
  27. package/package.json +9 -11
  28. package/src/clause_utils.ts +9 -23
  29. package/src/filter_interface.ts +14 -8
  30. package/src/grammars/fexpr_number.peggy +46 -0
  31. package/src/grammars/fexpr_string.peggy +32 -0
  32. package/src/grammars/ftemporal.peggy +112 -0
  33. package/src/lib/fexpr_number_parser.js +1411 -0
  34. package/src/lib/fexpr_string_parser.js +740 -0
  35. package/src/lib/ftemporal_parser.js +3641 -0
  36. package/src/number_filter_expression.ts +3 -7
  37. package/src/peggy_parse.ts +50 -0
  38. package/src/string_filter_expression.ts +3 -12
  39. package/src/temporal_filter_expression.ts +3 -7
  40. package/tsconfig.json +3 -2
  41. package/dist/nearley_parse.js +0 -51
  42. package/dist/nearley_parse.js.map +0 -1
  43. package/src/lib/fexpr_number_parser.ts +0 -126
  44. package/src/lib/fexpr_string_parser.ts +0 -84
  45. package/src/lib/ftemporal_parser.ts +0 -246
  46. package/src/nearley_parse.ts +0 -53
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy-filter",
3
- "version": "0.0.350",
3
+ "version": "0.0.351",
4
4
  "license": "MIT",
5
5
  "description": "Parsers for the Malloy filtering sub-languages",
6
6
  "main": "dist/index.js",
@@ -12,25 +12,23 @@
12
12
  "node": ">=20"
13
13
  },
14
14
  "scripts": {
15
- "build": "npm run build-parsers && tsc --build && npm run generate-flow",
16
- "build-parsers": "mkdir -p src/lib && nearleyc src/grammars/fexpr_string.ne --out src/lib/fexpr_string_parser.ts && nearleyc src/grammars/fexpr_number.ne --out src/lib/fexpr_number_parser.ts && nearleyc src/grammars/ftemporal.ne --out src/lib/ftemporal_parser.ts",
17
- "clean": "tsc --build --clean && rm -f tsconfig.tsbuildinfo && rimraf src/lib/* dist @flowtyped",
15
+ "codegen": "node ../../scripts/femto-build.js codegen",
16
+ "generate-flow": "node ../../scripts/femto-build.js flow",
17
+ "dev": "npm run codegen && tsc --build",
18
+ "build": "npm run codegen && tsc --build && npm run generate-flow",
19
+ "clean": "tsc --build --clean && rm -f tsconfig.tsbuildinfo && node ../../scripts/femto-build.js --clean && rimraf src/lib/* dist @flowtyped",
18
20
  "test": "jest",
19
- "prepublishOnly": "npm run clean && npm run build",
20
- "generate-flow": "ts-node ../../scripts/gen-flow.ts"
21
+ "prepublishOnly": "npm run clean && npm run build"
21
22
  },
22
23
  "keywords": [],
23
24
  "author": "",
24
25
  "dependencies": {
25
26
  "jest-diff": "^29.6.2",
26
- "luxon": "^3.5.0",
27
- "moo": "^0.5.2",
28
- "nearley": "^2.20.1"
27
+ "luxon": "^3.5.0"
29
28
  },
30
29
  "devDependencies": {
31
30
  "@types/luxon": "^3.5.0",
32
- "@types/moo": "^0.5.10",
33
- "@types/nearley": "^2.11.5",
31
+ "peggy": "^4.2.0",
34
32
  "rimraf": "^6.0.1"
35
33
  }
36
34
  }
@@ -25,7 +25,7 @@ import {
25
25
  /**
26
26
  * If there is a minus token, add "not:true" to the clause
27
27
  */
28
- export function maybeNot(data: (Object | undefined)[]) {
28
+ export function maybeNot(data: unknown[]) {
29
29
  const [isMinus, op] = data;
30
30
  if (isMinus && op && isStringFilter(op)) {
31
31
  return {...op, not: true};
@@ -150,9 +150,9 @@ function sameAs<T extends FilterExpression>(a: T, b: FilterExpression): b is T {
150
150
  }
151
151
 
152
152
  export function conjoin(
153
- left: Object,
153
+ left: unknown,
154
154
  op: string,
155
- right: Object
155
+ right: unknown
156
156
  ): StringFilter | null {
157
157
  op = op.toLowerCase();
158
158
  if (isStringFilter(left) && isStringFilter(right)) {
@@ -180,9 +180,9 @@ export function conjoin(
180
180
  }
181
181
 
182
182
  export function joinNumbers(
183
- left: Object,
183
+ left: unknown,
184
184
  op: string,
185
- right: Object
185
+ right: unknown
186
186
  ): NumberFilter | null {
187
187
  op = op.toLowerCase();
188
188
  if (isNumberFilter(left) && isNumberFilter(right)) {
@@ -225,7 +225,7 @@ export function mkValues(n: string, nList: string[]) {
225
225
  return {values: [n, ...nList]};
226
226
  }
227
227
 
228
- export function numNot(op: Object, notToken: unknown) {
228
+ export function numNot(op: unknown, notToken: unknown) {
229
229
  if (isNumberFilter(op) && notToken) {
230
230
  if (op.operator === '=') return {operator: '!=', values: op.values};
231
231
  if (op.operator === '!=') return {operator: '=', values: op.values};
@@ -234,7 +234,7 @@ export function numNot(op: Object, notToken: unknown) {
234
234
  return op;
235
235
  }
236
236
 
237
- export function temporalNot(op: Object, notToken: unknown) {
237
+ export function temporalNot(op: unknown, notToken: unknown) {
238
238
  if (isTemporalFilter(op) && notToken) {
239
239
  // maintain not-ness for ease of testing
240
240
  if ('not' in op) {
@@ -252,26 +252,12 @@ export function temporalNot(op: Object, notToken: unknown) {
252
252
  }
253
253
 
254
254
  export function joinTemporal(
255
- left: Object,
255
+ left: unknown,
256
256
  op: string,
257
- right: Object
257
+ right: unknown
258
258
  ): TemporalFilter | null {
259
259
  op = op.toLowerCase();
260
260
  if (isTemporalFilter(left) && isTemporalFilter(right)) {
261
- // if (
262
- // (op === ',' || op === 'or') &&
263
- // left.operator === '=' &&
264
- // sameAs(left, right)
265
- // ) {
266
- // const ret: NumberClause = {
267
- // operator: '=',
268
- // values: [...left.values, ...right.values],
269
- // };
270
- // if (left.not) {
271
- // ret.not = true;
272
- // }
273
- // return ret;
274
- // }
275
261
  if (op === 'and' || op === 'or') {
276
262
  if (left.operator === op) {
277
263
  return {...left, members: [...left.members, right]};
@@ -58,8 +58,10 @@ export type StringFilter =
58
58
  | ClauseChain<StringFilter>
59
59
  | ClauseGroup<StringFilter>;
60
60
 
61
- export function isStringFilter(sc: Object): sc is StringFilter {
61
+ export function isStringFilter(sc: unknown): sc is StringFilter {
62
62
  return (
63
+ typeof sc === 'object' &&
64
+ sc !== null &&
63
65
  'operator' in sc &&
64
66
  typeof sc.operator === 'string' &&
65
67
  [
@@ -86,8 +88,10 @@ export interface BooleanCondition extends Negatable {
86
88
 
87
89
  export type BooleanFilter = BooleanCondition | Null;
88
90
 
89
- export function isBooleanFilter(bc: Object): bc is BooleanFilter {
91
+ export function isBooleanFilter(bc: unknown): bc is BooleanFilter {
90
92
  return (
93
+ typeof bc === 'object' &&
94
+ bc !== null &&
91
95
  'operator' in bc &&
92
96
  typeof bc.operator === 'string' &&
93
97
  ['null', 'true', 'false', '=false', '=true'].includes(bc.operator)
@@ -118,8 +122,10 @@ export type NumberFilter =
118
122
  | ClauseGroup<NumberFilter>
119
123
  | BooleanChain<NumberFilter>;
120
124
 
121
- export function isNumberFilter(sc: Object): sc is NumberFilter {
125
+ export function isNumberFilter(sc: unknown): sc is NumberFilter {
122
126
  return (
127
+ typeof sc === 'object' &&
128
+ sc !== null &&
123
129
  'operator' in sc &&
124
130
  typeof sc.operator === 'string' &&
125
131
  [
@@ -246,8 +252,10 @@ export type TemporalFilter =
246
252
  | BooleanChain<TemporalFilter>
247
253
  | ClauseGroup<TemporalFilter>;
248
254
 
249
- export function isTemporalFilter(sc: Object): sc is TemporalFilter {
255
+ export function isTemporalFilter(sc: unknown): sc is TemporalFilter {
250
256
  return (
257
+ typeof sc === 'object' &&
258
+ sc !== null &&
251
259
  'operator' in sc &&
252
260
  typeof sc.operator === 'string' &&
253
261
  [
@@ -275,10 +283,8 @@ export type FilterExpression =
275
283
  | StringFilter
276
284
  | TemporalFilter;
277
285
 
278
- export function isFilterExpression(
279
- obj: Object | undefined
280
- ): obj is FilterExpression {
281
- return !!obj && 'operator' in obj;
286
+ export function isFilterExpression(obj: unknown): obj is FilterExpression {
287
+ return typeof obj === 'object' && obj !== null && 'operator' in obj;
282
288
  }
283
289
 
284
290
  export type FilterLogSeverity = 'error' | 'warn';
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ {{
7
+ const {numNot, mkRange, joinNumbers, mkValues} = require('../clause_utils');
8
+ }}
9
+
10
+ numberFilter
11
+ = head:numberUnary tail:(_ conjunction _ numberUnary)*
12
+ {
13
+ return tail.reduce((left, [, cop, , right]) => joinNumbers(left, cop, right), head);
14
+ }
15
+
16
+ numberUnary
17
+ = not:NOT _ clause:clause { return numNot(clause, not); }
18
+ / clause:clause { return clause; }
19
+
20
+ clause
21
+ = NULL { return {operator: 'null'}; }
22
+ / "(" _ expr:numberFilter _ ")" { return {operator: "()", expr}; }
23
+ / open:openInterval _ b:N _ TO _ e:N _ close:closeInterval { return mkRange(open, b, e, close); }
24
+ / "!=" _ n:N nList:(_ "," _ N)* { return {operator: '!=', ...mkValues(n, nList.map(x => x[3]))}; }
25
+ / "=" _ n:N nList:(_ "," _ N)* { return {operator: '=', ...mkValues(n, nList.map(x => x[3]))}; }
26
+ / op:$("<=" / ">=" / "<" / ">") _ n:N { return {operator: op, values: [n]}; }
27
+ / n:N nList:(_ "," _ N)* { return {operator: '=', ...mkValues(n, nList.map(x => x[3]))}; }
28
+
29
+ openInterval = "[" { return "["; } / "(" { return "("; }
30
+ closeInterval = "]" { return "]"; } / ")" { return ")"; }
31
+
32
+ N = n:$("-"? ([0-9]+ "."? [0-9]* / [0-9]* "." [0-9]+) ([Ee] [+-]? [0-9]+)?) { return n; }
33
+
34
+ conjunction
35
+ = OR { return "or"; }
36
+ / AND { return "and"; }
37
+
38
+ NOT = "not"i !idChar { return "not"; }
39
+ NULL = "null"i !idChar
40
+ AND = "and"i !idChar
41
+ OR = "or"i !idChar
42
+ TO = "to"i !idChar
43
+
44
+ idChar = [a-zA-Z]
45
+
46
+ _ "whitespace" = [ \t]*
@@ -0,0 +1,32 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ {{
7
+ const {conjoin, maybeNot, matchOp} = require('../clause_utils');
8
+ }}
9
+
10
+ stringFilter
11
+ = _ head:sfUnary tail:(_ conjunction _ sfUnary)*
12
+ {
13
+ return tail.reduce((left, [, cop, , right]) => conjoin(left, cop, right), head);
14
+ }
15
+
16
+ sfUnary
17
+ = "-" _ clause:clause { return maybeNot([true, clause]); }
18
+ / clause:clause { return clause; }
19
+
20
+ clause
21
+ = "(" _ expr:stringFilter _ ")" { return {operator: "()", expr}; }
22
+ / str:matchStr { return matchOp(str); }
23
+
24
+ matchStr "match string"
25
+ = s:$(("\\" . / [^\n,;()|])+) { return s; }
26
+
27
+ conjunction
28
+ = "," { return ","; }
29
+ / ";" { return ";"; }
30
+ / "|" { return "|"; }
31
+
32
+ _ "whitespace" = [ \t]*
@@ -0,0 +1,112 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ {{
7
+ const {temporalNot, joinTemporal, timeLiteral, mkUnits} = require('../clause_utils');
8
+ }}
9
+
10
+ temporalFilter
11
+ = head:temporalUnary tail:(_ conjunction _ temporalUnary)*
12
+ {
13
+ return tail.reduce((left, [, cop, , right]) => joinTemporal(left, cop, right), head);
14
+ }
15
+
16
+ temporalUnary
17
+ = NOT __ clause:clause { return temporalNot(clause, true); }
18
+ / clause:clause { return clause; }
19
+
20
+ clause
21
+ = NULL { return {operator: 'null'}; }
22
+ / "(" _ expr:temporalFilter _ ")" { return {operator: "()", expr}; }
23
+ / BEFORE __ m:moment { return {operator: 'before', before: m}; }
24
+ / STARTING __ m:moment { return {operator: 'before', before: m, not: true}; }
25
+ / AFTER __ m:moment { return {operator: 'after', after: m}; }
26
+ / THROUGH __ m:moment { return {operator: 'after', after: m, not: true}; }
27
+ / ln:lastOrNext __ d:duration { return {operator: ln, ...d}; }
28
+ / m:moment __ TO __ m2:moment { return {operator: 'to', fromMoment: m, toMoment: m2}; }
29
+ / m:moment __ FOR __ d:duration { return {...d, operator: 'for', begin: m}; }
30
+ / m:moment { return {operator: 'in', in: m}; }
31
+ / d:duration { return {operator: 'in_last', ...d}; }
32
+
33
+ lastOrNext
34
+ = LAST { return 'last'; }
35
+ / NEXT { return 'next'; }
36
+
37
+ duration = n:integer __ u:unit { return {units: u, n}; }
38
+
39
+ integer = n:$[0-9]+ { return n; }
40
+
41
+ unit
42
+ = u:$("second"i / "minute"i / "hour"i / "day"i / "week"i / "month"i / "quarter"i / "year"i) "s"i? !idChar
43
+ { return mkUnits(u); }
44
+
45
+ moment
46
+ = NOW { return {moment: 'now'}; }
47
+ / TODAY { return {moment: 'today'}; }
48
+ / YESTERDAY { return {moment: 'yesterday'}; }
49
+ / TOMORROW { return {moment: 'tomorrow'}; }
50
+ / d:duration __ AGO { return {moment: 'ago', ...d}; }
51
+ / d:duration __ FROM __ NOW { return {moment: 'from_now', ...d}; }
52
+ / NEXT __ dn:weekday { return {moment: dn.toLowerCase(), which: 'next'}; }
53
+ / LAST __ dn:weekday { return {moment: dn.toLowerCase(), which: 'last'}; }
54
+ / lnt:lastNextThis __ u:unit { return {moment: lnt, units: u}; }
55
+ / dn:weekday { return {moment: dn.toLowerCase(), which: 'last'}; }
56
+ / tl:timeLiteral { return tl; }
57
+
58
+ lastNextThis
59
+ = THIS { return 'this'; }
60
+ / NEXT { return 'next'; }
61
+ / LAST { return 'last'; }
62
+
63
+ timeLiteral
64
+ = l:$([0-9][0-9][0-9][0-9] "-" [0-9][0-9] "-" [0-9][0-9] [ Tt] [0-9][0-9] ":" [0-9][0-9] ":" [0-9][0-9] ([.,] [0-9]*)?) { return timeLiteral(l); }
65
+ / l:$([0-9][0-9][0-9][0-9] "-" [0-9][0-9] "-" [0-9][0-9] "-" [Ww] [Kk]) { return timeLiteral(l, 'week'); }
66
+ / l:$([0-9][0-9][0-9][0-9] "-" [Qq] [1234]) { return timeLiteral(l, 'quarter'); }
67
+ / l:$([0-9][0-9][0-9][0-9] "-" [0-9][0-9] "-" [0-9][0-9] [ Tt] [0-9][0-9] ":" [0-9][0-9]) { return timeLiteral(l, 'minute'); }
68
+ / l:$([0-9][0-9][0-9][0-9] "-" [0-9][0-9] "-" [0-9][0-9] [ Tt] [0-9][0-9]) { return timeLiteral(l, 'hour'); }
69
+ / l:$([0-9][0-9][0-9][0-9] "-" [0-9][0-9] "-" [0-9][0-9]) { return timeLiteral(l, 'day'); }
70
+ / l:$([0-9][0-9][0-9][0-9] "-" [0-9][0-9]) { return timeLiteral(l, 'month'); }
71
+ / l:$([0-9][0-9][0-9][0-9]) { return timeLiteral(l, 'year'); }
72
+
73
+ weekday
74
+ = w:$(MONDAY / TUESDAY / WEDNESDAY / THURSDAY / FRIDAY / SATURDAY / SUNDAY) { return w; }
75
+
76
+ conjunction
77
+ = OR { return "or"; }
78
+ / AND { return "and"; }
79
+
80
+ // Keywords - case insensitive, must not be followed by more id chars
81
+ NOT = "not"i !idChar
82
+ NULL = "null"i !idChar
83
+ TO = "to"i !idChar
84
+ NOW = "now"i !idChar
85
+ LAST = "last"i !idChar
86
+ THIS = "this"i !idChar
87
+ NEXT = "next"i !idChar
88
+ AGO = "ago"i !idChar
89
+ FROM = "from"i !idChar
90
+ BEFORE = "before"i !idChar
91
+ AFTER = "after"i !idChar
92
+ THROUGH = "through"i !idChar
93
+ STARTING = "starting"i !idChar
94
+ FOR = "for"i !idChar
95
+ TODAY = "today"i !idChar
96
+ YESTERDAY = "yesterday"i !idChar
97
+ TOMORROW = "tomorrow"i !idChar
98
+ AND = "and"i !idChar
99
+ OR = "or"i !idChar
100
+
101
+ MONDAY = "monday"i !idChar
102
+ TUESDAY = "tuesday"i !idChar
103
+ WEDNESDAY = "wednesday"i !idChar
104
+ THURSDAY = "thursday"i !idChar
105
+ FRIDAY = "friday"i !idChar
106
+ SATURDAY = "saturday"i !idChar
107
+ SUNDAY = "sunday"i !idChar
108
+
109
+ idChar = [a-zA-Z]
110
+
111
+ _ "optional whitespace" = [ \t]*
112
+ __ "whitespace" = [ \t]+