@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.esm.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
function _defineProperty(e, r, t) {
|
|
2
|
+
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
3
|
+
value: t,
|
|
4
|
+
enumerable: !0,
|
|
5
|
+
configurable: !0,
|
|
6
|
+
writable: !0
|
|
7
|
+
}) : e[r] = t, e;
|
|
8
|
+
}
|
|
1
9
|
function _toPrimitive(t, r) {
|
|
2
10
|
if ("object" != typeof t || !t) return t;
|
|
3
11
|
var e = t[Symbol.toPrimitive];
|
|
@@ -10,21 +18,7 @@ function _toPrimitive(t, r) {
|
|
|
10
18
|
}
|
|
11
19
|
function _toPropertyKey(t) {
|
|
12
20
|
var i = _toPrimitive(t, "string");
|
|
13
|
-
return "symbol" == typeof i ? i :
|
|
14
|
-
}
|
|
15
|
-
function _defineProperty(obj, key, value) {
|
|
16
|
-
key = _toPropertyKey(key);
|
|
17
|
-
if (key in obj) {
|
|
18
|
-
Object.defineProperty(obj, key, {
|
|
19
|
-
value: value,
|
|
20
|
-
enumerable: true,
|
|
21
|
-
configurable: true,
|
|
22
|
-
writable: true
|
|
23
|
-
});
|
|
24
|
-
} else {
|
|
25
|
-
obj[key] = value;
|
|
26
|
-
}
|
|
27
|
-
return obj;
|
|
21
|
+
return "symbol" == typeof i ? i : i + "";
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
/**
|
|
@@ -32,7 +26,14 @@ function _defineProperty(obj, key, value) {
|
|
|
32
26
|
* @param value Tested value.
|
|
33
27
|
*/
|
|
34
28
|
function isNumber(value) {
|
|
35
|
-
return typeof value === 'number'
|
|
29
|
+
return typeof value === 'number';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Is number predicate.
|
|
33
|
+
* @param value Tested value.
|
|
34
|
+
*/
|
|
35
|
+
function isInfinite(value) {
|
|
36
|
+
return typeof value === 'number' && !isFinite(value);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -54,50 +55,573 @@ function isObject(value) {
|
|
|
54
55
|
if (typeof value !== 'object' || (value === null || value === void 0 ? void 0 : value.constructor) !== Object) {
|
|
55
56
|
return false;
|
|
56
57
|
}
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Is Boolean predicate.
|
|
62
|
-
* @param value tested value.
|
|
63
|
-
* @return result of the test
|
|
64
|
-
*/
|
|
65
|
-
function isBoolean(value) {
|
|
66
|
-
return typeof value === 'boolean';
|
|
67
|
-
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Is Boolean predicate.
|
|
63
|
+
* @param value tested value.
|
|
64
|
+
* @return result of the test
|
|
65
|
+
*/
|
|
66
|
+
function isBoolean(value) {
|
|
67
|
+
return typeof value === 'boolean';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if a value is a an Evaluable
|
|
72
|
+
* @param {Result | Evaluable} value value to check if is Evaluable
|
|
73
|
+
* @returns {Evaluable}
|
|
74
|
+
*/
|
|
75
|
+
function isEvaluable(value) {
|
|
76
|
+
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';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Ensures all values are results.
|
|
81
|
+
* @param {(Result | Evaluable)[]} values results or evaluables
|
|
82
|
+
* @returns {boolean} type guard
|
|
83
|
+
*/
|
|
84
|
+
function areAllResults(values) {
|
|
85
|
+
return values.every(value => !isEvaluable(value));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Ensures all values are numbers.
|
|
90
|
+
* @param {Result[]} results results or evaluables
|
|
91
|
+
* @returns {boolean} type guard
|
|
92
|
+
*/
|
|
93
|
+
function areAllNumbers(results) {
|
|
94
|
+
return results.every(isNumber);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Valid types for context members
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Evaluation Context
|
|
103
|
+
* Holds references used during the evaluation process.
|
|
104
|
+
* Format: key: value.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Evaluation result
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
let EvaluableType = /*#__PURE__*/function (EvaluableType) {
|
|
112
|
+
EvaluableType["Operand"] = "Operand";
|
|
113
|
+
EvaluableType["Expression"] = "Expression";
|
|
114
|
+
return EvaluableType;
|
|
115
|
+
}({});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Evaluable
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Abstract arithmetic expression
|
|
123
|
+
*/
|
|
124
|
+
class Arithmetic {
|
|
125
|
+
/**
|
|
126
|
+
* @constructor
|
|
127
|
+
* @param {string} operator String representation of the operator.
|
|
128
|
+
* @param {symbol} operatorSymbol Operator symbol.
|
|
129
|
+
* @param {Operand[]} operands Operands.
|
|
130
|
+
*/
|
|
131
|
+
constructor(operator, operatorSymbol, operands) {
|
|
132
|
+
this.operator = operator;
|
|
133
|
+
this.operatorSymbol = operatorSymbol;
|
|
134
|
+
this.operands = operands;
|
|
135
|
+
_defineProperty(this, "type", EvaluableType.Expression);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Performs the arithmetic operation on the operands evaluated values.
|
|
140
|
+
* @param {Result[]} results Operand result values.
|
|
141
|
+
* @returns {Result}
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* {@link Evaluable.evaluate}
|
|
146
|
+
*/
|
|
147
|
+
evaluate(ctx) {
|
|
148
|
+
return this.operate(this.operands.map(operand => operand.evaluate(ctx)));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* {@link Evaluable.toString}
|
|
153
|
+
*/
|
|
154
|
+
toString() {
|
|
155
|
+
return `(${this.operands.map(operand => operand.toString()).join(` ${this.operator} `)})`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* {@link Evaluable.simplify}
|
|
160
|
+
*/
|
|
161
|
+
simplify(...args) {
|
|
162
|
+
const results = this.operands.map(operand => operand.simplify(...args));
|
|
163
|
+
if (areAllResults(results)) {
|
|
164
|
+
return this.operate(results);
|
|
165
|
+
}
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* {@link Evaluable.serialize}
|
|
171
|
+
*/
|
|
172
|
+
serialize(options) {
|
|
173
|
+
const {
|
|
174
|
+
operatorMapping
|
|
175
|
+
} = options;
|
|
176
|
+
const operator = operatorMapping.get(this.operatorSymbol);
|
|
177
|
+
if (operator === undefined) {
|
|
178
|
+
throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
|
|
179
|
+
}
|
|
180
|
+
return [operator, ...this.operands.map(operand => operand.serialize(options))];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Operator key
|
|
185
|
+
const OPERATOR$l = Symbol('DIVIDE');
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Divide operation expression
|
|
189
|
+
*/
|
|
190
|
+
class Divide extends Arithmetic {
|
|
191
|
+
/**
|
|
192
|
+
* @constructor Generic constructor
|
|
193
|
+
* @param {Evaluable[]} args
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @constructor
|
|
198
|
+
* @param {Operand[]} operands Operands.
|
|
199
|
+
*/
|
|
200
|
+
constructor(...operands) {
|
|
201
|
+
if (operands.length < 2) {
|
|
202
|
+
throw new Error('divide expression requires at least 2 operands');
|
|
203
|
+
}
|
|
204
|
+
super('/', OPERATOR$l, operands);
|
|
205
|
+
}
|
|
206
|
+
operate(results) {
|
|
207
|
+
if (!areAllNumbers(results)) {
|
|
208
|
+
throw new Error('operands must be numbers for divide');
|
|
209
|
+
}
|
|
210
|
+
const result = results.reduce((acc, result) => acc / result);
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const getNumDecimals = num => {
|
|
216
|
+
const numberSplit = num.toString().split('.');
|
|
217
|
+
return numberSplit.length == 2 ? numberSplit[1].length : 0;
|
|
218
|
+
};
|
|
219
|
+
const operateWithExpectedDecimals = operation => (first, second) => {
|
|
220
|
+
const numDecimals1 = getNumDecimals(first);
|
|
221
|
+
const numDecimals2 = getNumDecimals(second);
|
|
222
|
+
const maxDecimals = operation === 'multiply' ? numDecimals1 + numDecimals2 : numDecimals1 > numDecimals2 ? numDecimals1 : numDecimals2;
|
|
223
|
+
return operation === 'sum' ? Number((first + second).toFixed(maxDecimals)) : operation === 'subtract' ? Number((first - second).toFixed(maxDecimals)) : Number((first * second).toFixed(maxDecimals));
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// Operator key
|
|
227
|
+
const OPERATOR$k = Symbol('MULTIPLY');
|
|
228
|
+
const multiplyWithExpectedDecimals = operateWithExpectedDecimals('multiply');
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Multiply operation expression
|
|
232
|
+
*/
|
|
233
|
+
class Multiply extends Arithmetic {
|
|
234
|
+
/**
|
|
235
|
+
* @constructor Generic constructor
|
|
236
|
+
* @param {Evaluable[]} args
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @constructor
|
|
241
|
+
* @param {Operand[]} operands Operands.
|
|
242
|
+
*/
|
|
243
|
+
constructor(...operands) {
|
|
244
|
+
if (operands.length < 2) {
|
|
245
|
+
throw new Error('multiply expression requires at least 2 operands');
|
|
246
|
+
}
|
|
247
|
+
super('*', OPERATOR$k, operands);
|
|
248
|
+
}
|
|
249
|
+
operate(results) {
|
|
250
|
+
if (!areAllNumbers(results)) {
|
|
251
|
+
throw new Error('operands must be numbers for multiply');
|
|
252
|
+
}
|
|
253
|
+
return results.reduce((acc, result) => multiplyWithExpectedDecimals(acc, result));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Operator key
|
|
258
|
+
const OPERATOR$j = Symbol('SUBTRACT');
|
|
259
|
+
const subtractWithExpectedDecimals = operateWithExpectedDecimals('subtract');
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Subtract operation expression
|
|
263
|
+
*/
|
|
264
|
+
class Subtract extends Arithmetic {
|
|
265
|
+
/**
|
|
266
|
+
* @constructor Generic constructor
|
|
267
|
+
* @param {Evaluable[]} args
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @constructor
|
|
272
|
+
* @param {Operand[]} operands Operands.
|
|
273
|
+
*/
|
|
274
|
+
constructor(...operands) {
|
|
275
|
+
if (operands.length < 2) {
|
|
276
|
+
throw new Error('subtract expression requires at least 2 operands');
|
|
277
|
+
}
|
|
278
|
+
super('-', OPERATOR$j, operands);
|
|
279
|
+
}
|
|
280
|
+
operate(results) {
|
|
281
|
+
if (!areAllNumbers(results)) {
|
|
282
|
+
throw new Error('operands must be numbers for subtract');
|
|
283
|
+
}
|
|
284
|
+
return results.reduce((acc, result) => subtractWithExpectedDecimals(acc, result));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Operator key
|
|
289
|
+
const OPERATOR$i = Symbol('SUM');
|
|
290
|
+
const addWithExpectedDecimals = operateWithExpectedDecimals('sum');
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Sum operation expression
|
|
294
|
+
*/
|
|
295
|
+
class Sum extends Arithmetic {
|
|
296
|
+
/**
|
|
297
|
+
* @constructor Generic constructor
|
|
298
|
+
* @param {Evaluable[]} args
|
|
299
|
+
*/
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @constructor
|
|
303
|
+
* @param {Operand[]} operands Operands.
|
|
304
|
+
*/
|
|
305
|
+
constructor(...operands) {
|
|
306
|
+
if (operands.length < 2) {
|
|
307
|
+
throw new Error('sum expression requires at least 2 operands');
|
|
308
|
+
}
|
|
309
|
+
super('+', OPERATOR$i, operands);
|
|
310
|
+
}
|
|
311
|
+
operate(results) {
|
|
312
|
+
if (!areAllNumbers(results)) {
|
|
313
|
+
throw new Error('operands must be numbers for sum');
|
|
314
|
+
}
|
|
315
|
+
return results.reduce((acc, result) => addWithExpectedDecimals(acc, result));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Abstract operand
|
|
321
|
+
*/
|
|
322
|
+
class Operand {
|
|
323
|
+
constructor() {
|
|
324
|
+
_defineProperty(this, "type", EvaluableType.Operand);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* {@link Evaluable.evaluate}
|
|
328
|
+
*/
|
|
329
|
+
/**
|
|
330
|
+
* {@link Evaluable.simplify}
|
|
331
|
+
*/
|
|
332
|
+
/**
|
|
333
|
+
* {@link Evaluable.serialize}
|
|
334
|
+
*/
|
|
335
|
+
/**
|
|
336
|
+
* Get the strict representation.
|
|
337
|
+
*/
|
|
338
|
+
toString() {
|
|
339
|
+
throw new Error('not implemented exception');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Print value as string
|
|
345
|
+
* @param {Result} value
|
|
346
|
+
* @return {string}
|
|
347
|
+
*/
|
|
348
|
+
function printValue(value) {
|
|
349
|
+
if (isString(value)) {
|
|
350
|
+
return `"${value}"`;
|
|
351
|
+
}
|
|
352
|
+
return `${value}`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Static value operand
|
|
357
|
+
*/
|
|
358
|
+
class Value extends Operand {
|
|
359
|
+
/**
|
|
360
|
+
* @constructor
|
|
361
|
+
* @param {Result} value Constant value.
|
|
362
|
+
*/
|
|
363
|
+
constructor(value) {
|
|
364
|
+
if (Array.isArray(value)) {
|
|
365
|
+
throw new Error('deprecated direct usage of array, please use Collection operand');
|
|
366
|
+
}
|
|
367
|
+
super();
|
|
368
|
+
_defineProperty(this, "value", void 0);
|
|
369
|
+
this.value = value;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* {@link Evaluable.evaluate}
|
|
374
|
+
*/
|
|
375
|
+
evaluate() {
|
|
376
|
+
return this.value;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* {@link Evaluable.simplify}
|
|
381
|
+
*/
|
|
382
|
+
simplify() {
|
|
383
|
+
return this.value;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* {@link Evaluable.serialize}
|
|
388
|
+
*/
|
|
389
|
+
serialize() {
|
|
390
|
+
return this.value;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Get the strict representation of the operand.
|
|
395
|
+
* @return {string}
|
|
396
|
+
*/
|
|
397
|
+
toString() {
|
|
398
|
+
return printValue(this.value);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Convert a value to number if possible, otherwise return undefined
|
|
404
|
+
* @param value value to be converted to number
|
|
405
|
+
*/
|
|
406
|
+
const toNumber = value => {
|
|
407
|
+
const isValueNumber = isNumber(value);
|
|
408
|
+
if (isValueNumber) {
|
|
409
|
+
return value;
|
|
410
|
+
} else if (isString(value)) {
|
|
411
|
+
if (value.match(/^\d+\.\d+$/)) {
|
|
412
|
+
return parseFloat(value);
|
|
413
|
+
} else if (value.match(/^0$|^[1-9]\d*$/)) {
|
|
414
|
+
return parseInt(value);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return undefined;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Convert a value to string if possible, otherwise return undefined
|
|
422
|
+
* @param value value to be converted to string
|
|
423
|
+
*/
|
|
424
|
+
const toString = value => {
|
|
425
|
+
if (isNumber(value)) {
|
|
426
|
+
return `${value}`;
|
|
427
|
+
} else if (isString(value)) {
|
|
428
|
+
return value;
|
|
429
|
+
}
|
|
430
|
+
return undefined;
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* Convert a value to number if it's type is string, otherwise return NaN
|
|
434
|
+
* @param value value to be converted to number
|
|
435
|
+
*/
|
|
436
|
+
const toDateNumber = value => {
|
|
437
|
+
if (isString(value)) {
|
|
438
|
+
return Date.parse(value);
|
|
439
|
+
}
|
|
440
|
+
return NaN;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
|
|
444
|
+
const arrayIndexRegex = /\[(\d+)]/g;
|
|
445
|
+
const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
|
|
446
|
+
const parseKey = key => {
|
|
447
|
+
const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
|
|
448
|
+
return !keys ? [] : keys.flatMap(key => {
|
|
449
|
+
const unwrappedKey = parseBacktickWrappedKey(key);
|
|
450
|
+
const keys = [];
|
|
451
|
+
const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
|
|
452
|
+
if (parseResult) {
|
|
453
|
+
var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
|
|
454
|
+
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);
|
|
455
|
+
keys.push(extractedKey);
|
|
456
|
+
const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
|
|
457
|
+
if (rawIndexes) {
|
|
458
|
+
for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
|
|
459
|
+
keys.push(parseInt(indexResult[1]));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
keys.push(unwrappedKey);
|
|
464
|
+
}
|
|
465
|
+
return keys;
|
|
466
|
+
});
|
|
467
|
+
};
|
|
468
|
+
const complexKeyExpression = /{([^{}]+)}/;
|
|
469
|
+
function extractComplexKeys(ctx, key) {
|
|
470
|
+
// Resolve complex keys
|
|
471
|
+
let complexKeyMatches = complexKeyExpression.exec(key);
|
|
472
|
+
while (complexKeyMatches) {
|
|
473
|
+
const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
|
|
474
|
+
if (resolvedValue === undefined) {
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
key = key.replace(complexKeyExpression, `${resolvedValue}`);
|
|
478
|
+
complexKeyMatches = complexKeyExpression.exec(key);
|
|
479
|
+
}
|
|
480
|
+
return parseKey(key);
|
|
481
|
+
}
|
|
482
|
+
const isContext = value => isObject(value);
|
|
483
|
+
const simpleValueLookup = keys => ctx => {
|
|
484
|
+
let pointer = ctx;
|
|
485
|
+
for (const key of keys) {
|
|
486
|
+
if (typeof key === 'number') {
|
|
487
|
+
if (!Array.isArray(pointer)) {
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
pointer = pointer[key];
|
|
491
|
+
} else if (!isContext(pointer)) {
|
|
492
|
+
return undefined;
|
|
493
|
+
} else {
|
|
494
|
+
pointer = pointer[key];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return pointer;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Lookup for the reference in the context.
|
|
502
|
+
* The nested context value is annotated with "." delimiter.
|
|
503
|
+
* @example address.city
|
|
504
|
+
* @param {Context} ctx
|
|
505
|
+
* @param {string} key Context lookup key.
|
|
506
|
+
* @return {Result}
|
|
507
|
+
*/
|
|
508
|
+
function complexValueLookup(ctx, key) {
|
|
509
|
+
const keys = extractComplexKeys(ctx, key);
|
|
510
|
+
if (!keys) {
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
|
|
514
|
+
}
|
|
515
|
+
let DataType = /*#__PURE__*/function (DataType) {
|
|
516
|
+
DataType["Number"] = "Number";
|
|
517
|
+
DataType["String"] = "String";
|
|
518
|
+
return DataType;
|
|
519
|
+
}({});
|
|
520
|
+
|
|
521
|
+
// Equivalent to /^.+\.\((Number|String)\)$/
|
|
522
|
+
const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
|
|
523
|
+
const isComplexKey = key => key.indexOf('{') > -1;
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Reference operand resolved within the context
|
|
527
|
+
*/
|
|
528
|
+
class Reference extends Operand {
|
|
529
|
+
/**
|
|
530
|
+
* @constructor
|
|
531
|
+
* @param {string} key Context key.
|
|
532
|
+
*/
|
|
533
|
+
constructor(key) {
|
|
534
|
+
if (key.trim() === '') {
|
|
535
|
+
throw new Error('invalid reference key');
|
|
536
|
+
}
|
|
537
|
+
super();
|
|
538
|
+
_defineProperty(this, "key", void 0);
|
|
539
|
+
_defineProperty(this, "dataType", void 0);
|
|
540
|
+
_defineProperty(this, "valueLookup", void 0);
|
|
541
|
+
_defineProperty(this, "getKeys", void 0);
|
|
542
|
+
this.key = key;
|
|
543
|
+
const dataTypeMatch = dataTypeRegex.exec(this.key);
|
|
544
|
+
if (dataTypeMatch) {
|
|
545
|
+
this.dataType = DataType[dataTypeMatch[1]];
|
|
546
|
+
}
|
|
547
|
+
if (this.key.match(/.\(.+\)$/)) {
|
|
548
|
+
this.key = this.key.replace(/.\(.+\)$/, '');
|
|
549
|
+
}
|
|
550
|
+
if (isComplexKey(this.key)) {
|
|
551
|
+
this.valueLookup = context => complexValueLookup(context, this.key);
|
|
552
|
+
this.getKeys = context => extractComplexKeys(context, this.key);
|
|
553
|
+
} else {
|
|
554
|
+
const keys = parseKey(this.key);
|
|
555
|
+
this.valueLookup = simpleValueLookup(keys);
|
|
556
|
+
this.getKeys = () => keys;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
68
559
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
560
|
+
/**
|
|
561
|
+
* Evaluate in the given context.
|
|
562
|
+
* @param {Context} ctx
|
|
563
|
+
* @return {boolean}
|
|
564
|
+
*/
|
|
565
|
+
evaluate(ctx) {
|
|
566
|
+
return this.toDataType(this.valueLookup(ctx));
|
|
567
|
+
}
|
|
77
568
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
569
|
+
/**
|
|
570
|
+
* {@link Evaluable.simplify}
|
|
571
|
+
*/
|
|
572
|
+
simplify(ctx, strictKeys, optionalKeys) {
|
|
573
|
+
var _this$getKeys;
|
|
574
|
+
const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
|
|
575
|
+
if (ctx[key] !== undefined) {
|
|
576
|
+
return this.evaluate(ctx);
|
|
577
|
+
}
|
|
578
|
+
if (!key || typeof key === 'number') {
|
|
579
|
+
return this;
|
|
580
|
+
}
|
|
581
|
+
return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
|
|
582
|
+
}
|
|
81
583
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
584
|
+
/**
|
|
585
|
+
* {@link Evaluable.serialize}
|
|
586
|
+
*/
|
|
587
|
+
serialize({
|
|
588
|
+
referenceSerialization
|
|
589
|
+
}) {
|
|
590
|
+
const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
|
|
591
|
+
return referenceSerialization(key);
|
|
592
|
+
}
|
|
87
593
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
594
|
+
/**
|
|
595
|
+
* Get the strict representation of the operand.
|
|
596
|
+
* @return {string}
|
|
597
|
+
*/
|
|
598
|
+
toString() {
|
|
599
|
+
return `{${this.key}}`;
|
|
600
|
+
}
|
|
91
601
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
602
|
+
/**
|
|
603
|
+
* Converts a value to a specified data type
|
|
604
|
+
* Silently returns original value if data type conversion has not been implemented.
|
|
605
|
+
* @param value value to cast as data type
|
|
606
|
+
*/
|
|
607
|
+
toDataType(value) {
|
|
608
|
+
let result = value;
|
|
609
|
+
switch (this.dataType) {
|
|
610
|
+
case DataType.Number:
|
|
611
|
+
result = toNumber(value);
|
|
612
|
+
break;
|
|
613
|
+
case DataType.String:
|
|
614
|
+
result = toString(value);
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
if (value && result === undefined) {
|
|
618
|
+
console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
|
|
619
|
+
}
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
97
623
|
|
|
98
|
-
|
|
99
|
-
* Evaluable
|
|
100
|
-
*/
|
|
624
|
+
const isSimplifiedArithmeticExpression = (operand, result) => operand instanceof Arithmetic && !isEvaluable(result) && !isInfinite(result) && !(operand instanceof Reference);
|
|
101
625
|
|
|
102
626
|
/**
|
|
103
627
|
* Abstract comparison expression
|
|
@@ -148,6 +672,12 @@ class Comparison {
|
|
|
148
672
|
if (!isEvaluable(left) && !isEvaluable(right)) {
|
|
149
673
|
return this.comparison(left, right);
|
|
150
674
|
}
|
|
675
|
+
if (isEvaluable(left) && isSimplifiedArithmeticExpression(this.right, right)) {
|
|
676
|
+
return Reflect.construct(this.constructor, [left, new Value(right)]);
|
|
677
|
+
}
|
|
678
|
+
if (isEvaluable(right) && isSimplifiedArithmeticExpression(this.left, left)) {
|
|
679
|
+
return Reflect.construct(this.constructor, [new Value(left), right]);
|
|
680
|
+
}
|
|
151
681
|
return this;
|
|
152
682
|
}
|
|
153
683
|
|
|
@@ -194,47 +724,6 @@ class Equal extends Comparison {
|
|
|
194
724
|
}
|
|
195
725
|
}
|
|
196
726
|
|
|
197
|
-
/**
|
|
198
|
-
* Convert a value to number if possible, otherwise return undefined
|
|
199
|
-
* @param value value to be converted to number
|
|
200
|
-
*/
|
|
201
|
-
const toNumber = value => {
|
|
202
|
-
const isValueNumber = isNumber(value);
|
|
203
|
-
if (isValueNumber) {
|
|
204
|
-
return value;
|
|
205
|
-
} else if (isString(value)) {
|
|
206
|
-
if (value.match(/^\d+\.\d+$/)) {
|
|
207
|
-
return parseFloat(value);
|
|
208
|
-
} else if (value.match(/^0$|^[1-9]\d*$/)) {
|
|
209
|
-
return parseInt(value);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return undefined;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Convert a value to string if possible, otherwise return undefined
|
|
217
|
-
* @param value value to be converted to string
|
|
218
|
-
*/
|
|
219
|
-
const toString = value => {
|
|
220
|
-
if (isNumber(value)) {
|
|
221
|
-
return `${value}`;
|
|
222
|
-
} else if (isString(value)) {
|
|
223
|
-
return value;
|
|
224
|
-
}
|
|
225
|
-
return undefined;
|
|
226
|
-
};
|
|
227
|
-
/**
|
|
228
|
-
* Convert a value to number if it's type is string, otherwise return NaN
|
|
229
|
-
* @param value value to be converted to number
|
|
230
|
-
*/
|
|
231
|
-
const toDateNumber = value => {
|
|
232
|
-
if (isString(value)) {
|
|
233
|
-
return Date.parse(value);
|
|
234
|
-
}
|
|
235
|
-
return NaN;
|
|
236
|
-
};
|
|
237
|
-
|
|
238
727
|
// Operator key
|
|
239
728
|
const OPERATOR$g = Symbol('GE');
|
|
240
729
|
|
|
@@ -551,142 +1040,59 @@ class Overlap extends Comparison {
|
|
|
551
1040
|
const rightArray = right;
|
|
552
1041
|
if (leftArray.length === 0 && rightArray.length === 0) {
|
|
553
1042
|
return true;
|
|
554
|
-
}
|
|
555
|
-
return leftArray.some(element => rightArray.includes(element));
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* Get the strict representation of the expression.
|
|
560
|
-
* @return {string}
|
|
561
|
-
*/
|
|
562
|
-
toString() {
|
|
563
|
-
const left = this.left.toString();
|
|
564
|
-
const right = this.right.toString();
|
|
565
|
-
return `(${left} ${this.operator} ${right})`;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Operator key
|
|
570
|
-
const OPERATOR$8 = Symbol('PREFIX');
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Prefix comparison expression
|
|
574
|
-
*/
|
|
575
|
-
class Prefix extends Comparison {
|
|
576
|
-
/**
|
|
577
|
-
* @constructor
|
|
578
|
-
* @param {Evaluable} left Left operand.
|
|
579
|
-
* @param {Evaluable} right Right operand.
|
|
580
|
-
*/
|
|
581
|
-
|
|
582
|
-
constructor(left, right) {
|
|
583
|
-
if (arguments.length !== 2) {
|
|
584
|
-
throw new Error('comparison expression expects left and right operands');
|
|
585
|
-
}
|
|
586
|
-
super('prefix', OPERATOR$8, left, right);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* {@link Comparison.comparison}
|
|
591
|
-
*/
|
|
592
|
-
comparison(left, right) {
|
|
593
|
-
if (isString(left) === false || isString(right) === false) {
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
return right.startsWith(left);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Get the strict representation of the expression.
|
|
601
|
-
* @return {string}
|
|
602
|
-
*/
|
|
603
|
-
toString() {
|
|
604
|
-
const left = this.left.toString();
|
|
605
|
-
const right = this.right.toString();
|
|
606
|
-
return `(<${left}>${right})`;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* Abstract operand
|
|
612
|
-
*/
|
|
613
|
-
class Operand {
|
|
614
|
-
constructor() {
|
|
615
|
-
_defineProperty(this, "type", EvaluableType.Operand);
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* {@link Evaluable.evaluate}
|
|
619
|
-
*/
|
|
620
|
-
/**
|
|
621
|
-
* {@link Evaluable.simplify}
|
|
622
|
-
*/
|
|
623
|
-
/**
|
|
624
|
-
* {@link Evaluable.serialize}
|
|
625
|
-
*/
|
|
626
|
-
/**
|
|
627
|
-
* Get the strict representation.
|
|
628
|
-
*/
|
|
629
|
-
toString() {
|
|
630
|
-
throw new Error('not implemented exception');
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Print value as string
|
|
636
|
-
* @param {Result} value
|
|
637
|
-
* @return {string}
|
|
638
|
-
*/
|
|
639
|
-
function printValue(value) {
|
|
640
|
-
if (isString(value)) {
|
|
641
|
-
return `"${value}"`;
|
|
642
|
-
}
|
|
643
|
-
return `${value}`;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
/**
|
|
647
|
-
* Static value operand
|
|
648
|
-
*/
|
|
649
|
-
class Value extends Operand {
|
|
650
|
-
/**
|
|
651
|
-
* @constructor
|
|
652
|
-
* @param {Result} value Constant value.
|
|
653
|
-
*/
|
|
654
|
-
constructor(value) {
|
|
655
|
-
if (Array.isArray(value)) {
|
|
656
|
-
throw new Error('deprecated direct usage of array, please use Collection operand');
|
|
657
|
-
}
|
|
658
|
-
super();
|
|
659
|
-
_defineProperty(this, "value", void 0);
|
|
660
|
-
this.value = value;
|
|
1043
|
+
}
|
|
1044
|
+
return leftArray.some(element => rightArray.includes(element));
|
|
661
1045
|
}
|
|
662
1046
|
|
|
663
1047
|
/**
|
|
664
|
-
*
|
|
1048
|
+
* Get the strict representation of the expression.
|
|
1049
|
+
* @return {string}
|
|
665
1050
|
*/
|
|
666
|
-
|
|
667
|
-
|
|
1051
|
+
toString() {
|
|
1052
|
+
const left = this.left.toString();
|
|
1053
|
+
const right = this.right.toString();
|
|
1054
|
+
return `(${left} ${this.operator} ${right})`;
|
|
668
1055
|
}
|
|
1056
|
+
}
|
|
669
1057
|
|
|
1058
|
+
// Operator key
|
|
1059
|
+
const OPERATOR$8 = Symbol('PREFIX');
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Prefix comparison expression
|
|
1063
|
+
*/
|
|
1064
|
+
class Prefix extends Comparison {
|
|
670
1065
|
/**
|
|
671
|
-
*
|
|
1066
|
+
* @constructor
|
|
1067
|
+
* @param {Evaluable} left Left operand.
|
|
1068
|
+
* @param {Evaluable} right Right operand.
|
|
672
1069
|
*/
|
|
673
|
-
|
|
674
|
-
|
|
1070
|
+
|
|
1071
|
+
constructor(left, right) {
|
|
1072
|
+
if (arguments.length !== 2) {
|
|
1073
|
+
throw new Error('comparison expression expects left and right operands');
|
|
1074
|
+
}
|
|
1075
|
+
super('prefix', OPERATOR$8, left, right);
|
|
675
1076
|
}
|
|
676
1077
|
|
|
677
1078
|
/**
|
|
678
|
-
* {@link
|
|
1079
|
+
* {@link Comparison.comparison}
|
|
679
1080
|
*/
|
|
680
|
-
|
|
681
|
-
|
|
1081
|
+
comparison(left, right) {
|
|
1082
|
+
if (isString(left) === false || isString(right) === false) {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
return right.startsWith(left);
|
|
682
1086
|
}
|
|
683
1087
|
|
|
684
1088
|
/**
|
|
685
|
-
* Get the strict representation of the
|
|
1089
|
+
* Get the strict representation of the expression.
|
|
686
1090
|
* @return {string}
|
|
687
1091
|
*/
|
|
688
1092
|
toString() {
|
|
689
|
-
|
|
1093
|
+
const left = this.left.toString();
|
|
1094
|
+
const right = this.right.toString();
|
|
1095
|
+
return `(<${left}>${right})`;
|
|
690
1096
|
}
|
|
691
1097
|
}
|
|
692
1098
|
|
|
@@ -1233,187 +1639,6 @@ class Collection extends Operand {
|
|
|
1233
1639
|
}
|
|
1234
1640
|
}
|
|
1235
1641
|
|
|
1236
|
-
const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
|
|
1237
|
-
const arrayIndexRegex = /\[(\d+)]/g;
|
|
1238
|
-
const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
|
|
1239
|
-
const parseKey = key => {
|
|
1240
|
-
const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
|
|
1241
|
-
return !keys ? [] : keys.flatMap(key => {
|
|
1242
|
-
const unwrappedKey = parseBacktickWrappedKey(key);
|
|
1243
|
-
const keys = [];
|
|
1244
|
-
const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
|
|
1245
|
-
if (parseResult) {
|
|
1246
|
-
var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
|
|
1247
|
-
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);
|
|
1248
|
-
keys.push(extractedKey);
|
|
1249
|
-
const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
|
|
1250
|
-
if (rawIndexes) {
|
|
1251
|
-
for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
|
|
1252
|
-
keys.push(parseInt(indexResult[1]));
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
} else {
|
|
1256
|
-
keys.push(unwrappedKey);
|
|
1257
|
-
}
|
|
1258
|
-
return keys;
|
|
1259
|
-
});
|
|
1260
|
-
};
|
|
1261
|
-
const complexKeyExpression = /{([^{}]+)}/;
|
|
1262
|
-
function extractComplexKeys(ctx, key) {
|
|
1263
|
-
// Resolve complex keys
|
|
1264
|
-
let complexKeyMatches = complexKeyExpression.exec(key);
|
|
1265
|
-
while (complexKeyMatches) {
|
|
1266
|
-
const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
|
|
1267
|
-
if (resolvedValue === undefined) {
|
|
1268
|
-
return undefined;
|
|
1269
|
-
}
|
|
1270
|
-
key = key.replace(complexKeyExpression, `${resolvedValue}`);
|
|
1271
|
-
complexKeyMatches = complexKeyExpression.exec(key);
|
|
1272
|
-
}
|
|
1273
|
-
return parseKey(key);
|
|
1274
|
-
}
|
|
1275
|
-
const isContext = value => isObject(value);
|
|
1276
|
-
const simpleValueLookup = keys => ctx => {
|
|
1277
|
-
let pointer = ctx;
|
|
1278
|
-
for (const key of keys) {
|
|
1279
|
-
if (typeof key === 'number') {
|
|
1280
|
-
if (!Array.isArray(pointer)) {
|
|
1281
|
-
return undefined;
|
|
1282
|
-
}
|
|
1283
|
-
pointer = pointer[key];
|
|
1284
|
-
} else if (!isContext(pointer)) {
|
|
1285
|
-
return undefined;
|
|
1286
|
-
} else {
|
|
1287
|
-
pointer = pointer[key];
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
return pointer;
|
|
1291
|
-
};
|
|
1292
|
-
|
|
1293
|
-
/**
|
|
1294
|
-
* Lookup for the reference in the context.
|
|
1295
|
-
* The nested context value is annotated with "." delimiter.
|
|
1296
|
-
* @example address.city
|
|
1297
|
-
* @param {Context} ctx
|
|
1298
|
-
* @param {string} key Context lookup key.
|
|
1299
|
-
* @return {Result}
|
|
1300
|
-
*/
|
|
1301
|
-
function complexValueLookup(ctx, key) {
|
|
1302
|
-
const keys = extractComplexKeys(ctx, key);
|
|
1303
|
-
if (!keys) {
|
|
1304
|
-
return undefined;
|
|
1305
|
-
}
|
|
1306
|
-
return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
|
|
1307
|
-
}
|
|
1308
|
-
let DataType = /*#__PURE__*/function (DataType) {
|
|
1309
|
-
DataType["Number"] = "Number";
|
|
1310
|
-
DataType["String"] = "String";
|
|
1311
|
-
return DataType;
|
|
1312
|
-
}({});
|
|
1313
|
-
|
|
1314
|
-
// Equivalent to /^.+\.\((Number|String)\)$/
|
|
1315
|
-
const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
|
|
1316
|
-
const isComplexKey = key => key.indexOf('{') > -1;
|
|
1317
|
-
|
|
1318
|
-
/**
|
|
1319
|
-
* Reference operand resolved within the context
|
|
1320
|
-
*/
|
|
1321
|
-
class Reference extends Operand {
|
|
1322
|
-
/**
|
|
1323
|
-
* @constructor
|
|
1324
|
-
* @param {string} key Context key.
|
|
1325
|
-
*/
|
|
1326
|
-
constructor(key) {
|
|
1327
|
-
if (key.trim() === '') {
|
|
1328
|
-
throw new Error('invalid reference key');
|
|
1329
|
-
}
|
|
1330
|
-
super();
|
|
1331
|
-
_defineProperty(this, "key", void 0);
|
|
1332
|
-
_defineProperty(this, "dataType", void 0);
|
|
1333
|
-
_defineProperty(this, "valueLookup", void 0);
|
|
1334
|
-
_defineProperty(this, "getKeys", void 0);
|
|
1335
|
-
this.key = key;
|
|
1336
|
-
const dataTypeMatch = dataTypeRegex.exec(this.key);
|
|
1337
|
-
if (dataTypeMatch) {
|
|
1338
|
-
this.dataType = DataType[dataTypeMatch[1]];
|
|
1339
|
-
}
|
|
1340
|
-
if (this.key.match(/.\(.+\)$/)) {
|
|
1341
|
-
this.key = this.key.replace(/.\(.+\)$/, '');
|
|
1342
|
-
}
|
|
1343
|
-
if (isComplexKey(this.key)) {
|
|
1344
|
-
this.valueLookup = context => complexValueLookup(context, this.key);
|
|
1345
|
-
this.getKeys = context => extractComplexKeys(context, this.key);
|
|
1346
|
-
} else {
|
|
1347
|
-
const keys = parseKey(this.key);
|
|
1348
|
-
this.valueLookup = simpleValueLookup(keys);
|
|
1349
|
-
this.getKeys = () => keys;
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
/**
|
|
1354
|
-
* Evaluate in the given context.
|
|
1355
|
-
* @param {Context} ctx
|
|
1356
|
-
* @return {boolean}
|
|
1357
|
-
*/
|
|
1358
|
-
evaluate(ctx) {
|
|
1359
|
-
return this.toDataType(this.valueLookup(ctx));
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
/**
|
|
1363
|
-
* {@link Evaluable.simplify}
|
|
1364
|
-
*/
|
|
1365
|
-
simplify(ctx, strictKeys, optionalKeys) {
|
|
1366
|
-
var _this$getKeys;
|
|
1367
|
-
const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
|
|
1368
|
-
if (ctx[key] !== undefined) {
|
|
1369
|
-
return this.evaluate(ctx);
|
|
1370
|
-
}
|
|
1371
|
-
if (!key || typeof key === 'number') {
|
|
1372
|
-
return this;
|
|
1373
|
-
}
|
|
1374
|
-
return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
/**
|
|
1378
|
-
* {@link Evaluable.serialize}
|
|
1379
|
-
*/
|
|
1380
|
-
serialize({
|
|
1381
|
-
referenceSerialization
|
|
1382
|
-
}) {
|
|
1383
|
-
const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
|
|
1384
|
-
return referenceSerialization(key);
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
/**
|
|
1388
|
-
* Get the strict representation of the operand.
|
|
1389
|
-
* @return {string}
|
|
1390
|
-
*/
|
|
1391
|
-
toString() {
|
|
1392
|
-
return `{${this.key}}`;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
/**
|
|
1396
|
-
* Converts a value to a specified data type
|
|
1397
|
-
* Silently returns original value if data type conversion has not been implemented.
|
|
1398
|
-
* @param value value to cast as data type
|
|
1399
|
-
*/
|
|
1400
|
-
toDataType(value) {
|
|
1401
|
-
let result = value;
|
|
1402
|
-
switch (this.dataType) {
|
|
1403
|
-
case DataType.Number:
|
|
1404
|
-
result = toNumber(value);
|
|
1405
|
-
break;
|
|
1406
|
-
case DataType.String:
|
|
1407
|
-
result = toString(value);
|
|
1408
|
-
break;
|
|
1409
|
-
}
|
|
1410
|
-
if (value && result === undefined) {
|
|
1411
|
-
console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
|
|
1412
|
-
}
|
|
1413
|
-
return result;
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
1642
|
// Option value whitelist
|
|
1418
1643
|
|
|
1419
1644
|
// Parser options
|
|
@@ -1449,7 +1674,9 @@ const defaultOperatorMapping = new Map([
|
|
|
1449
1674
|
// Comparison
|
|
1450
1675
|
[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'],
|
|
1451
1676
|
// Logical
|
|
1452
|
-
[OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT']
|
|
1677
|
+
[OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT'],
|
|
1678
|
+
// Arithmetic
|
|
1679
|
+
[OPERATOR$i, '+'], [OPERATOR$j, '-'], [OPERATOR$k, '*'], [OPERATOR$l, '/']]);
|
|
1453
1680
|
|
|
1454
1681
|
/**
|
|
1455
1682
|
* Default parser options
|
|
@@ -1463,6 +1690,14 @@ const defaultOptions = {
|
|
|
1463
1690
|
|
|
1464
1691
|
// Input types
|
|
1465
1692
|
|
|
1693
|
+
const invalidExpression = 'invalid expression';
|
|
1694
|
+
const logicalIfValidOperands = (operands, logical) => {
|
|
1695
|
+
if (operands.every(operand => operand instanceof Logical || operand instanceof Comparison)) {
|
|
1696
|
+
return logical;
|
|
1697
|
+
}
|
|
1698
|
+
throw new Error(invalidExpression);
|
|
1699
|
+
};
|
|
1700
|
+
|
|
1466
1701
|
/**
|
|
1467
1702
|
* Parser of raw expressions into Evaluable expression
|
|
1468
1703
|
*/
|
|
@@ -1473,7 +1708,8 @@ class Parser {
|
|
|
1473
1708
|
*/
|
|
1474
1709
|
constructor(options) {
|
|
1475
1710
|
_defineProperty(this, "opts", void 0);
|
|
1476
|
-
_defineProperty(this, "
|
|
1711
|
+
_defineProperty(this, "expectedRootOperators", void 0);
|
|
1712
|
+
_defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
|
|
1477
1713
|
this.opts = {
|
|
1478
1714
|
...defaultOptions
|
|
1479
1715
|
};
|
|
@@ -1485,7 +1721,7 @@ class Parser {
|
|
|
1485
1721
|
}
|
|
1486
1722
|
}
|
|
1487
1723
|
}
|
|
1488
|
-
this.
|
|
1724
|
+
this.expectedRootOperators = new Set(Array.from(this.opts.operatorMapping.entries()).filter(([symbol]) => !this.unexpectedRootSymbols.has(symbol)).map(([, operator]) => operator));
|
|
1489
1725
|
}
|
|
1490
1726
|
|
|
1491
1727
|
/**
|
|
@@ -1503,10 +1739,10 @@ class Parser {
|
|
|
1503
1739
|
*/
|
|
1504
1740
|
parse(raw) {
|
|
1505
1741
|
if (raw === undefined || raw === null || Array.isArray(raw) === false) {
|
|
1506
|
-
throw new Error(
|
|
1742
|
+
throw new Error(invalidExpression);
|
|
1507
1743
|
}
|
|
1508
|
-
if (raw.length === 0 || !this.
|
|
1509
|
-
throw new Error(
|
|
1744
|
+
if (raw.length === 0 || !this.expectedRootOperators.has(`${raw[0]}`)) {
|
|
1745
|
+
throw new Error(invalidExpression);
|
|
1510
1746
|
}
|
|
1511
1747
|
return this.parseRawExp(raw);
|
|
1512
1748
|
}
|
|
@@ -1544,71 +1780,103 @@ class Parser {
|
|
|
1544
1780
|
return collapsible && operands.length === 1 ? operands[0] : undefined;
|
|
1545
1781
|
};
|
|
1546
1782
|
switch (operator) {
|
|
1547
|
-
|
|
1548
|
-
* Logical
|
|
1549
|
-
*/
|
|
1783
|
+
// Logical
|
|
1550
1784
|
case this.opts.operatorMapping.get(OPERATOR$4):
|
|
1551
|
-
expression = operands => logicalExpressionReducer(operands, true) || new And(operands);
|
|
1785
|
+
expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new And(operands));
|
|
1552
1786
|
operandParser = this.parseRawExp;
|
|
1553
1787
|
break;
|
|
1554
1788
|
case this.opts.operatorMapping.get(OPERATOR$1):
|
|
1555
|
-
expression = operands => logicalExpressionReducer(operands, true) || new Or(operands);
|
|
1789
|
+
expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new Or(operands));
|
|
1556
1790
|
operandParser = this.parseRawExp;
|
|
1557
1791
|
break;
|
|
1558
1792
|
case this.opts.operatorMapping.get(OPERATOR$2):
|
|
1559
|
-
expression = operands => logicalExpressionReducer(operands) || new Nor(operands);
|
|
1793
|
+
expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Nor(operands));
|
|
1560
1794
|
operandParser = this.parseRawExp;
|
|
1561
1795
|
break;
|
|
1562
1796
|
case this.opts.operatorMapping.get(OPERATOR):
|
|
1563
|
-
expression = operands => logicalExpressionReducer(operands) || new Xor(operands);
|
|
1797
|
+
expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Xor(operands));
|
|
1564
1798
|
operandParser = this.parseRawExp;
|
|
1565
1799
|
break;
|
|
1566
1800
|
case this.opts.operatorMapping.get(OPERATOR$3):
|
|
1567
|
-
expression = operands => logicalExpressionReducer(operands) || new Not(...operands);
|
|
1801
|
+
expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Not(...operands));
|
|
1568
1802
|
operandParser = this.parseRawExp;
|
|
1569
1803
|
break;
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
*/
|
|
1804
|
+
|
|
1805
|
+
// Comparison
|
|
1573
1806
|
case this.opts.operatorMapping.get(OPERATOR$h):
|
|
1574
1807
|
expression = operands => new Equal(...operands);
|
|
1808
|
+
operandParser = this.parseRawExp;
|
|
1575
1809
|
break;
|
|
1576
1810
|
case this.opts.operatorMapping.get(OPERATOR$b):
|
|
1577
1811
|
expression = operands => new NotEqual(...operands);
|
|
1812
|
+
operandParser = this.parseRawExp;
|
|
1578
1813
|
break;
|
|
1579
1814
|
case this.opts.operatorMapping.get(OPERATOR$f):
|
|
1580
1815
|
expression = operands => new GreaterThan(...operands);
|
|
1816
|
+
operandParser = this.parseRawExp;
|
|
1581
1817
|
break;
|
|
1582
1818
|
case this.opts.operatorMapping.get(OPERATOR$g):
|
|
1583
1819
|
expression = operands => new GreaterThanOrEqual(...operands);
|
|
1820
|
+
operandParser = this.parseRawExp;
|
|
1584
1821
|
break;
|
|
1585
1822
|
case this.opts.operatorMapping.get(OPERATOR$c):
|
|
1586
1823
|
expression = operands => new LessThan(...operands);
|
|
1824
|
+
operandParser = this.parseRawExp;
|
|
1587
1825
|
break;
|
|
1588
1826
|
case this.opts.operatorMapping.get(OPERATOR$d):
|
|
1589
1827
|
expression = operands => new LessThanOrEqual(...operands);
|
|
1828
|
+
operandParser = this.parseRawExp;
|
|
1590
1829
|
break;
|
|
1830
|
+
|
|
1831
|
+
// Containment
|
|
1591
1832
|
case this.opts.operatorMapping.get(OPERATOR$e):
|
|
1592
1833
|
expression = operands => new In(...operands);
|
|
1593
1834
|
break;
|
|
1594
1835
|
case this.opts.operatorMapping.get(OPERATOR$a):
|
|
1595
1836
|
expression = operands => new NotIn(...operands);
|
|
1596
1837
|
break;
|
|
1838
|
+
|
|
1839
|
+
// Prefix
|
|
1597
1840
|
case this.opts.operatorMapping.get(OPERATOR$8):
|
|
1598
1841
|
expression = operands => new Prefix(...operands);
|
|
1599
1842
|
break;
|
|
1843
|
+
|
|
1844
|
+
// Suffix
|
|
1600
1845
|
case this.opts.operatorMapping.get(OPERATOR$6):
|
|
1601
1846
|
expression = operands => new Suffix(...operands);
|
|
1602
1847
|
break;
|
|
1848
|
+
|
|
1849
|
+
// Overlap
|
|
1603
1850
|
case this.opts.operatorMapping.get(OPERATOR$9):
|
|
1604
1851
|
expression = operands => new Overlap(...operands);
|
|
1605
1852
|
break;
|
|
1853
|
+
|
|
1854
|
+
// Presence
|
|
1606
1855
|
case this.opts.operatorMapping.get(OPERATOR$5):
|
|
1607
1856
|
expression = operands => new Undefined(...operands);
|
|
1608
1857
|
break;
|
|
1609
1858
|
case this.opts.operatorMapping.get(OPERATOR$7):
|
|
1610
1859
|
expression = operands => new Present(...operands);
|
|
1611
1860
|
break;
|
|
1861
|
+
|
|
1862
|
+
// Arithmetic
|
|
1863
|
+
case this.opts.operatorMapping.get(OPERATOR$i):
|
|
1864
|
+
expression = operands => new Sum(...operands);
|
|
1865
|
+
operandParser = this.parseRawExp;
|
|
1866
|
+
break;
|
|
1867
|
+
case this.opts.operatorMapping.get(OPERATOR$j):
|
|
1868
|
+
expression = operands => new Subtract(...operands);
|
|
1869
|
+
operandParser = this.parseRawExp;
|
|
1870
|
+
break;
|
|
1871
|
+
case this.opts.operatorMapping.get(OPERATOR$k):
|
|
1872
|
+
expression = operands => new Multiply(...operands);
|
|
1873
|
+
operandParser = this.parseRawExp;
|
|
1874
|
+
break;
|
|
1875
|
+
case this.opts.operatorMapping.get(OPERATOR$l):
|
|
1876
|
+
expression = operands => new Divide(...operands);
|
|
1877
|
+
operandParser = this.parseRawExp;
|
|
1878
|
+
break;
|
|
1879
|
+
|
|
1612
1880
|
// Collection
|
|
1613
1881
|
default:
|
|
1614
1882
|
return this.getOperand(raw);
|
|
@@ -1629,6 +1897,8 @@ class Parser {
|
|
|
1629
1897
|
}
|
|
1630
1898
|
}
|
|
1631
1899
|
|
|
1900
|
+
const unexpectedResultError = 'non expression or boolean result should be returned';
|
|
1901
|
+
|
|
1632
1902
|
/**
|
|
1633
1903
|
* Condition engine
|
|
1634
1904
|
*/
|
|
@@ -1649,7 +1919,11 @@ class Engine {
|
|
|
1649
1919
|
* @return {boolean}
|
|
1650
1920
|
*/
|
|
1651
1921
|
evaluate(exp, ctx) {
|
|
1652
|
-
|
|
1922
|
+
const result = this.parse(exp).evaluate(ctx);
|
|
1923
|
+
if (isBoolean(result)) {
|
|
1924
|
+
return result;
|
|
1925
|
+
}
|
|
1926
|
+
throw new Error(unexpectedResultError);
|
|
1653
1927
|
}
|
|
1654
1928
|
|
|
1655
1929
|
/**
|
|
@@ -1693,8 +1967,8 @@ class Engine {
|
|
|
1693
1967
|
if (isBoolean(result)) {
|
|
1694
1968
|
return result;
|
|
1695
1969
|
}
|
|
1696
|
-
throw new Error(
|
|
1970
|
+
throw new Error(unexpectedResultError);
|
|
1697
1971
|
}
|
|
1698
1972
|
}
|
|
1699
1973
|
|
|
1700
|
-
export { OPERATOR$4 as OPERATOR_AND, OPERATOR$h as OPERATOR_EQ, OPERATOR$g as OPERATOR_GE, OPERATOR$f as OPERATOR_GT, OPERATOR$e as OPERATOR_IN, OPERATOR$d as OPERATOR_LE, OPERATOR$c as OPERATOR_LT, OPERATOR$b as OPERATOR_NE, OPERATOR$2 as OPERATOR_NOR, OPERATOR$3 as OPERATOR_NOT, OPERATOR$a as OPERATOR_NOT_IN, OPERATOR$1 as OPERATOR_OR, OPERATOR$9 as OPERATOR_OVERLAP, OPERATOR$8 as OPERATOR_PREFIX, OPERATOR$7 as OPERATOR_PRESENT, OPERATOR$6 as OPERATOR_SUFFIX, OPERATOR$5 as OPERATOR_UNDEFINED, OPERATOR as OPERATOR_XOR, Engine as default, defaultOptions, isEvaluable };
|
|
1974
|
+
export { OPERATOR$4 as OPERATOR_AND, OPERATOR$l as OPERATOR_DIVIDE, OPERATOR$h as OPERATOR_EQ, OPERATOR$g as OPERATOR_GE, OPERATOR$f as OPERATOR_GT, OPERATOR$e as OPERATOR_IN, OPERATOR$d as OPERATOR_LE, OPERATOR$c as OPERATOR_LT, OPERATOR$k as OPERATOR_MULTIPLY, OPERATOR$b as OPERATOR_NE, OPERATOR$2 as OPERATOR_NOR, OPERATOR$3 as OPERATOR_NOT, OPERATOR$a as OPERATOR_NOT_IN, OPERATOR$1 as OPERATOR_OR, OPERATOR$9 as OPERATOR_OVERLAP, OPERATOR$8 as OPERATOR_PREFIX, OPERATOR$7 as OPERATOR_PRESENT, OPERATOR$j as OPERATOR_SUBTRACT, OPERATOR$6 as OPERATOR_SUFFIX, OPERATOR$i as OPERATOR_SUM, OPERATOR$5 as OPERATOR_UNDEFINED, OPERATOR as OPERATOR_XOR, Engine as default, defaultOptions, isEvaluable };
|