@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.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,597 @@ 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
|
+
* Helper function to assist with arithmetic evaluation. Ensures that all
|
|
140
|
+
* operands are present and are numbers. Throws error if any operand is not a
|
|
141
|
+
* number.
|
|
142
|
+
*
|
|
143
|
+
* @param {Result[]} results
|
|
144
|
+
* @returns {number[] | false} false if any operand is missing, otherwise the
|
|
145
|
+
* array of numbers
|
|
146
|
+
*/
|
|
147
|
+
getResultValues(results) {
|
|
148
|
+
const presentValues = results.filter(result => result !== null && result !== undefined);
|
|
149
|
+
// If we have missing context values the result must be false
|
|
150
|
+
if (presentValues.length !== results.length) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (!areAllNumbers(presentValues)) {
|
|
154
|
+
throw new Error(`operands must be numbers for ${this.constructor.name}`);
|
|
155
|
+
}
|
|
156
|
+
return presentValues;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Performs the arithmetic operation on the operands evaluated values.
|
|
161
|
+
* @param {Result[]} results Operand result values.
|
|
162
|
+
* @returns {Result}
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* {@link Evaluable.evaluate}
|
|
167
|
+
*/
|
|
168
|
+
evaluate(ctx) {
|
|
169
|
+
return this.operate(this.operands.map(operand => operand.evaluate(ctx)));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* {@link Evaluable.toString}
|
|
174
|
+
*/
|
|
175
|
+
toString() {
|
|
176
|
+
return `(${this.operands.map(operand => operand.toString()).join(` ${this.operator} `)})`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* {@link Evaluable.simplify}
|
|
181
|
+
*/
|
|
182
|
+
simplify(...args) {
|
|
183
|
+
const results = this.operands.map(operand => operand.simplify(...args));
|
|
184
|
+
if (areAllResults(results)) {
|
|
185
|
+
return this.operate(results);
|
|
186
|
+
}
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* {@link Evaluable.serialize}
|
|
192
|
+
*/
|
|
193
|
+
serialize(options) {
|
|
194
|
+
const {
|
|
195
|
+
operatorMapping
|
|
196
|
+
} = options;
|
|
197
|
+
const operator = operatorMapping.get(this.operatorSymbol);
|
|
198
|
+
if (operator === undefined) {
|
|
199
|
+
throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
|
|
200
|
+
}
|
|
201
|
+
return [operator, ...this.operands.map(operand => operand.serialize(options))];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Operator key
|
|
206
|
+
const OPERATOR$l = Symbol('DIVIDE');
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Divide operation expression
|
|
210
|
+
*/
|
|
211
|
+
class Divide extends Arithmetic {
|
|
212
|
+
/**
|
|
213
|
+
* @constructor Generic constructor
|
|
214
|
+
* @param {Evaluable[]} args
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @constructor
|
|
219
|
+
* @param {Operand[]} operands Operands.
|
|
220
|
+
*/
|
|
221
|
+
constructor(...operands) {
|
|
222
|
+
if (operands.length < 2) {
|
|
223
|
+
throw new Error('divide expression requires at least 2 operands');
|
|
224
|
+
}
|
|
225
|
+
super('/', OPERATOR$l, operands);
|
|
226
|
+
}
|
|
227
|
+
operate(results) {
|
|
228
|
+
const presentResults = this.getResultValues(results);
|
|
229
|
+
if (presentResults === false) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
return presentResults.reduce((acc, result) => acc / result);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const getNumDecimals = num => {
|
|
237
|
+
const numberSplit = num.toString().split('.');
|
|
238
|
+
return numberSplit.length == 2 ? numberSplit[1].length : 0;
|
|
239
|
+
};
|
|
240
|
+
const operateWithExpectedDecimals = operation => (first, second) => {
|
|
241
|
+
const numDecimals1 = getNumDecimals(first);
|
|
242
|
+
const numDecimals2 = getNumDecimals(second);
|
|
243
|
+
const maxDecimals = operation === 'multiply' ? numDecimals1 + numDecimals2 : numDecimals1 > numDecimals2 ? numDecimals1 : numDecimals2;
|
|
244
|
+
return operation === 'sum' ? Number((first + second).toFixed(maxDecimals)) : operation === 'subtract' ? Number((first - second).toFixed(maxDecimals)) : Number((first * second).toFixed(maxDecimals));
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Operator key
|
|
248
|
+
const OPERATOR$k = Symbol('MULTIPLY');
|
|
249
|
+
const multiplyWithExpectedDecimals = operateWithExpectedDecimals('multiply');
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Multiply operation expression
|
|
253
|
+
*/
|
|
254
|
+
class Multiply extends Arithmetic {
|
|
255
|
+
/**
|
|
256
|
+
* @constructor Generic constructor
|
|
257
|
+
* @param {Evaluable[]} args
|
|
258
|
+
*/
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @constructor
|
|
262
|
+
* @param {Operand[]} operands Operands.
|
|
263
|
+
*/
|
|
264
|
+
constructor(...operands) {
|
|
265
|
+
if (operands.length < 2) {
|
|
266
|
+
throw new Error('multiply expression requires at least 2 operands');
|
|
267
|
+
}
|
|
268
|
+
super('*', OPERATOR$k, operands);
|
|
269
|
+
}
|
|
270
|
+
operate(results) {
|
|
271
|
+
const presentResults = this.getResultValues(results);
|
|
272
|
+
if (presentResults === false) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
return presentResults.reduce((acc, result) => multiplyWithExpectedDecimals(acc, result));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Operator key
|
|
280
|
+
const OPERATOR$j = Symbol('SUBTRACT');
|
|
281
|
+
const subtractWithExpectedDecimals = operateWithExpectedDecimals('subtract');
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Subtract operation expression
|
|
285
|
+
*/
|
|
286
|
+
class Subtract extends Arithmetic {
|
|
287
|
+
/**
|
|
288
|
+
* @constructor Generic constructor
|
|
289
|
+
* @param {Evaluable[]} args
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @constructor
|
|
294
|
+
* @param {Operand[]} operands Operands.
|
|
295
|
+
*/
|
|
296
|
+
constructor(...operands) {
|
|
297
|
+
if (operands.length < 2) {
|
|
298
|
+
throw new Error('subtract expression requires at least 2 operands');
|
|
299
|
+
}
|
|
300
|
+
super('-', OPERATOR$j, operands);
|
|
301
|
+
}
|
|
302
|
+
operate(results) {
|
|
303
|
+
const presentResults = this.getResultValues(results);
|
|
304
|
+
if (presentResults === false) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return presentResults.reduce((acc, result) => subtractWithExpectedDecimals(acc, result));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Operator key
|
|
312
|
+
const OPERATOR$i = Symbol('SUM');
|
|
313
|
+
const addWithExpectedDecimals = operateWithExpectedDecimals('sum');
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Sum operation expression
|
|
317
|
+
*/
|
|
318
|
+
class Sum extends Arithmetic {
|
|
319
|
+
/**
|
|
320
|
+
* @constructor Generic constructor
|
|
321
|
+
* @param {Evaluable[]} args
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @constructor
|
|
326
|
+
* @param {Operand[]} operands Operands.
|
|
327
|
+
*/
|
|
328
|
+
constructor(...operands) {
|
|
329
|
+
if (operands.length < 2) {
|
|
330
|
+
throw new Error('sum expression requires at least 2 operands');
|
|
331
|
+
}
|
|
332
|
+
super('+', OPERATOR$i, operands);
|
|
333
|
+
}
|
|
334
|
+
operate(results) {
|
|
335
|
+
const presentResults = this.getResultValues(results);
|
|
336
|
+
if (presentResults === false) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
return presentResults.reduce((acc, result) => addWithExpectedDecimals(acc, result));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Abstract operand
|
|
345
|
+
*/
|
|
346
|
+
class Operand {
|
|
347
|
+
constructor() {
|
|
348
|
+
_defineProperty(this, "type", EvaluableType.Operand);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* {@link Evaluable.evaluate}
|
|
352
|
+
*/
|
|
353
|
+
/**
|
|
354
|
+
* {@link Evaluable.simplify}
|
|
355
|
+
*/
|
|
356
|
+
/**
|
|
357
|
+
* {@link Evaluable.serialize}
|
|
358
|
+
*/
|
|
359
|
+
/**
|
|
360
|
+
* Get the strict representation.
|
|
361
|
+
*/
|
|
362
|
+
toString() {
|
|
363
|
+
throw new Error('not implemented exception');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Print value as string
|
|
369
|
+
* @param {Result} value
|
|
370
|
+
* @return {string}
|
|
371
|
+
*/
|
|
372
|
+
function printValue(value) {
|
|
373
|
+
if (isString(value)) {
|
|
374
|
+
return `"${value}"`;
|
|
375
|
+
}
|
|
376
|
+
return `${value}`;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Static value operand
|
|
381
|
+
*/
|
|
382
|
+
class Value extends Operand {
|
|
383
|
+
/**
|
|
384
|
+
* @constructor
|
|
385
|
+
* @param {Result} value Constant value.
|
|
386
|
+
*/
|
|
387
|
+
constructor(value) {
|
|
388
|
+
if (Array.isArray(value)) {
|
|
389
|
+
throw new Error('deprecated direct usage of array, please use Collection operand');
|
|
390
|
+
}
|
|
391
|
+
super();
|
|
392
|
+
_defineProperty(this, "value", void 0);
|
|
393
|
+
this.value = value;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* {@link Evaluable.evaluate}
|
|
398
|
+
*/
|
|
399
|
+
evaluate() {
|
|
400
|
+
return this.value;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* {@link Evaluable.simplify}
|
|
405
|
+
*/
|
|
406
|
+
simplify() {
|
|
407
|
+
return this.value;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* {@link Evaluable.serialize}
|
|
412
|
+
*/
|
|
413
|
+
serialize() {
|
|
414
|
+
return this.value;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Get the strict representation of the operand.
|
|
419
|
+
* @return {string}
|
|
420
|
+
*/
|
|
421
|
+
toString() {
|
|
422
|
+
return printValue(this.value);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Convert a value to number if possible, otherwise return undefined
|
|
428
|
+
* @param value value to be converted to number
|
|
429
|
+
*/
|
|
430
|
+
const toNumber = value => {
|
|
431
|
+
const isValueNumber = isNumber(value);
|
|
432
|
+
if (isValueNumber) {
|
|
433
|
+
return value;
|
|
434
|
+
} else if (isString(value)) {
|
|
435
|
+
if (value.match(/^\d+\.\d+$/)) {
|
|
436
|
+
return parseFloat(value);
|
|
437
|
+
} else if (value.match(/^0$|^[1-9]\d*$/)) {
|
|
438
|
+
return parseInt(value);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return undefined;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Convert a value to string if possible, otherwise return undefined
|
|
446
|
+
* @param value value to be converted to string
|
|
447
|
+
*/
|
|
448
|
+
const toString = value => {
|
|
449
|
+
if (isNumber(value)) {
|
|
450
|
+
return `${value}`;
|
|
451
|
+
} else if (isString(value)) {
|
|
452
|
+
return value;
|
|
453
|
+
}
|
|
454
|
+
return undefined;
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Convert a value to number if it's type is string, otherwise return NaN
|
|
458
|
+
* @param value value to be converted to number
|
|
459
|
+
*/
|
|
460
|
+
const toDateNumber = value => {
|
|
461
|
+
if (isString(value)) {
|
|
462
|
+
return Date.parse(value);
|
|
463
|
+
}
|
|
464
|
+
return NaN;
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
|
|
468
|
+
const arrayIndexRegex = /\[(\d+)]/g;
|
|
469
|
+
const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
|
|
470
|
+
const parseKey = key => {
|
|
471
|
+
const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
|
|
472
|
+
return !keys ? [] : keys.flatMap(key => {
|
|
473
|
+
const unwrappedKey = parseBacktickWrappedKey(key);
|
|
474
|
+
const keys = [];
|
|
475
|
+
const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
|
|
476
|
+
if (parseResult) {
|
|
477
|
+
var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
|
|
478
|
+
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);
|
|
479
|
+
keys.push(extractedKey);
|
|
480
|
+
const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
|
|
481
|
+
if (rawIndexes) {
|
|
482
|
+
for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
|
|
483
|
+
keys.push(parseInt(indexResult[1]));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
keys.push(unwrappedKey);
|
|
488
|
+
}
|
|
489
|
+
return keys;
|
|
490
|
+
});
|
|
491
|
+
};
|
|
492
|
+
const complexKeyExpression = /{([^{}]+)}/;
|
|
493
|
+
function extractComplexKeys(ctx, key) {
|
|
494
|
+
// Resolve complex keys
|
|
495
|
+
let complexKeyMatches = complexKeyExpression.exec(key);
|
|
496
|
+
while (complexKeyMatches) {
|
|
497
|
+
const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
|
|
498
|
+
if (resolvedValue === undefined) {
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
key = key.replace(complexKeyExpression, `${resolvedValue}`);
|
|
502
|
+
complexKeyMatches = complexKeyExpression.exec(key);
|
|
503
|
+
}
|
|
504
|
+
return parseKey(key);
|
|
505
|
+
}
|
|
506
|
+
const isContext = value => isObject(value);
|
|
507
|
+
const simpleValueLookup = keys => ctx => {
|
|
508
|
+
let pointer = ctx;
|
|
509
|
+
for (const key of keys) {
|
|
510
|
+
if (typeof key === 'number') {
|
|
511
|
+
if (!Array.isArray(pointer)) {
|
|
512
|
+
return undefined;
|
|
513
|
+
}
|
|
514
|
+
pointer = pointer[key];
|
|
515
|
+
} else if (!isContext(pointer)) {
|
|
516
|
+
return undefined;
|
|
517
|
+
} else {
|
|
518
|
+
pointer = pointer[key];
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return pointer;
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Lookup for the reference in the context.
|
|
526
|
+
* The nested context value is annotated with "." delimiter.
|
|
527
|
+
* @example address.city
|
|
528
|
+
* @param {Context} ctx
|
|
529
|
+
* @param {string} key Context lookup key.
|
|
530
|
+
* @return {Result}
|
|
531
|
+
*/
|
|
532
|
+
function complexValueLookup(ctx, key) {
|
|
533
|
+
const keys = extractComplexKeys(ctx, key);
|
|
534
|
+
if (!keys) {
|
|
535
|
+
return undefined;
|
|
536
|
+
}
|
|
537
|
+
return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
|
|
538
|
+
}
|
|
539
|
+
let DataType = /*#__PURE__*/function (DataType) {
|
|
540
|
+
DataType["Number"] = "Number";
|
|
541
|
+
DataType["String"] = "String";
|
|
542
|
+
return DataType;
|
|
543
|
+
}({});
|
|
544
|
+
|
|
545
|
+
// Equivalent to /^.+\.\((Number|String)\)$/
|
|
546
|
+
const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
|
|
547
|
+
const isComplexKey = key => key.indexOf('{') > -1;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Reference operand resolved within the context
|
|
551
|
+
*/
|
|
552
|
+
class Reference extends Operand {
|
|
553
|
+
/**
|
|
554
|
+
* @constructor
|
|
555
|
+
* @param {string} key Context key.
|
|
556
|
+
*/
|
|
557
|
+
constructor(key) {
|
|
558
|
+
if (key.trim() === '') {
|
|
559
|
+
throw new Error('invalid reference key');
|
|
560
|
+
}
|
|
561
|
+
super();
|
|
562
|
+
_defineProperty(this, "key", void 0);
|
|
563
|
+
_defineProperty(this, "dataType", void 0);
|
|
564
|
+
_defineProperty(this, "valueLookup", void 0);
|
|
565
|
+
_defineProperty(this, "getKeys", void 0);
|
|
566
|
+
this.key = key;
|
|
567
|
+
const dataTypeMatch = dataTypeRegex.exec(this.key);
|
|
568
|
+
if (dataTypeMatch) {
|
|
569
|
+
this.dataType = DataType[dataTypeMatch[1]];
|
|
570
|
+
}
|
|
571
|
+
if (this.key.match(/.\(.+\)$/)) {
|
|
572
|
+
this.key = this.key.replace(/.\(.+\)$/, '');
|
|
573
|
+
}
|
|
574
|
+
if (isComplexKey(this.key)) {
|
|
575
|
+
this.valueLookup = context => complexValueLookup(context, this.key);
|
|
576
|
+
this.getKeys = context => extractComplexKeys(context, this.key);
|
|
577
|
+
} else {
|
|
578
|
+
const keys = parseKey(this.key);
|
|
579
|
+
this.valueLookup = simpleValueLookup(keys);
|
|
580
|
+
this.getKeys = () => keys;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
68
583
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
584
|
+
/**
|
|
585
|
+
* Evaluate in the given context.
|
|
586
|
+
* @param {Context} ctx
|
|
587
|
+
* @return {boolean}
|
|
588
|
+
*/
|
|
589
|
+
evaluate(ctx) {
|
|
590
|
+
return this.toDataType(this.valueLookup(ctx));
|
|
591
|
+
}
|
|
77
592
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
593
|
+
/**
|
|
594
|
+
* {@link Evaluable.simplify}
|
|
595
|
+
*/
|
|
596
|
+
simplify(ctx, strictKeys, optionalKeys) {
|
|
597
|
+
var _this$getKeys;
|
|
598
|
+
const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
|
|
599
|
+
if (ctx[key] !== undefined) {
|
|
600
|
+
return this.evaluate(ctx);
|
|
601
|
+
}
|
|
602
|
+
if (!key || typeof key === 'number') {
|
|
603
|
+
return this;
|
|
604
|
+
}
|
|
605
|
+
return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
|
|
606
|
+
}
|
|
81
607
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
608
|
+
/**
|
|
609
|
+
* {@link Evaluable.serialize}
|
|
610
|
+
*/
|
|
611
|
+
serialize({
|
|
612
|
+
referenceSerialization
|
|
613
|
+
}) {
|
|
614
|
+
const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
|
|
615
|
+
return referenceSerialization(key);
|
|
616
|
+
}
|
|
87
617
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
618
|
+
/**
|
|
619
|
+
* Get the strict representation of the operand.
|
|
620
|
+
* @return {string}
|
|
621
|
+
*/
|
|
622
|
+
toString() {
|
|
623
|
+
return `{${this.key}}`;
|
|
624
|
+
}
|
|
91
625
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
626
|
+
/**
|
|
627
|
+
* Converts a value to a specified data type
|
|
628
|
+
* Silently returns original value if data type conversion has not been implemented.
|
|
629
|
+
* @param value value to cast as data type
|
|
630
|
+
*/
|
|
631
|
+
toDataType(value) {
|
|
632
|
+
let result = value;
|
|
633
|
+
switch (this.dataType) {
|
|
634
|
+
case DataType.Number:
|
|
635
|
+
result = toNumber(value);
|
|
636
|
+
break;
|
|
637
|
+
case DataType.String:
|
|
638
|
+
result = toString(value);
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
if (value && result === undefined) {
|
|
642
|
+
console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
|
|
643
|
+
}
|
|
644
|
+
return result;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
97
647
|
|
|
98
|
-
|
|
99
|
-
* Evaluable
|
|
100
|
-
*/
|
|
648
|
+
const isSimplifiedArithmeticExpression = (operand, result) => operand instanceof Arithmetic && !isEvaluable(result) && !isInfinite(result) && !(operand instanceof Reference);
|
|
101
649
|
|
|
102
650
|
/**
|
|
103
651
|
* Abstract comparison expression
|
|
@@ -148,6 +696,12 @@ class Comparison {
|
|
|
148
696
|
if (!isEvaluable(left) && !isEvaluable(right)) {
|
|
149
697
|
return this.comparison(left, right);
|
|
150
698
|
}
|
|
699
|
+
if (isEvaluable(left) && isSimplifiedArithmeticExpression(this.right, right)) {
|
|
700
|
+
return Reflect.construct(this.constructor, [left, new Value(right)]);
|
|
701
|
+
}
|
|
702
|
+
if (isEvaluable(right) && isSimplifiedArithmeticExpression(this.left, left)) {
|
|
703
|
+
return Reflect.construct(this.constructor, [new Value(left), right]);
|
|
704
|
+
}
|
|
151
705
|
return this;
|
|
152
706
|
}
|
|
153
707
|
|
|
@@ -194,47 +748,6 @@ class Equal extends Comparison {
|
|
|
194
748
|
}
|
|
195
749
|
}
|
|
196
750
|
|
|
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
751
|
// Operator key
|
|
239
752
|
const OPERATOR$g = Symbol('GE');
|
|
240
753
|
|
|
@@ -551,142 +1064,59 @@ class Overlap extends Comparison {
|
|
|
551
1064
|
const rightArray = right;
|
|
552
1065
|
if (leftArray.length === 0 && rightArray.length === 0) {
|
|
553
1066
|
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;
|
|
1067
|
+
}
|
|
1068
|
+
return leftArray.some(element => rightArray.includes(element));
|
|
661
1069
|
}
|
|
662
1070
|
|
|
663
1071
|
/**
|
|
664
|
-
*
|
|
1072
|
+
* Get the strict representation of the expression.
|
|
1073
|
+
* @return {string}
|
|
665
1074
|
*/
|
|
666
|
-
|
|
667
|
-
|
|
1075
|
+
toString() {
|
|
1076
|
+
const left = this.left.toString();
|
|
1077
|
+
const right = this.right.toString();
|
|
1078
|
+
return `(${left} ${this.operator} ${right})`;
|
|
668
1079
|
}
|
|
1080
|
+
}
|
|
669
1081
|
|
|
1082
|
+
// Operator key
|
|
1083
|
+
const OPERATOR$8 = Symbol('PREFIX');
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Prefix comparison expression
|
|
1087
|
+
*/
|
|
1088
|
+
class Prefix extends Comparison {
|
|
670
1089
|
/**
|
|
671
|
-
*
|
|
1090
|
+
* @constructor
|
|
1091
|
+
* @param {Evaluable} left Left operand.
|
|
1092
|
+
* @param {Evaluable} right Right operand.
|
|
672
1093
|
*/
|
|
673
|
-
|
|
674
|
-
|
|
1094
|
+
|
|
1095
|
+
constructor(left, right) {
|
|
1096
|
+
if (arguments.length !== 2) {
|
|
1097
|
+
throw new Error('comparison expression expects left and right operands');
|
|
1098
|
+
}
|
|
1099
|
+
super('prefix', OPERATOR$8, left, right);
|
|
675
1100
|
}
|
|
676
1101
|
|
|
677
1102
|
/**
|
|
678
|
-
* {@link
|
|
1103
|
+
* {@link Comparison.comparison}
|
|
679
1104
|
*/
|
|
680
|
-
|
|
681
|
-
|
|
1105
|
+
comparison(left, right) {
|
|
1106
|
+
if (isString(left) === false || isString(right) === false) {
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
return right.startsWith(left);
|
|
682
1110
|
}
|
|
683
1111
|
|
|
684
1112
|
/**
|
|
685
|
-
* Get the strict representation of the
|
|
1113
|
+
* Get the strict representation of the expression.
|
|
686
1114
|
* @return {string}
|
|
687
1115
|
*/
|
|
688
1116
|
toString() {
|
|
689
|
-
|
|
1117
|
+
const left = this.left.toString();
|
|
1118
|
+
const right = this.right.toString();
|
|
1119
|
+
return `(<${left}>${right})`;
|
|
690
1120
|
}
|
|
691
1121
|
}
|
|
692
1122
|
|
|
@@ -1233,187 +1663,6 @@ class Collection extends Operand {
|
|
|
1233
1663
|
}
|
|
1234
1664
|
}
|
|
1235
1665
|
|
|
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
1666
|
// Option value whitelist
|
|
1418
1667
|
|
|
1419
1668
|
// Parser options
|
|
@@ -1449,7 +1698,9 @@ const defaultOperatorMapping = new Map([
|
|
|
1449
1698
|
// Comparison
|
|
1450
1699
|
[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
1700
|
// Logical
|
|
1452
|
-
[OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT']
|
|
1701
|
+
[OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT'],
|
|
1702
|
+
// Arithmetic
|
|
1703
|
+
[OPERATOR$i, '+'], [OPERATOR$j, '-'], [OPERATOR$k, '*'], [OPERATOR$l, '/']]);
|
|
1453
1704
|
|
|
1454
1705
|
/**
|
|
1455
1706
|
* Default parser options
|
|
@@ -1463,6 +1714,14 @@ const defaultOptions = {
|
|
|
1463
1714
|
|
|
1464
1715
|
// Input types
|
|
1465
1716
|
|
|
1717
|
+
const invalidExpression = 'invalid expression';
|
|
1718
|
+
const logicalIfValidOperands = (operands, logical) => {
|
|
1719
|
+
if (operands.every(operand => operand instanceof Logical || operand instanceof Comparison)) {
|
|
1720
|
+
return logical;
|
|
1721
|
+
}
|
|
1722
|
+
throw new Error(invalidExpression);
|
|
1723
|
+
};
|
|
1724
|
+
|
|
1466
1725
|
/**
|
|
1467
1726
|
* Parser of raw expressions into Evaluable expression
|
|
1468
1727
|
*/
|
|
@@ -1473,7 +1732,8 @@ class Parser {
|
|
|
1473
1732
|
*/
|
|
1474
1733
|
constructor(options) {
|
|
1475
1734
|
_defineProperty(this, "opts", void 0);
|
|
1476
|
-
_defineProperty(this, "
|
|
1735
|
+
_defineProperty(this, "expectedRootOperators", void 0);
|
|
1736
|
+
_defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
|
|
1477
1737
|
this.opts = {
|
|
1478
1738
|
...defaultOptions
|
|
1479
1739
|
};
|
|
@@ -1485,7 +1745,7 @@ class Parser {
|
|
|
1485
1745
|
}
|
|
1486
1746
|
}
|
|
1487
1747
|
}
|
|
1488
|
-
this.
|
|
1748
|
+
this.expectedRootOperators = new Set(Array.from(this.opts.operatorMapping.entries()).filter(([symbol]) => !this.unexpectedRootSymbols.has(symbol)).map(([, operator]) => operator));
|
|
1489
1749
|
}
|
|
1490
1750
|
|
|
1491
1751
|
/**
|
|
@@ -1503,10 +1763,10 @@ class Parser {
|
|
|
1503
1763
|
*/
|
|
1504
1764
|
parse(raw) {
|
|
1505
1765
|
if (raw === undefined || raw === null || Array.isArray(raw) === false) {
|
|
1506
|
-
throw new Error(
|
|
1766
|
+
throw new Error(invalidExpression);
|
|
1507
1767
|
}
|
|
1508
|
-
if (raw.length === 0 || !this.
|
|
1509
|
-
throw new Error(
|
|
1768
|
+
if (raw.length === 0 || !this.expectedRootOperators.has(`${raw[0]}`)) {
|
|
1769
|
+
throw new Error(invalidExpression);
|
|
1510
1770
|
}
|
|
1511
1771
|
return this.parseRawExp(raw);
|
|
1512
1772
|
}
|
|
@@ -1544,71 +1804,103 @@ class Parser {
|
|
|
1544
1804
|
return collapsible && operands.length === 1 ? operands[0] : undefined;
|
|
1545
1805
|
};
|
|
1546
1806
|
switch (operator) {
|
|
1547
|
-
|
|
1548
|
-
* Logical
|
|
1549
|
-
*/
|
|
1807
|
+
// Logical
|
|
1550
1808
|
case this.opts.operatorMapping.get(OPERATOR$4):
|
|
1551
|
-
expression = operands => logicalExpressionReducer(operands, true) || new And(operands);
|
|
1809
|
+
expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new And(operands));
|
|
1552
1810
|
operandParser = this.parseRawExp;
|
|
1553
1811
|
break;
|
|
1554
1812
|
case this.opts.operatorMapping.get(OPERATOR$1):
|
|
1555
|
-
expression = operands => logicalExpressionReducer(operands, true) || new Or(operands);
|
|
1813
|
+
expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new Or(operands));
|
|
1556
1814
|
operandParser = this.parseRawExp;
|
|
1557
1815
|
break;
|
|
1558
1816
|
case this.opts.operatorMapping.get(OPERATOR$2):
|
|
1559
|
-
expression = operands => logicalExpressionReducer(operands) || new Nor(operands);
|
|
1817
|
+
expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Nor(operands));
|
|
1560
1818
|
operandParser = this.parseRawExp;
|
|
1561
1819
|
break;
|
|
1562
1820
|
case this.opts.operatorMapping.get(OPERATOR):
|
|
1563
|
-
expression = operands => logicalExpressionReducer(operands) || new Xor(operands);
|
|
1821
|
+
expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Xor(operands));
|
|
1564
1822
|
operandParser = this.parseRawExp;
|
|
1565
1823
|
break;
|
|
1566
1824
|
case this.opts.operatorMapping.get(OPERATOR$3):
|
|
1567
|
-
expression = operands => logicalExpressionReducer(operands) || new Not(...operands);
|
|
1825
|
+
expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Not(...operands));
|
|
1568
1826
|
operandParser = this.parseRawExp;
|
|
1569
1827
|
break;
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
*/
|
|
1828
|
+
|
|
1829
|
+
// Comparison
|
|
1573
1830
|
case this.opts.operatorMapping.get(OPERATOR$h):
|
|
1574
1831
|
expression = operands => new Equal(...operands);
|
|
1832
|
+
operandParser = this.parseRawExp;
|
|
1575
1833
|
break;
|
|
1576
1834
|
case this.opts.operatorMapping.get(OPERATOR$b):
|
|
1577
1835
|
expression = operands => new NotEqual(...operands);
|
|
1836
|
+
operandParser = this.parseRawExp;
|
|
1578
1837
|
break;
|
|
1579
1838
|
case this.opts.operatorMapping.get(OPERATOR$f):
|
|
1580
1839
|
expression = operands => new GreaterThan(...operands);
|
|
1840
|
+
operandParser = this.parseRawExp;
|
|
1581
1841
|
break;
|
|
1582
1842
|
case this.opts.operatorMapping.get(OPERATOR$g):
|
|
1583
1843
|
expression = operands => new GreaterThanOrEqual(...operands);
|
|
1844
|
+
operandParser = this.parseRawExp;
|
|
1584
1845
|
break;
|
|
1585
1846
|
case this.opts.operatorMapping.get(OPERATOR$c):
|
|
1586
1847
|
expression = operands => new LessThan(...operands);
|
|
1848
|
+
operandParser = this.parseRawExp;
|
|
1587
1849
|
break;
|
|
1588
1850
|
case this.opts.operatorMapping.get(OPERATOR$d):
|
|
1589
1851
|
expression = operands => new LessThanOrEqual(...operands);
|
|
1852
|
+
operandParser = this.parseRawExp;
|
|
1590
1853
|
break;
|
|
1854
|
+
|
|
1855
|
+
// Containment
|
|
1591
1856
|
case this.opts.operatorMapping.get(OPERATOR$e):
|
|
1592
1857
|
expression = operands => new In(...operands);
|
|
1593
1858
|
break;
|
|
1594
1859
|
case this.opts.operatorMapping.get(OPERATOR$a):
|
|
1595
1860
|
expression = operands => new NotIn(...operands);
|
|
1596
1861
|
break;
|
|
1862
|
+
|
|
1863
|
+
// Prefix
|
|
1597
1864
|
case this.opts.operatorMapping.get(OPERATOR$8):
|
|
1598
1865
|
expression = operands => new Prefix(...operands);
|
|
1599
1866
|
break;
|
|
1867
|
+
|
|
1868
|
+
// Suffix
|
|
1600
1869
|
case this.opts.operatorMapping.get(OPERATOR$6):
|
|
1601
1870
|
expression = operands => new Suffix(...operands);
|
|
1602
1871
|
break;
|
|
1872
|
+
|
|
1873
|
+
// Overlap
|
|
1603
1874
|
case this.opts.operatorMapping.get(OPERATOR$9):
|
|
1604
1875
|
expression = operands => new Overlap(...operands);
|
|
1605
1876
|
break;
|
|
1877
|
+
|
|
1878
|
+
// Presence
|
|
1606
1879
|
case this.opts.operatorMapping.get(OPERATOR$5):
|
|
1607
1880
|
expression = operands => new Undefined(...operands);
|
|
1608
1881
|
break;
|
|
1609
1882
|
case this.opts.operatorMapping.get(OPERATOR$7):
|
|
1610
1883
|
expression = operands => new Present(...operands);
|
|
1611
1884
|
break;
|
|
1885
|
+
|
|
1886
|
+
// Arithmetic
|
|
1887
|
+
case this.opts.operatorMapping.get(OPERATOR$i):
|
|
1888
|
+
expression = operands => new Sum(...operands);
|
|
1889
|
+
operandParser = this.parseRawExp;
|
|
1890
|
+
break;
|
|
1891
|
+
case this.opts.operatorMapping.get(OPERATOR$j):
|
|
1892
|
+
expression = operands => new Subtract(...operands);
|
|
1893
|
+
operandParser = this.parseRawExp;
|
|
1894
|
+
break;
|
|
1895
|
+
case this.opts.operatorMapping.get(OPERATOR$k):
|
|
1896
|
+
expression = operands => new Multiply(...operands);
|
|
1897
|
+
operandParser = this.parseRawExp;
|
|
1898
|
+
break;
|
|
1899
|
+
case this.opts.operatorMapping.get(OPERATOR$l):
|
|
1900
|
+
expression = operands => new Divide(...operands);
|
|
1901
|
+
operandParser = this.parseRawExp;
|
|
1902
|
+
break;
|
|
1903
|
+
|
|
1612
1904
|
// Collection
|
|
1613
1905
|
default:
|
|
1614
1906
|
return this.getOperand(raw);
|
|
@@ -1629,6 +1921,8 @@ class Parser {
|
|
|
1629
1921
|
}
|
|
1630
1922
|
}
|
|
1631
1923
|
|
|
1924
|
+
const unexpectedResultError = 'non expression or boolean result should be returned';
|
|
1925
|
+
|
|
1632
1926
|
/**
|
|
1633
1927
|
* Condition engine
|
|
1634
1928
|
*/
|
|
@@ -1649,7 +1943,11 @@ class Engine {
|
|
|
1649
1943
|
* @return {boolean}
|
|
1650
1944
|
*/
|
|
1651
1945
|
evaluate(exp, ctx) {
|
|
1652
|
-
|
|
1946
|
+
const result = this.parse(exp).evaluate(ctx);
|
|
1947
|
+
if (isBoolean(result)) {
|
|
1948
|
+
return result;
|
|
1949
|
+
}
|
|
1950
|
+
throw new Error(unexpectedResultError);
|
|
1653
1951
|
}
|
|
1654
1952
|
|
|
1655
1953
|
/**
|
|
@@ -1693,8 +1991,8 @@ class Engine {
|
|
|
1693
1991
|
if (isBoolean(result)) {
|
|
1694
1992
|
return result;
|
|
1695
1993
|
}
|
|
1696
|
-
throw new Error(
|
|
1994
|
+
throw new Error(unexpectedResultError);
|
|
1697
1995
|
}
|
|
1698
1996
|
}
|
|
1699
1997
|
|
|
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 };
|
|
1998
|
+
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 };
|