conjure-js 0.0.13 → 0.0.14
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/dist-cli/conjure-js.mjs +2328 -2089
- package/dist-vite-plugin/index.mjs +2327 -2088
- package/package.json +1 -1
- package/src/bin/version.ts +1 -1
- package/src/core/assertions.ts +10 -3
- package/src/core/bootstrap.ts +7 -23
- package/src/core/compiler/binding.ts +164 -0
- package/src/core/compiler/callable.ts +41 -0
- package/src/core/compiler/compile-env.ts +40 -0
- package/src/core/compiler/control-flow.ts +79 -0
- package/src/core/compiler/index.ts +121 -0
- package/src/core/env.ts +4 -4
- package/src/core/errors.ts +1 -0
- package/src/core/evaluator/apply.ts +7 -3
- package/src/core/evaluator/arity.ts +16 -6
- package/src/core/evaluator/async-evaluator.ts +68 -89
- package/src/core/evaluator/collections.ts +9 -4
- package/src/core/evaluator/destructure.ts +45 -55
- package/src/core/evaluator/dispatch.ts +21 -24
- package/src/core/evaluator/evaluate.ts +14 -2
- package/src/core/evaluator/expand.ts +5 -7
- package/src/core/evaluator/js-interop.ts +46 -33
- package/src/core/evaluator/quasiquote.ts +7 -11
- package/src/core/evaluator/recur-check.ts +1 -1
- package/src/core/evaluator/special-forms.ts +18 -38
- package/src/core/index.ts +1 -1
- package/src/core/keywords.ts +105 -0
- package/src/core/modules/core/index.ts +131 -0
- package/src/core/{stdlib → modules/core/stdlib}/arithmetic.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/async-fns.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/atoms.ts +7 -7
- package/src/core/{stdlib → modules/core/stdlib}/errors.ts +4 -4
- package/src/core/{stdlib → modules/core/stdlib}/hof.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/lazy.ts +4 -4
- package/src/core/{stdlib → modules/core/stdlib}/maps-sets.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/meta.ts +5 -5
- package/src/core/{stdlib → modules/core/stdlib}/predicates.ts +7 -7
- package/src/core/modules/core/stdlib/print.ts +108 -0
- package/src/core/{stdlib → modules/core/stdlib}/regex.ts +5 -5
- package/src/core/{stdlib → modules/core/stdlib}/seq.ts +7 -7
- package/src/core/{stdlib → modules/core/stdlib}/strings.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/transducers.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/utils.ts +10 -10
- package/src/core/{stdlib → modules/core/stdlib}/vars.ts +4 -4
- package/src/core/{stdlib → modules/core/stdlib}/vectors.ts +6 -6
- package/src/core/modules/js/index.ts +402 -0
- package/src/core/ns-forms.ts +25 -17
- package/src/core/positions.ts +22 -2
- package/src/core/printer.ts +162 -53
- package/src/core/reader.ts +25 -22
- package/src/core/registry.ts +10 -10
- package/src/core/runtime.ts +23 -23
- package/src/core/session.ts +17 -7
- package/src/core/tokenizer.ts +14 -4
- package/src/core/transformations.ts +48 -29
- package/src/core/types.ts +57 -81
- package/src/core/core-module.ts +0 -303
- package/src/core/stdlib/js-namespace.ts +0 -344
package/src/core/printer.ts
CHANGED
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
import { EvaluationError } from './errors'
|
|
2
2
|
import type { CljCons, CljLazySeq } from './types'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type CljMultiMethod,
|
|
5
|
+
type CljValue,
|
|
6
|
+
type EvaluationContext,
|
|
7
|
+
} from './types'
|
|
4
8
|
import { derefValue } from './env'
|
|
9
|
+
import { specialFormKeywords, valueKeywords } from './keywords.ts'
|
|
10
|
+
import { is } from './assertions.ts'
|
|
11
|
+
import { v } from './factories.ts'
|
|
5
12
|
|
|
6
13
|
const LAZY_PRINT_CAP = 100
|
|
7
14
|
|
|
8
15
|
/** Realize a lazy-seq (local copy to avoid circular dep with transformations). */
|
|
9
16
|
function realizeLazy(ls: CljLazySeq): CljValue {
|
|
10
17
|
let current: CljValue = ls
|
|
11
|
-
while (current
|
|
18
|
+
while (is.lazySeq(current)) {
|
|
12
19
|
const lazy = current as CljLazySeq
|
|
13
|
-
if (lazy.realized) {
|
|
20
|
+
if (lazy.realized) {
|
|
21
|
+
current = lazy.value!
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
14
24
|
if (lazy.thunk) {
|
|
15
25
|
lazy.value = lazy.thunk()
|
|
16
26
|
lazy.thunk = null
|
|
17
27
|
lazy.realized = true
|
|
18
28
|
current = lazy.value!
|
|
19
29
|
} else {
|
|
20
|
-
return
|
|
30
|
+
return v.nil()
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
return current
|
|
24
34
|
}
|
|
25
35
|
|
|
26
36
|
/** Walk a lazy/cons chain collecting up to `limit` elements for printing. */
|
|
27
|
-
function collectSeqElements(
|
|
37
|
+
function collectSeqElements(
|
|
38
|
+
value: CljValue,
|
|
39
|
+
limit: number,
|
|
40
|
+
depth: number
|
|
41
|
+
): { items: string[]; truncated: boolean } {
|
|
28
42
|
const items: string[] = []
|
|
29
43
|
let current = value
|
|
30
44
|
while (items.length < limit) {
|
|
31
|
-
if (
|
|
32
|
-
if (current
|
|
45
|
+
if (is.nil(current)) break
|
|
46
|
+
if (is.lazySeq(current)) {
|
|
33
47
|
current = realizeLazy(current as CljLazySeq)
|
|
34
48
|
continue
|
|
35
49
|
}
|
|
36
|
-
if (
|
|
50
|
+
if (is.cons(current)) {
|
|
37
51
|
const c = current as CljCons
|
|
38
52
|
items.push(printString(c.head, depth + 1))
|
|
39
53
|
current = c.tail
|
|
40
54
|
continue
|
|
41
55
|
}
|
|
42
|
-
if (
|
|
56
|
+
if (is.list(current)) {
|
|
43
57
|
for (const v of current.value) {
|
|
44
58
|
if (items.length >= limit) break
|
|
45
59
|
items.push(printString(v, depth + 1))
|
|
46
60
|
}
|
|
47
61
|
break
|
|
48
62
|
}
|
|
49
|
-
if (
|
|
63
|
+
if (is.vector(current)) {
|
|
50
64
|
for (const v of current.value) {
|
|
51
65
|
if (items.length >= limit) break
|
|
52
66
|
items.push(printString(v, depth + 1))
|
|
@@ -69,7 +83,9 @@ export interface PrintContext {
|
|
|
69
83
|
|
|
70
84
|
let _printCtx: PrintContext = { printLength: null, printLevel: null }
|
|
71
85
|
|
|
72
|
-
export function getPrintContext(): PrintContext {
|
|
86
|
+
export function getPrintContext(): PrintContext {
|
|
87
|
+
return _printCtx
|
|
88
|
+
}
|
|
73
89
|
|
|
74
90
|
export function withPrintContext<T>(ctx: PrintContext, fn: () => T): T {
|
|
75
91
|
const prev = _printCtx
|
|
@@ -93,8 +109,8 @@ export function buildPrintContext(ctx: EvaluationContext): PrintContext {
|
|
|
93
109
|
const len = lenVar ? derefValue(lenVar) : undefined
|
|
94
110
|
const level = lvlVar ? derefValue(lvlVar) : undefined
|
|
95
111
|
return {
|
|
96
|
-
printLength: len
|
|
97
|
-
printLevel: level
|
|
112
|
+
printLength: len && is.number(len) ? len.value : null,
|
|
113
|
+
printLevel: level && is.number(level) ? level.value : null,
|
|
98
114
|
}
|
|
99
115
|
}
|
|
100
116
|
|
|
@@ -102,10 +118,14 @@ export function printString(value: CljValue, _depth = 0): string {
|
|
|
102
118
|
const { printLevel } = _printCtx
|
|
103
119
|
if (printLevel !== null && _depth >= printLevel) {
|
|
104
120
|
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
value
|
|
108
|
-
|
|
121
|
+
is.list(value) ||
|
|
122
|
+
is.vector(value) ||
|
|
123
|
+
is.map(value) ||
|
|
124
|
+
is.set(value) ||
|
|
125
|
+
is.cons(value) ||
|
|
126
|
+
is.lazySeq(value)
|
|
127
|
+
)
|
|
128
|
+
return '#'
|
|
109
129
|
}
|
|
110
130
|
return printStringImpl(value, _depth)
|
|
111
131
|
}
|
|
@@ -148,33 +168,41 @@ function printStringImpl(value: CljValue, depth: number): string {
|
|
|
148
168
|
return `${value.name}`
|
|
149
169
|
case valueKeywords.list: {
|
|
150
170
|
const { printLength } = _printCtx
|
|
151
|
-
const items =
|
|
152
|
-
|
|
153
|
-
|
|
171
|
+
const items =
|
|
172
|
+
printLength !== null ? value.value.slice(0, printLength) : value.value
|
|
173
|
+
const suffix =
|
|
174
|
+
printLength !== null && value.value.length > printLength ? ' ...' : ''
|
|
175
|
+
return `(${items.map((v) => printString(v, depth + 1)).join(' ')}${suffix})`
|
|
154
176
|
}
|
|
155
177
|
case valueKeywords.vector: {
|
|
156
178
|
const { printLength } = _printCtx
|
|
157
|
-
const items =
|
|
158
|
-
|
|
159
|
-
|
|
179
|
+
const items =
|
|
180
|
+
printLength !== null ? value.value.slice(0, printLength) : value.value
|
|
181
|
+
const suffix =
|
|
182
|
+
printLength !== null && value.value.length > printLength ? ' ...' : ''
|
|
183
|
+
return `[${items.map((v) => printString(v, depth + 1)).join(' ')}${suffix}]`
|
|
160
184
|
}
|
|
161
185
|
case valueKeywords.map: {
|
|
162
186
|
const { printLength } = _printCtx
|
|
163
|
-
const entries =
|
|
164
|
-
|
|
187
|
+
const entries =
|
|
188
|
+
printLength !== null
|
|
189
|
+
? value.entries.slice(0, printLength)
|
|
190
|
+
: value.entries
|
|
191
|
+
const suffix =
|
|
192
|
+
printLength !== null && value.entries.length > printLength ? ' ...' : ''
|
|
165
193
|
return `{${entries.map(([key, v]) => `${printString(key, depth + 1)} ${printString(v, depth + 1)}`).join(' ')}${suffix}}`
|
|
166
194
|
}
|
|
167
195
|
case valueKeywords.function: {
|
|
168
196
|
if (value.arities.length === 1) {
|
|
169
197
|
const a = value.arities[0]
|
|
170
198
|
const params = a.restParam
|
|
171
|
-
? [...a.params,
|
|
199
|
+
? [...a.params, v.symbol('&'), a.restParam]
|
|
172
200
|
: a.params
|
|
173
201
|
return `(fn [${params.map(printString).join(' ')}] ${a.body.map(printString).join(' ')})`
|
|
174
202
|
}
|
|
175
203
|
const clauses = value.arities.map((a) => {
|
|
176
204
|
const params = a.restParam
|
|
177
|
-
? [...a.params,
|
|
205
|
+
? [...a.params, v.symbol('&'), a.restParam]
|
|
178
206
|
: a.params
|
|
179
207
|
return `([${params.map(printString).join(' ')}] ${a.body.map(printString).join(' ')})`
|
|
180
208
|
})
|
|
@@ -199,12 +227,15 @@ function printStringImpl(value: CljValue, depth: number): string {
|
|
|
199
227
|
return `#'${value.ns}/${value.name}`
|
|
200
228
|
case valueKeywords.set: {
|
|
201
229
|
const { printLength } = _printCtx
|
|
202
|
-
const items =
|
|
203
|
-
|
|
204
|
-
|
|
230
|
+
const items =
|
|
231
|
+
printLength !== null ? value.values.slice(0, printLength) : value.values
|
|
232
|
+
const suffix =
|
|
233
|
+
printLength !== null && value.values.length > printLength ? ' ...' : ''
|
|
234
|
+
return `#{${items.map((v) => printString(v, depth + 1)).join(' ')}${suffix}}`
|
|
205
235
|
}
|
|
206
236
|
case valueKeywords.delay:
|
|
207
|
-
if (value.realized)
|
|
237
|
+
if (value.realized)
|
|
238
|
+
return `#<Delay @${printString(value.value!, depth + 1)}>`
|
|
208
239
|
return '#<Delay pending>'
|
|
209
240
|
case valueKeywords.lazySeq:
|
|
210
241
|
case valueKeywords.cons: {
|
|
@@ -236,7 +267,9 @@ function printStringImpl(value: CljValue, depth: number): string {
|
|
|
236
267
|
} else if (raw instanceof Promise) {
|
|
237
268
|
typeName = 'Promise'
|
|
238
269
|
} else {
|
|
239
|
-
typeName =
|
|
270
|
+
typeName =
|
|
271
|
+
(raw as { constructor?: { name?: string } }).constructor?.name ??
|
|
272
|
+
'Object'
|
|
240
273
|
}
|
|
241
274
|
return `#<js ${typeName}>`
|
|
242
275
|
}
|
|
@@ -257,22 +290,59 @@ export function joinLines(lines: string[]): string {
|
|
|
257
290
|
// Remaining args are indented 2 spaces from the opening paren.
|
|
258
291
|
const BODY_FORM_HEADER_COUNT: Record<string, number> = {
|
|
259
292
|
// 0-header: entire body is indented
|
|
260
|
-
do: 0,
|
|
293
|
+
do: 0,
|
|
294
|
+
try: 0,
|
|
295
|
+
and: 0,
|
|
296
|
+
or: 0,
|
|
297
|
+
cond: 0,
|
|
298
|
+
'->': 0,
|
|
299
|
+
'->>': 0,
|
|
300
|
+
'some->': 0,
|
|
301
|
+
'some->>': 0,
|
|
261
302
|
// 1-header: one leading arg kept on first line (condition / binding vec / value)
|
|
262
|
-
when: 1,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
303
|
+
when: 1,
|
|
304
|
+
'when-not': 1,
|
|
305
|
+
'when-let': 1,
|
|
306
|
+
'when-some': 1,
|
|
307
|
+
'when-first': 1,
|
|
308
|
+
if: 1,
|
|
309
|
+
'if-not': 1,
|
|
310
|
+
'if-let': 1,
|
|
311
|
+
'if-some': 1,
|
|
312
|
+
while: 1,
|
|
313
|
+
let: 1,
|
|
314
|
+
loop: 1,
|
|
315
|
+
binding: 1,
|
|
316
|
+
'with-open': 1,
|
|
317
|
+
'with-local-vars': 1,
|
|
318
|
+
locking: 1,
|
|
319
|
+
fn: 1,
|
|
320
|
+
'fn*': 1,
|
|
321
|
+
def: 1,
|
|
322
|
+
defonce: 1,
|
|
323
|
+
ns: 1,
|
|
324
|
+
doseq: 1,
|
|
325
|
+
dotimes: 1,
|
|
326
|
+
for: 1,
|
|
327
|
+
case: 1,
|
|
328
|
+
'cond->': 1,
|
|
329
|
+
'cond->>': 1,
|
|
269
330
|
// 2-header: name + params/dispatch on first line
|
|
270
|
-
defn: 2,
|
|
331
|
+
defn: 2,
|
|
332
|
+
'defn-': 2,
|
|
333
|
+
defmacro: 2,
|
|
334
|
+
defmethod: 2,
|
|
271
335
|
}
|
|
272
336
|
|
|
273
337
|
// Forms whose first header arg (binding vector) should be printed as pairs.
|
|
274
338
|
const BINDING_FORMS = new Set([
|
|
275
|
-
|
|
339
|
+
specialFormKeywords.let,
|
|
340
|
+
specialFormKeywords.loop,
|
|
341
|
+
specialFormKeywords.binding,
|
|
342
|
+
'with-open',
|
|
343
|
+
'for',
|
|
344
|
+
'doseq',
|
|
345
|
+
'dotimes',
|
|
276
346
|
])
|
|
277
347
|
|
|
278
348
|
// Forms whose body args are pairs (test expr, test expr, ...).
|
|
@@ -330,19 +400,29 @@ function ppList(items: CljValue[], col: number, maxWidth: number): string {
|
|
|
330
400
|
for (let i = 0; i < headerArgs.length; i++) {
|
|
331
401
|
const arg = headerArgs[i]
|
|
332
402
|
const argCol = curCol + 1
|
|
333
|
-
const isPairVec =
|
|
403
|
+
const isPairVec =
|
|
404
|
+
BINDING_FORMS.has(name) && i === 0 && arg.kind === valueKeywords.vector
|
|
334
405
|
const argStr = isPairVec
|
|
335
|
-
? ppVec(
|
|
406
|
+
? ppVec(
|
|
407
|
+
(arg as Extract<CljValue, { kind: 'vector' }>).value,
|
|
408
|
+
argCol,
|
|
409
|
+
maxWidth,
|
|
410
|
+
true
|
|
411
|
+
)
|
|
336
412
|
: pp(arg, argCol, maxWidth)
|
|
337
413
|
result += ' ' + argStr
|
|
338
|
-
curCol = argStr.includes('\n')
|
|
414
|
+
curCol = argStr.includes('\n')
|
|
415
|
+
? lastLineLen(argStr)
|
|
416
|
+
: argCol + argStr.length - 1
|
|
339
417
|
}
|
|
340
418
|
|
|
341
419
|
if (bodyArgs.length === 0) return result + ')'
|
|
342
420
|
|
|
343
421
|
const bodyStr = PAIR_BODY_FORMS.has(name)
|
|
344
422
|
? ppPairs(bodyArgs, bodyIndent, maxWidth)
|
|
345
|
-
: bodyArgs
|
|
423
|
+
: bodyArgs
|
|
424
|
+
.map((a) => sp(bodyIndent) + pp(a, bodyIndent, maxWidth))
|
|
425
|
+
.join('\n')
|
|
346
426
|
|
|
347
427
|
return result + '\n' + bodyStr + ')'
|
|
348
428
|
}
|
|
@@ -359,18 +439,37 @@ function ppList(items: CljValue[], col: number, maxWidth: number): string {
|
|
|
359
439
|
// For short head names: align subsequent args with the first arg.
|
|
360
440
|
// For long head names: fall back to 2-space indent for all args.
|
|
361
441
|
const argIndent = headStr.length <= 10 ? firstArgCol : col + 2
|
|
362
|
-
const argStrs = args.map(a => pp(a, argIndent, maxWidth))
|
|
442
|
+
const argStrs = args.map((a) => pp(a, argIndent, maxWidth))
|
|
363
443
|
|
|
364
444
|
if (argIndent === firstArgCol) {
|
|
365
445
|
return (
|
|
366
|
-
'(' +
|
|
367
|
-
|
|
446
|
+
'(' +
|
|
447
|
+
headStr +
|
|
448
|
+
' ' +
|
|
449
|
+
argStrs[0] +
|
|
450
|
+
'\n' +
|
|
451
|
+
argStrs
|
|
452
|
+
.slice(1)
|
|
453
|
+
.map((s) => sp(argIndent) + s)
|
|
454
|
+
.join('\n') +
|
|
455
|
+
')'
|
|
368
456
|
)
|
|
369
457
|
}
|
|
370
|
-
return
|
|
458
|
+
return (
|
|
459
|
+
'(' +
|
|
460
|
+
headStr +
|
|
461
|
+
'\n' +
|
|
462
|
+
argStrs.map((s) => sp(argIndent) + s).join('\n') +
|
|
463
|
+
')'
|
|
464
|
+
)
|
|
371
465
|
}
|
|
372
466
|
|
|
373
|
-
function ppVec(
|
|
467
|
+
function ppVec(
|
|
468
|
+
items: CljValue[],
|
|
469
|
+
col: number,
|
|
470
|
+
maxWidth: number,
|
|
471
|
+
pairMode: boolean
|
|
472
|
+
): string {
|
|
374
473
|
if (items.length === 0) return '[]'
|
|
375
474
|
|
|
376
475
|
const innerCol = col + 1
|
|
@@ -403,7 +502,11 @@ function ppVec(items: CljValue[], col: number, maxWidth: number, pairMode: boole
|
|
|
403
502
|
return '[' + strs.join('\n') + ']'
|
|
404
503
|
}
|
|
405
504
|
|
|
406
|
-
function ppMap(
|
|
505
|
+
function ppMap(
|
|
506
|
+
entries: [CljValue, CljValue][],
|
|
507
|
+
col: number,
|
|
508
|
+
maxWidth: number
|
|
509
|
+
): string {
|
|
407
510
|
if (entries.length === 0) return '{}'
|
|
408
511
|
const innerCol = col + 1
|
|
409
512
|
const pairs = entries.map(([k, v], i) => {
|
|
@@ -437,7 +540,13 @@ function ppPairs(items: CljValue[], indent: number, maxWidth: number): string {
|
|
|
437
540
|
if (indent + pairFlat.length <= maxWidth) {
|
|
438
541
|
lines.push(sp(indent) + pairFlat)
|
|
439
542
|
} else {
|
|
440
|
-
lines.push(
|
|
543
|
+
lines.push(
|
|
544
|
+
sp(indent) +
|
|
545
|
+
testStr +
|
|
546
|
+
'\n' +
|
|
547
|
+
sp(indent + 2) +
|
|
548
|
+
pp(items[i + 1], indent + 2, maxWidth)
|
|
549
|
+
)
|
|
441
550
|
}
|
|
442
551
|
}
|
|
443
552
|
return lines.join('\n')
|
package/src/core/reader.ts
CHANGED
|
@@ -3,9 +3,15 @@ import { v } from './factories'
|
|
|
3
3
|
import { is } from './assertions'
|
|
4
4
|
import { makeTokenScanner, type TokenScanner } from './scanners'
|
|
5
5
|
import { getTokenValue } from './tokenizer'
|
|
6
|
-
import {
|
|
7
|
-
import type { CljValue
|
|
6
|
+
import { type Token } from './types'
|
|
7
|
+
import type { CljValue } from './types'
|
|
8
8
|
import { getPos, setPos } from './positions'
|
|
9
|
+
import {
|
|
10
|
+
tokenKeywords,
|
|
11
|
+
type TokenKinds,
|
|
12
|
+
tokenSymbols,
|
|
13
|
+
valueKeywords,
|
|
14
|
+
} from './keywords.ts'
|
|
9
15
|
|
|
10
16
|
function readAtom(ctx: ReaderCtx): CljValue {
|
|
11
17
|
const scanner = ctx.scanner
|
|
@@ -18,13 +24,13 @@ function readAtom(ctx: ReaderCtx): CljValue {
|
|
|
18
24
|
return readSymbol(scanner)
|
|
19
25
|
case tokenKeywords.String: {
|
|
20
26
|
scanner.advance()
|
|
21
|
-
const val: CljValue =
|
|
27
|
+
const val: CljValue = v.string(token.value)
|
|
22
28
|
setPos(val, { start: token.start.offset, end: token.end.offset })
|
|
23
29
|
return val
|
|
24
30
|
}
|
|
25
31
|
case tokenKeywords.Number: {
|
|
26
32
|
scanner.advance()
|
|
27
|
-
const val: CljValue =
|
|
33
|
+
const val: CljValue = v.number(token.value)
|
|
28
34
|
setPos(val, { start: token.start.offset, end: token.end.offset })
|
|
29
35
|
return val
|
|
30
36
|
}
|
|
@@ -46,12 +52,12 @@ function readAtom(ctx: ReaderCtx): CljValue {
|
|
|
46
52
|
{ start: token.start.offset, end: token.end.offset }
|
|
47
53
|
)
|
|
48
54
|
}
|
|
49
|
-
val =
|
|
55
|
+
val = v.keyword(`:${fullNs}/${localName}`)
|
|
50
56
|
} else {
|
|
51
|
-
val =
|
|
57
|
+
val = v.keyword(`:${ctx.namespace}/${rest}`)
|
|
52
58
|
}
|
|
53
59
|
} else {
|
|
54
|
-
val =
|
|
60
|
+
val = v.keyword(kwName)
|
|
55
61
|
}
|
|
56
62
|
setPos(val, { start: token.start.offset, end: token.end.offset })
|
|
57
63
|
return val
|
|
@@ -78,7 +84,7 @@ const readQuote = (ctx: ReaderCtx) => {
|
|
|
78
84
|
if (!value) {
|
|
79
85
|
throw new ReaderError(`Unexpected token: ${getTokenValue(token)}`, token)
|
|
80
86
|
}
|
|
81
|
-
return
|
|
87
|
+
return v.list([v.symbol('quote'), value])
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
const readQuasiquote = (ctx: ReaderCtx) => {
|
|
@@ -95,7 +101,7 @@ const readQuasiquote = (ctx: ReaderCtx) => {
|
|
|
95
101
|
if (!value) {
|
|
96
102
|
throw new ReaderError(`Unexpected token: ${getTokenValue(token)}`, token)
|
|
97
103
|
}
|
|
98
|
-
return
|
|
104
|
+
return v.list([v.symbol('quasiquote'), value])
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
const readUnquote = (ctx: ReaderCtx) => {
|
|
@@ -112,7 +118,7 @@ const readUnquote = (ctx: ReaderCtx) => {
|
|
|
112
118
|
if (!value) {
|
|
113
119
|
throw new ReaderError(`Unexpected token: ${getTokenValue(token)}`, token)
|
|
114
120
|
}
|
|
115
|
-
return
|
|
121
|
+
return v.list([v.symbol('unquote'), value])
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
const readMeta = (ctx: ReaderCtx): CljValue => {
|
|
@@ -131,11 +137,11 @@ const readMeta = (ctx: ReaderCtx): CljValue => {
|
|
|
131
137
|
|
|
132
138
|
// Convert metaForm to a CljMap
|
|
133
139
|
let metaEntries: [CljValue, CljValue][]
|
|
134
|
-
if (
|
|
140
|
+
if (is.keyword(metaForm)) {
|
|
135
141
|
metaEntries = [[metaForm, v.boolean(true)]]
|
|
136
|
-
} else if (
|
|
142
|
+
} else if (is.map(metaForm)) {
|
|
137
143
|
metaEntries = metaForm.entries
|
|
138
|
-
} else if (
|
|
144
|
+
} else if (is.symbol(metaForm)) {
|
|
139
145
|
metaEntries = [[v.keyword(':tag'), metaForm]]
|
|
140
146
|
} else {
|
|
141
147
|
throw new ReaderError('Metadata must be a keyword, map, or symbol', token)
|
|
@@ -143,10 +149,10 @@ const readMeta = (ctx: ReaderCtx): CljValue => {
|
|
|
143
149
|
|
|
144
150
|
// Attach metadata to IMeta targets: symbols, lists, vectors, maps.
|
|
145
151
|
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
is.symbol(target) ||
|
|
153
|
+
is.list(target) ||
|
|
154
|
+
is.vector(target) ||
|
|
155
|
+
is.map(target)
|
|
150
156
|
) {
|
|
151
157
|
const existingEntries = target.meta ? target.meta.entries : []
|
|
152
158
|
const result = {
|
|
@@ -206,10 +212,7 @@ const readUnquoteSplicing = (ctx: ReaderCtx) => {
|
|
|
206
212
|
if (!value) {
|
|
207
213
|
throw new ReaderError(`Unexpected token: ${getTokenValue(token)}`, token)
|
|
208
214
|
}
|
|
209
|
-
return
|
|
210
|
-
kind: valueKeywords.list,
|
|
211
|
-
value: [v.symbol('unquote-splicing'), value],
|
|
212
|
-
}
|
|
215
|
+
return v.list([v.symbol(tokenSymbols.UnquoteSplicing), value])
|
|
213
216
|
}
|
|
214
217
|
|
|
215
218
|
const isClosingToken = (token: Token) => {
|
|
@@ -537,7 +540,7 @@ const readAnonFn = (ctx: ReaderCtx) => {
|
|
|
537
540
|
}
|
|
538
541
|
|
|
539
542
|
// The entire body content is a single implicit list — #(* 2 %) ≡ (fn [p1] (* 2 p1))
|
|
540
|
-
const bodyList: CljValue =
|
|
543
|
+
const bodyList: CljValue = v.list(bodyForms)
|
|
541
544
|
|
|
542
545
|
const { maxIndex, hasRest } = collectAnonFnParams([bodyList])
|
|
543
546
|
|
package/src/core/registry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { is } from './assertions'
|
|
2
2
|
import { makeEnv, makeNamespace } from './env'
|
|
3
3
|
import { EvaluationError } from './errors'
|
|
4
4
|
import type { CljValue, Env } from './types'
|
|
@@ -89,7 +89,7 @@ export function processRequireSpec(
|
|
|
89
89
|
registry: NamespaceRegistry,
|
|
90
90
|
resolveNs?: (nsName: string) => boolean
|
|
91
91
|
): void {
|
|
92
|
-
if (!
|
|
92
|
+
if (!is.vector(spec)) {
|
|
93
93
|
throw new EvaluationError(
|
|
94
94
|
'require spec must be a vector, e.g. [my.ns :as alias]',
|
|
95
95
|
{ spec }
|
|
@@ -97,7 +97,7 @@ export function processRequireSpec(
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const elements = spec.value
|
|
100
|
-
if (elements.length === 0 || !
|
|
100
|
+
if (elements.length === 0 || !is.symbol(elements[0])) {
|
|
101
101
|
throw new EvaluationError(
|
|
102
102
|
'First element of require spec must be a namespace symbol',
|
|
103
103
|
{ spec }
|
|
@@ -107,13 +107,13 @@ export function processRequireSpec(
|
|
|
107
107
|
const nsName = elements[0].name
|
|
108
108
|
|
|
109
109
|
const hasAsAlias = elements.some(
|
|
110
|
-
(el) =>
|
|
110
|
+
(el) => is.keyword(el) && el.name === ':as-alias'
|
|
111
111
|
)
|
|
112
112
|
if (hasAsAlias) {
|
|
113
113
|
let i = 1
|
|
114
114
|
while (i < elements.length) {
|
|
115
115
|
const kw = elements[i]
|
|
116
|
-
if (!
|
|
116
|
+
if (!is.keyword(kw)) {
|
|
117
117
|
throw new EvaluationError(
|
|
118
118
|
`Expected keyword in require spec, got ${kw.kind}`,
|
|
119
119
|
{ spec, position: i }
|
|
@@ -122,7 +122,7 @@ export function processRequireSpec(
|
|
|
122
122
|
if (kw.name === ':as-alias') {
|
|
123
123
|
i++
|
|
124
124
|
const alias = elements[i]
|
|
125
|
-
if (!alias || !
|
|
125
|
+
if (!alias || !is.symbol(alias)) {
|
|
126
126
|
throw new EvaluationError(':as-alias expects a symbol alias', {
|
|
127
127
|
spec,
|
|
128
128
|
position: i,
|
|
@@ -155,7 +155,7 @@ export function processRequireSpec(
|
|
|
155
155
|
let i = 1
|
|
156
156
|
while (i < elements.length) {
|
|
157
157
|
const kw = elements[i]
|
|
158
|
-
if (!
|
|
158
|
+
if (!is.keyword(kw)) {
|
|
159
159
|
throw new EvaluationError(
|
|
160
160
|
`Expected keyword in require spec, got ${kw.kind}`,
|
|
161
161
|
{ spec, position: i }
|
|
@@ -165,7 +165,7 @@ export function processRequireSpec(
|
|
|
165
165
|
if (kw.name === ':as') {
|
|
166
166
|
i++
|
|
167
167
|
const alias = elements[i]
|
|
168
|
-
if (!alias || !
|
|
168
|
+
if (!alias || !is.symbol(alias)) {
|
|
169
169
|
throw new EvaluationError(':as expects a symbol alias', {
|
|
170
170
|
spec,
|
|
171
171
|
position: i,
|
|
@@ -176,14 +176,14 @@ export function processRequireSpec(
|
|
|
176
176
|
} else if (kw.name === ':refer') {
|
|
177
177
|
i++
|
|
178
178
|
const symsVec = elements[i]
|
|
179
|
-
if (!symsVec || !
|
|
179
|
+
if (!symsVec || !is.vector(symsVec)) {
|
|
180
180
|
throw new EvaluationError(':refer expects a vector of symbols', {
|
|
181
181
|
spec,
|
|
182
182
|
position: i,
|
|
183
183
|
})
|
|
184
184
|
}
|
|
185
185
|
for (const sym of symsVec.value) {
|
|
186
|
-
if (!
|
|
186
|
+
if (!is.symbol(sym)) {
|
|
187
187
|
throw new EvaluationError(':refer vector must contain only symbols', {
|
|
188
188
|
spec,
|
|
189
189
|
sym,
|
package/src/core/runtime.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { builtInNamespaceSources } from '../clojure/generated/builtin-namespace-registry'
|
|
2
|
+
import { is } from './assertions'
|
|
3
|
+
import { wireIdeStubs, wireNsCore } from './bootstrap'
|
|
4
|
+
import { internVar, makeEnv, makeNamespace } from './env'
|
|
5
|
+
import { EvaluationError } from './errors'
|
|
6
|
+
import { v } from './factories'
|
|
2
7
|
import {
|
|
3
8
|
resolveModuleOrder,
|
|
4
|
-
type RuntimeModule,
|
|
5
9
|
type ModuleContext,
|
|
10
|
+
type RuntimeModule,
|
|
6
11
|
} from './module'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
12
|
+
import { makeCoreModule } from './modules/core'
|
|
13
|
+
import { makeJsModule } from './modules/js'
|
|
14
|
+
import {
|
|
15
|
+
extractAliasMapFromTokens,
|
|
16
|
+
extractNsNameFromTokens,
|
|
17
|
+
extractRequireClauses,
|
|
18
|
+
} from './ns-forms'
|
|
10
19
|
import { readForms } from './reader'
|
|
11
|
-
import {
|
|
12
|
-
import type { CljNamespace, CljValue, Env, EvaluationContext } from './types'
|
|
13
|
-
import { builtInNamespaceSources } from '../clojure/generated/builtin-namespace-registry'
|
|
14
|
-
import { makeCoreModule } from './core-module'
|
|
15
|
-
import { makeJsModule } from './stdlib/js-namespace'
|
|
20
|
+
import type { NamespaceRegistry } from './registry'
|
|
16
21
|
import {
|
|
17
22
|
cloneRegistry,
|
|
18
23
|
ensureNamespaceInRegistry,
|
|
19
24
|
processRequireSpec,
|
|
20
25
|
} from './registry'
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
extractAliasMapFromTokens,
|
|
24
|
-
extractNsNameFromTokens,
|
|
25
|
-
extractRequireClauses,
|
|
26
|
-
} from './ns-forms'
|
|
27
|
-
import { wireIdeStubs, wireNsCore } from './bootstrap'
|
|
26
|
+
import { tokenize } from './tokenizer'
|
|
27
|
+
import type { CljNamespace, CljValue, Env, EvaluationContext } from './types'
|
|
28
28
|
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// Core types
|
|
@@ -184,9 +184,9 @@ function buildRuntime(
|
|
|
184
184
|
for (const specs of requireClauses) {
|
|
185
185
|
for (const spec of specs) {
|
|
186
186
|
if (
|
|
187
|
-
|
|
187
|
+
is.vector(spec) &&
|
|
188
188
|
spec.value.length > 0 &&
|
|
189
|
-
|
|
189
|
+
is.string(spec.value[0])
|
|
190
190
|
) {
|
|
191
191
|
const specifier = spec.value[0].value
|
|
192
192
|
throw new EvaluationError(
|
|
@@ -210,9 +210,9 @@ function buildRuntime(
|
|
|
210
210
|
for (const specs of requireClauses) {
|
|
211
211
|
for (const spec of specs) {
|
|
212
212
|
if (
|
|
213
|
-
|
|
213
|
+
is.vector(spec) &&
|
|
214
214
|
spec.value.length > 0 &&
|
|
215
|
-
|
|
215
|
+
is.string(spec.value[0])
|
|
216
216
|
) {
|
|
217
217
|
// String module require — calls importModule and interns result as a var
|
|
218
218
|
const specifier = spec.value[0].value
|
|
@@ -226,12 +226,12 @@ function buildRuntime(
|
|
|
226
226
|
let aliasName: string | null = null
|
|
227
227
|
for (let i = 1; i < elements.length; i++) {
|
|
228
228
|
if (
|
|
229
|
-
|
|
229
|
+
is.keyword(elements[i]) &&
|
|
230
230
|
(elements[i] as { name: string }).name === ':as'
|
|
231
231
|
) {
|
|
232
232
|
i++
|
|
233
233
|
const aliasSym = elements[i]
|
|
234
|
-
if (!aliasSym || !
|
|
234
|
+
if (!aliasSym || !is.symbol(aliasSym)) {
|
|
235
235
|
throw new EvaluationError(':as expects a symbol alias', {
|
|
236
236
|
spec,
|
|
237
237
|
})
|