@halleyassist/rule-parser 1.0.11 → 1.0.12
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 +4 -0
- package/package.json +1 -1
- package/src/RuleParser.js +52 -15
- package/src/RuleParser.production.js +52 -15
package/README.md
CHANGED
package/package.json
CHANGED
package/src/RuleParser.js
CHANGED
|
@@ -91,25 +91,22 @@ class RuleParser {
|
|
|
91
91
|
return ret
|
|
92
92
|
}
|
|
93
93
|
static _parseDowRange(dowRange) {
|
|
94
|
-
const dow = [];
|
|
95
94
|
// dow_range can have 1 or 2 children (single day or range)
|
|
96
95
|
if (dowRange.children.length === 1) {
|
|
97
|
-
// Single day: ON MONDAY
|
|
98
|
-
|
|
96
|
+
// Single day: ON MONDAY - return just the day string
|
|
97
|
+
return { start: normalizeDow(dowRange.children[0].text), end: normalizeDow(dowRange.children[0].text) };
|
|
99
98
|
} else if (dowRange.children.length === 2) {
|
|
100
|
-
// Range: ON MONDAY TO
|
|
101
|
-
|
|
102
|
-
dow.push(normalizeDow(dowRange.children[1].text));
|
|
99
|
+
// Range: ON MONDAY TO FRIDAY - return both start and end days
|
|
100
|
+
return { start: normalizeDow(dowRange.children[0].text), end: normalizeDow(dowRange.children[1].text) };
|
|
103
101
|
} else {
|
|
104
102
|
throw new Error(`Invalid dow_range with ${dowRange.children.length} children`);
|
|
105
103
|
}
|
|
106
|
-
return dow;
|
|
107
104
|
}
|
|
108
105
|
static _addDowToTods(startTod, endTod, dowRange) {
|
|
109
106
|
if (dowRange && dowRange.type === 'dow_range') {
|
|
110
107
|
const dow = RuleParser._parseDowRange(dowRange)
|
|
111
|
-
startTod.dow = dow
|
|
112
|
-
endTod.dow = dow
|
|
108
|
+
startTod.dow = dow.start
|
|
109
|
+
endTod.dow = dow.end
|
|
113
110
|
}
|
|
114
111
|
}
|
|
115
112
|
static _parseTimePeriod(tp){
|
|
@@ -143,11 +140,13 @@ class RuleParser {
|
|
|
143
140
|
// time_period_ago_between has children[0] = number_time, children[1] = between_tod_only
|
|
144
141
|
const betweenTodOnly = tp.children[1]
|
|
145
142
|
const betweenTod = betweenTodOnly.children[0]
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
144
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
148
145
|
|
|
149
146
|
// Check if there's a dow_range at betweenTod.children[2]
|
|
150
147
|
if (betweenTod.children.length > 2) {
|
|
148
|
+
if(typeof startTod === 'number') startTod = {seconds: startTod, dow: null}
|
|
149
|
+
if(typeof endTod === 'number') endTod = {seconds: endTod, dow: null}
|
|
151
150
|
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
152
151
|
}
|
|
153
152
|
|
|
@@ -156,11 +155,13 @@ class RuleParser {
|
|
|
156
155
|
case 'between_tod_only': {
|
|
157
156
|
// between_tod_only has children[0] = between_tod node
|
|
158
157
|
const betweenTod = tp.children[0]
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
159
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
161
160
|
|
|
162
161
|
// Check if there's a dow_range at betweenTod.children[2]
|
|
163
162
|
if (betweenTod.children.length > 2) {
|
|
163
|
+
if(typeof startTod === 'number') startTod = {seconds: startTod, dow: null}
|
|
164
|
+
if(typeof endTod === 'number') endTod = {seconds: endTod, dow: null}
|
|
164
165
|
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -176,8 +177,13 @@ class RuleParser {
|
|
|
176
177
|
// If DOW filters are provided, append them as additional parameters
|
|
177
178
|
if (betweenNumberTime.children.length > 2 && betweenNumberTime.children[2].type === 'dow_range') {
|
|
178
179
|
const dow = RuleParser._parseDowRange(betweenNumberTime.children[2])
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
if (dow.start === dow.end) {
|
|
181
|
+
// Single day: ["TimePeriodBetween", start, end, "MONDAY"]
|
|
182
|
+
return ["TimePeriodBetween", startValue, endValue, dow.start]
|
|
183
|
+
} else {
|
|
184
|
+
// Range: ["TimePeriodBetween", start, end, "MONDAY", "FRIDAY"]
|
|
185
|
+
return ["TimePeriodBetween", startValue, endValue, dow.start, dow.end]
|
|
186
|
+
}
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
return ["TimePeriodBetween", startValue, endValue]
|
|
@@ -285,12 +291,43 @@ class RuleParser {
|
|
|
285
291
|
}
|
|
286
292
|
throw new Error(`Unknown arithmetic operand type ${type}`)
|
|
287
293
|
}
|
|
294
|
+
static _isConstantValue(expr){
|
|
295
|
+
// Check if an expression is a constant value
|
|
296
|
+
return Array.isArray(expr) && expr.length === 2 && expr[0] === 'Value' && typeof expr[1] === 'number'
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
static _evaluateConstantArithmetic(operator, leftValue, rightValue){
|
|
300
|
+
// Evaluate constant arithmetic operations at parse time
|
|
301
|
+
switch(operator){
|
|
302
|
+
case 'MathAdd':
|
|
303
|
+
return leftValue + rightValue
|
|
304
|
+
case 'MathSub':
|
|
305
|
+
return leftValue - rightValue
|
|
306
|
+
case 'MathMul':
|
|
307
|
+
return leftValue * rightValue
|
|
308
|
+
case 'MathDiv':
|
|
309
|
+
return leftValue / rightValue
|
|
310
|
+
case 'MathMod':
|
|
311
|
+
return leftValue % rightValue
|
|
312
|
+
default:
|
|
313
|
+
return null
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
288
317
|
static _parseArithmeticResult(result){
|
|
289
318
|
assert(result.children.length == 3)
|
|
290
319
|
const partA = RuleParser._parseArithmeticOperand(result.children[0])
|
|
291
320
|
const operatorFn = ArithmeticOperators[result.children[1].text]
|
|
292
321
|
const partB = RuleParser.__parseArithmeticResult(result, 2)
|
|
293
322
|
|
|
323
|
+
// Compile out constant expressions
|
|
324
|
+
if (RuleParser._isConstantValue(partA) && RuleParser._isConstantValue(partB)) {
|
|
325
|
+
const result = RuleParser._evaluateConstantArithmetic(operatorFn, partA[1], partB[1])
|
|
326
|
+
if (result !== null) {
|
|
327
|
+
return ['Value', result]
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
294
331
|
return [operatorFn, partA, partB]
|
|
295
332
|
}
|
|
296
333
|
|
|
@@ -91,25 +91,22 @@ class RuleParser {
|
|
|
91
91
|
return ret
|
|
92
92
|
}
|
|
93
93
|
static _parseDowRange(dowRange) {
|
|
94
|
-
const dow = [];
|
|
95
94
|
// dow_range can have 1 or 2 children (single day or range)
|
|
96
95
|
if (dowRange.children.length === 1) {
|
|
97
|
-
// Single day: ON MONDAY
|
|
98
|
-
|
|
96
|
+
// Single day: ON MONDAY - return just the day string
|
|
97
|
+
return { start: normalizeDow(dowRange.children[0].text), end: normalizeDow(dowRange.children[0].text) };
|
|
99
98
|
} else if (dowRange.children.length === 2) {
|
|
100
|
-
// Range: ON MONDAY TO
|
|
101
|
-
|
|
102
|
-
dow.push(normalizeDow(dowRange.children[1].text));
|
|
99
|
+
// Range: ON MONDAY TO FRIDAY - return both start and end days
|
|
100
|
+
return { start: normalizeDow(dowRange.children[0].text), end: normalizeDow(dowRange.children[1].text) };
|
|
103
101
|
} else {
|
|
104
102
|
throw new Error(`Invalid dow_range with ${dowRange.children.length} children`);
|
|
105
103
|
}
|
|
106
|
-
return dow;
|
|
107
104
|
}
|
|
108
105
|
static _addDowToTods(startTod, endTod, dowRange) {
|
|
109
106
|
if (dowRange && dowRange.type === 'dow_range') {
|
|
110
107
|
const dow = RuleParser._parseDowRange(dowRange)
|
|
111
|
-
startTod.dow = dow
|
|
112
|
-
endTod.dow = dow
|
|
108
|
+
startTod.dow = dow.start
|
|
109
|
+
endTod.dow = dow.end
|
|
113
110
|
}
|
|
114
111
|
}
|
|
115
112
|
static _parseTimePeriod(tp){
|
|
@@ -143,11 +140,13 @@ class RuleParser {
|
|
|
143
140
|
// time_period_ago_between has children[0] = number_time, children[1] = between_tod_only
|
|
144
141
|
const betweenTodOnly = tp.children[1]
|
|
145
142
|
const betweenTod = betweenTodOnly.children[0]
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
144
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
148
145
|
|
|
149
146
|
// Check if there's a dow_range at betweenTod.children[2]
|
|
150
147
|
if (betweenTod.children.length > 2) {
|
|
148
|
+
if(typeof startTod === 'number') startTod = {seconds: startTod, dow: null}
|
|
149
|
+
if(typeof endTod === 'number') endTod = {seconds: endTod, dow: null}
|
|
151
150
|
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
152
151
|
}
|
|
153
152
|
|
|
@@ -156,11 +155,13 @@ class RuleParser {
|
|
|
156
155
|
case 'between_tod_only': {
|
|
157
156
|
// between_tod_only has children[0] = between_tod node
|
|
158
157
|
const betweenTod = tp.children[0]
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
159
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
161
160
|
|
|
162
161
|
// Check if there's a dow_range at betweenTod.children[2]
|
|
163
162
|
if (betweenTod.children.length > 2) {
|
|
163
|
+
if(typeof startTod === 'number') startTod = {seconds: startTod, dow: null}
|
|
164
|
+
if(typeof endTod === 'number') endTod = {seconds: endTod, dow: null}
|
|
164
165
|
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -176,8 +177,13 @@ class RuleParser {
|
|
|
176
177
|
// If DOW filters are provided, append them as additional parameters
|
|
177
178
|
if (betweenNumberTime.children.length > 2 && betweenNumberTime.children[2].type === 'dow_range') {
|
|
178
179
|
const dow = RuleParser._parseDowRange(betweenNumberTime.children[2])
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
if (dow.start === dow.end) {
|
|
181
|
+
// Single day: ["TimePeriodBetween", start, end, "MONDAY"]
|
|
182
|
+
return ["TimePeriodBetween", startValue, endValue, dow.start]
|
|
183
|
+
} else {
|
|
184
|
+
// Range: ["TimePeriodBetween", start, end, "MONDAY", "FRIDAY"]
|
|
185
|
+
return ["TimePeriodBetween", startValue, endValue, dow.start, dow.end]
|
|
186
|
+
}
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
return ["TimePeriodBetween", startValue, endValue]
|
|
@@ -285,12 +291,43 @@ class RuleParser {
|
|
|
285
291
|
}
|
|
286
292
|
throw new Error(`Unknown arithmetic operand type ${type}`)
|
|
287
293
|
}
|
|
294
|
+
static _isConstantValue(expr){
|
|
295
|
+
// Check if an expression is a constant value
|
|
296
|
+
return Array.isArray(expr) && expr.length === 2 && expr[0] === 'Value' && typeof expr[1] === 'number'
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
static _evaluateConstantArithmetic(operator, leftValue, rightValue){
|
|
300
|
+
// Evaluate constant arithmetic operations at parse time
|
|
301
|
+
switch(operator){
|
|
302
|
+
case 'MathAdd':
|
|
303
|
+
return leftValue + rightValue
|
|
304
|
+
case 'MathSub':
|
|
305
|
+
return leftValue - rightValue
|
|
306
|
+
case 'MathMul':
|
|
307
|
+
return leftValue * rightValue
|
|
308
|
+
case 'MathDiv':
|
|
309
|
+
return leftValue / rightValue
|
|
310
|
+
case 'MathMod':
|
|
311
|
+
return leftValue % rightValue
|
|
312
|
+
default:
|
|
313
|
+
return null
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
288
317
|
static _parseArithmeticResult(result){
|
|
289
318
|
assert(result.children.length == 3)
|
|
290
319
|
const partA = RuleParser._parseArithmeticOperand(result.children[0])
|
|
291
320
|
const operatorFn = ArithmeticOperators[result.children[1].text]
|
|
292
321
|
const partB = RuleParser.__parseArithmeticResult(result, 2)
|
|
293
322
|
|
|
323
|
+
// Compile out constant expressions
|
|
324
|
+
if (RuleParser._isConstantValue(partA) && RuleParser._isConstantValue(partB)) {
|
|
325
|
+
const result = RuleParser._evaluateConstantArithmetic(operatorFn, partA[1], partB[1])
|
|
326
|
+
if (result !== null) {
|
|
327
|
+
return ['Value', result]
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
294
331
|
return [operatorFn, partA, partB]
|
|
295
332
|
}
|
|
296
333
|
|