@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.
Files changed (76) hide show
  1. package/README.md +11 -2
  2. package/dist/Expression.d.ts +16 -6
  3. package/dist/Expression.js +34 -8
  4. package/dist/Expression.js.map +1 -1
  5. package/dist/Jexl.d.ts +13 -12
  6. package/dist/Jexl.js +31 -30
  7. package/dist/Jexl.js.map +1 -1
  8. package/dist/Lexer.d.ts +9 -8
  9. package/dist/Lexer.js +58 -18
  10. package/dist/Lexer.js.map +1 -1
  11. package/dist/evaluator/Evaluator.d.ts +20 -34
  12. package/dist/evaluator/Evaluator.js +19 -64
  13. package/dist/evaluator/Evaluator.js.map +1 -1
  14. package/dist/evaluator/compile.d.ts +27 -0
  15. package/dist/evaluator/compile.js +261 -0
  16. package/dist/evaluator/compile.js.map +1 -0
  17. package/dist/grammar.d.ts +29 -9
  18. package/dist/grammar.js +19 -3
  19. package/dist/grammar.js.map +1 -1
  20. package/dist/parser/Parser.d.ts +5 -7
  21. package/dist/parser/Parser.js +17 -2
  22. package/dist/parser/Parser.js.map +1 -1
  23. package/dist/parser/handlers.d.ts +0 -6
  24. package/dist/parser/handlers.js +54 -46
  25. package/dist/parser/handlers.js.map +1 -1
  26. package/dist/parser/states.d.ts +2 -2
  27. package/dist/parser/states.js +0 -20
  28. package/dist/parser/states.js.map +1 -1
  29. package/dist/types.d.ts +18 -7
  30. package/esm/Expression.d.ts +16 -6
  31. package/esm/Expression.js +34 -8
  32. package/esm/Expression.js.map +1 -1
  33. package/esm/Jexl.d.ts +13 -12
  34. package/esm/Jexl.js +31 -30
  35. package/esm/Jexl.js.map +1 -1
  36. package/esm/Lexer.d.ts +9 -8
  37. package/esm/Lexer.js +58 -18
  38. package/esm/Lexer.js.map +1 -1
  39. package/esm/evaluator/Evaluator.d.ts +20 -34
  40. package/esm/evaluator/Evaluator.js +19 -31
  41. package/esm/evaluator/Evaluator.js.map +1 -1
  42. package/esm/evaluator/compile.d.ts +27 -0
  43. package/esm/evaluator/compile.js +258 -0
  44. package/esm/evaluator/compile.js.map +1 -0
  45. package/esm/grammar.d.ts +29 -9
  46. package/esm/grammar.js +18 -3
  47. package/esm/grammar.js.map +1 -1
  48. package/esm/parser/Parser.d.ts +5 -7
  49. package/esm/parser/Parser.js +17 -2
  50. package/esm/parser/Parser.js.map +1 -1
  51. package/esm/parser/handlers.d.ts +0 -6
  52. package/esm/parser/handlers.js +54 -45
  53. package/esm/parser/handlers.js.map +1 -1
  54. package/esm/parser/states.d.ts +2 -2
  55. package/esm/parser/states.js +0 -20
  56. package/esm/parser/states.js.map +1 -1
  57. package/esm/types.d.ts +18 -7
  58. package/package.json +25 -28
  59. package/src/Expression.ts +33 -13
  60. package/src/Jexl.ts +59 -21
  61. package/src/Lexer.ts +60 -27
  62. package/src/evaluator/Evaluator.ts +27 -44
  63. package/src/evaluator/compile.ts +310 -0
  64. package/src/grammar.ts +66 -17
  65. package/src/parser/Parser.ts +24 -20
  66. package/src/parser/handlers.ts +96 -73
  67. package/src/parser/states.ts +2 -22
  68. package/src/types.ts +22 -7
  69. package/CHANGELOG.md +0 -249
  70. package/dist/evaluator/handlers.d.ts +0 -112
  71. package/dist/evaluator/handlers.js +0 -208
  72. package/dist/evaluator/handlers.js.map +0 -1
  73. package/esm/evaluator/handlers.d.ts +0 -112
  74. package/esm/evaluator/handlers.js +0 -194
  75. package/esm/evaluator/handlers.js.map +0 -1
  76. package/src/evaluator/handlers.ts +0 -212
@@ -0,0 +1,310 @@
1
+ /*
2
+ * Jexl
3
+ * Copyright 2020 Tom Shawver
4
+ */
5
+
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'
23
+ import type Evaluator from './Evaluator.ts'
24
+
25
+ /**
26
+ * An AST node lowered to a closure. Calling it with an Evaluator (which carries
27
+ * the context and grammar) produces the node's value.
28
+ */
29
+ export type CompiledNode = (ev: Evaluator) => JexlValue
30
+
31
+ /** Renders an interpolated value for a template literal. */
32
+ function stringify(value: JexlValue) {
33
+ if (value == null) {
34
+ return ''
35
+ }
36
+ if (
37
+ typeof value === 'string' ||
38
+ typeof value === 'number' ||
39
+ typeof value === 'boolean'
40
+ ) {
41
+ return String(value)
42
+ }
43
+ return JSON.stringify(value)
44
+ }
45
+
46
+ /**
47
+ * Lowers an expression tree into a tree of closures, resolving each node's type
48
+ * and its operator's implementation once, at compile time, rather than
49
+ * re-dispatching on `node.type` for every node on every evaluation. An
50
+ * expression compiled once and evaluated per-item — the usual shape for
51
+ * per-feature config callbacks — pays the dispatch cost once instead of N
52
+ * times.
53
+ *
54
+ * Operators are bound here, so a grammar change after compilation requires
55
+ * recompiling, which is what {@link Expression#compile} already documents.
56
+ * Functions are looked up per call, since they are commonly registered after
57
+ * an expression has been compiled.
58
+ *
59
+ * @param {{}} ast An expression tree, as produced by the Parser
60
+ * @param {{}} grammar The grammar to resolve operators against
61
+ * @returns {function} a closure returning the expression's value
62
+ * @throws {Error} if the tree contains an unrecognized node type
63
+ */
64
+ export function compileAst(ast: AstNode, grammar: Grammar): CompiledNode {
65
+ switch (ast.type) {
66
+ case 'Literal': {
67
+ const { value } = ast as Literal
68
+ return () => value
69
+ }
70
+
71
+ case 'Identifier': {
72
+ const node = ast as Identifier
73
+ const name = node.value
74
+ if (!node.from) {
75
+ return node.relative
76
+ ? (ev) => ev._relContext[name]
77
+ : (ev) => ev._context[name]
78
+ }
79
+ const from = compileAst(node.from, grammar)
80
+ return (ev) => {
81
+ const context = from(ev)
82
+ if (context == null) {
83
+ return undefined
84
+ }
85
+ // an identifier chained off an array reads through its first element
86
+ const ctx = Array.isArray(context) ? context[0] : context
87
+ return ctx == null
88
+ ? undefined
89
+ : (ctx as Record<string, JexlValue>)[name]
90
+ }
91
+ }
92
+
93
+ case 'BinaryExpression': {
94
+ const node = ast as BinaryExpression
95
+ const op = grammar.elements[node.operator]
96
+ if (op?.type !== 'binaryOp') {
97
+ // matches the tree-walking behaviour: an unknown operator yields
98
+ // undefined without evaluating either operand
99
+ return () => undefined
100
+ }
101
+ const left = compileAst(node.left, grammar)
102
+ const right = compileAst(node.right!, grammar)
103
+ const { evalOnDemand } = op
104
+ if (evalOnDemand) {
105
+ // operands stay unevaluated behind an `eval` thunk, so operators such
106
+ // as && and || can short-circuit
107
+ return (ev) =>
108
+ evalOnDemand({ eval: () => left(ev) }, { eval: () => right(ev) })
109
+ }
110
+ const fn = op.eval
111
+ if (!fn) {
112
+ return (ev) => {
113
+ left(ev)
114
+ right(ev)
115
+ return undefined
116
+ }
117
+ }
118
+ return (ev) => fn(left(ev), right(ev))
119
+ }
120
+
121
+ case 'UnaryExpression': {
122
+ const node = ast as UnaryExpression
123
+ const right = compileAst(node.right!, grammar)
124
+ const elem = grammar.elements[node.operator]
125
+ const fn =
126
+ elem?.type === 'unaryOp'
127
+ ? elem.eval
128
+ : elem?.type === 'binaryOp'
129
+ ? elem.unaryEval
130
+ : undefined
131
+ if (!fn) {
132
+ // the operand is still evaluated for an unknown operator, as before
133
+ return (ev) => {
134
+ right(ev)
135
+ return undefined
136
+ }
137
+ }
138
+ return (ev) => fn(right(ev))
139
+ }
140
+
141
+ case 'ConditionalExpression': {
142
+ const node = ast as ConditionalExpression
143
+ const test = compileAst(node.test, grammar)
144
+ const consequent = node.consequent
145
+ ? compileAst(node.consequent, grammar)
146
+ : undefined
147
+ const alternate = node.alternate
148
+ ? compileAst(node.alternate, grammar)
149
+ : undefined
150
+ return (ev) => {
151
+ const res = test(ev)
152
+ if (res) {
153
+ // an omitted consequent ("a ?: b") yields the test result
154
+ return consequent ? consequent(ev) : res
155
+ }
156
+ return alternate ? alternate(ev) : undefined
157
+ }
158
+ }
159
+
160
+ case 'FilterExpression': {
161
+ const node = ast as FilterExpression
162
+ if (node.relative) {
163
+ // thrown on evaluation rather than compilation, as before
164
+ return () => {
165
+ throw new Error('Relative filter expressions are not supported')
166
+ }
167
+ }
168
+ const subject = compileAst(node.subject, grammar)
169
+ const index = compileAst(node.expr, grammar)
170
+ return (ev) => {
171
+ const subjectVal = subject(ev)
172
+ const indexVal = index(ev)
173
+ if (subjectVal == null) {
174
+ return undefined
175
+ }
176
+ return typeof indexVal === 'string' || typeof indexVal === 'number'
177
+ ? (subjectVal as Record<string | number, JexlValue>)[indexVal]
178
+ : undefined
179
+ }
180
+ }
181
+
182
+ case 'ArrayLiteral': {
183
+ const items = (ast as ArrayLiteral).value.map((item) =>
184
+ compileAst(item, grammar)
185
+ )
186
+ const len = items.length
187
+ return (ev) => {
188
+ const out: JexlValue[] = new Array(len)
189
+ for (let i = 0; i < len; i++) {
190
+ out[i] = items[i]!(ev)
191
+ }
192
+ return out
193
+ }
194
+ }
195
+
196
+ case 'ObjectLiteral': {
197
+ const entries = Object.entries((ast as ObjectLiteral).value)
198
+ const keys = entries.map(([key]) => key)
199
+ const values = entries.map(([, node]) => compileAst(node, grammar))
200
+ const len = keys.length
201
+ return (ev) => {
202
+ const out: Record<string, JexlValue> = {}
203
+ for (let i = 0; i < len; i++) {
204
+ out[keys[i]!] = values[i]!(ev)
205
+ }
206
+ return out
207
+ }
208
+ }
209
+
210
+ case 'TemplateLiteral': {
211
+ // 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))
220
+ }
221
+ )
222
+ return (ev) => {
223
+ let out = ''
224
+ for (const part of parts) {
225
+ out += part(ev)
226
+ }
227
+ return out
228
+ }
229
+ }
230
+
231
+ case 'FunctionCall': {
232
+ const node = ast as FunctionCall
233
+ const { name } = node
234
+ const args = node.args.map((arg) => compileAst(arg, grammar))
235
+ // hasOwn, so that inherited Object.prototype members such as `toString`
236
+ // and `constructor` aren't callable as Jexl functions. Resolved per call
237
+ // rather than baked in, since functions are routinely registered after
238
+ // an expression has been compiled.
239
+ const lookup = (ev: Evaluator) => {
240
+ const { functions } = ev._grammar
241
+ if (!Object.hasOwn(functions, name)) {
242
+ throw new Error(`Jexl Function ${name} is not defined.`)
243
+ }
244
+ return functions[name]!
245
+ }
246
+ // the small arities are spelled out so an ordinary call allocates no
247
+ // argument array and needs no spread
248
+ switch (args.length) {
249
+ case 0: {
250
+ return (ev) => lookup(ev)()
251
+ }
252
+ case 1: {
253
+ const [a0] = args as [CompiledNode]
254
+ return (ev) => lookup(ev)(a0(ev))
255
+ }
256
+ case 2: {
257
+ const [a0, a1] = args as [CompiledNode, CompiledNode]
258
+ return (ev) => lookup(ev)(a0(ev), a1(ev))
259
+ }
260
+ case 3: {
261
+ const [a0, a1, a2] = args as [
262
+ CompiledNode,
263
+ CompiledNode,
264
+ CompiledNode
265
+ ]
266
+ return (ev) => lookup(ev)(a0(ev), a1(ev), a2(ev))
267
+ }
268
+ default: {
269
+ const len = args.length
270
+ return (ev) => {
271
+ const vals: JexlValue[] = new Array(len)
272
+ for (let i = 0; i < len; i++) {
273
+ vals[i] = args[i]!(ev)
274
+ }
275
+ return lookup(ev)(...vals)
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ case 'SequenceExpression': {
282
+ const exprs = (ast as SequenceExpression).expressions.map((expr) =>
283
+ compileAst(expr, grammar)
284
+ )
285
+ const len = exprs.length
286
+ return (ev) => {
287
+ let last: JexlValue
288
+ for (let i = 0; i < len; i++) {
289
+ last = exprs[i]!(ev)
290
+ }
291
+ return last
292
+ }
293
+ }
294
+
295
+ case 'AssignmentExpression': {
296
+ const node = ast as AssignmentExpression
297
+ const name = node.left.value
298
+ const right = compileAst(node.right!, grammar)
299
+ return (ev) => {
300
+ const value = right(ev)
301
+ ev._context[name] = value
302
+ return value
303
+ }
304
+ }
305
+
306
+ default: {
307
+ throw new Error(`Corrupt AST: unknown node type '${ast.type}'`)
308
+ }
309
+ }
310
+ }
package/src/grammar.ts CHANGED
@@ -5,28 +5,70 @@
5
5
 
6
6
  /* eslint eqeqeq:0 */
7
7
 
8
+ import type { JexlValue } from './types.ts'
9
+
10
+ export type BinaryOpEval = (left: JexlValue, right: JexlValue) => JexlValue
11
+
12
+ export type BinaryOpEvalOnDemand = (
13
+ left: { eval: () => JexlValue },
14
+ right: { eval: () => JexlValue }
15
+ ) => JexlValue
16
+
8
17
  export interface BinaryOp {
9
18
  type: 'binaryOp'
10
19
  precedence: number
11
- eval?: (left: any, right: any) => any
12
- evalOnDemand?: (left: { eval: () => any }, right: { eval: () => any }) => any
20
+ eval?: BinaryOpEval
21
+ evalOnDemand?: BinaryOpEvalOnDemand
22
+ /**
23
+ * Evaluator for the operator's prefix form, if it has one. The Lexer emits a
24
+ * unaryOp token when such an operator appears where an operand is expected,
25
+ * which is how `-x` is distinguished from `a - x`.
26
+ */
27
+ unaryEval?: (right: JexlValue) => JexlValue
13
28
  }
14
29
 
15
30
  export interface UnaryOp {
16
31
  type: 'unaryOp'
17
32
  precedence: number
18
- eval: (right: any) => any
33
+ eval: (right: JexlValue) => JexlValue
19
34
  }
20
35
 
36
+ /**
37
+ * The punctuation elements of the grammar. Unlike operators these carry no
38
+ * behavior of their own; the Parser's state machine gives them meaning. The
39
+ * type is a literal union rather than `string` so that `GrammarElement` is a
40
+ * discriminated union, letting operator properties be accessed without casts.
41
+ */
21
42
  export interface SimpleElement {
22
- type: string
43
+ type:
44
+ | 'dot'
45
+ | 'openBracket'
46
+ | 'closeBracket'
47
+ | 'openCurl'
48
+ | 'closeCurl'
49
+ | 'colon'
50
+ | 'comma'
51
+ | 'openParen'
52
+ | 'closeParen'
53
+ | 'question'
54
+ | 'semicolon'
23
55
  }
24
56
 
25
57
  export type GrammarElement = BinaryOp | UnaryOp | SimpleElement
26
58
 
27
59
  export interface Grammar {
28
60
  elements: Record<string, GrammarElement>
29
- functions: Record<string, (...args: any[]) => any>
61
+ functions: Record<string, (...args: JexlValue[]) => JexlValue>
62
+ }
63
+
64
+ /**
65
+ * Returns the binding power of a grammar element, or 0 for elements that
66
+ * aren't operators and therefore don't participate in precedence.
67
+ */
68
+ export function precedenceOf(elem: GrammarElement | undefined) {
69
+ return elem && (elem.type === 'binaryOp' || elem.type === 'unaryOp')
70
+ ? elem.precedence
71
+ : 0
30
72
  }
31
73
 
32
74
  export const getGrammar = (): Grammar => ({
@@ -50,37 +92,38 @@ export const getGrammar = (): Grammar => ({
50
92
  '+': {
51
93
  type: 'binaryOp',
52
94
  precedence: 30,
53
- eval: (left, right) => left + right
95
+ eval: (left, right) => (left as number) + (right as number)
54
96
  },
55
97
  '-': {
56
98
  type: 'binaryOp',
57
99
  precedence: 30,
58
- eval: (left, right) => left - right
100
+ eval: (left, right) => (left as number) - (right as number),
101
+ unaryEval: (right) => -(right as number)
59
102
  },
60
103
  '*': {
61
104
  type: 'binaryOp',
62
105
  precedence: 40,
63
- eval: (left, right) => left * right
106
+ eval: (left, right) => (left as number) * (right as number)
64
107
  },
65
108
  '/': {
66
109
  type: 'binaryOp',
67
110
  precedence: 40,
68
- eval: (left, right) => left / right
111
+ eval: (left, right) => (left as number) / (right as number)
69
112
  },
70
113
  '//': {
71
114
  type: 'binaryOp',
72
115
  precedence: 40,
73
- eval: (left, right) => Math.floor(left / right)
116
+ eval: (left, right) => Math.floor((left as number) / (right as number))
74
117
  },
75
118
  '%': {
76
119
  type: 'binaryOp',
77
120
  precedence: 50,
78
- eval: (left, right) => left % right
121
+ eval: (left, right) => (left as number) % (right as number)
79
122
  },
80
123
  '^': {
81
124
  type: 'binaryOp',
82
125
  precedence: 50,
83
- eval: (left, right) => Math.pow(left, right)
126
+ eval: (left, right) => (left as number) ** (right as number)
84
127
  },
85
128
  '==': {
86
129
  type: 'binaryOp',
@@ -95,22 +138,22 @@ export const getGrammar = (): Grammar => ({
95
138
  '>': {
96
139
  type: 'binaryOp',
97
140
  precedence: 20,
98
- eval: (left, right) => left > right
141
+ eval: (left, right) => (left as number) > (right as number)
99
142
  },
100
143
  '>=': {
101
144
  type: 'binaryOp',
102
145
  precedence: 20,
103
- eval: (left, right) => left >= right
146
+ eval: (left, right) => (left as number) >= (right as number)
104
147
  },
105
148
  '<': {
106
149
  type: 'binaryOp',
107
150
  precedence: 20,
108
- eval: (left, right) => left < right
151
+ eval: (left, right) => (left as number) < (right as number)
109
152
  },
110
153
  '<=': {
111
154
  type: 'binaryOp',
112
155
  precedence: 20,
113
- eval: (left, right) => left <= right
156
+ eval: (left, right) => (left as number) <= (right as number)
114
157
  },
115
158
  '&&': {
116
159
  type: 'binaryOp',
@@ -139,7 +182,13 @@ export const getGrammar = (): Grammar => ({
139
182
  precedence: 20,
140
183
  eval: (left, right) => {
141
184
  if (typeof right === 'string') {
142
- return right.includes(left)
185
+ const leftStr =
186
+ typeof left === 'string' ||
187
+ typeof left === 'number' ||
188
+ typeof left === 'boolean'
189
+ ? String(left)
190
+ : ''
191
+ return right.includes(leftStr)
143
192
  }
144
193
  if (Array.isArray(right)) {
145
194
  return right.includes(left)
@@ -7,11 +7,8 @@ import * as handlers from './handlers.ts'
7
7
  import { states } from './states.ts'
8
8
 
9
9
  import type Lexer from '../Lexer.ts'
10
- import type { AstNode, Token } from '../types.ts'
11
-
12
- interface Grammar {
13
- elements: Record<string, any>
14
- }
10
+ import type { Grammar } from '../grammar.ts'
11
+ import type { AstNode, SequenceExpression, Token } from '../types.ts'
15
12
 
16
13
  /**
17
14
  * The Parser is a state machine that converts tokens from the {@link Lexer}
@@ -37,7 +34,7 @@ class Parser {
37
34
  _tree: AstNode | null
38
35
  _exprStr: string
39
36
  _relative: boolean
40
- _stopMap: Record<string, any>
37
+ _stopMap: Record<string, string>
41
38
  _cursor?: AstNode | null
42
39
  _subParser?: Parser
43
40
  _parentStop?: boolean
@@ -50,7 +47,7 @@ class Parser {
50
47
  grammar: Grammar,
51
48
  lexer: Lexer,
52
49
  prefix?: string,
53
- stopMap?: Record<string, any>
50
+ stopMap?: Record<string, string>
54
51
  ) {
55
52
  this._grammar = grammar
56
53
  this._lexer = lexer
@@ -71,11 +68,11 @@ class Parser {
71
68
  * @returns {boolean|*} the stopState value if this parser encountered a token
72
69
  * in the stopState mapb false if tokens can continue.
73
70
  */
74
- addToken(token: Token): any {
71
+ addToken(token: Token): string | false {
75
72
  if (this._state === 'complete') {
76
73
  throw new Error('Cannot add a new token to a completed Parser')
77
74
  }
78
- const state = states[this._state]
75
+ const state = states[this._state]!
79
76
  const startExpr = this._exprStr
80
77
  this._exprStr += token.raw
81
78
  if (state.subHandler) {
@@ -94,10 +91,15 @@ class Parser {
94
91
  }
95
92
  }
96
93
  } else if (token.type === 'semicolon' && this._stopMap[token.type]) {
97
- return this._stopMap[token.type]
94
+ return this._stopMap[token.type]!
98
95
  } else if (state.tokenTypes?.[token.type]) {
99
- const typeOpts = state.tokenTypes[token.type]
100
- let handleFunc = (handlers as any)[token.type]
96
+ 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]
101
103
  if (typeOpts.handler) {
102
104
  handleFunc = typeOpts.handler
103
105
  }
@@ -108,7 +110,7 @@ class Parser {
108
110
  this._state = typeOpts.toState
109
111
  }
110
112
  } else if (this._stopMap[token.type]) {
111
- return this._stopMap[token.type]
113
+ return this._stopMap[token.type]!
112
114
  } else {
113
115
  throw new Error(
114
116
  `Token ${token.raw} (${token.type}) unexpected in expression: ${this._exprStr}`
@@ -124,7 +126,9 @@ class Parser {
124
126
  * the {@link Lexer#tokenize} function.
125
127
  */
126
128
  addTokens(tokens: Token[]) {
127
- tokens.forEach((token) => this.addToken(token))
129
+ for (const token of tokens) {
130
+ this.addToken(token)
131
+ }
128
132
  }
129
133
 
130
134
  /**
@@ -136,7 +140,7 @@ class Parser {
136
140
  * the expression, indicating that the expression is incomplete
137
141
  */
138
142
  complete() {
139
- if (this._cursor && !states[this._state].completable) {
143
+ if (this._cursor && !states[this._state]!.completable) {
140
144
  throw new Error(`Unexpected end of expression: ${this._exprStr}`)
141
145
  }
142
146
  if (this._subParser) {
@@ -147,7 +151,7 @@ class Parser {
147
151
  if (this._tree) {
148
152
  this._sequenceExpressions.push(this._tree)
149
153
  }
150
- const sequence: any = {
154
+ const sequence: SequenceExpression = {
151
155
  type: 'SequenceExpression',
152
156
  expressions: this._sequenceExpressions
153
157
  }
@@ -173,7 +177,7 @@ class Parser {
173
177
  * @private
174
178
  */
175
179
  _endSubExpression() {
176
- states[this._state].subHandler!.call(this, this._subParser!.complete())
180
+ states[this._state]!.subHandler!.call(this, this._subParser!.complete())
177
181
  this._subParser = undefined
178
182
  }
179
183
 
@@ -188,7 +192,7 @@ class Parser {
188
192
  if (!this._cursor) {
189
193
  this._tree = node
190
194
  } else {
191
- ;(this._cursor as any).right = node
195
+ this._cursor.right = node
192
196
  this._setParent(node, this._cursor)
193
197
  }
194
198
  this._cursor = node
@@ -230,12 +234,12 @@ class Parser {
230
234
  * @private
231
235
  */
232
236
  _startSubExpression(exprStr?: string) {
233
- let endStates = states[this._state].endStates
237
+ let endStates = states[this._state]!.endStates
234
238
  if (!endStates) {
235
239
  this._parentStop = true
236
240
  endStates = this._stopMap
237
241
  }
238
- if (states[this._state].completable && !endStates.semicolon) {
242
+ if (states[this._state]!.completable && !endStates.semicolon) {
239
243
  endStates = { ...endStates, semicolon: '_semicolon' }
240
244
  }
241
245
  this._subParser = new Parser(this._grammar, this._lexer, exprStr, endStates)