@halleyassist/rule-parser 1.0.8 → 1.0.10
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/package.json +1 -1
- package/src/RuleParser.ebnf.js +15 -7
- package/src/RuleParser.js +143 -6
- package/src/RuleParser.production.ebnf.js +1 -1
- package/src/RuleParser.production.js +143 -6
package/package.json
CHANGED
package/src/RuleParser.ebnf.js
CHANGED
|
@@ -18,11 +18,12 @@ DIVIDE ::= "/"
|
|
|
18
18
|
MODULUS ::= "%"
|
|
19
19
|
DEFAULT_VAL ::= "??"
|
|
20
20
|
arithmetic_operator ::= PLUS | MINUS | MULTIPLY | DIVIDE | MODULUS | DEFAULT_VAL
|
|
21
|
-
|
|
21
|
+
arithmetic_operand ::= fcall | number_time | number
|
|
22
|
+
arithmetic_result ::= arithmetic_operand WS* arithmetic_operator WS* ( arithmetic_result | arithmetic_operand )
|
|
22
23
|
|
|
23
24
|
simple_result ::= fcall | value
|
|
24
25
|
result ::= arithmetic_result | simple_result
|
|
25
|
-
value ::= false | true | array | number_time | number | number_tod |
|
|
26
|
+
value ::= false | true | array | time_period | number_time | number | number_tod | string
|
|
26
27
|
BEGIN_ARRAY ::= WS* #x5B WS* /* [ left square bracket */
|
|
27
28
|
BEGIN_OBJECT ::= WS* #x7B WS* /* { left curly bracket */
|
|
28
29
|
END_ARRAY ::= WS* #x5D WS* /* ] right square bracket */
|
|
@@ -45,13 +46,18 @@ arguments ::= argument*
|
|
|
45
46
|
fname ::= [a-zA-z0-9]+
|
|
46
47
|
fcall ::= fname WS* BEGIN_ARGUMENT WS* arguments? END_ARGUMENT
|
|
47
48
|
|
|
48
|
-
between_number ||= number ((WS+ "AND" WS+) | (WS* "-" WS*)) number
|
|
49
|
-
|
|
49
|
+
between_number ||= (number_time | number) ((WS+ "AND" WS+) | (WS* "-" WS*)) (number_time | number)
|
|
50
|
+
between_number_time ||= number_time ((WS+ "AND" WS+) | (WS* "-" WS*)) number_time
|
|
51
|
+
between_tod ||= number_tod ((WS+ "AND" WS+)) number_tod (WS+ dow_range)?
|
|
50
52
|
between ||= "BETWEEN" WS+ (between_number | between_tod)
|
|
53
|
+
dow ||= "MONDAY" | "TUESDAY" | "WEDNESDAY" | "THURSDAY" | "FRIDAY" | "SATURDAY" | "SUNDAY"
|
|
54
|
+
dow_range ||= "ON" WS+ dow (WS+ "TO" WS+ dow)?
|
|
55
|
+
between_time_only ||= "BETWEEN" WS+ between_number_time
|
|
51
56
|
between_tod_only ||= "BETWEEN" WS+ between_tod
|
|
52
57
|
|
|
53
58
|
AND ||= (WS* "&&" WS*) | (WS+ "AND" WS+)
|
|
54
59
|
OR ||= (WS* "||" WS*) | (WS+ "OR" WS+)
|
|
60
|
+
AGO ||= "AGO"
|
|
55
61
|
GT ::= ">"
|
|
56
62
|
LT ::= "<"
|
|
57
63
|
GTE ::= ">="
|
|
@@ -65,13 +71,15 @@ null ||= "null"
|
|
|
65
71
|
true ||= "TRUE"
|
|
66
72
|
array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY
|
|
67
73
|
|
|
68
|
-
unit ||= "seconds" | "
|
|
74
|
+
unit ||= "seconds" | "minutes" | "hours" | "weeks" | "days" | "second" | "minute" | "week" | "hour" | "day" | "mins" | "min"
|
|
69
75
|
number ::= "-"? ([0-9]+) ("." [0-9]+)? ("e" ( "-" | "+" )? ("0" | [1-9] [0-9]*))?
|
|
70
76
|
number_time ::= number WS+ unit
|
|
71
77
|
number_tod ::= ([0-9]+) ":" ([0-9]+)
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
time_period_ago ||= number_time (WS+ number_time)* WS+ AGO
|
|
80
|
+
time_period_ago_between ||= number_time (WS+ number_time)* WS+ AGO WS+ between_tod_only
|
|
81
|
+
time_period_const ||= "today" | time_period_ago
|
|
82
|
+
time_period ::= time_period_ago_between | time_period_const | between_tod_only | between_time_only
|
|
75
83
|
|
|
76
84
|
string ::= '"' (([#x20-#x21] | [#x23-#x5B] | [#x5D-#xFFFF]) | #x5C (#x22 | #x5C | #x2F | #x62 | #x66 | #x6E | #x72 | #x74 | #x75 HEXDIG HEXDIG HEXDIG HEXDIG))* '"'
|
|
77
85
|
HEXDIG ::= [a-fA-F0-9]
|
package/src/RuleParser.js
CHANGED
|
@@ -62,12 +62,92 @@ class RuleParser {
|
|
|
62
62
|
}
|
|
63
63
|
return ret
|
|
64
64
|
}
|
|
65
|
+
static _parseDowRange(dowRange) {
|
|
66
|
+
const dow = []
|
|
67
|
+
|
|
68
|
+
// dow_range can have 1 or 2 children (single day or range)
|
|
69
|
+
if (dowRange.children.length === 1) {
|
|
70
|
+
// Single day: ON MONDAY
|
|
71
|
+
dow.push(dowRange.children[0].text.toLowerCase())
|
|
72
|
+
} else if (dowRange.children.length === 2) {
|
|
73
|
+
// Range: ON MONDAY TO WEDNESDAY
|
|
74
|
+
dow.push(dowRange.children[0].text.toLowerCase())
|
|
75
|
+
dow.push(dowRange.children[1].text.toLowerCase())
|
|
76
|
+
} else {
|
|
77
|
+
throw new Error(`Invalid dow_range with ${dowRange.children.length} children`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return dow
|
|
81
|
+
}
|
|
82
|
+
static _addDowToTods(startTod, endTod, dowRange) {
|
|
83
|
+
if (dowRange && dowRange.type === 'dow_range') {
|
|
84
|
+
const dow = RuleParser._parseDowRange(dowRange)
|
|
85
|
+
startTod.dow = dow
|
|
86
|
+
endTod.dow = dow
|
|
87
|
+
}
|
|
88
|
+
}
|
|
65
89
|
static _parseTimePeriod(tp){
|
|
66
90
|
switch(tp.type){
|
|
67
91
|
case 'time_period_const':
|
|
92
|
+
// Check if this is a time_period_ago (has children)
|
|
93
|
+
if (tp.children && tp.children.length > 0 && tp.children[0].type === 'time_period_ago') {
|
|
94
|
+
const timePeriodAgo = tp.children[0]
|
|
95
|
+
// Extract all number_time children and sum them up
|
|
96
|
+
let totalSeconds = 0
|
|
97
|
+
const components = []
|
|
98
|
+
for (const child of timePeriodAgo.children) {
|
|
99
|
+
if (child.type === 'number_time') {
|
|
100
|
+
const number = parseFloat(child.children[0].text)
|
|
101
|
+
const unit = child.children[1].text.toUpperCase()
|
|
102
|
+
components.push([number, unit])
|
|
103
|
+
// Parse the value to get seconds
|
|
104
|
+
totalSeconds += RuleParser.__parseValue(child)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// If there's only one component, use its number and unit
|
|
108
|
+
// Otherwise, use the total in seconds and "SECONDS" as the unit
|
|
109
|
+
if (components.length === 1) {
|
|
110
|
+
return ["TimePeriodConstAgo", components[0][0], components[0][1]]
|
|
111
|
+
} else {
|
|
112
|
+
return ["TimePeriodConstAgo", totalSeconds, "SECONDS"]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
68
115
|
return ["TimePeriodConst", tp.text]
|
|
69
|
-
case '
|
|
70
|
-
|
|
116
|
+
case 'time_period_ago_between': {
|
|
117
|
+
// time_period_ago_between has children[0] = number_time, children[1] = between_tod_only
|
|
118
|
+
const betweenTodOnly = tp.children[1]
|
|
119
|
+
const betweenTod = betweenTodOnly.children[0]
|
|
120
|
+
const startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
121
|
+
const endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
122
|
+
|
|
123
|
+
// Check if there's a dow_range at betweenTod.children[2]
|
|
124
|
+
if (betweenTod.children.length > 2) {
|
|
125
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return ["TimePeriodBetween", startTod, endTod]
|
|
129
|
+
}
|
|
130
|
+
case 'between_tod_only': {
|
|
131
|
+
// between_tod_only has children[0] = between_tod node
|
|
132
|
+
const betweenTod = tp.children[0]
|
|
133
|
+
const startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
134
|
+
const endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
135
|
+
|
|
136
|
+
// Check if there's a dow_range at betweenTod.children[2]
|
|
137
|
+
if (betweenTod.children.length > 2) {
|
|
138
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return ["TimePeriodBetween", startTod, endTod]
|
|
142
|
+
}
|
|
143
|
+
case 'between_time_only': {
|
|
144
|
+
// between_number_only has children[0] = between_number node
|
|
145
|
+
const betweenNumber = tp.children[0]
|
|
146
|
+
const startValue = RuleParser.__parseValue(betweenNumber.children[0])
|
|
147
|
+
const endValue = RuleParser.__parseValue(betweenNumber.children[1])
|
|
148
|
+
|
|
149
|
+
return ["TimePeriodBetween", startValue, endValue]
|
|
150
|
+
}
|
|
71
151
|
}
|
|
72
152
|
}
|
|
73
153
|
static __parseValue(child){
|
|
@@ -157,15 +237,42 @@ class RuleParser {
|
|
|
157
237
|
}
|
|
158
238
|
return null
|
|
159
239
|
}
|
|
240
|
+
static _parseArithmeticOperand(operand){
|
|
241
|
+
assert(operand.children.length == 1)
|
|
242
|
+
const child = operand.children[0]
|
|
243
|
+
const type = child.type
|
|
244
|
+
switch(type){
|
|
245
|
+
case 'fcall':
|
|
246
|
+
return RuleParser._parseFcall(child)
|
|
247
|
+
case 'number':
|
|
248
|
+
return ['Value', parseFloat(child.text)]
|
|
249
|
+
case 'number_time':
|
|
250
|
+
return ['Value', RuleParser.__parseValue(child)]
|
|
251
|
+
}
|
|
252
|
+
throw new Error(`Unknown arithmetic operand type ${type}`)
|
|
253
|
+
}
|
|
160
254
|
static _parseArithmeticResult(result){
|
|
161
255
|
assert(result.children.length == 3)
|
|
162
|
-
const partA =
|
|
256
|
+
const partA = RuleParser._parseArithmeticOperand(result.children[0])
|
|
163
257
|
const operatorFn = ArithmeticOperators[result.children[1].text]
|
|
164
|
-
const partB =
|
|
258
|
+
const partB = RuleParser.__parseArithmeticResult(result, 2)
|
|
165
259
|
|
|
166
260
|
return [operatorFn, partA, partB]
|
|
167
261
|
}
|
|
168
262
|
|
|
263
|
+
static __parseArithmeticResult(result, idx){
|
|
264
|
+
const child = result.children[idx]
|
|
265
|
+
const type = child.type
|
|
266
|
+
switch(type){
|
|
267
|
+
case 'arithmetic_operand':
|
|
268
|
+
return RuleParser._parseArithmeticOperand(child)
|
|
269
|
+
case 'arithmetic_result':
|
|
270
|
+
return RuleParser._parseArithmeticResult(child)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
throw new Error(`Unknown arithmetic result node ${type}`)
|
|
274
|
+
}
|
|
275
|
+
|
|
169
276
|
static __parseResult(result, idx){
|
|
170
277
|
const child = result.children[idx]
|
|
171
278
|
const type = child.type
|
|
@@ -191,9 +298,39 @@ class RuleParser {
|
|
|
191
298
|
case 2: {
|
|
192
299
|
const rhs = expr.children[1]
|
|
193
300
|
switch(rhs.type){
|
|
194
|
-
case 'between_tod':
|
|
301
|
+
case 'between_tod': {
|
|
302
|
+
// Direct between_tod (without wrapping between node)
|
|
303
|
+
// between_tod has: children[0] = first tod, children[1] = second tod, children[2] = optional dow_range
|
|
304
|
+
const startTod = RuleParser.__parseValue(rhs.children[0])
|
|
305
|
+
const endTod = RuleParser.__parseValue(rhs.children[1])
|
|
306
|
+
|
|
307
|
+
// Check if there's a dow_range (children[2])
|
|
308
|
+
if (rhs.children.length > 2) {
|
|
309
|
+
RuleParser._addDowToTods(startTod, endTod, rhs.children[2])
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
313
|
+
}
|
|
314
|
+
case 'between': {
|
|
315
|
+
// between wraps either between_number or between_tod
|
|
316
|
+
const betweenChild = rhs.children[0]
|
|
317
|
+
if (betweenChild.type === 'between_tod') {
|
|
318
|
+
// between_tod has: children[0] = first tod, children[1] = second tod, children[2] = optional dow_range
|
|
319
|
+
const startTod = RuleParser.__parseValue(betweenChild.children[0])
|
|
320
|
+
const endTod = RuleParser.__parseValue(betweenChild.children[1])
|
|
321
|
+
|
|
322
|
+
// Check if there's a dow_range (children[2])
|
|
323
|
+
if (betweenChild.children.length > 2) {
|
|
324
|
+
RuleParser._addDowToTods(startTod, endTod, betweenChild.children[2])
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
328
|
+
} else {
|
|
329
|
+
// between_number - no dow support
|
|
330
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[1])]]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
195
333
|
case 'between_number':
|
|
196
|
-
case 'between':
|
|
197
334
|
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[1])]]
|
|
198
335
|
case 'basic_rhs':
|
|
199
336
|
return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports=[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement0","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement0*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression1","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression1"]]},{"name":"%%standard_expression23","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%%standard_expression24","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%%%standard_expression256","bnf":[["WS+","IS"]],"fragment":true},{"name":"%%standard_expression25","bnf":[["%%%standard_expression256?","WS+","between"]],"fragment":true},{"name":"%standard_expression2","bnf":[["%%standard_expression23"],["%%standard_expression24"],["%%standard_expression25"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression2?"]]},{"name":"basic_rhs","bnf":[["operator","WS*","result"]]},{"name":"eq_approx","bnf":[["eq_operator","WS*","\"~\"","WS*","result"]]},{"name":"PLUS","bnf":[["\"+\""]]},{"name":"MINUS","bnf":[["\"-\""]]},{"name":"MULTIPLY","bnf":[["\"*\""]]},{"name":"DIVIDE","bnf":[["\"/\""]]},{"name":"MODULUS","bnf":[["\"%\""]]},{"name":"DEFAULT_VAL","bnf":[["\"??\""]]},{"name":"arithmetic_operator","bnf":[["PLUS"],["MINUS"],["MULTIPLY"],["DIVIDE"],["MODULUS"],["DEFAULT_VAL"]]},{"name":"%arithmetic_result7","bnf":[["arithmetic_result"],["
|
|
1
|
+
module.exports=[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement0","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement0*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression1","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression1"]]},{"name":"%%standard_expression23","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%%standard_expression24","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%%%standard_expression256","bnf":[["WS+","IS"]],"fragment":true},{"name":"%%standard_expression25","bnf":[["%%%standard_expression256?","WS+","between"]],"fragment":true},{"name":"%standard_expression2","bnf":[["%%standard_expression23"],["%%standard_expression24"],["%%standard_expression25"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression2?"]]},{"name":"basic_rhs","bnf":[["operator","WS*","result"]]},{"name":"eq_approx","bnf":[["eq_operator","WS*","\"~\"","WS*","result"]]},{"name":"PLUS","bnf":[["\"+\""]]},{"name":"MINUS","bnf":[["\"-\""]]},{"name":"MULTIPLY","bnf":[["\"*\""]]},{"name":"DIVIDE","bnf":[["\"/\""]]},{"name":"MODULUS","bnf":[["\"%\""]]},{"name":"DEFAULT_VAL","bnf":[["\"??\""]]},{"name":"arithmetic_operator","bnf":[["PLUS"],["MINUS"],["MULTIPLY"],["DIVIDE"],["MODULUS"],["DEFAULT_VAL"]]},{"name":"arithmetic_operand","bnf":[["fcall"],["number_time"],["number"]]},{"name":"%arithmetic_result7","bnf":[["arithmetic_result"],["arithmetic_operand"]],"fragment":true},{"name":"arithmetic_result","bnf":[["arithmetic_operand","WS*","arithmetic_operator","WS*","%arithmetic_result7"]]},{"name":"simple_result","bnf":[["fcall"],["value"]]},{"name":"result","bnf":[["arithmetic_result"],["simple_result"]]},{"name":"value","bnf":[["false"],["true"],["array"],["time_period"],["number_time"],["number"],["number_tod"],["string"]]},{"name":"BEGIN_ARRAY","bnf":[["WS*",/\x5B/,"WS*"]]},{"name":"BEGIN_OBJECT","bnf":[["WS*",/\x7B/,"WS*"]]},{"name":"END_ARRAY","bnf":[["WS*",/\x5D/,"WS*"]]},{"name":"END_OBJECT","bnf":[["WS*",/\x7D/,"WS*"]]},{"name":"NAME_SEPARATOR","bnf":[["WS*",/\x3A/,"WS*"]]},{"name":"VALUE_SEPARATOR","bnf":[["WS*",/\x2C/,"WS*"]]},{"name":"%WS8","bnf":[[/[\x20\x09\x0A\x0D]/]]},{"name":"WS","bnf":[["%WS8+"]]},{"name":"operator","bnf":[["GTE"],["LTE"],["GT"],["LT"],["EQ"],["NEQ"]]},{"name":"eq_operator","bnf":[["EQ"],["NEQ"]]},{"name":"BEGIN_ARGUMENT","bnf":[["\"(\""]]},{"name":"END_ARGUMENT","bnf":[["\")\""]]},{"name":"BEGIN_PARENTHESIS","bnf":[["\"(\""]]},{"name":"END_PARENTHESIS","bnf":[["\")\""]]},{"name":"%argument9","bnf":[["\",\"","WS*"]],"fragment":true},{"name":"argument","bnf":[["statement","WS*","%argument9?"]]},{"name":"arguments","bnf":[["argument*"]]},{"name":"%fname10","bnf":[[/[a-zA-z0-9]/]]},{"name":"fname","bnf":[["%fname10+"]]},{"name":"fcall","bnf":[["fname","WS*","BEGIN_ARGUMENT","WS*","arguments?","END_ARGUMENT"]]},{"name":"%between_number11","bnf":[["number_time"],["number"]],"fragment":true},{"name":"%%between_number1213","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%%between_number1214","bnf":[["WS*",/\-/,"WS*"]],"fragment":true},{"name":"%between_number12","bnf":[["%%between_number1213"],["%%between_number1214"]],"fragment":true},{"name":"%between_number15","bnf":[["number_time"],["number"]],"fragment":true},{"name":"between_number","bnf":[["%between_number11","%between_number12","%between_number15"]]},{"name":"%%between_number_time1617","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%%between_number_time1618","bnf":[["WS*",/\-/,"WS*"]],"fragment":true},{"name":"%between_number_time16","bnf":[["%%between_number_time1617"],["%%between_number_time1618"]],"fragment":true},{"name":"between_number_time","bnf":[["number_time","%between_number_time16","number_time"]]},{"name":"%%between_tod1920","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%between_tod19","bnf":[["%%between_tod1920"]],"fragment":true},{"name":"%between_tod21","bnf":[["WS+","dow_range"]],"fragment":true},{"name":"between_tod","bnf":[["number_tod","%between_tod19","number_tod","%between_tod21?"]]},{"name":"%between22","bnf":[["between_number"],["between_tod"]],"fragment":true},{"name":"between","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","%between22"]]},{"name":"dow","bnf":[[/[Mm]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Tt]/,/[Uu]/,/[Ee]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ww]/,/[Ee]/,/[Dd]/,/[Nn]/,/[Ee]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Tt]/,/[Hh]/,/[Uu]/,/[Rr]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ff]/,/[Rr]/,/[Ii]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ss]/,/[Aa]/,/[Tt]/,/[Uu]/,/[Rr]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ss]/,/[Uu]/,/[Nn]/,/[Dd]/,/[Aa]/,/[Yy]/]]},{"name":"%dow_range23","bnf":[["WS+",/[Tt]/,/[Oo]/,"WS+","dow"]],"fragment":true},{"name":"dow_range","bnf":[[/[Oo]/,/[Nn]/,"WS+","dow","%dow_range23?"]]},{"name":"between_time_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_number_time"]]},{"name":"between_tod_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_tod"]]},{"name":"%AND24","bnf":[["WS*",/&/,/&/,"WS*"]],"fragment":true},{"name":"%AND25","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"AND","bnf":[["%AND24"],["%AND25"]]},{"name":"%OR26","bnf":[["WS*",/\|/,/\|/,"WS*"]],"fragment":true},{"name":"%OR27","bnf":[["WS+",/[Oo]/,/[Rr]/,"WS+"]],"fragment":true},{"name":"OR","bnf":[["%OR26"],["%OR27"]]},{"name":"AGO","bnf":[[/[Aa]/,/[Gg]/,/[Oo]/]]},{"name":"GT","bnf":[["\">\""]]},{"name":"LT","bnf":[["\"<\""]]},{"name":"GTE","bnf":[["\">=\""]]},{"name":"LTE","bnf":[["\"<=\""]]},{"name":"IS","bnf":[[/[Ii]/,/[Ss]/]]},{"name":"EQ","bnf":[["\"==\""],["\"=\""]]},{"name":"NEQ","bnf":[["\"!=\""]]},{"name":"%NOT28","bnf":[[/!/,"WS*"]],"fragment":true},{"name":"%NOT29","bnf":[[/[Nn]/,/[Oo]/,/[Tt]/,"WS+"]],"fragment":true},{"name":"NOT","bnf":[["%NOT28"],["%NOT29"]]},{"name":"false","bnf":[[/[Ff]/,/[Aa]/,/[Ll]/,/[Ss]/,/[Ee]/]]},{"name":"null","bnf":[[/[Nn]/,/[Uu]/,/[Ll]/,/[Ll]/]]},{"name":"true","bnf":[[/[Tt]/,/[Rr]/,/[Uu]/,/[Ee]/]]},{"name":"%%array3031","bnf":[["VALUE_SEPARATOR","value"]],"fragment":true},{"name":"%array30","bnf":[["value","%%array3031*"]],"fragment":true},{"name":"array","bnf":[["BEGIN_ARRAY","%array30?","END_ARRAY"]]},{"name":"unit","bnf":[[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/,/[Ss]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/,/[Ss]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/,/[Ss]/],[/[Dd]/,/[Aa]/,/[Yy]/,/[Ss]/],[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/],[/[Dd]/,/[Aa]/,/[Yy]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/]]},{"name":"%%number3233","bnf":[[/[0-9]/]]},{"name":"%number32","bnf":[["%%number3233+"]],"fragment":true},{"name":"%%number3435","bnf":[[/[0-9]/]]},{"name":"%number34","bnf":[["\".\"","%%number3435+"]],"fragment":true},{"name":"%%number3637","bnf":[["\"-\""],["\"+\""]],"fragment":true},{"name":"%%%number363839","bnf":[[/[0-9]/]]},{"name":"%%number3638","bnf":[["\"0\""],[/[1-9]/,"%%%number363839*"]],"fragment":true},{"name":"%number36","bnf":[["\"e\"","%%number3637?","%%number3638"]],"fragment":true},{"name":"number","bnf":[["\"-\"?","%number32","%number34?","%number36?"]]},{"name":"number_time","bnf":[["number","WS+","unit"]]},{"name":"%%number_tod4041","bnf":[[/[0-9]/]]},{"name":"%number_tod40","bnf":[["%%number_tod4041+"]],"fragment":true},{"name":"%%number_tod4243","bnf":[[/[0-9]/]]},{"name":"%number_tod42","bnf":[["%%number_tod4243+"]],"fragment":true},{"name":"number_tod","bnf":[["%number_tod40","\":\"","%number_tod42"]]},{"name":"%time_period_ago44","bnf":[["WS+","number_time"]],"fragment":true},{"name":"time_period_ago","bnf":[["number_time","%time_period_ago44*","WS+","AGO"]]},{"name":"%time_period_ago_between45","bnf":[["WS+","number_time"]],"fragment":true},{"name":"time_period_ago_between","bnf":[["number_time","%time_period_ago_between45*","WS+","AGO","WS+","between_tod_only"]]},{"name":"time_period_const","bnf":[[/[Tt]/,/[Oo]/,/[Dd]/,/[Aa]/,/[Yy]/],["time_period_ago"]]},{"name":"time_period","bnf":[["time_period_ago_between"],["time_period_const"],["between_tod_only"],["between_time_only"]]},{"name":"%%string4647","bnf":[[/[\x20-\x21]/],[/[\x23-\x5B]/],[/[\x5D-\uFFFF]/]],"fragment":true},{"name":"%%string4648","bnf":[[/\x22/],[/\x5C/],[/\x2F/],[/\x62/],[/\x66/],[/\x6E/],[/\x72/],[/\x74/],[/\x75/,"HEXDIG","HEXDIG","HEXDIG","HEXDIG"]],"fragment":true},{"name":"%string46","bnf":[["%%string4647"],[/\x5C/,"%%string4648"]],"fragment":true},{"name":"string","bnf":[["'\"'","%string46*","'\"'"]]},{"name":"HEXDIG","bnf":[[/[a-fA-F0-9]/]]}]
|
|
@@ -62,12 +62,92 @@ class RuleParser {
|
|
|
62
62
|
}
|
|
63
63
|
return ret
|
|
64
64
|
}
|
|
65
|
+
static _parseDowRange(dowRange) {
|
|
66
|
+
const dow = []
|
|
67
|
+
|
|
68
|
+
// dow_range can have 1 or 2 children (single day or range)
|
|
69
|
+
if (dowRange.children.length === 1) {
|
|
70
|
+
// Single day: ON MONDAY
|
|
71
|
+
dow.push(dowRange.children[0].text.toLowerCase())
|
|
72
|
+
} else if (dowRange.children.length === 2) {
|
|
73
|
+
// Range: ON MONDAY TO WEDNESDAY
|
|
74
|
+
dow.push(dowRange.children[0].text.toLowerCase())
|
|
75
|
+
dow.push(dowRange.children[1].text.toLowerCase())
|
|
76
|
+
} else {
|
|
77
|
+
throw new Error(`Invalid dow_range with ${dowRange.children.length} children`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return dow
|
|
81
|
+
}
|
|
82
|
+
static _addDowToTods(startTod, endTod, dowRange) {
|
|
83
|
+
if (dowRange && dowRange.type === 'dow_range') {
|
|
84
|
+
const dow = RuleParser._parseDowRange(dowRange)
|
|
85
|
+
startTod.dow = dow
|
|
86
|
+
endTod.dow = dow
|
|
87
|
+
}
|
|
88
|
+
}
|
|
65
89
|
static _parseTimePeriod(tp){
|
|
66
90
|
switch(tp.type){
|
|
67
91
|
case 'time_period_const':
|
|
92
|
+
// Check if this is a time_period_ago (has children)
|
|
93
|
+
if (tp.children && tp.children.length > 0 && tp.children[0].type === 'time_period_ago') {
|
|
94
|
+
const timePeriodAgo = tp.children[0]
|
|
95
|
+
// Extract all number_time children and sum them up
|
|
96
|
+
let totalSeconds = 0
|
|
97
|
+
const components = []
|
|
98
|
+
for (const child of timePeriodAgo.children) {
|
|
99
|
+
if (child.type === 'number_time') {
|
|
100
|
+
const number = parseFloat(child.children[0].text)
|
|
101
|
+
const unit = child.children[1].text.toUpperCase()
|
|
102
|
+
components.push([number, unit])
|
|
103
|
+
// Parse the value to get seconds
|
|
104
|
+
totalSeconds += RuleParser.__parseValue(child)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// If there's only one component, use its number and unit
|
|
108
|
+
// Otherwise, use the total in seconds and "SECONDS" as the unit
|
|
109
|
+
if (components.length === 1) {
|
|
110
|
+
return ["TimePeriodConstAgo", components[0][0], components[0][1]]
|
|
111
|
+
} else {
|
|
112
|
+
return ["TimePeriodConstAgo", totalSeconds, "SECONDS"]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
68
115
|
return ["TimePeriodConst", tp.text]
|
|
69
|
-
case '
|
|
70
|
-
|
|
116
|
+
case 'time_period_ago_between': {
|
|
117
|
+
// time_period_ago_between has children[0] = number_time, children[1] = between_tod_only
|
|
118
|
+
const betweenTodOnly = tp.children[1]
|
|
119
|
+
const betweenTod = betweenTodOnly.children[0]
|
|
120
|
+
const startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
121
|
+
const endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
122
|
+
|
|
123
|
+
// Check if there's a dow_range at betweenTod.children[2]
|
|
124
|
+
if (betweenTod.children.length > 2) {
|
|
125
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return ["TimePeriodBetween", startTod, endTod]
|
|
129
|
+
}
|
|
130
|
+
case 'between_tod_only': {
|
|
131
|
+
// between_tod_only has children[0] = between_tod node
|
|
132
|
+
const betweenTod = tp.children[0]
|
|
133
|
+
const startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
134
|
+
const endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
135
|
+
|
|
136
|
+
// Check if there's a dow_range at betweenTod.children[2]
|
|
137
|
+
if (betweenTod.children.length > 2) {
|
|
138
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return ["TimePeriodBetween", startTod, endTod]
|
|
142
|
+
}
|
|
143
|
+
case 'between_time_only': {
|
|
144
|
+
// between_number_only has children[0] = between_number node
|
|
145
|
+
const betweenNumber = tp.children[0]
|
|
146
|
+
const startValue = RuleParser.__parseValue(betweenNumber.children[0])
|
|
147
|
+
const endValue = RuleParser.__parseValue(betweenNumber.children[1])
|
|
148
|
+
|
|
149
|
+
return ["TimePeriodBetween", startValue, endValue]
|
|
150
|
+
}
|
|
71
151
|
}
|
|
72
152
|
}
|
|
73
153
|
static __parseValue(child){
|
|
@@ -157,15 +237,42 @@ class RuleParser {
|
|
|
157
237
|
}
|
|
158
238
|
return null
|
|
159
239
|
}
|
|
240
|
+
static _parseArithmeticOperand(operand){
|
|
241
|
+
assert(operand.children.length == 1)
|
|
242
|
+
const child = operand.children[0]
|
|
243
|
+
const type = child.type
|
|
244
|
+
switch(type){
|
|
245
|
+
case 'fcall':
|
|
246
|
+
return RuleParser._parseFcall(child)
|
|
247
|
+
case 'number':
|
|
248
|
+
return ['Value', parseFloat(child.text)]
|
|
249
|
+
case 'number_time':
|
|
250
|
+
return ['Value', RuleParser.__parseValue(child)]
|
|
251
|
+
}
|
|
252
|
+
throw new Error(`Unknown arithmetic operand type ${type}`)
|
|
253
|
+
}
|
|
160
254
|
static _parseArithmeticResult(result){
|
|
161
255
|
assert(result.children.length == 3)
|
|
162
|
-
const partA =
|
|
256
|
+
const partA = RuleParser._parseArithmeticOperand(result.children[0])
|
|
163
257
|
const operatorFn = ArithmeticOperators[result.children[1].text]
|
|
164
|
-
const partB =
|
|
258
|
+
const partB = RuleParser.__parseArithmeticResult(result, 2)
|
|
165
259
|
|
|
166
260
|
return [operatorFn, partA, partB]
|
|
167
261
|
}
|
|
168
262
|
|
|
263
|
+
static __parseArithmeticResult(result, idx){
|
|
264
|
+
const child = result.children[idx]
|
|
265
|
+
const type = child.type
|
|
266
|
+
switch(type){
|
|
267
|
+
case 'arithmetic_operand':
|
|
268
|
+
return RuleParser._parseArithmeticOperand(child)
|
|
269
|
+
case 'arithmetic_result':
|
|
270
|
+
return RuleParser._parseArithmeticResult(child)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
throw new Error(`Unknown arithmetic result node ${type}`)
|
|
274
|
+
}
|
|
275
|
+
|
|
169
276
|
static __parseResult(result, idx){
|
|
170
277
|
const child = result.children[idx]
|
|
171
278
|
const type = child.type
|
|
@@ -191,9 +298,39 @@ class RuleParser {
|
|
|
191
298
|
case 2: {
|
|
192
299
|
const rhs = expr.children[1]
|
|
193
300
|
switch(rhs.type){
|
|
194
|
-
case 'between_tod':
|
|
301
|
+
case 'between_tod': {
|
|
302
|
+
// Direct between_tod (without wrapping between node)
|
|
303
|
+
// between_tod has: children[0] = first tod, children[1] = second tod, children[2] = optional dow_range
|
|
304
|
+
const startTod = RuleParser.__parseValue(rhs.children[0])
|
|
305
|
+
const endTod = RuleParser.__parseValue(rhs.children[1])
|
|
306
|
+
|
|
307
|
+
// Check if there's a dow_range (children[2])
|
|
308
|
+
if (rhs.children.length > 2) {
|
|
309
|
+
RuleParser._addDowToTods(startTod, endTod, rhs.children[2])
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
313
|
+
}
|
|
314
|
+
case 'between': {
|
|
315
|
+
// between wraps either between_number or between_tod
|
|
316
|
+
const betweenChild = rhs.children[0]
|
|
317
|
+
if (betweenChild.type === 'between_tod') {
|
|
318
|
+
// between_tod has: children[0] = first tod, children[1] = second tod, children[2] = optional dow_range
|
|
319
|
+
const startTod = RuleParser.__parseValue(betweenChild.children[0])
|
|
320
|
+
const endTod = RuleParser.__parseValue(betweenChild.children[1])
|
|
321
|
+
|
|
322
|
+
// Check if there's a dow_range (children[2])
|
|
323
|
+
if (betweenChild.children.length > 2) {
|
|
324
|
+
RuleParser._addDowToTods(startTod, endTod, betweenChild.children[2])
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
328
|
+
} else {
|
|
329
|
+
// between_number - no dow support
|
|
330
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[1])]]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
195
333
|
case 'between_number':
|
|
196
|
-
case 'between':
|
|
197
334
|
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[1])]]
|
|
198
335
|
case 'basic_rhs':
|
|
199
336
|
return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
|