@briza/illogical 1.5.4 → 1.5.6

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,35 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ function _toPrimitive(t, r) {
6
+ if ("object" != typeof t || !t) return t;
7
+ var e = t[Symbol.toPrimitive];
8
+ if (void 0 !== e) {
9
+ var i = e.call(t, r || "default");
10
+ if ("object" != typeof i) return i;
11
+ throw new TypeError("@@toPrimitive must return a primitive value.");
12
+ }
13
+ return ("string" === r ? String : Number)(t);
14
+ }
15
+ function _toPropertyKey(t) {
16
+ 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;
32
+ }
33
+
5
34
  /**
6
35
  * Is number predicate.
7
36
  * @param value Tested value.
@@ -9,64 +38,47 @@ Object.defineProperty(exports, '__esModule', { value: true });
9
38
  function isNumber(value) {
10
39
  return typeof value === 'number' && isFinite(value);
11
40
  }
41
+
12
42
  /**
13
43
  * Is string type predicate.
14
44
  * @param value Tested value.
15
45
  */
16
-
17
46
  function isString(value) {
18
47
  return typeof value === 'string' || value instanceof String;
19
48
  }
49
+
20
50
  /**
21
51
  * Is Object
22
52
  * @param value tested value result of the test
23
53
  */
24
-
25
54
  function isObject(value) {
26
55
  if (value === null || value === undefined) {
27
56
  return false;
28
57
  }
29
-
30
58
  if (typeof value !== 'object' || (value === null || value === void 0 ? void 0 : value.constructor) !== Object) {
31
59
  return false;
32
60
  }
33
-
34
61
  return true;
35
62
  }
63
+
36
64
  /**
37
65
  * Is Boolean predicate.
38
66
  * @param value tested value.
39
67
  * @return result of the test
40
68
  */
41
-
42
69
  function isBoolean(value) {
43
70
  return typeof value === 'boolean';
44
71
  }
72
+
45
73
  /**
46
74
  * Check if a value is a an Evaluable
47
75
  * @param {Result | Evaluable} value value to check if is Evaluable
48
76
  * @returns {Evaluable}
49
77
  */
50
-
51
78
  function isEvaluable(value) {
52
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';
53
80
  }
54
81
 
55
- function _defineProperty(obj, key, value) {
56
- if (key in obj) {
57
- Object.defineProperty(obj, key, {
58
- value: value,
59
- enumerable: true,
60
- configurable: true,
61
- writable: true
62
- });
63
- } else {
64
- obj[key] = value;
65
- }
66
-
67
- return obj;
68
- }
69
-
70
82
  /**
71
83
  * Valid types for context members
72
84
  */
@@ -80,15 +92,16 @@ function _defineProperty(obj, key, value) {
80
92
  /**
81
93
  * Evaluation result
82
94
  */
83
- let EvaluableType;
84
- /**
85
- * Evaluable
86
- */
87
95
 
88
- (function (EvaluableType) {
96
+ let EvaluableType = /*#__PURE__*/function (EvaluableType) {
89
97
  EvaluableType["Operand"] = "Operand";
90
98
  EvaluableType["Expression"] = "Expression";
91
- })(EvaluableType || (EvaluableType = {}));
99
+ return EvaluableType;
100
+ }({});
101
+
102
+ /**
103
+ * Evaluable
104
+ */
92
105
 
93
106
  /**
94
107
  * Abstract comparison expression
@@ -101,30 +114,28 @@ class Comparison {
101
114
  * @param {Operand} right Right operand.
102
115
  */
103
116
  constructor(operator, operatorSymbol, left, right) {
104
- _defineProperty(this, "type", EvaluableType.Expression);
105
-
106
117
  this.operator = operator;
107
118
  this.operatorSymbol = operatorSymbol;
108
119
  this.left = left;
109
120
  this.right = right;
121
+ _defineProperty(this, "type", EvaluableType.Expression);
110
122
  }
123
+
111
124
  /**
112
125
  * {@link Evaluable.evaluate}
113
126
  */
114
-
115
-
116
127
  evaluate(ctx) {
117
128
  return this.comparison(this.left.evaluate(ctx), this.right.evaluate(ctx));
118
129
  }
130
+
119
131
  /**
120
132
  * Get the strict representation of the expression.
121
133
  * @return {string}
122
134
  */
123
-
124
-
125
135
  toString() {
126
136
  return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
127
137
  }
138
+
128
139
  /**
129
140
  * Compares left and right operands evaluated values.
130
141
  * @param {Result} left left operand result value
@@ -132,450 +143,469 @@ class Comparison {
132
143
  * @returns {boolean}
133
144
  */
134
145
 
135
-
136
146
  /**
137
147
  * {@link Evaluable.simplify}
138
148
  */
139
- simplify() {
140
- const left = this.left.simplify(...arguments);
141
- const right = this.right.simplify(...arguments);
142
-
149
+ simplify(...args) {
150
+ const left = this.left.simplify(...args);
151
+ const right = this.right.simplify(...args);
143
152
  if (!isEvaluable(left) && !isEvaluable(right)) {
144
153
  return this.comparison(left, right);
145
154
  }
146
-
147
155
  return this;
148
156
  }
157
+
149
158
  /**
150
159
  * {@link Evaluable.serialize}
151
160
  */
152
-
153
-
154
161
  serialize(options) {
155
162
  const {
156
163
  operatorMapping
157
164
  } = options;
158
165
  const operator = operatorMapping.get(this.operatorSymbol);
159
-
160
166
  if (operator === undefined) {
161
167
  throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
162
168
  }
163
-
164
169
  return [operator, this.left.serialize(options), this.right.serialize(options)];
165
170
  }
166
-
167
171
  }
168
172
 
173
+ // Operator key
169
174
  const OPERATOR$h = Symbol('EQ');
175
+
170
176
  /**
171
177
  * Equal comparison expression
172
178
  */
173
-
174
179
  class Equal extends Comparison {
175
180
  /**
176
181
  * @constructor
177
182
  * @param {Evaluable} left Left operand.
178
183
  * @param {Evaluable} right Right operand.
179
184
  */
185
+
180
186
  constructor(left, right) {
181
187
  if (arguments.length !== 2) {
182
188
  throw new Error('comparison expression expects left and right operands');
183
189
  }
184
-
185
190
  super('==', OPERATOR$h, left, right);
186
191
  }
192
+
187
193
  /**
188
194
  * {@link Comparison.comparison}
189
195
  */
190
-
191
-
192
196
  comparison(left, right) {
193
197
  return left === right;
194
198
  }
195
-
196
199
  }
197
200
 
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
+ // Operator key
198
243
  const OPERATOR$g = Symbol('GE');
244
+
199
245
  /**
200
246
  * Greater than or equal comparison expression
201
247
  */
202
-
203
248
  class GreaterThanOrEqual extends Comparison {
204
249
  /**
205
250
  * @constructor
206
251
  * @param {Evaluable} left Left operand.
207
252
  * @param {Evaluable} right Right operand.
208
253
  */
254
+
209
255
  constructor(left, right) {
210
256
  if (arguments.length !== 2) {
211
257
  throw new Error('comparison expression expects left and right operands');
212
258
  }
213
-
214
259
  super('>=', OPERATOR$g, left, right);
215
260
  }
261
+
216
262
  /**
217
263
  * {@link Comparison.comparison}
218
264
  */
219
-
220
-
221
265
  comparison(left, right) {
222
266
  if (isNumber(left) && isNumber(right)) {
223
267
  return left >= right;
224
268
  }
225
-
269
+ const leftDate = toDateNumber(left),
270
+ rightDate = toDateNumber(right);
271
+ if (leftDate && rightDate) {
272
+ return leftDate >= rightDate;
273
+ }
226
274
  return false;
227
275
  }
228
-
229
276
  }
230
277
 
278
+ // Operator key
231
279
  const OPERATOR$f = Symbol('GT');
280
+
232
281
  /**
233
282
  * Greater than comparison expression
234
283
  */
235
-
236
284
  class GreaterThan extends Comparison {
237
285
  /**
238
286
  * @constructor
239
287
  * @param {Evaluable} left Left operand.
240
288
  * @param {Evaluable} right Right operand.
241
289
  */
290
+
242
291
  constructor(left, right) {
243
292
  if (arguments.length !== 2) {
244
293
  throw new Error('comparison expression expects left and right operands');
245
294
  }
246
-
247
295
  super('>', OPERATOR$f, left, right);
248
296
  }
297
+
249
298
  /**
250
299
  * {@link Comparison.comparison}
251
300
  */
252
-
253
-
254
301
  comparison(left, right) {
255
302
  if (isNumber(left) && isNumber(right)) {
256
303
  return left > right;
257
304
  }
258
-
305
+ const leftDate = toDateNumber(left),
306
+ rightDate = toDateNumber(right);
307
+ if (leftDate && rightDate) {
308
+ return leftDate > rightDate;
309
+ }
259
310
  return false;
260
311
  }
261
-
262
312
  }
263
313
 
314
+ // Operator key
264
315
  const OPERATOR$e = Symbol('IN');
316
+
265
317
  /**
266
318
  * In comparison expression
267
319
  */
268
-
269
320
  class In extends Comparison {
270
321
  /**
271
322
  * @constructor
272
323
  * @param {Evaluable} left Left operand.
273
324
  * @param {Evaluable} right Right operand.
274
325
  */
326
+
275
327
  constructor(left, right) {
276
328
  if (arguments.length !== 2) {
277
329
  throw new Error('comparison expression expects left and right operands');
278
330
  }
279
-
280
331
  super('in', OPERATOR$e, left, right);
281
332
  }
333
+
282
334
  /**
283
335
  * {@link Comparison.comparison}
284
336
  */
285
-
286
-
287
337
  comparison(left, right) {
288
338
  if (left === undefined || left === null || right === undefined || right === null) {
289
339
  return false;
290
340
  }
291
-
292
341
  const leftArray = Array.isArray(left);
293
342
  const rightArray = Array.isArray(right);
294
-
295
343
  if (leftArray && rightArray) {
296
344
  throw new Error('invalid IN expression, both operands are array');
297
345
  }
298
-
299
346
  if (!leftArray && !rightArray) {
300
347
  throw new Error('invalid IN expression, non of the operands is array');
301
348
  }
302
-
303
349
  if (leftArray) {
304
350
  return left.indexOf(right) > -1;
305
351
  }
306
-
307
352
  return right.indexOf(left) > -1;
308
353
  }
354
+
309
355
  /**
310
356
  * Get the strict representation of the expression.
311
357
  * @return {string}
312
358
  */
313
-
314
-
315
359
  toString() {
316
360
  const left = this.left.toString();
317
361
  const right = this.right.toString();
318
-
319
362
  if (left.startsWith('[')) {
320
363
  return `(${right} ${this.operator} ${left})`;
321
364
  }
322
-
323
365
  return `(${left} ${this.operator} ${right})`;
324
366
  }
325
-
326
367
  }
327
368
 
369
+ // Operator key
328
370
  const OPERATOR$d = Symbol('LE');
371
+
329
372
  /**
330
373
  * Less than or equal comparison expression
331
374
  */
332
-
333
375
  class LessThanOrEqual extends Comparison {
334
376
  /**
335
377
  * @constructor
336
378
  * @param {Evaluable} left Left operand.
337
379
  * @param {Evaluable} right Right operand.
338
380
  */
381
+
339
382
  constructor(left, right) {
340
383
  if (arguments.length !== 2) {
341
384
  throw new Error('comparison expression expects left and right operands');
342
385
  }
343
-
344
386
  super('<=', OPERATOR$d, left, right);
345
387
  }
388
+
346
389
  /**
347
390
  * {@link Comparison.comparison}
348
391
  */
349
-
350
-
351
392
  comparison(left, right) {
352
393
  if (isNumber(left) && isNumber(right)) {
353
394
  return left <= right;
354
395
  }
355
-
396
+ const leftDate = toDateNumber(left),
397
+ rightDate = toDateNumber(right);
398
+ if (leftDate && rightDate) {
399
+ return leftDate <= rightDate;
400
+ }
356
401
  return false;
357
402
  }
358
-
359
403
  }
360
404
 
405
+ // Operator key
361
406
  const OPERATOR$c = Symbol('LT');
407
+
362
408
  /**
363
409
  * Less than comparison expression
364
410
  */
365
-
366
411
  class LessThan extends Comparison {
367
412
  /**
368
413
  * @constructor
369
414
  * @param {Evaluable} left Left operand.
370
415
  * @param {Evaluable} right Right operand.
371
416
  */
417
+
372
418
  constructor(left, right) {
373
419
  if (arguments.length !== 2) {
374
420
  throw new Error('comparison expression expects left and right operands');
375
421
  }
376
-
377
422
  super('<', OPERATOR$c, left, right);
378
423
  }
424
+
379
425
  /**
380
426
  * {@link Comparison.comparison}
381
427
  */
382
-
383
-
384
428
  comparison(left, right) {
385
429
  if (isNumber(left) && isNumber(right)) {
386
430
  return left < right;
387
431
  }
388
-
432
+ const leftDate = toDateNumber(left),
433
+ rightDate = toDateNumber(right);
434
+ if (leftDate && rightDate) {
435
+ return leftDate < rightDate;
436
+ }
389
437
  return false;
390
438
  }
391
-
392
439
  }
393
440
 
441
+ // Operator key
394
442
  const OPERATOR$b = Symbol('NE');
443
+
395
444
  /**
396
445
  * Not equal comparison expression
397
446
  */
398
-
399
447
  class NotEqual extends Comparison {
400
448
  /**
401
449
  * @constructor
402
450
  * @param {Evaluable} left Left operand.
403
451
  * @param {Evaluable} right Right operand.
404
452
  */
453
+
405
454
  constructor(left, right) {
406
455
  if (arguments.length !== 2) {
407
456
  throw new Error('comparison expression expects left and right operands');
408
457
  }
409
-
410
458
  super('!=', OPERATOR$b, left, right);
411
459
  }
460
+
412
461
  /**
413
462
  * {@link Comparison.comparison}
414
463
  */
415
-
416
-
417
464
  comparison(left, right) {
418
465
  return left !== right;
419
466
  }
420
-
421
467
  }
422
468
 
469
+ // Operator key
423
470
  const OPERATOR$a = Symbol('NOT IN');
471
+
424
472
  /**
425
473
  * Not in comparison expression
426
474
  */
427
-
428
475
  class NotIn extends Comparison {
429
476
  /**
430
477
  * @constructor
431
478
  * @param {Evaluable} left Left operand.
432
479
  * @param {Evaluable} right Right operand.
433
480
  */
481
+
434
482
  constructor(left, right) {
435
483
  if (arguments.length !== 2) {
436
484
  throw new Error('comparison expression expects left and right operands');
437
485
  }
438
-
439
486
  super('not in', OPERATOR$a, left, right);
440
487
  }
488
+
441
489
  /**
442
490
  * {@link Comparison.comparison}
443
491
  */
444
-
445
-
446
492
  comparison(left, right) {
447
493
  if (left === undefined || left === null || right === undefined || right === null) {
448
494
  return true;
449
495
  }
450
-
451
496
  const leftArray = Array.isArray(left);
452
497
  const rightArray = Array.isArray(right);
453
-
454
498
  if (leftArray && rightArray) {
455
499
  throw new Error('invalid NOT IN expression, both operands are array');
456
500
  }
457
-
458
501
  if (!leftArray && !rightArray) {
459
502
  throw new Error('invalid NOT IN expression, one operand must be array');
460
503
  }
461
-
462
504
  if (leftArray) {
463
505
  return left.indexOf(right) === -1;
464
506
  }
465
-
466
507
  return right.indexOf(left) === -1;
467
508
  }
509
+
468
510
  /**
469
511
  * Get the strict representation of the expression
470
512
  * @return {string}
471
513
  */
472
-
473
-
474
514
  toString() {
475
515
  const left = this.left.toString();
476
516
  const right = this.right.toString();
477
-
478
517
  if (left.startsWith('[')) {
479
518
  return `(${right} ${this.operator} ${left})`;
480
519
  }
481
-
482
520
  return `(${left} ${this.operator} ${right})`;
483
521
  }
484
-
485
522
  }
486
523
 
524
+ // Operator key
487
525
  const OPERATOR$9 = Symbol('OVERLAP');
526
+
488
527
  /**
489
528
  * Overlap comparison expression
490
529
  */
491
-
492
530
  class Overlap extends Comparison {
493
531
  /**
494
532
  * @constructor
495
533
  * @param {Evaluable} left Left operand.
496
534
  * @param {Evaluable} right Right operand.
497
535
  */
536
+
498
537
  constructor(left, right) {
499
538
  if (arguments.length !== 2) {
500
539
  throw new Error('comparison expression expects left and right operands');
501
540
  }
502
-
503
541
  super('overlap', OPERATOR$9, left, right);
504
542
  }
543
+
505
544
  /**
506
545
  * {@link Comparison.comparison}
507
546
  */
508
-
509
-
510
547
  comparison(left, right) {
511
548
  if (left === undefined || left === null || right === undefined || right === null) {
512
549
  return false;
513
550
  }
514
-
515
551
  if (!Array.isArray(left) || !Array.isArray(right)) {
516
552
  throw new Error('invalid OVERLAP expression, both operands must be array');
517
553
  }
518
-
519
554
  const leftArray = left;
520
555
  const rightArray = right;
521
556
  return leftArray.some(element => rightArray.includes(element));
522
557
  }
558
+
523
559
  /**
524
560
  * Get the strict representation of the expression.
525
561
  * @return {string}
526
562
  */
527
-
528
-
529
563
  toString() {
530
564
  const left = this.left.toString();
531
565
  const right = this.right.toString();
532
566
  return `(${left} ${this.operator} ${right})`;
533
567
  }
534
-
535
568
  }
536
569
 
570
+ // Operator key
537
571
  const OPERATOR$8 = Symbol('PREFIX');
572
+
538
573
  /**
539
574
  * Prefix comparison expression
540
575
  */
541
-
542
576
  class Prefix extends Comparison {
543
577
  /**
544
578
  * @constructor
545
579
  * @param {Evaluable} left Left operand.
546
580
  * @param {Evaluable} right Right operand.
547
581
  */
582
+
548
583
  constructor(left, right) {
549
584
  if (arguments.length !== 2) {
550
585
  throw new Error('comparison expression expects left and right operands');
551
586
  }
552
-
553
587
  super('prefix', OPERATOR$8, left, right);
554
588
  }
589
+
555
590
  /**
556
591
  * {@link Comparison.comparison}
557
592
  */
558
-
559
-
560
593
  comparison(left, right) {
561
594
  if (isString(left) === false || isString(right) === false) {
562
595
  return false;
563
596
  }
564
-
565
597
  return right.startsWith(left);
566
598
  }
599
+
567
600
  /**
568
601
  * Get the strict representation of the expression.
569
602
  * @return {string}
570
603
  */
571
-
572
-
573
604
  toString() {
574
605
  const left = this.left.toString();
575
606
  const right = this.right.toString();
576
607
  return `(<${left}>${right})`;
577
608
  }
578
-
579
609
  }
580
610
 
581
611
  /**
@@ -585,14 +615,21 @@ class Operand {
585
615
  constructor() {
586
616
  _defineProperty(this, "type", EvaluableType.Operand);
587
617
  }
588
-
618
+ /**
619
+ * {@link Evaluable.evaluate}
620
+ */
621
+ /**
622
+ * {@link Evaluable.simplify}
623
+ */
624
+ /**
625
+ * {@link Evaluable.serialize}
626
+ */
589
627
  /**
590
628
  * Get the strict representation.
591
629
  */
592
630
  toString() {
593
631
  throw new Error('not implemented exception');
594
632
  }
595
-
596
633
  }
597
634
 
598
635
  /**
@@ -600,19 +637,16 @@ class Operand {
600
637
  * @param {Result} value
601
638
  * @return {string}
602
639
  */
603
-
604
640
  function printValue(value) {
605
641
  if (isString(value)) {
606
642
  return `"${value}"`;
607
643
  }
608
-
609
644
  return `${value}`;
610
645
  }
646
+
611
647
  /**
612
648
  * Static value operand
613
649
  */
614
-
615
-
616
650
  class Value extends Operand {
617
651
  /**
618
652
  * @constructor
@@ -622,192 +656,174 @@ class Value extends Operand {
622
656
  if (Array.isArray(value)) {
623
657
  throw new Error('deprecated direct usage of array, please use Collection operand');
624
658
  }
625
-
626
659
  super();
660
+ _defineProperty(this, "value", void 0);
627
661
  this.value = value;
628
662
  }
663
+
629
664
  /**
630
665
  * {@link Evaluable.evaluate}
631
666
  */
632
-
633
-
634
667
  evaluate() {
635
668
  return this.value;
636
669
  }
670
+
637
671
  /**
638
672
  * {@link Evaluable.simplify}
639
673
  */
640
-
641
-
642
674
  simplify() {
643
675
  return this.value;
644
676
  }
677
+
645
678
  /**
646
679
  * {@link Evaluable.serialize}
647
680
  */
648
-
649
-
650
681
  serialize() {
651
682
  return this.value;
652
683
  }
684
+
653
685
  /**
654
686
  * Get the strict representation of the operand.
655
687
  * @return {string}
656
688
  */
657
-
658
-
659
689
  toString() {
660
690
  return printValue(this.value);
661
691
  }
662
-
663
692
  }
664
693
 
694
+ // Operator key
665
695
  const OPERATOR$7 = Symbol('PRESENT');
696
+
666
697
  /**
667
698
  * Present comparison expression
668
699
  */
669
-
670
700
  class Present extends Comparison {
671
701
  /**
672
702
  * @constructor
673
703
  * @param {Evaluable} operand
674
704
  */
705
+
675
706
  constructor(operand) {
676
707
  if (arguments.length !== 1) {
677
708
  throw new Error('comparison expression PRESENT expects exactly one operand');
678
709
  }
679
-
680
710
  super('PRESENT', OPERATOR$7, operand, new Value(true));
681
711
  }
712
+
682
713
  /**
683
714
  * {@link Comparison.comparison}
684
715
  */
685
-
686
-
687
716
  comparison(left) {
688
717
  return left !== undefined && left !== null;
689
718
  }
719
+
690
720
  /**
691
721
  * Get the strict representation of the expression.
692
722
  * @return {string}
693
723
  */
694
-
695
-
696
724
  toString() {
697
725
  return `(${this.left.toString()} is ${this.operator})`;
698
726
  }
699
-
700
727
  serialize(options) {
701
728
  const {
702
729
  operatorMapping
703
730
  } = options;
704
731
  const operator = operatorMapping.get(this.operatorSymbol);
705
-
706
732
  if (operator === undefined) {
707
733
  throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
708
734
  }
709
-
710
735
  return [operator, this.left.serialize(options)];
711
736
  }
712
-
713
737
  }
714
738
 
739
+ // Operator key
715
740
  const OPERATOR$6 = Symbol('PREFIX');
741
+
716
742
  /**
717
743
  * Suffix comparison expression
718
744
  */
719
-
720
745
  class Suffix extends Comparison {
721
746
  /**
722
747
  * @constructor
723
748
  * @param {Evaluable} left Left operand.
724
749
  * @param {Evaluable} right Right operand.
725
750
  */
751
+
726
752
  constructor(left, right) {
727
753
  if (arguments.length !== 2) {
728
754
  throw new Error('comparison expression expects left and right operands');
729
755
  }
730
-
731
756
  super('suffix', OPERATOR$6, left, right);
732
757
  }
758
+
733
759
  /**
734
760
  * {@link Comparison.comparison}
735
761
  */
736
-
737
-
738
762
  comparison(left, right) {
739
763
  if (isString(left) === false || isString(right) === false) {
740
764
  return false;
741
765
  }
742
-
743
766
  return left.endsWith(right);
744
767
  }
768
+
745
769
  /**
746
770
  * Get the strict representation of the expression.
747
771
  * @return {string}
748
772
  */
749
-
750
-
751
773
  toString() {
752
774
  const left = this.left.toString();
753
775
  const right = this.right.toString();
754
776
  return `(${left}<${right}>)`;
755
777
  }
756
-
757
778
  }
758
779
 
780
+ // Operator key
759
781
  const OPERATOR$5 = Symbol('UNDEFINED');
782
+
760
783
  /**
761
784
  * Undefined comparison expression
762
785
  */
763
-
764
786
  class Undefined extends Comparison {
765
787
  /**
766
788
  * @constructor
767
789
  * @param {Evaluable} operand
768
790
  */
791
+
769
792
  constructor(operand) {
770
793
  if (arguments.length !== 1) {
771
794
  throw new Error('comparison expression UNDEFINED expects exactly one operand');
772
795
  }
773
-
774
796
  super('UNDEFINED', OPERATOR$5, operand, new Value(true));
775
797
  }
798
+
776
799
  /**
777
800
  * {@link Comparison.comparison}
778
801
  */
779
-
780
-
781
802
  comparison(left) {
782
803
  return left === undefined;
783
804
  }
805
+
784
806
  /**
785
807
  * Get the strict representation of the expression.
786
808
  * @return {string}
787
809
  */
788
-
789
-
790
810
  toString() {
791
811
  return `(${this.left.toString()} is ${this.operator})`;
792
812
  }
813
+
793
814
  /**
794
815
  * {@link Evaluable.serialize}
795
816
  */
796
-
797
-
798
817
  serialize(options) {
799
818
  const {
800
819
  operatorMapping
801
820
  } = options;
802
821
  const operator = operatorMapping.get(this.operatorSymbol);
803
-
804
822
  if (operator === undefined) {
805
823
  throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
806
824
  }
807
-
808
825
  return [operator, this.left.serialize(options)];
809
826
  }
810
-
811
827
  }
812
828
 
813
829
  /**
@@ -820,16 +836,19 @@ class Logical {
820
836
  * @param {Evaluable[]} operands Collection of operands.
821
837
  */
822
838
  constructor(operator, operatorSymbol, operands) {
823
- _defineProperty(this, "type", EvaluableType.Expression);
824
-
825
839
  this.operator = operator;
826
840
  this.operatorSymbol = operatorSymbol;
827
841
  this.operands = operands;
842
+ _defineProperty(this, "type", EvaluableType.Expression);
828
843
  }
844
+
829
845
  /**
830
846
  * {@link Evaluable.evaluate}
831
847
  */
832
848
 
849
+ /**
850
+ * {@link Evaluable.simplify}
851
+ */
833
852
 
834
853
  /**
835
854
  * Get the strict representation of the expression.
@@ -838,27 +857,24 @@ class Logical {
838
857
  toString() {
839
858
  return '(' + this.operands.map(operand => operand.toString()).join(` ${this.operator} `) + ')';
840
859
  }
841
-
842
860
  serialize(options) {
843
861
  const {
844
862
  operatorMapping
845
863
  } = options;
846
864
  const operator = operatorMapping.get(this.operatorSymbol);
847
-
848
865
  if (operator === undefined) {
849
866
  throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
850
867
  }
851
-
852
868
  return [operator, ...this.operands.map(operand => operand.serialize(options))];
853
869
  }
854
-
855
870
  }
856
871
 
872
+ // Operator key
857
873
  const OPERATOR$4 = Symbol('AND');
874
+
858
875
  /**
859
876
  * And logical expression
860
877
  */
861
-
862
878
  class And extends Logical {
863
879
  /**
864
880
  * @constructor
@@ -868,127 +884,105 @@ class And extends Logical {
868
884
  if (operands.length < 2) {
869
885
  throw new Error('logical expression must have at least two operands');
870
886
  }
871
-
872
887
  super('AND', OPERATOR$4, operands);
873
888
  }
889
+
874
890
  /**
875
891
  * Evaluate in the given context.
876
892
  * @param {Context} ctx
877
893
  * @return {Result}
878
894
  */
879
-
880
-
881
895
  evaluate(ctx) {
882
896
  for (const operand of this.operands) {
883
897
  if (operand.evaluate(ctx) === false) {
884
898
  return false;
885
899
  }
886
900
  }
887
-
888
901
  return true;
889
902
  }
903
+
890
904
  /**
891
905
  * {@link Evaluable.simplify}
892
906
  */
893
-
894
-
895
- simplify() {
896
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
897
- args[_key] = arguments[_key];
898
- }
899
-
907
+ simplify(...args) {
900
908
  const simplified = this.operands.reduce((result, child) => {
901
909
  if (result !== false) {
902
910
  const childResult = child.simplify(...args);
903
-
904
911
  if (isEvaluable(childResult)) {
905
912
  if (isBoolean(result)) {
906
913
  return [childResult];
907
914
  }
908
-
909
915
  return [...result, childResult];
910
916
  }
911
-
912
917
  if (!childResult) {
913
918
  return false;
914
919
  }
915
920
  }
916
-
917
921
  return result;
918
922
  }, true);
919
-
920
923
  if (Array.isArray(simplified)) {
921
924
  if (simplified.length === 1) {
922
925
  return simplified[0];
923
926
  }
924
-
925
927
  return new And(simplified);
926
928
  }
927
-
928
929
  return simplified;
929
930
  }
930
-
931
931
  }
932
932
 
933
+ // Operator key
933
934
  const OPERATOR$3 = Symbol('NOT');
935
+
934
936
  /**
935
937
  * Not logical expression
936
938
  */
937
-
938
939
  class Not extends Logical {
939
940
  /**
940
941
  * @constructor
941
942
  * @param {Evaluable} operand
942
943
  */
944
+
943
945
  constructor(operand) {
944
946
  if (arguments.length !== 1) {
945
947
  throw new Error('logical NOT expression must have exactly one operand');
946
948
  }
947
-
948
949
  super('NOT', OPERATOR$3, [operand]);
949
950
  }
951
+
950
952
  /**
951
953
  * Evaluate in the given context.
952
954
  * @param {Context} ctx
953
955
  * @return {Result}
954
956
  */
955
-
956
-
957
957
  evaluate(ctx) {
958
958
  const result = this.operands[0].evaluate(ctx);
959
-
960
959
  if (result !== true && result !== false) {
961
960
  throw new Error("logical NOT expression's operand must be evaluated to boolean value");
962
961
  }
963
-
964
962
  return !result;
965
963
  }
964
+
966
965
  /**
967
966
  * {@link Evaluable.simplify}
968
967
  */
969
-
970
-
971
- simplify() {
972
- const simplified = this.operands[0].simplify(...arguments);
973
-
968
+ simplify(...args) {
969
+ const simplified = this.operands[0].simplify(...args);
974
970
  if (isBoolean(simplified)) {
975
971
  return !simplified;
976
972
  }
977
-
978
973
  if (isEvaluable(simplified)) {
979
974
  return new Not(simplified);
980
975
  }
981
-
982
976
  throw new Error("logical NOT expression's operand must be evaluated to boolean value");
983
977
  }
984
-
985
978
  }
986
979
 
980
+ // Operator key
987
981
  const OPERATOR$2 = Symbol('NOR');
982
+
988
983
  /**
989
984
  * Nor logical expression
990
985
  */
991
-
992
986
  class Nor extends Logical {
993
987
  /**
994
988
  * @constructor
@@ -998,73 +992,58 @@ class Nor extends Logical {
998
992
  if (operands.length < 2) {
999
993
  throw new Error('logical expression must have at least two operands');
1000
994
  }
1001
-
1002
995
  super('NOR', OPERATOR$2, operands);
1003
996
  }
997
+
1004
998
  /**
1005
999
  * Evaluate in the given context.
1006
1000
  * @param {Context} ctx
1007
1001
  * @return {Result}
1008
1002
  */
1009
-
1010
-
1011
1003
  evaluate(ctx) {
1012
1004
  for (const operand of this.operands) {
1013
1005
  if (operand.evaluate(ctx) === true) {
1014
1006
  return false;
1015
1007
  }
1016
1008
  }
1017
-
1018
1009
  return true;
1019
1010
  }
1011
+
1020
1012
  /**
1021
1013
  * {@link Evaluable.simplify}
1022
1014
  */
1023
-
1024
-
1025
- simplify() {
1026
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1027
- args[_key] = arguments[_key];
1028
- }
1029
-
1015
+ simplify(...args) {
1030
1016
  const simplified = this.operands.reduce((result, child) => {
1031
1017
  if (result !== false) {
1032
1018
  const childResult = child.simplify(...args);
1033
-
1034
1019
  if (isEvaluable(childResult)) {
1035
1020
  if (isBoolean(result)) {
1036
1021
  return [childResult];
1037
1022
  }
1038
-
1039
1023
  return [...result, childResult];
1040
1024
  }
1041
-
1042
1025
  if (childResult) {
1043
1026
  return false;
1044
1027
  }
1045
1028
  }
1046
-
1047
1029
  return result;
1048
1030
  }, true);
1049
-
1050
1031
  if (Array.isArray(simplified)) {
1051
1032
  if (simplified.length === 1) {
1052
1033
  return new Not(...simplified);
1053
1034
  }
1054
-
1055
1035
  return new Nor(simplified);
1056
1036
  }
1057
-
1058
1037
  return simplified;
1059
1038
  }
1060
-
1061
1039
  }
1062
1040
 
1041
+ // Operator key
1063
1042
  const OPERATOR$1 = Symbol('OR');
1043
+
1064
1044
  /**
1065
1045
  * Or logical expression
1066
1046
  */
1067
-
1068
1047
  class Or extends Logical {
1069
1048
  /**
1070
1049
  * @constructor
@@ -1074,84 +1053,68 @@ class Or extends Logical {
1074
1053
  if (operands.length < 2) {
1075
1054
  throw new Error('logical expression must have at least two operands');
1076
1055
  }
1077
-
1078
1056
  super('OR', OPERATOR$1, operands);
1079
1057
  }
1058
+
1080
1059
  /**
1081
1060
  * Evaluate in the given context.
1082
1061
  * @param {Context} ctx
1083
1062
  * @return {Result}
1084
1063
  */
1085
-
1086
-
1087
1064
  evaluate(ctx) {
1088
1065
  for (const operand of this.operands) {
1089
1066
  if (operand.evaluate(ctx) === true) {
1090
1067
  return true;
1091
1068
  }
1092
1069
  }
1093
-
1094
1070
  return false;
1095
1071
  }
1072
+
1096
1073
  /**
1097
1074
  * {@link Evaluable.simplify}
1098
1075
  */
1099
-
1100
-
1101
- simplify() {
1102
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1103
- args[_key] = arguments[_key];
1104
- }
1105
-
1076
+ simplify(...args) {
1106
1077
  const simplified = this.operands.reduce((result, child) => {
1107
1078
  if (result !== true) {
1108
1079
  const childResult = child.simplify(...args);
1109
-
1110
1080
  if (isEvaluable(childResult)) {
1111
1081
  if (isBoolean(result)) {
1112
1082
  return [childResult];
1113
1083
  }
1114
-
1115
1084
  return [...result, childResult];
1116
1085
  }
1117
-
1118
1086
  if (childResult) {
1119
1087
  return true;
1120
1088
  }
1121
1089
  }
1122
-
1123
1090
  return result;
1124
1091
  }, false);
1125
-
1126
1092
  if (Array.isArray(simplified)) {
1127
1093
  if (simplified.length === 1) {
1128
1094
  return simplified[0];
1129
1095
  }
1130
-
1131
1096
  return new Or(simplified);
1132
1097
  }
1133
-
1134
1098
  return simplified;
1135
1099
  }
1136
-
1137
1100
  }
1138
1101
 
1102
+ // Operator key
1139
1103
  const OPERATOR = Symbol('XOR');
1104
+
1140
1105
  /**
1141
1106
  * Logical xor
1142
1107
  * @param {boolean} a
1143
1108
  * @param {boolean} b
1144
1109
  * @return {boolean}
1145
1110
  */
1146
-
1147
1111
  function xor(a, b) {
1148
1112
  return (a || b) && !(a && b);
1149
1113
  }
1114
+
1150
1115
  /**
1151
1116
  * Xor logical expression
1152
1117
  */
1153
-
1154
-
1155
1118
  class Xor extends Logical {
1156
1119
  /**
1157
1120
  * @constructor
@@ -1161,19 +1124,16 @@ class Xor extends Logical {
1161
1124
  if (operands.length < 2) {
1162
1125
  throw new Error('logical expression must have at least two operands');
1163
1126
  }
1164
-
1165
1127
  super('XOR', OPERATOR, operands);
1166
1128
  }
1129
+
1167
1130
  /**
1168
1131
  * Evaluate in the given context.
1169
1132
  * @param {Context} ctx
1170
1133
  * @return {Result}
1171
1134
  */
1172
-
1173
-
1174
1135
  evaluate(ctx) {
1175
1136
  let res = null;
1176
-
1177
1137
  for (const operand of this.operands) {
1178
1138
  if (res === null) {
1179
1139
  res = operand.evaluate(ctx);
@@ -1181,62 +1141,43 @@ class Xor extends Logical {
1181
1141
  res = xor(res, operand.evaluate(ctx));
1182
1142
  }
1183
1143
  }
1184
-
1185
1144
  return res;
1186
1145
  }
1146
+
1187
1147
  /**
1188
1148
  * {@link Evaluable.simplify}
1189
1149
  */
1190
-
1191
-
1192
- simplify() {
1193
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1194
- args[_key] = arguments[_key];
1195
- }
1196
-
1197
- const [evaluablesLeft, trueCount] = this.operands.reduce((_ref, child) => {
1198
- let [notSimplifiedConditions, trueCount] = _ref;
1199
-
1150
+ simplify(...args) {
1151
+ const [evaluablesLeft, trueCount] = this.operands.reduce(([notSimplifiedConditions, trueCount], child) => {
1200
1152
  if (trueCount > 1) {
1201
1153
  return [notSimplifiedConditions, trueCount];
1202
1154
  }
1203
-
1204
1155
  const childResult = child.simplify(...args);
1205
-
1206
1156
  if (isEvaluable(childResult)) {
1207
1157
  return [[...notSimplifiedConditions, childResult], trueCount];
1208
1158
  }
1209
-
1210
1159
  if (childResult) {
1211
1160
  return [notSimplifiedConditions, trueCount + 1];
1212
1161
  }
1213
-
1214
1162
  return [notSimplifiedConditions, trueCount];
1215
1163
  }, [[], 0]);
1216
-
1217
1164
  if (trueCount > 1) {
1218
1165
  return false;
1219
1166
  }
1220
-
1221
1167
  if (evaluablesLeft.length === 0) {
1222
1168
  return trueCount === 1;
1223
1169
  }
1224
-
1225
1170
  if (evaluablesLeft.length === 1) {
1226
1171
  if (trueCount === 1) {
1227
1172
  return new Not(...evaluablesLeft);
1228
1173
  }
1229
-
1230
1174
  return evaluablesLeft[0];
1231
1175
  }
1232
-
1233
1176
  if (trueCount === 1) {
1234
1177
  return new Nor(evaluablesLeft);
1235
1178
  }
1236
-
1237
1179
  return new Xor(evaluablesLeft);
1238
1180
  }
1239
-
1240
1181
  }
1241
1182
 
1242
1183
  /**
@@ -1249,117 +1190,101 @@ class Collection extends Operand {
1249
1190
  */
1250
1191
  constructor(items) {
1251
1192
  super();
1193
+ _defineProperty(this, "items", void 0);
1252
1194
  this.items = items;
1253
1195
  }
1196
+
1254
1197
  /**
1255
1198
  * Evaluate in the given context.
1256
1199
  * @param {Context} ctx
1257
1200
  * @return {boolean}
1258
1201
  */
1259
-
1260
-
1261
1202
  evaluate(ctx) {
1262
1203
  return this.items.map(item => item.evaluate(ctx));
1263
1204
  }
1205
+
1264
1206
  /**
1265
1207
  * {@link Evaluable.simplify}
1266
1208
  */
1267
-
1268
-
1269
- simplify() {
1209
+ simplify(...args) {
1270
1210
  const values = [];
1271
-
1272
1211
  for (const item of this.items) {
1273
- const simplifiedItem = item.simplify(...arguments);
1274
-
1212
+ const simplifiedItem = item.simplify(...args);
1275
1213
  if (isEvaluable(simplifiedItem)) {
1276
1214
  return this;
1277
1215
  }
1278
-
1279
1216
  values.push(simplifiedItem);
1280
1217
  }
1281
-
1282
1218
  return values;
1283
1219
  }
1220
+
1284
1221
  /**
1285
1222
  * {@link Evaluable.serialize}
1286
1223
  */
1287
-
1288
-
1289
1224
  serialize(options) {
1290
1225
  return this.items.map(item => isEvaluable(item) ? item.serialize(options) : item);
1291
1226
  }
1227
+
1292
1228
  /**
1293
1229
  * Get the strict representation of the operand.
1294
1230
  * @return {string}
1295
1231
  */
1296
-
1297
-
1298
1232
  toString() {
1299
1233
  return '[' + this.items.map(item => item.toString()).join(', ') + ']';
1300
1234
  }
1301
-
1302
1235
  }
1303
1236
 
1304
- /**
1305
- * Convert a value to number if possible, otherwise return undefined
1306
- * @param value value to be converted to number
1307
- */
1308
-
1309
- const toNumber = value => {
1310
- const isValueNumber = isNumber(value);
1311
-
1312
- if (isValueNumber) {
1313
- return value;
1314
- } else if (isString(value)) {
1315
- if (value.match(/^\d+\.\d+$/)) {
1316
- return parseFloat(value);
1317
- } else if (value.match(/^0$|^[1-9]\d*$/)) {
1318
- return parseInt(value);
1237
+ const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
1238
+ const arrayIndexRegex = /\[(\d+)]/g;
1239
+ const parseKey = key => key.split('.').flatMap(key => {
1240
+ const parseResult = keyWithArrayIndexRegex.exec(key);
1241
+ const keys = [];
1242
+ if (parseResult) {
1243
+ var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
1244
+ keys.push((_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 : key);
1245
+ const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
1246
+ if (rawIndexes) {
1247
+ for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
1248
+ keys.push(parseInt(indexResult[1]));
1249
+ }
1319
1250
  }
1251
+ } else {
1252
+ keys.push(key);
1320
1253
  }
1321
-
1322
- return undefined;
1323
- };
1324
- /**
1325
- * Convert a value to string if possible, otherwise return undefined
1326
- * @param value value to be converted to string
1327
- */
1328
-
1329
- const toString = value => {
1330
- if (isNumber(value)) {
1331
- return `${value}`;
1332
- } else if (isString(value)) {
1333
- return value;
1334
- }
1335
-
1336
- return undefined;
1337
- };
1338
-
1339
- function extractKeys(ctx, key) {
1254
+ return keys;
1255
+ });
1256
+ const complexKeyExpression = /{([^{}]+)}/;
1257
+ function extractComplexKeys(ctx, key) {
1340
1258
  // Resolve complex keys
1341
- const complexKeyExpression = /{([^{}]+)}/;
1342
1259
  let complexKeyMatches = complexKeyExpression.exec(key);
1343
-
1344
1260
  while (complexKeyMatches) {
1345
- const resolvedValue = contextValueLookup(ctx, complexKeyMatches[1]);
1346
-
1261
+ const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
1347
1262
  if (resolvedValue === undefined) {
1348
1263
  return undefined;
1349
1264
  }
1350
-
1351
1265
  key = key.replace(complexKeyExpression, `${resolvedValue}`);
1352
1266
  complexKeyMatches = complexKeyExpression.exec(key);
1353
1267
  }
1354
-
1355
- let keys = [key]; // Nested reference
1356
-
1357
- if (key.includes('.')) {
1358
- keys = key.split('.');
1268
+ return parseKey(key);
1269
+ }
1270
+ const isContext = value => isObject(value);
1271
+ const simpleValueLookup = keys => ctx => {
1272
+ let pointer = ctx;
1273
+ for (const key of keys) {
1274
+ if (typeof key === 'number') {
1275
+ if (!Array.isArray(pointer)) {
1276
+ return undefined;
1277
+ }
1278
+ pointer = pointer[key];
1279
+ } else if (!isContext(pointer)) {
1280
+ return undefined;
1281
+ } else {
1282
+ pointer = pointer[key];
1283
+ }
1359
1284
  }
1285
+ return pointer;
1286
+ };
1360
1287
 
1361
- return keys;
1362
- }
1363
1288
  /**
1364
1289
  * Lookup for the reference in the context.
1365
1290
  * The nested context value is annotated with "." delimiter.
@@ -1368,58 +1293,26 @@ function extractKeys(ctx, key) {
1368
1293
  * @param {string} key Context lookup key.
1369
1294
  * @return {Result}
1370
1295
  */
1371
-
1372
-
1373
- function contextValueLookup(ctx, key) {
1374
- const keys = extractKeys(ctx, key);
1375
-
1296
+ function complexValueLookup(ctx, key) {
1297
+ const keys = extractComplexKeys(ctx, key);
1376
1298
  if (!keys) {
1377
1299
  return undefined;
1378
- } // Context pointer
1379
-
1380
-
1381
- let pointer = ctx;
1382
-
1383
- for (let i = 0; i < keys.length; i++) {
1384
- var _keys$i$match;
1385
-
1386
- const currentKey = keys[i].replace(/\[.+$/, '');
1387
- let currentValue = pointer[currentKey]; // Resolve array notation
1388
-
1389
- (_keys$i$match = keys[i].match(/\[\d+\]/g)) === null || _keys$i$match === void 0 ? void 0 : _keys$i$match.forEach(match => {
1390
- const arrayIndex = parseInt(match.replace(/[[\]]/g, ''));
1391
-
1392
- if (!Array.isArray(currentValue) || currentValue[arrayIndex] === undefined) {
1393
- currentValue = undefined;
1394
- } else {
1395
- currentValue = currentValue[arrayIndex];
1396
- }
1397
- }); // Last node
1398
-
1399
- if (i === keys.length - 1) {
1400
- return currentValue; // Nested path
1401
- } else if (currentValue !== undefined && isObject(currentValue)) {
1402
- pointer = currentValue; // Invalid nested reference path
1403
- } else {
1404
- break;
1405
- }
1406
1300
  }
1407
-
1408
- return undefined;
1301
+ return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
1409
1302
  }
1410
-
1411
- let DataType; // Equivalent to /^.+\.\((Number|String)\)$/
1412
-
1413
- (function (DataType) {
1303
+ let DataType = /*#__PURE__*/function (DataType) {
1414
1304
  DataType["Number"] = "Number";
1415
1305
  DataType["String"] = "String";
1416
- })(DataType || (DataType = {}));
1306
+ return DataType;
1307
+ }({});
1417
1308
 
1309
+ // Equivalent to /^.+\.\((Number|String)\)$/
1418
1310
  const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
1311
+ const isComplexKey = key => key.indexOf('{') > -1;
1312
+
1419
1313
  /**
1420
1314
  * Reference operand resolved within the context
1421
1315
  */
1422
-
1423
1316
  class Reference extends Operand {
1424
1317
  /**
1425
1318
  * @constructor
@@ -1429,99 +1322,97 @@ class Reference extends Operand {
1429
1322
  if (key.trim() === '') {
1430
1323
  throw new Error('invalid reference key');
1431
1324
  }
1432
-
1433
1325
  super();
1326
+ _defineProperty(this, "key", void 0);
1327
+ _defineProperty(this, "dataType", void 0);
1328
+ _defineProperty(this, "valueLookup", void 0);
1329
+ _defineProperty(this, "getKeys", void 0);
1434
1330
  this.key = key;
1435
1331
  const dataTypeMatch = dataTypeRegex.exec(this.key);
1436
-
1437
1332
  if (dataTypeMatch) {
1438
1333
  this.dataType = DataType[dataTypeMatch[1]];
1439
1334
  }
1440
-
1441
1335
  if (this.key.match(/.\(.+\)$/)) {
1442
1336
  this.key = this.key.replace(/.\(.+\)$/, '');
1443
1337
  }
1338
+ if (isComplexKey(this.key)) {
1339
+ this.valueLookup = context => complexValueLookup(context, this.key);
1340
+ this.getKeys = context => extractComplexKeys(context, this.key);
1341
+ } else {
1342
+ const keys = parseKey(this.key);
1343
+ this.valueLookup = simpleValueLookup(keys);
1344
+ this.getKeys = () => keys;
1345
+ }
1444
1346
  }
1347
+
1445
1348
  /**
1446
1349
  * Evaluate in the given context.
1447
1350
  * @param {Context} ctx
1448
1351
  * @return {boolean}
1449
1352
  */
1450
-
1451
-
1452
1353
  evaluate(ctx) {
1453
- return this.toDataType(contextValueLookup(ctx, this.key));
1354
+ return this.toDataType(this.valueLookup(ctx));
1454
1355
  }
1356
+
1455
1357
  /**
1456
1358
  * {@link Evaluable.simplify}
1457
1359
  */
1458
-
1459
-
1460
1360
  simplify(ctx, strictKeys, optionalKeys) {
1461
- const keys = extractKeys(ctx, this.key);
1462
-
1463
- if (!keys) {
1464
- return this;
1465
- }
1466
-
1467
- const key = keys[0].replace(/\[.+$/, '');
1468
-
1361
+ var _this$getKeys;
1362
+ const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
1469
1363
  if (ctx[key] !== undefined) {
1470
1364
  return this.evaluate(ctx);
1471
1365
  }
1472
-
1366
+ if (!key || typeof key === 'number') {
1367
+ return this;
1368
+ }
1473
1369
  return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
1474
1370
  }
1371
+
1475
1372
  /**
1476
1373
  * {@link Evaluable.serialize}
1477
1374
  */
1478
-
1479
-
1480
- serialize(_ref) {
1481
- let {
1482
- referenceSerialization
1483
- } = _ref;
1375
+ serialize({
1376
+ referenceSerialization
1377
+ }) {
1484
1378
  const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
1485
1379
  return referenceSerialization(key);
1486
1380
  }
1381
+
1487
1382
  /**
1488
1383
  * Get the strict representation of the operand.
1489
1384
  * @return {string}
1490
1385
  */
1491
-
1492
-
1493
1386
  toString() {
1494
1387
  return `{${this.key}}`;
1495
1388
  }
1389
+
1496
1390
  /**
1497
1391
  * Converts a value to a specified data type
1498
1392
  * Silently returns original value if data type conversion has not been implemented.
1499
1393
  * @param value value to cast as data type
1500
1394
  */
1501
-
1502
-
1503
1395
  toDataType(value) {
1504
1396
  let result = value;
1505
-
1506
1397
  switch (this.dataType) {
1507
1398
  case DataType.Number:
1508
1399
  result = toNumber(value);
1509
1400
  break;
1510
-
1511
1401
  case DataType.String:
1512
1402
  result = toString(value);
1513
1403
  break;
1514
1404
  }
1515
-
1516
1405
  if (value && result === undefined) {
1517
1406
  console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
1518
1407
  }
1519
-
1520
1408
  return result;
1521
1409
  }
1522
-
1523
1410
  }
1524
1411
 
1412
+ // Option value whitelist
1413
+
1414
+ // Parser options
1415
+
1525
1416
  /**
1526
1417
  * Default reference predicate.
1527
1418
  * The "$" symbol at the begging of the operand is used
@@ -1533,28 +1424,31 @@ class Reference extends Operand {
1533
1424
  function defaultReferencePredicate(key) {
1534
1425
  return isString(key) && key.startsWith('$');
1535
1426
  }
1427
+
1536
1428
  /**
1537
1429
  * Default reference transform.
1538
1430
  * It removes the "$" symbol at the begging of the operand name.
1539
1431
  * @param {string} key
1540
1432
  * @return {string}
1541
1433
  */
1542
-
1543
1434
  function defaultReferenceTransform(key) {
1544
1435
  return key.slice(1);
1545
1436
  }
1546
1437
  function defaultReferenceSerialization(key) {
1547
1438
  return `$${key}`;
1548
- } // Default operator mapping
1549
- // Unique operator key <-> raw expression key
1439
+ }
1550
1440
 
1551
- const defaultOperatorMapping = new Map([// Comparison
1552
- [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'], // Logical
1441
+ // Default operator mapping
1442
+ // Unique operator key <-> raw expression key
1443
+ const defaultOperatorMapping = new Map([
1444
+ // Comparison
1445
+ [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'],
1446
+ // Logical
1553
1447
  [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT']]);
1448
+
1554
1449
  /**
1555
1450
  * Default parser options
1556
1451
  */
1557
-
1558
1452
  const defaultOptions = {
1559
1453
  referencePredicate: defaultReferencePredicate,
1560
1454
  referenceTransform: defaultReferenceTransform,
@@ -1562,6 +1456,8 @@ const defaultOptions = {
1562
1456
  operatorMapping: defaultOperatorMapping
1563
1457
  };
1564
1458
 
1459
+ // Input types
1460
+
1565
1461
  /**
1566
1462
  * Parser of raw expressions into Evaluable expression
1567
1463
  */
@@ -1571,9 +1467,12 @@ class Parser {
1571
1467
  * @param {Options?} options Parser options.
1572
1468
  */
1573
1469
  constructor(options) {
1574
- this.opts = { ...defaultOptions
1575
- }; // Apply exclusive options overrides
1576
-
1470
+ _defineProperty(this, "opts", void 0);
1471
+ _defineProperty(this, "expectedOperators", void 0);
1472
+ this.opts = {
1473
+ ...defaultOptions
1474
+ };
1475
+ // Apply exclusive options overrides
1577
1476
  if (options) {
1578
1477
  for (const key of Object.keys(options)) {
1579
1478
  if (key in this.opts) {
@@ -1581,55 +1480,47 @@ class Parser {
1581
1480
  }
1582
1481
  }
1583
1482
  }
1584
-
1585
1483
  this.expectedOperators = new Set(this.opts.operatorMapping.values());
1586
1484
  }
1485
+
1587
1486
  /**
1588
1487
  * Parser options
1589
1488
  * @type {Options}
1590
1489
  */
1591
-
1592
-
1593
1490
  get options() {
1594
1491
  return this.opts;
1595
1492
  }
1493
+
1596
1494
  /**
1597
1495
  * Parse raw expression into evaluable expression.
1598
1496
  * @param {ExpressionInput} raw Raw expression.
1599
1497
  * @return {Evaluable}
1600
1498
  */
1601
-
1602
-
1603
1499
  parse(raw) {
1604
1500
  if (raw === undefined || raw === null || Array.isArray(raw) === false) {
1605
1501
  throw new Error('invalid expression');
1606
1502
  }
1607
-
1608
1503
  if (raw.length === 0 || !this.expectedOperators.has(`${raw[0]}`)) {
1609
1504
  throw new Error('invalid expression');
1610
1505
  }
1611
-
1612
1506
  return this.parseRawExp(raw);
1613
1507
  }
1508
+
1614
1509
  /**
1615
1510
  * Parse raw expression based on the expression type.
1616
1511
  * @param {Input} raw Raw expression.
1617
1512
  * @return {Evaluable}
1618
1513
  */
1619
-
1620
-
1621
1514
  parseRawExp(raw) {
1622
- var _this = this;
1623
-
1624
1515
  // Value / Reference
1625
1516
  if (!Array.isArray(raw)) {
1626
1517
  return this.getOperand(raw);
1627
1518
  }
1628
-
1629
1519
  let expression;
1630
1520
  let operandParser = this.getOperand;
1631
1521
  const operator = raw[0];
1632
1522
  const operands = raw.slice(1);
1523
+
1633
1524
  /**
1634
1525
  * Simplify the logical expression if possible.
1635
1526
  * - AND, OR with one operand is collapsed, i.e. reduced to inner expression
@@ -1641,192 +1532,139 @@ class Parser {
1641
1532
  * @param operands
1642
1533
  * @param collapsible
1643
1534
  */
1644
-
1645
- const logicalExpressionReducer = function (operands) {
1646
- let collapsible = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1647
-
1535
+ const logicalExpressionReducer = (operands, collapsible = false) => {
1648
1536
  if (operands.length === 0 || operands.filter(operand => operand.type === EvaluableType.Expression).length === 0) {
1649
- return _this.getOperand(raw);
1537
+ return this.getOperand(raw);
1650
1538
  }
1651
-
1652
1539
  return collapsible && operands.length === 1 ? operands[0] : undefined;
1653
1540
  };
1654
-
1655
1541
  switch (operator) {
1656
1542
  /**
1657
1543
  * Logical
1658
1544
  */
1659
1545
  case this.opts.operatorMapping.get(OPERATOR$4):
1660
1546
  expression = operands => logicalExpressionReducer(operands, true) || new And(operands);
1661
-
1662
1547
  operandParser = this.parseRawExp;
1663
1548
  break;
1664
-
1665
1549
  case this.opts.operatorMapping.get(OPERATOR$1):
1666
1550
  expression = operands => logicalExpressionReducer(operands, true) || new Or(operands);
1667
-
1668
1551
  operandParser = this.parseRawExp;
1669
1552
  break;
1670
-
1671
1553
  case this.opts.operatorMapping.get(OPERATOR$2):
1672
1554
  expression = operands => logicalExpressionReducer(operands) || new Nor(operands);
1673
-
1674
1555
  operandParser = this.parseRawExp;
1675
1556
  break;
1676
-
1677
1557
  case this.opts.operatorMapping.get(OPERATOR):
1678
1558
  expression = operands => logicalExpressionReducer(operands) || new Xor(operands);
1679
-
1680
1559
  operandParser = this.parseRawExp;
1681
1560
  break;
1682
-
1683
1561
  case this.opts.operatorMapping.get(OPERATOR$3):
1684
1562
  expression = operands => logicalExpressionReducer(operands) || new Not(...operands);
1685
-
1686
1563
  operandParser = this.parseRawExp;
1687
1564
  break;
1688
-
1689
1565
  /**
1690
1566
  * Comparison
1691
1567
  */
1692
-
1693
1568
  case this.opts.operatorMapping.get(OPERATOR$h):
1694
1569
  expression = operands => new Equal(...operands);
1695
-
1696
1570
  break;
1697
-
1698
1571
  case this.opts.operatorMapping.get(OPERATOR$b):
1699
1572
  expression = operands => new NotEqual(...operands);
1700
-
1701
1573
  break;
1702
-
1703
1574
  case this.opts.operatorMapping.get(OPERATOR$f):
1704
1575
  expression = operands => new GreaterThan(...operands);
1705
-
1706
1576
  break;
1707
-
1708
1577
  case this.opts.operatorMapping.get(OPERATOR$g):
1709
1578
  expression = operands => new GreaterThanOrEqual(...operands);
1710
-
1711
1579
  break;
1712
-
1713
1580
  case this.opts.operatorMapping.get(OPERATOR$c):
1714
1581
  expression = operands => new LessThan(...operands);
1715
-
1716
1582
  break;
1717
-
1718
1583
  case this.opts.operatorMapping.get(OPERATOR$d):
1719
1584
  expression = operands => new LessThanOrEqual(...operands);
1720
-
1721
1585
  break;
1722
-
1723
1586
  case this.opts.operatorMapping.get(OPERATOR$e):
1724
1587
  expression = operands => new In(...operands);
1725
-
1726
1588
  break;
1727
-
1728
1589
  case this.opts.operatorMapping.get(OPERATOR$a):
1729
1590
  expression = operands => new NotIn(...operands);
1730
-
1731
1591
  break;
1732
-
1733
1592
  case this.opts.operatorMapping.get(OPERATOR$8):
1734
1593
  expression = operands => new Prefix(...operands);
1735
-
1736
1594
  break;
1737
-
1738
1595
  case this.opts.operatorMapping.get(OPERATOR$6):
1739
1596
  expression = operands => new Suffix(...operands);
1740
-
1741
1597
  break;
1742
-
1743
1598
  case this.opts.operatorMapping.get(OPERATOR$9):
1744
1599
  expression = operands => new Overlap(...operands);
1745
-
1746
1600
  break;
1747
-
1748
1601
  case this.opts.operatorMapping.get(OPERATOR$5):
1749
1602
  expression = operands => new Undefined(...operands);
1750
-
1751
1603
  break;
1752
-
1753
1604
  case this.opts.operatorMapping.get(OPERATOR$7):
1754
1605
  expression = operands => new Present(...operands);
1755
-
1756
1606
  break;
1757
1607
  // Collection
1758
-
1759
1608
  default:
1760
1609
  return this.getOperand(raw);
1761
1610
  }
1762
-
1763
1611
  return expression(operands.map(operandParser.bind(this)));
1764
1612
  }
1613
+
1765
1614
  /**
1766
1615
  * Get resolved operand
1767
1616
  * @param raw Raw data
1768
1617
  */
1769
-
1770
-
1771
1618
  getOperand(raw) {
1772
1619
  const resolve = raw => this.opts.referencePredicate(raw) ? new Reference(this.opts.referenceTransform(raw)) : new Value(raw);
1773
-
1774
1620
  if (Array.isArray(raw)) {
1775
1621
  return new Collection(raw.map(item => resolve(item)));
1776
1622
  }
1777
-
1778
1623
  return resolve(raw);
1779
1624
  }
1780
-
1781
1625
  }
1782
1626
 
1783
- /**
1784
- * Main module.
1785
- * @module illogical
1786
- */
1787
1627
  /**
1788
1628
  * Condition engine
1789
1629
  */
1790
-
1791
1630
  class Engine {
1792
1631
  /**
1793
1632
  * @constructor
1794
1633
  * @param {Options?} options Parser options.
1795
1634
  */
1796
1635
  constructor(options) {
1636
+ _defineProperty(this, "parser", void 0);
1797
1637
  this.parser = new Parser(options);
1798
1638
  }
1639
+
1799
1640
  /**
1800
1641
  * Evaluate the expression.
1801
1642
  * @param {ExpressionInput} exp Raw expression.
1802
1643
  * @param {Context} ctx Evaluation data context.
1803
1644
  * @return {boolean}
1804
1645
  */
1805
-
1806
-
1807
1646
  evaluate(exp, ctx) {
1808
1647
  return this.parse(exp).evaluate(ctx);
1809
1648
  }
1649
+
1810
1650
  /**
1811
1651
  * Get expression statement
1812
1652
  * @param {ExpressionInput} exp Raw expression.
1813
1653
  * @return {string}
1814
1654
  */
1815
-
1816
-
1817
1655
  statement(exp) {
1818
1656
  return this.parse(exp).toString();
1819
1657
  }
1658
+
1820
1659
  /**
1821
1660
  * Parse expression.
1822
1661
  * @param {ExpressionInput} exp Raw expression.
1823
1662
  * @return {Evaluable}
1824
1663
  */
1825
-
1826
-
1827
1664
  parse(exp) {
1828
1665
  return this.parser.parse(exp);
1829
1666
  }
1667
+
1830
1668
  /**
1831
1669
  * Simplifies an expression with values in context.
1832
1670
  *
@@ -1842,22 +1680,16 @@ class Engine {
1842
1680
  * `optionalKeys` is considered to be present and thus will be evaluated
1843
1681
  * @returns {Inpunt | boolean}
1844
1682
  */
1845
-
1846
-
1847
1683
  simplify(exp, context, strictKeys, optionalKeys) {
1848
1684
  const result = this.parse(exp).simplify(context, strictKeys, optionalKeys);
1849
-
1850
1685
  if (isEvaluable(result)) {
1851
1686
  return result.serialize(this.parser.options);
1852
1687
  }
1853
-
1854
1688
  if (isBoolean(result)) {
1855
1689
  return result;
1856
1690
  }
1857
-
1858
1691
  throw new Error('non expression or boolean result should be returned');
1859
1692
  }
1860
-
1861
1693
  }
1862
1694
 
1863
1695
  exports.OPERATOR_AND = OPERATOR$4;
@@ -1878,4 +1710,6 @@ exports.OPERATOR_PRESENT = OPERATOR$7;
1878
1710
  exports.OPERATOR_SUFFIX = OPERATOR$6;
1879
1711
  exports.OPERATOR_UNDEFINED = OPERATOR$5;
1880
1712
  exports.OPERATOR_XOR = OPERATOR;
1881
- exports["default"] = Engine;
1713
+ exports.default = Engine;
1714
+ exports.defaultOptions = defaultOptions;
1715
+ exports.isEvaluable = isEvaluable;