@jbrowse/jexl 3.1.0 → 3.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.
Files changed (45) hide show
  1. package/README.md +15 -0
  2. package/dist/Expression.d.ts +3 -1
  3. package/dist/Expression.js +3 -8
  4. package/dist/Expression.js.map +1 -1
  5. package/dist/Lexer.d.ts +0 -1
  6. package/dist/Lexer.js +5 -8
  7. package/dist/Lexer.js.map +1 -1
  8. package/dist/evaluator/compile.js +34 -16
  9. package/dist/evaluator/compile.js.map +1 -1
  10. package/dist/grammar.js +8 -8
  11. package/dist/grammar.js.map +1 -1
  12. package/dist/parser/Parser.d.ts +2 -1
  13. package/dist/parser/Parser.js +29 -12
  14. package/dist/parser/Parser.js.map +1 -1
  15. package/dist/parser/handlers.d.ts +8 -2
  16. package/dist/parser/handlers.js +32 -7
  17. package/dist/parser/handlers.js.map +1 -1
  18. package/dist/parser/states.js +19 -20
  19. package/dist/parser/states.js.map +1 -1
  20. package/esm/Expression.d.ts +3 -1
  21. package/esm/Expression.js +3 -8
  22. package/esm/Expression.js.map +1 -1
  23. package/esm/Lexer.d.ts +0 -1
  24. package/esm/Lexer.js +5 -8
  25. package/esm/Lexer.js.map +1 -1
  26. package/esm/evaluator/compile.js +34 -16
  27. package/esm/evaluator/compile.js.map +1 -1
  28. package/esm/grammar.js +8 -8
  29. package/esm/grammar.js.map +1 -1
  30. package/esm/parser/Parser.d.ts +2 -1
  31. package/esm/parser/Parser.js +29 -12
  32. package/esm/parser/Parser.js.map +1 -1
  33. package/esm/parser/handlers.d.ts +8 -2
  34. package/esm/parser/handlers.js +29 -7
  35. package/esm/parser/handlers.js.map +1 -1
  36. package/esm/parser/states.js +19 -20
  37. package/esm/parser/states.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/Expression.ts +3 -9
  40. package/src/Lexer.ts +5 -8
  41. package/src/evaluator/compile.ts +51 -44
  42. package/src/grammar.ts +8 -9
  43. package/src/parser/Parser.ts +35 -17
  44. package/src/parser/handlers.ts +33 -10
  45. package/src/parser/states.ts +20 -20
@@ -3,6 +3,9 @@
3
3
  * Copyright 2020 Tom Shawver
4
4
  */
5
5
  import { precedenceOf } from "../grammar.js";
6
+ // circular with Parser.ts, which is fine: the binding is only read from inside
7
+ // a handler, long after both modules have finished evaluating
8
+ import Parser from "./Parser.js";
6
9
  /**
7
10
  * Handles a subexpression that's used to define a transform argument's value.
8
11
  * @param {{type: <string>}} ast The subexpression tree
@@ -115,13 +118,26 @@ export function filter(ast) {
115
118
  /**
116
119
  * Handles identifier tokens when used to indicate the name of a function to
117
120
  * be called.
118
- * @param {{type: <string>}} token A token object
121
+ *
122
+ * A call written against a value, `a.b(x)`, becomes `b(a, x)`: jexl functions
123
+ * live in one global pool and have no receiver, so the value the name was read
124
+ * from is passed as the first argument instead. Every function in the pool is
125
+ * already written that way — `get(feature, key)`, `split(str, sep)` — which is
126
+ * what makes the two spellings equivalent. Previously the receiver was simply
127
+ * dropped, so `refName.split(' ')` quietly called `split(' ')`.
119
128
  */
120
129
  export function functionCall() {
130
+ const cursor = this._cursor;
131
+ if (cursor?.type !== 'Identifier') {
132
+ // reached by `a[expr]()`, whose subject is a filter rather than a name;
133
+ // there is nothing to look up in the function pool
134
+ throw new Error(`Functions must be called by name: ${this._exprStr}`);
135
+ }
136
+ const { from } = cursor;
121
137
  const node = {
122
138
  type: 'FunctionCall',
123
- name: this._cursor.value,
124
- args: [],
139
+ name: cursor.value,
140
+ args: from ? [from] : [],
125
141
  pool: 'functions'
126
142
  };
127
143
  this._placeBeforeCursor(node);
@@ -177,8 +193,7 @@ export function templateString(token) {
177
193
  }
178
194
  else {
179
195
  const subTokens = this._lexer.tokenize(part.value);
180
- const ParserClass = this.constructor;
181
- const subParser = new ParserClass(this._grammar, this._lexer);
196
+ const subParser = new Parser(this._grammar, this._lexer);
182
197
  subParser.addTokens(subTokens);
183
198
  const subAst = subParser.complete();
184
199
  if (!subAst) {
@@ -220,8 +235,15 @@ export function objStart() {
220
235
  * @param {{type: <string>}} ast The subexpression tree
221
236
  */
222
237
  export function objVal(ast) {
223
- ;
224
- this._cursor.value[this._curObjKey] = ast;
238
+ // defined rather than assigned so that the key "__proto__" becomes an
239
+ // ordinary entry of the node's key map instead of re-pointing its prototype,
240
+ // which dropped the entry before the Evaluator ever saw it
241
+ Object.defineProperty(this._cursor.value, this._curObjKey, {
242
+ value: ast,
243
+ writable: true,
244
+ enumerable: true,
245
+ configurable: true
246
+ });
225
247
  }
226
248
  /**
227
249
  * Handles traditional subexpressions, delineated with the groupStart and
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/parser/handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/parser/handlers.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,19 @@
3
3
  * Copyright 2020 Tom Shawver
4
4
  */
5
5
  import * as h from "./handlers.js";
6
+ /**
7
+ * The tokens that may follow a finished operand: an operator to combine it
8
+ * with, a dot or subscript to keep drilling into it, a question mark opening a
9
+ * ternary, or a semicolon ending the statement. Shared by every state that
10
+ * sits on a complete value, which is also why each of them is completable.
11
+ */
12
+ const afterOperand = {
13
+ binaryOp: { toState: 'expectOperand' },
14
+ dot: { toState: 'traverse' },
15
+ openBracket: { toState: 'filter' },
16
+ question: { toState: 'ternaryMid', handler: h.ternaryStart },
17
+ semicolon: { handler: h.semicolon }
18
+ };
6
19
  /**
7
20
  * A mapping of all states in the finite state machine to a set of instructions
8
21
  * for handling or transitioning into other states. Each state can be handled
@@ -49,13 +62,7 @@ export const states = {
49
62
  }
50
63
  },
51
64
  expectBinOp: {
52
- tokenTypes: {
53
- binaryOp: { toState: 'expectOperand' },
54
- dot: { toState: 'traverse' },
55
- openBracket: { toState: 'filter' },
56
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
57
- semicolon: { handler: h.semicolon }
58
- },
65
+ tokenTypes: afterOperand,
59
66
  completable: true
60
67
  },
61
68
  expectObjKey: {
@@ -71,23 +78,15 @@ export const states = {
71
78
  }
72
79
  },
73
80
  postArgs: {
74
- tokenTypes: {
75
- binaryOp: { toState: 'expectOperand' },
76
- dot: { toState: 'traverse' },
77
- openBracket: { toState: 'filter' },
78
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
79
- semicolon: { handler: h.semicolon }
80
- },
81
+ tokenTypes: afterOperand,
81
82
  completable: true
82
83
  },
83
84
  identifier: {
84
85
  tokenTypes: {
85
- binaryOp: { toState: 'expectOperand' },
86
- dot: { toState: 'traverse' },
87
- openBracket: { toState: 'filter' },
88
- openParen: { toState: 'argVal', handler: h.functionCall },
89
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
90
- semicolon: { handler: h.semicolon }
86
+ ...afterOperand,
87
+ // a name is the only operand that can be followed by an argument list,
88
+ // since a function is called by name rather than evaluated as a value
89
+ openParen: { toState: 'argVal', handler: h.functionCall }
91
90
  },
92
91
  completable: true
93
92
  },
@@ -1 +1 @@
1
- {"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/parser/states.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/parser/states.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/jexl",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "A fork of the jexl lang for jbrowse",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/Expression.ts CHANGED
@@ -15,6 +15,9 @@ import type { AstNode } from './types.ts'
15
15
  class Expression {
16
16
  _grammar: Grammar
17
17
  _exprStr: string
18
+ /** The parsed tree, exposed for inspection. Legitimately null for an
19
+ * expression with no tokens, which is why compilation is tracked by
20
+ * `_compiled` rather than by this being set. */
18
21
  _ast: AstNode | null
19
22
  _lexer: Lexer
20
23
  _compiled = false
@@ -70,15 +73,6 @@ class Expression {
70
73
  }
71
74
  return this._fn(new Evaluator(this._grammar, context))
72
75
  }
73
-
74
- _getAst() {
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) {
78
- this.compile()
79
- }
80
- return this._ast
81
- }
82
76
  }
83
77
 
84
78
  export default Expression
package/src/Lexer.ts CHANGED
@@ -10,6 +10,9 @@ const numericRegex = /^-?(?:(?:[0-9]*\.[0-9]+)|[0-9]+)$/
10
10
  const identRegex =
11
11
  /^[a-zA-Zа-яА-Я_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$][a-zA-Zа-яА-Я0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$]*$/
12
12
  const escEscRegex = /\\\\/g
13
+ // a string literal opens with one of exactly two quote characters, so the two
14
+ // unescaping regexes can just be named rather than built and cached per quote
15
+ const escQuoteRegex = { "'": /\\'/g, '"': /\\"/g }
13
16
  const whitespaceRegex = /^\s*$/
14
17
  const preOpRegexElems = [
15
18
  // Template strings
@@ -58,7 +61,6 @@ const minusNegatesAfter = new Set([
58
61
  class Lexer {
59
62
  _grammar: Grammar
60
63
  _splitRegex?: RegExp
61
- _escQuoteRegexCache = new Map<string, RegExp>()
62
64
 
63
65
  constructor(grammar: Grammar) {
64
66
  this._grammar = grammar
@@ -284,15 +286,10 @@ class Lexer {
284
286
  * @private
285
287
  */
286
288
  _unquote(str: string) {
287
- const quote = str[0]!
288
- let escQuoteRegex = this._escQuoteRegexCache.get(quote)
289
- if (!escQuoteRegex) {
290
- escQuoteRegex = new RegExp('\\\\' + quote, 'g')
291
- this._escQuoteRegexCache.set(quote, escQuoteRegex)
292
- }
289
+ const quote = str.startsWith('"') ? '"' : "'"
293
290
  return str
294
291
  .slice(1, -1)
295
- .replaceAll(escQuoteRegex, quote)
292
+ .replaceAll(escQuoteRegex[quote], quote)
296
293
  .replaceAll(escEscRegex, '\\')
297
294
  }
298
295
 
@@ -4,22 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { Grammar } from '../grammar.ts'
7
- import type {
8
- ArrayLiteral,
9
- AssignmentExpression,
10
- AstNode,
11
- BinaryExpression,
12
- ConditionalExpression,
13
- FilterExpression,
14
- FunctionCall,
15
- Identifier,
16
- JexlValue,
17
- Literal,
18
- ObjectLiteral,
19
- SequenceExpression,
20
- TemplateLiteral,
21
- UnaryExpression
22
- } from '../types.ts'
7
+ import type { AstNode, AstNodeUnion, JexlValue } from '../types.ts'
23
8
  import type Evaluator from './Evaluator.ts'
24
9
 
25
10
  /**
@@ -28,6 +13,34 @@ import type Evaluator from './Evaluator.ts'
28
13
  */
29
14
  export type CompiledNode = (ev: Evaluator) => JexlValue
30
15
 
16
+ /**
17
+ * Writes a key that a plain assignment would mishandle. Storing to
18
+ * "__proto__" invokes the prototype setter rather than creating a property, so
19
+ * an object literal or assignment using that key silently lost its value (and
20
+ * re-pointed the target's prototype). Defining the property instead matches
21
+ * what `JSON.parse('{"__proto__":1}')` produces: an ordinary own property.
22
+ */
23
+ function defineOwn(
24
+ target: Record<string, JexlValue>,
25
+ key: string,
26
+ value: JexlValue
27
+ ) {
28
+ Object.defineProperty(target, key, {
29
+ value,
30
+ writable: true,
31
+ enumerable: true,
32
+ configurable: true
33
+ })
34
+ }
35
+
36
+ function assignOwn(
37
+ target: Record<string, JexlValue>,
38
+ key: string,
39
+ value: JexlValue
40
+ ) {
41
+ target[key] = value
42
+ }
43
+
31
44
  /** Renders an interpolated value for a template literal. */
32
45
  function stringify(value: JexlValue) {
33
46
  if (value == null) {
@@ -62,14 +75,17 @@ function stringify(value: JexlValue) {
62
75
  * @throws {Error} if the tree contains an unrecognized node type
63
76
  */
64
77
  export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
65
- switch (ast.type) {
78
+ // AstNode types its `type` as a plain string so the Parser can build the tree
79
+ // loosely; narrowing to the union once, here, lets every case below see its
80
+ // own node type instead of repeating the same cast in each branch
81
+ const node = ast as AstNodeUnion
82
+ switch (node.type) {
66
83
  case 'Literal': {
67
- const { value } = ast as Literal
84
+ const { value } = node
68
85
  return () => value
69
86
  }
70
87
 
71
88
  case 'Identifier': {
72
- const node = ast as Identifier
73
89
  const name = node.value
74
90
  if (!node.from) {
75
91
  return node.relative
@@ -91,7 +107,6 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
91
107
  }
92
108
 
93
109
  case 'BinaryExpression': {
94
- const node = ast as BinaryExpression
95
110
  const op = grammar.elements[node.operator]
96
111
  if (op?.type !== 'binaryOp') {
97
112
  // matches the tree-walking behaviour: an unknown operator yields
@@ -119,7 +134,6 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
119
134
  }
120
135
 
121
136
  case 'UnaryExpression': {
122
- const node = ast as UnaryExpression
123
137
  const right = compileAst(node.right!, grammar)
124
138
  const elem = grammar.elements[node.operator]
125
139
  const fn =
@@ -139,7 +153,6 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
139
153
  }
140
154
 
141
155
  case 'ConditionalExpression': {
142
- const node = ast as ConditionalExpression
143
156
  const test = compileAst(node.test, grammar)
144
157
  const consequent = node.consequent
145
158
  ? compileAst(node.consequent, grammar)
@@ -158,7 +171,6 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
158
171
  }
159
172
 
160
173
  case 'FilterExpression': {
161
- const node = ast as FilterExpression
162
174
  if (node.relative) {
163
175
  // thrown on evaluation rather than compilation, as before
164
176
  return () => {
@@ -180,9 +192,7 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
180
192
  }
181
193
 
182
194
  case 'ArrayLiteral': {
183
- const items = (ast as ArrayLiteral).value.map((item) =>
184
- compileAst(item, grammar)
185
- )
195
+ const items = node.value.map((item) => compileAst(item, grammar))
186
196
  const len = items.length
187
197
  return (ev) => {
188
198
  const out: JexlValue[] = new Array(len)
@@ -194,14 +204,16 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
194
204
  }
195
205
 
196
206
  case 'ObjectLiteral': {
197
- const entries = Object.entries((ast as ObjectLiteral).value)
207
+ const entries = Object.entries(node.value)
198
208
  const keys = entries.map(([key]) => key)
199
- const values = entries.map(([, node]) => compileAst(node, grammar))
209
+ const values = entries.map(([, value]) => compileAst(value, grammar))
200
210
  const len = keys.length
211
+ // resolved once, here, so the common case keeps its plain store
212
+ const store = keys.includes('__proto__') ? defineOwn : assignOwn
201
213
  return (ev) => {
202
214
  const out: Record<string, JexlValue> = {}
203
215
  for (let i = 0; i < len; i++) {
204
- out[keys[i]!] = values[i]!(ev)
216
+ store(out, keys[i]!, values[i]!(ev))
205
217
  }
206
218
  return out
207
219
  }
@@ -209,16 +221,14 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
209
221
 
210
222
  case 'TemplateLiteral': {
211
223
  // every part renders to a string, so these are narrower than CompiledNode
212
- const parts = (ast as TemplateLiteral).parts.map(
213
- (part): ((ev: Evaluator) => string) => {
214
- if (part.type === 'static') {
215
- const { value } = part
216
- return () => value
217
- }
218
- const expr = compileAst(part.value, grammar)
219
- return (ev) => stringify(expr(ev))
224
+ const parts = node.parts.map((part): ((ev: Evaluator) => string) => {
225
+ if (part.type === 'static') {
226
+ const { value } = part
227
+ return () => value
220
228
  }
221
- )
229
+ const expr = compileAst(part.value, grammar)
230
+ return (ev) => stringify(expr(ev))
231
+ })
222
232
  return (ev) => {
223
233
  let out = ''
224
234
  for (const part of parts) {
@@ -229,7 +239,6 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
229
239
  }
230
240
 
231
241
  case 'FunctionCall': {
232
- const node = ast as FunctionCall
233
242
  const { name } = node
234
243
  const args = node.args.map((arg) => compileAst(arg, grammar))
235
244
  // hasOwn, so that inherited Object.prototype members such as `toString`
@@ -279,9 +288,7 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
279
288
  }
280
289
 
281
290
  case 'SequenceExpression': {
282
- const exprs = (ast as SequenceExpression).expressions.map((expr) =>
283
- compileAst(expr, grammar)
284
- )
291
+ const exprs = node.expressions.map((expr) => compileAst(expr, grammar))
285
292
  const len = exprs.length
286
293
  return (ev) => {
287
294
  let last: JexlValue
@@ -293,12 +300,12 @@ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
293
300
  }
294
301
 
295
302
  case 'AssignmentExpression': {
296
- const node = ast as AssignmentExpression
297
303
  const name = node.left.value
298
304
  const right = compileAst(node.right!, grammar)
305
+ const store = name === '__proto__' ? defineOwn : assignOwn
299
306
  return (ev) => {
300
307
  const value = right(ev)
301
- ev._context[name] = value
308
+ store(ev._context, name, value)
302
309
  return value
303
310
  }
304
311
  }
package/src/grammar.ts CHANGED
@@ -182,18 +182,17 @@ export const getGrammar = (): Grammar => ({
182
182
  precedence: 20,
183
183
  eval: (left, right) => {
184
184
  if (typeof right === 'string') {
185
- const leftStr =
186
- typeof left === 'string' ||
185
+ // only a primitive has a meaningful substring form. An absent or
186
+ // structured left operand is not "in" a string, and must not be
187
+ // coerced to '' — every string contains the empty string, so that
188
+ // made `feature.missingAttr in "someString"` true for every feature.
189
+ return typeof left === 'string' ||
187
190
  typeof left === 'number' ||
188
191
  typeof left === 'boolean'
189
- ? String(left)
190
- : ''
191
- return right.includes(leftStr)
192
+ ? right.includes(String(left))
193
+ : false
192
194
  }
193
- if (Array.isArray(right)) {
194
- return right.includes(left)
195
- }
196
- return false
195
+ return Array.isArray(right) ? right.includes(left) : false
197
196
  }
198
197
  },
199
198
  '!': {
@@ -10,6 +10,25 @@ import type Lexer from '../Lexer.ts'
10
10
  import type { Grammar } from '../grammar.ts'
11
11
  import type { AstNode, SequenceExpression, Token } from '../types.ts'
12
12
 
13
+ /**
14
+ * The handler to run for a token type when the state it appears in doesn't
15
+ * name one of its own. Token types absent here — the brackets, parens and
16
+ * separators — carry no behavior beyond the state transition the state machine
17
+ * gives them.
18
+ */
19
+ const tokenHandlers: Record<
20
+ string,
21
+ ((this: Parser, token: Token) => void) | undefined
22
+ > = {
23
+ binaryOp: handlers.binaryOp,
24
+ dot: handlers.dot,
25
+ identifier: handlers.identifier,
26
+ literal: handlers.literal,
27
+ semicolon: handlers.semicolon,
28
+ templateString: handlers.templateString,
29
+ unaryOp: handlers.unaryOp
30
+ }
31
+
13
32
  /**
14
33
  * The Parser is a state machine that converts tokens from the {@link Lexer}
15
34
  * into an Abstract Syntax Tree (AST), capable of being evaluated in any
@@ -76,10 +95,8 @@ class Parser {
76
95
  const startExpr = this._exprStr
77
96
  this._exprStr += token.raw
78
97
  if (state.subHandler) {
79
- if (!this._subParser) {
80
- this._startSubExpression(startExpr)
81
- }
82
- const stopState = this._subParser!.addToken(token)
98
+ const subParser = this._subParser ?? this._startSubExpression(startExpr)
99
+ const stopState = subParser.addToken(token)
83
100
  if (stopState) {
84
101
  this._endSubExpression()
85
102
  if (stopState === '_semicolon') {
@@ -94,18 +111,10 @@ class Parser {
94
111
  return this._stopMap[token.type]!
95
112
  } else if (state.tokenTypes?.[token.type]) {
96
113
  const typeOpts = state.tokenTypes[token.type]!
97
- let handleFunc = (
98
- handlers as Record<
99
- string,
100
- ((this: Parser, token: Token) => void) | undefined
101
- >
102
- )[token.type]
103
- if (typeOpts.handler) {
104
- handleFunc = typeOpts.handler
105
- }
106
- if (handleFunc) {
107
- handleFunc.call(this, token)
108
- }
114
+ // the state's own handler wins over the token type's default one, and a
115
+ // token type with neither only drives the transition below
116
+ const handleFunc = typeOpts.handler ?? tokenHandlers[token.type]
117
+ handleFunc?.call(this, token)
109
118
  if (typeOpts.toState) {
110
119
  this._state = typeOpts.toState
111
120
  }
@@ -140,7 +149,14 @@ class Parser {
140
149
  * the expression, indicating that the expression is incomplete
141
150
  */
142
151
  complete() {
143
- if (this._cursor && !states[this._state]!.completable) {
152
+ // `_subParser` counts as unfinished business alongside `_cursor`: a group
153
+ // that opened before anything was placed in the tree, as in "(1", leaves
154
+ // the cursor null, and testing the cursor alone let the missing ")" pass
155
+ // silently and evaluate as if it were closed.
156
+ if (
157
+ (this._cursor || this._subParser) &&
158
+ !states[this._state]!.completable
159
+ ) {
144
160
  throw new Error(`Unexpected end of expression: ${this._exprStr}`)
145
161
  }
146
162
  if (this._subParser) {
@@ -231,6 +247,7 @@ class Parser {
231
247
  * Prepares the Parser to accept a subexpression by (re)instantiating the
232
248
  * subParser.
233
249
  * @param {string} [exprStr] The expression string to prefix to the new Parser
250
+ * @returns {Parser} the new subParser, also stored on this instance
234
251
  * @private
235
252
  */
236
253
  _startSubExpression(exprStr?: string) {
@@ -243,6 +260,7 @@ class Parser {
243
260
  endStates = { ...endStates, semicolon: '_semicolon' }
244
261
  }
245
262
  this._subParser = new Parser(this._grammar, this._lexer, exprStr, endStates)
263
+ return this._subParser
246
264
  }
247
265
  }
248
266
 
@@ -4,6 +4,9 @@
4
4
  */
5
5
 
6
6
  import { precedenceOf } from '../grammar.ts'
7
+ // circular with Parser.ts, which is fine: the binding is only read from inside
8
+ // a handler, long after both modules have finished evaluating
9
+ import Parser from './Parser.ts'
7
10
 
8
11
  import type {
9
12
  ArrayLiteral,
@@ -20,7 +23,6 @@ import type {
20
23
  Token,
21
24
  UnaryExpression
22
25
  } from '../types.ts'
23
- import type Parser from './Parser.ts'
24
26
 
25
27
  /**
26
28
  * Handles a subexpression that's used to define a transform argument's value.
@@ -145,13 +147,26 @@ export function filter(this: Parser, ast: AstNode | null) {
145
147
  /**
146
148
  * Handles identifier tokens when used to indicate the name of a function to
147
149
  * be called.
148
- * @param {{type: <string>}} token A token object
150
+ *
151
+ * A call written against a value, `a.b(x)`, becomes `b(a, x)`: jexl functions
152
+ * live in one global pool and have no receiver, so the value the name was read
153
+ * from is passed as the first argument instead. Every function in the pool is
154
+ * already written that way — `get(feature, key)`, `split(str, sep)` — which is
155
+ * what makes the two spellings equivalent. Previously the receiver was simply
156
+ * dropped, so `refName.split(' ')` quietly called `split(' ')`.
149
157
  */
150
158
  export function functionCall(this: Parser) {
159
+ const cursor = this._cursor
160
+ if (cursor?.type !== 'Identifier') {
161
+ // reached by `a[expr]()`, whose subject is a filter rather than a name;
162
+ // there is nothing to look up in the function pool
163
+ throw new Error(`Functions must be called by name: ${this._exprStr}`)
164
+ }
165
+ const { from } = cursor as Identifier
151
166
  const node: FunctionCall = {
152
167
  type: 'FunctionCall',
153
- name: (this._cursor as Identifier).value,
154
- args: [],
168
+ name: (cursor as Identifier).value,
169
+ args: from ? [from] : [],
155
170
  pool: 'functions'
156
171
  }
157
172
  this._placeBeforeCursor(node)
@@ -209,11 +224,7 @@ export function templateString(this: Parser, token: Token) {
209
224
  })
210
225
  } else {
211
226
  const subTokens = this._lexer.tokenize(part.value)
212
- const ParserClass = this.constructor as new (
213
- grammar: typeof this._grammar,
214
- lexer: typeof this._lexer
215
- ) => Parser
216
- const subParser = new ParserClass(this._grammar, this._lexer)
227
+ const subParser = new Parser(this._grammar, this._lexer)
217
228
  subParser.addTokens(subTokens)
218
229
  const subAst = subParser.complete()
219
230
 
@@ -261,7 +272,19 @@ export function objStart(this: Parser) {
261
272
  * @param {{type: <string>}} ast The subexpression tree
262
273
  */
263
274
  export function objVal(this: Parser, ast: AstNode | null) {
264
- ;(this._cursor as ObjectLiteral).value[this._curObjKey!] = ast!
275
+ // defined rather than assigned so that the key "__proto__" becomes an
276
+ // ordinary entry of the node's key map instead of re-pointing its prototype,
277
+ // which dropped the entry before the Evaluator ever saw it
278
+ Object.defineProperty(
279
+ (this._cursor as ObjectLiteral).value,
280
+ this._curObjKey!,
281
+ {
282
+ value: ast!,
283
+ writable: true,
284
+ enumerable: true,
285
+ configurable: true
286
+ }
287
+ )
265
288
  }
266
289
 
267
290
  /**