@halleyassist/rule-templater 0.0.1 → 0.0.4

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.
@@ -0,0 +1,2453 @@
1
+ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.RuleTemplater = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
+ const {Grammars} = require('ebnf');
3
+
4
+ const grammar = `
5
+ statement_main ::= statement EOF
6
+ logical_operator ||= AND | OR
7
+ statement ::= expression (logical_operator expression)*
8
+ expression ::= not_expression | standard_expression | parenthesis_expression
9
+ parenthesis_expression ::= BEGIN_PARENTHESIS WS* statement WS* END_PARENTHESIS
10
+ not_expression ||= NOT (result | parenthesis_expression)
11
+ standard_expression ||= result ((WS* eq_approx) | (WS* basic_rhs) | ((WS+ IS)? WS+ between) | (WS+ in_expr))?
12
+ basic_rhs ::= operator WS* result
13
+ eq_approx ::= eq_operator WS* "~" WS* result
14
+
15
+ PLUS ::= "+"
16
+ MINUS ::= "-"
17
+ MULTIPLY ::= "*"
18
+ DIVIDE ::= "/"
19
+ MODULUS ::= "%"
20
+ DEFAULT_VAL ::= "??"
21
+ arithmetic_operator ::= PLUS | MINUS | MULTIPLY | DIVIDE | MODULUS | DEFAULT_VAL
22
+
23
+ number_atom ::= number
24
+ number_time_atom ::= number_time
25
+ tod_atom ::= number_tod
26
+ dow_atom ::= dow
27
+
28
+ arithmetic_operand ::= fcall | number_time_atom | number_atom
29
+ arithmetic_result ::= arithmetic_operand WS* arithmetic_operator WS* (arithmetic_result | arithmetic_operand)
30
+
31
+ simple_result ::= fcall | value
32
+ result ::= arithmetic_result | simple_result
33
+
34
+ value_atom ::= false | true | array | time_period | number_time_atom | number_atom | tod_atom | string
35
+ value ::= value_atom
36
+
37
+ BEGIN_ARRAY ::= WS* #x5B WS* /* [ */
38
+ BEGIN_OBJECT ::= WS* #x7B WS* /* { */
39
+ END_ARRAY ::= WS* #x5D WS* /* ] */
40
+ END_OBJECT ::= WS* #x7D WS* /* } */
41
+ NAME_SEPARATOR ::= WS* #x3A WS* /* : */
42
+ VALUE_SEPARATOR ::= WS* #x2C WS* /* , */
43
+ WS ::= [#x20#x09#x0A#x0D]
44
+
45
+ operator ::= GTE | LTE | GT | LT | EQ | NEQ
46
+ eq_operator ::= EQ | NEQ
47
+
48
+ BEGIN_ARGUMENT ::= "("
49
+ END_ARGUMENT ::= ")"
50
+ BEGIN_PARENTHESIS ::= "("
51
+ END_PARENTHESIS ::= ")"
52
+
53
+ BEGIN_IN ||= "IN"
54
+ in_expr ::= BEGIN_IN WS* BEGIN_PARENTHESIS WS* arguments END_PARENTHESIS
55
+
56
+ argument ::= statement WS*
57
+ arguments ::= argument (WS* "," WS* argument)*
58
+ fname ::= [A-Za-z0-9]+
59
+ fcall ::= fname WS* BEGIN_ARGUMENT WS* arguments? END_ARGUMENT
60
+
61
+ between_dash_or_and ||= (WS+ "AND" WS+) | (WS* "-" WS*)
62
+
63
+ between_number_inner ::= number_atom | number_time_atom
64
+ between_number ||= between_number_inner between_dash_or_and between_number_inner
65
+
66
+ between_number_time_inner ::= number_time_atom
67
+ between_number_time ||= between_number_time_inner between_dash_or_and between_number_time_inner (WS+ dow_range)?
68
+
69
+ between_tod_inner ::= tod_atom
70
+ between_tod ||= between_tod_inner (WS+ "AND" WS+) between_tod_inner (WS+ dow_range)?
71
+
72
+ between ||= "BETWEEN" WS+ (between_number | between_tod)
73
+
74
+ dow ||= "MONDAY" | "MON" | "TUESDAY" | "TUE" | "WEDNESDAY" | "WED" | "THURSDAY" | "THU" | "THUR" | "FRIDAY" | "FRI" | "SATURDAY" | "SAT" | "SUNDAY" | "SUN"
75
+
76
+ dow_range_inner ::= dow_atom
77
+ dow_range ||= "ON" WS+ dow_range_inner (WS+ "TO" WS+ dow_range_inner)?
78
+
79
+ between_time_only ||= "BETWEEN" WS+ between_number_time
80
+ between_tod_only ||= "BETWEEN" WS+ between_tod
81
+
82
+ AND ||= (WS* "&&" WS*) | (WS+ "AND" WS+)
83
+ OR ||= (WS* "||" WS*) | (WS+ "OR" WS+)
84
+ AGO ||= "AGO"
85
+ GT ::= ">"
86
+ LT ::= "<"
87
+ GTE ::= ">="
88
+ LTE ::= "<="
89
+ IS ||= "is"
90
+ EQ ::= "==" | "="
91
+ NEQ ::= "!="
92
+ NOT ||= ("!" WS*) | ("not" WS+)
93
+
94
+ false ||= "FALSE"
95
+ null ||= "null"
96
+ true ||= "TRUE"
97
+
98
+ array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY
99
+
100
+ unit ||= "seconds" | "minutes" | "hours" | "weeks" | "days" | "second" | "minute" | "week" | "hour" | "day" | "mins" | "min"
101
+
102
+ number ::= "-"? ([0-9]+) ("." [0-9]+)? ("e" ("-" | "+")? ("0" | [1-9] [0-9]*))?
103
+ number_time ::= number WS+ unit
104
+ number_tod ::= ([0-9]+) ":" ([0-9]+)
105
+
106
+ time_period_ago ||= number_time_atom (WS+ number_time_atom)* WS+ AGO
107
+ time_period_ago_between ||= number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ between_tod_only
108
+ time_period_const ||= "today" | time_period_ago
109
+ time_period ::= time_period_ago_between | time_period_const | between_tod_only | between_time_only
110
+
111
+ string ::= '"' (([#x20-#x21] | [#x23-#x5B] | [#x5D-#xFFFF]) | #x5C (#x22 | #x5C | #x2F | #x62 | #x66 | #x6E | #x72 | #x74 | #x75 HEXDIG HEXDIG HEXDIG HEXDIG))* '"'
112
+ HEXDIG ::= [a-fA-F0-9]
113
+ `
114
+
115
+ module.exports = Grammars.W3C.getRules(grammar);
116
+
117
+ },{"ebnf":10}],2:[function(require,module,exports){
118
+ "use strict";
119
+ // https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form
120
+ Object.defineProperty(exports, "__esModule", { value: true });
121
+ /*
122
+ syntax ::= RULE_EOL* rule+
123
+ rule ::= " "* "<" rule-name ">" " "* "::=" firstExpression otherExpression* " "* RULE_EOL+ " "*
124
+ firstExpression ::= " "* list
125
+ otherExpression ::= " "* "|" " "* list
126
+ RULE_EOL ::= "\r" | "\n"
127
+ list ::= term " "* list | term
128
+ term ::= literal | "<" rule-name ">"
129
+ literal ::= '"' RULE_CHARACTER1* '"' | "'" RULE_CHARACTER2* "'"
130
+ RULE_CHARACTER ::= " " | RULE_LETTER | RULE_DIGIT | RULE_SYMBOL
131
+ RULE_LETTER ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
132
+ RULE_DIGIT ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
133
+ RULE_SYMBOL ::= "-" | "_" | "!" | "#" | "$" | "%" | "&" | "(" | ")" | "*" | "+" | "," | "-" | "." | "/" | ":" | ";" | "<" | "=" | ">" | "?" | "@" | "[" | "\" | "]" | "^" | "_" | "`" | "{" | "|" | "}" | "~"
134
+ RULE_CHARACTER1 ::= RULE_CHARACTER | "'"
135
+ RULE_CHARACTER2 ::= RULE_CHARACTER | '"'
136
+ rule-name ::= RULE_LETTER RULE_CHAR*
137
+ RULE_CHAR ::= RULE_LETTER | RULE_DIGIT | "_" | "-"
138
+ */
139
+ const SemanticHelpers_1 = require("../SemanticHelpers");
140
+ const Parser_1 = require("../Parser");
141
+ var BNF;
142
+ (function (BNF) {
143
+ BNF.RULES = [
144
+ {
145
+ name: 'syntax',
146
+ bnf: [['RULE_EOL*', 'rule+']]
147
+ },
148
+ {
149
+ name: 'rule',
150
+ bnf: [
151
+ [
152
+ '" "*',
153
+ '"<"',
154
+ 'rule-name',
155
+ '">"',
156
+ '" "*',
157
+ '"::="',
158
+ 'firstExpression',
159
+ 'otherExpression*',
160
+ '" "*',
161
+ 'RULE_EOL+',
162
+ '" "*'
163
+ ]
164
+ ]
165
+ },
166
+ {
167
+ name: 'firstExpression',
168
+ bnf: [['" "*', 'list']]
169
+ },
170
+ {
171
+ name: 'otherExpression',
172
+ bnf: [['" "*', '"|"', '" "*', 'list']]
173
+ },
174
+ {
175
+ name: 'RULE_EOL',
176
+ bnf: [['"\\r"'], ['"\\n"']]
177
+ },
178
+ {
179
+ name: 'list',
180
+ bnf: [['term', '" "*', 'list'], ['term']]
181
+ },
182
+ {
183
+ name: 'term',
184
+ bnf: [['literal'], ['"<"', 'rule-name', '">"']]
185
+ },
186
+ {
187
+ name: 'literal',
188
+ bnf: [[`'"'`, 'RULE_CHARACTER1*', `'"'`], [`"'"`, 'RULE_CHARACTER2*', `"'"`]]
189
+ },
190
+ {
191
+ name: 'RULE_CHARACTER',
192
+ bnf: [['" "'], ['RULE_LETTER'], ['RULE_DIGIT'], ['RULE_SYMBOL']]
193
+ },
194
+ {
195
+ name: 'RULE_LETTER',
196
+ bnf: [
197
+ ['"A"'],
198
+ ['"B"'],
199
+ ['"C"'],
200
+ ['"D"'],
201
+ ['"E"'],
202
+ ['"F"'],
203
+ ['"G"'],
204
+ ['"H"'],
205
+ ['"I"'],
206
+ ['"J"'],
207
+ ['"K"'],
208
+ ['"L"'],
209
+ ['"M"'],
210
+ ['"N"'],
211
+ ['"O"'],
212
+ ['"P"'],
213
+ ['"Q"'],
214
+ ['"R"'],
215
+ ['"S"'],
216
+ ['"T"'],
217
+ ['"U"'],
218
+ ['"V"'],
219
+ ['"W"'],
220
+ ['"X"'],
221
+ ['"Y"'],
222
+ ['"Z"'],
223
+ ['"a"'],
224
+ ['"b"'],
225
+ ['"c"'],
226
+ ['"d"'],
227
+ ['"e"'],
228
+ ['"f"'],
229
+ ['"g"'],
230
+ ['"h"'],
231
+ ['"i"'],
232
+ ['"j"'],
233
+ ['"k"'],
234
+ ['"l"'],
235
+ ['"m"'],
236
+ ['"n"'],
237
+ ['"o"'],
238
+ ['"p"'],
239
+ ['"q"'],
240
+ ['"r"'],
241
+ ['"s"'],
242
+ ['"t"'],
243
+ ['"u"'],
244
+ ['"v"'],
245
+ ['"w"'],
246
+ ['"x"'],
247
+ ['"y"'],
248
+ ['"z"']
249
+ ]
250
+ },
251
+ {
252
+ name: 'RULE_DIGIT',
253
+ bnf: [['"0"'], ['"1"'], ['"2"'], ['"3"'], ['"4"'], ['"5"'], ['"6"'], ['"7"'], ['"8"'], ['"9"']]
254
+ },
255
+ {
256
+ name: 'RULE_SYMBOL',
257
+ bnf: [
258
+ ['"-"'],
259
+ ['"_"'],
260
+ ['"!"'],
261
+ ['"#"'],
262
+ ['"$"'],
263
+ ['"%"'],
264
+ ['"&"'],
265
+ ['"("'],
266
+ ['")"'],
267
+ ['"*"'],
268
+ ['"+"'],
269
+ ['","'],
270
+ ['"-"'],
271
+ ['"."'],
272
+ ['"/"'],
273
+ ['":"'],
274
+ ['";"'],
275
+ ['"<"'],
276
+ ['"="'],
277
+ ['">"'],
278
+ ['"?"'],
279
+ ['"@"'],
280
+ ['"["'],
281
+ ['"\\"'],
282
+ ['"]"'],
283
+ ['"^"'],
284
+ ['"_"'],
285
+ ['"`"'],
286
+ ['"{"'],
287
+ ['"|"'],
288
+ ['"}"'],
289
+ ['"~"']
290
+ ]
291
+ },
292
+ {
293
+ name: 'RULE_CHARACTER1',
294
+ bnf: [['RULE_CHARACTER'], [`"'"`]]
295
+ },
296
+ {
297
+ name: 'RULE_CHARACTER2',
298
+ bnf: [['RULE_CHARACTER'], [`'"'`]]
299
+ },
300
+ {
301
+ name: 'rule-name',
302
+ bnf: [['RULE_LETTER', 'RULE_CHAR*']]
303
+ },
304
+ {
305
+ name: 'RULE_CHAR',
306
+ bnf: [['RULE_LETTER'], ['RULE_DIGIT'], ['"_"'], ['"-"']]
307
+ }
308
+ ];
309
+ BNF.defaultParser = new Parser_1.Parser(BNF.RULES, { debug: false });
310
+ function getAllTerms(expr) {
311
+ let terms = (0, SemanticHelpers_1.findChildrenByType)(expr, 'term').map(term => {
312
+ return (0, SemanticHelpers_1.findChildrenByType)(term, 'literal').concat((0, SemanticHelpers_1.findChildrenByType)(term, 'rule-name'))[0].text;
313
+ });
314
+ for (const exprItem of (0, SemanticHelpers_1.findChildrenByType)(expr, 'list')) {
315
+ terms = terms.concat(getAllTerms(exprItem));
316
+ }
317
+ return terms;
318
+ }
319
+ function getRules(source, parser = BNF.defaultParser) {
320
+ let ast = parser.getAST(source);
321
+ if (!ast)
322
+ throw new Error('Could not parse ' + source);
323
+ if (ast.errors && ast.errors.length) {
324
+ throw ast.errors[0];
325
+ }
326
+ let rules = (0, SemanticHelpers_1.findChildrenByType)(ast, 'rule');
327
+ let ret = rules.map((rule) => {
328
+ let name = (0, SemanticHelpers_1.findChildrenByType)(rule, 'rule-name')[0].text;
329
+ let expressions = (0, SemanticHelpers_1.findChildrenByType)(rule, 'firstExpression').concat((0, SemanticHelpers_1.findChildrenByType)(rule, 'otherExpression'));
330
+ let bnf = [];
331
+ for (const expr of expressions) {
332
+ bnf.push(getAllTerms(expr));
333
+ }
334
+ return {
335
+ name: name,
336
+ bnf
337
+ };
338
+ });
339
+ if (!ret.some(x => x.name == 'EOL')) {
340
+ ret.push({
341
+ name: 'EOL',
342
+ bnf: [['"\\r\\n"', '"\\r"', '"\\n"']]
343
+ });
344
+ }
345
+ return ret;
346
+ }
347
+ BNF.getRules = getRules;
348
+ function Transform(source, subParser = BNF.defaultParser) {
349
+ return getRules(source.join(''), subParser);
350
+ }
351
+ BNF.Transform = Transform;
352
+ class Parser extends Parser_1.Parser {
353
+ constructor(source, options) {
354
+ const subParser = options && options.debugRulesParser === true ? new Parser_1.Parser(BNF.RULES, { debug: true }) : BNF.defaultParser;
355
+ super(getRules(source, subParser), options);
356
+ this.source = source;
357
+ }
358
+ emitSource() {
359
+ return this.source;
360
+ }
361
+ }
362
+ BNF.Parser = Parser;
363
+ })(BNF || (BNF = {}));
364
+ exports.default = BNF;
365
+
366
+ },{"../Parser":6,"../SemanticHelpers":8}],3:[function(require,module,exports){
367
+ "use strict";
368
+ // https://www.w3.org/TR/REC-xml/#NT-Name
369
+ // http://www.bottlecaps.de/rr/ui
370
+ Object.defineProperty(exports, "__esModule", { value: true });
371
+ // Grammar ::= Production*
372
+ // Production ::= NCName '::=' Choice
373
+ // NCName ::= [http://www.w3.org/TR/xml-names/#NT-NCName]
374
+ // Choice ::= SequenceOrDifference ( '|' SequenceOrDifference )*
375
+ // SequenceOrDifference ::= (Item ( '-' Item | Item* ))?
376
+ // Item ::= Primary ( '?' | '*' | '+' )?
377
+ // Primary ::= NCName | StringLiteral | CharCode | CharClass | '(' Choice ')'
378
+ // StringLiteral ::= '"' [^"]* '"' | "'" [^']* "'"
379
+ // CharCode ::= '#x' [0-9a-fA-F]+
380
+ // CharClass ::= '[' '^'? ( RULE_Char | CharCode | CharRange | CharCodeRange )+ ']'
381
+ // RULE_Char ::= [http://www.w3.org/TR/xml#NT-RULE_Char]
382
+ // CharRange ::= RULE_Char '-' ( RULE_Char - ']' )
383
+ // CharCodeRange ::= CharCode '-' CharCode
384
+ // RULE_WHITESPACE ::= RULE_S | Comment
385
+ // RULE_S ::= #x9 | #xA | #xD | #x20
386
+ // Comment ::= '/*' ( [^*] | '*'+ [^*/] )* '*'* '*/'
387
+ const TokenError_1 = require("../TokenError");
388
+ const Parser_1 = require("../Parser");
389
+ var BNF;
390
+ (function (BNF) {
391
+ BNF.RULES = [
392
+ {
393
+ name: 'Grammar',
394
+ bnf: [['RULE_S*', 'Attributes?', 'RULE_S*', '%Atomic*', 'EOF']]
395
+ },
396
+ {
397
+ name: '%Atomic',
398
+ bnf: [['Production', 'RULE_S*']],
399
+ fragment: true
400
+ },
401
+ {
402
+ name: 'Production',
403
+ bnf: [
404
+ [
405
+ 'NCName',
406
+ 'RULE_S*',
407
+ '"::="',
408
+ 'RULE_WHITESPACE*',
409
+ '%Choice',
410
+ 'RULE_WHITESPACE*',
411
+ 'Attributes?',
412
+ 'RULE_EOL+',
413
+ 'RULE_S*'
414
+ ]
415
+ ]
416
+ },
417
+ {
418
+ name: 'NCName',
419
+ bnf: [[/[a-zA-Z][a-zA-Z_0-9]*/]]
420
+ },
421
+ {
422
+ name: 'Attributes',
423
+ bnf: [['"{"', 'Attribute', '%Attributes*', 'RULE_S*', '"}"']]
424
+ },
425
+ {
426
+ name: '%Attributes',
427
+ bnf: [['RULE_S*', '","', 'Attribute']],
428
+ fragment: true
429
+ },
430
+ {
431
+ name: 'Attribute',
432
+ bnf: [['RULE_S*', 'NCName', 'RULE_WHITESPACE*', '"="', 'RULE_WHITESPACE*', 'AttributeValue']]
433
+ },
434
+ {
435
+ name: 'AttributeValue',
436
+ bnf: [['NCName'], [/[1-9][0-9]*/]]
437
+ },
438
+ {
439
+ name: '%Choice',
440
+ bnf: [['SequenceOrDifference', '%_Choice_1*']],
441
+ fragment: true
442
+ },
443
+ {
444
+ name: '%_Choice_1',
445
+ bnf: [['RULE_S*', '"|"', 'RULE_S*', 'SequenceOrDifference']],
446
+ fragment: true
447
+ },
448
+ {
449
+ name: 'SequenceOrDifference',
450
+ bnf: [['%Item', 'RULE_WHITESPACE*', '%_Item_1?']]
451
+ },
452
+ {
453
+ name: '%_Item_1',
454
+ bnf: [['Minus', '%Item'], ['%Item*']],
455
+ fragment: true
456
+ },
457
+ {
458
+ name: 'Minus',
459
+ bnf: [['"-"']]
460
+ },
461
+ {
462
+ name: '%Item',
463
+ bnf: [['RULE_WHITESPACE*', 'PrimaryPreDecoration?', '%Primary', 'PrimaryDecoration?']],
464
+ fragment: true
465
+ },
466
+ {
467
+ name: 'PrimaryDecoration',
468
+ bnf: [['"?"'], ['"*"'], ['"+"']]
469
+ },
470
+ {
471
+ name: 'PrimaryPreDecoration',
472
+ bnf: [['"&"'], ['"!"'], ['"~"']]
473
+ },
474
+ {
475
+ name: '%Primary',
476
+ bnf: [['NCName'], ['StringLiteral'], ['CharCode'], ['CharClass'], ['SubItem']],
477
+ fragment: true
478
+ },
479
+ {
480
+ name: 'SubItem',
481
+ bnf: [['"("', 'RULE_S*', '%Choice', 'RULE_S*', '")"']]
482
+ },
483
+ {
484
+ name: 'StringLiteral',
485
+ bnf: [[`'"'`, /[^"]*/, `'"'`], [`"'"`, /[^']*/, `"'"`]]
486
+ },
487
+ {
488
+ name: 'CharCode',
489
+ bnf: [['"#x"', /[0-9a-zA-Z]+/]]
490
+ },
491
+ {
492
+ name: 'CharClass',
493
+ bnf: [["'['", "'^'?", '%RULE_CharClass_1+', '"]"']]
494
+ },
495
+ {
496
+ name: '%RULE_CharClass_1',
497
+ bnf: [['CharCodeRange'], ['CharRange'], ['CharCode'], ['RULE_Char']],
498
+ fragment: true
499
+ },
500
+ {
501
+ name: 'RULE_Char',
502
+ bnf: [[/\x09/], [/\x0A/], [/\x0D/], [/[\x20-\x5c]/], [/[\x5e-\uD7FF]/], [/[\uE000-\uFFFD]/]]
503
+ },
504
+ {
505
+ name: 'CharRange',
506
+ bnf: [['RULE_Char', '"-"', 'RULE_Char']]
507
+ },
508
+ {
509
+ name: 'CharCodeRange',
510
+ bnf: [['CharCode', '"-"', 'CharCode']]
511
+ },
512
+ {
513
+ name: 'RULE_WHITESPACE',
514
+ bnf: [['%RULE_WHITESPACE_CHAR*'], ['Comment', 'RULE_WHITESPACE*']]
515
+ },
516
+ {
517
+ name: 'RULE_S',
518
+ bnf: [['RULE_WHITESPACE', 'RULE_S*'], ['RULE_EOL', 'RULE_S*']]
519
+ },
520
+ {
521
+ name: '%RULE_WHITESPACE_CHAR',
522
+ bnf: [[/\x09/], [/\x20/]],
523
+ fragment: true
524
+ },
525
+ {
526
+ name: 'Comment',
527
+ bnf: [['"/*"', '%RULE_Comment_Body*', '"*/"']]
528
+ },
529
+ {
530
+ name: '%RULE_Comment_Body',
531
+ bnf: [[/[^*]/], ['"*"+', /[^/]*/]],
532
+ fragment: true
533
+ },
534
+ {
535
+ name: 'RULE_EOL',
536
+ bnf: [[/\x0D/, /\x0A/], [/\x0A/], [/\x0D/]]
537
+ },
538
+ {
539
+ name: 'Link',
540
+ bnf: [["'['", 'Url', "']'"]]
541
+ },
542
+ {
543
+ name: 'Url',
544
+ bnf: [[/[^\x5D:/?#]/, '"://"', /[^\x5D#]+/, '%Url1?']]
545
+ },
546
+ {
547
+ name: '%Url1',
548
+ bnf: [['"#"', 'NCName']],
549
+ fragment: true
550
+ }
551
+ ];
552
+ BNF.defaultParser = new Parser_1.Parser(BNF.RULES, { debug: false });
553
+ const preDecorationRE = /^(!|&)/;
554
+ const decorationRE = /(\?|\+|\*)$/;
555
+ const subExpressionRE = /^%/;
556
+ function getBNFRule(name, parser) {
557
+ if (typeof name == 'string') {
558
+ let decoration = decorationRE.exec(name);
559
+ let preDecoration = preDecorationRE.exec(name);
560
+ let preDecorationText = preDecoration ? preDecoration[0] : '';
561
+ let decorationText = decoration ? decoration[0] + ' ' : '';
562
+ let subexpression = subExpressionRE.test(name);
563
+ if (subexpression) {
564
+ let lonely = isLonelyRule(name, parser);
565
+ if (lonely)
566
+ return preDecorationText + getBNFBody(name, parser) + decorationText;
567
+ return preDecorationText + '(' + getBNFBody(name, parser) + ')' + decorationText;
568
+ }
569
+ return name.replace(preDecorationRE, preDecorationText);
570
+ }
571
+ else {
572
+ return name.source
573
+ .replace(/\\(?:x|u)([a-zA-Z0-9]+)/g, '#x$1')
574
+ .replace(/\[\\(?:x|u)([a-zA-Z0-9]+)-\\(?:x|u)([a-zA-Z0-9]+)\]/g, '[#x$1-#x$2]');
575
+ }
576
+ }
577
+ /// Returns true if the rule is a string literal or regular expression without a descendant tree
578
+ function isLonelyRule(name, parser) {
579
+ let rule = (0, Parser_1.findRuleByName)(name, parser);
580
+ return (rule &&
581
+ rule.bnf.length == 1 &&
582
+ rule.bnf[0].length == 1 &&
583
+ (rule.bnf[0][0] instanceof RegExp || rule.bnf[0][0][0] == '"' || rule.bnf[0][0][0] == "'"));
584
+ }
585
+ function getBNFChoice(rules, parser) {
586
+ return rules.map(x => getBNFRule(x, parser)).join(' ');
587
+ }
588
+ function getBNFBody(name, parser) {
589
+ let rule = (0, Parser_1.findRuleByName)(name, parser);
590
+ if (rule)
591
+ return rule.bnf.map(x => getBNFChoice(x, parser)).join(' | ');
592
+ return 'RULE_NOT_FOUND {' + name + '}';
593
+ }
594
+ function emit(parser) {
595
+ let acumulator = [];
596
+ for (const l of parser.grammarRules) {
597
+ if (!/^%/.test(l.name)) {
598
+ let recover = l.recover ? ' { recoverUntil=' + l.recover + ' }' : '';
599
+ acumulator.push(l.name + ' ::= ' + getBNFBody(l.name, parser) + recover);
600
+ }
601
+ }
602
+ return acumulator.join('\n');
603
+ }
604
+ BNF.emit = emit;
605
+ function restar(total, resta) {
606
+ console.log('reberia restar ' + resta + ' a ' + total);
607
+ throw new Error('Difference not supported yet');
608
+ }
609
+ function convertRegex(txt) {
610
+ return new RegExp(txt
611
+ .replace(/#x([a-zA-Z0-9]{4})/g, '\\u$1')
612
+ .replace(/#x([a-zA-Z0-9]{3})/g, '\\u0$1')
613
+ .replace(/#x([a-zA-Z0-9]{2})/g, '\\x$1')
614
+ .replace(/#x([a-zA-Z0-9]{1})/g, '\\x0$1'));
615
+ }
616
+ function getSubItems(tmpRules, seq, parentName, optionIndex, parentAttributes, isSingleSequence = false) {
617
+ let anterior = null;
618
+ let bnfSeq = [];
619
+ const children = seq.children;
620
+ let subitemIndex = 0; // Track subitems within this sequence
621
+ let itemPosition = 0; // Track all items for position-based naming
622
+ for (let i = 0; i < children.length; i++) {
623
+ const x = children[i];
624
+ if (x.type == 'Minus') {
625
+ restar(anterior, x);
626
+ }
627
+ let decoration = children[i + 1];
628
+ decoration = (decoration && decoration.type == 'PrimaryDecoration' && decoration.text) || '';
629
+ let preDecoration = '';
630
+ if (anterior && anterior.type == 'PrimaryPreDecoration') {
631
+ preDecoration = anterior.text;
632
+ }
633
+ let pinned = preDecoration == '~' ? 1 : undefined;
634
+ if (pinned) {
635
+ preDecoration = '';
636
+ }
637
+ switch (x.type) {
638
+ case 'SubItem':
639
+ subitemIndex++;
640
+ itemPosition++; // Increment position for SubItems
641
+ let name;
642
+ // Build the fragment name by appending to parentName
643
+ const prefix = parentName.startsWith('%') ? '' : '%';
644
+ if (isSingleSequence) {
645
+ // Single sequence: use position-based naming
646
+ name = prefix + parentName + '[' + itemPosition + ']';
647
+ }
648
+ else {
649
+ // Multiple sequences: use option-based naming
650
+ if (subitemIndex === 1) {
651
+ name = prefix + parentName + '[' + optionIndex + ']';
652
+ }
653
+ else {
654
+ name = prefix + parentName + '[' + optionIndex + '][' + subitemIndex + ']';
655
+ }
656
+ }
657
+ createRule(tmpRules, x, name, parentAttributes);
658
+ bnfSeq.push(preDecoration + name + decoration);
659
+ break;
660
+ case 'NCName':
661
+ itemPosition++; // Increment position for NCNames
662
+ bnfSeq.push(preDecoration + x.text + decoration);
663
+ break;
664
+ case 'StringLiteral':
665
+ itemPosition++; // Increment position for StringLiterals
666
+ if (decoration || preDecoration || !/^['"/()a-zA-Z0-9&_.:=,+*\-\^\\]+$/.test(x.text)) {
667
+ bnfSeq.push(preDecoration + x.text + decoration);
668
+ }
669
+ else {
670
+ for (const c of x.text.slice(1, -1)) {
671
+ if (parentAttributes && parentAttributes["ignoreCase"] == "true" && /[a-zA-Z]/.test(c)) {
672
+ bnfSeq.push(new RegExp("[" + c.toUpperCase() + c.toLowerCase() + "]"));
673
+ }
674
+ else {
675
+ bnfSeq.push(new RegExp((0, Parser_1.escapeRegExp)(c)));
676
+ }
677
+ }
678
+ }
679
+ break;
680
+ case 'CharCode':
681
+ case 'CharClass':
682
+ itemPosition++; // Increment position for CharCode/CharClass
683
+ if (decoration || preDecoration) {
684
+ subitemIndex++;
685
+ let name;
686
+ // Build the fragment name by appending to parentName
687
+ const prefix = parentName.startsWith('%') ? '' : '%';
688
+ if (isSingleSequence) {
689
+ // Single sequence: use position-based naming
690
+ name = prefix + parentName + '[' + itemPosition + ']';
691
+ }
692
+ else {
693
+ // Multiple sequences: use option-based naming
694
+ if (subitemIndex === 1) {
695
+ name = prefix + parentName + '[' + optionIndex + ']';
696
+ }
697
+ else {
698
+ name = prefix + parentName + '[' + optionIndex + '][' + subitemIndex + ']';
699
+ }
700
+ }
701
+ let newRule = {
702
+ name,
703
+ bnf: [[convertRegex(x.text)]],
704
+ pinned
705
+ };
706
+ tmpRules.push(newRule);
707
+ bnfSeq.push(preDecoration + newRule.name + decoration);
708
+ }
709
+ else {
710
+ bnfSeq.push(convertRegex(x.text));
711
+ }
712
+ break;
713
+ case 'PrimaryPreDecoration':
714
+ case 'PrimaryDecoration':
715
+ break;
716
+ default:
717
+ throw new Error(' HOW SHOULD I PARSE THIS? ' + x.type + ' -> ' + JSON.stringify(x.text));
718
+ }
719
+ anterior = x;
720
+ }
721
+ return bnfSeq;
722
+ }
723
+ function createRule(tmpRules, token, name, parentAttributes = undefined) {
724
+ let attrNode = token.children.filter(x => x.type == 'Attributes')[0];
725
+ let attributes = {};
726
+ if (attrNode) {
727
+ for (const x of attrNode.children) {
728
+ let attrName = x.children.filter(x => x.type == 'NCName')[0].text;
729
+ if (attrName in attributes) {
730
+ throw new TokenError_1.TokenError('Duplicated attribute ' + attrName, x);
731
+ }
732
+ else {
733
+ attributes[attrName] = x.children.filter(x => x.type == 'AttributeValue')[0].text;
734
+ }
735
+ }
736
+ }
737
+ let sequences = token.children.filter(x => x.type == 'SequenceOrDifference');
738
+ // Determine if this rule has a single sequence (no alternatives)
739
+ const isSingleSequence = sequences.length === 1;
740
+ let bnf = sequences.map((s, optionIndex) => getSubItems(tmpRules, s, name, optionIndex + 1, parentAttributes ? parentAttributes : attributes, isSingleSequence));
741
+ let rule = {
742
+ name,
743
+ bnf
744
+ };
745
+ if (name.indexOf('%') == 0)
746
+ rule.fragment = true;
747
+ if (attributes['recoverUntil']) {
748
+ rule.recover = attributes['recoverUntil'];
749
+ if (rule.bnf.length > 1)
750
+ throw new TokenError_1.TokenError('only one-option productions are suitable for error recovering', token);
751
+ }
752
+ if ('pin' in attributes) {
753
+ let num = parseInt(attributes['pin']);
754
+ if (!isNaN(num)) {
755
+ rule.pinned = num;
756
+ }
757
+ if (rule.bnf.length > 1)
758
+ throw new TokenError_1.TokenError('only one-option productions are suitable for pinning', token);
759
+ }
760
+ if ('ws' in attributes) {
761
+ rule.implicitWs = attributes['ws'] != 'explicit';
762
+ }
763
+ else {
764
+ rule.implicitWs = null;
765
+ }
766
+ rule.fragment = rule.fragment || attributes['fragment'] == 'true';
767
+ rule.simplifyWhenOneChildren = attributes['simplifyWhenOneChildren'] == 'true';
768
+ tmpRules.push(rule);
769
+ }
770
+ function getRules(source, parser = BNF.defaultParser) {
771
+ let ast = parser.getAST(source);
772
+ if (!ast)
773
+ throw new Error('Could not parse ' + source);
774
+ if (ast.errors && ast.errors.length) {
775
+ throw ast.errors[0];
776
+ }
777
+ let implicitWs = null;
778
+ let attrNode = ast.children.filter(x => x.type == 'Attributes')[0];
779
+ let attributes = {};
780
+ if (attrNode) {
781
+ for (const x of attrNode.children) {
782
+ let attrName = x.children.filter(x => x.type == 'NCName')[0].text;
783
+ if (attrName in attributes) {
784
+ throw new TokenError_1.TokenError('Duplicated attribute ' + attrName, x);
785
+ }
786
+ else {
787
+ attributes[attrName] = x.children.filter(x => x.type == 'AttributeValue')[0].text;
788
+ }
789
+ }
790
+ }
791
+ implicitWs = attributes['ws'] == 'implicit';
792
+ let tmpRules = [];
793
+ ast.children.filter(x => x.type == 'Production').map((x) => {
794
+ let name = x.children.filter(x => x.type == 'NCName')[0].text;
795
+ createRule(tmpRules, x, name);
796
+ });
797
+ for (const rule of tmpRules) {
798
+ if (rule.implicitWs === null)
799
+ rule.implicitWs = implicitWs;
800
+ }
801
+ return tmpRules;
802
+ }
803
+ BNF.getRules = getRules;
804
+ function Transform(source, subParser = BNF.defaultParser) {
805
+ return getRules(source.join(''), subParser);
806
+ }
807
+ BNF.Transform = Transform;
808
+ class Parser extends Parser_1.Parser {
809
+ constructor(source, options) {
810
+ const subParser = options && options.debugRulesParser === true ? new Parser_1.Parser(BNF.RULES, { debug: true }) : BNF.defaultParser;
811
+ super(getRules(source, subParser), options);
812
+ }
813
+ emitSource() {
814
+ return emit(this);
815
+ }
816
+ }
817
+ BNF.Parser = Parser;
818
+ })(BNF || (BNF = {}));
819
+ exports.default = BNF;
820
+
821
+ },{"../Parser":6,"../TokenError":9}],4:[function(require,module,exports){
822
+ "use strict";
823
+ // https://www.w3.org/TR/REC-xml/#NT-Name
824
+ // http://www.bottlecaps.de/rr/ui
825
+ Object.defineProperty(exports, "__esModule", { value: true });
826
+ // Grammar ::= Production*
827
+ // Production ::= NCName '::=' Choice
828
+ // NCName ::= [http://www.w3.org/TR/xml-names/#NT-NCName]
829
+ // Choice ::= SequenceOrDifference ( '|' SequenceOrDifference )*
830
+ // SequenceOrDifference ::= (Item ( '-' Item | Item* ))?
831
+ // Item ::= Primary ( '?' | '*' | '+' )?
832
+ // Primary ::= NCName | StringLiteral | CharCode | CharClass | '(' Choice ')'
833
+ // StringLiteral ::= '"' [^"]* '"' | "'" [^']* "'"
834
+ // CharCode ::= '#x' [0-9a-fA-F]+
835
+ // CharClass ::= '[' '^'? ( RULE_Char | CharCode | CharRange | CharCodeRange )+ ']'
836
+ // RULE_Char ::= [http://www.w3.org/TR/xml#NT-RULE_Char]
837
+ // CharRange ::= RULE_Char '-' ( RULE_Char - ']' )
838
+ // CharCodeRange ::= CharCode '-' CharCode
839
+ // RULE_WHITESPACE ::= RULE_S | Comment
840
+ // RULE_S ::= #x9 | #xA | #xD | #x20
841
+ // Comment ::= '/*' ( [^*] | '*'+ [^*/] )* '*'* '*/'
842
+ const Parser_1 = require("../Parser");
843
+ var BNF;
844
+ (function (BNF) {
845
+ BNF.RULES = [
846
+ {
847
+ name: 'Grammar',
848
+ bnf: [['RULE_S*', '%Atomic*', 'EOF']]
849
+ },
850
+ {
851
+ name: '%Atomic',
852
+ bnf: [['Production', 'RULE_S*']],
853
+ fragment: true
854
+ },
855
+ {
856
+ name: 'Production',
857
+ bnf: [['NCName', 'RULE_S*', 'ProductionOperator', 'RULE_WHITESPACE*', 'Choice', 'RULE_WHITESPACE*', 'RULE_EOL+', 'RULE_S*']]
858
+ },
859
+ {
860
+ name: 'ProductionOperator',
861
+ bnf: [['"::="'], ['"||="']]
862
+ },
863
+ {
864
+ name: 'NCName',
865
+ bnf: [[/[a-zA-Z][a-zA-Z_0-9]*/]]
866
+ },
867
+ {
868
+ name: 'Choice',
869
+ bnf: [['SequenceOrDifference', '%_Choice_1*']],
870
+ fragment: true
871
+ },
872
+ {
873
+ name: '%_Choice_1',
874
+ bnf: [['RULE_WHITESPACE*', '"|"', 'RULE_WHITESPACE*', 'SequenceOrDifference']],
875
+ fragment: true
876
+ },
877
+ {
878
+ name: 'SequenceOrDifference',
879
+ bnf: [['Item', 'RULE_WHITESPACE*', '%_Item_1?']]
880
+ },
881
+ {
882
+ name: '%_Item_1',
883
+ bnf: [['Minus', 'Item'], ['Item*']],
884
+ fragment: true
885
+ },
886
+ {
887
+ name: 'Minus',
888
+ bnf: [['"-"']]
889
+ },
890
+ {
891
+ name: 'Item',
892
+ bnf: [['RULE_WHITESPACE*', '%Primary', 'PrimaryDecoration?']],
893
+ fragment: true
894
+ },
895
+ {
896
+ name: 'PrimaryDecoration',
897
+ bnf: [['"?"'], ['"*"'], ['"+"']]
898
+ },
899
+ {
900
+ name: 'DecorationName',
901
+ bnf: [['"ebnf://"', /[^\x5D#]+/]]
902
+ },
903
+ {
904
+ name: '%Primary',
905
+ bnf: [['NCName'], ['StringLiteral'], ['CharCode'], ['CharClass'], ['SubItem']],
906
+ fragment: true
907
+ },
908
+ {
909
+ name: 'SubItem',
910
+ bnf: [['"("', 'RULE_WHITESPACE*', 'Choice', 'RULE_WHITESPACE*', '")"']]
911
+ },
912
+ {
913
+ name: 'StringLiteral',
914
+ bnf: [[`'"'`, /[^"]*/, `'"'`], [`"'"`, /[^']*/, `"'"`]],
915
+ pinned: 1
916
+ },
917
+ {
918
+ name: 'CharCode',
919
+ bnf: [['"#x"', /[0-9a-zA-Z]+/]]
920
+ },
921
+ {
922
+ name: 'CharClass',
923
+ bnf: [["'['", "'^'?", '%RULE_CharClass_1+', '"]"']]
924
+ },
925
+ {
926
+ name: '%RULE_CharClass_1',
927
+ bnf: [['CharCodeRange'], ['CharRange'], ['CharCode'], ['RULE_Char']],
928
+ fragment: true
929
+ },
930
+ {
931
+ name: 'RULE_Char',
932
+ bnf: [[/\x09/], [/\x0A/], [/\x0D/], [/[\x20-\x5c]/], [/[\x5e-\uD7FF]/], [/[\uE000-\uFFFD]/]]
933
+ },
934
+ {
935
+ name: 'CharRange',
936
+ bnf: [['RULE_Char', '"-"', 'RULE_Char']]
937
+ },
938
+ {
939
+ name: 'CharCodeRange',
940
+ bnf: [['CharCode', '"-"', 'CharCode']]
941
+ },
942
+ {
943
+ name: 'RULE_WHITESPACE',
944
+ bnf: [['%RULE_WHITESPACE_CHAR*'], ['Comment', 'RULE_WHITESPACE*']]
945
+ },
946
+ {
947
+ name: 'RULE_S',
948
+ bnf: [['RULE_WHITESPACE', 'RULE_S*'], ['RULE_EOL', 'RULE_S*']]
949
+ },
950
+ {
951
+ name: '%RULE_WHITESPACE_CHAR',
952
+ bnf: [[/\x09/], [/\x20/]],
953
+ fragment: true
954
+ },
955
+ {
956
+ name: 'Comment',
957
+ bnf: [['"/*"', '%RULE_Comment_Body*', '"*/"']]
958
+ },
959
+ {
960
+ name: '%RULE_Comment_Body',
961
+ bnf: [['!"*/"', /[^*]/]],
962
+ fragment: true
963
+ },
964
+ {
965
+ name: 'RULE_EOL',
966
+ bnf: [[/\x0D/, /\x0A/], [/\x0A/], [/\x0D/]]
967
+ },
968
+ {
969
+ name: 'Link',
970
+ bnf: [["'['", 'Url', "']'"]]
971
+ },
972
+ {
973
+ name: 'Url',
974
+ bnf: [[/[^\x5D:/?#]/, '"://"', /[^\x5D#]+/, '%Url1?']]
975
+ },
976
+ {
977
+ name: '%Url1',
978
+ bnf: [['"#"', 'NCName']],
979
+ fragment: true
980
+ }
981
+ ];
982
+ BNF.defaultParser = new Parser_1.Parser(BNF.RULES, { debug: false });
983
+ const preDecorationRE = /^(!|&)/;
984
+ const decorationRE = /(\?|\+|\*)$/;
985
+ const subExpressionRE = /^%/;
986
+ function getBNFRule(name, parser) {
987
+ if (typeof name == 'string') {
988
+ if (preDecorationRE.test(name))
989
+ return '';
990
+ let subexpression = subExpressionRE.test(name);
991
+ if (subexpression) {
992
+ let decoration = decorationRE.exec(name);
993
+ let decorationText = decoration ? decoration[0] + ' ' : '';
994
+ let lonely = isLonelyRule(name, parser);
995
+ if (lonely)
996
+ return getBNFBody(name, parser) + decorationText;
997
+ return '(' + getBNFBody(name, parser) + ')' + decorationText;
998
+ }
999
+ return name;
1000
+ }
1001
+ else {
1002
+ return name.source
1003
+ .replace(/\\(?:x|u)([a-zA-Z0-9]+)/g, '#x$1')
1004
+ .replace(/\[\\(?:x|u)([a-zA-Z0-9]+)-\\(?:x|u)([a-zA-Z0-9]+)\]/g, '[#x$1-#x$2]');
1005
+ }
1006
+ }
1007
+ /// Returns true if the rule is a string literal or regular expression without a descendant tree
1008
+ function isLonelyRule(name, parser) {
1009
+ let rule = (0, Parser_1.findRuleByName)(name, parser);
1010
+ return (rule &&
1011
+ rule.bnf.length == 1 &&
1012
+ rule.bnf[0].length == 1 &&
1013
+ (rule.bnf[0][0] instanceof RegExp || rule.bnf[0][0][0] == '"' || rule.bnf[0][0][0] == "'"));
1014
+ }
1015
+ function getBNFChoice(rules, parser) {
1016
+ return rules.map(x => getBNFRule(x, parser)).join(' ');
1017
+ }
1018
+ function getBNFBody(name, parser) {
1019
+ let rule = (0, Parser_1.findRuleByName)(name, parser);
1020
+ if (rule)
1021
+ return rule.bnf.map(x => getBNFChoice(x, parser)).join(' | ');
1022
+ return 'RULE_NOT_FOUND {' + name + '}';
1023
+ }
1024
+ function emit(parser) {
1025
+ let acumulator = [];
1026
+ for (const l of parser.grammarRules) {
1027
+ if (!/^%/.test(l.name)) {
1028
+ let recover = l.recover ? ' /* { recoverUntil=' + l.recover + ' } */' : '';
1029
+ acumulator.push(l.name + ' ::= ' + getBNFBody(l.name, parser) + recover);
1030
+ }
1031
+ }
1032
+ return acumulator.join('\n');
1033
+ }
1034
+ BNF.emit = emit;
1035
+ function restar(total, resta) {
1036
+ console.log('reberia restar ' + resta + ' a ' + total);
1037
+ throw new Error('Difference not supported yet');
1038
+ }
1039
+ function convertRegex(txt, caseInsensitive = false) {
1040
+ const pattern = txt
1041
+ .replace(/#x([a-zA-Z0-9]{4})/g, '\\u$1')
1042
+ .replace(/#x([a-zA-Z0-9]{3})/g, '\\u0$1')
1043
+ .replace(/#x([a-zA-Z0-9]{2})/g, '\\x$1')
1044
+ .replace(/#x([a-zA-Z0-9]{1})/g, '\\x0$1');
1045
+ return new RegExp(pattern, caseInsensitive ? 'i' : '');
1046
+ }
1047
+ function getSubItems(tmpRules, seq, parentName, optionIndex, caseInsensitive = false, isSingleSequence = false) {
1048
+ let anterior = null;
1049
+ let bnfSeq = [];
1050
+ const children = seq.children;
1051
+ let subitemIndex = 0; // Track subitems within this sequence
1052
+ let itemPosition = 0; // Track all items (including literals, NCNames, SubItems) for position-based naming
1053
+ for (let i = 0; i < children.length; i++) {
1054
+ const x = children[i];
1055
+ if (x.type == 'Minus') {
1056
+ restar(anterior, x);
1057
+ }
1058
+ let decoration = children[i + 1];
1059
+ decoration = (decoration && decoration.type == 'PrimaryDecoration' && decoration.text) || '';
1060
+ let preDecoration = '';
1061
+ switch (x.type) {
1062
+ case 'SubItem':
1063
+ subitemIndex++;
1064
+ itemPosition++; // Increment position for SubItems
1065
+ let name;
1066
+ // Build the fragment name by appending to parentName
1067
+ const prefix = parentName.startsWith('%') ? '' : '%';
1068
+ if (isSingleSequence) {
1069
+ // Single sequence: use position-based naming
1070
+ name = prefix + parentName + '[' + itemPosition + ']';
1071
+ }
1072
+ else {
1073
+ // Multiple sequences: use option-based naming
1074
+ if (subitemIndex === 1) {
1075
+ name = prefix + parentName + '[' + optionIndex + ']';
1076
+ }
1077
+ else {
1078
+ name = prefix + parentName + '[' + optionIndex + '][' + subitemIndex + ']';
1079
+ }
1080
+ }
1081
+ createRule(tmpRules, x, name, caseInsensitive);
1082
+ bnfSeq.push(preDecoration + name + decoration);
1083
+ break;
1084
+ case 'NCName':
1085
+ itemPosition++; // Increment position for NCNames
1086
+ bnfSeq.push(preDecoration + x.text + decoration);
1087
+ break;
1088
+ case 'StringLiteral':
1089
+ itemPosition++; // Increment position for StringLiterals
1090
+ if (caseInsensitive) {
1091
+ // For case insensitive string literals, convert each character to a case-insensitive regex
1092
+ const literalText = x.text.slice(1, -1); // Remove quotes
1093
+ for (const c of literalText) {
1094
+ if (/[a-zA-Z]/.test(c)) {
1095
+ bnfSeq.push(new RegExp('[' + c.toUpperCase() + c.toLowerCase() + ']'));
1096
+ }
1097
+ else {
1098
+ bnfSeq.push(new RegExp((0, Parser_1.escapeRegExp)(c)));
1099
+ }
1100
+ }
1101
+ }
1102
+ else {
1103
+ bnfSeq.push(preDecoration + x.text + decoration);
1104
+ }
1105
+ break;
1106
+ case 'CharCode':
1107
+ case 'CharClass':
1108
+ itemPosition++; // Increment position for CharCode/CharClass
1109
+ if (decoration || preDecoration) {
1110
+ subitemIndex++;
1111
+ let name;
1112
+ // Build the fragment name by appending to parentName
1113
+ const prefix = parentName.startsWith('%') ? '' : '%';
1114
+ if (isSingleSequence) {
1115
+ // Single sequence: use position-based naming
1116
+ name = prefix + parentName + '[' + itemPosition + ']';
1117
+ }
1118
+ else {
1119
+ // Multiple sequences: use option-based naming
1120
+ if (subitemIndex === 1) {
1121
+ name = prefix + parentName + '[' + optionIndex + ']';
1122
+ }
1123
+ else {
1124
+ name = prefix + parentName + '[' + optionIndex + '][' + subitemIndex + ']';
1125
+ }
1126
+ }
1127
+ let newRule = {
1128
+ name,
1129
+ bnf: [[convertRegex(x.text, caseInsensitive)]]
1130
+ };
1131
+ tmpRules.push(newRule);
1132
+ bnfSeq.push(preDecoration + newRule.name + decoration);
1133
+ }
1134
+ else {
1135
+ bnfSeq.push(convertRegex(x.text, caseInsensitive));
1136
+ }
1137
+ break;
1138
+ case 'PrimaryDecoration':
1139
+ break;
1140
+ default:
1141
+ throw new Error(' HOW SHOULD I PARSE THIS? ' + x.type + ' -> ' + JSON.stringify(x.text));
1142
+ }
1143
+ anterior = x;
1144
+ }
1145
+ return bnfSeq;
1146
+ }
1147
+ function createRule(tmpRules, token, name, caseInsensitive = false) {
1148
+ // Check if this production uses the case insensitive operator ||=
1149
+ const operatorNode = token.children.find(x => x.type == 'ProductionOperator');
1150
+ const isCaseInsensitive = caseInsensitive || (operatorNode && operatorNode.text === '||=');
1151
+ let sequences = token.children.filter(x => x.type == 'SequenceOrDifference');
1152
+ // Determine if this rule has a single sequence (no alternatives)
1153
+ const isSingleSequence = sequences.length === 1;
1154
+ let bnf = sequences.map((s, optionIndex) => getSubItems(tmpRules, s, name, optionIndex + 1, isCaseInsensitive, isSingleSequence));
1155
+ let rule = {
1156
+ name,
1157
+ bnf
1158
+ };
1159
+ let recover = null;
1160
+ for (const x of bnf) {
1161
+ recover = recover || x['recover'];
1162
+ delete x['recover'];
1163
+ }
1164
+ if (name.indexOf('%') == 0)
1165
+ rule.fragment = true;
1166
+ if (recover)
1167
+ rule.recover = recover;
1168
+ tmpRules.push(rule);
1169
+ }
1170
+ function getRules(source, parser = BNF.defaultParser) {
1171
+ let ast = parser.getAST(source);
1172
+ if (!ast)
1173
+ throw new Error('Could not parse ' + source);
1174
+ if (ast.errors && ast.errors.length) {
1175
+ throw ast.errors[0];
1176
+ }
1177
+ let tmpRules = [];
1178
+ ast.children.filter(x => x.type == 'Production').map((x) => {
1179
+ let name = x.children.filter(x => x.type == 'NCName')[0].text;
1180
+ createRule(tmpRules, x, name);
1181
+ });
1182
+ return tmpRules;
1183
+ }
1184
+ BNF.getRules = getRules;
1185
+ function Transform(source, subParser = BNF.defaultParser) {
1186
+ return getRules(source.join(''), subParser);
1187
+ }
1188
+ BNF.Transform = Transform;
1189
+ class Parser extends Parser_1.Parser {
1190
+ constructor(source, options) {
1191
+ const subParser = options && options.debugRulesParser === true ? new Parser_1.Parser(BNF.RULES, { debug: true }) : BNF.defaultParser;
1192
+ super(getRules(source, subParser), options);
1193
+ }
1194
+ emitSource() {
1195
+ return emit(this);
1196
+ }
1197
+ }
1198
+ BNF.Parser = Parser;
1199
+ })(BNF || (BNF = {}));
1200
+ exports.default = BNF;
1201
+
1202
+ },{"../Parser":6}],5:[function(require,module,exports){
1203
+ "use strict";
1204
+ Object.defineProperty(exports, "__esModule", { value: true });
1205
+ exports.Custom = exports.W3C = exports.BNF = void 0;
1206
+ var BNF_1 = require("./BNF");
1207
+ Object.defineProperty(exports, "BNF", { enumerable: true, get: function () { return BNF_1.default; } });
1208
+ var W3CEBNF_1 = require("./W3CEBNF");
1209
+ Object.defineProperty(exports, "W3C", { enumerable: true, get: function () { return W3CEBNF_1.default; } });
1210
+ var Custom_1 = require("./Custom");
1211
+ Object.defineProperty(exports, "Custom", { enumerable: true, get: function () { return Custom_1.default; } });
1212
+
1213
+ },{"./BNF":2,"./Custom":3,"./W3CEBNF":4}],6:[function(require,module,exports){
1214
+ "use strict";
1215
+ // https://www.ics.uci.edu/~pattis/ICS-33/lectures/ebnf.pdf
1216
+ Object.defineProperty(exports, "__esModule", { value: true });
1217
+ exports.Parser = void 0;
1218
+ exports.readToken = readToken;
1219
+ exports.escapeRegExp = escapeRegExp;
1220
+ exports.parseRuleName = parseRuleName;
1221
+ exports.findRuleByName = findRuleByName;
1222
+ const UPPER_SNAKE_RE = /^[A-Z0-9_]+$/;
1223
+ const decorationRE = /(\?|\+|\*)$/;
1224
+ const preDecorationRE = /^(@|&|!)/;
1225
+ const WS_RULE = 'WS';
1226
+ const TokenError_1 = require("./TokenError");
1227
+ const ParsingError_1 = require("./ParsingError");
1228
+ function readToken(txt, expr) {
1229
+ let result = expr.exec(txt);
1230
+ if (result && result.index == 0) {
1231
+ if (result[0].length == 0 && expr.source.length > 0)
1232
+ return null;
1233
+ return {
1234
+ type: null,
1235
+ text: result[0],
1236
+ rest: txt.substr(result[0].length),
1237
+ start: 0,
1238
+ end: result[0].length - 1,
1239
+ fullText: result[0],
1240
+ errors: [],
1241
+ children: [],
1242
+ parent: null
1243
+ };
1244
+ }
1245
+ return null;
1246
+ }
1247
+ function escapeRegExp(str) {
1248
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
1249
+ }
1250
+ function fixRest(token) {
1251
+ token.rest = '';
1252
+ if (token.children) {
1253
+ for (const c of token.children) {
1254
+ fixRest(c);
1255
+ }
1256
+ }
1257
+ }
1258
+ function fixPositions(token, start) {
1259
+ token.start += start;
1260
+ token.end += start;
1261
+ if (token.children) {
1262
+ for (const c of token.children) {
1263
+ fixPositions(c, token.start);
1264
+ }
1265
+ }
1266
+ }
1267
+ function agregateErrors(errors, token) {
1268
+ if (token.errors && token.errors.length) {
1269
+ for (const err of token.errors) {
1270
+ errors.push(err);
1271
+ }
1272
+ }
1273
+ if (token.children) {
1274
+ for (const tok of token.children) {
1275
+ agregateErrors(errors, tok);
1276
+ }
1277
+ }
1278
+ }
1279
+ function parseRuleName(name) {
1280
+ let postDecoration = decorationRE.exec(name);
1281
+ let preDecoration = preDecorationRE.exec(name);
1282
+ let postDecorationText = (postDecoration && postDecoration[0]) || '';
1283
+ let preDecorationText = (preDecoration && preDecoration[0]) || '';
1284
+ let out = {
1285
+ raw: name,
1286
+ name: name.replace(decorationRE, '').replace(preDecorationRE, ''),
1287
+ isOptional: postDecorationText == '?' || postDecorationText == '*',
1288
+ allowRepetition: postDecorationText == '+' || postDecorationText == '*',
1289
+ atLeastOne: postDecorationText == '+',
1290
+ lookupPositive: preDecorationText == '&',
1291
+ lookupNegative: preDecorationText == '!',
1292
+ pinned: preDecorationText == '@',
1293
+ lookup: false,
1294
+ isLiteral: false
1295
+ };
1296
+ out.isLiteral = out.name[0] == "'" || out.name[0] == '"';
1297
+ out.lookup = out.lookupNegative || out.lookupPositive;
1298
+ return out;
1299
+ }
1300
+ function findRuleByName(name, parser) {
1301
+ let parsed = parseRuleName(name);
1302
+ return parser.cachedRules[parsed.name] || null;
1303
+ }
1304
+ /// Removes all the nodes starting with 'RULE_'
1305
+ function stripRules(token, re) {
1306
+ if (token.children) {
1307
+ let localRules = token.children.filter(x => x.type && re.test(x.type));
1308
+ for (let i = 0; i < localRules.length; i++) {
1309
+ let indexOnChildren = token.children.indexOf(localRules[i]);
1310
+ if (indexOnChildren != -1) {
1311
+ token.children.splice(indexOnChildren, 1);
1312
+ }
1313
+ }
1314
+ for (const c of token.children) {
1315
+ stripRules(c, re);
1316
+ }
1317
+ }
1318
+ }
1319
+ const ignoreMissingRules = ['EOF'];
1320
+ class Parser {
1321
+ constructor(grammarRules, options) {
1322
+ this.grammarRules = grammarRules;
1323
+ this.options = options;
1324
+ this.furthestFailure = null;
1325
+ this.originalInput = '';
1326
+ this.parseStack = []; // Track parsing context
1327
+ this.cachedRules = {};
1328
+ this.debug = options ? options.debug === true : false;
1329
+ let errors = [];
1330
+ let neededRules = [];
1331
+ for (const rule of grammarRules) {
1332
+ let parsedName = parseRuleName(rule.name);
1333
+ if (parsedName.name in this.cachedRules) {
1334
+ errors.push('Duplicated rule ' + parsedName.name);
1335
+ continue;
1336
+ }
1337
+ else {
1338
+ this.cachedRules[parsedName.name] = rule;
1339
+ }
1340
+ if (!rule.bnf || !rule.bnf.length) {
1341
+ let error = 'Missing rule content, rule: ' + rule.name;
1342
+ if (errors.indexOf(error) == -1)
1343
+ errors.push(error);
1344
+ }
1345
+ else {
1346
+ for (const options of rule.bnf) {
1347
+ if (typeof options[0] === 'string') {
1348
+ let parsed = parseRuleName(options[0]);
1349
+ if (parsed.name == rule.name) {
1350
+ let error = 'Left recursion is not allowed, rule: ' + rule.name;
1351
+ if (errors.indexOf(error) == -1)
1352
+ errors.push(error);
1353
+ }
1354
+ }
1355
+ for (const option of options) {
1356
+ if (typeof option == 'string') {
1357
+ let name = parseRuleName(option);
1358
+ if (!name.isLiteral &&
1359
+ neededRules.indexOf(name.name) == -1 &&
1360
+ ignoreMissingRules.indexOf(name.name) == -1)
1361
+ neededRules.push(name.name);
1362
+ }
1363
+ }
1364
+ }
1365
+ }
1366
+ if (WS_RULE == rule.name)
1367
+ rule.implicitWs = false;
1368
+ if (rule.implicitWs) {
1369
+ if (neededRules.indexOf(WS_RULE) == -1)
1370
+ neededRules.push(WS_RULE);
1371
+ }
1372
+ if (rule.recover) {
1373
+ if (neededRules.indexOf(rule.recover) == -1)
1374
+ neededRules.push(rule.recover);
1375
+ }
1376
+ }
1377
+ for (const ruleName of neededRules) {
1378
+ if (!(ruleName in this.cachedRules)) {
1379
+ errors.push('Missing rule ' + ruleName);
1380
+ }
1381
+ }
1382
+ if (errors.length)
1383
+ throw new Error(errors.join('\n'));
1384
+ }
1385
+ getAST(txt, target) {
1386
+ if (!target) {
1387
+ target = this.grammarRules.filter(x => !x.fragment && x.name.indexOf('%') != 0)[0].name;
1388
+ }
1389
+ // Reset failure tracking for each new parse
1390
+ this.furthestFailure = null;
1391
+ this.originalInput = txt;
1392
+ this.parseStack = [];
1393
+ let result = this.parse(txt, target, 0, 0);
1394
+ if (result) {
1395
+ agregateErrors(result.errors, result);
1396
+ fixPositions(result, 0);
1397
+ // REMOVE ALL THE TAGS MATCHING /^%/
1398
+ stripRules(result, /^%/);
1399
+ if (!this.options || !this.options.keepUpperRules)
1400
+ stripRules(result, UPPER_SNAKE_RE);
1401
+ let rest = result.rest;
1402
+ if (rest) {
1403
+ new TokenError_1.TokenError('Unexpected end of input: \n' + rest, result);
1404
+ }
1405
+ fixRest(result);
1406
+ result.rest = rest;
1407
+ }
1408
+ else {
1409
+ // Parsing failed completely - throw ParsingError
1410
+ if (this.furthestFailure) {
1411
+ const position = this.calculatePosition(this.originalInput, this.furthestFailure.offset);
1412
+ const found = this.furthestFailure.found;
1413
+ // Extract parent-most rules first
1414
+ const parentMostRules = this.extractParentMostRules(this.furthestFailure.tree);
1415
+ // Build failure tree starting from the parent-most failing rules
1416
+ const failureTree = this.buildFailureTree(this.furthestFailure.tree, parentMostRules, this.furthestFailure.regexMap);
1417
+ throw new ParsingError_1.ParsingError('Failed to parse input', position, parentMostRules, found, failureTree);
1418
+ }
1419
+ else {
1420
+ // Fallback if no failure was tracked
1421
+ throw new ParsingError_1.ParsingError('Failed to parse input', { offset: 0, line: 1, column: 1 }, [target], txt.length > 0 ? txt.charAt(0) : 'end of input');
1422
+ }
1423
+ }
1424
+ return result;
1425
+ }
1426
+ emitSource() {
1427
+ return 'CANNOT EMIT SOURCE FROM BASE Parser';
1428
+ }
1429
+ calculatePosition(txt, offset) {
1430
+ let line = 1;
1431
+ let column = 1;
1432
+ for (let i = 0; i < offset && i < txt.length; i++) {
1433
+ // Handle \r\n as a single line ending
1434
+ if (txt[i] === '\r' && i + 1 < txt.length && txt[i + 1] === '\n') {
1435
+ line++;
1436
+ column = 1;
1437
+ i++; // Skip the \n
1438
+ }
1439
+ else if (txt[i] === '\n' || txt[i] === '\r') {
1440
+ line++;
1441
+ column = 1;
1442
+ }
1443
+ else {
1444
+ column++;
1445
+ }
1446
+ }
1447
+ return { offset, line, column };
1448
+ }
1449
+ recordFailure(offset, expected) {
1450
+ const expectedKey = expected instanceof RegExp ? expected.source : expected;
1451
+ if (!this.furthestFailure || offset > this.furthestFailure.offset) {
1452
+ // This is a new furthest failure
1453
+ let found;
1454
+ if (offset >= this.originalInput.length) {
1455
+ found = 'end of input';
1456
+ }
1457
+ else {
1458
+ // Extract a meaningful token/substring from the failure position
1459
+ const remaining = this.originalInput.substring(offset);
1460
+ // Skip whitespace using replace
1461
+ const trimmed = remaining.replace(/^\s+/, '');
1462
+ if (trimmed.length === 0) {
1463
+ found = 'end of input';
1464
+ }
1465
+ else {
1466
+ // Try to extract a word/token (alphanumeric + some symbols)
1467
+ const match = trimmed.match(/^[^\s\}\]\),;]+/);
1468
+ if (match && match[0].length > 0) {
1469
+ found = match[0];
1470
+ }
1471
+ else {
1472
+ // Fallback to first character after whitespace
1473
+ found = trimmed.charAt(0);
1474
+ }
1475
+ }
1476
+ }
1477
+ this.furthestFailure = {
1478
+ offset,
1479
+ expected: new Set([expectedKey]),
1480
+ found,
1481
+ tree: new Map(),
1482
+ regexMap: new Map()
1483
+ };
1484
+ // Store regex instance if it's a RegExp
1485
+ if (expected instanceof RegExp) {
1486
+ this.furthestFailure.regexMap.set(expected.source, expected);
1487
+ }
1488
+ this.recordParentChildRelationship(expected);
1489
+ this.furthestFailure.expected.add(expectedKey);
1490
+ }
1491
+ else if (offset === this.furthestFailure.offset) {
1492
+ // Same position, add to expected set
1493
+ this.furthestFailure.expected.add(expectedKey);
1494
+ // Store regex instance if it's a RegExp
1495
+ if (expected instanceof RegExp) {
1496
+ this.furthestFailure.regexMap.set(expected.source, expected);
1497
+ }
1498
+ this.recordParentChildRelationship(expected);
1499
+ }
1500
+ }
1501
+ recordParentChildRelationship(expected) {
1502
+ if (!this.furthestFailure)
1503
+ return;
1504
+ if (this.parseStack.length > 0) {
1505
+ const parent = this.parseStack[this.parseStack.length - 1];
1506
+ if (!this.furthestFailure.tree.has(parent)) {
1507
+ this.furthestFailure.tree.set(parent, new Set());
1508
+ }
1509
+ this.furthestFailure.tree.get(parent).add(expected);
1510
+ }
1511
+ else {
1512
+ // No parent, this is a top-level failure
1513
+ if (!this.furthestFailure.tree.has('__ROOT__')) {
1514
+ this.furthestFailure.tree.set('__ROOT__', new Set());
1515
+ }
1516
+ this.furthestFailure.tree.get('__ROOT__').add(expected);
1517
+ }
1518
+ }
1519
+ extractParentMostRules(tree) {
1520
+ // The "parent most failing option" is the rule we were trying to match
1521
+ // when all its alternatives failed. In the tree structure, this is typically
1522
+ // the direct child of the top-most parent that has alternatives.
1523
+ // Helper to get string name from value
1524
+ const getName = (value) => value instanceof RegExp ? value.source : value;
1525
+ // If we have __ROOT__, find its direct children that have alternatives
1526
+ if (tree.has('__ROOT__')) {
1527
+ const rootChildren = Array.from(tree.get('__ROOT__')).map(getName);
1528
+ // Return root children that have their own children (alternatives)
1529
+ const result = rootChildren.filter(child => tree.has(child) && tree.get(child).size > 0);
1530
+ if (result.length > 0) {
1531
+ return result;
1532
+ }
1533
+ // If none have children, return the root children themselves
1534
+ return rootChildren;
1535
+ }
1536
+ // Find the top-most parent (not a child of any other parent)
1537
+ const allChildren = new Set();
1538
+ const allParents = new Set();
1539
+ for (const [parent, children] of tree.entries()) {
1540
+ allParents.add(parent);
1541
+ for (const child of children) {
1542
+ allChildren.add(getName(child));
1543
+ }
1544
+ }
1545
+ const topMostParents = Array.from(allParents).filter(parent => !allChildren.has(parent));
1546
+ // For each top-most parent, get its direct children that have alternatives
1547
+ const result = [];
1548
+ for (const parent of topMostParents) {
1549
+ if (tree.has(parent)) {
1550
+ const children = Array.from(tree.get(parent));
1551
+ for (const child of children) {
1552
+ const childName = getName(child);
1553
+ if (tree.has(childName) && tree.get(childName).size > 0) {
1554
+ result.push(childName);
1555
+ }
1556
+ }
1557
+ }
1558
+ }
1559
+ if (result.length > 0) {
1560
+ return result;
1561
+ }
1562
+ // Fallback: return top-most parents
1563
+ if (topMostParents.length > 0) {
1564
+ return topMostParents;
1565
+ }
1566
+ // Last fallback: return all unique rules
1567
+ return Array.from(new Set([...allParents, ...allChildren]));
1568
+ }
1569
+ /**
1570
+ * Determines if a value represents a terminal/literal rather than a non-terminal rule.
1571
+ */
1572
+ isLiteralOrTerminal(value) {
1573
+ if (value instanceof RegExp) {
1574
+ return true;
1575
+ }
1576
+ // Check if it's a literal (starts with " or ')
1577
+ if (value.startsWith('"') || value.startsWith("'")) {
1578
+ return true;
1579
+ }
1580
+ // Check if it's a regex pattern (starts with [ or contains #x followed by hex digits)
1581
+ if (value.startsWith('[') || /\#x[0-9a-fA-F]/.test(value)) {
1582
+ return true;
1583
+ }
1584
+ return false;
1585
+ }
1586
+ /**
1587
+ * Extracts the expected value from a terminal/literal.
1588
+ * - For string literals (quoted), removes the quotes and returns the content
1589
+ * - For regex patterns, returns the RegExp instance
1590
+ */
1591
+ extractExpectedValue(value, regexMap) {
1592
+ if (value instanceof RegExp) {
1593
+ return value;
1594
+ }
1595
+ // If it's a string literal (starts with " or '), parse it to remove quotes
1596
+ if (value.startsWith('"')) {
1597
+ try {
1598
+ return JSON.parse(value);
1599
+ }
1600
+ catch (_a) {
1601
+ return value;
1602
+ }
1603
+ }
1604
+ else if (value.startsWith("'")) {
1605
+ // Single-quoted string - remove quotes
1606
+ return value.replace(/^'(.+)'$/, '$1').replace(/\\'/g, "'");
1607
+ }
1608
+ // Check if this is a regex pattern string that we have a RegExp instance for
1609
+ if (regexMap.has(value)) {
1610
+ return regexMap.get(value);
1611
+ }
1612
+ // For other terminals, return as-is
1613
+ return value;
1614
+ }
1615
+ buildFailureTree(tree, startRules, regexMap) {
1616
+ const buildNode = (value) => {
1617
+ const name = value instanceof RegExp ? value.source : value;
1618
+ const node = { name };
1619
+ if (tree.has(name)) {
1620
+ const children = Array.from(tree.get(name));
1621
+ if (children.length > 0) {
1622
+ // If this rule has exactly one child and that child is a terminal,
1623
+ // set expected to that terminal instead of creating children
1624
+ if (children.length === 1 && this.isLiteralOrTerminal(children[0])) {
1625
+ node.expected = this.extractExpectedValue(children[0], regexMap || new Map());
1626
+ }
1627
+ else {
1628
+ // Build child nodes
1629
+ const childNodes = children.map(child => buildNode(child));
1630
+ // Check if this node should be flattened:
1631
+ // If it has exactly one child AND that child has an expected value (is terminal)
1632
+ if (childNodes.length === 1 && childNodes[0].expected !== undefined && childNodes[0].children === undefined) {
1633
+ node.expected = childNodes[0].expected;
1634
+ }
1635
+ else {
1636
+ node.children = childNodes;
1637
+ }
1638
+ }
1639
+ }
1640
+ }
1641
+ else if (this.isLiteralOrTerminal(value)) {
1642
+ // If this is a terminal/literal with no children, set expected to itself
1643
+ node.expected = this.extractExpectedValue(value, regexMap || new Map());
1644
+ }
1645
+ return node;
1646
+ };
1647
+ // If startRules are provided, start from those rules
1648
+ if (startRules && startRules.length > 0) {
1649
+ return startRules.map(rule => buildNode(rule));
1650
+ }
1651
+ // Start from root if it exists, otherwise from parent-most rules
1652
+ if (tree.has('__ROOT__')) {
1653
+ const rootChildren = Array.from(tree.get('__ROOT__'));
1654
+ return rootChildren.map(child => buildNode(child));
1655
+ }
1656
+ // Find parent-most rules (rules that are not children of other rules)
1657
+ const allChildren = new Set();
1658
+ for (const children of tree.values()) {
1659
+ for (const child of children) {
1660
+ const childName = child instanceof RegExp ? child.source : child;
1661
+ allChildren.add(childName);
1662
+ }
1663
+ }
1664
+ const parentMost = Array.from(tree.keys()).filter(parent => !allChildren.has(parent));
1665
+ return parentMost.map(parent => buildNode(parent));
1666
+ }
1667
+ parse(txt, target, recursion = 0, offset = 0) {
1668
+ let out = null;
1669
+ let type = parseRuleName(target);
1670
+ let expr;
1671
+ let printable = this.debug && /*!isLiteral &*/ !UPPER_SNAKE_RE.test(type.name);
1672
+ printable &&
1673
+ console.log(new Array(recursion).join('│ ') + 'Trying to get ' + target + ' from ' + JSON.stringify(txt.split('\n')[0]));
1674
+ let realType = type.name;
1675
+ let targetLex = findRuleByName(type.name, this);
1676
+ if (type.name == 'EOF') {
1677
+ if (txt.length) {
1678
+ this.recordFailure(offset, 'EOF');
1679
+ return null;
1680
+ }
1681
+ else if (txt.length == 0) {
1682
+ return {
1683
+ type: 'EOF',
1684
+ text: '',
1685
+ rest: '',
1686
+ start: 0,
1687
+ end: 0,
1688
+ fullText: '',
1689
+ errors: [],
1690
+ children: [],
1691
+ parent: null
1692
+ };
1693
+ }
1694
+ }
1695
+ try {
1696
+ if (!targetLex && type.isLiteral) {
1697
+ let src = type.name.trim();
1698
+ if (src.startsWith('"')) {
1699
+ src = JSON.parse(src);
1700
+ }
1701
+ else if (src.startsWith("'")) {
1702
+ src = src.replace(/^'(.+)'$/, '$1').replace(/\\'/g, "'");
1703
+ }
1704
+ if (src === '') {
1705
+ return {
1706
+ type: '%%EMPTY%%',
1707
+ text: '',
1708
+ rest: txt,
1709
+ start: 0,
1710
+ end: 0,
1711
+ fullText: '',
1712
+ errors: [],
1713
+ children: [],
1714
+ parent: null
1715
+ };
1716
+ }
1717
+ expr = new RegExp(escapeRegExp(src));
1718
+ realType = null;
1719
+ }
1720
+ }
1721
+ catch (e) {
1722
+ if (e instanceof ReferenceError) {
1723
+ console.error(e);
1724
+ }
1725
+ this.recordFailure(offset, target);
1726
+ return null;
1727
+ }
1728
+ if (expr) {
1729
+ let result = readToken(txt, expr);
1730
+ if (result) {
1731
+ result.type = realType;
1732
+ return result;
1733
+ }
1734
+ else {
1735
+ // Literal or regex match failed
1736
+ this.recordFailure(offset, type.isLiteral ? type.name : target);
1737
+ }
1738
+ }
1739
+ else {
1740
+ let options = targetLex.bnf;
1741
+ if (options instanceof Array) {
1742
+ // Push this rule onto the parse stack
1743
+ this.parseStack.push(type.name);
1744
+ optionsLoop: for (const phases of options) {
1745
+ if (out)
1746
+ break;
1747
+ let pinned = null;
1748
+ let tmp = {
1749
+ type: type.name,
1750
+ text: '',
1751
+ children: [],
1752
+ end: 0,
1753
+ errors: [],
1754
+ fullText: '',
1755
+ parent: null,
1756
+ start: 0,
1757
+ rest: txt
1758
+ };
1759
+ if (targetLex.fragment)
1760
+ tmp.fragment = true;
1761
+ let tmpTxt = txt;
1762
+ let position = 0;
1763
+ let allOptional = phases.length > 0;
1764
+ let foundSomething = false;
1765
+ for (let i = 0; i < phases.length; i++) {
1766
+ if (typeof phases[i] == 'string') {
1767
+ let localTarget = parseRuleName(phases[i]);
1768
+ allOptional = allOptional && localTarget.isOptional;
1769
+ let got;
1770
+ let foundAtLeastOne = false;
1771
+ do {
1772
+ got = null;
1773
+ if (targetLex.implicitWs) {
1774
+ got = this.parse(tmpTxt, localTarget.name, recursion + 1, offset + position);
1775
+ if (!got) {
1776
+ let WS;
1777
+ do {
1778
+ WS = this.parse(tmpTxt, WS_RULE, recursion + 1, offset + position);
1779
+ if (WS) {
1780
+ tmp.text = tmp.text + WS.text;
1781
+ tmp.end = tmp.text.length;
1782
+ WS.parent = tmp;
1783
+ tmp.children.push(WS);
1784
+ tmpTxt = tmpTxt.substr(WS.text.length);
1785
+ position += WS.text.length;
1786
+ }
1787
+ else {
1788
+ break;
1789
+ }
1790
+ } while (WS && WS.text.length);
1791
+ }
1792
+ }
1793
+ got = got || this.parse(tmpTxt, localTarget.name, recursion + 1, offset + position);
1794
+ // rule ::= "true" ![a-zA-Z]
1795
+ // negative lookup, if it does not match, we should continue
1796
+ if (localTarget.lookupNegative) {
1797
+ if (got)
1798
+ continue optionsLoop; /* cancel this path */
1799
+ break;
1800
+ }
1801
+ if (localTarget.lookupPositive) {
1802
+ if (!got)
1803
+ continue optionsLoop;
1804
+ }
1805
+ if (!got) {
1806
+ if (localTarget.isOptional)
1807
+ break;
1808
+ if (localTarget.atLeastOne && foundAtLeastOne)
1809
+ break;
1810
+ // Record this failure for error reporting
1811
+ this.recordFailure(offset + position, localTarget.name);
1812
+ }
1813
+ if (got && targetLex.pinned == i + 1) {
1814
+ pinned = got;
1815
+ printable && console.log(new Array(recursion + 1).join('│ ') + '└─ ' + got.type + ' PINNED');
1816
+ }
1817
+ if (!got)
1818
+ got = this.parseRecovery(targetLex, tmpTxt, recursion + 1, offset + position);
1819
+ if (!got) {
1820
+ if (pinned) {
1821
+ out = tmp;
1822
+ got = {
1823
+ type: 'SyntaxError',
1824
+ text: tmpTxt,
1825
+ children: [],
1826
+ end: tmpTxt.length,
1827
+ errors: [],
1828
+ fullText: '',
1829
+ parent: null,
1830
+ start: 0,
1831
+ rest: ''
1832
+ };
1833
+ if (tmpTxt.length) {
1834
+ new TokenError_1.TokenError(`Unexpected end of input. Expecting ${localTarget.name} Got: ${tmpTxt}`, got);
1835
+ }
1836
+ else {
1837
+ new TokenError_1.TokenError(`Unexpected end of input. Missing ${localTarget.name}`, got);
1838
+ }
1839
+ printable &&
1840
+ console.log(new Array(recursion + 1).join('│ ') + '└─ ' + got.type + ' ' + JSON.stringify(got.text));
1841
+ }
1842
+ else {
1843
+ continue optionsLoop;
1844
+ }
1845
+ }
1846
+ foundAtLeastOne = true;
1847
+ foundSomething = true;
1848
+ if (got.type == '%%EMPTY%%') {
1849
+ break;
1850
+ }
1851
+ got.start += position;
1852
+ got.end += position;
1853
+ if (!localTarget.lookupPositive && got.type) {
1854
+ if (got.fragment) {
1855
+ if (got.children) {
1856
+ for (const x of got.children) {
1857
+ x.start += position;
1858
+ x.end += position;
1859
+ x.parent = tmp;
1860
+ tmp.children.push(x);
1861
+ }
1862
+ }
1863
+ }
1864
+ else {
1865
+ got.parent = tmp;
1866
+ tmp.children.push(got);
1867
+ }
1868
+ }
1869
+ if (localTarget.lookup)
1870
+ got.lookup = true;
1871
+ printable &&
1872
+ console.log(new Array(recursion + 1).join('│ ') + '└─ ' + got.type + ' ' + JSON.stringify(got.text));
1873
+ // Eat it from the input stream, only if it is not a lookup
1874
+ if (!localTarget.lookup && !got.lookup) {
1875
+ tmp.text = tmp.text + got.text;
1876
+ tmp.end = tmp.text.length;
1877
+ tmpTxt = tmpTxt.substr(got.text.length);
1878
+ position += got.text.length;
1879
+ }
1880
+ tmp.rest = tmpTxt;
1881
+ } while (got && localTarget.allowRepetition && tmpTxt.length && !got.lookup);
1882
+ } /* IS A REGEXP */
1883
+ else {
1884
+ let got = readToken(tmpTxt, phases[i]);
1885
+ if (!got) {
1886
+ this.recordFailure(offset + position, phases[i]);
1887
+ continue optionsLoop;
1888
+ }
1889
+ printable &&
1890
+ console.log(new Array(recursion + 1).join('│ ') + '└> ' + JSON.stringify(got.text) + phases[i].source);
1891
+ foundSomething = true;
1892
+ got.start += position;
1893
+ got.end += position;
1894
+ tmp.text = tmp.text + got.text;
1895
+ tmp.end = tmp.text.length;
1896
+ tmpTxt = tmpTxt.substr(got.text.length);
1897
+ position += got.text.length;
1898
+ tmp.rest = tmpTxt;
1899
+ }
1900
+ }
1901
+ if (foundSomething) {
1902
+ out = tmp;
1903
+ printable &&
1904
+ console.log(new Array(recursion).join('│ ') + '├<─┴< PUSHING ' + out.type + ' ' + JSON.stringify(out.text));
1905
+ }
1906
+ }
1907
+ // Pop this rule from the parse stack
1908
+ this.parseStack.pop();
1909
+ }
1910
+ if (out && targetLex.simplifyWhenOneChildren && out.children.length == 1) {
1911
+ out = out.children[0];
1912
+ }
1913
+ }
1914
+ if (!out) {
1915
+ printable && console.log(target + ' NOT RESOLVED FROM ' + txt);
1916
+ }
1917
+ return out;
1918
+ }
1919
+ parseRecovery(recoverableToken, tmpTxt, recursion, offset) {
1920
+ if (recoverableToken.recover && tmpTxt.length) {
1921
+ let printable = this.debug;
1922
+ printable &&
1923
+ console.log(new Array(recursion + 1).join('│ ') +
1924
+ 'Trying to recover until token ' +
1925
+ recoverableToken.recover +
1926
+ ' from ' +
1927
+ JSON.stringify(tmpTxt.split('\n')[0] + tmpTxt.split('\n')[1]));
1928
+ let tmp = {
1929
+ type: 'SyntaxError',
1930
+ text: '',
1931
+ children: [],
1932
+ end: 0,
1933
+ errors: [],
1934
+ fullText: '',
1935
+ parent: null,
1936
+ start: 0,
1937
+ rest: ''
1938
+ };
1939
+ let got;
1940
+ let currentOffset = offset;
1941
+ do {
1942
+ got = this.parse(tmpTxt, recoverableToken.recover, recursion + 1, currentOffset);
1943
+ if (got) {
1944
+ new TokenError_1.TokenError('Unexpected input: "' + tmp.text + `" Expecting: ${recoverableToken.name}`, tmp);
1945
+ break;
1946
+ }
1947
+ else {
1948
+ tmp.text = tmp.text + tmpTxt[0];
1949
+ tmp.end = tmp.text.length;
1950
+ tmpTxt = tmpTxt.substr(1);
1951
+ currentOffset++;
1952
+ }
1953
+ } while (!got && tmpTxt.length > 0);
1954
+ if (tmp.text.length > 0 && got) {
1955
+ printable && console.log(new Array(recursion + 1).join('│ ') + 'Recovered text: ' + JSON.stringify(tmp.text));
1956
+ return tmp;
1957
+ }
1958
+ }
1959
+ return null;
1960
+ }
1961
+ }
1962
+ exports.Parser = Parser;
1963
+ exports.default = Parser;
1964
+
1965
+ },{"./ParsingError":7,"./TokenError":9}],7:[function(require,module,exports){
1966
+ "use strict";
1967
+ Object.defineProperty(exports, "__esModule", { value: true });
1968
+ exports.ParsingError = void 0;
1969
+ class ParsingError extends Error {
1970
+ constructor(message, position, expected, found, failureTree) {
1971
+ super(message);
1972
+ this.name = 'ParsingError';
1973
+ this.position = position;
1974
+ this.expected = expected;
1975
+ this.found = found;
1976
+ this.failureTree = failureTree;
1977
+ // Maintain proper prototype chain for instanceof checks
1978
+ Object.setPrototypeOf(this, ParsingError.prototype);
1979
+ }
1980
+ toString() {
1981
+ const { line, column, offset } = this.position;
1982
+ let msg = `${this.name}: ${this.message}\n`;
1983
+ msg += ` at line ${line}, column ${column} (offset ${offset})\n`;
1984
+ msg += ` Expected: ${this.expected.join(', ')}\n`;
1985
+ msg += ` Found: ${this.found}`;
1986
+ return msg;
1987
+ }
1988
+ }
1989
+ exports.ParsingError = ParsingError;
1990
+
1991
+ },{}],8:[function(require,module,exports){
1992
+ "use strict";
1993
+ Object.defineProperty(exports, "__esModule", { value: true });
1994
+ exports.findChildrenByType = findChildrenByType;
1995
+ /**
1996
+ * Finds all the direct childs of a specifyed type
1997
+ */
1998
+ function findChildrenByType(token, type) {
1999
+ return token.children ? token.children.filter(x => x.type == type) : [];
2000
+ }
2001
+
2002
+ },{}],9:[function(require,module,exports){
2003
+ "use strict";
2004
+ Object.defineProperty(exports, "__esModule", { value: true });
2005
+ exports.TokenError = void 0;
2006
+ class TokenError extends Error {
2007
+ constructor(message, token) {
2008
+ super(message);
2009
+ this.message = message;
2010
+ this.token = token;
2011
+ if (token && token.errors)
2012
+ token.errors.push(this);
2013
+ else
2014
+ throw this;
2015
+ }
2016
+ inspect() {
2017
+ return 'SyntaxError: ' + this.message;
2018
+ }
2019
+ }
2020
+ exports.TokenError = TokenError;
2021
+
2022
+ },{}],10:[function(require,module,exports){
2023
+ "use strict";
2024
+ Object.defineProperty(exports, "__esModule", { value: true });
2025
+ exports.ParsingError = exports.TokenError = exports.Parser = void 0;
2026
+ var Parser_1 = require("./Parser");
2027
+ Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return Parser_1.Parser; } });
2028
+ var TokenError_1 = require("./TokenError");
2029
+ Object.defineProperty(exports, "TokenError", { enumerable: true, get: function () { return TokenError_1.TokenError; } });
2030
+ var ParsingError_1 = require("./ParsingError");
2031
+ Object.defineProperty(exports, "ParsingError", { enumerable: true, get: function () { return ParsingError_1.ParsingError; } });
2032
+ exports.Grammars = require("./Grammars");
2033
+
2034
+ },{"./Grammars":5,"./Parser":6,"./ParsingError":7,"./TokenError":9}],11:[function(require,module,exports){
2035
+ module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]}]
2036
+ },{}],12:[function(require,module,exports){
2037
+ module.exports = require('./RuleTemplater.production.js');
2038
+ },{"./RuleTemplater.production.js":13}],13:[function(require,module,exports){
2039
+ // Note: We import the internal RuleParser.ebnf to extend the grammar with template rules.
2040
+ // This creates coupling to the internal structure of @halleyassist/rule-parser.
2041
+ const RuleParserRules = require('@halleyassist/rule-parser/src/RuleParser.ebnf'),
2042
+ TemplateGrammar = require('./RuleTemplate.production.ebnf.js'),
2043
+ TemplateFilters = require('./TemplateFilters'),
2044
+ {Parser} = require('ebnf');
2045
+
2046
+ let ParserCache = null;
2047
+
2048
+ const VariableTypes = [
2049
+ 'string',
2050
+ 'number',
2051
+ 'boolean',
2052
+ 'object',
2053
+ 'time period',
2054
+ 'time value',
2055
+ 'string array',
2056
+ 'number array',
2057
+ 'boolean array',
2058
+ 'object array'
2059
+ ]
2060
+
2061
+ const AllowedTypeMapping = {
2062
+ 'string': ['string_atom', 'string_concat'],
2063
+ 'number': ['number_atom', 'math_expr'],
2064
+ 'boolean': ['boolean_atom', 'boolean_expr'],
2065
+ 'time period': ['time_period_atom'],
2066
+ 'time value': ['time_value_atom', 'tod_atom'],
2067
+ 'string array': ['string_array'],
2068
+ 'number array': ['number_array'],
2069
+ 'boolean array': ['boolean_array'],
2070
+ 'object': ['object_atom'],
2071
+ 'object array': ['object_array']
2072
+ };
2073
+
2074
+ // Merge the base grammar with template-specific grammar rules
2075
+ const extendedGrammar = [...RuleParserRules]
2076
+ for(const rule of TemplateGrammar){
2077
+ const idx = extendedGrammar.findIndex(r => r.name === rule.name);
2078
+ if(idx !== -1){
2079
+ extendedGrammar[idx] = rule;
2080
+ } else {
2081
+ extendedGrammar.push(rule);
2082
+ }
2083
+ }
2084
+
2085
+ // Add template_value as an alternative to value_atom so templates can be parsed
2086
+ const valueAtomIdx = extendedGrammar.findIndex(r => r.name === 'value_atom');
2087
+ if (valueAtomIdx !== -1) {
2088
+ extendedGrammar[valueAtomIdx].bnf.push(['template_value']);
2089
+ }
2090
+
2091
+ // Export the parser rules for potential external use
2092
+ const ParserRules = extendedGrammar;
2093
+
2094
+ class RuleTemplate {
2095
+ constructor(ruleTemplateText, ast) {
2096
+ this.ruleTemplateText = ruleTemplateText;
2097
+ this.ast = ast;
2098
+ }
2099
+
2100
+ /**
2101
+ * Parse a rule template string and return a RuleTemplate instance
2102
+ * @param {string} ruleTemplate - The template string to parse
2103
+ * @returns {RuleTemplate} Instance with AST and template text
2104
+ */
2105
+ static parse(ruleTemplate){
2106
+ if(!ParserCache){
2107
+ ParserCache = new Parser(ParserRules, {debug: false})
2108
+ }
2109
+
2110
+ const ast = ParserCache.getAST(ruleTemplate.trim(), 'statement_main');
2111
+ return new RuleTemplate(ruleTemplate, ast);
2112
+ }
2113
+
2114
+ /**
2115
+ * Extract variables from the template using the AST
2116
+ * @returns {Array} Array of {name, filters: []} objects
2117
+ */
2118
+ extractVariables(){
2119
+ const variables = [];
2120
+ const seen = new Set();
2121
+
2122
+ const traverse = (node) => {
2123
+ if (!node) return;
2124
+
2125
+ // Check if this is a template_value node
2126
+ if (node.type === 'template_value') {
2127
+ // Extract the variable information
2128
+ const varInfo = this._extractVariableFromNode(node);
2129
+ if (varInfo && !seen.has(varInfo.name)) {
2130
+ seen.add(varInfo.name);
2131
+ variables.push(varInfo);
2132
+ }
2133
+ }
2134
+
2135
+ // Traverse children
2136
+ if (node.children) {
2137
+ for (const child of node.children) {
2138
+ traverse(child);
2139
+ }
2140
+ }
2141
+ };
2142
+
2143
+ traverse(this.ast);
2144
+ return variables;
2145
+ }
2146
+
2147
+ /**
2148
+ * Extract variable name and filters from a template_value AST node
2149
+ * @private
2150
+ */
2151
+ _extractVariableFromNode(node) {
2152
+ if (node.type !== 'template_value') return null;
2153
+
2154
+ // Find the template_expr child
2155
+ const templateExpr = node.children?.find(c => c.type === 'template_expr');
2156
+ if (!templateExpr) return null;
2157
+
2158
+ // Extract the path (variable name)
2159
+ const templatePath = templateExpr.children?.find(c => c.type === 'template_path');
2160
+ if (!templatePath || !templatePath.text) return null;
2161
+
2162
+ const name = templatePath.text.trim();
2163
+
2164
+ // Extract filters
2165
+ const filters = [];
2166
+ for (const child of templateExpr.children || []) {
2167
+ if (child.type === 'template_filter_call') {
2168
+ const filterName = this._extractFilterName(child);
2169
+ if (filterName) {
2170
+ filters.push(filterName);
2171
+ }
2172
+ }
2173
+ }
2174
+
2175
+ return { name, filters };
2176
+ }
2177
+
2178
+ /**
2179
+ * Extract filter name from template_filter_call node
2180
+ * @private
2181
+ */
2182
+ _extractFilterName(node) {
2183
+ const filterNameNode = node.children?.find(c => c.type === 'template_filter_name');
2184
+ if (!filterNameNode || !filterNameNode.text) return null;
2185
+
2186
+ return filterNameNode.text.trim();
2187
+ }
2188
+
2189
+ /**
2190
+ * Validate variable types against the AST
2191
+ * @param {Object} variables - Object mapping variable names to {type} objects
2192
+ * @returns {Object} Object with validation results: {valid: boolean, errors: []}
2193
+ */
2194
+ validate(variables) {
2195
+ if (!variables || typeof variables !== 'object') {
2196
+ return {
2197
+ valid: false,
2198
+ errors: ['Variables must be provided as an object']
2199
+ };
2200
+ }
2201
+
2202
+ const errors = [];
2203
+ const extractedVars = this.extractVariables();
2204
+
2205
+ for (const varInfo of extractedVars) {
2206
+ const varName = varInfo.name;
2207
+
2208
+ // Check if variable is provided
2209
+ if (!variables.hasOwnProperty(varName)) {
2210
+ errors.push(`Variable '${varName}' not provided in variables object`);
2211
+ continue;
2212
+ }
2213
+
2214
+ const varData = variables[varName];
2215
+ if (typeof varData !== 'object') {
2216
+ errors.push(`Variable '${varName}' must be an object`);
2217
+ continue;
2218
+ }
2219
+
2220
+ const { type } = varData;
2221
+
2222
+ // Validate type if provided
2223
+ if (type && !VariableTypes.includes(type)) {
2224
+ errors.push(`Invalid variable type '${type}' for variable '${varName}'`);
2225
+ }
2226
+ }
2227
+
2228
+ return {
2229
+ valid: errors.length === 0,
2230
+ errors
2231
+ };
2232
+ }
2233
+
2234
+ /**
2235
+ * Prepare the template by replacing variables with their values
2236
+ * Rebuilds from AST by iterating through children
2237
+ * @param {Object} variables - Object mapping variable names to {value, type} objects
2238
+ * @returns {string} The prepared rule string
2239
+ */
2240
+ prepare(variables){
2241
+ if (!variables || typeof variables !== 'object') {
2242
+ throw new Error('Variables must be provided as an object');
2243
+ }
2244
+
2245
+ // Rebuild the rule string from AST
2246
+ return this._rebuildFromAST(this.ast, variables);
2247
+ }
2248
+
2249
+ /**
2250
+ * Rebuild rule string from AST node, replacing template_value nodes with variable values
2251
+ * @private
2252
+ * @param {Object} node - AST node
2253
+ * @param {Object} variables - Object mapping variable names to {value, type} objects
2254
+ * @returns {string} Rebuilt string
2255
+ */
2256
+ _rebuildFromAST(node, variables) {
2257
+ if (!node) return '';
2258
+
2259
+ // If this is a template_value node, replace it with the computed value
2260
+ if (node.type === 'template_value') {
2261
+ return this._computeTemplateReplacement(node, variables);
2262
+ }
2263
+
2264
+ // If node has no children, it's a leaf - return its text
2265
+ if (!node.children || node.children.length === 0) {
2266
+ return node.text || '';
2267
+ }
2268
+
2269
+ // Node has children - rebuild by iterating through children and preserving gaps
2270
+ let result = '';
2271
+ const originalText = node.text || '';
2272
+ let lastEnd = node.start || 0;
2273
+
2274
+ for (const child of node.children) {
2275
+ // Add any text between the last child's end and this child's start (gaps/syntax)
2276
+ if (child.start !== undefined && child.start > lastEnd) {
2277
+ result += originalText.substring(lastEnd - (node.start || 0), child.start - (node.start || 0));
2278
+ }
2279
+
2280
+ // Add the child's rebuilt text
2281
+ result += this._rebuildFromAST(child, variables);
2282
+
2283
+ // Update lastEnd to this child's end position
2284
+ if (child.end !== undefined) {
2285
+ lastEnd = child.end;
2286
+ }
2287
+ }
2288
+
2289
+ // Add any remaining text after the last child
2290
+ if (node.end !== undefined && lastEnd < node.end) {
2291
+ result += originalText.substring(lastEnd - (node.start || 0), node.end - (node.start || 0));
2292
+ }
2293
+
2294
+ return result;
2295
+ }
2296
+
2297
+ /**
2298
+ * Compute the replacement value for a template_value node
2299
+ * @private
2300
+ * @param {Object} node - template_value AST node
2301
+ * @param {Object} variables - Object mapping variable names to {value, type} objects
2302
+ * @returns {string} Replacement string
2303
+ */
2304
+ _computeTemplateReplacement(node, variables) {
2305
+ const templateInfo = this._extractVariableFromNode(node);
2306
+ if (!templateInfo) {
2307
+ throw new Error(`Failed to extract variable information from template node`);
2308
+ }
2309
+
2310
+ const varName = templateInfo.name;
2311
+
2312
+ // Validate variable is provided
2313
+ if (!variables.hasOwnProperty(varName)) {
2314
+ throw new Error(`Variable '${varName}' not provided in variables object`);
2315
+ }
2316
+
2317
+ const varData = variables[varName];
2318
+ if (typeof varData !== 'object' || !varData.hasOwnProperty('value')) {
2319
+ throw new Error(`Variable '${varName}' must be an object with 'value' property`);
2320
+ }
2321
+
2322
+ let { value, type } = varData;
2323
+
2324
+ // Require type property for all variables
2325
+ if (!varData.hasOwnProperty('type')) {
2326
+ throw new Error(`Variable '${varName}' must have a 'type' property`);
2327
+ }
2328
+
2329
+ // Validate type
2330
+ if (!VariableTypes.includes(type)) {
2331
+ throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
2332
+ }
2333
+
2334
+ // Apply filters if present
2335
+ if (templateInfo.filters && templateInfo.filters.length > 0) {
2336
+ for (const filterName of templateInfo.filters) {
2337
+ if (!TemplateFilters[filterName]) {
2338
+ throw new Error(`Unknown filter '${filterName}'`);
2339
+ }
2340
+ value = TemplateFilters[filterName](value);
2341
+ }
2342
+ // After applying filters, the result is already a string representation
2343
+ return String(value);
2344
+ }
2345
+
2346
+ // Convert value to string representation based on type
2347
+ if (type === 'string') {
2348
+ // Escape backslashes first, then quotes in string values.
2349
+ // Order is critical: escaping backslashes first prevents double-escaping.
2350
+ // E.g., "test\" becomes "test\\" then "test\\\"" (correct)
2351
+ // If reversed, "test\" would become "test\\"" then "test\\\\"" (incorrect)
2352
+ return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
2353
+ } else if (type === 'number') {
2354
+ return String(value);
2355
+ } else if (type === 'boolean') {
2356
+ return value ? 'true' : 'false';
2357
+ } else {
2358
+ // Default behavior - just insert the value as-is
2359
+ return String(value);
2360
+ }
2361
+ }
2362
+
2363
+ /**
2364
+ * Helper method to validate if an AST node matches a variable type
2365
+ * @param {Object} astNode - The AST node to validate
2366
+ * @param {string} variableType - The expected variable type
2367
+ * @returns {boolean} True if valid, false otherwise
2368
+ */
2369
+ static validateVariableNode(astNode, variableType) {
2370
+ if (!astNode || !astNode.type) {
2371
+ return false;
2372
+ }
2373
+
2374
+ const allowedTypes = AllowedTypeMapping[variableType];
2375
+ if (!allowedTypes) {
2376
+ return false;
2377
+ }
2378
+
2379
+ return allowedTypes.includes(astNode.type);
2380
+ }
2381
+ }
2382
+
2383
+ // Export the class and parser rules
2384
+ module.exports = RuleTemplate;
2385
+ module.exports.ParserRules = ParserRules;
2386
+ module.exports.VariableTypes = VariableTypes;
2387
+ module.exports.TemplateFilters = TemplateFilters;
2388
+ },{"./RuleTemplate.production.ebnf.js":11,"./TemplateFilters":14,"@halleyassist/rule-parser/src/RuleParser.ebnf":1,"ebnf":10}],14:[function(require,module,exports){
2389
+ /*
2390
+ Template filters are functions that transform variable values.
2391
+ They are applied in the template syntax as ${variable|filter} or ${variable|filter1|filter2}
2392
+ */
2393
+ const TemplateFilters = {
2394
+ // Convert value to JSON string representation
2395
+ string: value => JSON.stringify(String(value)),
2396
+
2397
+ // Convert to uppercase
2398
+ upper: value => String(value).toUpperCase(),
2399
+
2400
+ // Convert to lowercase
2401
+ lower: value => String(value).toLowerCase(),
2402
+
2403
+ // Capitalize first letter
2404
+ capitalize: value => {
2405
+ const str = String(value);
2406
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
2407
+ },
2408
+
2409
+ // Convert to title case
2410
+ title: value => {
2411
+ return String(value).split(' ')
2412
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
2413
+ .join(' ');
2414
+ },
2415
+
2416
+ // Trim whitespace
2417
+ trim: value => String(value).trim(),
2418
+
2419
+ // Convert to number
2420
+ number: value => Number(value),
2421
+
2422
+ // Convert to boolean
2423
+ boolean: value => {
2424
+ if (typeof value === 'boolean') return value;
2425
+ if (typeof value === 'string') {
2426
+ const lower = value.toLowerCase();
2427
+ if (lower === 'true' || lower === '1' || lower === 'yes') return true;
2428
+ if (lower === 'false' || lower === '0' || lower === 'no') return false;
2429
+ }
2430
+ return Boolean(value);
2431
+ },
2432
+
2433
+ // Convert to absolute value (for numbers)
2434
+ abs: value => Math.abs(Number(value)),
2435
+
2436
+ // Round number
2437
+ round: value => Math.round(Number(value)),
2438
+
2439
+ // Floor number
2440
+ floor: value => Math.floor(Number(value)),
2441
+
2442
+ // Ceil number
2443
+ ceil: value => Math.ceil(Number(value)),
2444
+
2445
+ // Default value if empty/null/undefined
2446
+ default: (value, defaultValue = '') => {
2447
+ return (value === null || value === undefined || value === '') ? defaultValue : value;
2448
+ }
2449
+ }
2450
+
2451
+ module.exports = TemplateFilters;
2452
+ },{}]},{},[12])(12)
2453
+ });