@briza/illogical 1.6.1 → 1.7.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 +14 -4
- package/lib/illogical.esm.js +589 -34
- package/lib/illogical.js +589 -34
- package/package.json +3 -3
- package/readme.md +21 -9
- package/types/common/type-check.d.ts +2 -0
- package/types/index.d.ts +1 -0
- package/types/operand/collection.d.ts +5 -0
- package/types/parser/index.d.ts +4 -1
- package/types/unsafe/and.d.ts +3 -0
- package/types/unsafe/arithmetic.d.ts +6 -0
- package/types/unsafe/comparison.d.ts +7 -0
- package/types/unsafe/equality.d.ts +5 -0
- package/types/unsafe/in.d.ts +3 -0
- package/types/unsafe/nor.d.ts +4 -0
- package/types/unsafe/not-in.d.ts +3 -0
- package/types/unsafe/not.d.ts +3 -0
- package/types/unsafe/or.d.ts +3 -0
- package/types/unsafe/overlap.d.ts +3 -0
- package/types/unsafe/prefix.d.ts +5 -0
- package/types/unsafe/present.d.ts +4 -0
- package/types/unsafe/simplify.d.ts +4 -0
- package/types/unsafe/type-check.d.ts +12 -0
- package/types/unsafe/xor.d.ts +4 -0
package/lib/illogical.esm.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
function _defineProperty(e, r, t) {
|
|
2
2
|
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
3
3
|
value: t,
|
|
4
|
-
enumerable:
|
|
5
|
-
configurable:
|
|
6
|
-
writable:
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true
|
|
7
7
|
}) : e[r] = t, e;
|
|
8
8
|
}
|
|
9
9
|
function _toPrimitive(t, r) {
|
|
10
10
|
if ("object" != typeof t || !t) return t;
|
|
11
11
|
var e = t[Symbol.toPrimitive];
|
|
12
12
|
if (void 0 !== e) {
|
|
13
|
-
var i = e.call(t, r
|
|
13
|
+
var i = e.call(t, r);
|
|
14
14
|
if ("object" != typeof i) return i;
|
|
15
15
|
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
16
16
|
}
|
|
@@ -90,9 +90,15 @@ function areAllResults(values) {
|
|
|
90
90
|
* @param {Result[]} results results or evaluables
|
|
91
91
|
* @returns {boolean} type guard
|
|
92
92
|
*/
|
|
93
|
-
function areAllNumbers(results) {
|
|
93
|
+
function areAllNumbers$1(results) {
|
|
94
94
|
return results.every(isNumber);
|
|
95
95
|
}
|
|
96
|
+
function isUndefined(value) {
|
|
97
|
+
return value === undefined;
|
|
98
|
+
}
|
|
99
|
+
function isNull(value) {
|
|
100
|
+
return value === null;
|
|
101
|
+
}
|
|
96
102
|
|
|
97
103
|
/**
|
|
98
104
|
* Valid types for context members
|
|
@@ -150,7 +156,7 @@ class Arithmetic {
|
|
|
150
156
|
if (presentValues.length !== results.length) {
|
|
151
157
|
return false;
|
|
152
158
|
}
|
|
153
|
-
if (!areAllNumbers(presentValues)) {
|
|
159
|
+
if (!areAllNumbers$1(presentValues)) {
|
|
154
160
|
throw new Error(`operands must be numbers for ${this.constructor.name}`);
|
|
155
161
|
}
|
|
156
162
|
return presentValues;
|
|
@@ -466,29 +472,33 @@ const toDateNumber = value => {
|
|
|
466
472
|
|
|
467
473
|
const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
|
|
468
474
|
const arrayIndexRegex = /\[(\d+)]/g;
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
475
|
+
function parseBacktickWrappedKey(key) {
|
|
476
|
+
return key[0] === '`' && key[key.length - 1] === '`' ? key.slice(1, -1) : key;
|
|
477
|
+
}
|
|
478
|
+
function parseKeyComponents(key) {
|
|
479
|
+
const unwrappedKey = parseBacktickWrappedKey(key);
|
|
480
|
+
const keys = [];
|
|
481
|
+
const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
|
|
482
|
+
if (parseResult) {
|
|
483
|
+
var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
|
|
484
|
+
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);
|
|
485
|
+
keys.push(extractedKey);
|
|
486
|
+
const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
|
|
487
|
+
if (rawIndexes) {
|
|
488
|
+
for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
|
|
489
|
+
keys.push(parseInt(indexResult[1]));
|
|
485
490
|
}
|
|
486
|
-
} else {
|
|
487
|
-
keys.push(unwrappedKey);
|
|
488
491
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
+
} else {
|
|
493
|
+
keys.push(unwrappedKey);
|
|
494
|
+
}
|
|
495
|
+
return keys;
|
|
496
|
+
}
|
|
497
|
+
const parseKeyRegex = /(`[^[\]]+`(\[\d+\])*|[^`.]+)/g;
|
|
498
|
+
function parseKey(key) {
|
|
499
|
+
const keys = key.match(parseKeyRegex);
|
|
500
|
+
return !keys ? [] : keys.flatMap(parseKeyComponents);
|
|
501
|
+
}
|
|
492
502
|
const complexKeyExpression = /{([^{}]+)}/;
|
|
493
503
|
function extractComplexKeys(ctx, key) {
|
|
494
504
|
// Resolve complex keys
|
|
@@ -545,6 +555,7 @@ let DataType = /*#__PURE__*/function (DataType) {
|
|
|
545
555
|
// Equivalent to /^.+\.\((Number|String)\)$/
|
|
546
556
|
const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
|
|
547
557
|
const isComplexKey = key => key.indexOf('{') > -1;
|
|
558
|
+
const castingRegex = /\.\(.+\)$/;
|
|
548
559
|
|
|
549
560
|
/**
|
|
550
561
|
* Reference operand resolved within the context
|
|
@@ -567,9 +578,7 @@ class Reference extends Operand {
|
|
|
567
578
|
const dataTypeMatch = dataTypeRegex.exec(this.key);
|
|
568
579
|
if (dataTypeMatch) {
|
|
569
580
|
this.dataType = DataType[dataTypeMatch[1]];
|
|
570
|
-
|
|
571
|
-
if (this.key.match(/.\(.+\)$/)) {
|
|
572
|
-
this.key = this.key.replace(/.\(.+\)$/, '');
|
|
581
|
+
this.key = this.key.replace(castingRegex, '');
|
|
573
582
|
}
|
|
574
583
|
if (isComplexKey(this.key)) {
|
|
575
584
|
this.valueLookup = context => complexValueLookup(context, this.key);
|
|
@@ -1613,6 +1622,14 @@ class Xor extends Logical {
|
|
|
1613
1622
|
* Collection operand resolved containing mixture of value and references.
|
|
1614
1623
|
*/
|
|
1615
1624
|
class Collection extends Operand {
|
|
1625
|
+
/**
|
|
1626
|
+
* Get the items in the collection.
|
|
1627
|
+
* @returns {Array<Value | Reference>}
|
|
1628
|
+
*/
|
|
1629
|
+
getItems() {
|
|
1630
|
+
return this.items;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1616
1633
|
/**
|
|
1617
1634
|
* @constructor
|
|
1618
1635
|
* @param {Operand[]} items Collection of operands.
|
|
@@ -1676,7 +1693,7 @@ class Collection extends Operand {
|
|
|
1676
1693
|
* @return {boolean}
|
|
1677
1694
|
*/
|
|
1678
1695
|
function defaultReferencePredicate(key) {
|
|
1679
|
-
return
|
|
1696
|
+
return key[0] === '$';
|
|
1680
1697
|
}
|
|
1681
1698
|
|
|
1682
1699
|
/**
|
|
@@ -1734,6 +1751,7 @@ class Parser {
|
|
|
1734
1751
|
_defineProperty(this, "opts", void 0);
|
|
1735
1752
|
_defineProperty(this, "expectedRootOperators", void 0);
|
|
1736
1753
|
_defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
|
|
1754
|
+
_defineProperty(this, "referenceCache", new Map());
|
|
1737
1755
|
this.opts = {
|
|
1738
1756
|
...defaultOptions
|
|
1739
1757
|
};
|
|
@@ -1755,6 +1773,18 @@ class Parser {
|
|
|
1755
1773
|
get options() {
|
|
1756
1774
|
return this.opts;
|
|
1757
1775
|
}
|
|
1776
|
+
getReference(key) {
|
|
1777
|
+
const cached = this.referenceCache.get(key);
|
|
1778
|
+
if (cached !== undefined) {
|
|
1779
|
+
return cached;
|
|
1780
|
+
}
|
|
1781
|
+
const reference = new Reference(this.opts.referenceTransform(key));
|
|
1782
|
+
this.referenceCache.set(key, reference);
|
|
1783
|
+
return reference;
|
|
1784
|
+
}
|
|
1785
|
+
resolve(raw) {
|
|
1786
|
+
return isString(raw) && this.opts.referencePredicate(raw) ? this.getReference(raw) : new Value(raw);
|
|
1787
|
+
}
|
|
1758
1788
|
|
|
1759
1789
|
/**
|
|
1760
1790
|
* Parse raw expression into evaluable expression.
|
|
@@ -1913,14 +1943,527 @@ class Parser {
|
|
|
1913
1943
|
* @param raw Raw data
|
|
1914
1944
|
*/
|
|
1915
1945
|
getOperand(raw) {
|
|
1916
|
-
const resolve = raw => this.opts.referencePredicate(raw) ? new Reference(this.opts.referenceTransform(raw)) : new Value(raw);
|
|
1917
1946
|
if (Array.isArray(raw)) {
|
|
1918
|
-
|
|
1947
|
+
const collectionItems = [];
|
|
1948
|
+
for (const item of raw) {
|
|
1949
|
+
collectionItems.push(this.resolve(item));
|
|
1950
|
+
}
|
|
1951
|
+
return new Collection(collectionItems);
|
|
1919
1952
|
}
|
|
1920
|
-
return resolve(raw);
|
|
1953
|
+
return this.resolve(raw);
|
|
1921
1954
|
}
|
|
1922
1955
|
}
|
|
1923
1956
|
|
|
1957
|
+
const isTrueResult = value => value === true;
|
|
1958
|
+
const isFalseResult = value => value === false;
|
|
1959
|
+
const isNonFalseResult = operand => operand !== false && !isEvaluable(operand);
|
|
1960
|
+
const isNonTrueResult = operand => operand !== true && !isEvaluable(operand);
|
|
1961
|
+
const resultToInputInternal = value => {
|
|
1962
|
+
if (isUndefined(value)) {
|
|
1963
|
+
return undefined;
|
|
1964
|
+
}
|
|
1965
|
+
if (Array.isArray(value)) {
|
|
1966
|
+
return undefined;
|
|
1967
|
+
}
|
|
1968
|
+
return value;
|
|
1969
|
+
};
|
|
1970
|
+
const resultToInput = value => {
|
|
1971
|
+
if (isUndefined(value)) {
|
|
1972
|
+
return undefined;
|
|
1973
|
+
}
|
|
1974
|
+
if (Array.isArray(value)) {
|
|
1975
|
+
const definedValues = value.map(resultToInputInternal).filter(val => !isUndefined(val));
|
|
1976
|
+
if (definedValues.length === 0) {
|
|
1977
|
+
return undefined;
|
|
1978
|
+
}
|
|
1979
|
+
return definedValues;
|
|
1980
|
+
}
|
|
1981
|
+
return value;
|
|
1982
|
+
};
|
|
1983
|
+
const areAllNumbers = results => {
|
|
1984
|
+
return results.every(isNumber);
|
|
1985
|
+
};
|
|
1986
|
+
const areAllInputs = values => values.every(value => !isEvaluable(value));
|
|
1987
|
+
const getInputValues = results => {
|
|
1988
|
+
const presentValues = results.filter(result => !isNull(result) && !isUndefined(result));
|
|
1989
|
+
// If we have missing context values the result or we still have refences
|
|
1990
|
+
// simplify to false.
|
|
1991
|
+
if (presentValues.length !== results.length || !areAllNumbers(presentValues)) {
|
|
1992
|
+
return false;
|
|
1993
|
+
}
|
|
1994
|
+
return presentValues;
|
|
1995
|
+
};
|
|
1996
|
+
const extractValues = input => {
|
|
1997
|
+
const evaluable = isEvaluable(input);
|
|
1998
|
+
if (!evaluable) {
|
|
1999
|
+
return Array.isArray(input) ? input : [input];
|
|
2000
|
+
}
|
|
2001
|
+
if (input instanceof Collection) {
|
|
2002
|
+
const results = input.getItems();
|
|
2003
|
+
const values = results.map(item => item instanceof Value ? item.evaluate() : null).filter(value => !isNull(value));
|
|
2004
|
+
return values;
|
|
2005
|
+
}
|
|
2006
|
+
return [];
|
|
2007
|
+
};
|
|
2008
|
+
|
|
2009
|
+
const simplifyAnd = simplifyInput => input => {
|
|
2010
|
+
const [operator, ...operands] = input;
|
|
2011
|
+
const simplifiedOperands = [];
|
|
2012
|
+
for (const operand of operands) {
|
|
2013
|
+
const simplification = simplifyInput(operand);
|
|
2014
|
+
if (isUndefined(simplification) || isBoolean(simplification) && !isTrueResult(simplification)) {
|
|
2015
|
+
// Short-circuit for AND
|
|
2016
|
+
return false;
|
|
2017
|
+
}
|
|
2018
|
+
simplifiedOperands.push(simplification);
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// Remove false operands leaving only the Inputs
|
|
2022
|
+
const simplified = simplifiedOperands.filter(isNonTrueResult);
|
|
2023
|
+
if (simplified.length === 0) {
|
|
2024
|
+
return true;
|
|
2025
|
+
}
|
|
2026
|
+
if (simplified.length === 1) {
|
|
2027
|
+
return simplified[0];
|
|
2028
|
+
}
|
|
2029
|
+
return [operator, ...simplified];
|
|
2030
|
+
};
|
|
2031
|
+
|
|
2032
|
+
const simplify$3 = (simplifyInput, predicate) => input => {
|
|
2033
|
+
const [, ...operands] = input;
|
|
2034
|
+
const results = operands.map(simplifyInput);
|
|
2035
|
+
if (areAllInputs(results)) {
|
|
2036
|
+
const presentValues = getInputValues(results);
|
|
2037
|
+
if (isFalseResult(presentValues)) {
|
|
2038
|
+
return false;
|
|
2039
|
+
}
|
|
2040
|
+
return presentValues.reduce(predicate);
|
|
2041
|
+
}
|
|
2042
|
+
return input;
|
|
2043
|
+
};
|
|
2044
|
+
const simplifySum = simplifyInput => input => simplify$3(simplifyInput, operateWithExpectedDecimals('sum'))(input);
|
|
2045
|
+
const simplifySubtract = simplifyInput => input => simplify$3(simplifyInput, operateWithExpectedDecimals('subtract'))(input);
|
|
2046
|
+
const simplifyMultiply = simplifyInput => input => simplify$3(simplifyInput, operateWithExpectedDecimals('multiply'))(input);
|
|
2047
|
+
const simplifyDivide = simplifyInput => input => simplify$3(simplifyInput, (acc, result) => acc / result)(input);
|
|
2048
|
+
|
|
2049
|
+
const simplifyComparison = predicate => (opts, simplifyInput) => input => {
|
|
2050
|
+
const [operator, ...operands] = input;
|
|
2051
|
+
const [left, right] = operands;
|
|
2052
|
+
const leftSimplified = simplifyInput(left);
|
|
2053
|
+
const rightSimplified = simplifyInput(right);
|
|
2054
|
+
const isLeftEvaluable = isEvaluable(leftSimplified);
|
|
2055
|
+
const isRightEvaluable = isEvaluable(rightSimplified);
|
|
2056
|
+
if (isLeftEvaluable || isRightEvaluable) {
|
|
2057
|
+
// If either left or right is an array, we cannot simplify further
|
|
2058
|
+
return [operator, isLeftEvaluable ? leftSimplified.serialize(opts) : left, isRightEvaluable ? rightSimplified.serialize(opts) : right];
|
|
2059
|
+
}
|
|
2060
|
+
if (isNumber(leftSimplified) && isNumber(rightSimplified)) {
|
|
2061
|
+
return predicate(leftSimplified, rightSimplified);
|
|
2062
|
+
}
|
|
2063
|
+
const leftDate = toDateNumber(leftSimplified);
|
|
2064
|
+
const rightDate = toDateNumber(rightSimplified);
|
|
2065
|
+
if (leftDate && rightDate) {
|
|
2066
|
+
return predicate(leftDate, rightDate);
|
|
2067
|
+
}
|
|
2068
|
+
if (Array.isArray(leftSimplified) || Array.isArray(rightSimplified)) {
|
|
2069
|
+
return [operator, leftSimplified, rightSimplified];
|
|
2070
|
+
}
|
|
2071
|
+
return false;
|
|
2072
|
+
};
|
|
2073
|
+
const simplifyGt = (opts, simplifyInput) => input => simplifyComparison((left, right) => left > right)(opts, simplifyInput)(input);
|
|
2074
|
+
const simplifyGe = (opts, simplifyInput) => input => simplifyComparison((left, right) => left >= right)(opts, simplifyInput)(input);
|
|
2075
|
+
const simplifyLt = (opts, simplifyInput) => input => simplifyComparison((left, right) => left < right)(opts, simplifyInput)(input);
|
|
2076
|
+
const simplifyLe = (opts, simplifyInput) => input => simplifyComparison((left, right) => left <= right)(opts, simplifyInput)(input);
|
|
2077
|
+
|
|
2078
|
+
const simplifyEquality = predicate => (opts, simplifyInput) => input => {
|
|
2079
|
+
const [operator, ...operands] = input;
|
|
2080
|
+
const [left, right] = operands;
|
|
2081
|
+
const leftSimplified = simplifyInput(left);
|
|
2082
|
+
const rightSimplified = simplifyInput(right);
|
|
2083
|
+
const isLeftEvaluable = isEvaluable(leftSimplified);
|
|
2084
|
+
const isRightEvaluable = isEvaluable(rightSimplified);
|
|
2085
|
+
if (isLeftEvaluable || isRightEvaluable) {
|
|
2086
|
+
// If either left or right is an array, we cannot simplify further
|
|
2087
|
+
return [operator, isLeftEvaluable ? leftSimplified.serialize(opts) : left, isRightEvaluable ? rightSimplified.serialize(opts) : right];
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// See Equal.comparison
|
|
2091
|
+
return predicate(leftSimplified, rightSimplified);
|
|
2092
|
+
};
|
|
2093
|
+
const simplifyEq = (opts, simplifyInput) => input => simplifyEquality((left, right) => left === right)(opts, simplifyInput)(input);
|
|
2094
|
+
const simplifyNe = (opts, simplifyInput) => input => simplifyEquality((left, right) => left !== right)(opts, simplifyInput)(input);
|
|
2095
|
+
|
|
2096
|
+
const simplify$2 = (originalInput, nonArrayInput, arrayInput) => {
|
|
2097
|
+
// If we don't have the non-array operand, there is nothing to do.
|
|
2098
|
+
if (isEvaluable(nonArrayInput)) {
|
|
2099
|
+
return originalInput;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// If we don't have all the values, try with what we have for the
|
|
2103
|
+
// positive case, but otherwise return the original input.
|
|
2104
|
+
if (isEvaluable(arrayInput)) {
|
|
2105
|
+
const leftValues = extractValues(arrayInput);
|
|
2106
|
+
const isFound = leftValues.indexOf(nonArrayInput) > -1;
|
|
2107
|
+
return isFound ? true : originalInput;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// If we have all the values, we can check if the non-array operand is
|
|
2111
|
+
// included in the array operand.
|
|
2112
|
+
return arrayInput.indexOf(nonArrayInput) > -1;
|
|
2113
|
+
};
|
|
2114
|
+
const simplifyIn = simplifyInput => input => {
|
|
2115
|
+
const [, ...operands] = input;
|
|
2116
|
+
const [left, right] = operands;
|
|
2117
|
+
if (isNull(left) || isUndefined(left) || isNull(right) || isUndefined(right)) {
|
|
2118
|
+
return false;
|
|
2119
|
+
}
|
|
2120
|
+
const leftSimplified = simplifyInput(left);
|
|
2121
|
+
const rightSimplified = simplifyInput(right);
|
|
2122
|
+
const isLeftArray = Array.isArray(leftSimplified) || isEvaluable(leftSimplified);
|
|
2123
|
+
const isRightArray = Array.isArray(rightSimplified) || isEvaluable(rightSimplified);
|
|
2124
|
+
if (isLeftArray) {
|
|
2125
|
+
return simplify$2(input, rightSimplified, leftSimplified);
|
|
2126
|
+
}
|
|
2127
|
+
if (isRightArray) {
|
|
2128
|
+
return simplify$2(input, leftSimplified, rightSimplified);
|
|
2129
|
+
}
|
|
2130
|
+
return false;
|
|
2131
|
+
};
|
|
2132
|
+
|
|
2133
|
+
const simplifyNor = (opts, simplifyInput) => input => {
|
|
2134
|
+
const [operator, ...operands] = input;
|
|
2135
|
+
const simplifiedOperands = [];
|
|
2136
|
+
for (const operand of operands) {
|
|
2137
|
+
const simplification = simplifyInput(operand);
|
|
2138
|
+
if (isUndefined(simplification) || isBoolean(simplification) && isTrueResult(simplification)) {
|
|
2139
|
+
// Short-circuit for AND
|
|
2140
|
+
return false;
|
|
2141
|
+
}
|
|
2142
|
+
simplifiedOperands.push(simplification);
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// Remove true operands leaving only the Inputs
|
|
2146
|
+
const simplified = simplifiedOperands.filter(isNonFalseResult);
|
|
2147
|
+
if (simplified.length === 0) {
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2150
|
+
if (simplified.length === 1) {
|
|
2151
|
+
var _opts$operatorMapping;
|
|
2152
|
+
return [(_opts$operatorMapping = opts.operatorMapping.get(OPERATOR$3)) !== null && _opts$operatorMapping !== void 0 ? _opts$operatorMapping : 'NOT', simplified[0]];
|
|
2153
|
+
}
|
|
2154
|
+
return [operator, ...simplified];
|
|
2155
|
+
};
|
|
2156
|
+
|
|
2157
|
+
const simplifyNot = simplifyInput => input => {
|
|
2158
|
+
const [, ...operands] = input;
|
|
2159
|
+
const simplification = simplifyInput(operands[0]);
|
|
2160
|
+
if (isBoolean(simplification)) {
|
|
2161
|
+
return !simplification;
|
|
2162
|
+
}
|
|
2163
|
+
return input;
|
|
2164
|
+
};
|
|
2165
|
+
|
|
2166
|
+
const simplifyNotIn = simplifyInput => input => {
|
|
2167
|
+
const [, ...operands] = input;
|
|
2168
|
+
const [left, right] = operands;
|
|
2169
|
+
const leftArray = Array.isArray(left);
|
|
2170
|
+
const rightArray = Array.isArray(right);
|
|
2171
|
+
if (isNull(left) || isUndefined(left) || isNull(right) || isUndefined(right) || leftArray && rightArray || !leftArray && !rightArray) {
|
|
2172
|
+
return true;
|
|
2173
|
+
}
|
|
2174
|
+
if (leftArray) {
|
|
2175
|
+
// If any operand is still an Evaluable, we cannot simplify further
|
|
2176
|
+
const rightSimplified = simplifyInput(right);
|
|
2177
|
+
if (isEvaluable(rightSimplified)) {
|
|
2178
|
+
return input;
|
|
2179
|
+
}
|
|
2180
|
+
const leftSimplified = left.map(simplifyInput);
|
|
2181
|
+
if (leftSimplified.some(isEvaluable)) {
|
|
2182
|
+
return input;
|
|
2183
|
+
}
|
|
2184
|
+
return leftSimplified.indexOf(rightSimplified) === -1;
|
|
2185
|
+
}
|
|
2186
|
+
if (rightArray) {
|
|
2187
|
+
const leftSimplified = simplifyInput(left);
|
|
2188
|
+
if (isEvaluable(leftSimplified)) {
|
|
2189
|
+
return input;
|
|
2190
|
+
}
|
|
2191
|
+
const rightSimplified = right.map(simplifyInput);
|
|
2192
|
+
if (rightSimplified.some(isEvaluable)) {
|
|
2193
|
+
return input;
|
|
2194
|
+
}
|
|
2195
|
+
return rightSimplified.indexOf(leftSimplified) === -1;
|
|
2196
|
+
}
|
|
2197
|
+
return input;
|
|
2198
|
+
};
|
|
2199
|
+
|
|
2200
|
+
const simplifyOr = simplifyInput => input => {
|
|
2201
|
+
const [operator, ...operands] = input;
|
|
2202
|
+
const simplifiedOperands = [];
|
|
2203
|
+
for (const operand of operands) {
|
|
2204
|
+
const simplification = simplifyInput(operand);
|
|
2205
|
+
if (isBoolean(simplification) && isTrueResult(simplification)) {
|
|
2206
|
+
// Short-circuit for OR
|
|
2207
|
+
return true;
|
|
2208
|
+
} else if (simplification) {
|
|
2209
|
+
simplifiedOperands.push(simplification);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
const simplified = simplifiedOperands.filter(isNonFalseResult);
|
|
2213
|
+
if (simplified.length === 0) {
|
|
2214
|
+
return false;
|
|
2215
|
+
}
|
|
2216
|
+
if (simplified.length === 1) {
|
|
2217
|
+
return simplified[0];
|
|
2218
|
+
}
|
|
2219
|
+
return [operator, ...simplified];
|
|
2220
|
+
};
|
|
2221
|
+
|
|
2222
|
+
const simplifyOverlap = simplifyInput => input => {
|
|
2223
|
+
const [, ...operands] = input;
|
|
2224
|
+
const [left, right] = operands;
|
|
2225
|
+
const leftSimplified = simplifyInput(left);
|
|
2226
|
+
const rightSimplified = simplifyInput(right);
|
|
2227
|
+
const isLeftEvaluable = isEvaluable(leftSimplified);
|
|
2228
|
+
const isRightEvaluable = isEvaluable(rightSimplified);
|
|
2229
|
+
|
|
2230
|
+
// If we don't have all operands simplified, we can try the positive case
|
|
2231
|
+
// of the values we have satisfying the OVERLAP. But if not, we need to
|
|
2232
|
+
// return the original input.
|
|
2233
|
+
if (isLeftEvaluable || isRightEvaluable) {
|
|
2234
|
+
const leftValues = extractValues(leftSimplified);
|
|
2235
|
+
const rightValues = extractValues(rightSimplified);
|
|
2236
|
+
const rightSet = new Set(rightValues);
|
|
2237
|
+
const res = leftValues.some(element => rightSet.has(element));
|
|
2238
|
+
if (res) {
|
|
2239
|
+
return true;
|
|
2240
|
+
}
|
|
2241
|
+
return input;
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// If simplified results are not arrays, it means we had a strictKey
|
|
2245
|
+
// without values provided. Simplify to false.
|
|
2246
|
+
if (!Array.isArray(leftSimplified) || !Array.isArray(rightSimplified)) {
|
|
2247
|
+
return false;
|
|
2248
|
+
}
|
|
2249
|
+
const rightSet = new Set(rightSimplified);
|
|
2250
|
+
const res = leftSimplified.some(element => rightSet.has(element));
|
|
2251
|
+
if (res) {
|
|
2252
|
+
return true;
|
|
2253
|
+
}
|
|
2254
|
+
return false;
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
const simplify$1 = (opts, simplifyInput, startsWith = true) => input => {
|
|
2258
|
+
const [operator, ...operands] = input;
|
|
2259
|
+
const [left, right] = operands;
|
|
2260
|
+
const leftSimplified = simplifyInput(left);
|
|
2261
|
+
const rightSimplified = simplifyInput(right);
|
|
2262
|
+
const isLeftEvaluable = isEvaluable(leftSimplified);
|
|
2263
|
+
const isRightEvaluable = isEvaluable(rightSimplified);
|
|
2264
|
+
if (isLeftEvaluable || isRightEvaluable) {
|
|
2265
|
+
// If either left or right is an array, we cannot simplify further
|
|
2266
|
+
return [operator, isLeftEvaluable ? leftSimplified.serialize(opts) : left, isRightEvaluable ? rightSimplified.serialize(opts) : right];
|
|
2267
|
+
}
|
|
2268
|
+
if (isString(leftSimplified) && isString(rightSimplified)) {
|
|
2269
|
+
return startsWith ? rightSimplified.startsWith(leftSimplified) : leftSimplified.endsWith(rightSimplified);
|
|
2270
|
+
}
|
|
2271
|
+
return false;
|
|
2272
|
+
};
|
|
2273
|
+
const simplifyPrefix = (opts, simplifyInput) => input => simplify$1(opts, simplifyInput)(input);
|
|
2274
|
+
const simplifySuffix = (opts, simplifyInput) => input => simplify$1(opts, simplifyInput, false)(input);
|
|
2275
|
+
|
|
2276
|
+
const simplify = (simplifyInput, predicate, present = true) => input => {
|
|
2277
|
+
const [, ...operands] = input;
|
|
2278
|
+
const [operand] = operands;
|
|
2279
|
+
const simplified = simplifyInput(operand);
|
|
2280
|
+
const isOperandEvaluable = isEvaluable(simplified);
|
|
2281
|
+
if (isOperandEvaluable) {
|
|
2282
|
+
return input;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// Operand simplifies to itself when it is included in strictKeys or
|
|
2286
|
+
// optionalKeys, thus it was undefined.
|
|
2287
|
+
if (operand === simplified) {
|
|
2288
|
+
return !present;
|
|
2289
|
+
}
|
|
2290
|
+
return predicate(simplified);
|
|
2291
|
+
};
|
|
2292
|
+
const simplifyPresent = simplifyInput => input => simplify(simplifyInput, input => !isUndefined(input) && !isNull(input))(input);
|
|
2293
|
+
const simplifyUndefined = simplifyInput => input => simplify(simplifyInput, isUndefined, false)(input);
|
|
2294
|
+
|
|
2295
|
+
const simplifyXor = (opts, simplifyInput) => input => {
|
|
2296
|
+
const [operator, ...operands] = input;
|
|
2297
|
+
let trueCount = 0;
|
|
2298
|
+
const simplifiedOperands = [];
|
|
2299
|
+
for (const operand of operands) {
|
|
2300
|
+
const simplification = simplifyInput(operand);
|
|
2301
|
+
if (isBoolean(simplification) && isTrueResult(simplification)) {
|
|
2302
|
+
trueCount++;
|
|
2303
|
+
continue;
|
|
2304
|
+
}
|
|
2305
|
+
if (isBoolean(simplification) && isFalseResult(simplification)) {
|
|
2306
|
+
continue;
|
|
2307
|
+
}
|
|
2308
|
+
if (!isEvaluable(simplification)) {
|
|
2309
|
+
simplifiedOperands.push(simplification);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
if (trueCount > 1) {
|
|
2313
|
+
return false;
|
|
2314
|
+
}
|
|
2315
|
+
if (simplifiedOperands.length === 0) {
|
|
2316
|
+
return trueCount === 1;
|
|
2317
|
+
}
|
|
2318
|
+
if (simplifiedOperands.length === 1) {
|
|
2319
|
+
if (trueCount === 1) {
|
|
2320
|
+
var _opts$operatorMapping;
|
|
2321
|
+
return [(_opts$operatorMapping = opts.operatorMapping.get(OPERATOR$3)) !== null && _opts$operatorMapping !== void 0 ? _opts$operatorMapping : 'NOT', simplifiedOperands[0]];
|
|
2322
|
+
}
|
|
2323
|
+
return simplifiedOperands[0];
|
|
2324
|
+
}
|
|
2325
|
+
if (trueCount === 1) {
|
|
2326
|
+
var _opts$operatorMapping2;
|
|
2327
|
+
return [(_opts$operatorMapping2 = opts.operatorMapping.get(OPERATOR$2)) !== null && _opts$operatorMapping2 !== void 0 ? _opts$operatorMapping2 : 'NOR', ...simplifiedOperands];
|
|
2328
|
+
}
|
|
2329
|
+
return [operator, ...simplifiedOperands];
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2332
|
+
const unsafeSimplify = (context, opts, strictKeys, optionalKeys) => {
|
|
2333
|
+
const simplifyInput = input => {
|
|
2334
|
+
// Value or Reference
|
|
2335
|
+
if (!Array.isArray(input)) {
|
|
2336
|
+
// Reference
|
|
2337
|
+
if (isString(input) && opts.referencePredicate(input)) {
|
|
2338
|
+
const result = new Reference(opts.referenceTransform(input)).simplify(context, strictKeys, optionalKeys);
|
|
2339
|
+
if (isEvaluable(result)) {
|
|
2340
|
+
return result;
|
|
2341
|
+
}
|
|
2342
|
+
const inputResult = resultToInput(result);
|
|
2343
|
+
if (!isUndefined(inputResult)) {
|
|
2344
|
+
return inputResult;
|
|
2345
|
+
}
|
|
2346
|
+
// It should have been a boolean or an Evaluable, but just in case
|
|
2347
|
+
// fallback to returning the input.
|
|
2348
|
+
}
|
|
2349
|
+
// Value
|
|
2350
|
+
return input;
|
|
2351
|
+
}
|
|
2352
|
+
const [operator] = input;
|
|
2353
|
+
switch (operator) {
|
|
2354
|
+
// Logical operators
|
|
2355
|
+
case opts.operatorMapping.get(OPERATOR$4):
|
|
2356
|
+
{
|
|
2357
|
+
return simplifyAnd(simplifyInput)(input);
|
|
2358
|
+
}
|
|
2359
|
+
case opts.operatorMapping.get(OPERATOR$1):
|
|
2360
|
+
{
|
|
2361
|
+
return simplifyOr(simplifyInput)(input);
|
|
2362
|
+
}
|
|
2363
|
+
case opts.operatorMapping.get(OPERATOR$2):
|
|
2364
|
+
{
|
|
2365
|
+
return simplifyNor(opts, simplifyInput)(input);
|
|
2366
|
+
}
|
|
2367
|
+
case opts.operatorMapping.get(OPERATOR):
|
|
2368
|
+
{
|
|
2369
|
+
return simplifyXor(opts, simplifyInput)(input);
|
|
2370
|
+
}
|
|
2371
|
+
case opts.operatorMapping.get(OPERATOR$3):
|
|
2372
|
+
{
|
|
2373
|
+
return simplifyNot(simplifyInput)(input);
|
|
2374
|
+
}
|
|
2375
|
+
// Comparison operators
|
|
2376
|
+
case opts.operatorMapping.get(OPERATOR$h):
|
|
2377
|
+
{
|
|
2378
|
+
return simplifyEq(opts, simplifyInput)(input);
|
|
2379
|
+
}
|
|
2380
|
+
case opts.operatorMapping.get(OPERATOR$b):
|
|
2381
|
+
{
|
|
2382
|
+
return simplifyNe(opts, simplifyInput)(input);
|
|
2383
|
+
}
|
|
2384
|
+
case opts.operatorMapping.get(OPERATOR$f):
|
|
2385
|
+
{
|
|
2386
|
+
return simplifyGt(opts, simplifyInput)(input);
|
|
2387
|
+
}
|
|
2388
|
+
case opts.operatorMapping.get(OPERATOR$g):
|
|
2389
|
+
{
|
|
2390
|
+
return simplifyGe(opts, simplifyInput)(input);
|
|
2391
|
+
}
|
|
2392
|
+
case opts.operatorMapping.get(OPERATOR$c):
|
|
2393
|
+
{
|
|
2394
|
+
return simplifyLt(opts, simplifyInput)(input);
|
|
2395
|
+
}
|
|
2396
|
+
case opts.operatorMapping.get(OPERATOR$d):
|
|
2397
|
+
{
|
|
2398
|
+
return simplifyLe(opts, simplifyInput)(input);
|
|
2399
|
+
}
|
|
2400
|
+
case opts.operatorMapping.get(OPERATOR$e):
|
|
2401
|
+
{
|
|
2402
|
+
return simplifyIn(simplifyInput)(input);
|
|
2403
|
+
}
|
|
2404
|
+
case opts.operatorMapping.get(OPERATOR$a):
|
|
2405
|
+
{
|
|
2406
|
+
return simplifyNotIn(simplifyInput)(input);
|
|
2407
|
+
}
|
|
2408
|
+
case opts.operatorMapping.get(OPERATOR$8):
|
|
2409
|
+
{
|
|
2410
|
+
return simplifyPrefix(opts, simplifyInput)(input);
|
|
2411
|
+
}
|
|
2412
|
+
case opts.operatorMapping.get(OPERATOR$6):
|
|
2413
|
+
{
|
|
2414
|
+
return simplifySuffix(opts, simplifyInput)(input);
|
|
2415
|
+
}
|
|
2416
|
+
case opts.operatorMapping.get(OPERATOR$9):
|
|
2417
|
+
{
|
|
2418
|
+
return simplifyOverlap(simplifyInput)(input);
|
|
2419
|
+
}
|
|
2420
|
+
case opts.operatorMapping.get(OPERATOR$5):
|
|
2421
|
+
{
|
|
2422
|
+
return simplifyUndefined(simplifyInput)(input);
|
|
2423
|
+
}
|
|
2424
|
+
case opts.operatorMapping.get(OPERATOR$7):
|
|
2425
|
+
{
|
|
2426
|
+
return simplifyPresent(simplifyInput)(input);
|
|
2427
|
+
}
|
|
2428
|
+
// Arithmetic operators
|
|
2429
|
+
case opts.operatorMapping.get(OPERATOR$i):
|
|
2430
|
+
{
|
|
2431
|
+
return simplifySum(simplifyInput)(input);
|
|
2432
|
+
}
|
|
2433
|
+
case opts.operatorMapping.get(OPERATOR$j):
|
|
2434
|
+
{
|
|
2435
|
+
return simplifySubtract(simplifyInput)(input);
|
|
2436
|
+
}
|
|
2437
|
+
case opts.operatorMapping.get(OPERATOR$k):
|
|
2438
|
+
{
|
|
2439
|
+
return simplifyMultiply(simplifyInput)(input);
|
|
2440
|
+
}
|
|
2441
|
+
case opts.operatorMapping.get(OPERATOR$l):
|
|
2442
|
+
{
|
|
2443
|
+
return simplifyDivide(simplifyInput)(input);
|
|
2444
|
+
}
|
|
2445
|
+
default:
|
|
2446
|
+
{
|
|
2447
|
+
// Handle as an array of References / Values if no operator matches
|
|
2448
|
+
const result = input.map(simplifyInput);
|
|
2449
|
+
if (!areAllInputs(result)) {
|
|
2450
|
+
return new Collection(result.map(item => {
|
|
2451
|
+
if (item instanceof Reference) {
|
|
2452
|
+
return item;
|
|
2453
|
+
}
|
|
2454
|
+
if (!isEvaluable(item)) {
|
|
2455
|
+
return new Value(item);
|
|
2456
|
+
}
|
|
2457
|
+
throw new Error('Unexpected expression found within a collection of values/references');
|
|
2458
|
+
}));
|
|
2459
|
+
}
|
|
2460
|
+
return result;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
};
|
|
2464
|
+
return simplifyInput;
|
|
2465
|
+
};
|
|
2466
|
+
|
|
1924
2467
|
const unexpectedResultError = 'non expression or boolean result should be returned';
|
|
1925
2468
|
|
|
1926
2469
|
/**
|
|
@@ -1993,6 +2536,18 @@ class Engine {
|
|
|
1993
2536
|
}
|
|
1994
2537
|
throw new Error(unexpectedResultError);
|
|
1995
2538
|
}
|
|
2539
|
+
unsafeSimplify(exp, context, strictKeys, optionalKeys) {
|
|
2540
|
+
const result = unsafeSimplify(context, this.parser.options, strictKeys, optionalKeys)(exp);
|
|
2541
|
+
if (isBoolean(result)) {
|
|
2542
|
+
return result;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
// The unsafe implementation should not expose the Evaluable interface
|
|
2546
|
+
if (isEvaluable(result)) {
|
|
2547
|
+
throw new Error('Unexpected Evaluable not serialized result');
|
|
2548
|
+
}
|
|
2549
|
+
return result;
|
|
2550
|
+
}
|
|
1996
2551
|
}
|
|
1997
2552
|
|
|
1998
2553
|
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 };
|