@halleyassist/rule-parser 1.0.9 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halleyassist/rule-parser",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "The grammar for HalleyAssist rules",
5
5
  "main": "src/RuleParser.production.js",
6
6
  "scripts": {
@@ -18,11 +18,12 @@ DIVIDE ::= "/"
18
18
  MODULUS ::= "%"
19
19
  DEFAULT_VAL ::= "??"
20
20
  arithmetic_operator ::= PLUS | MINUS | MULTIPLY | DIVIDE | MODULUS | DEFAULT_VAL
21
- arithmetic_result ::= simple_result WS* arithmetic_operator WS* ( arithmetic_result | simple_result )
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 | time_period | string
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 */
@@ -46,15 +47,17 @@ fname ::= [a-zA-z0-9]+
46
47
  fcall ::= fname WS* BEGIN_ARGUMENT WS* arguments? END_ARGUMENT
47
48
 
48
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
49
51
  between_tod ||= number_tod ((WS+ "AND" WS+)) number_tod (WS+ dow_range)?
50
52
  between ||= "BETWEEN" WS+ (between_number | between_tod)
51
53
  dow ||= "MONDAY" | "TUESDAY" | "WEDNESDAY" | "THURSDAY" | "FRIDAY" | "SATURDAY" | "SUNDAY"
52
54
  dow_range ||= "ON" WS+ dow (WS+ "TO" WS+ dow)?
53
- between_number_only ||= "BETWEEN" WS+ between_number
55
+ between_time_only ||= "BETWEEN" WS+ between_number_time
54
56
  between_tod_only ||= "BETWEEN" WS+ between_tod
55
57
 
56
58
  AND ||= (WS* "&&" WS*) | (WS+ "AND" WS+)
57
59
  OR ||= (WS* "||" WS*) | (WS+ "OR" WS+)
60
+ AGO ||= "AGO"
58
61
  GT ::= ">"
59
62
  LT ::= "<"
60
63
  GTE ::= ">="
@@ -68,13 +71,15 @@ null ||= "null"
68
71
  true ||= "TRUE"
69
72
  array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY
70
73
 
71
- unit ||= "seconds" | "second" | "minutes" | "minute" | "min" | "mins" | "min" | "hours" | "hour" | "days" | "day" | "weeks" | "week"
74
+ unit ||= "seconds" | "minutes" | "hours" | "weeks" | "days" | "second" | "minute" | "week" | "hour" | "day" | "mins" | "min"
72
75
  number ::= "-"? ([0-9]+) ("." [0-9]+)? ("e" ( "-" | "+" )? ("0" | [1-9] [0-9]*))?
73
76
  number_time ::= number WS+ unit
74
77
  number_tod ::= ([0-9]+) ":" ([0-9]+)
75
78
 
76
- time_period_const ||= "today"
77
- time_period ::= time_period_const | between_tod_only | between_number_only
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
78
83
 
79
84
  string ::= '"' (([#x20-#x21] | [#x23-#x5B] | [#x5D-#xFFFF]) | #x5C (#x22 | #x5C | #x2F | #x62 | #x66 | #x6E | #x72 | #x74 | #x75 HEXDIG HEXDIG HEXDIG HEXDIG))* '"'
80
85
  HEXDIG ::= [a-fA-F0-9]
package/src/RuleParser.js CHANGED
@@ -89,7 +89,44 @@ class RuleParser {
89
89
  static _parseTimePeriod(tp){
90
90
  switch(tp.type){
91
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
+ }
92
115
  return ["TimePeriodConst", tp.text]
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
+ }
93
130
  case 'between_tod_only': {
94
131
  // between_tod_only has children[0] = between_tod node
95
132
  const betweenTod = tp.children[0]
@@ -103,7 +140,7 @@ class RuleParser {
103
140
 
104
141
  return ["TimePeriodBetween", startTod, endTod]
105
142
  }
106
- case 'between_number_only': {
143
+ case 'between_time_only': {
107
144
  // between_number_only has children[0] = between_number node
108
145
  const betweenNumber = tp.children[0]
109
146
  const startValue = RuleParser.__parseValue(betweenNumber.children[0])
@@ -200,15 +237,42 @@ class RuleParser {
200
237
  }
201
238
  return null
202
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
+ }
203
254
  static _parseArithmeticResult(result){
204
255
  assert(result.children.length == 3)
205
- const partA = this._parseSimpleResult(result.children[0])
256
+ const partA = RuleParser._parseArithmeticOperand(result.children[0])
206
257
  const operatorFn = ArithmeticOperators[result.children[1].text]
207
- const partB = this.__parseResult(result, 2)
258
+ const partB = RuleParser.__parseArithmeticResult(result, 2)
208
259
 
209
260
  return [operatorFn, partA, partB]
210
261
  }
211
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
+
212
276
  static __parseResult(result, idx){
213
277
  const child = result.children[idx]
214
278
  const type = child.type
@@ -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"],["simple_result"]],"fragment":true},{"name":"arithmetic_result","bnf":[["simple_result","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"],["number_time"],["number"],["number_tod"],["time_period"],["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_tod1617","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%between_tod16","bnf":[["%%between_tod1617"]],"fragment":true},{"name":"%between_tod18","bnf":[["WS+","dow_range"]],"fragment":true},{"name":"between_tod","bnf":[["number_tod","%between_tod16","number_tod","%between_tod18?"]]},{"name":"%between19","bnf":[["between_number"],["between_tod"]],"fragment":true},{"name":"between","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","%between19"]]},{"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_range20","bnf":[["WS+",/[Tt]/,/[Oo]/,"WS+","dow"]],"fragment":true},{"name":"dow_range","bnf":[[/[Oo]/,/[Nn]/,"WS+","dow","%dow_range20?"]]},{"name":"between_number_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_number"]]},{"name":"between_tod_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_tod"]]},{"name":"%AND21","bnf":[["WS*",/&/,/&/,"WS*"]],"fragment":true},{"name":"%AND22","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"AND","bnf":[["%AND21"],["%AND22"]]},{"name":"%OR23","bnf":[["WS*",/\|/,/\|/,"WS*"]],"fragment":true},{"name":"%OR24","bnf":[["WS+",/[Oo]/,/[Rr]/,"WS+"]],"fragment":true},{"name":"OR","bnf":[["%OR23"],["%OR24"]]},{"name":"GT","bnf":[["\">\""]]},{"name":"LT","bnf":[["\"<\""]]},{"name":"GTE","bnf":[["\">=\""]]},{"name":"LTE","bnf":[["\"<=\""]]},{"name":"IS","bnf":[[/[Ii]/,/[Ss]/]]},{"name":"EQ","bnf":[["\"==\""],["\"=\""]]},{"name":"NEQ","bnf":[["\"!=\""]]},{"name":"%NOT25","bnf":[[/!/,"WS*"]],"fragment":true},{"name":"%NOT26","bnf":[[/[Nn]/,/[Oo]/,/[Tt]/,"WS+"]],"fragment":true},{"name":"NOT","bnf":[["%NOT25"],["%NOT26"]]},{"name":"false","bnf":[[/[Ff]/,/[Aa]/,/[Ll]/,/[Ss]/,/[Ee]/]]},{"name":"null","bnf":[[/[Nn]/,/[Uu]/,/[Ll]/,/[Ll]/]]},{"name":"true","bnf":[[/[Tt]/,/[Rr]/,/[Uu]/,/[Ee]/]]},{"name":"%%array2728","bnf":[["VALUE_SEPARATOR","value"]],"fragment":true},{"name":"%array27","bnf":[["value","%%array2728*"]],"fragment":true},{"name":"array","bnf":[["BEGIN_ARRAY","%array27?","END_ARRAY"]]},{"name":"unit","bnf":[[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Ss]/],[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/],[/[Mm]/,/[Ii]/,/[Nn]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/,/[Ss]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/],[/[Dd]/,/[Aa]/,/[Yy]/,/[Ss]/],[/[Dd]/,/[Aa]/,/[Yy]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/,/[Ss]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/]]},{"name":"%%number2930","bnf":[[/[0-9]/]]},{"name":"%number29","bnf":[["%%number2930+"]],"fragment":true},{"name":"%%number3132","bnf":[[/[0-9]/]]},{"name":"%number31","bnf":[["\".\"","%%number3132+"]],"fragment":true},{"name":"%%number3334","bnf":[["\"-\""],["\"+\""]],"fragment":true},{"name":"%%%number333536","bnf":[[/[0-9]/]]},{"name":"%%number3335","bnf":[["\"0\""],[/[1-9]/,"%%%number333536*"]],"fragment":true},{"name":"%number33","bnf":[["\"e\"","%%number3334?","%%number3335"]],"fragment":true},{"name":"number","bnf":[["\"-\"?","%number29","%number31?","%number33?"]]},{"name":"number_time","bnf":[["number","WS+","unit"]]},{"name":"%%number_tod3738","bnf":[[/[0-9]/]]},{"name":"%number_tod37","bnf":[["%%number_tod3738+"]],"fragment":true},{"name":"%%number_tod3940","bnf":[[/[0-9]/]]},{"name":"%number_tod39","bnf":[["%%number_tod3940+"]],"fragment":true},{"name":"number_tod","bnf":[["%number_tod37","\":\"","%number_tod39"]]},{"name":"time_period_const","bnf":[[/[Tt]/,/[Oo]/,/[Dd]/,/[Aa]/,/[Yy]/]]},{"name":"time_period","bnf":[["time_period_const"],["between_tod_only"],["between_number_only"]]},{"name":"%%string4142","bnf":[[/[\x20-\x21]/],[/[\x23-\x5B]/],[/[\x5D-\uFFFF]/]],"fragment":true},{"name":"%%string4143","bnf":[[/\x22/],[/\x5C/],[/\x2F/],[/\x62/],[/\x66/],[/\x6E/],[/\x72/],[/\x74/],[/\x75/,"HEXDIG","HEXDIG","HEXDIG","HEXDIG"]],"fragment":true},{"name":"%string41","bnf":[["%%string4142"],[/\x5C/,"%%string4143"]],"fragment":true},{"name":"string","bnf":[["'\"'","%string41*","'\"'"]]},{"name":"HEXDIG","bnf":[[/[a-fA-F0-9]/]]}]
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]/]]}]
@@ -89,7 +89,44 @@ class RuleParser {
89
89
  static _parseTimePeriod(tp){
90
90
  switch(tp.type){
91
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
+ }
92
115
  return ["TimePeriodConst", tp.text]
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
+ }
93
130
  case 'between_tod_only': {
94
131
  // between_tod_only has children[0] = between_tod node
95
132
  const betweenTod = tp.children[0]
@@ -103,7 +140,7 @@ class RuleParser {
103
140
 
104
141
  return ["TimePeriodBetween", startTod, endTod]
105
142
  }
106
- case 'between_number_only': {
143
+ case 'between_time_only': {
107
144
  // between_number_only has children[0] = between_number node
108
145
  const betweenNumber = tp.children[0]
109
146
  const startValue = RuleParser.__parseValue(betweenNumber.children[0])
@@ -200,15 +237,42 @@ class RuleParser {
200
237
  }
201
238
  return null
202
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
+ }
203
254
  static _parseArithmeticResult(result){
204
255
  assert(result.children.length == 3)
205
- const partA = this._parseSimpleResult(result.children[0])
256
+ const partA = RuleParser._parseArithmeticOperand(result.children[0])
206
257
  const operatorFn = ArithmeticOperators[result.children[1].text]
207
- const partB = this.__parseResult(result, 2)
258
+ const partB = RuleParser.__parseArithmeticResult(result, 2)
208
259
 
209
260
  return [operatorFn, partA, partB]
210
261
  }
211
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
+
212
276
  static __parseResult(result, idx){
213
277
  const child = result.children[idx]
214
278
  const type = child.type