@jbrowse/jexl 3.0.2 → 3.1.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 +11 -2
- package/dist/Expression.d.ts +16 -6
- package/dist/Expression.js +34 -8
- package/dist/Expression.js.map +1 -1
- package/dist/Jexl.d.ts +13 -12
- package/dist/Jexl.js +31 -30
- package/dist/Jexl.js.map +1 -1
- package/dist/Lexer.d.ts +9 -8
- package/dist/Lexer.js +58 -18
- package/dist/Lexer.js.map +1 -1
- package/dist/evaluator/Evaluator.d.ts +20 -34
- package/dist/evaluator/Evaluator.js +19 -64
- package/dist/evaluator/Evaluator.js.map +1 -1
- package/dist/evaluator/compile.d.ts +27 -0
- package/dist/evaluator/compile.js +261 -0
- package/dist/evaluator/compile.js.map +1 -0
- package/dist/grammar.d.ts +29 -9
- package/dist/grammar.js +19 -3
- package/dist/grammar.js.map +1 -1
- package/dist/parser/Parser.d.ts +5 -7
- package/dist/parser/Parser.js +17 -2
- package/dist/parser/Parser.js.map +1 -1
- package/dist/parser/handlers.d.ts +0 -6
- package/dist/parser/handlers.js +54 -46
- package/dist/parser/handlers.js.map +1 -1
- package/dist/parser/states.d.ts +2 -2
- package/dist/parser/states.js +0 -20
- package/dist/parser/states.js.map +1 -1
- package/dist/types.d.ts +18 -7
- package/esm/Expression.d.ts +16 -6
- package/esm/Expression.js +34 -8
- package/esm/Expression.js.map +1 -1
- package/esm/Jexl.d.ts +13 -12
- package/esm/Jexl.js +31 -30
- package/esm/Jexl.js.map +1 -1
- package/esm/Lexer.d.ts +9 -8
- package/esm/Lexer.js +58 -18
- package/esm/Lexer.js.map +1 -1
- package/esm/evaluator/Evaluator.d.ts +20 -34
- package/esm/evaluator/Evaluator.js +19 -31
- package/esm/evaluator/Evaluator.js.map +1 -1
- package/esm/evaluator/compile.d.ts +27 -0
- package/esm/evaluator/compile.js +258 -0
- package/esm/evaluator/compile.js.map +1 -0
- package/esm/grammar.d.ts +29 -9
- package/esm/grammar.js +18 -3
- package/esm/grammar.js.map +1 -1
- package/esm/parser/Parser.d.ts +5 -7
- package/esm/parser/Parser.js +17 -2
- package/esm/parser/Parser.js.map +1 -1
- package/esm/parser/handlers.d.ts +0 -6
- package/esm/parser/handlers.js +54 -45
- package/esm/parser/handlers.js.map +1 -1
- package/esm/parser/states.d.ts +2 -2
- package/esm/parser/states.js +0 -20
- package/esm/parser/states.js.map +1 -1
- package/esm/types.d.ts +18 -7
- package/package.json +25 -28
- package/src/Expression.ts +33 -13
- package/src/Jexl.ts +59 -21
- package/src/Lexer.ts +60 -27
- package/src/evaluator/Evaluator.ts +27 -44
- package/src/evaluator/compile.ts +310 -0
- package/src/grammar.ts +66 -17
- package/src/parser/Parser.ts +24 -20
- package/src/parser/handlers.ts +96 -73
- package/src/parser/states.ts +2 -22
- package/src/types.ts +22 -7
- package/CHANGELOG.md +0 -249
- package/dist/evaluator/handlers.d.ts +0 -112
- package/dist/evaluator/handlers.js +0 -208
- package/dist/evaluator/handlers.js.map +0 -1
- package/esm/evaluator/handlers.d.ts +0 -112
- package/esm/evaluator/handlers.js +0 -194
- package/esm/evaluator/handlers.js.map +0 -1
- package/src/evaluator/handlers.ts +0 -212
package/esm/parser/handlers.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Jexl
|
|
3
3
|
* Copyright 2020 Tom Shawver
|
|
4
4
|
*/
|
|
5
|
+
import { precedenceOf } from "../grammar.js";
|
|
5
6
|
/**
|
|
6
7
|
* Handles a subexpression that's used to define a transform argument's value.
|
|
7
8
|
* @param {{type: <string>}} ast The subexpression tree
|
|
@@ -17,10 +18,11 @@ export function argVal(ast) {
|
|
|
17
18
|
* initialized with an empty array.
|
|
18
19
|
*/
|
|
19
20
|
export function arrayStart() {
|
|
20
|
-
|
|
21
|
+
const node = {
|
|
21
22
|
type: 'ArrayLiteral',
|
|
22
23
|
value: []
|
|
23
|
-
}
|
|
24
|
+
};
|
|
25
|
+
this._placeAtCursor(node);
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* Handles a subexpression representing an element of an array literal.
|
|
@@ -39,7 +41,10 @@ export function arrayVal(ast) {
|
|
|
39
41
|
*/
|
|
40
42
|
export function binaryOp(token) {
|
|
41
43
|
if (token.value === '=') {
|
|
42
|
-
|
|
44
|
+
// `from` marks a member access such as `a.b`; only a bare name can be
|
|
45
|
+
// assigned to, since the Evaluator writes straight into the context
|
|
46
|
+
if (this._cursor?.type !== 'Identifier' ||
|
|
47
|
+
this._cursor.from) {
|
|
43
48
|
throw new Error('Left side of assignment must be a variable name');
|
|
44
49
|
}
|
|
45
50
|
const node = {
|
|
@@ -51,17 +56,23 @@ export function binaryOp(token) {
|
|
|
51
56
|
this._placeAtCursor(node);
|
|
52
57
|
return;
|
|
53
58
|
}
|
|
54
|
-
const
|
|
59
|
+
const tokenValue = token.value;
|
|
60
|
+
const precedence = precedenceOf(this._grammar.elements[tokenValue]);
|
|
55
61
|
let parent = this._cursor?._parent;
|
|
56
|
-
while (parent
|
|
57
|
-
parent
|
|
58
|
-
|
|
62
|
+
while (parent) {
|
|
63
|
+
const parentExpr = parent;
|
|
64
|
+
if (!parentExpr.operator) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (precedenceOf(this._grammar.elements[parentExpr.operator]) < precedence) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
59
70
|
this._cursor = parent;
|
|
60
71
|
parent = parent._parent;
|
|
61
72
|
}
|
|
62
73
|
const node = {
|
|
63
74
|
type: 'BinaryExpression',
|
|
64
|
-
operator:
|
|
75
|
+
operator: tokenValue,
|
|
65
76
|
left: this._cursor
|
|
66
77
|
};
|
|
67
78
|
this._setParent(this._cursor, node);
|
|
@@ -74,13 +85,15 @@ export function binaryOp(token) {
|
|
|
74
85
|
* AST.
|
|
75
86
|
*/
|
|
76
87
|
export function dot() {
|
|
77
|
-
const
|
|
88
|
+
const cursor = this._cursor;
|
|
89
|
+
// the dot continues an identifier chain unless the cursor is an operator
|
|
90
|
+
// still waiting on its right-hand side, in which case it opens a relative
|
|
91
|
+
// path instead — as in the leading dot of `list[.price > 5]`
|
|
78
92
|
this._nextIdentEncapsulate =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
(
|
|
82
|
-
|
|
83
|
-
this._nextIdentRelative = !this._cursor || !this._nextIdentEncapsulate;
|
|
93
|
+
!!cursor &&
|
|
94
|
+
cursor.type !== 'UnaryExpression' &&
|
|
95
|
+
(cursor.type !== 'BinaryExpression' || !!cursor.right);
|
|
96
|
+
this._nextIdentRelative = !this._nextIdentEncapsulate;
|
|
84
97
|
if (this._nextIdentRelative) {
|
|
85
98
|
this._relative = true;
|
|
86
99
|
}
|
|
@@ -91,12 +104,13 @@ export function dot() {
|
|
|
91
104
|
* @param {{type: <string>}} ast The subexpression tree
|
|
92
105
|
*/
|
|
93
106
|
export function filter(ast) {
|
|
94
|
-
|
|
107
|
+
const node = {
|
|
95
108
|
type: 'FilterExpression',
|
|
96
109
|
expr: ast,
|
|
97
110
|
relative: this._subParser.isRelative(),
|
|
98
111
|
subject: this._cursor
|
|
99
|
-
}
|
|
112
|
+
};
|
|
113
|
+
this._placeBeforeCursor(node);
|
|
100
114
|
}
|
|
101
115
|
/**
|
|
102
116
|
* Handles identifier tokens when used to indicate the name of a function to
|
|
@@ -104,12 +118,13 @@ export function filter(ast) {
|
|
|
104
118
|
* @param {{type: <string>}} token A token object
|
|
105
119
|
*/
|
|
106
120
|
export function functionCall() {
|
|
107
|
-
|
|
121
|
+
const node = {
|
|
108
122
|
type: 'FunctionCall',
|
|
109
123
|
name: this._cursor.value,
|
|
110
124
|
args: [],
|
|
111
125
|
pool: 'functions'
|
|
112
|
-
}
|
|
126
|
+
};
|
|
127
|
+
this._placeBeforeCursor(node);
|
|
113
128
|
}
|
|
114
129
|
/**
|
|
115
130
|
* Handles identifier tokens by adding them as a new node in the AST.
|
|
@@ -139,10 +154,11 @@ export function identifier(token) {
|
|
|
139
154
|
* @param {{type: <string>}} token A token object
|
|
140
155
|
*/
|
|
141
156
|
export function literal(token) {
|
|
142
|
-
|
|
157
|
+
const node = {
|
|
143
158
|
type: 'Literal',
|
|
144
159
|
value: token.value
|
|
145
|
-
}
|
|
160
|
+
};
|
|
161
|
+
this._placeAtCursor(node);
|
|
146
162
|
}
|
|
147
163
|
/**
|
|
148
164
|
* Handles template string tokens by parsing their interpolation expressions
|
|
@@ -151,7 +167,8 @@ export function literal(token) {
|
|
|
151
167
|
*/
|
|
152
168
|
export function templateString(token) {
|
|
153
169
|
const parts = [];
|
|
154
|
-
|
|
170
|
+
const tokenParts = Array.isArray(token.value) ? token.value : [];
|
|
171
|
+
for (const part of tokenParts) {
|
|
155
172
|
if (part.type === 'static') {
|
|
156
173
|
parts.push({
|
|
157
174
|
type: 'static',
|
|
@@ -160,7 +177,8 @@ export function templateString(token) {
|
|
|
160
177
|
}
|
|
161
178
|
else {
|
|
162
179
|
const subTokens = this._lexer.tokenize(part.value);
|
|
163
|
-
const
|
|
180
|
+
const ParserClass = this.constructor;
|
|
181
|
+
const subParser = new ParserClass(this._grammar, this._lexer);
|
|
164
182
|
subParser.addTokens(subTokens);
|
|
165
183
|
const subAst = subParser.complete();
|
|
166
184
|
if (!subAst) {
|
|
@@ -172,10 +190,11 @@ export function templateString(token) {
|
|
|
172
190
|
});
|
|
173
191
|
}
|
|
174
192
|
}
|
|
175
|
-
|
|
193
|
+
const node = {
|
|
176
194
|
type: 'TemplateLiteral',
|
|
177
|
-
parts
|
|
178
|
-
}
|
|
195
|
+
parts
|
|
196
|
+
};
|
|
197
|
+
this._placeAtCursor(node);
|
|
179
198
|
}
|
|
180
199
|
/**
|
|
181
200
|
* Queues a new object literal key to be written once a value is collected.
|
|
@@ -189,10 +208,11 @@ export function objKey(token) {
|
|
|
189
208
|
* initialized with an empty object.
|
|
190
209
|
*/
|
|
191
210
|
export function objStart() {
|
|
192
|
-
|
|
211
|
+
const node = {
|
|
193
212
|
type: 'ObjectLiteral',
|
|
194
213
|
value: {}
|
|
195
|
-
}
|
|
214
|
+
};
|
|
215
|
+
this._placeAtCursor(node);
|
|
196
216
|
}
|
|
197
217
|
/**
|
|
198
218
|
* Handles an object value by adding its AST to the queued key on the object
|
|
@@ -217,7 +237,7 @@ export function subExpression(ast) {
|
|
|
217
237
|
*/
|
|
218
238
|
export function ternaryEnd(ast) {
|
|
219
239
|
;
|
|
220
|
-
this._cursor.alternate = ast;
|
|
240
|
+
this._cursor.alternate = ast ?? undefined;
|
|
221
241
|
}
|
|
222
242
|
/**
|
|
223
243
|
* Handles a completed consequent subexpression of a ternary operator.
|
|
@@ -225,7 +245,7 @@ export function ternaryEnd(ast) {
|
|
|
225
245
|
*/
|
|
226
246
|
export function ternaryMid(ast) {
|
|
227
247
|
;
|
|
228
|
-
this._cursor.consequent = ast;
|
|
248
|
+
this._cursor.consequent = ast ?? undefined;
|
|
229
249
|
}
|
|
230
250
|
/**
|
|
231
251
|
* Handles the start of a new ternary expression by encapsulating the entire
|
|
@@ -233,35 +253,24 @@ export function ternaryMid(ast) {
|
|
|
233
253
|
* test element.
|
|
234
254
|
*/
|
|
235
255
|
export function ternaryStart() {
|
|
236
|
-
|
|
256
|
+
const node = {
|
|
237
257
|
type: 'ConditionalExpression',
|
|
238
258
|
test: this._tree
|
|
239
259
|
};
|
|
260
|
+
this._tree = node;
|
|
240
261
|
this._cursor = this._tree;
|
|
241
262
|
}
|
|
242
|
-
/**
|
|
243
|
-
* Handles identifier tokens when used to indicate the name of a transform to
|
|
244
|
-
* be applied.
|
|
245
|
-
* @param {{type: <string>}} token A token object
|
|
246
|
-
*/
|
|
247
|
-
export function transform(token) {
|
|
248
|
-
this._placeBeforeCursor({
|
|
249
|
-
type: 'FunctionCall',
|
|
250
|
-
name: token.value,
|
|
251
|
-
args: [this._cursor],
|
|
252
|
-
pool: 'transforms'
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
263
|
/**
|
|
256
264
|
* Handles token of type 'unaryOp', indicating that the operation has only
|
|
257
265
|
* one input: a right side.
|
|
258
266
|
* @param {{type: <string>}} token A token object
|
|
259
267
|
*/
|
|
260
268
|
export function unaryOp(token) {
|
|
261
|
-
|
|
269
|
+
const node = {
|
|
262
270
|
type: 'UnaryExpression',
|
|
263
271
|
operator: token.value
|
|
264
|
-
}
|
|
272
|
+
};
|
|
273
|
+
this._placeAtCursor(node);
|
|
265
274
|
}
|
|
266
275
|
/**
|
|
267
276
|
* Handles semicolon separator between expressions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/parser/handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/parser/handlers.ts"],"names":[],"mappings":"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"}
|
package/esm/parser/states.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { AstNode } from '../types.ts';
|
|
1
|
+
import type { AstNode, Token } from '../types.ts';
|
|
2
2
|
import type Parser from './Parser.ts';
|
|
3
3
|
interface TokenTypeOptions {
|
|
4
4
|
toState?: string;
|
|
5
|
-
handler?: (this: Parser,
|
|
5
|
+
handler?: (this: Parser, token: Token) => void;
|
|
6
6
|
}
|
|
7
7
|
interface StateDefinition {
|
|
8
8
|
tokenTypes?: Record<string, TokenTypeOptions>;
|
package/esm/parser/states.js
CHANGED
|
@@ -51,7 +51,6 @@ export const states = {
|
|
|
51
51
|
expectBinOp: {
|
|
52
52
|
tokenTypes: {
|
|
53
53
|
binaryOp: { toState: 'expectOperand' },
|
|
54
|
-
pipe: { toState: 'expectTransform' },
|
|
55
54
|
dot: { toState: 'traverse' },
|
|
56
55
|
openBracket: { toState: 'filter' },
|
|
57
56
|
question: { toState: 'ternaryMid', handler: h.ternaryStart },
|
|
@@ -59,11 +58,6 @@ export const states = {
|
|
|
59
58
|
},
|
|
60
59
|
completable: true
|
|
61
60
|
},
|
|
62
|
-
expectTransform: {
|
|
63
|
-
tokenTypes: {
|
|
64
|
-
identifier: { toState: 'postTransform', handler: h.transform }
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
61
|
expectObjKey: {
|
|
68
62
|
tokenTypes: {
|
|
69
63
|
literal: { toState: 'expectKeyValSep', handler: h.objKey },
|
|
@@ -76,24 +70,11 @@ export const states = {
|
|
|
76
70
|
colon: { toState: 'objVal' }
|
|
77
71
|
}
|
|
78
72
|
},
|
|
79
|
-
postTransform: {
|
|
80
|
-
tokenTypes: {
|
|
81
|
-
openParen: { toState: 'argVal' },
|
|
82
|
-
binaryOp: { toState: 'expectOperand' },
|
|
83
|
-
dot: { toState: 'traverse' },
|
|
84
|
-
openBracket: { toState: 'filter' },
|
|
85
|
-
pipe: { toState: 'expectTransform' },
|
|
86
|
-
question: { toState: 'ternaryMid', handler: h.ternaryStart },
|
|
87
|
-
semicolon: { handler: h.semicolon }
|
|
88
|
-
},
|
|
89
|
-
completable: true
|
|
90
|
-
},
|
|
91
73
|
postArgs: {
|
|
92
74
|
tokenTypes: {
|
|
93
75
|
binaryOp: { toState: 'expectOperand' },
|
|
94
76
|
dot: { toState: 'traverse' },
|
|
95
77
|
openBracket: { toState: 'filter' },
|
|
96
|
-
pipe: { toState: 'expectTransform' },
|
|
97
78
|
question: { toState: 'ternaryMid', handler: h.ternaryStart },
|
|
98
79
|
semicolon: { handler: h.semicolon }
|
|
99
80
|
},
|
|
@@ -105,7 +86,6 @@ export const states = {
|
|
|
105
86
|
dot: { toState: 'traverse' },
|
|
106
87
|
openBracket: { toState: 'filter' },
|
|
107
88
|
openParen: { toState: 'argVal', handler: h.functionCall },
|
|
108
|
-
pipe: { toState: 'expectTransform' },
|
|
109
89
|
question: { toState: 'ternaryMid', handler: h.ternaryStart },
|
|
110
90
|
semicolon: { handler: h.semicolon }
|
|
111
91
|
},
|
package/esm/parser/states.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/parser/states.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,CAAC,MAAM,eAAe,CAAA;AAmBlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW;IAC5B,aAAa,EAAE;QACb,UAAU,EAAE;YACV,OAAO,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE;YACnC,cAAc,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE;YAC1C,UAAU,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE;YACrC,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE;YACvC,QAAQ,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;YAC1D,GAAG,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE;YAC5B,WAAW,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,UAAU,EAAE;SAC5D;KACF;IACD,WAAW,EAAE;QACX,UAAU,EAAE;YACV,QAAQ,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE;YACtC,
|
|
1
|
+
{"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/parser/states.ts"],"names":[],"mappings":"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"}
|
package/esm/types.d.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
export interface TemplatePart {
|
|
2
|
+
type: 'static' | 'interpolation';
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export type JexlValue = string | number | boolean | null | undefined | JexlValue[] | {
|
|
6
|
+
[key: string]: JexlValue;
|
|
7
|
+
};
|
|
1
8
|
export interface Token {
|
|
2
9
|
type: string;
|
|
3
|
-
value:
|
|
10
|
+
value: string | number | boolean | TemplatePart[];
|
|
4
11
|
raw: string;
|
|
5
12
|
}
|
|
6
13
|
export interface AstNode {
|
|
7
14
|
type: string;
|
|
8
15
|
_parent?: AstNode;
|
|
16
|
+
right?: AstNode;
|
|
9
17
|
}
|
|
10
18
|
export interface Literal extends AstNode {
|
|
11
19
|
type: 'Literal';
|
|
@@ -36,18 +44,22 @@ export interface ObjectLiteral extends AstNode {
|
|
|
36
44
|
type: 'ObjectLiteral';
|
|
37
45
|
value: Record<string, AstNode>;
|
|
38
46
|
}
|
|
47
|
+
export type TemplateLiteralPart = {
|
|
48
|
+
type: 'static';
|
|
49
|
+
value: string;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'expression';
|
|
52
|
+
value: AstNode;
|
|
53
|
+
};
|
|
39
54
|
export interface TemplateLiteral extends AstNode {
|
|
40
55
|
type: 'TemplateLiteral';
|
|
41
|
-
parts:
|
|
42
|
-
type: 'static' | 'expression';
|
|
43
|
-
value: string | AstNode;
|
|
44
|
-
}[];
|
|
56
|
+
parts: TemplateLiteralPart[];
|
|
45
57
|
}
|
|
46
58
|
export interface FunctionCall extends AstNode {
|
|
47
59
|
type: 'FunctionCall';
|
|
48
60
|
name: string;
|
|
49
61
|
args: AstNode[];
|
|
50
|
-
pool: 'functions'
|
|
62
|
+
pool: 'functions';
|
|
51
63
|
}
|
|
52
64
|
export interface FilterExpression extends AstNode {
|
|
53
65
|
type: 'FilterExpression';
|
|
@@ -69,7 +81,6 @@ export interface AssignmentExpression extends AstNode {
|
|
|
69
81
|
type: 'AssignmentExpression';
|
|
70
82
|
operator: '=';
|
|
71
83
|
left: Identifier;
|
|
72
|
-
right: AstNode;
|
|
73
84
|
}
|
|
74
85
|
export type AstNodeUnion = Literal | Identifier | BinaryExpression | UnaryExpression | ArrayLiteral | ObjectLiteral | TemplateLiteral | FunctionCall | FilterExpression | ConditionalExpression | SequenceExpression | AssignmentExpression;
|
|
75
86
|
export type NodeByType<T extends AstNodeUnion['type']> = Extract<AstNodeUnion, {
|
package/package.json
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/jexl",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "A fork of the jexl lang for jbrowse",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
6
|
+
"main": "dist/index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
"import":
|
|
9
|
-
|
|
10
|
-
},
|
|
11
|
-
"require": {
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
}
|
|
8
|
+
"import": "./esm/index.js",
|
|
9
|
+
"require": "./dist/index.js"
|
|
14
10
|
},
|
|
15
11
|
"files": [
|
|
16
12
|
"dist",
|
|
@@ -18,20 +14,24 @@
|
|
|
18
14
|
"src"
|
|
19
15
|
],
|
|
20
16
|
"scripts": {
|
|
21
|
-
"format": "prettier --write .",
|
|
22
|
-
"lint": "eslint --report-unused-disable-directives --max-warnings 0",
|
|
23
17
|
"test": "vitest",
|
|
18
|
+
"coverage": "pnpm test --coverage",
|
|
24
19
|
"clean": "rimraf dist esm",
|
|
20
|
+
"format": "prettier --write .",
|
|
21
|
+
"format:check": "prettier --check .",
|
|
22
|
+
"lint": "eslint --report-unused-disable-directives --max-warnings 0",
|
|
23
|
+
"typecheck": "tsc --noEmit -p tsconfig.lint.json",
|
|
24
|
+
"prebuild": "pnpm clean",
|
|
25
25
|
"build:esm": "tsc --outDir esm",
|
|
26
|
-
"build:es5": "tsc --module commonjs --outDir dist",
|
|
27
|
-
"build": "yarn build:esm && yarn build:es5",
|
|
28
|
-
"prebuild": "yarn clean",
|
|
26
|
+
"build:es5": "tsc --module commonjs --moduleResolution bundler --outDir dist",
|
|
29
27
|
"postbuild:es5": "echo '{\"type\": \"commonjs\"}' > dist/package.json",
|
|
30
|
-
"preversion": "
|
|
28
|
+
"preversion": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm test --run && pnpm build",
|
|
29
|
+
"postversion": "git push --follow-tags",
|
|
30
|
+
"build": "pnpm build:esm && pnpm build:es5"
|
|
31
31
|
},
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/GMOD/jexl.git"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [
|
|
37
37
|
"JSON",
|
|
@@ -46,22 +46,19 @@
|
|
|
46
46
|
],
|
|
47
47
|
"author": "Tom Shawver <tom@frosteddesign.com>",
|
|
48
48
|
"license": "MIT",
|
|
49
|
-
"
|
|
50
|
-
"url": "https://github.com/TomFrost/jexl/issues"
|
|
51
|
-
},
|
|
52
|
-
"homepage": "https://github.com/TomFrost/jexl",
|
|
49
|
+
"packageManager": "pnpm@11.20.0",
|
|
53
50
|
"devDependencies": {
|
|
54
51
|
"@eslint/js": "^10.0.1",
|
|
55
|
-
"@types/node": "^25.
|
|
56
|
-
"@vitest/coverage-v8": "^4.
|
|
57
|
-
"eslint": "^10.
|
|
58
|
-
"eslint-plugin-import": "^
|
|
59
|
-
"eslint-plugin-unicorn": "^
|
|
60
|
-
"prettier": "^3.8.
|
|
52
|
+
"@types/node": "^25.6.0",
|
|
53
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
54
|
+
"eslint": "^10.2.1",
|
|
55
|
+
"eslint-plugin-import-x": "^4.16.2",
|
|
56
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
57
|
+
"prettier": "^3.8.3",
|
|
61
58
|
"rimraf": "^6.1.3",
|
|
62
|
-
"typescript": "^
|
|
63
|
-
"typescript-eslint": "^8.
|
|
64
|
-
"vitest": "^4.
|
|
59
|
+
"typescript": "^6.0.3",
|
|
60
|
+
"typescript-eslint": "^8.59.0",
|
|
61
|
+
"vitest": "^4.1.5"
|
|
65
62
|
},
|
|
66
63
|
"publishConfig": {
|
|
67
64
|
"access": "public"
|
package/src/Expression.ts
CHANGED
|
@@ -5,24 +5,35 @@
|
|
|
5
5
|
|
|
6
6
|
import Lexer from './Lexer.ts'
|
|
7
7
|
import Evaluator from './evaluator/Evaluator.ts'
|
|
8
|
+
import { compileAst } from './evaluator/compile.ts'
|
|
8
9
|
import Parser from './parser/Parser.ts'
|
|
9
10
|
|
|
11
|
+
import type { CompiledNode } from './evaluator/compile.ts'
|
|
12
|
+
import type { Grammar } from './grammar.ts'
|
|
10
13
|
import type { AstNode } from './types.ts'
|
|
11
14
|
|
|
12
|
-
interface Grammar {
|
|
13
|
-
elements: Record<string, any>
|
|
14
|
-
[key: string]: any
|
|
15
|
-
}
|
|
16
|
-
|
|
17
15
|
class Expression {
|
|
18
16
|
_grammar: Grammar
|
|
19
17
|
_exprStr: string
|
|
20
18
|
_ast: AstNode | null
|
|
19
|
+
_lexer: Lexer
|
|
20
|
+
_compiled = false
|
|
21
|
+
// the AST lowered to closures, so repeated eval() calls skip node dispatch
|
|
22
|
+
_fn: CompiledNode | null = null
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
/**
|
|
25
|
+
* @param {{}} grammar The grammar to compile and evaluate against
|
|
26
|
+
* @param {string} exprStr The Jexl expression string
|
|
27
|
+
* @param {Lexer} [lexer] A Lexer to reuse. Lexers memoize the regex used to
|
|
28
|
+
* split expressions, which is expensive to build, so passing a shared
|
|
29
|
+
* one avoids paying that cost per expression. It must be invalidated
|
|
30
|
+
* via {@link Lexer#_clearCache} whenever the grammar's elements change.
|
|
31
|
+
*/
|
|
32
|
+
constructor(grammar: Grammar, exprStr: string, lexer?: Lexer) {
|
|
23
33
|
this._grammar = grammar
|
|
24
34
|
this._exprStr = exprStr
|
|
25
35
|
this._ast = null
|
|
36
|
+
this._lexer = lexer ?? new Lexer(grammar)
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
/**
|
|
@@ -32,11 +43,13 @@ class Expression {
|
|
|
32
43
|
* @returns {Expression} this Expression instance, for convenience
|
|
33
44
|
*/
|
|
34
45
|
compile() {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const tokens = lexer.tokenize(this._exprStr)
|
|
46
|
+
const parser = new Parser(this._grammar, this._lexer)
|
|
47
|
+
const tokens = this._lexer.tokenize(this._exprStr)
|
|
38
48
|
parser.addTokens(tokens)
|
|
39
49
|
this._ast = parser.complete()
|
|
50
|
+
// lower the tree to closures once, here, so that eval() is just a call
|
|
51
|
+
this._fn = this._ast ? compileAst(this._ast, this._grammar) : null
|
|
52
|
+
this._compiled = true
|
|
40
53
|
return this
|
|
41
54
|
}
|
|
42
55
|
|
|
@@ -48,13 +61,20 @@ class Expression {
|
|
|
48
61
|
* @throws {*} on error
|
|
49
62
|
*/
|
|
50
63
|
eval(context = {}) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
if (!this._compiled) {
|
|
65
|
+
this.compile()
|
|
66
|
+
}
|
|
67
|
+
// an expression with no tokens (empty or whitespace-only) has no AST
|
|
68
|
+
if (!this._fn) {
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
return this._fn(new Evaluator(this._grammar, context))
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
_getAst() {
|
|
57
|
-
|
|
75
|
+
// tracked separately from _ast, which is legitimately null for an
|
|
76
|
+
// expression with no tokens and would otherwise recompile on every eval
|
|
77
|
+
if (!this._compiled) {
|
|
58
78
|
this.compile()
|
|
59
79
|
}
|
|
60
80
|
return this._ast
|