@halleyassist/rule-parser 1.0.18 → 1.0.20

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.
@@ -1,6 +1,7 @@
1
1
  const {Parser} = require('ebnf/dist/Parser.js'),
2
2
  {ParsingError} = require('ebnf'),
3
- assert = require('assert')
3
+ assert = require('assert'),
4
+ RuleParseError = require('./errors/RuleParseError')
4
5
 
5
6
  let ParserRules = require('./RuleParser.production.ebnf.js')
6
7
  let ParserCache;
@@ -108,10 +109,14 @@ class RuleParser {
108
109
  // dow_range can have 1 or 2 children (single day or range)
109
110
  if (dowRange.children.length === 1) {
110
111
  // Single day: ON MONDAY - return just the day string
111
- return { start: normalizeDow(dowRange.children[0].text), end: normalizeDow(dowRange.children[0].text) };
112
+ // New structure: dow_range -> dow_range_inner -> dow_atom -> dow
113
+ const dayText = RuleParser.__parseValue(dowRange.children[0])
114
+ return { start: dayText, end: dayText };
112
115
  } else if (dowRange.children.length === 2) {
113
116
  // Range: ON MONDAY TO FRIDAY - return both start and end days
114
- return { start: normalizeDow(dowRange.children[0].text), end: normalizeDow(dowRange.children[1].text) };
117
+ const startDay = RuleParser.__parseValue(dowRange.children[0])
118
+ const endDay = RuleParser.__parseValue(dowRange.children[1])
119
+ return { start: startDay, end: endDay };
115
120
  } else {
116
121
  throw new Error(`Invalid dow_range with ${dowRange.children.length} children`);
117
122
  }
@@ -133,12 +138,14 @@ class RuleParser {
133
138
  let totalSeconds = 0
134
139
  const components = []
135
140
  for (const child of timePeriodAgo.children) {
136
- if (child.type === 'number_time') {
137
- const number = parseFloat(child.children[0].text)
138
- const unit = child.children[1].text.toUpperCase()
141
+ if (child.type === 'number_time_atom') {
142
+ // New structure: number_time_atom -> number_time -> number, unit
143
+ const numberTime = child.children[0]
144
+ const number = parseFloat(numberTime.children[0].text)
145
+ const unit = numberTime.children[1].text.toUpperCase()
139
146
  components.push([number, unit])
140
147
  // Parse the value to get seconds
141
- totalSeconds += RuleParser.__parseValue(child)
148
+ totalSeconds += RuleParser.__parseValue(numberTime)
142
149
  }
143
150
  }
144
151
  // If there's only one component, use its number and unit
@@ -151,15 +158,16 @@ class RuleParser {
151
158
  }
152
159
  return ["TimePeriodConst", tp.text]
153
160
  case 'time_period_ago_between': {
154
- // time_period_ago_between has: number_time (WS+ number_time)* WS+ AGO WS+ between_tod_only
161
+ // time_period_ago_between has: number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ between_tod_only
155
162
  // We need to extract all number_time children and sum them up, then return TimePeriodBetweenAgo
156
163
  let totalSeconds = 0
157
164
  let betweenTodOnly = null
158
165
 
159
- // Find all number_time children and the between_tod_only child
166
+ // Find all number_time_atom children and the between_tod_only child
160
167
  for (let i = 0; i < tp.children.length; i++) {
161
- if (tp.children[i].type === 'number_time') {
162
- totalSeconds += RuleParser.__parseValue(tp.children[i])
168
+ if (tp.children[i].type === 'number_time_atom') {
169
+ // New structure: number_time_atom -> number_time
170
+ totalSeconds += RuleParser.__parseValue(tp.children[i].children[0])
163
171
  } else if (tp.children[i].type === 'between_tod_only') {
164
172
  betweenTodOnly = tp.children[i]
165
173
  }
@@ -171,6 +179,7 @@ class RuleParser {
171
179
  }
172
180
 
173
181
  const betweenTod = betweenTodOnly.children[0]
182
+ // between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
174
183
  let startTod = RuleParser.__parseValue(betweenTod.children[0])
175
184
  let endTod = RuleParser.__parseValue(betweenTod.children[1])
176
185
 
@@ -185,6 +194,7 @@ class RuleParser {
185
194
  case 'between_tod_only': {
186
195
  // between_tod_only has children[0] = between_tod node
187
196
  const betweenTod = tp.children[0]
197
+ // between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
188
198
  let startTod = RuleParser.__parseValue(betweenTod.children[0])
189
199
  let endTod = RuleParser.__parseValue(betweenTod.children[1])
190
200
 
@@ -200,13 +210,14 @@ class RuleParser {
200
210
  case 'between_time_only': {
201
211
  // between_time_only has children[0] = between_number_time node
202
212
  const betweenNumberTime = tp.children[0]
213
+ // between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
203
214
  const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
204
- const endValue = RuleParser.__parseValue(betweenNumberTime.children[1])
215
+ const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
205
216
 
206
- // Check if there's a dow_range at betweenNumberTime.children[2]
217
+ // Check if there's a dow_range at betweenNumberTime.children[3]
207
218
  // If DOW filters are provided, append them as additional parameters
208
- if (betweenNumberTime.children.length > 2 && betweenNumberTime.children[2].type === 'dow_range') {
209
- const dow = RuleParser._parseDowRange(betweenNumberTime.children[2])
219
+ if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
220
+ const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
210
221
  if (dow.start === dow.end) {
211
222
  // Single day: ["TimePeriodBetween", start, end, "MONDAY"]
212
223
  return ["TimePeriodBetween", startValue, endValue, dow.start]
@@ -223,6 +234,25 @@ class RuleParser {
223
234
  static __parseValue(child){
224
235
  const type = child.type
225
236
  switch(type){
237
+ case 'value': {
238
+ // Arrays have value nodes as children - unwrap to get value_atom
239
+ return RuleParser.__parseValue(child.children[0])
240
+ }
241
+ case 'value_atom': {
242
+ // New layer: unwrap value_atom to get the actual atomic type
243
+ return RuleParser.__parseValue(child.children[0])
244
+ }
245
+ case 'number_atom':
246
+ case 'number_time_atom':
247
+ case 'tod_atom':
248
+ case 'dow_atom':
249
+ case 'between_tod_inner':
250
+ case 'between_number_inner':
251
+ case 'between_number_time_inner':
252
+ case 'dow_range_inner': {
253
+ // New layer: unwrap atom wrappers to get the actual leaf nodes
254
+ return RuleParser.__parseValue(child.children[0])
255
+ }
226
256
  case 'string': {
227
257
  const str = child.text
228
258
  return str.slice(1, -1)
@@ -274,10 +304,13 @@ class RuleParser {
274
304
  case 'array': {
275
305
  const ret = []
276
306
  for(const c of child.children){
277
- ret.push(RuleParser.__parseValue(c.children[0]))
307
+ ret.push(RuleParser.__parseValue(c))
278
308
  }
279
309
  return ret;
280
310
  }
311
+ case 'dow': {
312
+ return normalizeDow(child.text)
313
+ }
281
314
  default:
282
315
  throw new Error(`Unknown value type ${type}`)
283
316
  }
@@ -287,7 +320,17 @@ class RuleParser {
287
320
 
288
321
  const type = child.type
289
322
  switch(type){
323
+ case 'value_atom': {
324
+ // New layer: unwrap value_atom to get the actual atomic type
325
+ const atomChild = child.children[0]
326
+ if (atomChild.type === 'time_period') {
327
+ const tp = atomChild.children[0]
328
+ return RuleParser._parseTimePeriod(tp)
329
+ }
330
+ return ['Value', RuleParser.__parseValue(atomChild)]
331
+ }
290
332
  case 'time_period': {
333
+ // Old structure (shouldn't happen with new EBNF)
291
334
  const tp = child.children[0]
292
335
  return RuleParser._parseTimePeriod(tp)
293
336
  }
@@ -314,6 +357,11 @@ class RuleParser {
314
357
  switch(type){
315
358
  case 'fcall':
316
359
  return RuleParser._parseFcall(child)
360
+ case 'number_atom':
361
+ case 'number_time_atom': {
362
+ // New layer: unwrap atom wrappers to get the actual leaf nodes
363
+ return ['Value', RuleParser.__parseValue(child.children[0])]
364
+ }
317
365
  case 'number':
318
366
  return ['Value', parseFloat(child.text)]
319
367
  case 'number_time':
@@ -406,7 +454,7 @@ class RuleParser {
406
454
  switch(rhs.type){
407
455
  case 'between_tod': {
408
456
  // Direct between_tod (without wrapping between node)
409
- // between_tod has: children[0] = first tod, children[1] = second tod, children[2] = optional dow_range
457
+ // between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
410
458
  const startTod = RuleParser.__parseValue(rhs.children[0])
411
459
  const endTod = RuleParser.__parseValue(rhs.children[1])
412
460
 
@@ -421,7 +469,7 @@ class RuleParser {
421
469
  // between wraps either between_number or between_tod
422
470
  const betweenChild = rhs.children[0]
423
471
  if (betweenChild.type === 'between_tod') {
424
- // between_tod has: children[0] = first tod, children[1] = second tod, children[2] = optional dow_range
472
+ // between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
425
473
  const startTod = RuleParser.__parseValue(betweenChild.children[0])
426
474
  const endTod = RuleParser.__parseValue(betweenChild.children[1])
427
475
 
@@ -432,12 +480,13 @@ class RuleParser {
432
480
 
433
481
  return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
434
482
  } else {
435
- // between_number - no dow support
436
- return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[1])]]
483
+ // between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
484
+ return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[2])]]
437
485
  }
438
486
  }
439
487
  case 'between_number':
440
- return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[1])]]
488
+ // between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
489
+ return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[2])]]
441
490
  case 'basic_rhs':
442
491
  return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
443
492
  case 'eq_approx': {
@@ -559,8 +608,6 @@ class RuleParser {
559
608
  // Extract the invalid time from the error message
560
609
  const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
561
610
  const badTod = match ? match[1] : 'invalid';
562
- const { ParsingError } = require('ebnf');
563
- const { RuleParseError } = require('./errors/RuleParseError');
564
611
 
565
612
  // Calculate position (simplified - at end of input)
566
613
  const lines = txt.trim().split('\n');
@@ -585,7 +632,6 @@ class RuleParser {
585
632
  if (e.message && e.message.includes('Invalid day of week')) {
586
633
  const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
587
634
  const badDow = match ? match[1] : 'invalid';
588
- const { RuleParseError } = require('./errors/RuleParseError');
589
635
 
590
636
  const lines = txt.trim().split('\n');
591
637
  const position = {
@@ -611,5 +657,6 @@ class RuleParser {
611
657
  }
612
658
  }
613
659
  module.exports = RuleParser
614
- module.exports.ParsingError = require('ebnf').ParsingError
615
- module.exports.RuleParseError = require('./errors/RuleParseError').RuleParseError
660
+ module.exports.ParserRules = ParserRules
661
+ module.exports.ParsingError = ParsingError
662
+ module.exports.RuleParseError = RuleParseError
@@ -1,5 +1,5 @@
1
1
  const { ParsingError } = require('ebnf');
2
- const { RuleParseError } = require('./RuleParseError');
2
+ const RuleParseError = require('./RuleParseError');
3
3
 
4
4
  /**
5
5
  * Analyzes parsing errors and maps them to user-friendly error codes
@@ -61,4 +61,4 @@ class RuleParseError extends Error {
61
61
  }
62
62
  }
63
63
 
64
- module.exports = { RuleParseError };
64
+ module.exports = RuleParseError;