simplex-lang 0.2.2 → 1.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 +530 -13
- package/build/parser/index.js +1161 -479
- package/build/parser/index.js.map +1 -1
- package/build/src/compiler.d.ts +26 -18
- package/build/src/compiler.js +211 -457
- package/build/src/compiler.js.map +1 -1
- package/build/src/constants.d.ts +25 -0
- package/build/src/constants.js +29 -0
- package/build/src/constants.js.map +1 -0
- package/build/src/error-mapping.d.ts +31 -0
- package/build/src/error-mapping.js +72 -0
- package/build/src/error-mapping.js.map +1 -0
- package/build/src/errors.d.ts +8 -5
- package/build/src/errors.js +8 -10
- package/build/src/errors.js.map +1 -1
- package/build/src/index.d.ts +1 -0
- package/build/src/index.js +1 -0
- package/build/src/index.js.map +1 -1
- package/build/src/simplex-tree.d.ts +25 -3
- package/build/src/simplex.peggy +120 -3
- package/build/src/tools/index.d.ts +25 -6
- package/build/src/tools/index.js +91 -19
- package/build/src/tools/index.js.map +1 -1
- package/build/src/version.js +1 -1
- package/build/src/visitors.d.ts +15 -0
- package/build/src/visitors.js +330 -0
- package/build/src/visitors.js.map +1 -0
- package/package.json +10 -8
- package/parser/index.js +1161 -479
- package/parser/index.js.map +1 -1
- package/src/compiler.ts +332 -609
- package/src/constants.ts +30 -0
- package/src/error-mapping.ts +112 -0
- package/src/errors.ts +8 -12
- package/src/index.ts +1 -0
- package/src/simplex-tree.ts +30 -2
- package/src/simplex.peggy +120 -3
- package/src/tools/index.ts +117 -24
- package/src/visitors.ts +491 -0
- package/build/src/tools/cast.d.ts +0 -2
- package/build/src/tools/cast.js +0 -20
- package/build/src/tools/cast.js.map +0 -1
- package/build/src/tools/ensure.d.ts +0 -3
- package/build/src/tools/ensure.js +0 -30
- package/build/src/tools/ensure.js.map +0 -1
- package/build/src/tools/guards.d.ts +0 -2
- package/build/src/tools/guards.js +0 -20
- package/build/src/tools/guards.js.map +0 -1
- package/src/tools/cast.ts +0 -26
- package/src/tools/ensure.ts +0 -41
- package/src/tools/guards.ts +0 -29
package/src/visitors.ts
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { CompileError } from './errors.js'
|
|
2
|
+
import { TOPIC_TOKEN, GEN } from './constants.js'
|
|
3
|
+
import {
|
|
4
|
+
Expression,
|
|
5
|
+
ExpressionByType,
|
|
6
|
+
ExpressionStatement,
|
|
7
|
+
Location
|
|
8
|
+
} from './simplex-tree.js'
|
|
9
|
+
|
|
10
|
+
// --- Visitor Helpers ---
|
|
11
|
+
|
|
12
|
+
export interface SourceLocation {
|
|
13
|
+
len: number
|
|
14
|
+
location: Location
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface VisitResult {
|
|
18
|
+
code: string
|
|
19
|
+
offsets: SourceLocation[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TraverseContext {
|
|
23
|
+
expression: string
|
|
24
|
+
insidePipe: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Visit = (node: Expression) => VisitResult[]
|
|
28
|
+
|
|
29
|
+
const codePart = (
|
|
30
|
+
codePart: string,
|
|
31
|
+
ownerNode: { location: Location }
|
|
32
|
+
): VisitResult => ({
|
|
33
|
+
code: codePart,
|
|
34
|
+
offsets: [{ len: codePart.length, location: ownerNode.location }]
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const combineVisitResults = (parts: VisitResult[]) => {
|
|
38
|
+
return parts.reduce((res, it) => {
|
|
39
|
+
return {
|
|
40
|
+
code: res.code + it.code,
|
|
41
|
+
offsets: res.offsets.concat(it.offsets)
|
|
42
|
+
} as VisitResult
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Build a comma-separated list of VisitResult[] segments. */
|
|
47
|
+
const commaSeparated = (
|
|
48
|
+
items: VisitResult[][],
|
|
49
|
+
commaNode: { location: Location }
|
|
50
|
+
): VisitResult[] => {
|
|
51
|
+
if (items.length === 0) return []
|
|
52
|
+
return items.flatMap((item, i) =>
|
|
53
|
+
i < items.length - 1 ? [...item, codePart(',', commaNode)] : item
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Wrap visited args as `fnName(arg1, arg2, ...)`. */
|
|
58
|
+
const wrapCall = (
|
|
59
|
+
fnName: string,
|
|
60
|
+
node: { location: Location },
|
|
61
|
+
...args: VisitResult[][]
|
|
62
|
+
): VisitResult[] => [
|
|
63
|
+
codePart(`${fnName}(`, node),
|
|
64
|
+
...commaSeparated(args, node),
|
|
65
|
+
codePart(')', node)
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
/** Wrap visited args as `registry["op"](arg1, arg2, ...)`. */
|
|
69
|
+
const wrapOp = (
|
|
70
|
+
registry: string,
|
|
71
|
+
operator: string,
|
|
72
|
+
node: { location: Location },
|
|
73
|
+
...args: VisitResult[][]
|
|
74
|
+
): VisitResult[] => [
|
|
75
|
+
codePart(`${registry}["${operator}"](`, node),
|
|
76
|
+
...commaSeparated(args, node),
|
|
77
|
+
codePart(')', node)
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
/** Wrap visit results as a thunk: `()=>(...)`. */
|
|
81
|
+
const thunk = (
|
|
82
|
+
node: { location: Location },
|
|
83
|
+
parts: VisitResult[]
|
|
84
|
+
): VisitResult[] => [codePart('()=>(', node), ...parts, codePart(')', node)]
|
|
85
|
+
|
|
86
|
+
// --- AST Visitors ---
|
|
87
|
+
|
|
88
|
+
const visitors: {
|
|
89
|
+
[P in keyof ExpressionByType]: (
|
|
90
|
+
node: ExpressionByType[P],
|
|
91
|
+
visit: Visit,
|
|
92
|
+
context: TraverseContext
|
|
93
|
+
) => VisitResult[]
|
|
94
|
+
} = {
|
|
95
|
+
Literal: node => {
|
|
96
|
+
const parts: VisitResult[] = [codePart(JSON.stringify(node.value), node)]
|
|
97
|
+
|
|
98
|
+
return parts
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
Identifier: node => {
|
|
102
|
+
const parts: VisitResult[] = [
|
|
103
|
+
codePart(`${GEN.get}(${GEN.scope},${JSON.stringify(node.name)})`, node)
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
return parts
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
UnaryExpression: (node, visit) =>
|
|
110
|
+
wrapOp(GEN.uop, node.operator, node, visit(node.argument)),
|
|
111
|
+
|
|
112
|
+
BinaryExpression: (node, visit) =>
|
|
113
|
+
wrapOp(GEN.bop, node.operator, node, visit(node.left), visit(node.right)),
|
|
114
|
+
|
|
115
|
+
LogicalExpression: (node, visit) =>
|
|
116
|
+
wrapOp(
|
|
117
|
+
GEN.lop,
|
|
118
|
+
node.operator,
|
|
119
|
+
node,
|
|
120
|
+
thunk(node, visit(node.left)),
|
|
121
|
+
thunk(node, visit(node.right))
|
|
122
|
+
),
|
|
123
|
+
|
|
124
|
+
ConditionalExpression: (node, visit) => {
|
|
125
|
+
const parts: VisitResult[] = [
|
|
126
|
+
codePart(`(${GEN.bool}(`, node),
|
|
127
|
+
...visit(node.test),
|
|
128
|
+
codePart(')?', node),
|
|
129
|
+
...visit(node.consequent),
|
|
130
|
+
codePart(':', node),
|
|
131
|
+
...(node.alternate !== null
|
|
132
|
+
? visit(node.alternate)
|
|
133
|
+
: [codePart('undefined', node)]),
|
|
134
|
+
codePart(')', node)
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
return parts
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
ObjectExpression: (node, visit, context) => {
|
|
141
|
+
const items = node.properties.map(p => {
|
|
142
|
+
if (p.type === 'SpreadElement') {
|
|
143
|
+
return [
|
|
144
|
+
codePart(`...${GEN.ensObj}(`, p),
|
|
145
|
+
...visit(p.argument),
|
|
146
|
+
codePart(')', p)
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
let keyParts: VisitResult[]
|
|
150
|
+
if (p.computed) {
|
|
151
|
+
keyParts = [codePart('[', p), ...visit(p.key), codePart(']', p)]
|
|
152
|
+
} else if (p.key.type === 'Identifier') {
|
|
153
|
+
keyParts = [codePart(p.key.name, p)]
|
|
154
|
+
} else if (p.key.type === 'Literal') {
|
|
155
|
+
// JSON.stringify(Infinity) returns "null", producing wrong key
|
|
156
|
+
if (typeof p.key.value === 'number' && !Number.isFinite(p.key.value)) {
|
|
157
|
+
throw new CompileError(
|
|
158
|
+
`Invalid object key: ${p.key.value}`,
|
|
159
|
+
context.expression,
|
|
160
|
+
p.key.location
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
keyParts = [codePart(JSON.stringify(p.key.value), p)]
|
|
164
|
+
} else {
|
|
165
|
+
// Unreachable: grammar restricts keys to Identifier and Literal
|
|
166
|
+
throw new CompileError(
|
|
167
|
+
`Unsupported object key type: ${p.key.type}`,
|
|
168
|
+
context.expression,
|
|
169
|
+
p.key.location
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
return [...keyParts, codePart(':', node), ...visit(p.value)]
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
return [
|
|
176
|
+
codePart('{', node),
|
|
177
|
+
...commaSeparated(items, node),
|
|
178
|
+
codePart('}', node)
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
ArrayExpression: (node, visit) => {
|
|
183
|
+
const items = node.elements.map(el => {
|
|
184
|
+
if (el === null) return []
|
|
185
|
+
if (el.type === 'SpreadElement') {
|
|
186
|
+
return [
|
|
187
|
+
codePart(`...${GEN.ensArr}(`, el),
|
|
188
|
+
...visit(el.argument),
|
|
189
|
+
codePart(')', el)
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
return visit(el)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
return [
|
|
196
|
+
codePart('[', node),
|
|
197
|
+
...commaSeparated(items, node),
|
|
198
|
+
codePart(']', node)
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
NonNullAssertExpression: (node, visit) =>
|
|
203
|
+
wrapCall(GEN.nna, node, visit(node.expression)),
|
|
204
|
+
|
|
205
|
+
MemberExpression: (node, visit) => {
|
|
206
|
+
const { computed, object, property } = node
|
|
207
|
+
|
|
208
|
+
// TODO Pass computed to prop?
|
|
209
|
+
|
|
210
|
+
const propertyPart = computed
|
|
211
|
+
? visit(property)
|
|
212
|
+
: [codePart(JSON.stringify(property.name), property)]
|
|
213
|
+
|
|
214
|
+
const extension = !computed && node.extension === true
|
|
215
|
+
return wrapCall(GEN.prop, node, visit(object), propertyPart, [
|
|
216
|
+
codePart(String(extension), node)
|
|
217
|
+
])
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
CallExpression: (node, visit) => {
|
|
221
|
+
if (node.arguments.length > 0) {
|
|
222
|
+
const items = node.arguments.map((arg, index) =>
|
|
223
|
+
arg.type === 'CurryPlaceholder'
|
|
224
|
+
? [codePart(`a${index}`, arg)]
|
|
225
|
+
: visit(arg)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
const curriedArgs = node.arguments.flatMap((arg, index) =>
|
|
229
|
+
arg.type === 'CurryPlaceholder' ? [`a${index}`] : []
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
// call({{callee}},[{{arguments}}])
|
|
233
|
+
let parts: VisitResult[] = [
|
|
234
|
+
codePart(`${GEN.call}(`, node),
|
|
235
|
+
...visit(node.callee),
|
|
236
|
+
codePart(',[', node),
|
|
237
|
+
...commaSeparated(items, node),
|
|
238
|
+
codePart('])', node)
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
if (curriedArgs.length > 0) {
|
|
242
|
+
parts = [
|
|
243
|
+
codePart(`(${GEN.scope}=>(${curriedArgs.join()})=>`, node),
|
|
244
|
+
...parts,
|
|
245
|
+
codePart(`)(${GEN.scope})`, node)
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return parts
|
|
250
|
+
} else {
|
|
251
|
+
const parts: VisitResult[] = [
|
|
252
|
+
codePart(`${GEN.call}(`, node),
|
|
253
|
+
...visit(node.callee),
|
|
254
|
+
codePart(',null)', node)
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
return parts
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
NullishCoalescingExpression: (node, visit) => {
|
|
262
|
+
const parts: VisitResult[] = [
|
|
263
|
+
codePart('(', node),
|
|
264
|
+
...visit(node.left),
|
|
265
|
+
codePart('??', node),
|
|
266
|
+
...visit(node.right),
|
|
267
|
+
codePart(')', node)
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
return parts
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
PipeSequence: (node, visit, context) => {
|
|
274
|
+
const headCode = visit(node.head)
|
|
275
|
+
|
|
276
|
+
const prevInsidePipe = context.insidePipe
|
|
277
|
+
context.insidePipe = true
|
|
278
|
+
|
|
279
|
+
const items = node.tail.map(t => {
|
|
280
|
+
const opt = t.operator === '|?'
|
|
281
|
+
const fwd = t.operator === '|>'
|
|
282
|
+
return [
|
|
283
|
+
codePart(
|
|
284
|
+
`{opt:${opt},fwd:${fwd},next:(${GEN.scope}=>topic=>{${GEN.scope}=[["${TOPIC_TOKEN}"],[topic],${GEN.scope}];return `,
|
|
285
|
+
t.expression
|
|
286
|
+
),
|
|
287
|
+
...visit(t.expression),
|
|
288
|
+
codePart(`})(${GEN.scope})}`, t.expression)
|
|
289
|
+
]
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
context.insidePipe = prevInsidePipe
|
|
293
|
+
|
|
294
|
+
return [
|
|
295
|
+
codePart(`${GEN.pipe}(`, node),
|
|
296
|
+
...headCode,
|
|
297
|
+
codePart(',[', node),
|
|
298
|
+
...commaSeparated(items, node),
|
|
299
|
+
codePart('])', node)
|
|
300
|
+
]
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
TopicReference: (node, _visit, context) => {
|
|
304
|
+
if (!context.insidePipe) {
|
|
305
|
+
throw new CompileError(
|
|
306
|
+
`Topic reference "${TOPIC_TOKEN}" is unbound; it must be inside a pipe body`,
|
|
307
|
+
context.expression,
|
|
308
|
+
node.location
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
const parts: VisitResult[] = [
|
|
312
|
+
codePart(`${GEN.get}(${GEN.scope},"${TOPIC_TOKEN}")`, node)
|
|
313
|
+
]
|
|
314
|
+
return parts
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
LambdaExpression: (node, visit) => {
|
|
318
|
+
// Lambda with parameters
|
|
319
|
+
if (node.params.length > 0) {
|
|
320
|
+
const paramsNames = node.params.map(p => p.name)
|
|
321
|
+
|
|
322
|
+
const fnParams = Array.from(
|
|
323
|
+
{ length: paramsNames.length },
|
|
324
|
+
(_, index) => `p${index}`
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
const fnParamsList = fnParams.join()
|
|
328
|
+
const fnParamsNamesList = paramsNames.map(p => JSON.stringify(p)).join()
|
|
329
|
+
|
|
330
|
+
const parts: VisitResult[] = [
|
|
331
|
+
codePart(
|
|
332
|
+
`((${GEN.scope},params)=>function(${fnParamsList}){${GEN.scope}=[params,[${fnParamsList}],${GEN.scope}];return `,
|
|
333
|
+
node
|
|
334
|
+
),
|
|
335
|
+
...visit(node.expression),
|
|
336
|
+
codePart(`})(${GEN.scope},[${fnParamsNamesList}])`, node)
|
|
337
|
+
]
|
|
338
|
+
|
|
339
|
+
return parts
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Lambda without parameters
|
|
343
|
+
else {
|
|
344
|
+
// (() => {{code}})
|
|
345
|
+
const parts: VisitResult[] = [
|
|
346
|
+
codePart(`(()=>`, node),
|
|
347
|
+
...visit(node.expression),
|
|
348
|
+
codePart(`)`, node)
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
return parts
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
TemplateLiteral: (node, visit) => {
|
|
356
|
+
const { quasis, expressions, tag } = node
|
|
357
|
+
|
|
358
|
+
// --- Tagged template literal ---
|
|
359
|
+
if (tag !== null) {
|
|
360
|
+
const quasisItems = quasis.map(q => [codePart(JSON.stringify(q.value), q)])
|
|
361
|
+
const quasisArray: VisitResult[] = [
|
|
362
|
+
codePart('[', node),
|
|
363
|
+
...commaSeparated(quasisItems, node),
|
|
364
|
+
codePart(']', node)
|
|
365
|
+
]
|
|
366
|
+
const allArgs = [quasisArray, ...expressions.map(e => visit(e))]
|
|
367
|
+
return [
|
|
368
|
+
codePart(`${GEN.call}(`, node),
|
|
369
|
+
...visit(tag),
|
|
370
|
+
codePart(',[', node),
|
|
371
|
+
...commaSeparated(allArgs, node),
|
|
372
|
+
codePart('])', node)
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// No interpolations → emit as plain string literal
|
|
377
|
+
if (expressions.length === 0) {
|
|
378
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
379
|
+
return [codePart(JSON.stringify(quasis[0]!.value), node)]
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const parts: VisitResult[] = [codePart('(', node)]
|
|
383
|
+
|
|
384
|
+
for (let i = 0; i < quasis.length; i++) {
|
|
385
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
386
|
+
const quasi = quasis[i]!
|
|
387
|
+
const expr = expressions[i]
|
|
388
|
+
|
|
389
|
+
// Add "+" between parts (skip before the first part)
|
|
390
|
+
if (i > 0) {
|
|
391
|
+
parts.push(codePart('+', node))
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Emit the static string part
|
|
395
|
+
if (quasi.value !== '' || expr == null) {
|
|
396
|
+
parts.push(codePart(JSON.stringify(quasi.value), quasi))
|
|
397
|
+
if (expr != null) {
|
|
398
|
+
parts.push(codePart('+', node))
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Emit the interpolated expression wrapped in castToString
|
|
403
|
+
if (expr != null) {
|
|
404
|
+
parts.push(codePart(`${GEN.str}(`, node))
|
|
405
|
+
parts.push(...visit(expr))
|
|
406
|
+
parts.push(codePart(')', node))
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
parts.push(codePart(')', node))
|
|
411
|
+
return parts
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
LetExpression: (node, visit, context) => {
|
|
415
|
+
const declarationsNamesSet = new Set()
|
|
416
|
+
|
|
417
|
+
for (const d of node.declarations) {
|
|
418
|
+
if (declarationsNamesSet.has(d.id.name)) {
|
|
419
|
+
throw new CompileError(
|
|
420
|
+
`"${d.id.name}" name defined inside let expression was repeated`,
|
|
421
|
+
context.expression,
|
|
422
|
+
d.id.location
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
declarationsNamesSet.add(d.id.name)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// (scope=> {
|
|
429
|
+
// var _varNames = [];
|
|
430
|
+
// var _varValues = [];
|
|
431
|
+
// scope = [_varNames, _varValues, scope];
|
|
432
|
+
|
|
433
|
+
// // a = {{init}}
|
|
434
|
+
// _varNames.push("a");
|
|
435
|
+
// _varValues.push({{init}});
|
|
436
|
+
|
|
437
|
+
// // {{expression}}
|
|
438
|
+
// return {{expression}}
|
|
439
|
+
// })(scope)
|
|
440
|
+
|
|
441
|
+
const parts: VisitResult[] = [
|
|
442
|
+
codePart(
|
|
443
|
+
`(${GEN.scope}=>{var ${GEN._varNames}=[];var ${GEN._varValues}=[];${GEN.scope}=[${GEN._varNames},${GEN._varValues},${GEN.scope}];`,
|
|
444
|
+
node
|
|
445
|
+
),
|
|
446
|
+
...node.declarations.flatMap(d => [
|
|
447
|
+
codePart(`${GEN._varValues}.push(`, d),
|
|
448
|
+
...visit(d.init),
|
|
449
|
+
codePart(`);`, d),
|
|
450
|
+
codePart(`${GEN._varNames}.push(`, d),
|
|
451
|
+
codePart(JSON.stringify(d.id.name), d.id),
|
|
452
|
+
codePart(`);`, d)
|
|
453
|
+
]),
|
|
454
|
+
codePart(`return `, node),
|
|
455
|
+
...visit(node.expression),
|
|
456
|
+
codePart(`})(${GEN.scope})`, node)
|
|
457
|
+
]
|
|
458
|
+
|
|
459
|
+
return parts
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// --- Traverse ---
|
|
464
|
+
|
|
465
|
+
const visit = (
|
|
466
|
+
node: Expression,
|
|
467
|
+
_parentNode: Expression | null,
|
|
468
|
+
context: TraverseContext
|
|
469
|
+
): VisitResult[] => {
|
|
470
|
+
const nodeTypeVisitor = visitors[node.type]
|
|
471
|
+
|
|
472
|
+
if (nodeTypeVisitor === undefined) {
|
|
473
|
+
throw new Error(`No handler for node type - ${node.type}`)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const innerVisit: Visit = (childNode: Expression) => {
|
|
477
|
+
return visit(childNode, node, context)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// @ts-expect-error skip node is never
|
|
481
|
+
return nodeTypeVisitor(node, innerVisit, context)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Walk the AST and produce generated JS code with source location offsets. */
|
|
485
|
+
export function traverse(
|
|
486
|
+
tree: ExpressionStatement,
|
|
487
|
+
expression: string
|
|
488
|
+
): VisitResult {
|
|
489
|
+
const context: TraverseContext = { expression, insidePipe: false }
|
|
490
|
+
return combineVisitResults(visit(tree.expression, null, context))
|
|
491
|
+
}
|
package/build/src/tools/cast.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { unbox } from './index.js';
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
3
|
-
const toString = Object.prototype.toString;
|
|
4
|
-
export function castToBoolean(val) {
|
|
5
|
-
return Boolean(unbox(val));
|
|
6
|
-
}
|
|
7
|
-
export function castToString(val) {
|
|
8
|
-
val = unbox(val);
|
|
9
|
-
const type = typeof val;
|
|
10
|
-
if (type === 'string')
|
|
11
|
-
return val;
|
|
12
|
-
if (val == null ||
|
|
13
|
-
type === 'number' ||
|
|
14
|
-
type === 'boolean' ||
|
|
15
|
-
type === 'bigint') {
|
|
16
|
-
return String(val);
|
|
17
|
-
}
|
|
18
|
-
return toString.call(val);
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=cast.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cast.js","sourceRoot":"","sources":["../../../src/tools/cast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,6DAA6D;AAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAA;AAE1C,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IAEhB,MAAM,IAAI,GAAG,OAAO,GAAG,CAAA;IAEvB,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAa,CAAA;IAC3C,IACE,GAAG,IAAI,IAAI;QACX,IAAI,KAAK,QAAQ;QACjB,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,QAAQ,EACjB,CAAC;QACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAA;IACpB,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC3B,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { UnexpectedTypeError } from '../errors.js';
|
|
2
|
-
import { unbox } from './index.js';
|
|
3
|
-
export function ensureNumber(val) {
|
|
4
|
-
if (typeof val === 'number' && Number.isFinite(val)) {
|
|
5
|
-
return val;
|
|
6
|
-
}
|
|
7
|
-
if (typeof val === 'bigint') {
|
|
8
|
-
return val;
|
|
9
|
-
}
|
|
10
|
-
if (typeof val === 'object' && val instanceof Number) {
|
|
11
|
-
return ensureNumber(val.valueOf());
|
|
12
|
-
}
|
|
13
|
-
throw new UnexpectedTypeError(['number', 'bigint'], val);
|
|
14
|
-
}
|
|
15
|
-
export function ensureFunction(val) {
|
|
16
|
-
if (typeof val === 'function')
|
|
17
|
-
return val;
|
|
18
|
-
throw new UnexpectedTypeError(['function'], val);
|
|
19
|
-
}
|
|
20
|
-
export function ensureRelationalComparable(val) {
|
|
21
|
-
val = unbox(val);
|
|
22
|
-
const type = typeof val;
|
|
23
|
-
if ((type === 'number' && Number.isNaN(val) === false) ||
|
|
24
|
-
type === 'string' ||
|
|
25
|
-
type === 'bigint') {
|
|
26
|
-
return val;
|
|
27
|
-
}
|
|
28
|
-
throw new UnexpectedTypeError(['number', 'bigint', 'string'], val);
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=ensure.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ensure.js","sourceRoot":"","sources":["../../../src/tools/ensure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACpD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,YAAY,MAAM,EAAE,CAAC;QACrD,OAAO,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;IACpC,CAAC;IAED,MAAM,IAAI,mBAAmB,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAA;AAC1D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,OAAO,GAAG,KAAK,UAAU;QAAE,OAAO,GAAG,CAAA;IACzC,MAAM,IAAI,mBAAmB,CAAC,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,GAAY;IAEZ,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IAEhB,MAAM,IAAI,GAAG,OAAO,GAAG,CAAA;IAEvB,IACE,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;QAClD,IAAI,KAAK,QAAQ;QACjB,IAAI,KAAK,QAAQ,EACjB,CAAC;QACD,OAAO,GAA+B,CAAA;IACxC,CAAC;IAED,MAAM,IAAI,mBAAmB,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAA;AACpE,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { unbox } from './index.js';
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
3
|
-
var objToStrProto = Object.prototype.toString;
|
|
4
|
-
export function isObject(val) {
|
|
5
|
-
return objToStrProto.call(val) === '[object Object]';
|
|
6
|
-
}
|
|
7
|
-
export function isSimpleValue(val) {
|
|
8
|
-
val = unbox(val);
|
|
9
|
-
const type = typeof val;
|
|
10
|
-
if (type === 'string' ||
|
|
11
|
-
type === 'number' ||
|
|
12
|
-
type === 'boolean' ||
|
|
13
|
-
type === 'bigint') {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
if (val == null)
|
|
17
|
-
return true;
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=guards.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../src/tools/guards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,6DAA6D;AAC7D,IAAI,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAA;AAE7C,MAAM,UAAU,QAAQ,CAAC,GAAY;IACnC,OAAO,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,iBAAiB,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAY;IAEZ,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IAEhB,MAAM,IAAI,GAAG,OAAO,GAAG,CAAA;IAEvB,IACE,IAAI,KAAK,QAAQ;QACjB,IAAI,KAAK,QAAQ;QACjB,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,QAAQ,EACjB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IAE5B,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/src/tools/cast.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { unbox } from './index.js'
|
|
2
|
-
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
4
|
-
const toString = Object.prototype.toString
|
|
5
|
-
|
|
6
|
-
export function castToBoolean(val: unknown): boolean {
|
|
7
|
-
return Boolean(unbox(val))
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function castToString(val: unknown): string {
|
|
11
|
-
val = unbox(val)
|
|
12
|
-
|
|
13
|
-
const type = typeof val
|
|
14
|
-
|
|
15
|
-
if (type === 'string') return val as string
|
|
16
|
-
if (
|
|
17
|
-
val == null ||
|
|
18
|
-
type === 'number' ||
|
|
19
|
-
type === 'boolean' ||
|
|
20
|
-
type === 'bigint'
|
|
21
|
-
) {
|
|
22
|
-
return String(val)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return toString.call(val)
|
|
26
|
-
}
|
package/src/tools/ensure.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { UnexpectedTypeError } from '../errors.js'
|
|
2
|
-
import { unbox } from './index.js'
|
|
3
|
-
|
|
4
|
-
export function ensureNumber(val: unknown): number | bigint {
|
|
5
|
-
if (typeof val === 'number' && Number.isFinite(val)) {
|
|
6
|
-
return val
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (typeof val === 'bigint') {
|
|
10
|
-
return val
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (typeof val === 'object' && val instanceof Number) {
|
|
14
|
-
return ensureNumber(val.valueOf())
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
throw new UnexpectedTypeError(['number', 'bigint'], val)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function ensureFunction(val: unknown): Function {
|
|
21
|
-
if (typeof val === 'function') return val
|
|
22
|
-
throw new UnexpectedTypeError(['function'], val)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function ensureRelationalComparable(
|
|
26
|
-
val: unknown
|
|
27
|
-
): number | string | bigint {
|
|
28
|
-
val = unbox(val)
|
|
29
|
-
|
|
30
|
-
const type = typeof val
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
(type === 'number' && Number.isNaN(val) === false) ||
|
|
34
|
-
type === 'string' ||
|
|
35
|
-
type === 'bigint'
|
|
36
|
-
) {
|
|
37
|
-
return val as number | string | bigint
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
throw new UnexpectedTypeError(['number', 'bigint', 'string'], val)
|
|
41
|
-
}
|
package/src/tools/guards.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { unbox } from './index.js'
|
|
2
|
-
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
4
|
-
var objToStrProto = Object.prototype.toString
|
|
5
|
-
|
|
6
|
-
export function isObject(val: unknown): val is object {
|
|
7
|
-
return objToStrProto.call(val) === '[object Object]'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function isSimpleValue(
|
|
11
|
-
val: unknown
|
|
12
|
-
): val is number | string | boolean | bigint | null | undefined {
|
|
13
|
-
val = unbox(val)
|
|
14
|
-
|
|
15
|
-
const type = typeof val
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
type === 'string' ||
|
|
19
|
-
type === 'number' ||
|
|
20
|
-
type === 'boolean' ||
|
|
21
|
-
type === 'bigint'
|
|
22
|
-
) {
|
|
23
|
-
return true
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (val == null) return true
|
|
27
|
-
|
|
28
|
-
return false
|
|
29
|
-
}
|