@briza/illogical 2.0.0 → 2.0.2
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/lib/illogical.cjs +6 -8
- package/lib/illogical.esm.js +6 -8
- package/package.json +21 -14
- package/readme.md +54 -848
- /package/{lib/types → types}/common/evaluable.d.ts +0 -0
- /package/{lib/types → types}/common/type-check.d.ts +0 -0
- /package/{lib/types → types}/common/util.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/divide.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/index.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/isSimplifiedArithmeticExpression.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/multiply.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/operateWithExpectedDecimals.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/subtract.d.ts +0 -0
- /package/{lib/types → types}/expression/arithmetic/sum.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/eq.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/ge.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/gt.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/in.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/index.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/le.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/lt.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/ne.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/not-in.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/overlap.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/prefix.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/present.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/suffix.d.ts +0 -0
- /package/{lib/types → types}/expression/comparison/undefined.d.ts +0 -0
- /package/{lib/types → types}/expression/logical/and.d.ts +0 -0
- /package/{lib/types → types}/expression/logical/index.d.ts +0 -0
- /package/{lib/types → types}/expression/logical/nor.d.ts +0 -0
- /package/{lib/types → types}/expression/logical/not.d.ts +0 -0
- /package/{lib/types → types}/expression/logical/or.d.ts +0 -0
- /package/{lib/types → types}/expression/logical/xor.d.ts +0 -0
- /package/{lib/types → types}/index.d.ts +0 -0
- /package/{lib/types → types}/operand/collection.d.ts +0 -0
- /package/{lib/types → types}/operand/index.d.ts +0 -0
- /package/{lib/types → types}/operand/reference.d.ts +0 -0
- /package/{lib/types → types}/operand/value.d.ts +0 -0
- /package/{lib/types → types}/parser/index.d.ts +0 -0
- /package/{lib/types → types}/parser/options.d.ts +0 -0
package/lib/illogical.cjs
CHANGED
|
@@ -56,7 +56,7 @@ function isObject(value) {
|
|
|
56
56
|
if (value === null || value === undefined) {
|
|
57
57
|
return false;
|
|
58
58
|
}
|
|
59
|
-
if (typeof value !== 'object' ||
|
|
59
|
+
if (typeof value !== 'object' || value?.constructor !== Object) {
|
|
60
60
|
return false;
|
|
61
61
|
}
|
|
62
62
|
return true;
|
|
@@ -364,7 +364,7 @@ class Operand {
|
|
|
364
364
|
* Get the strict representation.
|
|
365
365
|
*/
|
|
366
366
|
toString() {
|
|
367
|
-
|
|
367
|
+
return '[Operand]';
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
|
|
@@ -478,10 +478,9 @@ function parseKeyComponents(key) {
|
|
|
478
478
|
const keys = [];
|
|
479
479
|
const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
|
|
480
480
|
if (parseResult) {
|
|
481
|
-
|
|
482
|
-
const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
|
|
481
|
+
const extractedKey = parseBacktickWrappedKey(parseResult?.groups?.currentKey ?? unwrappedKey);
|
|
483
482
|
keys.push(extractedKey);
|
|
484
|
-
const rawIndexes = parseResult
|
|
483
|
+
const rawIndexes = parseResult?.groups?.indexes;
|
|
485
484
|
if (rawIndexes) {
|
|
486
485
|
for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
|
|
487
486
|
keys.push(parseInt(indexResult[1]));
|
|
@@ -542,7 +541,7 @@ function complexValueLookup(ctx, key) {
|
|
|
542
541
|
if (!keys) {
|
|
543
542
|
return undefined;
|
|
544
543
|
}
|
|
545
|
-
return simpleValueLookup(keys
|
|
544
|
+
return simpleValueLookup(keys ?? [])(ctx);
|
|
546
545
|
}
|
|
547
546
|
let DataType = /*#__PURE__*/function (DataType) {
|
|
548
547
|
DataType["Number"] = "Number";
|
|
@@ -604,8 +603,7 @@ class Reference extends Operand {
|
|
|
604
603
|
* {@link Evaluable.simplify}
|
|
605
604
|
*/
|
|
606
605
|
simplify(ctx, strictKeys, optionalKeys) {
|
|
607
|
-
|
|
608
|
-
const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
|
|
606
|
+
const [key] = this.getKeys(ctx) ?? [];
|
|
609
607
|
if (ctx[key] !== undefined) {
|
|
610
608
|
return this.evaluate(ctx);
|
|
611
609
|
}
|
package/lib/illogical.esm.js
CHANGED
|
@@ -52,7 +52,7 @@ function isObject(value) {
|
|
|
52
52
|
if (value === null || value === undefined) {
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
55
|
-
if (typeof value !== 'object' ||
|
|
55
|
+
if (typeof value !== 'object' || value?.constructor !== Object) {
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
return true;
|
|
@@ -360,7 +360,7 @@ class Operand {
|
|
|
360
360
|
* Get the strict representation.
|
|
361
361
|
*/
|
|
362
362
|
toString() {
|
|
363
|
-
|
|
363
|
+
return '[Operand]';
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
@@ -474,10 +474,9 @@ function parseKeyComponents(key) {
|
|
|
474
474
|
const keys = [];
|
|
475
475
|
const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
|
|
476
476
|
if (parseResult) {
|
|
477
|
-
|
|
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);
|
|
477
|
+
const extractedKey = parseBacktickWrappedKey(parseResult?.groups?.currentKey ?? unwrappedKey);
|
|
479
478
|
keys.push(extractedKey);
|
|
480
|
-
const rawIndexes = parseResult
|
|
479
|
+
const rawIndexes = parseResult?.groups?.indexes;
|
|
481
480
|
if (rawIndexes) {
|
|
482
481
|
for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
|
|
483
482
|
keys.push(parseInt(indexResult[1]));
|
|
@@ -538,7 +537,7 @@ function complexValueLookup(ctx, key) {
|
|
|
538
537
|
if (!keys) {
|
|
539
538
|
return undefined;
|
|
540
539
|
}
|
|
541
|
-
return simpleValueLookup(keys
|
|
540
|
+
return simpleValueLookup(keys ?? [])(ctx);
|
|
542
541
|
}
|
|
543
542
|
let DataType = /*#__PURE__*/function (DataType) {
|
|
544
543
|
DataType["Number"] = "Number";
|
|
@@ -600,8 +599,7 @@ class Reference extends Operand {
|
|
|
600
599
|
* {@link Evaluable.simplify}
|
|
601
600
|
*/
|
|
602
601
|
simplify(ctx, strictKeys, optionalKeys) {
|
|
603
|
-
|
|
604
|
-
const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
|
|
602
|
+
const [key] = this.getKeys(ctx) ?? [];
|
|
605
603
|
if (ctx[key] !== undefined) {
|
|
606
604
|
return this.evaluate(ctx);
|
|
607
605
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@briza/illogical",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A micro conditional javascript engine used to parse the raw logical and comparison expressions, evaluate the expression in the given data context, and provide access to a text form of the given expressions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/illogical.cjs",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"require": "./lib/illogical.cjs"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
|
-
"lib/"
|
|
14
|
+
"lib/",
|
|
15
|
+
"types/"
|
|
15
16
|
],
|
|
16
17
|
"author": {
|
|
17
18
|
"name": "David Horak",
|
|
@@ -20,15 +21,21 @@
|
|
|
20
21
|
"license": "MIT",
|
|
21
22
|
"scripts": {
|
|
22
23
|
"type-check": "tsc --noEmit",
|
|
23
|
-
"build:types": "tsc --emitDeclarationOnly",
|
|
24
|
+
"build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly",
|
|
24
25
|
"build:js": "rollup -c",
|
|
25
|
-
"build": "rm -rf types && npm run build:types && npm run build:js",
|
|
26
|
+
"build": "rm -rf lib types && npm run build:types && npm run build:js",
|
|
26
27
|
"docs": "typedoc src && git checkout docs/.nojekyll",
|
|
27
|
-
"test": "
|
|
28
|
+
"test": "node --import tsx --test \"src/**/*.test.ts\"",
|
|
29
|
+
"test:sample-conditions": "node --import tsx --test --test-reporter=spec \"src/__test__/unit/sample-conditions.test.ts\"",
|
|
30
|
+
"test:coverage": "node --import tsx --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage.lcov \"src/**/*.test.ts\"",
|
|
28
31
|
"lint": "eslint --max-warnings 0 \"src/**/*.{ts,js}\"",
|
|
29
32
|
"lint:fix": "eslint --max-warnings 0 \"src/**/*.{ts,js}\" --fix",
|
|
30
33
|
"prepublishOnly": "npm run test && npm run build",
|
|
31
|
-
"check-licenses": "license-checker --summary --excludePrivatePackages --onlyAllow \"MIT;MIT OR X11;Apache-2.0;ISC;BSD-3-Clause;BSD-2-Clause;CC-BY-4.0;Public Domain;BSD;CC-BY-3.0;CC0-1.0;Python-2.0;BlueOak-1.0.0;Unlicense\""
|
|
34
|
+
"check-licenses": "license-checker --summary --excludePrivatePackages --onlyAllow \"MIT;MIT OR X11;Apache-2.0;ISC;BSD-3-Clause;BSD-2-Clause;CC-BY-4.0;Public Domain;BSD;CC-BY-3.0;CC0-1.0;Python-2.0;BlueOak-1.0.0;Unlicense\"",
|
|
35
|
+
"bench:sample:oop:evaluate": "npm run build && node --import tsx src/benchmark/evaluate.ts --cases conditions/sample-conditions --out benchmark/results-sample-evaluate-oop.json",
|
|
36
|
+
"bench:sample:oop:simplify": "npm run build && node --import tsx src/benchmark/simplify.ts --cases conditions/sample-conditions --out benchmark/results-sample-simplify-oop.json",
|
|
37
|
+
"bench:synthetic:oop:evaluate": "npm run build && node --import tsx src/benchmark/evaluate.ts --cases conditions/synthetic-conditions --out benchmark/results-synthetic-evaluate-oop.json",
|
|
38
|
+
"bench:synthetic:oop:simplify": "npm run build && node --import tsx src/benchmark/simplify.ts --cases conditions/synthetic-conditions --out benchmark/results-synthetic-simplify-oop.json"
|
|
32
39
|
},
|
|
33
40
|
"repository": {
|
|
34
41
|
"type": "git",
|
|
@@ -51,11 +58,11 @@
|
|
|
51
58
|
"@babel/preset-typescript": "^7.28.5",
|
|
52
59
|
"@eslint/eslintrc": "^3.3.3",
|
|
53
60
|
"@eslint/js": "^9.39.2",
|
|
54
|
-
"@rollup/plugin-babel": "^
|
|
55
|
-
"@rollup/plugin-commonjs": "^
|
|
61
|
+
"@rollup/plugin-babel": "^7.0.0",
|
|
62
|
+
"@rollup/plugin-commonjs": "^29.0.2",
|
|
56
63
|
"@rollup/plugin-eslint": "^9.1.0",
|
|
57
|
-
"@rollup/plugin-node-resolve": "^
|
|
58
|
-
"@types/
|
|
64
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
65
|
+
"@types/node": "^25.5.0",
|
|
59
66
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
60
67
|
"@typescript-eslint/parser": "^8.54.0",
|
|
61
68
|
"eslint": "^9.39.2",
|
|
@@ -66,12 +73,12 @@
|
|
|
66
73
|
"eslint-plugin-prettier": "^5.5.5",
|
|
67
74
|
"eslint-plugin-promise": "^7.2.1",
|
|
68
75
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
69
|
-
"jest": "^29.7.0",
|
|
70
76
|
"license-checker": "^25.0.1",
|
|
71
77
|
"prettier": "^3.6.2",
|
|
72
78
|
"rollup": "^4.52.5",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
79
|
+
"tinybench": "^6.0.0",
|
|
80
|
+
"tsx": "^4.19.1",
|
|
81
|
+
"typedoc": "^0.28.18",
|
|
82
|
+
"typescript": "^6.0.2"
|
|
76
83
|
}
|
|
77
84
|
}
|
package/readme.md
CHANGED
|
@@ -1,82 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<!-- PROJECT LOGO -->
|
|
3
|
+
<br />
|
|
4
|
+
<a href="https://github.com/briza-insurance/illogical">
|
|
5
|
+
<img src="specs/header.png" alt="illogical Header">
|
|
6
|
+
</a>
|
|
7
|
+
<br />
|
|
8
|
+
<div align="center">
|
|
9
|
+
<h3 align="center">illogical</h3>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
[](https://github.com/briza-insurance/illogical/actions?branch=master)
|
|
13
|
+
[](https://badge.fury.io/js/@briza%2Fillogical)
|
|
14
|
+
[](https://packagephobia.com/result?p=@briza/illogical)
|
|
15
|
+

|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div align="center">
|
|
21
|
+
<p>
|
|
22
|
+
<br />
|
|
23
|
+
JSON DSL for expressing and evaluating business rules. Underwriters use illogical to model business rules for their question sets, enabling distributors to render great user experiences.
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
2
26
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## About
|
|
6
|
-
|
|
7
|
-
This project has been developed to provide a shared conditional logic between front-end and back-end code, stored in JSON or in any other data serialization format.
|
|
8
|
-
|
|
9
|
-
> Code documentation could be found here: https://briza-insurance.github.io/illogical/index.html.
|
|
10
|
-
|
|
11
|
-
> The library is being build as **CommonJS** module and **ESM**.
|
|
27
|
+
---
|
|
12
28
|
|
|
13
|
-
|
|
29
|
+
This project is designed such that business rules can be shared between the front-end and back-end, serialized in JSON.
|
|
14
30
|
|
|
15
|
-
|
|
16
|
-
npm install -D @briza/illogical
|
|
17
|
-
```
|
|
31
|
+
## 🚀 Get Started
|
|
18
32
|
|
|
19
33
|
```sh
|
|
20
|
-
|
|
34
|
+
# install illogical
|
|
35
|
+
npm install @briza/illogical
|
|
21
36
|
```
|
|
22
37
|
|
|
23
|
-
**Table of Content**
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
- [Basic Usage](#basic-usage)
|
|
28
|
-
- [Evaluate](#evaluate)
|
|
29
|
-
- [Statement](#statement)
|
|
30
|
-
- [Parse](#parse)
|
|
31
|
-
- [Evaluable Function](#evaluable-function)
|
|
32
|
-
- [Simplify](#simplify)
|
|
33
|
-
- [Working with Expressions](#working-with-expressions)
|
|
34
|
-
- [Evaluation Data Context](#evaluation-data-context)
|
|
35
|
-
- [Operand Types](#operand-types)
|
|
36
|
-
- [Value](#value)
|
|
37
|
-
- [Reference](#reference)
|
|
38
|
-
- [Collection](#collection)
|
|
39
|
-
- [Comparison Expressions](#comparison-expressions)
|
|
40
|
-
- [Equal](#equal)
|
|
41
|
-
- [Not Equal](#not-equal)
|
|
42
|
-
- [Greater Than](#greater-than)
|
|
43
|
-
- [Greater Than or Equal](#greater-than-or-equal)
|
|
44
|
-
- [Less Than](#less-than)
|
|
45
|
-
- [Less Than or Equal](#less-than-or-equal)
|
|
46
|
-
- [In](#in)
|
|
47
|
-
- [Not In](#not-in)
|
|
48
|
-
- [Prefix](#prefix)
|
|
49
|
-
- [Suffix](#suffix)
|
|
50
|
-
- [Overlap](#overlap)
|
|
51
|
-
- [Undefined](#undefined)
|
|
52
|
-
- [Present](#present)
|
|
53
|
-
- [Logical Expressions](#logical-expressions)
|
|
54
|
-
- [And](#and)
|
|
55
|
-
- [Or](#or)
|
|
56
|
-
- [Nor](#nor)
|
|
57
|
-
- [Xor](#xor)
|
|
58
|
-
- [Not](#not)
|
|
59
|
-
- [Arithmetic Expressions](#arithmetic-expressions)
|
|
60
|
-
- [Divide](#divide)
|
|
61
|
-
- [Multiply](#multiply)
|
|
62
|
-
- [Subtract](#subtract)
|
|
63
|
-
- [Sum](#sum)
|
|
64
|
-
- [Engine Options](#engine-options)
|
|
65
|
-
- [Parser Options](#parser-options)
|
|
66
|
-
- [Reference Predicate](#reference-predicate)
|
|
67
|
-
- [Reference Transform](#reference-transform)
|
|
68
|
-
- [Operator Mapping](#operator-mapping)
|
|
69
|
-
- [Breaking Changes](#breaking-changes)
|
|
70
|
-
- [v1.2.0](#v120)
|
|
71
|
-
- [v1.4.2](#v142)
|
|
72
|
-
- [Contributing](#contributing)
|
|
73
|
-
- [Pull Request Process](#pull-request-process)
|
|
74
|
-
- [License](#license)
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Basic Usage
|
|
79
|
-
|
|
80
38
|
```js
|
|
81
39
|
// Import the illogical engine
|
|
82
40
|
import Engine from '@briza/illogical'
|
|
@@ -84,811 +42,59 @@ import Engine from '@briza/illogical'
|
|
|
84
42
|
// Create a new instance of the engine
|
|
85
43
|
const engine = new Engine()
|
|
86
44
|
|
|
87
|
-
// Evaluate the raw expression
|
|
88
|
-
const result = engine.evaluate(['==', 5, 5])
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
> For advanced usage, please [Engine Options](#engine-options).
|
|
92
|
-
|
|
93
|
-
### Evaluate
|
|
94
|
-
|
|
95
|
-
Evaluate comparison or logical expression as TRUE or FALSE result:
|
|
96
|
-
|
|
97
|
-
`engine.evaluate(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions), [Evaluation Data Context](#evaluation-data-context)`)` => `boolean`
|
|
98
|
-
|
|
99
|
-
> Data context is optional.
|
|
100
|
-
|
|
101
|
-
**Example**
|
|
102
|
-
|
|
103
|
-
```js
|
|
104
|
-
// Comparison expression
|
|
105
|
-
engine.evaluate(['==', 5, 5])
|
|
106
|
-
engine.evaluate(['==', 'circle', 'circle'])
|
|
107
|
-
engine.evaluate(['==', true, true])
|
|
108
|
-
engine.evaluate(['==', '$name', 'peter'], { name: 'peter' })
|
|
109
|
-
engine.evaluate(['UNDEFINED', '$RefA'], {})
|
|
110
|
-
|
|
111
|
-
// Logical expression
|
|
112
|
-
engine.evaluate(['AND', ['==', 5, 5], ['==', 10, 10]])
|
|
113
|
-
engine.evaluate(['AND', ['==', 'circle', 'circle'], ['==', 10, 10]])
|
|
114
|
-
engine.evaluate(['OR', ['==', '$name', 'peter'], ['==', 5, 10]], {
|
|
115
|
-
name: 'peter',
|
|
116
|
-
})
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Statement
|
|
120
|
-
|
|
121
|
-
Get expression string representation:
|
|
122
|
-
|
|
123
|
-
`engine.statement(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions)`)` => `string`
|
|
124
|
-
|
|
125
|
-
**Example**
|
|
126
|
-
|
|
127
|
-
```js
|
|
128
|
-
/* Comparison expression */
|
|
129
|
-
|
|
130
|
-
engine.statement(['==', 5, 5])
|
|
131
|
-
// (5 == 5)
|
|
132
|
-
|
|
133
|
-
engine.statement(['==', 'circle', 'circle'])
|
|
134
|
-
// ("circle" == "circle")
|
|
135
|
-
|
|
136
|
-
engine.statement(['==', true, true])
|
|
137
|
-
// (true == true)
|
|
138
|
-
|
|
139
|
-
engine.statement(['==', '$name', 'peter'], { name: 'peter' })
|
|
140
|
-
// ({name} == "peter")
|
|
141
|
-
|
|
142
|
-
engine.statement(['UNDEFINED', '$RefA'])
|
|
143
|
-
// ({RefA} is UNDEFINED)
|
|
144
|
-
|
|
145
|
-
/* Logical expression */
|
|
146
|
-
|
|
147
|
-
engine.statement(['AND', ['==', 5, 5], ['==', 10, 10]])
|
|
148
|
-
// ((5 == 5) AND (10 == 10))
|
|
149
|
-
|
|
150
|
-
engine.statement(['AND', ['==', 'circle', 'circle'], ['==', 10, 10]])
|
|
151
|
-
// (("circle" == "circle") AND (10 == 10))
|
|
152
|
-
|
|
153
|
-
engine.statement(['OR', ['==', '$name', 'peter'], ['==', 5, 10]], {
|
|
154
|
-
name: 'peter',
|
|
155
|
-
})
|
|
156
|
-
// (({name} == "peter") OR (5 == 10))
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Parse
|
|
160
|
-
|
|
161
|
-
Parse the expression into a evaluable object, i.e. it returns the parsed self-evaluable condition expression.
|
|
162
|
-
|
|
163
|
-
`engine.parse(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions)`)` => `evaluable`
|
|
164
|
-
|
|
165
|
-
#### Evaluate Function
|
|
166
|
-
|
|
167
|
-
- `evaluable.evaluate(context)` please see [Evaluation Data Context](#evaluation-data-context).
|
|
168
|
-
- `evaluable.toString()` please see [Statement](#statement).
|
|
169
|
-
|
|
170
|
-
**Example**
|
|
171
|
-
|
|
172
|
-
```js
|
|
173
|
-
let evaluable = engine.parse(['==', '$name', 'peter'])
|
|
174
|
-
|
|
175
|
-
evaluable.evaluate({ name: 'peter' }) // true
|
|
176
|
-
|
|
177
|
-
evaluable.toString()
|
|
178
|
-
// ({name} == "peter")
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Simplify
|
|
182
|
-
|
|
183
|
-
Simplifies an expression with a given context. This is useful when you already have some of
|
|
184
|
-
the properties of context and wants to try to evaluate the expression.
|
|
185
|
-
|
|
186
|
-
**Example**
|
|
187
|
-
|
|
188
|
-
```js
|
|
189
|
-
engine.simplify(['AND', ['==', '$a', 10], ['==', '$b', 20]], { a: 10 }) // ['==', '$b', 20]
|
|
190
|
-
|
|
191
|
-
engine.simplify(['AND', ['==', '$a', 10], ['==', '$b', 20]], { a: 20 }) // false
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Values not found in the context will cause the parent operand not to be evaluated and returned
|
|
195
|
-
as part of the simplified expression.
|
|
196
|
-
|
|
197
|
-
In some situations we might want to evaluate the expression even if referred value is not
|
|
198
|
-
present. You can provide a list of keys that will be strictly evaluated even if they are not
|
|
199
|
-
present in the context.
|
|
200
|
-
|
|
201
|
-
**Example**
|
|
202
|
-
|
|
203
|
-
```js
|
|
204
|
-
engine.simplify(
|
|
205
|
-
['AND', ['==', '$a', 10], ['==', '$b', 20]],
|
|
206
|
-
{ a: 10 },
|
|
207
|
-
['b'] // '$b' will be evaluated to undefined.
|
|
208
|
-
) // false
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Alternatively we might want to do the opposite and strictly evaluate the expression for all referred
|
|
212
|
-
values not present in the context except for a specified list of optional keys.
|
|
213
|
-
|
|
214
|
-
**Example**
|
|
215
|
-
|
|
216
|
-
```js
|
|
217
|
-
engine.simplify(
|
|
218
|
-
['OR', ['==', '$a', 10], ['==', '$b', 20], ['==', '$c', 20]],
|
|
219
|
-
{ c: 10 },
|
|
220
|
-
undefined,
|
|
221
|
-
['b'] // except for '$b' everything not in context will be evaluated to undefined.
|
|
222
|
-
) // ['==', '$b', 20]
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## Working with Expressions
|
|
226
|
-
|
|
227
|
-
### Evaluation Data Context
|
|
228
|
-
|
|
229
|
-
The evaluation data context is used to provide the expression with variable references, i.e. this allows for the dynamic expressions. The data context is object with properties used as the references keys, and its values as reference values.
|
|
230
|
-
|
|
231
|
-
> Valid reference values: object, string, number, boolean, string[], number[].
|
|
232
|
-
|
|
233
|
-
To reference the nested reference, please use "." delimiter, e.g.:
|
|
234
|
-
`$address.city`
|
|
235
|
-
|
|
236
|
-
If the key of the nested reference includes the "." delimiter, please wrap the whole key with backticks `` ` ``, e.g.:
|
|
237
|
-
`` $address.`city.code` `` can reference the object
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
{
|
|
241
|
-
address: {
|
|
242
|
-
'city.code': 'TOR'
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
``$address.`city.code`[0]`` can reference the object
|
|
248
|
-
|
|
249
|
-
```javascript
|
|
250
|
-
{
|
|
251
|
-
address: {
|
|
252
|
-
'city.code': ['TOR']
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
when the value of the nested reference is an array.
|
|
258
|
-
|
|
259
|
-
#### Accessing Array Element:
|
|
260
|
-
|
|
261
|
-
`$options[1]`
|
|
262
|
-
|
|
263
|
-
#### Accessing Array Element via Reference:
|
|
264
|
-
|
|
265
|
-
`$options[{index}]`
|
|
266
|
-
|
|
267
|
-
- The **index** reference is resolved within the data context as an array index.
|
|
268
|
-
|
|
269
|
-
#### Nested Referencing
|
|
270
|
-
|
|
271
|
-
`$address.{segment}`
|
|
272
|
-
|
|
273
|
-
- The **segment** reference is resolved within the data context as a property key.
|
|
274
|
-
|
|
275
|
-
#### Composite Reference Key
|
|
276
|
-
|
|
277
|
-
`$shape{shapeType}`
|
|
278
|
-
|
|
279
|
-
- The **shapeType** reference is resolved within the data context, and inserted into the outer reference key.
|
|
280
|
-
- E.g. **shapeType** is resolved as "**B**" and would compose the **$shapeB** outer reference.
|
|
281
|
-
- This resolution could be n-nested.
|
|
282
|
-
|
|
283
|
-
#### Data Type Casting
|
|
284
|
-
|
|
285
|
-
`$payment.amount.(Type)`
|
|
286
|
-
|
|
287
|
-
Cast the given data context into the desired data type before being used as an operand in the evaluation.
|
|
288
|
-
|
|
289
|
-
> Note: If the conversion is invalid, then a warning message is being logged.
|
|
290
|
-
|
|
291
|
-
Supported data type conversions:
|
|
292
|
-
|
|
293
|
-
- .(String): cast a given reference to String.
|
|
294
|
-
- .(Number): cast a given reference to Number.
|
|
295
|
-
|
|
296
|
-
**Example**
|
|
297
|
-
|
|
298
|
-
```js
|
|
299
45
|
// Data context
|
|
300
46
|
const ctx = {
|
|
301
47
|
name: 'peter',
|
|
302
|
-
country: 'canada',
|
|
303
48
|
age: 21,
|
|
304
|
-
options: [1, 2, 3],
|
|
305
49
|
address: {
|
|
306
50
|
city: 'Toronto',
|
|
307
51
|
country: 'Canada',
|
|
308
52
|
},
|
|
309
|
-
index: 2,
|
|
310
|
-
segment: 'city',
|
|
311
|
-
shapeA: 'box',
|
|
312
|
-
shapeB: 'circle',
|
|
313
|
-
shapeType: 'B',
|
|
314
53
|
}
|
|
315
54
|
|
|
316
55
|
// Evaluate an expression in the given data context
|
|
317
56
|
engine.evaluate(['>', '$age', 20], ctx) // true
|
|
318
57
|
|
|
319
|
-
//
|
|
58
|
+
// Accessing a property
|
|
320
59
|
engine.evaluate(['==', '$address.city', 'Toronto'], ctx) // true
|
|
321
60
|
|
|
322
|
-
// Accessing Array Element
|
|
323
|
-
engine.evaluate(['==', '$options[1]', 2], ctx) // true
|
|
324
|
-
|
|
325
|
-
// Accessing Array Element via Reference
|
|
326
|
-
engine.evaluate(['==', '$options[{index}]', 3], ctx) // true
|
|
327
|
-
|
|
328
|
-
// Nested Referencing
|
|
329
|
-
engine.evaluate(['==', '$address.{segment}', 'Toronto'], ctx) // true
|
|
330
|
-
|
|
331
|
-
// Composite Reference Key
|
|
332
|
-
engine.evaluate(['==', '$shape{shapeType}', 'circle'], ctx) // true
|
|
333
|
-
|
|
334
61
|
// Data Type Casting
|
|
335
62
|
engine.evaluate(['==', '$age.(String)', '21'], ctx) // true
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Operand Types
|
|
339
|
-
|
|
340
|
-
The [Comparison Expression](#comparison-expression) expect operands to be one of the below:
|
|
341
|
-
|
|
342
|
-
#### Value
|
|
343
|
-
|
|
344
|
-
Simple value types: string, number, boolean.
|
|
345
|
-
|
|
346
|
-
**Example**
|
|
347
|
-
|
|
348
|
-
```js
|
|
349
|
-
;['==', 5, 5][('==', 'circle', 'circle')][('==', true, true)]
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
#### Reference
|
|
353
|
-
|
|
354
|
-
The reference operand value is resolved from the [Evaluation Data Context](#evaluation-data-context), where the the operands name is used as key in the context.
|
|
355
|
-
|
|
356
|
-
The reference operand must be prefixed with `$` symbol, e.g.: `$name`. This might be customized via [Reference Predicate Parser Option](#reference-predicate).
|
|
357
|
-
|
|
358
|
-
**Example**
|
|
359
|
-
|
|
360
|
-
| Expression | Data Context |
|
|
361
|
-
| ----------------------------- | ------------------------------------- |
|
|
362
|
-
| `['==', '$age', 21]` | `{age: 21}` |
|
|
363
|
-
| `['==', 'circle', '$shape'] ` | `{shape: 'circle'}` |
|
|
364
|
-
| `['==', '$visible', true]` | `{visible: true}` |
|
|
365
|
-
| `['==', '$circle', '$shape']` | `{circle: 'circle', shape: 'circle'}` |
|
|
366
|
-
|
|
367
|
-
#### Collection
|
|
368
|
-
|
|
369
|
-
The operand could be an array mixed from [Value](#value) and [Reference](#reference).
|
|
370
|
-
|
|
371
|
-
**Example**
|
|
372
|
-
|
|
373
|
-
| Expression | Data Context |
|
|
374
|
-
| ---------------------------------------- | ----------------------------------- |
|
|
375
|
-
| `['IN', [1, 2], 1]` | `{}` |
|
|
376
|
-
| `['IN', 'circle', ['$shapeA', $shapeB] ` | `{shapeA: 'circle', shapeB: 'box'}` |
|
|
377
|
-
| `['IN', [$number, 5], 5]` | `{number: 3}` |
|
|
378
|
-
|
|
379
|
-
### Comparison Expressions
|
|
380
|
-
|
|
381
|
-
#### Equal
|
|
382
|
-
|
|
383
|
-
Expression format: `["==", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
384
|
-
|
|
385
|
-
> Valid operand types: string, number, boolean.
|
|
386
|
-
|
|
387
|
-
```json
|
|
388
|
-
["==", 5, 5]
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
```js
|
|
392
|
-
engine.evaluate(['==', 5, 5]) // true
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
#### Not Equal
|
|
396
|
-
|
|
397
|
-
Expression format: `["!=", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
398
|
-
|
|
399
|
-
> Valid operand types: string, number, boolean.
|
|
400
|
-
|
|
401
|
-
```json
|
|
402
|
-
["!=", "circle", "square"]
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
```js
|
|
406
|
-
engine.evaluate(['!=', 'circle', 'square']) // true
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
#### Greater Than
|
|
410
|
-
|
|
411
|
-
Expression format: `[">", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
412
|
-
|
|
413
|
-
> Valid operand types: number, string.
|
|
414
|
-
|
|
415
|
-
- String comparison only supports ISO-8601 formatted dates.
|
|
416
|
-
|
|
417
|
-
```json
|
|
418
|
-
[">", 10, 5]
|
|
419
|
-
[">", "2023-01-01", "2022-12-31"]
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
```js
|
|
423
|
-
engine.evaluate(['>', 10, 5]) // true
|
|
424
|
-
engine.evaluate(['>', '2023-01-01', '2022-12-31']) // true
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
#### Greater Than or Equal
|
|
428
|
-
|
|
429
|
-
Expression format: `[">=", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
430
|
-
|
|
431
|
-
> Valid operand types: number, string.
|
|
432
|
-
|
|
433
|
-
- String comparison only supports ISO-8601 formatted dates.
|
|
434
|
-
|
|
435
|
-
```json
|
|
436
|
-
[">=", 5, 5]
|
|
437
|
-
[">=", "2023-01-01", "2023-01-01"]
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
```js
|
|
441
|
-
engine.evaluate(['>=', 5, 5]) // true
|
|
442
|
-
engine.evaluate(['>=', '2023-01-01', '2023-01-01']) // true
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
#### Less Than
|
|
446
|
-
|
|
447
|
-
Expression format: `["<", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
448
|
-
|
|
449
|
-
> Valid operand types: number, string.
|
|
450
|
-
|
|
451
|
-
- String comparison only supports ISO-8601 formatted dates.
|
|
452
|
-
|
|
453
|
-
```json
|
|
454
|
-
["<", 5, 10]
|
|
455
|
-
["<", "2022-12-31", "2023-01-01"]
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
```js
|
|
459
|
-
engine.evaluate(['<', 5, 10]) // true
|
|
460
|
-
engine.evaluate(['<', '2022-12-31', '2023-01-01']) // true
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
#### Less Than or Equal
|
|
464
|
-
|
|
465
|
-
Expression format: `["<=", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
466
|
-
|
|
467
|
-
> Valid operand types: number, string.
|
|
468
|
-
|
|
469
|
-
- String comparison only supports ISO-8601 formatted dates.
|
|
470
|
-
|
|
471
|
-
```json
|
|
472
|
-
["<=", 5, 5]
|
|
473
|
-
["<=", "2023-01-01", "2023-01-01"]
|
|
474
|
-
```
|
|
475
63
|
|
|
476
|
-
|
|
477
|
-
engine.evaluate(['
|
|
478
|
-
engine.evaluate(['<=', '2023-01-01', '2023-01-01']) // true
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
#### In
|
|
482
|
-
|
|
483
|
-
Expression format: `["IN", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
484
|
-
|
|
485
|
-
> Valid operand types: number and number[] or string and string[].
|
|
486
|
-
|
|
487
|
-
```json
|
|
488
|
-
["IN", 5, [1,2,3,4,5]]
|
|
489
|
-
["IN", ["circle", "square", "triangle"], "square"]
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
```js
|
|
493
|
-
engine.evaluate(['IN', 5, [1, 2, 3, 4, 5]]) // true
|
|
494
|
-
engine.evaluate(['IN', ['circle', 'square', 'triangle'], 'square']) // true
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
#### Not In
|
|
498
|
-
|
|
499
|
-
Expression format: `["NOT IN", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
500
|
-
|
|
501
|
-
> Valid operand types: number and number[] or string and string[].
|
|
502
|
-
|
|
503
|
-
```json
|
|
504
|
-
["IN", 10, [1,2,3,4,5]]
|
|
505
|
-
["IN", ["circle", "square", "triangle"], "oval"]
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
```js
|
|
509
|
-
engine.evaluate(['NOT IN', 10, [1, 2, 3, 4, 5]]) // true
|
|
510
|
-
engine.evaluate(['NOT IN', ['circle', 'square', 'triangle'], 'oval']) // true
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
#### Prefix
|
|
514
|
-
|
|
515
|
-
Expression format: `["PREFIX", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
516
|
-
|
|
517
|
-
> Valid operand types: string.
|
|
518
|
-
|
|
519
|
-
- Left operand is the PREFIX term.
|
|
520
|
-
- Right operand is the tested word.
|
|
521
|
-
|
|
522
|
-
```json
|
|
523
|
-
["PREFIX", "hemi", "hemisphere"]
|
|
64
|
+
// Evaluate a logical expression
|
|
65
|
+
engine.evaluate(['AND', ['>', '$age', 20], ['==', '$name', 'peter']]) // true
|
|
524
66
|
```
|
|
525
67
|
|
|
526
|
-
|
|
527
|
-
engine.evaluate(['PREFIX', 'hemi', 'hemisphere']) // true
|
|
528
|
-
engine.evaluate(['PREFIX', 'hemi', 'sphere']) // false
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
#### Suffix
|
|
532
|
-
|
|
533
|
-
Expression format: `["SUFFIX", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.
|
|
534
|
-
|
|
535
|
-
> Valid operand types: string.
|
|
536
|
-
|
|
537
|
-
- Left operand is the tested word.
|
|
538
|
-
- Right operand is the SUFFIX term.
|
|
68
|
+
## 🖼️ Resources
|
|
539
69
|
|
|
540
|
-
|
|
541
|
-
["SUFFIX", "establishment", "ment"]
|
|
542
|
-
```
|
|
70
|
+
Understand supported expressions:
|
|
543
71
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
72
|
+
- [Comparison Expressions](./specs/comparison-expressions.md)
|
|
73
|
+
- [Logical Expressions](./specs/logical-expressions.md)
|
|
74
|
+
- [Arithmetic Expressions](./specs/arithmetic-expressions.md)
|
|
75
|
+
- [Evaluation Data Context](./specs/evaluation-data-context.md)
|
|
76
|
+
- [Operand Types](./specs/operand-types.md)
|
|
548
77
|
|
|
549
|
-
|
|
78
|
+
Learn about usages:
|
|
550
79
|
|
|
551
|
-
|
|
80
|
+
- [Evaluate](./specs/evaluate.md)
|
|
81
|
+
- [Statement](./specs/statement.md)
|
|
82
|
+
- [Parse](./specs/parse.md)
|
|
83
|
+
- [Simplify](./specs/simplify.md)
|
|
552
84
|
|
|
553
|
-
|
|
85
|
+
Customize the engine and the documentation:
|
|
554
86
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
["OVERLAP", ["circle", "square", "triangle"], ["square"]]
|
|
558
|
-
```
|
|
87
|
+
- [Engine Options](./specs/engine.md)
|
|
88
|
+
- [Code Documentation](https://briza-insurance.github.io/illogical/index.html)
|
|
559
89
|
|
|
560
|
-
|
|
561
|
-
engine.evaluate(['OVERLAP', [1, 2, 6], [1, 2, 3, 4, 5]]) // true
|
|
562
|
-
engine.evaluate([
|
|
563
|
-
'OVERLAP',
|
|
564
|
-
['circle', 'square', 'triangle'],
|
|
565
|
-
['square', 'oval'],
|
|
566
|
-
]) // true
|
|
567
|
-
```
|
|
90
|
+
## 📖 Changelog
|
|
568
91
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
Expression format: `["UNDEFINED", `[Reference Operand](#reference)`]`.
|
|
572
|
-
|
|
573
|
-
```json
|
|
574
|
-
["UNDEFINED", "$RefA"]
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
```js
|
|
578
|
-
engine.evaluate(['UNDEFINED', 'RefA'], {}) // true
|
|
579
|
-
engine.evaluate(['UNDEFINED', 'RefA'], { RefA: undefined }) // true
|
|
580
|
-
engine.evaluate(['UNDEFINED', 'RefA'], { RefA: 10 }) // false
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
#### Present
|
|
584
|
-
|
|
585
|
-
Evaluates as FALSE when the operand is UNDEFINED or NULL.
|
|
586
|
-
|
|
587
|
-
Expression format: `["PRESENT", `[Reference Operand](#reference)`]`.
|
|
588
|
-
|
|
589
|
-
```json
|
|
590
|
-
["PRESENT", "$RefA"]
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
```js
|
|
594
|
-
engine.evaluate(['PRESENT', 'RefA'], {}) // false
|
|
595
|
-
engine.evaluate(['PRESENT', 'RefA'], { RefA: undefined }) // false
|
|
596
|
-
engine.evaluate(['PRESENT', 'RefA'], { RefA: null }) // false
|
|
597
|
-
engine.evaluate(['PRESENT', 'RefA'], { RefA: 10 }) // true
|
|
598
|
-
engine.evaluate(['PRESENT', 'RefA'], { RefA: false }) // true
|
|
599
|
-
engine.evaluate(['PRESENT', 'RefA'], { RefA: 0 }) // true
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
### Logical Expressions
|
|
603
|
-
|
|
604
|
-
#### And
|
|
605
|
-
|
|
606
|
-
The logical AND operator (&&) returns the boolean value TRUE if both operands are TRUE and returns FALSE otherwise.
|
|
607
|
-
|
|
608
|
-
Expression format: `["AND", Left Operand 1, Right Operand 2, ... , Right Operand N]`.
|
|
609
|
-
|
|
610
|
-
> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).
|
|
611
|
-
|
|
612
|
-
```json
|
|
613
|
-
["AND", ["==", 5, 5], ["==", 10, 10]]
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
```js
|
|
617
|
-
engine.evaluate(['AND', ['==', 5, 5], ['==', 10, 10]]) // true
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
#### Or
|
|
621
|
-
|
|
622
|
-
The logical OR operator (||) returns the boolean value TRUE if either or both operands is TRUE and returns FALSE otherwise.
|
|
623
|
-
|
|
624
|
-
Expression format: `["OR", Left Operand 1, Right Operand 2, ... , Right Operand N]`.
|
|
625
|
-
|
|
626
|
-
> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).
|
|
627
|
-
|
|
628
|
-
```json
|
|
629
|
-
["OR", ["==", 5, 5], ["==", 10, 5]]
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
```js
|
|
633
|
-
engine.evaluate(['OR', ['==', 5, 5], ['==', 10, 5]]) // true
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
#### Nor
|
|
637
|
-
|
|
638
|
-
The logical NOR operator returns the boolean value TRUE if both operands are FALSE and returns FALSE otherwise.
|
|
639
|
-
|
|
640
|
-
Expression format: `["NOR", Left Operand 1, Right Operand 2, ... , Right Operand N]`
|
|
641
|
-
|
|
642
|
-
> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).
|
|
643
|
-
|
|
644
|
-
```json
|
|
645
|
-
["NOR", ["==", 5, 1], ["==", 10, 5]]
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
```js
|
|
649
|
-
engine.evaluate(['NOR', ['==', 5, 1], ['==', 10, 5]]) // true
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
#### Xor
|
|
653
|
-
|
|
654
|
-
The logical NOR operator returns the boolean value TRUE if both operands are FALSE and returns FALSE otherwise.
|
|
655
|
-
|
|
656
|
-
Expression format: `["XOR", Left Operand 1, Right Operand 2, ... , Right Operand N]`
|
|
657
|
-
|
|
658
|
-
> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).
|
|
659
|
-
|
|
660
|
-
```json
|
|
661
|
-
["XOR", ["==", 5, 5], ["==", 10, 5]]
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
```js
|
|
665
|
-
engine.evaluate(['XOR', ['==', 5, 5], ['==', 10, 5]]) // true
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
```json
|
|
669
|
-
["XOR", ["==", 5, 5], ["==", 10, 10]]
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
```js
|
|
673
|
-
engine.evaluate(['XOR', ['==', 5, 5], ['==', 10, 10]]) // false
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
#### Not
|
|
677
|
-
|
|
678
|
-
The logical NOT operator returns the boolean value TRUE if the operand is FALSE, TRUE otherwise.
|
|
679
|
-
|
|
680
|
-
Expression format: `["NOT", Operand]`
|
|
681
|
-
|
|
682
|
-
> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).
|
|
683
|
-
|
|
684
|
-
```json
|
|
685
|
-
["NOT", ["==", 5, 5]]
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
```js
|
|
689
|
-
engine.evaluate(['NOT', ['==', 5, 5]]) // true
|
|
690
|
-
```
|
|
691
|
-
|
|
692
|
-
### Arithmetic Expressions
|
|
693
|
-
|
|
694
|
-
Arithmetic Expressions are not supported as root level expressions since they must evaluate to a boolean. But it can be used nested within [Comparisson Expressions](#comparison-expressions).
|
|
695
|
-
|
|
696
|
-
#### Division
|
|
697
|
-
|
|
698
|
-
The arithmetical operator for division produces the quotient of its operands where the left-most operand is the dividend and the subsequent one is the divisor, done from left to right.
|
|
699
|
-
|
|
700
|
-
Expression format: `["/", First Operand, Second Operand, ... , Nth Operand]`.
|
|
701
|
-
|
|
702
|
-
> Valid operand types: [Arithmetic Expressions](#arithmetic-expressions) or [Operands](#operand-types).
|
|
703
|
-
|
|
704
|
-
```json
|
|
705
|
-
["==", ["/", 100, 10], 10]
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
```js
|
|
709
|
-
engine.evaluate(['==', ['/', 100, 10], 10]) // true
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
#### Multiplication
|
|
713
|
-
|
|
714
|
-
The arithmetical operator for multiplication produces the product of the operands.
|
|
715
|
-
|
|
716
|
-
Expression format: `["*", First Operand, Second Operand, ... , Nth Operand]`.
|
|
717
|
-
|
|
718
|
-
> Valid operand types: [Arithmetic Expressions](#arithmetic-expressions) or [Operands](#operand-types).
|
|
719
|
-
|
|
720
|
-
```json
|
|
721
|
-
["==", ["*", 100, 10], 10]
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
```js
|
|
725
|
-
engine.evaluate(['==', ['*', 10, 10], 100]) // true
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
#### Subtraction
|
|
729
|
-
|
|
730
|
-
The arithmetical operator for subtraction subtracts the operands, producing their difference.
|
|
731
|
-
|
|
732
|
-
Expression format: `["-", First Operand, Second Operand, ... , Nth Operand]`.
|
|
733
|
-
|
|
734
|
-
> Valid operand types: [Arithmetic Expressions](#arithmetic-expressions) or [Operands](#operand-types).
|
|
735
|
-
|
|
736
|
-
```json
|
|
737
|
-
["==", ["-", 20, 10], 10]
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
```js
|
|
741
|
-
engine.evaluate(['==', ['-', 20, 10], 10]) // true
|
|
742
|
-
```
|
|
743
|
-
|
|
744
|
-
#### Addition
|
|
745
|
-
|
|
746
|
-
The arithmetical operator for addition produces the sum of the operands.
|
|
747
|
-
|
|
748
|
-
Expression format: `["+", First Operand, Second Operand, ... , Nth Operand]`.
|
|
749
|
-
|
|
750
|
-
> Valid operand types: [Arithmetic Expressions](#arithmetic-expressions) or [Operands](#operand-types).
|
|
751
|
-
|
|
752
|
-
```json
|
|
753
|
-
["==", ["+", 5, 5], 10]
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
```js
|
|
757
|
-
engine.evaluate(['==', ['+', 5, 5], 10]) // true
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
## Engine Options
|
|
761
|
-
|
|
762
|
-
### Parser Options
|
|
763
|
-
|
|
764
|
-
Below described, are individual options object properties which could be used individually. Any missing options will be substituted with the default options.
|
|
765
|
-
|
|
766
|
-
**Usage**
|
|
767
|
-
|
|
768
|
-
```js
|
|
769
|
-
// Import the illogical engine
|
|
770
|
-
import Engine from '@briza/illogical'
|
|
771
|
-
|
|
772
|
-
// Create a new instance of the engine
|
|
773
|
-
const opts = {
|
|
774
|
-
referencePredicate: (operand) => operand.startsWith('$'),
|
|
775
|
-
}
|
|
776
|
-
const engine = new Engine(opts)
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
#### Reference Predicate
|
|
780
|
-
|
|
781
|
-
A function used to determine if the operand is a reference type, otherwise evaluated as a static value.
|
|
782
|
-
|
|
783
|
-
```typescript
|
|
784
|
-
referencePredicate: (operand: string) => boolean
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
**Return value:**
|
|
788
|
-
|
|
789
|
-
- `true` = reference type
|
|
790
|
-
- `false` = value type
|
|
791
|
-
|
|
792
|
-
**Default reference predicate:**
|
|
793
|
-
|
|
794
|
-
> The `$` symbol at the begging of the operand is used to predicate the reference type., E.g. `$State`, `$Country`.
|
|
795
|
-
|
|
796
|
-
#### Reference Transform
|
|
797
|
-
|
|
798
|
-
A function used to transform the operand into the reference annotation stripped form. I.e. remove any annotation used to detect the reference type. E.g. "$Reference" => "Reference".
|
|
799
|
-
|
|
800
|
-
```typescript
|
|
801
|
-
referenceTransform: (operand: string) => string
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
> **Default reference transform:**
|
|
805
|
-
> It removes the `$` symbol at the begging of the operand name.
|
|
806
|
-
|
|
807
|
-
#### Operator Mapping
|
|
808
|
-
|
|
809
|
-
Mapping of the operators. The key is unique operator key, and the value is the key used to represent the given operator in the raw expression.
|
|
810
|
-
|
|
811
|
-
```typescript
|
|
812
|
-
operatorMapping: Map<symbol, string>
|
|
813
|
-
```
|
|
814
|
-
|
|
815
|
-
**Default operator mapping:**
|
|
816
|
-
|
|
817
|
-
```typescript
|
|
818
|
-
// Comparison
|
|
819
|
-
[OPERATOR_EQ, '=='],
|
|
820
|
-
[OPERATOR_NE, '!='],
|
|
821
|
-
[OPERATOR_GT, '>'],
|
|
822
|
-
[OPERATOR_GE, '>='],
|
|
823
|
-
[OPERATOR_LT, '<'],
|
|
824
|
-
[OPERATOR_LE, '<='],
|
|
825
|
-
[OPERATOR_IN, 'IN'],
|
|
826
|
-
[OPERATOR_NOT_IN, 'NOT IN'],
|
|
827
|
-
[OPERATOR_PREFIX, 'PREFIX'],
|
|
828
|
-
[OPERATOR_SUFFIX, 'SUFFIX'],
|
|
829
|
-
[OPERATOR_OVERLAP, 'OVERLAP'],
|
|
830
|
-
[OPERATOR_UNDEFINED, 'UNDEFINED'],
|
|
831
|
-
[OPERATOR_PRESENT, 'PRESENT'],
|
|
832
|
-
// Logical
|
|
833
|
-
[OPERATOR_AND, 'AND'],
|
|
834
|
-
[OPERATOR_OR, 'OR'],
|
|
835
|
-
[OPERATOR_NOR, 'NOR'],
|
|
836
|
-
[OPERATOR_XOR, 'XOR'],
|
|
837
|
-
[OPERATOR_NOT, 'NOT'],
|
|
838
|
-
// Arithmetic
|
|
839
|
-
[OPERATOR_SUM, '+'],
|
|
840
|
-
[OPERATOR_SUBTRACT, '-'],
|
|
841
|
-
[OPERATOR_MULTIPLY, '*'],
|
|
842
|
-
[OPERATOR_DIVIDE, '/'],
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
> The operator keys are unique symbols which could be imported from the engine package:
|
|
846
|
-
|
|
847
|
-
```js
|
|
848
|
-
import {
|
|
849
|
-
OPERATOR_EQ,
|
|
850
|
-
OPERATOR_NE,
|
|
851
|
-
OPERATOR_GT,
|
|
852
|
-
OPERATOR_GE,
|
|
853
|
-
OPERATOR_LT,
|
|
854
|
-
OPERATOR_LE,
|
|
855
|
-
OPERATOR_IN,
|
|
856
|
-
OPERATOR_NOT_IN,
|
|
857
|
-
OPERATOR_PREFIX,
|
|
858
|
-
OPERATOR_SUFFIX,
|
|
859
|
-
OPERATOR_OVERLAP,
|
|
860
|
-
OPERATOR_UNDEFINED,
|
|
861
|
-
OPERATOR_PRESENT,
|
|
862
|
-
OPERATOR_AND,
|
|
863
|
-
OPERATOR_OR,
|
|
864
|
-
OPERATOR_NOR,
|
|
865
|
-
OPERATOR_XOR,
|
|
866
|
-
OPERATOR_NOT,
|
|
867
|
-
OPERATOR_DIVIDE,
|
|
868
|
-
OPERATOR_MULTIPLY,
|
|
869
|
-
OPERATOR_SUBTRACT,
|
|
870
|
-
OPERATOR_SUM,
|
|
871
|
-
} from '@briza/illogical'
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
## Breaking Changes
|
|
875
|
-
|
|
876
|
-
### v1.4.2
|
|
877
|
-
|
|
878
|
-
- Change on `@babel/env` preset to target `> 1%, node 12` this will remove some polyfills that were causing performance
|
|
879
|
-
problems in some projects.
|
|
880
|
-
|
|
881
|
-
### v1.2.0
|
|
882
|
-
|
|
883
|
-
- Removed **strict** mode from the Engine constructor options.
|
|
884
|
-
`const engine = new Engine(strictMode, opts);` -> `const engine = new Engine(opts);`
|
|
885
|
-
|
|
886
|
-
---
|
|
92
|
+
See [changelog.md](./changelog.md).
|
|
887
93
|
|
|
888
|
-
## Contributing
|
|
94
|
+
## 🤝 Contributing
|
|
889
95
|
|
|
890
|
-
See [contributing.md](
|
|
96
|
+
See [contributing.md](./contributing.md).
|
|
891
97
|
|
|
892
|
-
## License
|
|
98
|
+
## 📜 License
|
|
893
99
|
|
|
894
|
-
Illogical is released under the MIT license. See [license.txt](
|
|
100
|
+
Illogical is released under the MIT license. See [license.txt](./license.txt) for details.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|