@briza/illogical 1.5.9 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/illogical.js CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ function _defineProperty(e, r, t) {
6
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
7
+ value: t,
8
+ enumerable: !0,
9
+ configurable: !0,
10
+ writable: !0
11
+ }) : e[r] = t, e;
12
+ }
5
13
  function _toPrimitive(t, r) {
6
14
  if ("object" != typeof t || !t) return t;
7
15
  var e = t[Symbol.toPrimitive];
@@ -14,21 +22,7 @@ function _toPrimitive(t, r) {
14
22
  }
15
23
  function _toPropertyKey(t) {
16
24
  var i = _toPrimitive(t, "string");
17
- return "symbol" == typeof i ? i : String(i);
18
- }
19
- function _defineProperty(obj, key, value) {
20
- key = _toPropertyKey(key);
21
- if (key in obj) {
22
- Object.defineProperty(obj, key, {
23
- value: value,
24
- enumerable: true,
25
- configurable: true,
26
- writable: true
27
- });
28
- } else {
29
- obj[key] = value;
30
- }
31
- return obj;
25
+ return "symbol" == typeof i ? i : i + "";
32
26
  }
33
27
 
34
28
  /**
@@ -36,7 +30,14 @@ function _defineProperty(obj, key, value) {
36
30
  * @param value Tested value.
37
31
  */
38
32
  function isNumber(value) {
39
- return typeof value === 'number' && isFinite(value);
33
+ return typeof value === 'number';
34
+ }
35
+ /**
36
+ * Is number predicate.
37
+ * @param value Tested value.
38
+ */
39
+ function isInfinite(value) {
40
+ return typeof value === 'number' && !isFinite(value);
40
41
  }
41
42
 
42
43
  /**
@@ -58,50 +59,597 @@ function isObject(value) {
58
59
  if (typeof value !== 'object' || (value === null || value === void 0 ? void 0 : value.constructor) !== Object) {
59
60
  return false;
60
61
  }
61
- return true;
62
- }
63
-
64
- /**
65
- * Is Boolean predicate.
66
- * @param value tested value.
67
- * @return result of the test
68
- */
69
- function isBoolean(value) {
70
- return typeof value === 'boolean';
71
- }
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * Is Boolean predicate.
67
+ * @param value tested value.
68
+ * @return result of the test
69
+ */
70
+ function isBoolean(value) {
71
+ return typeof value === 'boolean';
72
+ }
73
+
74
+ /**
75
+ * Check if a value is a an Evaluable
76
+ * @param {Result | Evaluable} value value to check if is Evaluable
77
+ * @returns {Evaluable}
78
+ */
79
+ function isEvaluable(value) {
80
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value.evaluate === 'function' && typeof value.simplify === 'function' && typeof value.serialize === 'function' && typeof value.toString === 'function';
81
+ }
82
+
83
+ /**
84
+ * Ensures all values are results.
85
+ * @param {(Result | Evaluable)[]} values results or evaluables
86
+ * @returns {boolean} type guard
87
+ */
88
+ function areAllResults(values) {
89
+ return values.every(value => !isEvaluable(value));
90
+ }
91
+
92
+ /**
93
+ * Ensures all values are numbers.
94
+ * @param {Result[]} results results or evaluables
95
+ * @returns {boolean} type guard
96
+ */
97
+ function areAllNumbers(results) {
98
+ return results.every(isNumber);
99
+ }
100
+
101
+ /**
102
+ * Valid types for context members
103
+ */
104
+
105
+ /**
106
+ * Evaluation Context
107
+ * Holds references used during the evaluation process.
108
+ * Format: key: value.
109
+ */
110
+
111
+ /**
112
+ * Evaluation result
113
+ */
114
+
115
+ let EvaluableType = /*#__PURE__*/function (EvaluableType) {
116
+ EvaluableType["Operand"] = "Operand";
117
+ EvaluableType["Expression"] = "Expression";
118
+ return EvaluableType;
119
+ }({});
120
+
121
+ /**
122
+ * Evaluable
123
+ */
124
+
125
+ /**
126
+ * Abstract arithmetic expression
127
+ */
128
+ class Arithmetic {
129
+ /**
130
+ * @constructor
131
+ * @param {string} operator String representation of the operator.
132
+ * @param {symbol} operatorSymbol Operator symbol.
133
+ * @param {Operand[]} operands Operands.
134
+ */
135
+ constructor(operator, operatorSymbol, operands) {
136
+ this.operator = operator;
137
+ this.operatorSymbol = operatorSymbol;
138
+ this.operands = operands;
139
+ _defineProperty(this, "type", EvaluableType.Expression);
140
+ }
141
+
142
+ /**
143
+ * Helper function to assist with arithmetic evaluation. Ensures that all
144
+ * operands are present and are numbers. Throws error if any operand is not a
145
+ * number.
146
+ *
147
+ * @param {Result[]} results
148
+ * @returns {number[] | false} false if any operand is missing, otherwise the
149
+ * array of numbers
150
+ */
151
+ getResultValues(results) {
152
+ const presentValues = results.filter(result => result !== null && result !== undefined);
153
+ // If we have missing context values the result must be false
154
+ if (presentValues.length !== results.length) {
155
+ return false;
156
+ }
157
+ if (!areAllNumbers(presentValues)) {
158
+ throw new Error(`operands must be numbers for ${this.constructor.name}`);
159
+ }
160
+ return presentValues;
161
+ }
162
+
163
+ /**
164
+ * Performs the arithmetic operation on the operands evaluated values.
165
+ * @param {Result[]} results Operand result values.
166
+ * @returns {Result}
167
+ */
168
+
169
+ /**
170
+ * {@link Evaluable.evaluate}
171
+ */
172
+ evaluate(ctx) {
173
+ return this.operate(this.operands.map(operand => operand.evaluate(ctx)));
174
+ }
175
+
176
+ /**
177
+ * {@link Evaluable.toString}
178
+ */
179
+ toString() {
180
+ return `(${this.operands.map(operand => operand.toString()).join(` ${this.operator} `)})`;
181
+ }
182
+
183
+ /**
184
+ * {@link Evaluable.simplify}
185
+ */
186
+ simplify(...args) {
187
+ const results = this.operands.map(operand => operand.simplify(...args));
188
+ if (areAllResults(results)) {
189
+ return this.operate(results);
190
+ }
191
+ return this;
192
+ }
193
+
194
+ /**
195
+ * {@link Evaluable.serialize}
196
+ */
197
+ serialize(options) {
198
+ const {
199
+ operatorMapping
200
+ } = options;
201
+ const operator = operatorMapping.get(this.operatorSymbol);
202
+ if (operator === undefined) {
203
+ throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
204
+ }
205
+ return [operator, ...this.operands.map(operand => operand.serialize(options))];
206
+ }
207
+ }
208
+
209
+ // Operator key
210
+ const OPERATOR$l = Symbol('DIVIDE');
211
+
212
+ /**
213
+ * Divide operation expression
214
+ */
215
+ class Divide extends Arithmetic {
216
+ /**
217
+ * @constructor Generic constructor
218
+ * @param {Evaluable[]} args
219
+ */
220
+
221
+ /**
222
+ * @constructor
223
+ * @param {Operand[]} operands Operands.
224
+ */
225
+ constructor(...operands) {
226
+ if (operands.length < 2) {
227
+ throw new Error('divide expression requires at least 2 operands');
228
+ }
229
+ super('/', OPERATOR$l, operands);
230
+ }
231
+ operate(results) {
232
+ const presentResults = this.getResultValues(results);
233
+ if (presentResults === false) {
234
+ return false;
235
+ }
236
+ return presentResults.reduce((acc, result) => acc / result);
237
+ }
238
+ }
239
+
240
+ const getNumDecimals = num => {
241
+ const numberSplit = num.toString().split('.');
242
+ return numberSplit.length == 2 ? numberSplit[1].length : 0;
243
+ };
244
+ const operateWithExpectedDecimals = operation => (first, second) => {
245
+ const numDecimals1 = getNumDecimals(first);
246
+ const numDecimals2 = getNumDecimals(second);
247
+ const maxDecimals = operation === 'multiply' ? numDecimals1 + numDecimals2 : numDecimals1 > numDecimals2 ? numDecimals1 : numDecimals2;
248
+ return operation === 'sum' ? Number((first + second).toFixed(maxDecimals)) : operation === 'subtract' ? Number((first - second).toFixed(maxDecimals)) : Number((first * second).toFixed(maxDecimals));
249
+ };
250
+
251
+ // Operator key
252
+ const OPERATOR$k = Symbol('MULTIPLY');
253
+ const multiplyWithExpectedDecimals = operateWithExpectedDecimals('multiply');
254
+
255
+ /**
256
+ * Multiply operation expression
257
+ */
258
+ class Multiply extends Arithmetic {
259
+ /**
260
+ * @constructor Generic constructor
261
+ * @param {Evaluable[]} args
262
+ */
263
+
264
+ /**
265
+ * @constructor
266
+ * @param {Operand[]} operands Operands.
267
+ */
268
+ constructor(...operands) {
269
+ if (operands.length < 2) {
270
+ throw new Error('multiply expression requires at least 2 operands');
271
+ }
272
+ super('*', OPERATOR$k, operands);
273
+ }
274
+ operate(results) {
275
+ const presentResults = this.getResultValues(results);
276
+ if (presentResults === false) {
277
+ return false;
278
+ }
279
+ return presentResults.reduce((acc, result) => multiplyWithExpectedDecimals(acc, result));
280
+ }
281
+ }
282
+
283
+ // Operator key
284
+ const OPERATOR$j = Symbol('SUBTRACT');
285
+ const subtractWithExpectedDecimals = operateWithExpectedDecimals('subtract');
286
+
287
+ /**
288
+ * Subtract operation expression
289
+ */
290
+ class Subtract extends Arithmetic {
291
+ /**
292
+ * @constructor Generic constructor
293
+ * @param {Evaluable[]} args
294
+ */
295
+
296
+ /**
297
+ * @constructor
298
+ * @param {Operand[]} operands Operands.
299
+ */
300
+ constructor(...operands) {
301
+ if (operands.length < 2) {
302
+ throw new Error('subtract expression requires at least 2 operands');
303
+ }
304
+ super('-', OPERATOR$j, operands);
305
+ }
306
+ operate(results) {
307
+ const presentResults = this.getResultValues(results);
308
+ if (presentResults === false) {
309
+ return false;
310
+ }
311
+ return presentResults.reduce((acc, result) => subtractWithExpectedDecimals(acc, result));
312
+ }
313
+ }
314
+
315
+ // Operator key
316
+ const OPERATOR$i = Symbol('SUM');
317
+ const addWithExpectedDecimals = operateWithExpectedDecimals('sum');
318
+
319
+ /**
320
+ * Sum operation expression
321
+ */
322
+ class Sum extends Arithmetic {
323
+ /**
324
+ * @constructor Generic constructor
325
+ * @param {Evaluable[]} args
326
+ */
327
+
328
+ /**
329
+ * @constructor
330
+ * @param {Operand[]} operands Operands.
331
+ */
332
+ constructor(...operands) {
333
+ if (operands.length < 2) {
334
+ throw new Error('sum expression requires at least 2 operands');
335
+ }
336
+ super('+', OPERATOR$i, operands);
337
+ }
338
+ operate(results) {
339
+ const presentResults = this.getResultValues(results);
340
+ if (presentResults === false) {
341
+ return false;
342
+ }
343
+ return presentResults.reduce((acc, result) => addWithExpectedDecimals(acc, result));
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Abstract operand
349
+ */
350
+ class Operand {
351
+ constructor() {
352
+ _defineProperty(this, "type", EvaluableType.Operand);
353
+ }
354
+ /**
355
+ * {@link Evaluable.evaluate}
356
+ */
357
+ /**
358
+ * {@link Evaluable.simplify}
359
+ */
360
+ /**
361
+ * {@link Evaluable.serialize}
362
+ */
363
+ /**
364
+ * Get the strict representation.
365
+ */
366
+ toString() {
367
+ throw new Error('not implemented exception');
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Print value as string
373
+ * @param {Result} value
374
+ * @return {string}
375
+ */
376
+ function printValue(value) {
377
+ if (isString(value)) {
378
+ return `"${value}"`;
379
+ }
380
+ return `${value}`;
381
+ }
382
+
383
+ /**
384
+ * Static value operand
385
+ */
386
+ class Value extends Operand {
387
+ /**
388
+ * @constructor
389
+ * @param {Result} value Constant value.
390
+ */
391
+ constructor(value) {
392
+ if (Array.isArray(value)) {
393
+ throw new Error('deprecated direct usage of array, please use Collection operand');
394
+ }
395
+ super();
396
+ _defineProperty(this, "value", void 0);
397
+ this.value = value;
398
+ }
399
+
400
+ /**
401
+ * {@link Evaluable.evaluate}
402
+ */
403
+ evaluate() {
404
+ return this.value;
405
+ }
406
+
407
+ /**
408
+ * {@link Evaluable.simplify}
409
+ */
410
+ simplify() {
411
+ return this.value;
412
+ }
413
+
414
+ /**
415
+ * {@link Evaluable.serialize}
416
+ */
417
+ serialize() {
418
+ return this.value;
419
+ }
420
+
421
+ /**
422
+ * Get the strict representation of the operand.
423
+ * @return {string}
424
+ */
425
+ toString() {
426
+ return printValue(this.value);
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Convert a value to number if possible, otherwise return undefined
432
+ * @param value value to be converted to number
433
+ */
434
+ const toNumber = value => {
435
+ const isValueNumber = isNumber(value);
436
+ if (isValueNumber) {
437
+ return value;
438
+ } else if (isString(value)) {
439
+ if (value.match(/^\d+\.\d+$/)) {
440
+ return parseFloat(value);
441
+ } else if (value.match(/^0$|^[1-9]\d*$/)) {
442
+ return parseInt(value);
443
+ }
444
+ }
445
+ return undefined;
446
+ };
447
+
448
+ /**
449
+ * Convert a value to string if possible, otherwise return undefined
450
+ * @param value value to be converted to string
451
+ */
452
+ const toString = value => {
453
+ if (isNumber(value)) {
454
+ return `${value}`;
455
+ } else if (isString(value)) {
456
+ return value;
457
+ }
458
+ return undefined;
459
+ };
460
+ /**
461
+ * Convert a value to number if it's type is string, otherwise return NaN
462
+ * @param value value to be converted to number
463
+ */
464
+ const toDateNumber = value => {
465
+ if (isString(value)) {
466
+ return Date.parse(value);
467
+ }
468
+ return NaN;
469
+ };
470
+
471
+ const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
472
+ const arrayIndexRegex = /\[(\d+)]/g;
473
+ const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
474
+ const parseKey = key => {
475
+ const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
476
+ return !keys ? [] : keys.flatMap(key => {
477
+ const unwrappedKey = parseBacktickWrappedKey(key);
478
+ const keys = [];
479
+ const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
480
+ if (parseResult) {
481
+ var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
482
+ const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
483
+ keys.push(extractedKey);
484
+ const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
485
+ if (rawIndexes) {
486
+ for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
487
+ keys.push(parseInt(indexResult[1]));
488
+ }
489
+ }
490
+ } else {
491
+ keys.push(unwrappedKey);
492
+ }
493
+ return keys;
494
+ });
495
+ };
496
+ const complexKeyExpression = /{([^{}]+)}/;
497
+ function extractComplexKeys(ctx, key) {
498
+ // Resolve complex keys
499
+ let complexKeyMatches = complexKeyExpression.exec(key);
500
+ while (complexKeyMatches) {
501
+ const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
502
+ if (resolvedValue === undefined) {
503
+ return undefined;
504
+ }
505
+ key = key.replace(complexKeyExpression, `${resolvedValue}`);
506
+ complexKeyMatches = complexKeyExpression.exec(key);
507
+ }
508
+ return parseKey(key);
509
+ }
510
+ const isContext = value => isObject(value);
511
+ const simpleValueLookup = keys => ctx => {
512
+ let pointer = ctx;
513
+ for (const key of keys) {
514
+ if (typeof key === 'number') {
515
+ if (!Array.isArray(pointer)) {
516
+ return undefined;
517
+ }
518
+ pointer = pointer[key];
519
+ } else if (!isContext(pointer)) {
520
+ return undefined;
521
+ } else {
522
+ pointer = pointer[key];
523
+ }
524
+ }
525
+ return pointer;
526
+ };
527
+
528
+ /**
529
+ * Lookup for the reference in the context.
530
+ * The nested context value is annotated with "." delimiter.
531
+ * @example address.city
532
+ * @param {Context} ctx
533
+ * @param {string} key Context lookup key.
534
+ * @return {Result}
535
+ */
536
+ function complexValueLookup(ctx, key) {
537
+ const keys = extractComplexKeys(ctx, key);
538
+ if (!keys) {
539
+ return undefined;
540
+ }
541
+ return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
542
+ }
543
+ let DataType = /*#__PURE__*/function (DataType) {
544
+ DataType["Number"] = "Number";
545
+ DataType["String"] = "String";
546
+ return DataType;
547
+ }({});
548
+
549
+ // Equivalent to /^.+\.\((Number|String)\)$/
550
+ const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
551
+ const isComplexKey = key => key.indexOf('{') > -1;
552
+
553
+ /**
554
+ * Reference operand resolved within the context
555
+ */
556
+ class Reference extends Operand {
557
+ /**
558
+ * @constructor
559
+ * @param {string} key Context key.
560
+ */
561
+ constructor(key) {
562
+ if (key.trim() === '') {
563
+ throw new Error('invalid reference key');
564
+ }
565
+ super();
566
+ _defineProperty(this, "key", void 0);
567
+ _defineProperty(this, "dataType", void 0);
568
+ _defineProperty(this, "valueLookup", void 0);
569
+ _defineProperty(this, "getKeys", void 0);
570
+ this.key = key;
571
+ const dataTypeMatch = dataTypeRegex.exec(this.key);
572
+ if (dataTypeMatch) {
573
+ this.dataType = DataType[dataTypeMatch[1]];
574
+ }
575
+ if (this.key.match(/.\(.+\)$/)) {
576
+ this.key = this.key.replace(/.\(.+\)$/, '');
577
+ }
578
+ if (isComplexKey(this.key)) {
579
+ this.valueLookup = context => complexValueLookup(context, this.key);
580
+ this.getKeys = context => extractComplexKeys(context, this.key);
581
+ } else {
582
+ const keys = parseKey(this.key);
583
+ this.valueLookup = simpleValueLookup(keys);
584
+ this.getKeys = () => keys;
585
+ }
586
+ }
72
587
 
73
- /**
74
- * Check if a value is a an Evaluable
75
- * @param {Result | Evaluable} value value to check if is Evaluable
76
- * @returns {Evaluable}
77
- */
78
- function isEvaluable(value) {
79
- return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value.evaluate === 'function' && typeof value.simplify === 'function' && typeof value.serialize === 'function' && typeof value.toString === 'function';
80
- }
588
+ /**
589
+ * Evaluate in the given context.
590
+ * @param {Context} ctx
591
+ * @return {boolean}
592
+ */
593
+ evaluate(ctx) {
594
+ return this.toDataType(this.valueLookup(ctx));
595
+ }
81
596
 
82
- /**
83
- * Valid types for context members
84
- */
597
+ /**
598
+ * {@link Evaluable.simplify}
599
+ */
600
+ simplify(ctx, strictKeys, optionalKeys) {
601
+ var _this$getKeys;
602
+ const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
603
+ if (ctx[key] !== undefined) {
604
+ return this.evaluate(ctx);
605
+ }
606
+ if (!key || typeof key === 'number') {
607
+ return this;
608
+ }
609
+ return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
610
+ }
85
611
 
86
- /**
87
- * Evaluation Context
88
- * Holds references used during the evaluation process.
89
- * Format: key: value.
90
- */
612
+ /**
613
+ * {@link Evaluable.serialize}
614
+ */
615
+ serialize({
616
+ referenceSerialization
617
+ }) {
618
+ const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
619
+ return referenceSerialization(key);
620
+ }
91
621
 
92
- /**
93
- * Evaluation result
94
- */
622
+ /**
623
+ * Get the strict representation of the operand.
624
+ * @return {string}
625
+ */
626
+ toString() {
627
+ return `{${this.key}}`;
628
+ }
95
629
 
96
- let EvaluableType = /*#__PURE__*/function (EvaluableType) {
97
- EvaluableType["Operand"] = "Operand";
98
- EvaluableType["Expression"] = "Expression";
99
- return EvaluableType;
100
- }({});
630
+ /**
631
+ * Converts a value to a specified data type
632
+ * Silently returns original value if data type conversion has not been implemented.
633
+ * @param value value to cast as data type
634
+ */
635
+ toDataType(value) {
636
+ let result = value;
637
+ switch (this.dataType) {
638
+ case DataType.Number:
639
+ result = toNumber(value);
640
+ break;
641
+ case DataType.String:
642
+ result = toString(value);
643
+ break;
644
+ }
645
+ if (value && result === undefined) {
646
+ console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
647
+ }
648
+ return result;
649
+ }
650
+ }
101
651
 
102
- /**
103
- * Evaluable
104
- */
652
+ const isSimplifiedArithmeticExpression = (operand, result) => operand instanceof Arithmetic && !isEvaluable(result) && !isInfinite(result) && !(operand instanceof Reference);
105
653
 
106
654
  /**
107
655
  * Abstract comparison expression
@@ -152,6 +700,12 @@ class Comparison {
152
700
  if (!isEvaluable(left) && !isEvaluable(right)) {
153
701
  return this.comparison(left, right);
154
702
  }
703
+ if (isEvaluable(left) && isSimplifiedArithmeticExpression(this.right, right)) {
704
+ return Reflect.construct(this.constructor, [left, new Value(right)]);
705
+ }
706
+ if (isEvaluable(right) && isSimplifiedArithmeticExpression(this.left, left)) {
707
+ return Reflect.construct(this.constructor, [new Value(left), right]);
708
+ }
155
709
  return this;
156
710
  }
157
711
 
@@ -198,47 +752,6 @@ class Equal extends Comparison {
198
752
  }
199
753
  }
200
754
 
201
- /**
202
- * Convert a value to number if possible, otherwise return undefined
203
- * @param value value to be converted to number
204
- */
205
- const toNumber = value => {
206
- const isValueNumber = isNumber(value);
207
- if (isValueNumber) {
208
- return value;
209
- } else if (isString(value)) {
210
- if (value.match(/^\d+\.\d+$/)) {
211
- return parseFloat(value);
212
- } else if (value.match(/^0$|^[1-9]\d*$/)) {
213
- return parseInt(value);
214
- }
215
- }
216
- return undefined;
217
- };
218
-
219
- /**
220
- * Convert a value to string if possible, otherwise return undefined
221
- * @param value value to be converted to string
222
- */
223
- const toString = value => {
224
- if (isNumber(value)) {
225
- return `${value}`;
226
- } else if (isString(value)) {
227
- return value;
228
- }
229
- return undefined;
230
- };
231
- /**
232
- * Convert a value to number if it's type is string, otherwise return NaN
233
- * @param value value to be converted to number
234
- */
235
- const toDateNumber = value => {
236
- if (isString(value)) {
237
- return Date.parse(value);
238
- }
239
- return NaN;
240
- };
241
-
242
755
  // Operator key
243
756
  const OPERATOR$g = Symbol('GE');
244
757
 
@@ -555,142 +1068,59 @@ class Overlap extends Comparison {
555
1068
  const rightArray = right;
556
1069
  if (leftArray.length === 0 && rightArray.length === 0) {
557
1070
  return true;
558
- }
559
- return leftArray.some(element => rightArray.includes(element));
560
- }
561
-
562
- /**
563
- * Get the strict representation of the expression.
564
- * @return {string}
565
- */
566
- toString() {
567
- const left = this.left.toString();
568
- const right = this.right.toString();
569
- return `(${left} ${this.operator} ${right})`;
570
- }
571
- }
572
-
573
- // Operator key
574
- const OPERATOR$8 = Symbol('PREFIX');
575
-
576
- /**
577
- * Prefix comparison expression
578
- */
579
- class Prefix extends Comparison {
580
- /**
581
- * @constructor
582
- * @param {Evaluable} left Left operand.
583
- * @param {Evaluable} right Right operand.
584
- */
585
-
586
- constructor(left, right) {
587
- if (arguments.length !== 2) {
588
- throw new Error('comparison expression expects left and right operands');
589
- }
590
- super('prefix', OPERATOR$8, left, right);
591
- }
592
-
593
- /**
594
- * {@link Comparison.comparison}
595
- */
596
- comparison(left, right) {
597
- if (isString(left) === false || isString(right) === false) {
598
- return false;
599
- }
600
- return right.startsWith(left);
601
- }
602
-
603
- /**
604
- * Get the strict representation of the expression.
605
- * @return {string}
606
- */
607
- toString() {
608
- const left = this.left.toString();
609
- const right = this.right.toString();
610
- return `(<${left}>${right})`;
611
- }
612
- }
613
-
614
- /**
615
- * Abstract operand
616
- */
617
- class Operand {
618
- constructor() {
619
- _defineProperty(this, "type", EvaluableType.Operand);
620
- }
621
- /**
622
- * {@link Evaluable.evaluate}
623
- */
624
- /**
625
- * {@link Evaluable.simplify}
626
- */
627
- /**
628
- * {@link Evaluable.serialize}
629
- */
630
- /**
631
- * Get the strict representation.
632
- */
633
- toString() {
634
- throw new Error('not implemented exception');
635
- }
636
- }
637
-
638
- /**
639
- * Print value as string
640
- * @param {Result} value
641
- * @return {string}
642
- */
643
- function printValue(value) {
644
- if (isString(value)) {
645
- return `"${value}"`;
646
- }
647
- return `${value}`;
648
- }
649
-
650
- /**
651
- * Static value operand
652
- */
653
- class Value extends Operand {
654
- /**
655
- * @constructor
656
- * @param {Result} value Constant value.
657
- */
658
- constructor(value) {
659
- if (Array.isArray(value)) {
660
- throw new Error('deprecated direct usage of array, please use Collection operand');
661
- }
662
- super();
663
- _defineProperty(this, "value", void 0);
664
- this.value = value;
1071
+ }
1072
+ return leftArray.some(element => rightArray.includes(element));
665
1073
  }
666
1074
 
667
1075
  /**
668
- * {@link Evaluable.evaluate}
1076
+ * Get the strict representation of the expression.
1077
+ * @return {string}
669
1078
  */
670
- evaluate() {
671
- return this.value;
1079
+ toString() {
1080
+ const left = this.left.toString();
1081
+ const right = this.right.toString();
1082
+ return `(${left} ${this.operator} ${right})`;
672
1083
  }
1084
+ }
673
1085
 
1086
+ // Operator key
1087
+ const OPERATOR$8 = Symbol('PREFIX');
1088
+
1089
+ /**
1090
+ * Prefix comparison expression
1091
+ */
1092
+ class Prefix extends Comparison {
674
1093
  /**
675
- * {@link Evaluable.simplify}
1094
+ * @constructor
1095
+ * @param {Evaluable} left Left operand.
1096
+ * @param {Evaluable} right Right operand.
676
1097
  */
677
- simplify() {
678
- return this.value;
1098
+
1099
+ constructor(left, right) {
1100
+ if (arguments.length !== 2) {
1101
+ throw new Error('comparison expression expects left and right operands');
1102
+ }
1103
+ super('prefix', OPERATOR$8, left, right);
679
1104
  }
680
1105
 
681
1106
  /**
682
- * {@link Evaluable.serialize}
1107
+ * {@link Comparison.comparison}
683
1108
  */
684
- serialize() {
685
- return this.value;
1109
+ comparison(left, right) {
1110
+ if (isString(left) === false || isString(right) === false) {
1111
+ return false;
1112
+ }
1113
+ return right.startsWith(left);
686
1114
  }
687
1115
 
688
1116
  /**
689
- * Get the strict representation of the operand.
1117
+ * Get the strict representation of the expression.
690
1118
  * @return {string}
691
1119
  */
692
1120
  toString() {
693
- return printValue(this.value);
1121
+ const left = this.left.toString();
1122
+ const right = this.right.toString();
1123
+ return `(<${left}>${right})`;
694
1124
  }
695
1125
  }
696
1126
 
@@ -1237,187 +1667,6 @@ class Collection extends Operand {
1237
1667
  }
1238
1668
  }
1239
1669
 
1240
- const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
1241
- const arrayIndexRegex = /\[(\d+)]/g;
1242
- const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
1243
- const parseKey = key => {
1244
- const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
1245
- return !keys ? [] : keys.flatMap(key => {
1246
- const unwrappedKey = parseBacktickWrappedKey(key);
1247
- const keys = [];
1248
- const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
1249
- if (parseResult) {
1250
- var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
1251
- const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
1252
- keys.push(extractedKey);
1253
- const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
1254
- if (rawIndexes) {
1255
- for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
1256
- keys.push(parseInt(indexResult[1]));
1257
- }
1258
- }
1259
- } else {
1260
- keys.push(unwrappedKey);
1261
- }
1262
- return keys;
1263
- });
1264
- };
1265
- const complexKeyExpression = /{([^{}]+)}/;
1266
- function extractComplexKeys(ctx, key) {
1267
- // Resolve complex keys
1268
- let complexKeyMatches = complexKeyExpression.exec(key);
1269
- while (complexKeyMatches) {
1270
- const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
1271
- if (resolvedValue === undefined) {
1272
- return undefined;
1273
- }
1274
- key = key.replace(complexKeyExpression, `${resolvedValue}`);
1275
- complexKeyMatches = complexKeyExpression.exec(key);
1276
- }
1277
- return parseKey(key);
1278
- }
1279
- const isContext = value => isObject(value);
1280
- const simpleValueLookup = keys => ctx => {
1281
- let pointer = ctx;
1282
- for (const key of keys) {
1283
- if (typeof key === 'number') {
1284
- if (!Array.isArray(pointer)) {
1285
- return undefined;
1286
- }
1287
- pointer = pointer[key];
1288
- } else if (!isContext(pointer)) {
1289
- return undefined;
1290
- } else {
1291
- pointer = pointer[key];
1292
- }
1293
- }
1294
- return pointer;
1295
- };
1296
-
1297
- /**
1298
- * Lookup for the reference in the context.
1299
- * The nested context value is annotated with "." delimiter.
1300
- * @example address.city
1301
- * @param {Context} ctx
1302
- * @param {string} key Context lookup key.
1303
- * @return {Result}
1304
- */
1305
- function complexValueLookup(ctx, key) {
1306
- const keys = extractComplexKeys(ctx, key);
1307
- if (!keys) {
1308
- return undefined;
1309
- }
1310
- return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
1311
- }
1312
- let DataType = /*#__PURE__*/function (DataType) {
1313
- DataType["Number"] = "Number";
1314
- DataType["String"] = "String";
1315
- return DataType;
1316
- }({});
1317
-
1318
- // Equivalent to /^.+\.\((Number|String)\)$/
1319
- const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
1320
- const isComplexKey = key => key.indexOf('{') > -1;
1321
-
1322
- /**
1323
- * Reference operand resolved within the context
1324
- */
1325
- class Reference extends Operand {
1326
- /**
1327
- * @constructor
1328
- * @param {string} key Context key.
1329
- */
1330
- constructor(key) {
1331
- if (key.trim() === '') {
1332
- throw new Error('invalid reference key');
1333
- }
1334
- super();
1335
- _defineProperty(this, "key", void 0);
1336
- _defineProperty(this, "dataType", void 0);
1337
- _defineProperty(this, "valueLookup", void 0);
1338
- _defineProperty(this, "getKeys", void 0);
1339
- this.key = key;
1340
- const dataTypeMatch = dataTypeRegex.exec(this.key);
1341
- if (dataTypeMatch) {
1342
- this.dataType = DataType[dataTypeMatch[1]];
1343
- }
1344
- if (this.key.match(/.\(.+\)$/)) {
1345
- this.key = this.key.replace(/.\(.+\)$/, '');
1346
- }
1347
- if (isComplexKey(this.key)) {
1348
- this.valueLookup = context => complexValueLookup(context, this.key);
1349
- this.getKeys = context => extractComplexKeys(context, this.key);
1350
- } else {
1351
- const keys = parseKey(this.key);
1352
- this.valueLookup = simpleValueLookup(keys);
1353
- this.getKeys = () => keys;
1354
- }
1355
- }
1356
-
1357
- /**
1358
- * Evaluate in the given context.
1359
- * @param {Context} ctx
1360
- * @return {boolean}
1361
- */
1362
- evaluate(ctx) {
1363
- return this.toDataType(this.valueLookup(ctx));
1364
- }
1365
-
1366
- /**
1367
- * {@link Evaluable.simplify}
1368
- */
1369
- simplify(ctx, strictKeys, optionalKeys) {
1370
- var _this$getKeys;
1371
- const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
1372
- if (ctx[key] !== undefined) {
1373
- return this.evaluate(ctx);
1374
- }
1375
- if (!key || typeof key === 'number') {
1376
- return this;
1377
- }
1378
- return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
1379
- }
1380
-
1381
- /**
1382
- * {@link Evaluable.serialize}
1383
- */
1384
- serialize({
1385
- referenceSerialization
1386
- }) {
1387
- const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
1388
- return referenceSerialization(key);
1389
- }
1390
-
1391
- /**
1392
- * Get the strict representation of the operand.
1393
- * @return {string}
1394
- */
1395
- toString() {
1396
- return `{${this.key}}`;
1397
- }
1398
-
1399
- /**
1400
- * Converts a value to a specified data type
1401
- * Silently returns original value if data type conversion has not been implemented.
1402
- * @param value value to cast as data type
1403
- */
1404
- toDataType(value) {
1405
- let result = value;
1406
- switch (this.dataType) {
1407
- case DataType.Number:
1408
- result = toNumber(value);
1409
- break;
1410
- case DataType.String:
1411
- result = toString(value);
1412
- break;
1413
- }
1414
- if (value && result === undefined) {
1415
- console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
1416
- }
1417
- return result;
1418
- }
1419
- }
1420
-
1421
1670
  // Option value whitelist
1422
1671
 
1423
1672
  // Parser options
@@ -1453,7 +1702,9 @@ const defaultOperatorMapping = new Map([
1453
1702
  // Comparison
1454
1703
  [OPERATOR$h, '=='], [OPERATOR$b, '!='], [OPERATOR$f, '>'], [OPERATOR$g, '>='], [OPERATOR$c, '<'], [OPERATOR$d, '<='], [OPERATOR$e, 'IN'], [OPERATOR$a, 'NOT IN'], [OPERATOR$8, 'PREFIX'], [OPERATOR$6, 'SUFFIX'], [OPERATOR$9, 'OVERLAP'], [OPERATOR$5, 'UNDEFINED'], [OPERATOR$7, 'PRESENT'],
1455
1704
  // Logical
1456
- [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT']]);
1705
+ [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT'],
1706
+ // Arithmetic
1707
+ [OPERATOR$i, '+'], [OPERATOR$j, '-'], [OPERATOR$k, '*'], [OPERATOR$l, '/']]);
1457
1708
 
1458
1709
  /**
1459
1710
  * Default parser options
@@ -1467,6 +1718,14 @@ const defaultOptions = {
1467
1718
 
1468
1719
  // Input types
1469
1720
 
1721
+ const invalidExpression = 'invalid expression';
1722
+ const logicalIfValidOperands = (operands, logical) => {
1723
+ if (operands.every(operand => operand instanceof Logical || operand instanceof Comparison)) {
1724
+ return logical;
1725
+ }
1726
+ throw new Error(invalidExpression);
1727
+ };
1728
+
1470
1729
  /**
1471
1730
  * Parser of raw expressions into Evaluable expression
1472
1731
  */
@@ -1477,7 +1736,8 @@ class Parser {
1477
1736
  */
1478
1737
  constructor(options) {
1479
1738
  _defineProperty(this, "opts", void 0);
1480
- _defineProperty(this, "expectedOperators", void 0);
1739
+ _defineProperty(this, "expectedRootOperators", void 0);
1740
+ _defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
1481
1741
  this.opts = {
1482
1742
  ...defaultOptions
1483
1743
  };
@@ -1489,7 +1749,7 @@ class Parser {
1489
1749
  }
1490
1750
  }
1491
1751
  }
1492
- this.expectedOperators = new Set(this.opts.operatorMapping.values());
1752
+ this.expectedRootOperators = new Set(Array.from(this.opts.operatorMapping.entries()).filter(([symbol]) => !this.unexpectedRootSymbols.has(symbol)).map(([, operator]) => operator));
1493
1753
  }
1494
1754
 
1495
1755
  /**
@@ -1507,10 +1767,10 @@ class Parser {
1507
1767
  */
1508
1768
  parse(raw) {
1509
1769
  if (raw === undefined || raw === null || Array.isArray(raw) === false) {
1510
- throw new Error('invalid expression');
1770
+ throw new Error(invalidExpression);
1511
1771
  }
1512
- if (raw.length === 0 || !this.expectedOperators.has(`${raw[0]}`)) {
1513
- throw new Error('invalid expression');
1772
+ if (raw.length === 0 || !this.expectedRootOperators.has(`${raw[0]}`)) {
1773
+ throw new Error(invalidExpression);
1514
1774
  }
1515
1775
  return this.parseRawExp(raw);
1516
1776
  }
@@ -1548,71 +1808,103 @@ class Parser {
1548
1808
  return collapsible && operands.length === 1 ? operands[0] : undefined;
1549
1809
  };
1550
1810
  switch (operator) {
1551
- /**
1552
- * Logical
1553
- */
1811
+ // Logical
1554
1812
  case this.opts.operatorMapping.get(OPERATOR$4):
1555
- expression = operands => logicalExpressionReducer(operands, true) || new And(operands);
1813
+ expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new And(operands));
1556
1814
  operandParser = this.parseRawExp;
1557
1815
  break;
1558
1816
  case this.opts.operatorMapping.get(OPERATOR$1):
1559
- expression = operands => logicalExpressionReducer(operands, true) || new Or(operands);
1817
+ expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new Or(operands));
1560
1818
  operandParser = this.parseRawExp;
1561
1819
  break;
1562
1820
  case this.opts.operatorMapping.get(OPERATOR$2):
1563
- expression = operands => logicalExpressionReducer(operands) || new Nor(operands);
1821
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Nor(operands));
1564
1822
  operandParser = this.parseRawExp;
1565
1823
  break;
1566
1824
  case this.opts.operatorMapping.get(OPERATOR):
1567
- expression = operands => logicalExpressionReducer(operands) || new Xor(operands);
1825
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Xor(operands));
1568
1826
  operandParser = this.parseRawExp;
1569
1827
  break;
1570
1828
  case this.opts.operatorMapping.get(OPERATOR$3):
1571
- expression = operands => logicalExpressionReducer(operands) || new Not(...operands);
1829
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Not(...operands));
1572
1830
  operandParser = this.parseRawExp;
1573
1831
  break;
1574
- /**
1575
- * Comparison
1576
- */
1832
+
1833
+ // Comparison
1577
1834
  case this.opts.operatorMapping.get(OPERATOR$h):
1578
1835
  expression = operands => new Equal(...operands);
1836
+ operandParser = this.parseRawExp;
1579
1837
  break;
1580
1838
  case this.opts.operatorMapping.get(OPERATOR$b):
1581
1839
  expression = operands => new NotEqual(...operands);
1840
+ operandParser = this.parseRawExp;
1582
1841
  break;
1583
1842
  case this.opts.operatorMapping.get(OPERATOR$f):
1584
1843
  expression = operands => new GreaterThan(...operands);
1844
+ operandParser = this.parseRawExp;
1585
1845
  break;
1586
1846
  case this.opts.operatorMapping.get(OPERATOR$g):
1587
1847
  expression = operands => new GreaterThanOrEqual(...operands);
1848
+ operandParser = this.parseRawExp;
1588
1849
  break;
1589
1850
  case this.opts.operatorMapping.get(OPERATOR$c):
1590
1851
  expression = operands => new LessThan(...operands);
1852
+ operandParser = this.parseRawExp;
1591
1853
  break;
1592
1854
  case this.opts.operatorMapping.get(OPERATOR$d):
1593
1855
  expression = operands => new LessThanOrEqual(...operands);
1856
+ operandParser = this.parseRawExp;
1594
1857
  break;
1858
+
1859
+ // Containment
1595
1860
  case this.opts.operatorMapping.get(OPERATOR$e):
1596
1861
  expression = operands => new In(...operands);
1597
1862
  break;
1598
1863
  case this.opts.operatorMapping.get(OPERATOR$a):
1599
1864
  expression = operands => new NotIn(...operands);
1600
1865
  break;
1866
+
1867
+ // Prefix
1601
1868
  case this.opts.operatorMapping.get(OPERATOR$8):
1602
1869
  expression = operands => new Prefix(...operands);
1603
1870
  break;
1871
+
1872
+ // Suffix
1604
1873
  case this.opts.operatorMapping.get(OPERATOR$6):
1605
1874
  expression = operands => new Suffix(...operands);
1606
1875
  break;
1876
+
1877
+ // Overlap
1607
1878
  case this.opts.operatorMapping.get(OPERATOR$9):
1608
1879
  expression = operands => new Overlap(...operands);
1609
1880
  break;
1881
+
1882
+ // Presence
1610
1883
  case this.opts.operatorMapping.get(OPERATOR$5):
1611
1884
  expression = operands => new Undefined(...operands);
1612
1885
  break;
1613
1886
  case this.opts.operatorMapping.get(OPERATOR$7):
1614
1887
  expression = operands => new Present(...operands);
1615
1888
  break;
1889
+
1890
+ // Arithmetic
1891
+ case this.opts.operatorMapping.get(OPERATOR$i):
1892
+ expression = operands => new Sum(...operands);
1893
+ operandParser = this.parseRawExp;
1894
+ break;
1895
+ case this.opts.operatorMapping.get(OPERATOR$j):
1896
+ expression = operands => new Subtract(...operands);
1897
+ operandParser = this.parseRawExp;
1898
+ break;
1899
+ case this.opts.operatorMapping.get(OPERATOR$k):
1900
+ expression = operands => new Multiply(...operands);
1901
+ operandParser = this.parseRawExp;
1902
+ break;
1903
+ case this.opts.operatorMapping.get(OPERATOR$l):
1904
+ expression = operands => new Divide(...operands);
1905
+ operandParser = this.parseRawExp;
1906
+ break;
1907
+
1616
1908
  // Collection
1617
1909
  default:
1618
1910
  return this.getOperand(raw);
@@ -1633,6 +1925,8 @@ class Parser {
1633
1925
  }
1634
1926
  }
1635
1927
 
1928
+ const unexpectedResultError = 'non expression or boolean result should be returned';
1929
+
1636
1930
  /**
1637
1931
  * Condition engine
1638
1932
  */
@@ -1653,7 +1947,11 @@ class Engine {
1653
1947
  * @return {boolean}
1654
1948
  */
1655
1949
  evaluate(exp, ctx) {
1656
- return this.parse(exp).evaluate(ctx);
1950
+ const result = this.parse(exp).evaluate(ctx);
1951
+ if (isBoolean(result)) {
1952
+ return result;
1953
+ }
1954
+ throw new Error(unexpectedResultError);
1657
1955
  }
1658
1956
 
1659
1957
  /**
@@ -1697,17 +1995,19 @@ class Engine {
1697
1995
  if (isBoolean(result)) {
1698
1996
  return result;
1699
1997
  }
1700
- throw new Error('non expression or boolean result should be returned');
1998
+ throw new Error(unexpectedResultError);
1701
1999
  }
1702
2000
  }
1703
2001
 
1704
2002
  exports.OPERATOR_AND = OPERATOR$4;
2003
+ exports.OPERATOR_DIVIDE = OPERATOR$l;
1705
2004
  exports.OPERATOR_EQ = OPERATOR$h;
1706
2005
  exports.OPERATOR_GE = OPERATOR$g;
1707
2006
  exports.OPERATOR_GT = OPERATOR$f;
1708
2007
  exports.OPERATOR_IN = OPERATOR$e;
1709
2008
  exports.OPERATOR_LE = OPERATOR$d;
1710
2009
  exports.OPERATOR_LT = OPERATOR$c;
2010
+ exports.OPERATOR_MULTIPLY = OPERATOR$k;
1711
2011
  exports.OPERATOR_NE = OPERATOR$b;
1712
2012
  exports.OPERATOR_NOR = OPERATOR$2;
1713
2013
  exports.OPERATOR_NOT = OPERATOR$3;
@@ -1716,7 +2016,9 @@ exports.OPERATOR_OR = OPERATOR$1;
1716
2016
  exports.OPERATOR_OVERLAP = OPERATOR$9;
1717
2017
  exports.OPERATOR_PREFIX = OPERATOR$8;
1718
2018
  exports.OPERATOR_PRESENT = OPERATOR$7;
2019
+ exports.OPERATOR_SUBTRACT = OPERATOR$j;
1719
2020
  exports.OPERATOR_SUFFIX = OPERATOR$6;
2021
+ exports.OPERATOR_SUM = OPERATOR$i;
1720
2022
  exports.OPERATOR_UNDEFINED = OPERATOR$5;
1721
2023
  exports.OPERATOR_XOR = OPERATOR;
1722
2024
  exports.default = Engine;