@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/changelog.md +8 -0
- package/lib/illogical.esm.js +688 -414
- package/lib/illogical.js +691 -413
- package/package.json +19 -19
- package/readme.md +103 -12
- package/types/common/type-check.d.ts +17 -0
- package/types/expression/arithmetic/divide.d.ts +14 -0
- package/types/expression/arithmetic/index.d.ts +42 -0
- package/types/expression/arithmetic/isSimplifiedArithmeticExpression.d.ts +3 -0
- package/types/expression/arithmetic/multiply.d.ts +14 -0
- package/types/expression/arithmetic/operateWithExpectedDecimals.d.ts +1 -0
- package/types/expression/arithmetic/subtract.d.ts +14 -0
- package/types/expression/arithmetic/sum.d.ts +14 -0
- package/types/index.d.ts +5 -1
- package/types/parser/index.d.ts +2 -1
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 :
|
|
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'
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
94
|
-
|
|
598
|
+
/**
|
|
599
|
+
* Get the strict representation of the operand.
|
|
600
|
+
* @return {string}
|
|
601
|
+
*/
|
|
602
|
+
toString() {
|
|
603
|
+
return `{${this.key}}`;
|
|
604
|
+
}
|
|
95
605
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
*
|
|
1052
|
+
* Get the strict representation of the expression.
|
|
1053
|
+
* @return {string}
|
|
669
1054
|
*/
|
|
670
|
-
|
|
671
|
-
|
|
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
|
-
*
|
|
1070
|
+
* @constructor
|
|
1071
|
+
* @param {Evaluable} left Left operand.
|
|
1072
|
+
* @param {Evaluable} right Right operand.
|
|
676
1073
|
*/
|
|
677
|
-
|
|
678
|
-
|
|
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
|
|
1083
|
+
* {@link Comparison.comparison}
|
|
683
1084
|
*/
|
|
684
|
-
|
|
685
|
-
|
|
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
|
|
1093
|
+
* Get the strict representation of the expression.
|
|
690
1094
|
* @return {string}
|
|
691
1095
|
*/
|
|
692
1096
|
toString() {
|
|
693
|
-
|
|
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, "
|
|
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.
|
|
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(
|
|
1746
|
+
throw new Error(invalidExpression);
|
|
1511
1747
|
}
|
|
1512
|
-
if (raw.length === 0 || !this.
|
|
1513
|
-
throw new Error(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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;
|