@jbrowse/jexl 3.0.2 → 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 (76) hide show
  1. package/README.md +26 -2
  2. package/dist/Expression.d.ts +19 -7
  3. package/dist/Expression.js +32 -11
  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 -9
  9. package/dist/Lexer.js +61 -24
  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 +279 -0
  16. package/dist/evaluator/compile.js.map +1 -0
  17. package/dist/grammar.d.ts +29 -9
  18. package/dist/grammar.js +23 -7
  19. package/dist/grammar.js.map +1 -1
  20. package/dist/parser/Parser.d.ts +7 -8
  21. package/dist/parser/Parser.js +46 -14
  22. package/dist/parser/Parser.js.map +1 -1
  23. package/dist/parser/handlers.d.ts +8 -8
  24. package/dist/parser/handlers.js +84 -51
  25. package/dist/parser/handlers.js.map +1 -1
  26. package/dist/parser/states.d.ts +2 -2
  27. package/dist/parser/states.js +19 -40
  28. package/dist/parser/states.js.map +1 -1
  29. package/dist/types.d.ts +18 -7
  30. package/esm/Expression.d.ts +19 -7
  31. package/esm/Expression.js +32 -11
  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 -9
  37. package/esm/Lexer.js +61 -24
  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 +276 -0
  44. package/esm/evaluator/compile.js.map +1 -0
  45. package/esm/grammar.d.ts +29 -9
  46. package/esm/grammar.js +22 -7
  47. package/esm/grammar.js.map +1 -1
  48. package/esm/parser/Parser.d.ts +7 -8
  49. package/esm/parser/Parser.js +46 -14
  50. package/esm/parser/Parser.js.map +1 -1
  51. package/esm/parser/handlers.d.ts +8 -8
  52. package/esm/parser/handlers.js +81 -50
  53. package/esm/parser/handlers.js.map +1 -1
  54. package/esm/parser/states.d.ts +2 -2
  55. package/esm/parser/states.js +19 -40
  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 +31 -17
  60. package/src/Jexl.ts +59 -21
  61. package/src/Lexer.ts +63 -33
  62. package/src/evaluator/Evaluator.ts +27 -44
  63. package/src/evaluator/compile.ts +317 -0
  64. package/src/grammar.ts +69 -21
  65. package/src/parser/Parser.ts +50 -28
  66. package/src/parser/handlers.ts +122 -76
  67. package/src/parser/states.ts +22 -42
  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
@@ -3,8 +3,26 @@
3
3
  * Copyright 2020 Tom Shawver
4
4
  */
5
5
 
6
- import type { AstNode, Token } from '../types.ts'
7
- import type Parser from './Parser.ts'
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'
10
+
11
+ import type {
12
+ ArrayLiteral,
13
+ AssignmentExpression,
14
+ AstNode,
15
+ BinaryExpression,
16
+ ConditionalExpression,
17
+ FilterExpression,
18
+ FunctionCall,
19
+ Identifier,
20
+ Literal,
21
+ ObjectLiteral,
22
+ TemplateLiteral,
23
+ Token,
24
+ UnaryExpression
25
+ } from '../types.ts'
8
26
 
9
27
  /**
10
28
  * Handles a subexpression that's used to define a transform argument's value.
@@ -12,7 +30,7 @@ import type Parser from './Parser.ts'
12
30
  */
13
31
  export function argVal(this: Parser, ast: AstNode | null) {
14
32
  if (ast) {
15
- ;(this._cursor as any).args.push(ast)
33
+ ;(this._cursor as FunctionCall).args.push(ast)
16
34
  }
17
35
  }
18
36
 
@@ -21,10 +39,11 @@ export function argVal(this: Parser, ast: AstNode | null) {
21
39
  * initialized with an empty array.
22
40
  */
23
41
  export function arrayStart(this: Parser) {
24
- this._placeAtCursor({
42
+ const node: ArrayLiteral = {
25
43
  type: 'ArrayLiteral',
26
44
  value: []
27
- } as any)
45
+ }
46
+ this._placeAtCursor(node)
28
47
  }
29
48
 
30
49
  /**
@@ -33,7 +52,7 @@ export function arrayStart(this: Parser) {
33
52
  */
34
53
  export function arrayVal(this: Parser, ast: AstNode | null) {
35
54
  if (ast) {
36
- ;(this._cursor as any).value.push(ast)
55
+ ;(this._cursor as ArrayLiteral).value.push(ast)
37
56
  }
38
57
  }
39
58
 
@@ -44,34 +63,45 @@ export function arrayVal(this: Parser, ast: AstNode | null) {
44
63
  */
45
64
  export function binaryOp(this: Parser, token: Token) {
46
65
  if (token.value === '=') {
47
- if (this._cursor?.type !== 'Identifier') {
66
+ // `from` marks a member access such as `a.b`; only a bare name can be
67
+ // assigned to, since the Evaluator writes straight into the context
68
+ if (
69
+ this._cursor?.type !== 'Identifier' ||
70
+ (this._cursor as Identifier).from
71
+ ) {
48
72
  throw new Error('Left side of assignment must be a variable name')
49
73
  }
50
74
 
51
- const node: any = {
75
+ const node: AssignmentExpression = {
52
76
  type: 'AssignmentExpression',
53
77
  operator: '=',
54
- left: this._cursor
78
+ left: this._cursor as Identifier
55
79
  }
56
80
  this._cursor = this._cursor._parent
57
81
  this._placeAtCursor(node)
58
82
  return
59
83
  }
60
84
 
61
- const precedence = this._grammar.elements[token.value].precedence || 0
85
+ const tokenValue = token.value as string
86
+ const precedence = precedenceOf(this._grammar.elements[tokenValue])
62
87
  let parent = this._cursor?._parent
63
- while (
64
- parent &&
65
- (parent as any).operator &&
66
- this._grammar.elements[(parent as any).operator].precedence >= precedence
67
- ) {
88
+ while (parent) {
89
+ const parentExpr = parent as BinaryExpression
90
+ if (!parentExpr.operator) {
91
+ break
92
+ }
93
+ if (
94
+ precedenceOf(this._grammar.elements[parentExpr.operator]) < precedence
95
+ ) {
96
+ break
97
+ }
68
98
  this._cursor = parent
69
99
  parent = parent._parent
70
100
  }
71
- const node: any = {
101
+ const node: BinaryExpression = {
72
102
  type: 'BinaryExpression',
73
- operator: token.value,
74
- left: this._cursor
103
+ operator: tokenValue,
104
+ left: this._cursor!
75
105
  }
76
106
  this._setParent(this._cursor!, node)
77
107
  this._cursor = parent
@@ -84,16 +114,16 @@ export function binaryOp(this: Parser, token: Token) {
84
114
  * AST.
85
115
  */
86
116
  export function dot(this: Parser) {
87
- const isBinaryExprWithRight = (node: AstNode) =>
88
- node.type === 'BinaryExpression' && (node as any).right
89
-
117
+ const cursor = this._cursor
118
+ // the dot continues an identifier chain unless the cursor is an operator
119
+ // still waiting on its right-hand side, in which case it opens a relative
120
+ // path instead — as in the leading dot of `list[.price > 5]`
90
121
  this._nextIdentEncapsulate =
91
- this._cursor &&
92
- this._cursor.type !== 'UnaryExpression' &&
93
- (this._cursor.type !== 'BinaryExpression' ||
94
- isBinaryExprWithRight(this._cursor))
122
+ !!cursor &&
123
+ cursor.type !== 'UnaryExpression' &&
124
+ (cursor.type !== 'BinaryExpression' || !!cursor.right)
95
125
 
96
- this._nextIdentRelative = !this._cursor || !this._nextIdentEncapsulate
126
+ this._nextIdentRelative = !this._nextIdentEncapsulate
97
127
  if (this._nextIdentRelative) {
98
128
  this._relative = true
99
129
  }
@@ -105,26 +135,41 @@ export function dot(this: Parser) {
105
135
  * @param {{type: <string>}} ast The subexpression tree
106
136
  */
107
137
  export function filter(this: Parser, ast: AstNode | null) {
108
- this._placeBeforeCursor({
138
+ const node: FilterExpression = {
109
139
  type: 'FilterExpression',
110
- expr: ast,
140
+ expr: ast!,
111
141
  relative: this._subParser!.isRelative(),
112
- subject: this._cursor
113
- } as any)
142
+ subject: this._cursor!
143
+ }
144
+ this._placeBeforeCursor(node)
114
145
  }
115
146
 
116
147
  /**
117
148
  * Handles identifier tokens when used to indicate the name of a function to
118
149
  * be called.
119
- * @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(' ')`.
120
157
  */
121
158
  export function functionCall(this: Parser) {
122
- this._placeBeforeCursor({
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
166
+ const node: FunctionCall = {
123
167
  type: 'FunctionCall',
124
- name: (this._cursor as any).value,
125
- args: [],
168
+ name: (cursor as Identifier).value,
169
+ args: from ? [from] : [],
126
170
  pool: 'functions'
127
- } as any)
171
+ }
172
+ this._placeBeforeCursor(node)
128
173
  }
129
174
 
130
175
  /**
@@ -132,12 +177,12 @@ export function functionCall(this: Parser) {
132
177
  * @param {{type: <string>}} token A token object
133
178
  */
134
179
  export function identifier(this: Parser, token: Token) {
135
- const node: any = {
180
+ const node: Identifier = {
136
181
  type: 'Identifier',
137
- value: token.value
182
+ value: token.value as string
138
183
  }
139
184
  if (this._nextIdentEncapsulate) {
140
- node.from = this._cursor
185
+ node.from = this._cursor!
141
186
  this._placeBeforeCursor(node)
142
187
  this._nextIdentEncapsulate = false
143
188
  } else {
@@ -155,10 +200,11 @@ export function identifier(this: Parser, token: Token) {
155
200
  * @param {{type: <string>}} token A token object
156
201
  */
157
202
  export function literal(this: Parser, token: Token) {
158
- this._placeAtCursor({
203
+ const node: Literal = {
159
204
  type: 'Literal',
160
- value: token.value
161
- } as any)
205
+ value: token.value as string | number | boolean
206
+ }
207
+ this._placeAtCursor(node)
162
208
  }
163
209
 
164
210
  /**
@@ -167,9 +213,10 @@ export function literal(this: Parser, token: Token) {
167
213
  * @param {{type: <string>}} token A token object
168
214
  */
169
215
  export function templateString(this: Parser, token: Token) {
170
- const parts: { type: 'static' | 'expression'; value: string | AstNode }[] = []
216
+ const parts: TemplateLiteral['parts'] = []
217
+ const tokenParts = Array.isArray(token.value) ? token.value : []
171
218
 
172
- for (const part of token.value) {
219
+ for (const part of tokenParts) {
173
220
  if (part.type === 'static') {
174
221
  parts.push({
175
222
  type: 'static',
@@ -177,10 +224,7 @@ export function templateString(this: Parser, token: Token) {
177
224
  })
178
225
  } else {
179
226
  const subTokens = this._lexer.tokenize(part.value)
180
- const subParser = new (this.constructor as any)(
181
- this._grammar,
182
- this._lexer
183
- )
227
+ const subParser = new Parser(this._grammar, this._lexer)
184
228
  subParser.addTokens(subTokens)
185
229
  const subAst = subParser.complete()
186
230
 
@@ -195,10 +239,11 @@ export function templateString(this: Parser, token: Token) {
195
239
  }
196
240
  }
197
241
 
198
- this._placeAtCursor({
242
+ const node: TemplateLiteral = {
199
243
  type: 'TemplateLiteral',
200
- parts: parts
201
- } as any)
244
+ parts
245
+ }
246
+ this._placeAtCursor(node)
202
247
  }
203
248
 
204
249
  /**
@@ -206,7 +251,7 @@ export function templateString(this: Parser, token: Token) {
206
251
  * @param {{type: <string>}} token A token object
207
252
  */
208
253
  export function objKey(this: Parser, token: Token) {
209
- this._curObjKey = token.value
254
+ this._curObjKey = token.value as string
210
255
  }
211
256
 
212
257
  /**
@@ -214,10 +259,11 @@ export function objKey(this: Parser, token: Token) {
214
259
  * initialized with an empty object.
215
260
  */
216
261
  export function objStart(this: Parser) {
217
- this._placeAtCursor({
262
+ const node: ObjectLiteral = {
218
263
  type: 'ObjectLiteral',
219
264
  value: {}
220
- } as any)
265
+ }
266
+ this._placeAtCursor(node)
221
267
  }
222
268
 
223
269
  /**
@@ -226,7 +272,19 @@ export function objStart(this: Parser) {
226
272
  * @param {{type: <string>}} ast The subexpression tree
227
273
  */
228
274
  export function objVal(this: Parser, ast: AstNode | null) {
229
- ;(this._cursor as any).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
+ )
230
288
  }
231
289
 
232
290
  /**
@@ -243,7 +301,7 @@ export function subExpression(this: Parser, ast: AstNode | null) {
243
301
  * @param {{type: <string>}} ast The subexpression tree
244
302
  */
245
303
  export function ternaryEnd(this: Parser, ast: AstNode | null) {
246
- ;(this._cursor as any).alternate = ast
304
+ ;(this._cursor as ConditionalExpression).alternate = ast ?? undefined
247
305
  }
248
306
 
249
307
  /**
@@ -251,7 +309,7 @@ export function ternaryEnd(this: Parser, ast: AstNode | null) {
251
309
  * @param {{type: <string>}} ast The subexpression tree
252
310
  */
253
311
  export function ternaryMid(this: Parser, ast: AstNode | null) {
254
- ;(this._cursor as any).consequent = ast
312
+ ;(this._cursor as ConditionalExpression).consequent = ast ?? undefined
255
313
  }
256
314
 
257
315
  /**
@@ -260,37 +318,25 @@ export function ternaryMid(this: Parser, ast: AstNode | null) {
260
318
  * test element.
261
319
  */
262
320
  export function ternaryStart(this: Parser) {
263
- this._tree = {
321
+ const node: ConditionalExpression = {
264
322
  type: 'ConditionalExpression',
265
- test: this._tree
266
- } as any
323
+ test: this._tree!
324
+ }
325
+ this._tree = node
267
326
  this._cursor = this._tree
268
327
  }
269
328
 
270
- /**
271
- * Handles identifier tokens when used to indicate the name of a transform to
272
- * be applied.
273
- * @param {{type: <string>}} token A token object
274
- */
275
- export function transform(this: Parser, token: Token) {
276
- this._placeBeforeCursor({
277
- type: 'FunctionCall',
278
- name: token.value,
279
- args: [this._cursor],
280
- pool: 'transforms'
281
- } as any)
282
- }
283
-
284
329
  /**
285
330
  * Handles token of type 'unaryOp', indicating that the operation has only
286
331
  * one input: a right side.
287
332
  * @param {{type: <string>}} token A token object
288
333
  */
289
334
  export function unaryOp(this: Parser, token: Token) {
290
- this._placeAtCursor({
335
+ const node: UnaryExpression = {
291
336
  type: 'UnaryExpression',
292
- operator: token.value
293
- } as any)
337
+ operator: token.value as string
338
+ }
339
+ this._placeAtCursor(node)
294
340
  }
295
341
 
296
342
  /**
@@ -5,12 +5,12 @@
5
5
 
6
6
  import * as h from './handlers.ts'
7
7
 
8
- import type { AstNode } from '../types.ts'
8
+ import type { AstNode, Token } from '../types.ts'
9
9
  import type Parser from './Parser.ts'
10
10
 
11
11
  interface TokenTypeOptions {
12
12
  toState?: string
13
- handler?: (this: Parser, ...args: any[]) => void
13
+ handler?: (this: Parser, token: Token) => void
14
14
  }
15
15
 
16
16
  interface StateDefinition {
@@ -22,6 +22,20 @@ interface StateDefinition {
22
22
 
23
23
  type States = Record<string, StateDefinition>
24
24
 
25
+ /**
26
+ * The tokens that may follow a finished operand: an operator to combine it
27
+ * with, a dot or subscript to keep drilling into it, a question mark opening a
28
+ * ternary, or a semicolon ending the statement. Shared by every state that
29
+ * sits on a complete value, which is also why each of them is completable.
30
+ */
31
+ const afterOperand: Record<string, TokenTypeOptions> = {
32
+ binaryOp: { toState: 'expectOperand' },
33
+ dot: { toState: 'traverse' },
34
+ openBracket: { toState: 'filter' },
35
+ question: { toState: 'ternaryMid', handler: h.ternaryStart },
36
+ semicolon: { handler: h.semicolon }
37
+ }
38
+
25
39
  /**
26
40
  * A mapping of all states in the finite state machine to a set of instructions
27
41
  * for handling or transitioning into other states. Each state can be handled
@@ -68,21 +82,9 @@ export const states: States = {
68
82
  }
69
83
  },
70
84
  expectBinOp: {
71
- tokenTypes: {
72
- binaryOp: { toState: 'expectOperand' },
73
- pipe: { toState: 'expectTransform' },
74
- dot: { toState: 'traverse' },
75
- openBracket: { toState: 'filter' },
76
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
77
- semicolon: { handler: h.semicolon }
78
- },
85
+ tokenTypes: afterOperand,
79
86
  completable: true
80
87
  },
81
- expectTransform: {
82
- tokenTypes: {
83
- identifier: { toState: 'postTransform', handler: h.transform }
84
- }
85
- },
86
88
  expectObjKey: {
87
89
  tokenTypes: {
88
90
  literal: { toState: 'expectKeyValSep', handler: h.objKey },
@@ -95,38 +97,16 @@ export const states: States = {
95
97
  colon: { toState: 'objVal' }
96
98
  }
97
99
  },
98
- postTransform: {
99
- tokenTypes: {
100
- openParen: { toState: 'argVal' },
101
- binaryOp: { toState: 'expectOperand' },
102
- dot: { toState: 'traverse' },
103
- openBracket: { toState: 'filter' },
104
- pipe: { toState: 'expectTransform' },
105
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
106
- semicolon: { handler: h.semicolon }
107
- },
108
- completable: true
109
- },
110
100
  postArgs: {
111
- tokenTypes: {
112
- binaryOp: { toState: 'expectOperand' },
113
- dot: { toState: 'traverse' },
114
- openBracket: { toState: 'filter' },
115
- pipe: { toState: 'expectTransform' },
116
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
117
- semicolon: { handler: h.semicolon }
118
- },
101
+ tokenTypes: afterOperand,
119
102
  completable: true
120
103
  },
121
104
  identifier: {
122
105
  tokenTypes: {
123
- binaryOp: { toState: 'expectOperand' },
124
- dot: { toState: 'traverse' },
125
- openBracket: { toState: 'filter' },
126
- openParen: { toState: 'argVal', handler: h.functionCall },
127
- pipe: { toState: 'expectTransform' },
128
- question: { toState: 'ternaryMid', handler: h.ternaryStart },
129
- semicolon: { handler: h.semicolon }
106
+ ...afterOperand,
107
+ // a name is the only operand that can be followed by an argument list,
108
+ // since a function is called by name rather than evaluated as a value
109
+ openParen: { toState: 'argVal', handler: h.functionCall }
130
110
  },
131
111
  completable: true
132
112
  },
package/src/types.ts CHANGED
@@ -3,15 +3,30 @@
3
3
  * Copyright 2020 Tom Shawver
4
4
  */
5
5
 
6
+ export interface TemplatePart {
7
+ type: 'static' | 'interpolation'
8
+ value: string
9
+ }
10
+
11
+ export type JexlValue =
12
+ | string
13
+ | number
14
+ | boolean
15
+ | null
16
+ | undefined
17
+ | JexlValue[]
18
+ | { [key: string]: JexlValue }
19
+
6
20
  export interface Token {
7
21
  type: string
8
- value: any
22
+ value: string | number | boolean | TemplatePart[]
9
23
  raw: string
10
24
  }
11
25
 
12
26
  export interface AstNode {
13
27
  type: string
14
28
  _parent?: AstNode
29
+ right?: AstNode
15
30
  }
16
31
 
17
32
  export interface Literal extends AstNode {
@@ -49,19 +64,20 @@ export interface ObjectLiteral extends AstNode {
49
64
  value: Record<string, AstNode>
50
65
  }
51
66
 
67
+ export type TemplateLiteralPart =
68
+ | { type: 'static'; value: string }
69
+ | { type: 'expression'; value: AstNode }
70
+
52
71
  export interface TemplateLiteral extends AstNode {
53
72
  type: 'TemplateLiteral'
54
- parts: {
55
- type: 'static' | 'expression'
56
- value: string | AstNode
57
- }[]
73
+ parts: TemplateLiteralPart[]
58
74
  }
59
75
 
60
76
  export interface FunctionCall extends AstNode {
61
77
  type: 'FunctionCall'
62
78
  name: string
63
79
  args: AstNode[]
64
- pool: 'functions' | 'transforms'
80
+ pool: 'functions'
65
81
  }
66
82
 
67
83
  export interface FilterExpression extends AstNode {
@@ -87,7 +103,6 @@ export interface AssignmentExpression extends AstNode {
87
103
  type: 'AssignmentExpression'
88
104
  operator: '='
89
105
  left: Identifier
90
- right: AstNode
91
106
  }
92
107
 
93
108
  export type AstNodeUnion =