@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.
- package/README.md +3 -3
- package/dist/clause_utils.d.ts +7 -62
- package/dist/clause_utils.js +0 -14
- package/dist/clause_utils.js.map +1 -1
- package/dist/filter_interface.d.ts +5 -5
- package/dist/filter_interface.js +13 -5
- package/dist/filter_interface.js.map +1 -1
- package/dist/lib/fexpr_number_parser.d.ts +9 -26
- package/dist/lib/fexpr_number_parser.js +1476 -83
- package/dist/lib/fexpr_number_parser.js.map +1 -1
- package/dist/lib/fexpr_string_parser.d.ts +9 -26
- package/dist/lib/fexpr_string_parser.js +709 -50
- package/dist/lib/fexpr_string_parser.js.map +1 -1
- package/dist/lib/ftemporal_parser.d.ts +9 -26
- package/dist/lib/ftemporal_parser.js +4095 -163
- package/dist/lib/ftemporal_parser.js.map +1 -1
- package/dist/number_filter_expression.js +3 -41
- package/dist/number_filter_expression.js.map +1 -1
- package/dist/{nearley_parse.d.ts → peggy_parse.d.ts} +1 -2
- package/dist/peggy_parse.js +40 -0
- package/dist/peggy_parse.js.map +1 -0
- package/dist/string_filter_expression.js +3 -46
- package/dist/string_filter_expression.js.map +1 -1
- package/dist/temporal_filter_expression.js +3 -41
- package/dist/temporal_filter_expression.js.map +1 -1
- package/femto-config.motly +19 -0
- package/package.json +9 -11
- package/src/clause_utils.ts +9 -23
- package/src/filter_interface.ts +14 -8
- package/src/grammars/fexpr_number.peggy +46 -0
- package/src/grammars/fexpr_string.peggy +32 -0
- package/src/grammars/ftemporal.peggy +112 -0
- package/src/lib/fexpr_number_parser.js +1411 -0
- package/src/lib/fexpr_string_parser.js +740 -0
- package/src/lib/ftemporal_parser.js +3641 -0
- package/src/number_filter_expression.ts +3 -7
- package/src/peggy_parse.ts +50 -0
- package/src/string_filter_expression.ts +3 -12
- package/src/temporal_filter_expression.ts +3 -7
- package/tsconfig.json +3 -2
- package/dist/nearley_parse.js +0 -51
- package/dist/nearley_parse.js.map +0 -1
- package/src/lib/fexpr_number_parser.ts +0 -126
- package/src/lib/fexpr_string_parser.ts +0 -84
- package/src/lib/ftemporal_parser.ts +0 -246
- 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.
|
|
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
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
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
|
-
"
|
|
33
|
-
"@types/nearley": "^2.11.5",
|
|
31
|
+
"peggy": "^4.2.0",
|
|
34
32
|
"rimraf": "^6.0.1"
|
|
35
33
|
}
|
|
36
34
|
}
|
package/src/clause_utils.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
153
|
+
left: unknown,
|
|
154
154
|
op: string,
|
|
155
|
-
right:
|
|
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:
|
|
183
|
+
left: unknown,
|
|
184
184
|
op: string,
|
|
185
|
-
right:
|
|
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:
|
|
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:
|
|
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:
|
|
255
|
+
left: unknown,
|
|
256
256
|
op: string,
|
|
257
|
-
right:
|
|
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]};
|
package/src/filter_interface.ts
CHANGED
|
@@ -58,8 +58,10 @@ export type StringFilter =
|
|
|
58
58
|
| ClauseChain<StringFilter>
|
|
59
59
|
| ClauseGroup<StringFilter>;
|
|
60
60
|
|
|
61
|
-
export function isStringFilter(sc:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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]+
|