@briza/illogical 1.5.9 → 1.6.0

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,573 @@ 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
+ * Performs the arithmetic operation on the operands evaluated values.
144
+ * @param {Result[]} results Operand result values.
145
+ * @returns {Result}
146
+ */
147
+
148
+ /**
149
+ * {@link Evaluable.evaluate}
150
+ */
151
+ evaluate(ctx) {
152
+ return this.operate(this.operands.map(operand => operand.evaluate(ctx)));
153
+ }
154
+
155
+ /**
156
+ * {@link Evaluable.toString}
157
+ */
158
+ toString() {
159
+ return `(${this.operands.map(operand => operand.toString()).join(` ${this.operator} `)})`;
160
+ }
161
+
162
+ /**
163
+ * {@link Evaluable.simplify}
164
+ */
165
+ simplify(...args) {
166
+ const results = this.operands.map(operand => operand.simplify(...args));
167
+ if (areAllResults(results)) {
168
+ return this.operate(results);
169
+ }
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * {@link Evaluable.serialize}
175
+ */
176
+ serialize(options) {
177
+ const {
178
+ operatorMapping
179
+ } = options;
180
+ const operator = operatorMapping.get(this.operatorSymbol);
181
+ if (operator === undefined) {
182
+ throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
183
+ }
184
+ return [operator, ...this.operands.map(operand => operand.serialize(options))];
185
+ }
186
+ }
187
+
188
+ // Operator key
189
+ const OPERATOR$l = Symbol('DIVIDE');
190
+
191
+ /**
192
+ * Divide operation expression
193
+ */
194
+ class Divide extends Arithmetic {
195
+ /**
196
+ * @constructor Generic constructor
197
+ * @param {Evaluable[]} args
198
+ */
199
+
200
+ /**
201
+ * @constructor
202
+ * @param {Operand[]} operands Operands.
203
+ */
204
+ constructor(...operands) {
205
+ if (operands.length < 2) {
206
+ throw new Error('divide expression requires at least 2 operands');
207
+ }
208
+ super('/', OPERATOR$l, operands);
209
+ }
210
+ operate(results) {
211
+ if (!areAllNumbers(results)) {
212
+ throw new Error('operands must be numbers for divide');
213
+ }
214
+ const result = results.reduce((acc, result) => acc / result);
215
+ return result;
216
+ }
217
+ }
218
+
219
+ const getNumDecimals = num => {
220
+ const numberSplit = num.toString().split('.');
221
+ return numberSplit.length == 2 ? numberSplit[1].length : 0;
222
+ };
223
+ const operateWithExpectedDecimals = operation => (first, second) => {
224
+ const numDecimals1 = getNumDecimals(first);
225
+ const numDecimals2 = getNumDecimals(second);
226
+ const maxDecimals = operation === 'multiply' ? numDecimals1 + numDecimals2 : numDecimals1 > numDecimals2 ? numDecimals1 : numDecimals2;
227
+ return operation === 'sum' ? Number((first + second).toFixed(maxDecimals)) : operation === 'subtract' ? Number((first - second).toFixed(maxDecimals)) : Number((first * second).toFixed(maxDecimals));
228
+ };
229
+
230
+ // Operator key
231
+ const OPERATOR$k = Symbol('MULTIPLY');
232
+ const multiplyWithExpectedDecimals = operateWithExpectedDecimals('multiply');
233
+
234
+ /**
235
+ * Multiply operation expression
236
+ */
237
+ class Multiply extends Arithmetic {
238
+ /**
239
+ * @constructor Generic constructor
240
+ * @param {Evaluable[]} args
241
+ */
242
+
243
+ /**
244
+ * @constructor
245
+ * @param {Operand[]} operands Operands.
246
+ */
247
+ constructor(...operands) {
248
+ if (operands.length < 2) {
249
+ throw new Error('multiply expression requires at least 2 operands');
250
+ }
251
+ super('*', OPERATOR$k, operands);
252
+ }
253
+ operate(results) {
254
+ if (!areAllNumbers(results)) {
255
+ throw new Error('operands must be numbers for multiply');
256
+ }
257
+ return results.reduce((acc, result) => multiplyWithExpectedDecimals(acc, result));
258
+ }
259
+ }
260
+
261
+ // Operator key
262
+ const OPERATOR$j = Symbol('SUBTRACT');
263
+ const subtractWithExpectedDecimals = operateWithExpectedDecimals('subtract');
264
+
265
+ /**
266
+ * Subtract operation expression
267
+ */
268
+ class Subtract extends Arithmetic {
269
+ /**
270
+ * @constructor Generic constructor
271
+ * @param {Evaluable[]} args
272
+ */
273
+
274
+ /**
275
+ * @constructor
276
+ * @param {Operand[]} operands Operands.
277
+ */
278
+ constructor(...operands) {
279
+ if (operands.length < 2) {
280
+ throw new Error('subtract expression requires at least 2 operands');
281
+ }
282
+ super('-', OPERATOR$j, operands);
283
+ }
284
+ operate(results) {
285
+ if (!areAllNumbers(results)) {
286
+ throw new Error('operands must be numbers for subtract');
287
+ }
288
+ return results.reduce((acc, result) => subtractWithExpectedDecimals(acc, result));
289
+ }
290
+ }
291
+
292
+ // Operator key
293
+ const OPERATOR$i = Symbol('SUM');
294
+ const addWithExpectedDecimals = operateWithExpectedDecimals('sum');
295
+
296
+ /**
297
+ * Sum operation expression
298
+ */
299
+ class Sum extends Arithmetic {
300
+ /**
301
+ * @constructor Generic constructor
302
+ * @param {Evaluable[]} args
303
+ */
304
+
305
+ /**
306
+ * @constructor
307
+ * @param {Operand[]} operands Operands.
308
+ */
309
+ constructor(...operands) {
310
+ if (operands.length < 2) {
311
+ throw new Error('sum expression requires at least 2 operands');
312
+ }
313
+ super('+', OPERATOR$i, operands);
314
+ }
315
+ operate(results) {
316
+ if (!areAllNumbers(results)) {
317
+ throw new Error('operands must be numbers for sum');
318
+ }
319
+ return results.reduce((acc, result) => addWithExpectedDecimals(acc, result));
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Abstract operand
325
+ */
326
+ class Operand {
327
+ constructor() {
328
+ _defineProperty(this, "type", EvaluableType.Operand);
329
+ }
330
+ /**
331
+ * {@link Evaluable.evaluate}
332
+ */
333
+ /**
334
+ * {@link Evaluable.simplify}
335
+ */
336
+ /**
337
+ * {@link Evaluable.serialize}
338
+ */
339
+ /**
340
+ * Get the strict representation.
341
+ */
342
+ toString() {
343
+ throw new Error('not implemented exception');
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Print value as string
349
+ * @param {Result} value
350
+ * @return {string}
351
+ */
352
+ function printValue(value) {
353
+ if (isString(value)) {
354
+ return `"${value}"`;
355
+ }
356
+ return `${value}`;
357
+ }
358
+
359
+ /**
360
+ * Static value operand
361
+ */
362
+ class Value extends Operand {
363
+ /**
364
+ * @constructor
365
+ * @param {Result} value Constant value.
366
+ */
367
+ constructor(value) {
368
+ if (Array.isArray(value)) {
369
+ throw new Error('deprecated direct usage of array, please use Collection operand');
370
+ }
371
+ super();
372
+ _defineProperty(this, "value", void 0);
373
+ this.value = value;
374
+ }
375
+
376
+ /**
377
+ * {@link Evaluable.evaluate}
378
+ */
379
+ evaluate() {
380
+ return this.value;
381
+ }
382
+
383
+ /**
384
+ * {@link Evaluable.simplify}
385
+ */
386
+ simplify() {
387
+ return this.value;
388
+ }
389
+
390
+ /**
391
+ * {@link Evaluable.serialize}
392
+ */
393
+ serialize() {
394
+ return this.value;
395
+ }
396
+
397
+ /**
398
+ * Get the strict representation of the operand.
399
+ * @return {string}
400
+ */
401
+ toString() {
402
+ return printValue(this.value);
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Convert a value to number if possible, otherwise return undefined
408
+ * @param value value to be converted to number
409
+ */
410
+ const toNumber = value => {
411
+ const isValueNumber = isNumber(value);
412
+ if (isValueNumber) {
413
+ return value;
414
+ } else if (isString(value)) {
415
+ if (value.match(/^\d+\.\d+$/)) {
416
+ return parseFloat(value);
417
+ } else if (value.match(/^0$|^[1-9]\d*$/)) {
418
+ return parseInt(value);
419
+ }
420
+ }
421
+ return undefined;
422
+ };
423
+
424
+ /**
425
+ * Convert a value to string if possible, otherwise return undefined
426
+ * @param value value to be converted to string
427
+ */
428
+ const toString = value => {
429
+ if (isNumber(value)) {
430
+ return `${value}`;
431
+ } else if (isString(value)) {
432
+ return value;
433
+ }
434
+ return undefined;
435
+ };
436
+ /**
437
+ * Convert a value to number if it's type is string, otherwise return NaN
438
+ * @param value value to be converted to number
439
+ */
440
+ const toDateNumber = value => {
441
+ if (isString(value)) {
442
+ return Date.parse(value);
443
+ }
444
+ return NaN;
445
+ };
446
+
447
+ const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
448
+ const arrayIndexRegex = /\[(\d+)]/g;
449
+ const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
450
+ const parseKey = key => {
451
+ const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
452
+ return !keys ? [] : keys.flatMap(key => {
453
+ const unwrappedKey = parseBacktickWrappedKey(key);
454
+ const keys = [];
455
+ const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
456
+ if (parseResult) {
457
+ var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
458
+ 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);
459
+ keys.push(extractedKey);
460
+ const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
461
+ if (rawIndexes) {
462
+ for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
463
+ keys.push(parseInt(indexResult[1]));
464
+ }
465
+ }
466
+ } else {
467
+ keys.push(unwrappedKey);
468
+ }
469
+ return keys;
470
+ });
471
+ };
472
+ const complexKeyExpression = /{([^{}]+)}/;
473
+ function extractComplexKeys(ctx, key) {
474
+ // Resolve complex keys
475
+ let complexKeyMatches = complexKeyExpression.exec(key);
476
+ while (complexKeyMatches) {
477
+ const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
478
+ if (resolvedValue === undefined) {
479
+ return undefined;
480
+ }
481
+ key = key.replace(complexKeyExpression, `${resolvedValue}`);
482
+ complexKeyMatches = complexKeyExpression.exec(key);
483
+ }
484
+ return parseKey(key);
485
+ }
486
+ const isContext = value => isObject(value);
487
+ const simpleValueLookup = keys => ctx => {
488
+ let pointer = ctx;
489
+ for (const key of keys) {
490
+ if (typeof key === 'number') {
491
+ if (!Array.isArray(pointer)) {
492
+ return undefined;
493
+ }
494
+ pointer = pointer[key];
495
+ } else if (!isContext(pointer)) {
496
+ return undefined;
497
+ } else {
498
+ pointer = pointer[key];
499
+ }
500
+ }
501
+ return pointer;
502
+ };
503
+
504
+ /**
505
+ * Lookup for the reference in the context.
506
+ * The nested context value is annotated with "." delimiter.
507
+ * @example address.city
508
+ * @param {Context} ctx
509
+ * @param {string} key Context lookup key.
510
+ * @return {Result}
511
+ */
512
+ function complexValueLookup(ctx, key) {
513
+ const keys = extractComplexKeys(ctx, key);
514
+ if (!keys) {
515
+ return undefined;
516
+ }
517
+ return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
518
+ }
519
+ let DataType = /*#__PURE__*/function (DataType) {
520
+ DataType["Number"] = "Number";
521
+ DataType["String"] = "String";
522
+ return DataType;
523
+ }({});
524
+
525
+ // Equivalent to /^.+\.\((Number|String)\)$/
526
+ const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
527
+ const isComplexKey = key => key.indexOf('{') > -1;
528
+
529
+ /**
530
+ * Reference operand resolved within the context
531
+ */
532
+ class Reference extends Operand {
533
+ /**
534
+ * @constructor
535
+ * @param {string} key Context key.
536
+ */
537
+ constructor(key) {
538
+ if (key.trim() === '') {
539
+ throw new Error('invalid reference key');
540
+ }
541
+ super();
542
+ _defineProperty(this, "key", void 0);
543
+ _defineProperty(this, "dataType", void 0);
544
+ _defineProperty(this, "valueLookup", void 0);
545
+ _defineProperty(this, "getKeys", void 0);
546
+ this.key = key;
547
+ const dataTypeMatch = dataTypeRegex.exec(this.key);
548
+ if (dataTypeMatch) {
549
+ this.dataType = DataType[dataTypeMatch[1]];
550
+ }
551
+ if (this.key.match(/.\(.+\)$/)) {
552
+ this.key = this.key.replace(/.\(.+\)$/, '');
553
+ }
554
+ if (isComplexKey(this.key)) {
555
+ this.valueLookup = context => complexValueLookup(context, this.key);
556
+ this.getKeys = context => extractComplexKeys(context, this.key);
557
+ } else {
558
+ const keys = parseKey(this.key);
559
+ this.valueLookup = simpleValueLookup(keys);
560
+ this.getKeys = () => keys;
561
+ }
562
+ }
72
563
 
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
- }
564
+ /**
565
+ * Evaluate in the given context.
566
+ * @param {Context} ctx
567
+ * @return {boolean}
568
+ */
569
+ evaluate(ctx) {
570
+ return this.toDataType(this.valueLookup(ctx));
571
+ }
81
572
 
82
- /**
83
- * Valid types for context members
84
- */
573
+ /**
574
+ * {@link Evaluable.simplify}
575
+ */
576
+ simplify(ctx, strictKeys, optionalKeys) {
577
+ var _this$getKeys;
578
+ const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
579
+ if (ctx[key] !== undefined) {
580
+ return this.evaluate(ctx);
581
+ }
582
+ if (!key || typeof key === 'number') {
583
+ return this;
584
+ }
585
+ return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
586
+ }
85
587
 
86
- /**
87
- * Evaluation Context
88
- * Holds references used during the evaluation process.
89
- * Format: key: value.
90
- */
588
+ /**
589
+ * {@link Evaluable.serialize}
590
+ */
591
+ serialize({
592
+ referenceSerialization
593
+ }) {
594
+ const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
595
+ return referenceSerialization(key);
596
+ }
91
597
 
92
- /**
93
- * Evaluation result
94
- */
598
+ /**
599
+ * Get the strict representation of the operand.
600
+ * @return {string}
601
+ */
602
+ toString() {
603
+ return `{${this.key}}`;
604
+ }
95
605
 
96
- let EvaluableType = /*#__PURE__*/function (EvaluableType) {
97
- EvaluableType["Operand"] = "Operand";
98
- EvaluableType["Expression"] = "Expression";
99
- return EvaluableType;
100
- }({});
606
+ /**
607
+ * Converts a value to a specified data type
608
+ * Silently returns original value if data type conversion has not been implemented.
609
+ * @param value value to cast as data type
610
+ */
611
+ toDataType(value) {
612
+ let result = value;
613
+ switch (this.dataType) {
614
+ case DataType.Number:
615
+ result = toNumber(value);
616
+ break;
617
+ case DataType.String:
618
+ result = toString(value);
619
+ break;
620
+ }
621
+ if (value && result === undefined) {
622
+ console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
623
+ }
624
+ return result;
625
+ }
626
+ }
101
627
 
102
- /**
103
- * Evaluable
104
- */
628
+ const isSimplifiedArithmeticExpression = (operand, result) => operand instanceof Arithmetic && !isEvaluable(result) && !isInfinite(result) && !(operand instanceof Reference);
105
629
 
106
630
  /**
107
631
  * Abstract comparison expression
@@ -152,6 +676,12 @@ class Comparison {
152
676
  if (!isEvaluable(left) && !isEvaluable(right)) {
153
677
  return this.comparison(left, right);
154
678
  }
679
+ if (isEvaluable(left) && isSimplifiedArithmeticExpression(this.right, right)) {
680
+ return Reflect.construct(this.constructor, [left, new Value(right)]);
681
+ }
682
+ if (isEvaluable(right) && isSimplifiedArithmeticExpression(this.left, left)) {
683
+ return Reflect.construct(this.constructor, [new Value(left), right]);
684
+ }
155
685
  return this;
156
686
  }
157
687
 
@@ -198,47 +728,6 @@ class Equal extends Comparison {
198
728
  }
199
729
  }
200
730
 
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
731
  // Operator key
243
732
  const OPERATOR$g = Symbol('GE');
244
733
 
@@ -555,142 +1044,59 @@ class Overlap extends Comparison {
555
1044
  const rightArray = right;
556
1045
  if (leftArray.length === 0 && rightArray.length === 0) {
557
1046
  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;
1047
+ }
1048
+ return leftArray.some(element => rightArray.includes(element));
665
1049
  }
666
1050
 
667
1051
  /**
668
- * {@link Evaluable.evaluate}
1052
+ * Get the strict representation of the expression.
1053
+ * @return {string}
669
1054
  */
670
- evaluate() {
671
- return this.value;
1055
+ toString() {
1056
+ const left = this.left.toString();
1057
+ const right = this.right.toString();
1058
+ return `(${left} ${this.operator} ${right})`;
672
1059
  }
1060
+ }
673
1061
 
1062
+ // Operator key
1063
+ const OPERATOR$8 = Symbol('PREFIX');
1064
+
1065
+ /**
1066
+ * Prefix comparison expression
1067
+ */
1068
+ class Prefix extends Comparison {
674
1069
  /**
675
- * {@link Evaluable.simplify}
1070
+ * @constructor
1071
+ * @param {Evaluable} left Left operand.
1072
+ * @param {Evaluable} right Right operand.
676
1073
  */
677
- simplify() {
678
- return this.value;
1074
+
1075
+ constructor(left, right) {
1076
+ if (arguments.length !== 2) {
1077
+ throw new Error('comparison expression expects left and right operands');
1078
+ }
1079
+ super('prefix', OPERATOR$8, left, right);
679
1080
  }
680
1081
 
681
1082
  /**
682
- * {@link Evaluable.serialize}
1083
+ * {@link Comparison.comparison}
683
1084
  */
684
- serialize() {
685
- return this.value;
1085
+ comparison(left, right) {
1086
+ if (isString(left) === false || isString(right) === false) {
1087
+ return false;
1088
+ }
1089
+ return right.startsWith(left);
686
1090
  }
687
1091
 
688
1092
  /**
689
- * Get the strict representation of the operand.
1093
+ * Get the strict representation of the expression.
690
1094
  * @return {string}
691
1095
  */
692
1096
  toString() {
693
- return printValue(this.value);
1097
+ const left = this.left.toString();
1098
+ const right = this.right.toString();
1099
+ return `(<${left}>${right})`;
694
1100
  }
695
1101
  }
696
1102
 
@@ -1237,187 +1643,6 @@ class Collection extends Operand {
1237
1643
  }
1238
1644
  }
1239
1645
 
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
1646
  // Option value whitelist
1422
1647
 
1423
1648
  // Parser options
@@ -1453,7 +1678,9 @@ const defaultOperatorMapping = new Map([
1453
1678
  // Comparison
1454
1679
  [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
1680
  // Logical
1456
- [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT']]);
1681
+ [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT'],
1682
+ // Arithmetic
1683
+ [OPERATOR$i, '+'], [OPERATOR$j, '-'], [OPERATOR$k, '*'], [OPERATOR$l, '/']]);
1457
1684
 
1458
1685
  /**
1459
1686
  * Default parser options
@@ -1467,6 +1694,14 @@ const defaultOptions = {
1467
1694
 
1468
1695
  // Input types
1469
1696
 
1697
+ const invalidExpression = 'invalid expression';
1698
+ const logicalIfValidOperands = (operands, logical) => {
1699
+ if (operands.every(operand => operand instanceof Logical || operand instanceof Comparison)) {
1700
+ return logical;
1701
+ }
1702
+ throw new Error(invalidExpression);
1703
+ };
1704
+
1470
1705
  /**
1471
1706
  * Parser of raw expressions into Evaluable expression
1472
1707
  */
@@ -1477,7 +1712,8 @@ class Parser {
1477
1712
  */
1478
1713
  constructor(options) {
1479
1714
  _defineProperty(this, "opts", void 0);
1480
- _defineProperty(this, "expectedOperators", void 0);
1715
+ _defineProperty(this, "expectedRootOperators", void 0);
1716
+ _defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
1481
1717
  this.opts = {
1482
1718
  ...defaultOptions
1483
1719
  };
@@ -1489,7 +1725,7 @@ class Parser {
1489
1725
  }
1490
1726
  }
1491
1727
  }
1492
- this.expectedOperators = new Set(this.opts.operatorMapping.values());
1728
+ this.expectedRootOperators = new Set(Array.from(this.opts.operatorMapping.entries()).filter(([symbol]) => !this.unexpectedRootSymbols.has(symbol)).map(([, operator]) => operator));
1493
1729
  }
1494
1730
 
1495
1731
  /**
@@ -1507,10 +1743,10 @@ class Parser {
1507
1743
  */
1508
1744
  parse(raw) {
1509
1745
  if (raw === undefined || raw === null || Array.isArray(raw) === false) {
1510
- throw new Error('invalid expression');
1746
+ throw new Error(invalidExpression);
1511
1747
  }
1512
- if (raw.length === 0 || !this.expectedOperators.has(`${raw[0]}`)) {
1513
- throw new Error('invalid expression');
1748
+ if (raw.length === 0 || !this.expectedRootOperators.has(`${raw[0]}`)) {
1749
+ throw new Error(invalidExpression);
1514
1750
  }
1515
1751
  return this.parseRawExp(raw);
1516
1752
  }
@@ -1548,71 +1784,103 @@ class Parser {
1548
1784
  return collapsible && operands.length === 1 ? operands[0] : undefined;
1549
1785
  };
1550
1786
  switch (operator) {
1551
- /**
1552
- * Logical
1553
- */
1787
+ // Logical
1554
1788
  case this.opts.operatorMapping.get(OPERATOR$4):
1555
- expression = operands => logicalExpressionReducer(operands, true) || new And(operands);
1789
+ expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new And(operands));
1556
1790
  operandParser = this.parseRawExp;
1557
1791
  break;
1558
1792
  case this.opts.operatorMapping.get(OPERATOR$1):
1559
- expression = operands => logicalExpressionReducer(operands, true) || new Or(operands);
1793
+ expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new Or(operands));
1560
1794
  operandParser = this.parseRawExp;
1561
1795
  break;
1562
1796
  case this.opts.operatorMapping.get(OPERATOR$2):
1563
- expression = operands => logicalExpressionReducer(operands) || new Nor(operands);
1797
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Nor(operands));
1564
1798
  operandParser = this.parseRawExp;
1565
1799
  break;
1566
1800
  case this.opts.operatorMapping.get(OPERATOR):
1567
- expression = operands => logicalExpressionReducer(operands) || new Xor(operands);
1801
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Xor(operands));
1568
1802
  operandParser = this.parseRawExp;
1569
1803
  break;
1570
1804
  case this.opts.operatorMapping.get(OPERATOR$3):
1571
- expression = operands => logicalExpressionReducer(operands) || new Not(...operands);
1805
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Not(...operands));
1572
1806
  operandParser = this.parseRawExp;
1573
1807
  break;
1574
- /**
1575
- * Comparison
1576
- */
1808
+
1809
+ // Comparison
1577
1810
  case this.opts.operatorMapping.get(OPERATOR$h):
1578
1811
  expression = operands => new Equal(...operands);
1812
+ operandParser = this.parseRawExp;
1579
1813
  break;
1580
1814
  case this.opts.operatorMapping.get(OPERATOR$b):
1581
1815
  expression = operands => new NotEqual(...operands);
1816
+ operandParser = this.parseRawExp;
1582
1817
  break;
1583
1818
  case this.opts.operatorMapping.get(OPERATOR$f):
1584
1819
  expression = operands => new GreaterThan(...operands);
1820
+ operandParser = this.parseRawExp;
1585
1821
  break;
1586
1822
  case this.opts.operatorMapping.get(OPERATOR$g):
1587
1823
  expression = operands => new GreaterThanOrEqual(...operands);
1824
+ operandParser = this.parseRawExp;
1588
1825
  break;
1589
1826
  case this.opts.operatorMapping.get(OPERATOR$c):
1590
1827
  expression = operands => new LessThan(...operands);
1828
+ operandParser = this.parseRawExp;
1591
1829
  break;
1592
1830
  case this.opts.operatorMapping.get(OPERATOR$d):
1593
1831
  expression = operands => new LessThanOrEqual(...operands);
1832
+ operandParser = this.parseRawExp;
1594
1833
  break;
1834
+
1835
+ // Containment
1595
1836
  case this.opts.operatorMapping.get(OPERATOR$e):
1596
1837
  expression = operands => new In(...operands);
1597
1838
  break;
1598
1839
  case this.opts.operatorMapping.get(OPERATOR$a):
1599
1840
  expression = operands => new NotIn(...operands);
1600
1841
  break;
1842
+
1843
+ // Prefix
1601
1844
  case this.opts.operatorMapping.get(OPERATOR$8):
1602
1845
  expression = operands => new Prefix(...operands);
1603
1846
  break;
1847
+
1848
+ // Suffix
1604
1849
  case this.opts.operatorMapping.get(OPERATOR$6):
1605
1850
  expression = operands => new Suffix(...operands);
1606
1851
  break;
1852
+
1853
+ // Overlap
1607
1854
  case this.opts.operatorMapping.get(OPERATOR$9):
1608
1855
  expression = operands => new Overlap(...operands);
1609
1856
  break;
1857
+
1858
+ // Presence
1610
1859
  case this.opts.operatorMapping.get(OPERATOR$5):
1611
1860
  expression = operands => new Undefined(...operands);
1612
1861
  break;
1613
1862
  case this.opts.operatorMapping.get(OPERATOR$7):
1614
1863
  expression = operands => new Present(...operands);
1615
1864
  break;
1865
+
1866
+ // Arithmetic
1867
+ case this.opts.operatorMapping.get(OPERATOR$i):
1868
+ expression = operands => new Sum(...operands);
1869
+ operandParser = this.parseRawExp;
1870
+ break;
1871
+ case this.opts.operatorMapping.get(OPERATOR$j):
1872
+ expression = operands => new Subtract(...operands);
1873
+ operandParser = this.parseRawExp;
1874
+ break;
1875
+ case this.opts.operatorMapping.get(OPERATOR$k):
1876
+ expression = operands => new Multiply(...operands);
1877
+ operandParser = this.parseRawExp;
1878
+ break;
1879
+ case this.opts.operatorMapping.get(OPERATOR$l):
1880
+ expression = operands => new Divide(...operands);
1881
+ operandParser = this.parseRawExp;
1882
+ break;
1883
+
1616
1884
  // Collection
1617
1885
  default:
1618
1886
  return this.getOperand(raw);
@@ -1633,6 +1901,8 @@ class Parser {
1633
1901
  }
1634
1902
  }
1635
1903
 
1904
+ const unexpectedResultError = 'non expression or boolean result should be returned';
1905
+
1636
1906
  /**
1637
1907
  * Condition engine
1638
1908
  */
@@ -1653,7 +1923,11 @@ class Engine {
1653
1923
  * @return {boolean}
1654
1924
  */
1655
1925
  evaluate(exp, ctx) {
1656
- return this.parse(exp).evaluate(ctx);
1926
+ const result = this.parse(exp).evaluate(ctx);
1927
+ if (isBoolean(result)) {
1928
+ return result;
1929
+ }
1930
+ throw new Error(unexpectedResultError);
1657
1931
  }
1658
1932
 
1659
1933
  /**
@@ -1697,17 +1971,19 @@ class Engine {
1697
1971
  if (isBoolean(result)) {
1698
1972
  return result;
1699
1973
  }
1700
- throw new Error('non expression or boolean result should be returned');
1974
+ throw new Error(unexpectedResultError);
1701
1975
  }
1702
1976
  }
1703
1977
 
1704
1978
  exports.OPERATOR_AND = OPERATOR$4;
1979
+ exports.OPERATOR_DIVIDE = OPERATOR$l;
1705
1980
  exports.OPERATOR_EQ = OPERATOR$h;
1706
1981
  exports.OPERATOR_GE = OPERATOR$g;
1707
1982
  exports.OPERATOR_GT = OPERATOR$f;
1708
1983
  exports.OPERATOR_IN = OPERATOR$e;
1709
1984
  exports.OPERATOR_LE = OPERATOR$d;
1710
1985
  exports.OPERATOR_LT = OPERATOR$c;
1986
+ exports.OPERATOR_MULTIPLY = OPERATOR$k;
1711
1987
  exports.OPERATOR_NE = OPERATOR$b;
1712
1988
  exports.OPERATOR_NOR = OPERATOR$2;
1713
1989
  exports.OPERATOR_NOT = OPERATOR$3;
@@ -1716,7 +1992,9 @@ exports.OPERATOR_OR = OPERATOR$1;
1716
1992
  exports.OPERATOR_OVERLAP = OPERATOR$9;
1717
1993
  exports.OPERATOR_PREFIX = OPERATOR$8;
1718
1994
  exports.OPERATOR_PRESENT = OPERATOR$7;
1995
+ exports.OPERATOR_SUBTRACT = OPERATOR$j;
1719
1996
  exports.OPERATOR_SUFFIX = OPERATOR$6;
1997
+ exports.OPERATOR_SUM = OPERATOR$i;
1720
1998
  exports.OPERATOR_UNDEFINED = OPERATOR$5;
1721
1999
  exports.OPERATOR_XOR = OPERATOR;
1722
2000
  exports.default = Engine;