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