@jbrowse/jexl 5.0.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/Jexl.js +12 -16
- package/dist/Jexl.js.map +1 -1
- package/dist/Lexer.d.ts +12 -94
- package/dist/Lexer.js +41 -142
- package/dist/Lexer.js.map +1 -1
- package/dist/analyze.d.ts +2 -0
- package/dist/analyze.js +7 -7
- package/dist/analyze.js.map +1 -1
- package/dist/check.js +30 -30
- package/dist/check.js.map +1 -1
- package/dist/collections.d.ts +7 -0
- package/dist/collections.js +9 -2
- package/dist/collections.js.map +1 -1
- package/dist/conditions.d.ts +1 -1
- package/dist/conditions.js +25 -47
- package/dist/conditions.js.map +1 -1
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +8 -0
- package/dist/errors.js.map +1 -1
- package/dist/evaluator/compile.js +37 -24
- package/dist/evaluator/compile.js.map +1 -1
- package/dist/grammar.d.ts +2 -2
- package/dist/grammar.js +1 -8
- package/dist/grammar.js.map +1 -1
- package/dist/operators.d.ts +5 -0
- package/dist/operators.js +9 -5
- package/dist/operators.js.map +1 -1
- package/dist/parser/Parser.d.ts +11 -12
- package/dist/parser/Parser.js +4 -10
- package/dist/parser/Parser.js.map +1 -1
- package/dist/print.d.ts +1 -1
- package/dist/print.js +41 -47
- package/dist/print.js.map +1 -1
- package/dist/types.d.ts +18 -21
- package/esm/Jexl.js +12 -16
- package/esm/Jexl.js.map +1 -1
- package/esm/Lexer.d.ts +12 -94
- package/esm/Lexer.js +40 -142
- package/esm/Lexer.js.map +1 -1
- package/esm/analyze.d.ts +2 -0
- package/esm/analyze.js +6 -7
- package/esm/analyze.js.map +1 -1
- package/esm/check.js +28 -28
- package/esm/check.js.map +1 -1
- package/esm/collections.d.ts +7 -0
- package/esm/collections.js +8 -1
- package/esm/collections.js.map +1 -1
- package/esm/conditions.d.ts +1 -1
- package/esm/conditions.js +26 -48
- package/esm/conditions.js.map +1 -1
- package/esm/errors.d.ts +5 -0
- package/esm/errors.js +7 -0
- package/esm/errors.js.map +1 -1
- package/esm/evaluator/compile.js +37 -24
- package/esm/evaluator/compile.js.map +1 -1
- package/esm/grammar.d.ts +2 -2
- package/esm/grammar.js +1 -8
- package/esm/grammar.js.map +1 -1
- package/esm/operators.d.ts +5 -0
- package/esm/operators.js +8 -5
- package/esm/operators.js.map +1 -1
- package/esm/parser/Parser.d.ts +11 -12
- package/esm/parser/Parser.js +4 -8
- package/esm/parser/Parser.js.map +1 -1
- package/esm/print.d.ts +1 -1
- package/esm/print.js +41 -47
- package/esm/print.js.map +1 -1
- package/esm/types.d.ts +18 -21
- package/package.json +1 -1
- package/src/Jexl.ts +13 -20
- package/src/Lexer.ts +42 -144
- package/src/analyze.ts +10 -10
- package/src/check.ts +47 -53
- package/src/collections.ts +11 -1
- package/src/conditions.ts +38 -61
- package/src/errors.ts +10 -0
- package/src/evaluator/compile.ts +49 -36
- package/src/grammar.ts +3 -9
- package/src/operators.ts +10 -6
- package/src/parser/Parser.ts +13 -19
- package/src/print.ts +50 -52
- package/src/types.ts +19 -23
package/src/grammar.ts
CHANGED
|
@@ -56,9 +56,9 @@ export interface BinaryOp {
|
|
|
56
56
|
|
|
57
57
|
export type UnaryOpEval = (right: JexlValue) => JexlValue
|
|
58
58
|
|
|
59
|
+
/** A prefix operator, which binds tighter than any binary one. */
|
|
59
60
|
export interface UnaryOp {
|
|
60
61
|
type: 'unaryOp'
|
|
61
|
-
precedence: number
|
|
62
62
|
eval: UnaryOpEval
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -82,6 +82,7 @@ export interface SimpleElement {
|
|
|
82
82
|
| 'question'
|
|
83
83
|
| 'semicolon'
|
|
84
84
|
| 'arrow'
|
|
85
|
+
| 'assign'
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
export type GrammarElement = BinaryOp | UnaryOp | SimpleElement
|
|
@@ -174,6 +175,7 @@ export const getGrammar = (): Grammar => ({
|
|
|
174
175
|
'?': { type: 'question' },
|
|
175
176
|
';': { type: 'semicolon' },
|
|
176
177
|
'=>': { type: 'arrow' },
|
|
178
|
+
'=': { type: 'assign' },
|
|
177
179
|
'+': {
|
|
178
180
|
type: 'binaryOp',
|
|
179
181
|
precedence: 30,
|
|
@@ -299,15 +301,7 @@ export const getGrammar = (): Grammar => ({
|
|
|
299
301
|
},
|
|
300
302
|
'!': {
|
|
301
303
|
type: 'unaryOp',
|
|
302
|
-
precedence: Infinity,
|
|
303
304
|
eval: (right) => !right
|
|
304
|
-
},
|
|
305
|
-
'=': {
|
|
306
|
-
type: 'binaryOp',
|
|
307
|
-
precedence: 2,
|
|
308
|
-
eval: (_left, _right) => {
|
|
309
|
-
throw new Error('Assignment handled specially')
|
|
310
|
-
}
|
|
311
305
|
}
|
|
312
306
|
},
|
|
313
307
|
|
package/src/operators.ts
CHANGED
|
@@ -74,20 +74,24 @@ export function pairwise(
|
|
|
74
74
|
return out
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
const patterns = new Map<string, RegExp>()
|
|
78
|
-
const MAX_PATTERNS = 1000
|
|
79
|
-
|
|
80
77
|
/**
|
|
81
78
|
* The regular expression a pattern string stands for. A leading `(?i)`, the
|
|
82
79
|
* inline flag PCRE, Python and Go read, makes it case-insensitive.
|
|
83
80
|
*/
|
|
81
|
+
export function toRegExp(pattern: string) {
|
|
82
|
+
return pattern.startsWith('(?i)')
|
|
83
|
+
? new RegExp(pattern.slice(4), 'i')
|
|
84
|
+
: new RegExp(pattern)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const patterns = new Map<string, RegExp>()
|
|
88
|
+
const MAX_PATTERNS = 1000
|
|
89
|
+
|
|
84
90
|
function regex(pattern: string) {
|
|
85
91
|
let re = patterns.get(pattern)
|
|
86
92
|
if (!re) {
|
|
87
93
|
try {
|
|
88
|
-
re = pattern
|
|
89
|
-
? new RegExp(pattern.slice(4), 'i')
|
|
90
|
-
: new RegExp(pattern)
|
|
94
|
+
re = toRegExp(pattern)
|
|
91
95
|
} catch {
|
|
92
96
|
throw new Error(`Invalid regular expression: ${pattern}`)
|
|
93
97
|
}
|
package/src/parser/Parser.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type Lexer from '../Lexer.ts'
|
|
|
9
9
|
import type { Grammar } from '../grammar.ts'
|
|
10
10
|
import type {
|
|
11
11
|
AstNode,
|
|
12
|
-
AstNodeUnion,
|
|
13
12
|
ConditionalExpression,
|
|
14
13
|
Lambda,
|
|
15
14
|
Literal,
|
|
@@ -29,8 +28,6 @@ const omittedAlternateBefore = new Set([
|
|
|
29
28
|
'semicolon'
|
|
30
29
|
])
|
|
31
30
|
|
|
32
|
-
export { JexlSyntaxError } from '../errors.ts'
|
|
33
|
-
|
|
34
31
|
/**
|
|
35
32
|
* Converts the tokens from the {@link Lexer} into an Abstract Syntax Tree, for
|
|
36
33
|
* {@link compileAst} to lower to closures. A Pratt parser: each level of the
|
|
@@ -79,7 +76,7 @@ class Parser {
|
|
|
79
76
|
* @returns the expression tree, or null for an expression with no tokens
|
|
80
77
|
* @throws {JexlSyntaxError} if the tokens do not form exactly one expression
|
|
81
78
|
*/
|
|
82
|
-
complete():
|
|
79
|
+
complete(): AstNode | null {
|
|
83
80
|
if (this._tokens.length === 0) {
|
|
84
81
|
return null
|
|
85
82
|
}
|
|
@@ -90,7 +87,7 @@ class Parser {
|
|
|
90
87
|
return ast
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
_sequence():
|
|
90
|
+
_sequence(): AstNode {
|
|
94
91
|
const first = this._assignment()
|
|
95
92
|
if (this._peek()?.type !== 'semicolon') {
|
|
96
93
|
return first
|
|
@@ -106,13 +103,12 @@ class Parser {
|
|
|
106
103
|
return { type: 'SequenceExpression', expressions }
|
|
107
104
|
}
|
|
108
105
|
|
|
109
|
-
_assignment():
|
|
106
|
+
_assignment(): AstNode {
|
|
110
107
|
if (this._atLambda()) {
|
|
111
108
|
return this._lambda()
|
|
112
109
|
}
|
|
113
110
|
const left = this._ternary()
|
|
114
|
-
|
|
115
|
-
if (token?.type !== 'binaryOp' || token.value !== '=') {
|
|
111
|
+
if (this._peek()?.type !== 'assign') {
|
|
116
112
|
return left
|
|
117
113
|
}
|
|
118
114
|
if (left.type !== 'Identifier' || left.from) {
|
|
@@ -176,7 +172,7 @@ class Parser {
|
|
|
176
172
|
return { type: 'Lambda', params, body }
|
|
177
173
|
}
|
|
178
174
|
|
|
179
|
-
_ternary():
|
|
175
|
+
_ternary(): AstNode {
|
|
180
176
|
const test = this._binary(-Infinity)
|
|
181
177
|
if (!this._eat('question')) {
|
|
182
178
|
return test
|
|
@@ -198,11 +194,11 @@ class Parser {
|
|
|
198
194
|
* One exactly at `floor` binds only when it continues a right-associative
|
|
199
195
|
* chain, which is how `a ^ b ^ c` groups from the right.
|
|
200
196
|
*/
|
|
201
|
-
_binary(floor: number, rightAssociative = false):
|
|
197
|
+
_binary(floor: number, rightAssociative = false): AstNode {
|
|
202
198
|
let left = this._unary()
|
|
203
199
|
for (;;) {
|
|
204
200
|
const token = this._peek()
|
|
205
|
-
if (token?.type !== 'binaryOp'
|
|
201
|
+
if (token?.type !== 'binaryOp') {
|
|
206
202
|
return left
|
|
207
203
|
}
|
|
208
204
|
const operator = token.value as string
|
|
@@ -224,7 +220,7 @@ class Parser {
|
|
|
224
220
|
}
|
|
225
221
|
|
|
226
222
|
/** `a ?? b || c` has no grouping a reader can guess; JS refuses it too. */
|
|
227
|
-
_mixesNullish(operator: string, ...operands:
|
|
223
|
+
_mixesNullish(operator: string, ...operands: AstNode[]) {
|
|
228
224
|
return operands.some(
|
|
229
225
|
(operand) =>
|
|
230
226
|
operand.type === 'BinaryExpression' &&
|
|
@@ -235,22 +231,20 @@ class Parser {
|
|
|
235
231
|
)
|
|
236
232
|
}
|
|
237
233
|
|
|
238
|
-
_unary():
|
|
234
|
+
_unary(): AstNode {
|
|
239
235
|
const token = this._peek()
|
|
240
236
|
if (token?.type !== 'unaryOp') {
|
|
241
237
|
return this._postfix(this._primary())
|
|
242
238
|
}
|
|
243
239
|
this._pos++
|
|
244
|
-
const operator = token.value as string
|
|
245
|
-
const op = this._grammar.elements[operator]
|
|
246
240
|
return {
|
|
247
241
|
type: 'UnaryExpression',
|
|
248
|
-
operator,
|
|
249
|
-
right: this.
|
|
242
|
+
operator: token.value as string,
|
|
243
|
+
right: this._unary()
|
|
250
244
|
}
|
|
251
245
|
}
|
|
252
246
|
|
|
253
|
-
_postfix(subject:
|
|
247
|
+
_postfix(subject: AstNode): AstNode {
|
|
254
248
|
let node = subject
|
|
255
249
|
for (;;) {
|
|
256
250
|
switch (this._peek()?.type) {
|
|
@@ -294,7 +288,7 @@ class Parser {
|
|
|
294
288
|
}
|
|
295
289
|
}
|
|
296
290
|
|
|
297
|
-
_primary():
|
|
291
|
+
_primary(): AstNode {
|
|
298
292
|
const token = this._next()
|
|
299
293
|
switch (token.type) {
|
|
300
294
|
case 'literal': {
|
package/src/print.ts
CHANGED
|
@@ -3,34 +3,20 @@
|
|
|
3
3
|
* Copyright 2020 Tom Shawver
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { identRegex } from './Lexer.ts'
|
|
7
|
+
import { getGrammar } from './grammar.ts'
|
|
7
8
|
|
|
8
|
-
type
|
|
9
|
+
import type { AstNode, BinaryExpression } from './types.ts'
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'
|
|
17
|
-
'~': 20,
|
|
18
|
-
'!~': 20,
|
|
19
|
-
'<': 20,
|
|
20
|
-
'<=': 20,
|
|
21
|
-
'>': 20,
|
|
22
|
-
'>=': 20,
|
|
23
|
-
in: 20,
|
|
24
|
-
'+': 30,
|
|
25
|
-
'-': 30,
|
|
26
|
-
'*': 40,
|
|
27
|
-
'/': 40,
|
|
28
|
-
'//': 40,
|
|
29
|
-
'%': 50,
|
|
30
|
-
'^': 50
|
|
11
|
+
const { elements } = getGrammar()
|
|
12
|
+
|
|
13
|
+
// print knows where only the built-in operators bind, so a host's own ones
|
|
14
|
+
// print with their operands parenthesized
|
|
15
|
+
function builtIn(operator: string) {
|
|
16
|
+
const element = elements[operator]
|
|
17
|
+
return element?.type === 'binaryOp' ? element : undefined
|
|
31
18
|
}
|
|
32
19
|
|
|
33
|
-
const RIGHT_ASSOCIATIVE = new Set(['^'])
|
|
34
20
|
const LOGICAL = new Set(['||', '&&'])
|
|
35
21
|
|
|
36
22
|
// how tightly each form binds, mirroring the Parser's levels: `;`, then `=`
|
|
@@ -41,12 +27,15 @@ const CONDITIONAL = 2
|
|
|
41
27
|
const CUSTOM_BINARY = 3
|
|
42
28
|
const UNARY = 100
|
|
43
29
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
30
|
+
const WORDS = new Set([
|
|
31
|
+
'true',
|
|
32
|
+
'false',
|
|
33
|
+
'null',
|
|
34
|
+
...Object.keys(elements).filter((word) => identRegex.test(word))
|
|
35
|
+
])
|
|
47
36
|
|
|
48
37
|
export function isName(text: string) {
|
|
49
|
-
return
|
|
38
|
+
return identRegex.test(text) && !WORDS.has(text)
|
|
50
39
|
}
|
|
51
40
|
|
|
52
41
|
export function key(name: string) {
|
|
@@ -57,7 +46,7 @@ export function quote(text: string) {
|
|
|
57
46
|
return `'${text.replaceAll('\\', '\\\\').replaceAll("'", String.raw`\'`)}'`
|
|
58
47
|
}
|
|
59
48
|
|
|
60
|
-
function precedenceOf(node:
|
|
49
|
+
function precedenceOf(node: AstNode) {
|
|
61
50
|
switch (node.type) {
|
|
62
51
|
case 'SequenceExpression': {
|
|
63
52
|
return SEQUENCE
|
|
@@ -71,7 +60,7 @@ function precedenceOf(node: Node) {
|
|
|
71
60
|
return node.alternate ? CONDITIONAL : SEQUENCE
|
|
72
61
|
}
|
|
73
62
|
case 'BinaryExpression': {
|
|
74
|
-
return
|
|
63
|
+
return builtIn(node.operator)?.precedence ?? CUSTOM_BINARY
|
|
75
64
|
}
|
|
76
65
|
case 'UnaryExpression': {
|
|
77
66
|
return UNARY
|
|
@@ -82,27 +71,31 @@ function precedenceOf(node: Node) {
|
|
|
82
71
|
}
|
|
83
72
|
}
|
|
84
73
|
|
|
85
|
-
function within(
|
|
86
|
-
const text = print(
|
|
87
|
-
return precedenceOf(
|
|
74
|
+
function within(node: AstNode, least: number) {
|
|
75
|
+
const text = print(node)
|
|
76
|
+
return precedenceOf(node) < least ? `(${text})` : text
|
|
88
77
|
}
|
|
89
78
|
|
|
90
|
-
function binaryOperand(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
function binaryOperand(
|
|
80
|
+
parent: BinaryExpression,
|
|
81
|
+
child: AstNode,
|
|
82
|
+
left: boolean
|
|
83
|
+
) {
|
|
84
|
+
const op = builtIn(parent.operator)
|
|
85
|
+
if (!op) {
|
|
86
|
+
return within(child, UNARY)
|
|
94
87
|
}
|
|
95
|
-
const
|
|
88
|
+
const own = op.precedence
|
|
96
89
|
const theirs = precedenceOf(child)
|
|
97
90
|
const chained =
|
|
98
91
|
child.type === 'BinaryExpression' &&
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
!!op.rightAssociative &&
|
|
93
|
+
!!builtIn(child.operator)?.rightAssociative
|
|
101
94
|
const mixesNullish =
|
|
102
95
|
child.type === 'BinaryExpression' &&
|
|
103
96
|
((parent.operator === '??' && LOGICAL.has(child.operator)) ||
|
|
104
97
|
(child.operator === '??' && LOGICAL.has(parent.operator)))
|
|
105
|
-
const text = print(
|
|
98
|
+
const text = print(child)
|
|
106
99
|
return theirs < own || (theirs === own && left === chained) || mixesNullish
|
|
107
100
|
? `(${text})`
|
|
108
101
|
: text
|
|
@@ -112,16 +105,21 @@ function binaryOperand(parent: BinaryExpression, ast: AstNode, left: boolean) {
|
|
|
112
105
|
* Renders a tree back to expression text, for messages and suggestions. The
|
|
113
106
|
* text parses back to the same tree.
|
|
114
107
|
*/
|
|
115
|
-
export function print(
|
|
116
|
-
const node = ast as Node
|
|
108
|
+
export function print(node: AstNode): string {
|
|
117
109
|
switch (node.type) {
|
|
118
110
|
case 'Literal': {
|
|
119
111
|
const { value } = node
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
112
|
+
if (typeof value === 'string') {
|
|
113
|
+
return quote(value)
|
|
114
|
+
}
|
|
115
|
+
// a number too large for a double reads as Infinity, which is a name
|
|
116
|
+
return Object.is(value, -0)
|
|
117
|
+
? '-0'
|
|
118
|
+
: value === Infinity
|
|
119
|
+
? '1e999'
|
|
120
|
+
: value === -Infinity
|
|
121
|
+
? '-1e999'
|
|
122
|
+
: String(value)
|
|
125
123
|
}
|
|
126
124
|
case 'Identifier': {
|
|
127
125
|
return node.from
|
|
@@ -132,16 +130,16 @@ export function print(ast: AstNode): string {
|
|
|
132
130
|
return `${within(node.subject, Infinity)}[${within(node.expr, ASSIGNMENT)}]`
|
|
133
131
|
}
|
|
134
132
|
case 'BinaryExpression': {
|
|
135
|
-
return `${binaryOperand(node, node.left, true)} ${node.operator} ${binaryOperand(node, node.right
|
|
133
|
+
return `${binaryOperand(node, node.left, true)} ${node.operator} ${binaryOperand(node, node.right, false)}`
|
|
136
134
|
}
|
|
137
135
|
case 'UnaryExpression': {
|
|
138
136
|
const { operator } = node
|
|
139
|
-
const right = within(node.right
|
|
137
|
+
const right = within(node.right, UNARY)
|
|
140
138
|
// the Lexer folds a minus before a digit into the number
|
|
141
139
|
if (operator === '-' && /^\d/.test(right)) {
|
|
142
140
|
return `-(${right})`
|
|
143
141
|
}
|
|
144
|
-
return
|
|
142
|
+
return identRegex.test(operator)
|
|
145
143
|
? `${operator} ${right}`
|
|
146
144
|
: operator + right
|
|
147
145
|
}
|
|
@@ -189,7 +187,7 @@ export function print(ast: AstNode): string {
|
|
|
189
187
|
return expressions.length === 1 ? `${text};` : text
|
|
190
188
|
}
|
|
191
189
|
case 'AssignmentExpression': {
|
|
192
|
-
return `${node.left.value} = ${within(node.right
|
|
190
|
+
return `${node.left.value} = ${within(node.right, ASSIGNMENT)}`
|
|
193
191
|
}
|
|
194
192
|
case 'Lambda': {
|
|
195
193
|
const params =
|
package/src/types.ts
CHANGED
|
@@ -27,41 +27,36 @@ export interface Token {
|
|
|
27
27
|
raw: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export interface
|
|
31
|
-
type: string
|
|
32
|
-
right?: AstNode
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface Literal extends AstNode {
|
|
30
|
+
export interface Literal {
|
|
36
31
|
type: 'Literal'
|
|
37
32
|
value: string | number | boolean | null
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
export interface Identifier
|
|
35
|
+
export interface Identifier {
|
|
41
36
|
type: 'Identifier'
|
|
42
37
|
value: string
|
|
43
38
|
from?: AstNode
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
export interface BinaryExpression
|
|
41
|
+
export interface BinaryExpression {
|
|
47
42
|
type: 'BinaryExpression'
|
|
48
43
|
operator: string
|
|
49
44
|
left: AstNode
|
|
50
|
-
right
|
|
45
|
+
right: AstNode
|
|
51
46
|
}
|
|
52
47
|
|
|
53
|
-
export interface UnaryExpression
|
|
48
|
+
export interface UnaryExpression {
|
|
54
49
|
type: 'UnaryExpression'
|
|
55
50
|
operator: string
|
|
56
|
-
right
|
|
51
|
+
right: AstNode
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
export interface ArrayLiteral
|
|
54
|
+
export interface ArrayLiteral {
|
|
60
55
|
type: 'ArrayLiteral'
|
|
61
56
|
value: AstNode[]
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
export interface ObjectLiteral
|
|
59
|
+
export interface ObjectLiteral {
|
|
65
60
|
type: 'ObjectLiteral'
|
|
66
61
|
value: Record<string, AstNode>
|
|
67
62
|
}
|
|
@@ -70,48 +65,49 @@ export type TemplateLiteralPart =
|
|
|
70
65
|
| { type: 'static'; value: string }
|
|
71
66
|
| { type: 'expression'; value: AstNode }
|
|
72
67
|
|
|
73
|
-
export interface TemplateLiteral
|
|
68
|
+
export interface TemplateLiteral {
|
|
74
69
|
type: 'TemplateLiteral'
|
|
75
70
|
parts: TemplateLiteralPart[]
|
|
76
71
|
}
|
|
77
72
|
|
|
78
|
-
export interface FunctionCall
|
|
73
|
+
export interface FunctionCall {
|
|
79
74
|
type: 'FunctionCall'
|
|
80
75
|
name: string
|
|
81
76
|
args: AstNode[]
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
export interface FilterExpression
|
|
79
|
+
export interface FilterExpression {
|
|
85
80
|
type: 'FilterExpression'
|
|
86
81
|
expr: AstNode
|
|
87
82
|
subject: AstNode
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
export interface ConditionalExpression
|
|
85
|
+
export interface ConditionalExpression {
|
|
91
86
|
type: 'ConditionalExpression'
|
|
92
87
|
test: AstNode
|
|
93
88
|
consequent?: AstNode
|
|
94
89
|
alternate?: AstNode
|
|
95
90
|
}
|
|
96
91
|
|
|
97
|
-
export interface SequenceExpression
|
|
92
|
+
export interface SequenceExpression {
|
|
98
93
|
type: 'SequenceExpression'
|
|
99
94
|
expressions: AstNode[]
|
|
100
95
|
}
|
|
101
96
|
|
|
102
|
-
export interface AssignmentExpression
|
|
97
|
+
export interface AssignmentExpression {
|
|
103
98
|
type: 'AssignmentExpression'
|
|
104
99
|
operator: '='
|
|
105
100
|
left: Identifier
|
|
101
|
+
right: AstNode
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
export interface Lambda
|
|
104
|
+
export interface Lambda {
|
|
109
105
|
type: 'Lambda'
|
|
110
106
|
params: string[]
|
|
111
107
|
body: AstNode
|
|
112
108
|
}
|
|
113
109
|
|
|
114
|
-
export type
|
|
110
|
+
export type AstNode =
|
|
115
111
|
| Literal
|
|
116
112
|
| Identifier
|
|
117
113
|
| BinaryExpression
|
|
@@ -126,7 +122,7 @@ export type AstNodeUnion =
|
|
|
126
122
|
| AssignmentExpression
|
|
127
123
|
| Lambda
|
|
128
124
|
|
|
129
|
-
export type NodeByType<T extends
|
|
130
|
-
|
|
125
|
+
export type NodeByType<T extends AstNode['type']> = Extract<
|
|
126
|
+
AstNode,
|
|
131
127
|
{ type: T }
|
|
132
128
|
>
|