@jmespath-community/jmespath 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -72
- package/dist/jmespath.esm.js +121 -85
- package/dist/jmespath.esm.min.js +1 -1
- package/dist/jmespath.esm.min.js.map +1 -1
- package/dist/jmespath.umd.js +121 -85
- package/dist/jmespath.umd.min.js +1 -1
- package/dist/jmespath.umd.min.js.map +1 -1
- package/dist/lib/bin/jp.js +127 -0
- package/dist/lib/bin/jp.js.map +1 -0
- package/dist/lib/package.json +70 -0
- package/dist/lib/{AST.type.js.map → src/AST.type.js.map} +1 -1
- package/dist/lib/{JSON.type.js.map → src/JSON.type.js.map} +1 -1
- package/dist/lib/{Lexer.js → src/Lexer.js} +7 -2
- package/dist/lib/src/Lexer.js.map +1 -0
- package/dist/lib/{Lexer.type.js → src/Lexer.type.js} +1 -0
- package/dist/lib/src/Lexer.type.js.map +1 -0
- package/dist/lib/{Parser.js → src/Parser.js} +36 -13
- package/dist/lib/src/Parser.js.map +1 -0
- package/dist/lib/{Parser.type.js.map → src/Parser.type.js.map} +1 -1
- package/dist/lib/{Runtime.js → src/Runtime.js} +1 -1
- package/dist/lib/src/Runtime.js.map +1 -0
- package/dist/lib/{Scope.js → src/Scope.js} +5 -5
- package/dist/lib/src/Scope.js.map +1 -0
- package/dist/lib/{TreeInterpreter.js → src/TreeInterpreter.js} +17 -10
- package/dist/lib/src/TreeInterpreter.js.map +1 -0
- package/dist/lib/{index.js → src/index.js} +2 -4
- package/dist/lib/src/index.js.map +1 -0
- package/dist/lib/{utils → src/utils}/index.js +12 -11
- package/dist/lib/src/utils/index.js.map +1 -0
- package/dist/lib/src/utils/strings.js.map +1 -0
- package/dist/lib/{utils → src/utils}/text.js +1 -0
- package/dist/lib/src/utils/text.js.map +1 -0
- package/dist/types/bin/jp.d.ts +2 -0
- package/dist/types/{AST.type.d.ts → src/AST.type.d.ts} +7 -1
- package/dist/types/{Lexer.type.d.ts → src/Lexer.type.d.ts} +2 -1
- package/dist/types/{Runtime.d.ts → src/Runtime.d.ts} +1 -1
- package/dist/types/{Scope.d.ts → src/Scope.d.ts} +1 -0
- package/dist/types/{index.d.ts → src/index.d.ts} +4 -3
- package/package.json +4 -1
- package/dist/lib/Lexer.js.map +0 -1
- package/dist/lib/Lexer.type.js.map +0 -1
- package/dist/lib/Parser.js.map +0 -1
- package/dist/lib/Runtime.js.map +0 -1
- package/dist/lib/Scope.js.map +0 -1
- package/dist/lib/TreeInterpreter.js.map +0 -1
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/utils/index.js.map +0 -1
- package/dist/lib/utils/strings.js.map +0 -1
- package/dist/lib/utils/text.js.map +0 -1
- /package/dist/lib/{AST.type.js → src/AST.type.js} +0 -0
- /package/dist/lib/{JSON.type.js → src/JSON.type.js} +0 -0
- /package/dist/lib/{Parser.type.js → src/Parser.type.js} +0 -0
- /package/dist/lib/{utils → src/utils}/strings.js +0 -0
- /package/dist/types/{JSON.type.d.ts → src/JSON.type.d.ts} +0 -0
- /package/dist/types/{Lexer.d.ts → src/Lexer.d.ts} +0 -0
- /package/dist/types/{Parser.d.ts → src/Parser.d.ts} +0 -0
- /package/dist/types/{Parser.type.d.ts → src/Parser.type.d.ts} +0 -0
- /package/dist/types/{TreeInterpreter.d.ts → src/TreeInterpreter.d.ts} +0 -0
- /package/dist/types/{utils → src/utils}/index.d.ts +0 -0
- /package/dist/types/{utils → src/utils}/strings.d.ts +0 -0
- /package/dist/types/{utils → src/utils}/text.d.ts +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
# @jmespath-community/jmespath
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
@jmespath-community/jmespath is a **TypeScript** implementation of the [JMESPath](https://jmespath.site/) spec.
|
|
7
6
|
|
|
8
7
|
JMESPath is a query language for JSON. It will take a JSON document
|
|
@@ -20,12 +19,9 @@ npm install @jmespath-community/jmespath
|
|
|
20
19
|
### `search(data: JSONValue, expression: string): JSONValue`
|
|
21
20
|
|
|
22
21
|
```javascript
|
|
23
|
-
import { search } from
|
|
22
|
+
import { search } from "@jmespath-community/jmespath";
|
|
24
23
|
|
|
25
|
-
search(
|
|
26
|
-
{foo: {bar: {baz: [0, 1, 2, 3, 4]}}},
|
|
27
|
-
"foo.bar.baz[2]"
|
|
28
|
-
);
|
|
24
|
+
search({ foo: { bar: { baz: [0, 1, 2, 3, 4] } } }, "foo.bar.baz[2]");
|
|
29
25
|
|
|
30
26
|
// OUTPUTS: 2
|
|
31
27
|
```
|
|
@@ -35,50 +31,42 @@ In the example we gave the `search` function input data of
|
|
|
35
31
|
expression `foo.bar.baz[2]`, and the `search` function evaluated
|
|
36
32
|
the expression against the input data to produce the result `2`.
|
|
37
33
|
|
|
38
|
-
The JMESPath language can do
|
|
39
|
-
from a list.
|
|
34
|
+
The JMESPath language can do _a lot_ more than select an element
|
|
35
|
+
from a list. Here are a few more examples:
|
|
40
36
|
|
|
41
37
|
```javascript
|
|
42
|
-
import { search } from
|
|
38
|
+
import { search } from "@jmespath-community/jmespath";
|
|
43
39
|
|
|
44
40
|
/* --- EXAMPLE 1 --- */
|
|
45
41
|
|
|
46
42
|
let JSON_DOCUMENT = {
|
|
47
43
|
foo: {
|
|
48
44
|
bar: {
|
|
49
|
-
baz: [0, 1, 2, 3, 4]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
45
|
+
baz: [0, 1, 2, 3, 4],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
52
48
|
};
|
|
53
49
|
|
|
54
50
|
search(JSON_DOCUMENT, "foo.bar");
|
|
55
51
|
// OUTPUTS: { baz: [ 0, 1, 2, 3, 4 ] }
|
|
56
52
|
|
|
57
|
-
|
|
58
53
|
/* --- EXAMPLE 2 --- */
|
|
59
54
|
|
|
60
55
|
JSON_DOCUMENT = {
|
|
61
|
-
|
|
62
|
-
{
|
|
63
|
-
{
|
|
64
|
-
]
|
|
56
|
+
foo: [
|
|
57
|
+
{ first: "a", last: "b" },
|
|
58
|
+
{ first: "c", last: "d" },
|
|
59
|
+
],
|
|
65
60
|
};
|
|
66
61
|
|
|
67
|
-
search(JSON_DOCUMENT, "foo[*].first")
|
|
62
|
+
search(JSON_DOCUMENT, "foo[*].first");
|
|
68
63
|
// OUTPUTS: [ 'a', 'c' ]
|
|
69
64
|
|
|
70
|
-
|
|
71
65
|
/* --- EXAMPLE 3 --- */
|
|
72
66
|
|
|
73
67
|
JSON_DOCUMENT = {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{"age": 25},
|
|
77
|
-
{"age": 30},
|
|
78
|
-
{"age": 35},
|
|
79
|
-
{"age": 40}
|
|
80
|
-
]
|
|
81
|
-
}
|
|
68
|
+
foo: [{ age: 20 }, { age: 25 }, { age: 30 }, { age: 35 }, { age: 40 }],
|
|
69
|
+
};
|
|
82
70
|
|
|
83
71
|
search(JSON_DOCUMENT, "foo[?age > `30`]");
|
|
84
72
|
// OUTPUTS: [ { age: 35 }, { age: 40 } ]
|
|
@@ -86,91 +74,138 @@ search(JSON_DOCUMENT, "foo[?age > `30`]");
|
|
|
86
74
|
|
|
87
75
|
### `compile(expression: string): ExpressionNodeTree`
|
|
88
76
|
|
|
89
|
-
You can precompile all your expressions ready for use later on.
|
|
77
|
+
You can precompile all your expressions ready for use later on. The `compile`
|
|
90
78
|
function takes a JMESPath expression and returns an abstract syntax tree that
|
|
91
79
|
can be used by the TreeInterpreter function
|
|
92
80
|
|
|
93
81
|
```javascript
|
|
94
|
-
import { compile, TreeInterpreter } from
|
|
82
|
+
import { compile, TreeInterpreter } from "@jmespath-community/jmespath";
|
|
95
83
|
|
|
96
|
-
const ast = compile(
|
|
84
|
+
const ast = compile("foo.bar");
|
|
97
85
|
|
|
98
|
-
TreeInterpreter.search(ast, {foo: {bar:
|
|
86
|
+
TreeInterpreter.search(ast, { foo: { bar: "BAZ" } });
|
|
99
87
|
// RETURNS: "BAZ"
|
|
100
|
-
|
|
101
88
|
```
|
|
102
89
|
|
|
103
90
|
---
|
|
91
|
+
|
|
104
92
|
## EXTENSIONS TO ORIGINAL SPEC
|
|
105
93
|
|
|
106
94
|
1. ### Register you own custom functions
|
|
107
95
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Extend the list of built in JMESpath expressions with your own functions.
|
|
111
|
-
|
|
112
|
-
```javascript
|
|
113
|
-
import {search, registerFunction, TYPE_NUMBER} from '@jmespath-community/jmespath'
|
|
114
|
-
|
|
96
|
+
#### `registerFunction(functionName: string, customFunction: RuntimeFunction, signature: InputSignature[]): void`
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
// THROWS ERROR: Error: Unknown function: divide()
|
|
98
|
+
Extend the list of built in JMESpath expressions with your own functions.
|
|
118
99
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
(resolvedArgs) => { // CUSTOM FUNCTION
|
|
122
|
-
const [dividend, divisor] = resolvedArgs;
|
|
123
|
-
return dividend / divisor;
|
|
124
|
-
},
|
|
125
|
-
[{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER] }] //SIGNATURE
|
|
126
|
-
);
|
|
100
|
+
```javascript
|
|
101
|
+
import { search, registerFunction, TYPE_NUMBER } from "@jmespath-community/jmespath";
|
|
127
102
|
|
|
128
|
-
|
|
129
|
-
|
|
103
|
+
search({ foo: 60, bar: 10 }, "divide(foo, bar)");
|
|
104
|
+
// THROWS ERROR: Error: Unknown function: divide()
|
|
130
105
|
|
|
131
|
-
|
|
106
|
+
registerFunction(
|
|
107
|
+
"divide", // FUNCTION NAME
|
|
108
|
+
(resolvedArgs) => {
|
|
109
|
+
// CUSTOM FUNCTION
|
|
110
|
+
const [dividend, divisor] = resolvedArgs;
|
|
111
|
+
return dividend / divisor;
|
|
112
|
+
},
|
|
113
|
+
[{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER] }], //SIGNATURE
|
|
114
|
+
);
|
|
132
115
|
|
|
133
|
-
|
|
116
|
+
search({ foo: 60, bar: 10 }, "divide(foo, bar)");
|
|
117
|
+
// OUTPUTS: 6
|
|
118
|
+
```
|
|
134
119
|
|
|
120
|
+
Optional arguments are supported by setting `{..., optional: true}` in argument signatures
|
|
135
121
|
|
|
136
|
-
|
|
122
|
+
```javascript
|
|
123
|
+
registerFunction(
|
|
124
|
+
"divide",
|
|
125
|
+
(resolvedArgs) => {
|
|
126
|
+
const [dividend, divisor] = resolvedArgs;
|
|
127
|
+
return dividend / divisor ?? 1; //OPTIONAL DIVISOR THAT DEFAULTS TO 1
|
|
128
|
+
},
|
|
129
|
+
[{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER], optional: true }], //SIGNATURE
|
|
130
|
+
);
|
|
137
131
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const [dividend, divisor] = resolvedArgs;
|
|
142
|
-
return dividend / divisor ?? 1; //OPTIONAL DIVISOR THAT DEFAULTS TO 1
|
|
143
|
-
},
|
|
144
|
-
[{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER], optional: true }] //SIGNATURE
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
search({ foo: 60, bar: 10 }, 'divide(foo)');
|
|
148
|
-
// OUTPUTS: 60
|
|
149
|
-
|
|
150
|
-
```
|
|
132
|
+
search({ foo: 60, bar: 10 }, "divide(foo)");
|
|
133
|
+
// OUTPUTS: 60
|
|
134
|
+
```
|
|
151
135
|
|
|
152
136
|
2. ### Root value access with `$` symbol
|
|
153
137
|
|
|
154
138
|
```javascript
|
|
155
|
-
|
|
156
|
-
search({foo: {bar: 999}, baz: [1, 2, 3]}, '$.baz[*].[@, $.foo.bar]')
|
|
139
|
+
search({ foo: { bar: 999 }, baz: [1, 2, 3] }, "$.baz[*].[@, $.foo.bar]");
|
|
157
140
|
|
|
158
141
|
// OUTPUTS:
|
|
159
142
|
// [ [ 1, 999 ], [ 2, 999 ], [ 3, 999 ] ]
|
|
160
143
|
```
|
|
161
144
|
|
|
162
|
-
|
|
163
145
|
## More Resources
|
|
164
146
|
|
|
165
147
|
The example above only show a small amount of what
|
|
166
148
|
a JMESPath expression can do. If you want to take a
|
|
167
|
-
tour of the language, the
|
|
149
|
+
tour of the language, the _best_ place to go is the
|
|
168
150
|
[JMESPath Tutorial](http://jmespath.site/main#tutorial).
|
|
169
151
|
|
|
170
152
|
One of the best things about JMESPath is that it is
|
|
171
153
|
implemented in many different programming languages including
|
|
172
|
-
python, ruby, php, lua, etc.
|
|
154
|
+
python, ruby, php, lua, etc. To see a complete list of libraries,
|
|
173
155
|
check out the [JMESPath libraries page](http://jmespath.site/main#libraries).
|
|
174
156
|
|
|
175
157
|
And finally, the full JMESPath specification can be found
|
|
176
158
|
on the [JMESPath site](https://jmespath.site/main/#specification).
|
|
159
|
+
|
|
160
|
+
## Experimental Features
|
|
161
|
+
|
|
162
|
+
### Ternary Operations (`? :`)
|
|
163
|
+
|
|
164
|
+
**Supported Version:** 1.1.6
|
|
165
|
+
|
|
166
|
+
Experimental support for [ternary operations](https://github.com/jmespath-community/jmespath.spec/discussions/179) has been added, allowing for conditional logic within your JMESPath expressions. The syntax is `condition ? value_if_true : value_if_false`.
|
|
167
|
+
|
|
168
|
+
- **Condition:** The expression before the `?`. JMESPath determines truthiness based on the evaluated value:
|
|
169
|
+
- `true` is truthy.
|
|
170
|
+
- Any non-empty object, array, or string is truthy.
|
|
171
|
+
- Any non-zero number is truthy.
|
|
172
|
+
- `false`, `null`, empty objects `{}`, empty arrays `[]`, and empty strings `''` are falsy.
|
|
173
|
+
- **Value if true:** The expression between the `?` and `:`. This is evaluated and returned if the condition is truthy.
|
|
174
|
+
- **Value if false:** The expression after the `:`. This is evaluated and returned if the condition is falsy.
|
|
175
|
+
|
|
176
|
+
**Examples:**
|
|
177
|
+
|
|
178
|
+
Basic usage:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
search({ is_active: true, user: "Alice" }, "is_active ? user : 'Guest'");
|
|
182
|
+
// OUTPUTS: "Alice"
|
|
183
|
+
|
|
184
|
+
search({ is_active: false, user: "Bob" }, "is_active ? user : 'Guest'");
|
|
185
|
+
// OUTPUTS: "Guest"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Truthiness with different types:
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
search({ data: [1, 2] }, "data ? 'has_data' : 'no_data'");
|
|
192
|
+
// OUTPUTS: "has_data"
|
|
193
|
+
|
|
194
|
+
search({ data: [] }, "data ? 'has_data' : 'no_data'");
|
|
195
|
+
// OUTPUTS: "no_data"
|
|
196
|
+
|
|
197
|
+
search({ count: 5 }, "count ? 'count_present' : 'no_count'");
|
|
198
|
+
// OUTPUTS: "count_present"
|
|
199
|
+
|
|
200
|
+
search({ count: 0 }, "count ? 'count_present' : 'no_count'");
|
|
201
|
+
// OUTPUTS: "no_count"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Nested Ternaries:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
search({ a: true, b: false, val1: 10, val2: 20, val3: 30 }, "a ? (b ? val1 : val2) : val3");
|
|
208
|
+
// OUTPUTS: 20
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
This feature is currently experimental and its syntax or behavior might change in future releases.
|
package/dist/jmespath.esm.js
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
var Token;
|
|
2
|
+
(function (Token) {
|
|
3
|
+
Token["TOK_EOF"] = "EOF";
|
|
4
|
+
Token["TOK_VARIABLE"] = "Variable";
|
|
5
|
+
Token["TOK_ASSIGN"] = "Assign";
|
|
6
|
+
Token["TOK_UNQUOTEDIDENTIFIER"] = "UnquotedIdentifier";
|
|
7
|
+
Token["TOK_QUOTEDIDENTIFIER"] = "QuotedIdentifier";
|
|
8
|
+
Token["TOK_RBRACKET"] = "Rbracket";
|
|
9
|
+
Token["TOK_RPAREN"] = "Rparen";
|
|
10
|
+
Token["TOK_COMMA"] = "Comma";
|
|
11
|
+
Token["TOK_COLON"] = "Colon";
|
|
12
|
+
Token["TOK_RBRACE"] = "Rbrace";
|
|
13
|
+
Token["TOK_NUMBER"] = "Number";
|
|
14
|
+
Token["TOK_CURRENT"] = "Current";
|
|
15
|
+
Token["TOK_ROOT"] = "Root";
|
|
16
|
+
Token["TOK_EXPREF"] = "Expref";
|
|
17
|
+
Token["TOK_PIPE"] = "Pipe";
|
|
18
|
+
Token["TOK_OR"] = "Or";
|
|
19
|
+
Token["TOK_AND"] = "And";
|
|
20
|
+
Token["TOK_EQ"] = "EQ";
|
|
21
|
+
Token["TOK_GT"] = "GT";
|
|
22
|
+
Token["TOK_LT"] = "LT";
|
|
23
|
+
Token["TOK_GTE"] = "GTE";
|
|
24
|
+
Token["TOK_LTE"] = "LTE";
|
|
25
|
+
Token["TOK_NE"] = "NE";
|
|
26
|
+
Token["TOK_PLUS"] = "Plus";
|
|
27
|
+
Token["TOK_MINUS"] = "Minus";
|
|
28
|
+
Token["TOK_MULTIPLY"] = "Multiply";
|
|
29
|
+
Token["TOK_DIVIDE"] = "Divide";
|
|
30
|
+
Token["TOK_MODULO"] = "Modulo";
|
|
31
|
+
Token["TOK_DIV"] = "Div";
|
|
32
|
+
Token["TOK_FLATTEN"] = "Flatten";
|
|
33
|
+
Token["TOK_STAR"] = "Star";
|
|
34
|
+
Token["TOK_FILTER"] = "Filter";
|
|
35
|
+
Token["TOK_DOT"] = "Dot";
|
|
36
|
+
Token["TOK_NOT"] = "Not";
|
|
37
|
+
Token["TOK_LBRACE"] = "Lbrace";
|
|
38
|
+
Token["TOK_LBRACKET"] = "Lbracket";
|
|
39
|
+
Token["TOK_LPAREN"] = "Lparen";
|
|
40
|
+
Token["TOK_LITERAL"] = "Literal";
|
|
41
|
+
Token["TOK_QUESTION"] = "Question";
|
|
42
|
+
})(Token || (Token = {}));
|
|
43
|
+
|
|
1
44
|
const isObject = (obj) => {
|
|
2
45
|
return obj !== null && Object.prototype.toString.call(obj) === '[object Object]';
|
|
3
46
|
};
|
|
@@ -36,31 +79,32 @@ const strictDeepEqual = (first, second) => {
|
|
|
36
79
|
return false;
|
|
37
80
|
};
|
|
38
81
|
const isFalse = (obj) => {
|
|
82
|
+
// falsey values
|
|
83
|
+
if (obj === null || obj === undefined || obj === false) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (typeof obj === 'string') {
|
|
87
|
+
return obj === '';
|
|
88
|
+
}
|
|
39
89
|
if (typeof obj === 'object') {
|
|
40
|
-
if (obj === null) {
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
90
|
if (Array.isArray(obj)) {
|
|
44
91
|
return obj.length === 0;
|
|
45
92
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return false;
|
|
93
|
+
if (obj === null) {
|
|
94
|
+
return true;
|
|
49
95
|
}
|
|
50
|
-
|
|
96
|
+
// check if object is empty
|
|
97
|
+
return Object.keys(obj).length === 0;
|
|
51
98
|
}
|
|
52
|
-
return
|
|
99
|
+
return false;
|
|
53
100
|
};
|
|
54
101
|
const isAlpha = (ch) => {
|
|
55
|
-
// tslint:disable-next-line: strict-comparisons
|
|
56
102
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_';
|
|
57
103
|
};
|
|
58
104
|
const isNum = (ch) => {
|
|
59
|
-
// tslint:disable-next-line: strict-comparisons
|
|
60
105
|
return (ch >= '0' && ch <= '9') || ch === '-';
|
|
61
106
|
};
|
|
62
107
|
const isAlphaNum = (ch) => {
|
|
63
|
-
// tslint:disable-next-line: strict-comparisons
|
|
64
108
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch === '_';
|
|
65
109
|
};
|
|
66
110
|
const ensureInteger = (value) => {
|
|
@@ -201,48 +245,6 @@ const trimImpl = (subject, regExper, chars) => {
|
|
|
201
245
|
};
|
|
202
246
|
const upper = (subject) => subject.toUpperCase();
|
|
203
247
|
|
|
204
|
-
var Token;
|
|
205
|
-
(function (Token) {
|
|
206
|
-
Token["TOK_EOF"] = "EOF";
|
|
207
|
-
Token["TOK_VARIABLE"] = "Variable";
|
|
208
|
-
Token["TOK_ASSIGN"] = "Assign";
|
|
209
|
-
Token["TOK_UNQUOTEDIDENTIFIER"] = "UnquotedIdentifier";
|
|
210
|
-
Token["TOK_QUOTEDIDENTIFIER"] = "QuotedIdentifier";
|
|
211
|
-
Token["TOK_RBRACKET"] = "Rbracket";
|
|
212
|
-
Token["TOK_RPAREN"] = "Rparen";
|
|
213
|
-
Token["TOK_COMMA"] = "Comma";
|
|
214
|
-
Token["TOK_COLON"] = "Colon";
|
|
215
|
-
Token["TOK_RBRACE"] = "Rbrace";
|
|
216
|
-
Token["TOK_NUMBER"] = "Number";
|
|
217
|
-
Token["TOK_CURRENT"] = "Current";
|
|
218
|
-
Token["TOK_ROOT"] = "Root";
|
|
219
|
-
Token["TOK_EXPREF"] = "Expref";
|
|
220
|
-
Token["TOK_PIPE"] = "Pipe";
|
|
221
|
-
Token["TOK_OR"] = "Or";
|
|
222
|
-
Token["TOK_AND"] = "And";
|
|
223
|
-
Token["TOK_EQ"] = "EQ";
|
|
224
|
-
Token["TOK_GT"] = "GT";
|
|
225
|
-
Token["TOK_LT"] = "LT";
|
|
226
|
-
Token["TOK_GTE"] = "GTE";
|
|
227
|
-
Token["TOK_LTE"] = "LTE";
|
|
228
|
-
Token["TOK_NE"] = "NE";
|
|
229
|
-
Token["TOK_PLUS"] = "Plus";
|
|
230
|
-
Token["TOK_MINUS"] = "Minus";
|
|
231
|
-
Token["TOK_MULTIPLY"] = "Multiply";
|
|
232
|
-
Token["TOK_DIVIDE"] = "Divide";
|
|
233
|
-
Token["TOK_MODULO"] = "Modulo";
|
|
234
|
-
Token["TOK_DIV"] = "Div";
|
|
235
|
-
Token["TOK_FLATTEN"] = "Flatten";
|
|
236
|
-
Token["TOK_STAR"] = "Star";
|
|
237
|
-
Token["TOK_FILTER"] = "Filter";
|
|
238
|
-
Token["TOK_DOT"] = "Dot";
|
|
239
|
-
Token["TOK_NOT"] = "Not";
|
|
240
|
-
Token["TOK_LBRACE"] = "Lbrace";
|
|
241
|
-
Token["TOK_LBRACKET"] = "Lbracket";
|
|
242
|
-
Token["TOK_LPAREN"] = "Lparen";
|
|
243
|
-
Token["TOK_LITERAL"] = "Literal";
|
|
244
|
-
})(Token || (Token = {}));
|
|
245
|
-
|
|
246
248
|
const basicTokens = {
|
|
247
249
|
'(': Token.TOK_LPAREN,
|
|
248
250
|
')': Token.TOK_RPAREN,
|
|
@@ -256,6 +258,7 @@ const basicTokens = {
|
|
|
256
258
|
'}': Token.TOK_RBRACE,
|
|
257
259
|
'+': Token.TOK_PLUS,
|
|
258
260
|
'%': Token.TOK_MODULO,
|
|
261
|
+
'?': Token.TOK_QUESTION,
|
|
259
262
|
'\u2212': Token.TOK_MINUS,
|
|
260
263
|
'\u00d7': Token.TOK_MULTIPLY,
|
|
261
264
|
'\u00f7': Token.TOK_DIVIDE,
|
|
@@ -467,7 +470,11 @@ class StreamLexer {
|
|
|
467
470
|
this._current += 1;
|
|
468
471
|
if (this._current < stream.length && stream[this._current] === peek) {
|
|
469
472
|
this._current += 1;
|
|
470
|
-
return {
|
|
473
|
+
return {
|
|
474
|
+
start: start,
|
|
475
|
+
type: orElse,
|
|
476
|
+
value: stream.slice(start, this._current),
|
|
477
|
+
};
|
|
471
478
|
}
|
|
472
479
|
return { start: start, type: token, value: stream[start] };
|
|
473
480
|
}
|
|
@@ -573,8 +580,9 @@ const bindingPower = {
|
|
|
573
580
|
[Token.TOK_ROOT]: 0,
|
|
574
581
|
[Token.TOK_ASSIGN]: 1,
|
|
575
582
|
[Token.TOK_PIPE]: 1,
|
|
576
|
-
[Token.
|
|
577
|
-
[Token.
|
|
583
|
+
[Token.TOK_QUESTION]: 2,
|
|
584
|
+
[Token.TOK_OR]: 3,
|
|
585
|
+
[Token.TOK_AND]: 4,
|
|
578
586
|
[Token.TOK_EQ]: 5,
|
|
579
587
|
[Token.TOK_GT]: 5,
|
|
580
588
|
[Token.TOK_LT]: 5,
|
|
@@ -665,23 +673,33 @@ class TokenParser {
|
|
|
665
673
|
}
|
|
666
674
|
case Token.TOK_MINUS: {
|
|
667
675
|
const child = this.expression(bindingPower.Minus);
|
|
668
|
-
return {
|
|
676
|
+
return {
|
|
677
|
+
type: 'Unary',
|
|
678
|
+
operator: token.type,
|
|
679
|
+
operand: child,
|
|
680
|
+
};
|
|
669
681
|
}
|
|
670
682
|
case Token.TOK_PLUS: {
|
|
671
683
|
const child = this.expression(bindingPower.Plus);
|
|
672
|
-
return {
|
|
684
|
+
return {
|
|
685
|
+
type: 'Unary',
|
|
686
|
+
operator: token.type,
|
|
687
|
+
operand: child,
|
|
688
|
+
};
|
|
673
689
|
}
|
|
674
690
|
case Token.TOK_STAR: {
|
|
675
691
|
const left = { type: 'Identity' };
|
|
676
|
-
|
|
677
|
-
return { type: 'ValueProjection', left, right };
|
|
692
|
+
return { type: 'ValueProjection', left, right: this.parseProjectionRHS(bindingPower.Star) };
|
|
678
693
|
}
|
|
679
694
|
case Token.TOK_FILTER:
|
|
680
695
|
return this.led(token.type, { type: 'Identity' });
|
|
681
696
|
case Token.TOK_LBRACE:
|
|
682
697
|
return this.parseMultiselectHash();
|
|
683
698
|
case Token.TOK_FLATTEN: {
|
|
684
|
-
const left = {
|
|
699
|
+
const left = {
|
|
700
|
+
type: 'Flatten',
|
|
701
|
+
child: { type: 'Identity' },
|
|
702
|
+
};
|
|
685
703
|
const right = this.parseProjectionRHS(bindingPower.Flatten);
|
|
686
704
|
return { type: 'Projection', left, right };
|
|
687
705
|
}
|
|
@@ -711,12 +729,9 @@ class TokenParser {
|
|
|
711
729
|
return { type: 'ExpressionReference', child };
|
|
712
730
|
}
|
|
713
731
|
case Token.TOK_LPAREN: {
|
|
714
|
-
|
|
715
|
-
const args = [];
|
|
716
|
-
let expression = this.expression(0);
|
|
717
|
-
args.push(expression);
|
|
732
|
+
const expression = this.expression(0);
|
|
718
733
|
this.match(Token.TOK_RPAREN);
|
|
719
|
-
return
|
|
734
|
+
return expression;
|
|
720
735
|
}
|
|
721
736
|
default:
|
|
722
737
|
this.errorToken(token);
|
|
@@ -724,6 +739,17 @@ class TokenParser {
|
|
|
724
739
|
}
|
|
725
740
|
led(tokenName, left) {
|
|
726
741
|
switch (tokenName) {
|
|
742
|
+
case Token.TOK_QUESTION: {
|
|
743
|
+
const trueExpr = this.expression(0);
|
|
744
|
+
this.match(Token.TOK_COLON);
|
|
745
|
+
const falseExpr = this.expression(0);
|
|
746
|
+
return {
|
|
747
|
+
type: 'Ternary',
|
|
748
|
+
condition: left,
|
|
749
|
+
trueExpr,
|
|
750
|
+
falseExpr,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
727
753
|
case Token.TOK_DOT: {
|
|
728
754
|
const rbp = bindingPower.Dot;
|
|
729
755
|
if (this.lookahead(0) !== Token.TOK_STAR) {
|
|
@@ -833,7 +859,11 @@ class TokenParser {
|
|
|
833
859
|
return { type: 'Index', value };
|
|
834
860
|
}
|
|
835
861
|
projectIfSlice(left, right) {
|
|
836
|
-
const indexExpr = {
|
|
862
|
+
const indexExpr = {
|
|
863
|
+
type: 'IndexExpression',
|
|
864
|
+
left,
|
|
865
|
+
right,
|
|
866
|
+
};
|
|
837
867
|
if (right.type === 'Slice') {
|
|
838
868
|
return {
|
|
839
869
|
left: indexExpr,
|
|
@@ -1029,6 +1059,7 @@ class Text {
|
|
|
1029
1059
|
}
|
|
1030
1060
|
get codePoints() {
|
|
1031
1061
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1062
|
+
// biome-ignore lint: lint/style/noNonNullAssertion
|
|
1032
1063
|
const array = [...this._text].map(s => s.codePointAt(0));
|
|
1033
1064
|
return array;
|
|
1034
1065
|
}
|
|
@@ -1957,6 +1988,9 @@ class ScopeChain {
|
|
|
1957
1988
|
this.inner = undefined;
|
|
1958
1989
|
this.data = {};
|
|
1959
1990
|
}
|
|
1991
|
+
get currentScopeData() {
|
|
1992
|
+
return this.data;
|
|
1993
|
+
}
|
|
1960
1994
|
withScope(data) {
|
|
1961
1995
|
const outer = new ScopeChain();
|
|
1962
1996
|
outer.inner = this;
|
|
@@ -1964,11 +1998,8 @@ class ScopeChain {
|
|
|
1964
1998
|
return outer;
|
|
1965
1999
|
}
|
|
1966
2000
|
getValue(identifier) {
|
|
1967
|
-
if (
|
|
1968
|
-
|
|
1969
|
-
if (result !== null && result !== undefined) {
|
|
1970
|
-
return result;
|
|
1971
|
-
}
|
|
2001
|
+
if (Object.prototype.hasOwnProperty.call(this.data, identifier)) {
|
|
2002
|
+
return this.data[identifier];
|
|
1972
2003
|
}
|
|
1973
2004
|
if (this.inner) {
|
|
1974
2005
|
return this.inner.getValue(identifier);
|
|
@@ -1996,15 +2027,22 @@ class TreeInterpreter$1 {
|
|
|
1996
2027
|
return this.visit(node, value);
|
|
1997
2028
|
}
|
|
1998
2029
|
visit(node, value) {
|
|
1999
|
-
var _a, _b, _c
|
|
2030
|
+
var _a, _b, _c;
|
|
2000
2031
|
switch (node.type) {
|
|
2032
|
+
case 'Ternary': {
|
|
2033
|
+
const condition = this.visit(node.condition, value);
|
|
2034
|
+
if (!isFalse(condition)) {
|
|
2035
|
+
return this.visit(node.trueExpr, value);
|
|
2036
|
+
}
|
|
2037
|
+
return this.visit(node.falseExpr, value);
|
|
2038
|
+
}
|
|
2001
2039
|
case 'Field':
|
|
2002
2040
|
const identifier = node.name;
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
result = (_a = value[identifier]) !== null && _a !== void 0 ? _a : null;
|
|
2041
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
2042
|
+
return null;
|
|
2006
2043
|
}
|
|
2007
|
-
return
|
|
2044
|
+
// return the value of the field
|
|
2045
|
+
return (_a = value[identifier]) !== null && _a !== void 0 ? _a : null;
|
|
2008
2046
|
case 'LetExpression': {
|
|
2009
2047
|
const { bindings, expression } = node;
|
|
2010
2048
|
let scope = {};
|
|
@@ -2021,24 +2059,24 @@ class TreeInterpreter$1 {
|
|
|
2021
2059
|
}
|
|
2022
2060
|
case 'Variable': {
|
|
2023
2061
|
const variable = node.name;
|
|
2024
|
-
|
|
2025
|
-
|
|
2062
|
+
if (!this._scope.getValue(variable) &&
|
|
2063
|
+
!Object.prototype.hasOwnProperty.call(this._scope.currentScopeData, variable)) {
|
|
2026
2064
|
throw new Error(`Error referencing undefined variable ${variable}`);
|
|
2027
2065
|
}
|
|
2028
|
-
return
|
|
2066
|
+
return this._scope.getValue(variable);
|
|
2029
2067
|
}
|
|
2030
2068
|
case 'IndexExpression':
|
|
2031
2069
|
return this.visit(node.right, this.visit(node.left, value));
|
|
2032
2070
|
case 'Subexpression': {
|
|
2033
2071
|
const result = this.visit(node.left, value);
|
|
2034
|
-
return result != null ? (
|
|
2072
|
+
return result != null ? ((_b = this.visit(node.right, result)) !== null && _b !== void 0 ? _b : null) : null;
|
|
2035
2073
|
}
|
|
2036
2074
|
case 'Index': {
|
|
2037
2075
|
if (!Array.isArray(value)) {
|
|
2038
2076
|
return null;
|
|
2039
2077
|
}
|
|
2040
2078
|
const index = node.value < 0 ? value.length + node.value : node.value;
|
|
2041
|
-
return (
|
|
2079
|
+
return (_c = value[index]) !== null && _c !== void 0 ? _c : null;
|
|
2042
2080
|
}
|
|
2043
2081
|
case 'Slice': {
|
|
2044
2082
|
if (!Array.isArray(value) && typeof value !== 'string') {
|
|
@@ -2299,9 +2337,7 @@ function compile(expression, options) {
|
|
|
2299
2337
|
function tokenize(expression, options) {
|
|
2300
2338
|
return Lexer.tokenize(expression, options);
|
|
2301
2339
|
}
|
|
2302
|
-
const registerFunction = (functionName,
|
|
2303
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2304
|
-
customFunction, signature) => {
|
|
2340
|
+
const registerFunction = (functionName, customFunction, signature) => {
|
|
2305
2341
|
TreeInterpreterInstance.runtime.registerFunction(functionName, customFunction, signature);
|
|
2306
2342
|
};
|
|
2307
2343
|
function search(data, expression, options) {
|