@jbrowse/jexl 4.0.1 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -20
- package/dist/Expression.d.ts +16 -0
- package/dist/Expression.js +28 -4
- package/dist/Expression.js.map +1 -1
- package/dist/Jexl.d.ts +20 -8
- package/dist/Jexl.js +34 -15
- package/dist/Jexl.js.map +1 -1
- package/dist/Lexer.d.ts +4 -3
- package/dist/Lexer.js +29 -21
- package/dist/Lexer.js.map +1 -1
- package/dist/analyze.d.ts +84 -0
- package/dist/analyze.js +259 -0
- package/dist/analyze.js.map +1 -0
- package/dist/check.d.ts +109 -0
- package/dist/check.js +1267 -0
- package/dist/check.js.map +1 -0
- package/dist/collections.d.ts +3 -0
- package/dist/collections.js +140 -0
- package/dist/collections.js.map +1 -0
- package/dist/conditions.d.ts +62 -0
- package/dist/conditions.js +207 -0
- package/dist/conditions.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/evaluator/compile.d.ts +10 -7
- package/dist/evaluator/compile.js +292 -99
- package/dist/evaluator/compile.js.map +1 -1
- package/dist/grammar.d.ts +22 -9
- package/dist/grammar.js +83 -55
- package/dist/grammar.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/operators.d.ts +34 -0
- package/dist/operators.js +131 -0
- package/dist/operators.js.map +1 -0
- package/dist/parser/Parser.d.ts +48 -91
- package/dist/parser/Parser.js +347 -221
- package/dist/parser/Parser.js.map +1 -1
- package/dist/types.d.ts +11 -5
- package/esm/Expression.d.ts +16 -0
- package/esm/Expression.js +28 -4
- package/esm/Expression.js.map +1 -1
- package/esm/Jexl.d.ts +20 -8
- package/esm/Jexl.js +34 -15
- package/esm/Jexl.js.map +1 -1
- package/esm/Lexer.d.ts +4 -3
- package/esm/Lexer.js +29 -21
- package/esm/Lexer.js.map +1 -1
- package/esm/analyze.d.ts +84 -0
- package/esm/analyze.js +256 -0
- package/esm/analyze.js.map +1 -0
- package/esm/check.d.ts +109 -0
- package/esm/check.js +1260 -0
- package/esm/check.js.map +1 -0
- package/esm/collections.d.ts +3 -0
- package/esm/collections.js +137 -0
- package/esm/collections.js.map +1 -0
- package/esm/conditions.d.ts +62 -0
- package/esm/conditions.js +200 -0
- package/esm/conditions.js.map +1 -0
- package/esm/errors.d.ts +5 -0
- package/esm/errors.js +14 -0
- package/esm/errors.js.map +1 -0
- package/esm/evaluator/compile.d.ts +10 -7
- package/esm/evaluator/compile.js +292 -99
- package/esm/evaluator/compile.js.map +1 -1
- package/esm/grammar.d.ts +22 -9
- package/esm/grammar.js +79 -53
- package/esm/grammar.js.map +1 -1
- package/esm/index.d.ts +9 -0
- package/esm/index.js +4 -0
- package/esm/index.js.map +1 -1
- package/esm/operators.d.ts +34 -0
- package/esm/operators.js +123 -0
- package/esm/operators.js.map +1 -0
- package/esm/parser/Parser.d.ts +48 -91
- package/esm/parser/Parser.js +349 -192
- package/esm/parser/Parser.js.map +1 -1
- package/esm/types.d.ts +11 -5
- package/package.json +1 -1
- package/src/Expression.ts +34 -4
- package/src/Jexl.ts +52 -16
- package/src/Lexer.ts +32 -21
- package/src/analyze.ts +378 -0
- package/src/check.ts +1682 -0
- package/src/collections.ts +148 -0
- package/src/conditions.ts +262 -0
- package/src/errors.ts +15 -0
- package/src/evaluator/compile.ts +351 -102
- package/src/grammar.ts +137 -55
- package/src/index.ts +15 -0
- package/src/operators.ts +151 -0
- package/src/parser/Parser.ts +389 -199
- package/src/types.ts +13 -3
- package/dist/parser/handlers.d.ts +0 -115
- package/dist/parser/handlers.js +0 -352
- package/dist/parser/handlers.js.map +0 -1
- package/dist/parser/states.d.ts +0 -47
- package/dist/parser/states.js +0 -176
- package/dist/parser/states.js.map +0 -1
- package/esm/parser/handlers.d.ts +0 -115
- package/esm/parser/handlers.js +0 -328
- package/esm/parser/handlers.js.map +0 -1
- package/esm/parser/states.d.ts +0 -47
- package/esm/parser/states.js +0 -140
- package/esm/parser/states.js.map +0 -1
- package/src/parser/handlers.ts +0 -372
- package/src/parser/states.ts +0 -159
package/dist/types.d.ts
CHANGED
|
@@ -4,20 +4,21 @@ export interface TemplatePart {
|
|
|
4
4
|
}
|
|
5
5
|
export type JexlValue = string | number | boolean | null | undefined | JexlValue[] | {
|
|
6
6
|
[key: string]: JexlValue;
|
|
7
|
-
};
|
|
7
|
+
} | JexlFunction;
|
|
8
|
+
/** What a lambda evaluates to, for a registered function to call. */
|
|
9
|
+
export type JexlFunction = (...args: JexlValue[]) => JexlValue;
|
|
8
10
|
export interface Token {
|
|
9
11
|
type: string;
|
|
10
|
-
value: string | number | boolean | TemplatePart[];
|
|
12
|
+
value: string | number | boolean | null | TemplatePart[];
|
|
11
13
|
raw: string;
|
|
12
14
|
}
|
|
13
15
|
export interface AstNode {
|
|
14
16
|
type: string;
|
|
15
|
-
_parent?: AstNode;
|
|
16
17
|
right?: AstNode;
|
|
17
18
|
}
|
|
18
19
|
export interface Literal extends AstNode {
|
|
19
20
|
type: 'Literal';
|
|
20
|
-
value: string | number | boolean;
|
|
21
|
+
value: string | number | boolean | null;
|
|
21
22
|
}
|
|
22
23
|
export interface Identifier extends AstNode {
|
|
23
24
|
type: 'Identifier';
|
|
@@ -79,7 +80,12 @@ export interface AssignmentExpression extends AstNode {
|
|
|
79
80
|
operator: '=';
|
|
80
81
|
left: Identifier;
|
|
81
82
|
}
|
|
82
|
-
export
|
|
83
|
+
export interface Lambda extends AstNode {
|
|
84
|
+
type: 'Lambda';
|
|
85
|
+
params: string[];
|
|
86
|
+
body: AstNode;
|
|
87
|
+
}
|
|
88
|
+
export type AstNodeUnion = Literal | Identifier | BinaryExpression | UnaryExpression | ArrayLiteral | ObjectLiteral | TemplateLiteral | FunctionCall | FilterExpression | ConditionalExpression | SequenceExpression | AssignmentExpression | Lambda;
|
|
83
89
|
export type NodeByType<T extends AstNodeUnion['type']> = Extract<AstNodeUnion, {
|
|
84
90
|
type: T;
|
|
85
91
|
}>;
|
package/esm/Expression.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Lexer from './Lexer.ts';
|
|
2
|
+
import type { AnalyzeOptions } from './analyze.ts';
|
|
3
|
+
import type { CheckOptions } from './check.ts';
|
|
2
4
|
import type { CompiledNode } from './evaluator/compile.ts';
|
|
3
5
|
import type { Grammar } from './grammar.ts';
|
|
4
6
|
import type { AstNode } from './types.ts';
|
|
@@ -36,5 +38,19 @@ declare class Expression {
|
|
|
36
38
|
* @throws {*} on error
|
|
37
39
|
*/
|
|
38
40
|
eval(context?: {}): import("./types.ts").JexlValue;
|
|
41
|
+
/** The expression text this Expression was made from. */
|
|
42
|
+
get source(): string;
|
|
43
|
+
/** The parsed tree, or null for an expression with no tokens. */
|
|
44
|
+
get ast(): AstNode | null;
|
|
45
|
+
/**
|
|
46
|
+
* Lists what the expression reads from its context, without evaluating it.
|
|
47
|
+
* See {@link analyze}.
|
|
48
|
+
*/
|
|
49
|
+
analyze(options?: AnalyzeOptions): import("./analyze.ts").Analysis;
|
|
50
|
+
/**
|
|
51
|
+
* Checks the expression against a host's fields and functions, without
|
|
52
|
+
* evaluating it. See {@link check}.
|
|
53
|
+
*/
|
|
54
|
+
check(options?: CheckOptions): import("./check.ts").CheckResult;
|
|
39
55
|
}
|
|
40
56
|
export default Expression;
|
package/esm/Expression.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Copyright 2020 Tom Shawver
|
|
4
4
|
*/
|
|
5
5
|
import Lexer from "./Lexer.js";
|
|
6
|
+
import { analyze } from "./analyze.js";
|
|
7
|
+
import { check } from "./check.js";
|
|
6
8
|
import { compileAst } from "./evaluator/compile.js";
|
|
7
9
|
import Parser from "./parser/Parser.js";
|
|
8
10
|
class Expression {
|
|
@@ -37,10 +39,7 @@ class Expression {
|
|
|
37
39
|
* @returns {Expression} this Expression instance, for convenience
|
|
38
40
|
*/
|
|
39
41
|
compile() {
|
|
40
|
-
|
|
41
|
-
const tokens = this._lexer.tokenize(this._exprStr);
|
|
42
|
-
parser.addTokens(tokens);
|
|
43
|
-
this._ast = parser.complete();
|
|
42
|
+
this._ast = new Parser(this._grammar, this._lexer).parse(this._exprStr);
|
|
44
43
|
// lower the tree to closures once, here, so that eval() is just a call
|
|
45
44
|
this._fn = this._ast ? compileAst(this._ast, this._grammar) : null;
|
|
46
45
|
this._compiled = true;
|
|
@@ -63,6 +62,31 @@ class Expression {
|
|
|
63
62
|
}
|
|
64
63
|
return this._fn(context);
|
|
65
64
|
}
|
|
65
|
+
/** The expression text this Expression was made from. */
|
|
66
|
+
get source() {
|
|
67
|
+
return this._exprStr;
|
|
68
|
+
}
|
|
69
|
+
/** The parsed tree, or null for an expression with no tokens. */
|
|
70
|
+
get ast() {
|
|
71
|
+
if (!this._compiled) {
|
|
72
|
+
this.compile();
|
|
73
|
+
}
|
|
74
|
+
return this._ast;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Lists what the expression reads from its context, without evaluating it.
|
|
78
|
+
* See {@link analyze}.
|
|
79
|
+
*/
|
|
80
|
+
analyze(options) {
|
|
81
|
+
return analyze(this.ast, options);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Checks the expression against a host's fields and functions, without
|
|
85
|
+
* evaluating it. See {@link check}.
|
|
86
|
+
*/
|
|
87
|
+
check(options) {
|
|
88
|
+
return check(this.ast, options);
|
|
89
|
+
}
|
|
66
90
|
}
|
|
67
91
|
export default Expression;
|
|
68
92
|
//# sourceMappingURL=Expression.js.map
|
package/esm/Expression.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expression.js","sourceRoot":"","sources":["../src/Expression.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,MAAM,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"Expression.js","sourceRoot":"","sources":["../src/Expression.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,MAAM,MAAM,oBAAoB,CAAA;AAQvC,MAAM,UAAU;IACd,QAAQ,CAAS;IACjB,QAAQ,CAAQ;IAChB;;oDAEgD;IAChD,IAAI,CAAgB;IACpB,MAAM,CAAO;IACb,SAAS,GAAG,KAAK,CAAA;IACjB,2EAA2E;IAC3E,GAAG,GAAwB,IAAI,CAAA;IAE/B;;;;;;;OAOG;IACH,YAAY,OAAgB,EAAE,OAAe,EAAE,KAAa;QAC1D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,MAAM,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;IAC3C,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACvE,uEAAuE;QACvE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAClE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,OAAO,GAAG,EAAE;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QACD,qEAAqE;QACrE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;IAED,yDAAyD;IACzD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAED,iEAAiE;IACjE,IAAI,GAAG;QACL,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,OAAwB;QAC9B,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACnC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAsB;QAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACjC,CAAC;CACF;AAED,eAAe,UAAU,CAAA"}
|
package/esm/Jexl.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import Expression from './Expression.ts';
|
|
2
2
|
import Lexer from './Lexer.ts';
|
|
3
|
-
import type { BinaryOpEvalOnDemand, Grammar, GrammarElement, GrammarFn, UncheckedFn } from './grammar.ts';
|
|
3
|
+
import type { BinaryOpEvalOnDemand, GetMember, Grammar, GrammarElement, GrammarFn, UncheckedFn, VariableReader } from './grammar.ts';
|
|
4
4
|
import type { JexlValue } from './types.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Hooks for a host whose values keep their fields behind an accessor. Both are
|
|
7
|
+
* fixed at construction, since a compiled expression binds them.
|
|
8
|
+
*/
|
|
9
|
+
export interface JexlOptions {
|
|
10
|
+
/** Resolves `a.b` and `a[k]`. */
|
|
11
|
+
getMember?: GetMember;
|
|
12
|
+
/** Resolves a bare name, such as a field of the current row. */
|
|
13
|
+
variableReader?: VariableReader;
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* Jexl is the Javascript Expression Language, capable of parsing and
|
|
7
17
|
* evaluating basic to complex expression strings, combined with advanced
|
|
@@ -11,7 +21,8 @@ import type { JexlValue } from './types.ts';
|
|
|
11
21
|
declare class Jexl {
|
|
12
22
|
_grammar: Grammar;
|
|
13
23
|
_lexer: Lexer;
|
|
14
|
-
|
|
24
|
+
_compiled: Map<string, Expression>;
|
|
25
|
+
constructor({ getMember, variableReader }?: JexlOptions);
|
|
15
26
|
/**
|
|
16
27
|
* Adds a binary operator to Jexl at the specified precedence. The higher the
|
|
17
28
|
* precedence, the earlier the operator is applied in the order of operations.
|
|
@@ -59,12 +70,10 @@ declare class Jexl {
|
|
|
59
70
|
*/
|
|
60
71
|
addUnaryOp(operator: string, fn: UncheckedFn): void;
|
|
61
72
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* @param {string} expression The Jexl expression to be compiled
|
|
67
|
-
* @returns {Expression} The compiled Expression object
|
|
73
|
+
* Compiles an expression string, or returns the Expression this instance
|
|
74
|
+
* already compiled from the same string. Adding or removing an operator
|
|
75
|
+
* empties the cache, since a compiled expression binds its operators;
|
|
76
|
+
* functions are looked up per call and leave it alone.
|
|
68
77
|
*/
|
|
69
78
|
compile(expression: string): Expression;
|
|
70
79
|
/**
|
|
@@ -74,6 +83,8 @@ declare class Jexl {
|
|
|
74
83
|
* @returns {Expression} The Expression object representing the given string
|
|
75
84
|
*/
|
|
76
85
|
createExpression(expression: string): Expression;
|
|
86
|
+
/** Parses an expression string, or throws a JexlSyntaxError. */
|
|
87
|
+
parse(expression: string): import("./types.ts").AstNode | null;
|
|
77
88
|
/**
|
|
78
89
|
* Retrieves a previously set expression function.
|
|
79
90
|
* @param {string} name The name of the expression function
|
|
@@ -109,6 +120,7 @@ declare class Jexl {
|
|
|
109
120
|
* @private
|
|
110
121
|
*/
|
|
111
122
|
_addGrammarElement(str: string, obj: GrammarElement): void;
|
|
123
|
+
_grammarChanged(): void;
|
|
112
124
|
}
|
|
113
125
|
declare const jexlInstance: Jexl;
|
|
114
126
|
export default jexlInstance;
|
package/esm/Jexl.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import Expression from "./Expression.js";
|
|
6
6
|
import Lexer from "./Lexer.js";
|
|
7
7
|
import { getGrammar } from "./grammar.js";
|
|
8
|
+
const MAX_COMPILED = 10_000;
|
|
8
9
|
/**
|
|
9
10
|
* Jexl is the Javascript Expression Language, capable of parsing and
|
|
10
11
|
* evaluating basic to complex expression strings, combined with advanced
|
|
@@ -16,24 +17,30 @@ class Jexl {
|
|
|
16
17
|
// shared by every Expression this instance creates, so that the expensive
|
|
17
18
|
// element-splitting regex is built once rather than per compile
|
|
18
19
|
_lexer;
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
_compiled = new Map();
|
|
21
|
+
constructor({ getMember, variableReader } = {}) {
|
|
22
|
+
this._grammar = { ...getGrammar(), getMember, variableReader };
|
|
21
23
|
this._lexer = new Lexer(this._grammar);
|
|
22
24
|
this.expr = this.expr.bind(this);
|
|
23
25
|
}
|
|
24
26
|
addBinaryOp(operator, precedence, fn, manualEval) {
|
|
27
|
+
// replacing `-` keeps its prefix form, which is a separate operation
|
|
28
|
+
const previous = this._grammar.elements[operator];
|
|
29
|
+
const unaryEval = previous?.type === 'binaryOp' ? previous.unaryEval : undefined;
|
|
25
30
|
// the overloads above pair `fn` with `manualEval`; the implementation
|
|
26
31
|
// signature can't express that correlation, hence the assertions
|
|
27
32
|
this._addGrammarElement(operator, manualEval
|
|
28
33
|
? {
|
|
29
34
|
type: 'binaryOp',
|
|
30
35
|
precedence,
|
|
31
|
-
evalOnDemand: fn
|
|
36
|
+
evalOnDemand: fn,
|
|
37
|
+
unaryEval
|
|
32
38
|
}
|
|
33
39
|
: {
|
|
34
40
|
type: 'binaryOp',
|
|
35
41
|
precedence,
|
|
36
|
-
eval: fn
|
|
42
|
+
eval: fn,
|
|
43
|
+
unaryEval
|
|
37
44
|
});
|
|
38
45
|
}
|
|
39
46
|
/**
|
|
@@ -74,16 +81,21 @@ class Jexl {
|
|
|
74
81
|
});
|
|
75
82
|
}
|
|
76
83
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* @param {string} expression The Jexl expression to be compiled
|
|
82
|
-
* @returns {Expression} The compiled Expression object
|
|
84
|
+
* Compiles an expression string, or returns the Expression this instance
|
|
85
|
+
* already compiled from the same string. Adding or removing an operator
|
|
86
|
+
* empties the cache, since a compiled expression binds its operators;
|
|
87
|
+
* functions are looked up per call and leave it alone.
|
|
83
88
|
*/
|
|
84
89
|
compile(expression) {
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
let compiled = this._compiled.get(expression);
|
|
91
|
+
if (!compiled) {
|
|
92
|
+
compiled = this.createExpression(expression).compile();
|
|
93
|
+
if (this._compiled.size >= MAX_COMPILED) {
|
|
94
|
+
this._compiled.delete(this._compiled.keys().next().value);
|
|
95
|
+
}
|
|
96
|
+
this._compiled.set(expression, compiled);
|
|
97
|
+
}
|
|
98
|
+
return compiled;
|
|
87
99
|
}
|
|
88
100
|
/**
|
|
89
101
|
* Constructs an Expression object from a Jexl expression string.
|
|
@@ -94,6 +106,10 @@ class Jexl {
|
|
|
94
106
|
createExpression(expression) {
|
|
95
107
|
return new Expression(this._grammar, expression, this._lexer);
|
|
96
108
|
}
|
|
109
|
+
/** Parses an expression string, or throws a JexlSyntaxError. */
|
|
110
|
+
parse(expression) {
|
|
111
|
+
return this.compile(expression).ast;
|
|
112
|
+
}
|
|
97
113
|
/**
|
|
98
114
|
* Retrieves a previously set expression function.
|
|
99
115
|
* @param {string} name The name of the expression function
|
|
@@ -111,8 +127,7 @@ class Jexl {
|
|
|
111
127
|
* @throws {*} on error
|
|
112
128
|
*/
|
|
113
129
|
eval(expression, context = {}) {
|
|
114
|
-
|
|
115
|
-
return exprObj.eval(context);
|
|
130
|
+
return this.compile(expression).eval(context);
|
|
116
131
|
}
|
|
117
132
|
/**
|
|
118
133
|
* A JavaScript template literal to allow expressions to be defined by the
|
|
@@ -146,7 +161,7 @@ class Jexl {
|
|
|
146
161
|
const elem = this._grammar.elements[operator];
|
|
147
162
|
if (elem?.type === 'binaryOp' || elem?.type === 'unaryOp') {
|
|
148
163
|
Reflect.deleteProperty(this._grammar.elements, operator);
|
|
149
|
-
this.
|
|
164
|
+
this._grammarChanged();
|
|
150
165
|
}
|
|
151
166
|
}
|
|
152
167
|
/**
|
|
@@ -158,7 +173,11 @@ class Jexl {
|
|
|
158
173
|
*/
|
|
159
174
|
_addGrammarElement(str, obj) {
|
|
160
175
|
this._grammar.elements[str] = obj;
|
|
176
|
+
this._grammarChanged();
|
|
177
|
+
}
|
|
178
|
+
_grammarChanged() {
|
|
161
179
|
this._lexer._clearCache();
|
|
180
|
+
this._compiled.clear();
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
183
|
const jexlInstance = new Jexl();
|
package/esm/Jexl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Jexl.js","sourceRoot":"","sources":["../src/Jexl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,UAAU,MAAM,iBAAiB,CAAA;AACxC,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"Jexl.js","sourceRoot":"","sources":["../src/Jexl.ts"],"names":[],"mappings":"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"}
|
package/esm/Lexer.d.ts
CHANGED
|
@@ -78,10 +78,11 @@ declare class Lexer {
|
|
|
78
78
|
* @throws {Error} if the provided string is not a valid expression element.
|
|
79
79
|
* @private
|
|
80
80
|
*/
|
|
81
|
-
_createToken(element: string): Token;
|
|
81
|
+
_createToken(element: string, offset?: number): Token;
|
|
82
82
|
/**
|
|
83
83
|
* Escapes a string so that it can be treated as a string literal within a
|
|
84
|
-
* regular expression.
|
|
84
|
+
* regular expression. A word such as `in` also stops matching inside a
|
|
85
|
+
* longer name.
|
|
85
86
|
* @param {string} str The string to be escaped
|
|
86
87
|
* @returns {string} the RegExp-escaped string.
|
|
87
88
|
* @see https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
|
|
@@ -126,7 +127,7 @@ declare class Lexer {
|
|
|
126
127
|
* @private
|
|
127
128
|
*/
|
|
128
129
|
_unquote(str: string): string;
|
|
129
|
-
_parseTemplateString(str: string): TemplatePart[];
|
|
130
|
+
_parseTemplateString(str: string, offset?: number): TemplatePart[];
|
|
130
131
|
_unescapeTemplateString(str: string): string;
|
|
131
132
|
}
|
|
132
133
|
export default Lexer;
|
package/esm/Lexer.js
CHANGED
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
* Jexl
|
|
3
3
|
* Copyright 2020 Tom Shawver
|
|
4
4
|
*/
|
|
5
|
+
import { JexlSyntaxError } from "./errors.js";
|
|
5
6
|
// what an identifier and a number look like, written once: the split regex
|
|
6
7
|
// below carves elements out of the expression with these, and _createToken
|
|
7
8
|
// then re-tests the elements it produced. Two spellings of either would
|
|
8
9
|
// disagree on some input, and each disagreement is an "Invalid expression
|
|
9
10
|
// token" for something the splitter was happy to produce.
|
|
10
11
|
const identChars = String.raw `a-zA-Zа-яА-Я_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$`;
|
|
11
|
-
const
|
|
12
|
+
const identPart = `[${identChars}0-9]`;
|
|
13
|
+
const identPattern = `[${identChars}]${identPart}*`;
|
|
14
|
+
// a word operator such as `in`, but not part of a longer name. \b would do only
|
|
15
|
+
// for ASCII names, and split `in$` or `inà` into the operator and a remainder
|
|
16
|
+
const wholeWord = (word) => `(?<!${identPart})${word}(?!${identPart})`;
|
|
12
17
|
// unsigned: whether a leading '-' negates is decided separately, in getTokens
|
|
13
|
-
const numberPattern = String.raw `(?:(?:[0-9]*\.[0-9]+)|[0-9]+)
|
|
18
|
+
const numberPattern = String.raw `(?:(?:[0-9]*\.[0-9]+)|[0-9]+)(?:[eE][+-]?[0-9]+)?`;
|
|
14
19
|
const numericRegex = new RegExp(`^-?${numberPattern}$`);
|
|
15
20
|
const identRegex = new RegExp(`^${identPattern}$`);
|
|
16
21
|
const escEscRegex = /\\\\/g;
|
|
@@ -30,11 +35,10 @@ const preOpRegexElems = [
|
|
|
30
35
|
String.raw `"(?:(?:\\")|[^"])*"`,
|
|
31
36
|
// Whitespace
|
|
32
37
|
String.raw `\s+`,
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
String.raw `\bfalse\b`
|
|
38
|
+
// ahead of the grammar's '.', so that '.5' is a number rather than a dot
|
|
39
|
+
numberPattern
|
|
36
40
|
];
|
|
37
|
-
const postOpRegexElems = [identPattern
|
|
41
|
+
const postOpRegexElems = [identPattern];
|
|
38
42
|
const unaryMinusToken = () => ({
|
|
39
43
|
type: 'unaryOp',
|
|
40
44
|
value: '-',
|
|
@@ -48,7 +52,8 @@ const minusNegatesAfter = new Set([
|
|
|
48
52
|
'question',
|
|
49
53
|
'colon',
|
|
50
54
|
'comma',
|
|
51
|
-
'semicolon'
|
|
55
|
+
'semicolon',
|
|
56
|
+
'arrow'
|
|
52
57
|
]);
|
|
53
58
|
/**
|
|
54
59
|
* Lexer handles the lexical parsing of a Jexl string. Its responsibility is to
|
|
@@ -103,6 +108,7 @@ class Lexer {
|
|
|
103
108
|
// 1") accumulates on the minus rather than on the token before it, which
|
|
104
109
|
// is what lets the parser's error messages quote the expression verbatim.
|
|
105
110
|
let pendingMinus;
|
|
111
|
+
let offset = 0;
|
|
106
112
|
for (const element of elements) {
|
|
107
113
|
if (this._isWhitespace(element)) {
|
|
108
114
|
const last = pendingMinus ?? tokens.at(-1);
|
|
@@ -121,20 +127,21 @@ class Lexer {
|
|
|
121
127
|
else if (pendingMinus) {
|
|
122
128
|
if (numericRegex.exec(element)) {
|
|
123
129
|
// fold the sign into the number, so "-1" stays a single literal
|
|
124
|
-
const token = this._createToken('-' + element);
|
|
130
|
+
const token = this._createToken('-' + element, offset);
|
|
125
131
|
token.raw = pendingMinus.raw + element;
|
|
126
132
|
tokens.push(token);
|
|
127
133
|
}
|
|
128
134
|
else {
|
|
129
135
|
// anything else gets a standalone prefix operator, letting "-x",
|
|
130
136
|
// "-(a + b)" and "-foo.bar" negate a computed value
|
|
131
|
-
tokens.push(pendingMinus, this._createToken(element));
|
|
137
|
+
tokens.push(pendingMinus, this._createToken(element, offset));
|
|
132
138
|
}
|
|
133
139
|
pendingMinus = undefined;
|
|
134
140
|
}
|
|
135
141
|
else {
|
|
136
|
-
tokens.push(this._createToken(element));
|
|
142
|
+
tokens.push(this._createToken(element, offset));
|
|
137
143
|
}
|
|
144
|
+
offset += element.length;
|
|
138
145
|
}
|
|
139
146
|
// Catch a - at the end of the string. Let the parser handle that issue.
|
|
140
147
|
if (pendingMinus) {
|
|
@@ -183,7 +190,7 @@ class Lexer {
|
|
|
183
190
|
* @throws {Error} if the provided string is not a valid expression element.
|
|
184
191
|
* @private
|
|
185
192
|
*/
|
|
186
|
-
_createToken(element) {
|
|
193
|
+
_createToken(element, offset = 0) {
|
|
187
194
|
const token = {
|
|
188
195
|
type: 'literal',
|
|
189
196
|
value: element,
|
|
@@ -191,7 +198,7 @@ class Lexer {
|
|
|
191
198
|
};
|
|
192
199
|
if (element.startsWith('`')) {
|
|
193
200
|
token.type = 'templateString';
|
|
194
|
-
token.value = this._parseTemplateString(element);
|
|
201
|
+
token.value = this._parseTemplateString(element, offset);
|
|
195
202
|
return token;
|
|
196
203
|
}
|
|
197
204
|
else if (element.startsWith('"') || element.startsWith("'")) {
|
|
@@ -203,6 +210,9 @@ class Lexer {
|
|
|
203
210
|
else if (element === 'true' || element === 'false') {
|
|
204
211
|
token.value = element === 'true';
|
|
205
212
|
}
|
|
213
|
+
else if (element === 'null') {
|
|
214
|
+
token.value = null;
|
|
215
|
+
}
|
|
206
216
|
else if (Object.hasOwn(this._grammar.elements, element)) {
|
|
207
217
|
token.type = this._grammar.elements[element].type;
|
|
208
218
|
}
|
|
@@ -210,24 +220,22 @@ class Lexer {
|
|
|
210
220
|
token.type = 'identifier';
|
|
211
221
|
}
|
|
212
222
|
else {
|
|
213
|
-
throw new
|
|
223
|
+
throw new JexlSyntaxError(`Invalid expression token: ${element}`, offset);
|
|
214
224
|
}
|
|
215
225
|
return token;
|
|
216
226
|
}
|
|
217
227
|
/**
|
|
218
228
|
* Escapes a string so that it can be treated as a string literal within a
|
|
219
|
-
* regular expression.
|
|
229
|
+
* regular expression. A word such as `in` also stops matching inside a
|
|
230
|
+
* longer name.
|
|
220
231
|
* @param {string} str The string to be escaped
|
|
221
232
|
* @returns {string} the RegExp-escaped string.
|
|
222
233
|
* @see https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
|
|
223
234
|
* @private
|
|
224
235
|
*/
|
|
225
236
|
_escapeRegExp(str) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
str = String.raw `\b` + str + String.raw `\b`;
|
|
229
|
-
}
|
|
230
|
-
return str;
|
|
237
|
+
const escaped = str.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
238
|
+
return identRegex.exec(str) ? wholeWord(escaped) : escaped;
|
|
231
239
|
}
|
|
232
240
|
/**
|
|
233
241
|
* Gets a RegEx object appropriate for splitting a Jexl string into its core
|
|
@@ -297,7 +305,7 @@ class Lexer {
|
|
|
297
305
|
.replaceAll(escQuoteRegex[quote], quote)
|
|
298
306
|
.replaceAll(escEscRegex, '\\');
|
|
299
307
|
}
|
|
300
|
-
_parseTemplateString(str) {
|
|
308
|
+
_parseTemplateString(str, offset = 0) {
|
|
301
309
|
const parts = [];
|
|
302
310
|
let current = 1;
|
|
303
311
|
let staticStart = 1;
|
|
@@ -341,7 +349,7 @@ class Lexer {
|
|
|
341
349
|
current++;
|
|
342
350
|
}
|
|
343
351
|
if (braceDepth !== 0) {
|
|
344
|
-
throw new
|
|
352
|
+
throw new JexlSyntaxError(`Unclosed interpolation in template string: ${str}`, offset + interpStart - '${'.length);
|
|
345
353
|
}
|
|
346
354
|
parts.push({
|
|
347
355
|
type: 'interpolation',
|
package/esm/Lexer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Lexer.js","sourceRoot":"","sources":["../src/Lexer.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"Lexer.js","sourceRoot":"","sources":["../src/Lexer.ts"],"names":[],"mappings":"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"}
|
package/esm/analyze.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { AstNode } from './types.ts';
|
|
2
|
+
export type PathKey = string | number;
|
|
3
|
+
/**
|
|
4
|
+
* A value the expression reads out of its context: the variable `root`, then
|
|
5
|
+
* each literal key in `path`. `dynamic` means a computed key followed, so the
|
|
6
|
+
* read reaches somewhere under `path` that only evaluation can name.
|
|
7
|
+
*
|
|
8
|
+
* A dot and a bracket both add a key.
|
|
9
|
+
*/
|
|
10
|
+
export interface Read {
|
|
11
|
+
root: string;
|
|
12
|
+
path: PathKey[];
|
|
13
|
+
dynamic?: true;
|
|
14
|
+
}
|
|
15
|
+
export type CallArg = {
|
|
16
|
+
type: 'literal';
|
|
17
|
+
value: string | number | boolean | null;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'path';
|
|
20
|
+
read: Read;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'dynamic';
|
|
23
|
+
};
|
|
24
|
+
/** A path under the row, the record a per-row expression is evaluated for. */
|
|
25
|
+
export interface Field {
|
|
26
|
+
path: PathKey[];
|
|
27
|
+
dynamic?: true;
|
|
28
|
+
}
|
|
29
|
+
export interface Call {
|
|
30
|
+
name: string;
|
|
31
|
+
args: CallArg[];
|
|
32
|
+
}
|
|
33
|
+
export interface Analysis {
|
|
34
|
+
/** The context variables read, in the order first read. */
|
|
35
|
+
variables: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Every path whose value the expression uses, in the order first used. A
|
|
38
|
+
* path only extended, as `feature` is in `feature.score`, is not a use.
|
|
39
|
+
*/
|
|
40
|
+
reads: Read[];
|
|
41
|
+
/** The reads that are fields of the row, as `row` and `env` say. */
|
|
42
|
+
fields: Field[];
|
|
43
|
+
/** The paths whose value the expression returns as is. */
|
|
44
|
+
returns: Read[];
|
|
45
|
+
/**
|
|
46
|
+
* Whether the expression is only a path: a name, then dots and literal
|
|
47
|
+
* subscripts, as in `score` or `feature.INFO.DP[0]`. Its one read is then
|
|
48
|
+
* its value, which a host can read without jexl.
|
|
49
|
+
*/
|
|
50
|
+
bare: boolean;
|
|
51
|
+
/** Every call, outermost first, including accessor calls. */
|
|
52
|
+
calls: Call[];
|
|
53
|
+
/** The names the expression writes into its context. */
|
|
54
|
+
assigned: string[];
|
|
55
|
+
}
|
|
56
|
+
export interface AnalyzeOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Functions that read a path: `name(subject, ...keys)` reads the subject,
|
|
59
|
+
* then this prefix, then each key. `{ get: [] }` makes `get(feature, 'x')`
|
|
60
|
+
* and `feature.get('x')` both read `feature.x`.
|
|
61
|
+
*/
|
|
62
|
+
accessors?: Record<string, readonly PathKey[]>;
|
|
63
|
+
/** The variable holding the row, whose reads `fields` lists: `'feature'`. */
|
|
64
|
+
row?: string;
|
|
65
|
+
/**
|
|
66
|
+
* For a host that binds each of the row's fields as a variable, so that
|
|
67
|
+
* `pvalue` means the row's `pvalue`: the variables it binds to anything
|
|
68
|
+
* else. Every other variable the expression reads is then a field.
|
|
69
|
+
*/
|
|
70
|
+
env?: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Lists what an expression reads from its context, without evaluating it or
|
|
74
|
+
* consulting any registered function.
|
|
75
|
+
*
|
|
76
|
+
* A name assigned earlier in the expression is a local: `x = feature.score;
|
|
77
|
+
* x > 1` reads `feature.score` and no `x`. An assignment that may not run — in
|
|
78
|
+
* a conditional's branch, or right of a binary operator, which may short-circuit
|
|
79
|
+
* — counts its value as read and binds nothing past that operand, so a later
|
|
80
|
+
* read of the name is a context read.
|
|
81
|
+
*
|
|
82
|
+
* Every path evaluation reads is reported, or a prefix of it is.
|
|
83
|
+
*/
|
|
84
|
+
export declare function analyze(ast: AstNode | null, { accessors, row, env }?: AnalyzeOptions): Analysis;
|