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.
Files changed (58) hide show
  1. package/dist-cli/conjure-js.mjs +2328 -2089
  2. package/dist-vite-plugin/index.mjs +2327 -2088
  3. package/package.json +1 -1
  4. package/src/bin/version.ts +1 -1
  5. package/src/core/assertions.ts +10 -3
  6. package/src/core/bootstrap.ts +7 -23
  7. package/src/core/compiler/binding.ts +164 -0
  8. package/src/core/compiler/callable.ts +41 -0
  9. package/src/core/compiler/compile-env.ts +40 -0
  10. package/src/core/compiler/control-flow.ts +79 -0
  11. package/src/core/compiler/index.ts +121 -0
  12. package/src/core/env.ts +4 -4
  13. package/src/core/errors.ts +1 -0
  14. package/src/core/evaluator/apply.ts +7 -3
  15. package/src/core/evaluator/arity.ts +16 -6
  16. package/src/core/evaluator/async-evaluator.ts +68 -89
  17. package/src/core/evaluator/collections.ts +9 -4
  18. package/src/core/evaluator/destructure.ts +45 -55
  19. package/src/core/evaluator/dispatch.ts +21 -24
  20. package/src/core/evaluator/evaluate.ts +14 -2
  21. package/src/core/evaluator/expand.ts +5 -7
  22. package/src/core/evaluator/js-interop.ts +46 -33
  23. package/src/core/evaluator/quasiquote.ts +7 -11
  24. package/src/core/evaluator/recur-check.ts +1 -1
  25. package/src/core/evaluator/special-forms.ts +18 -38
  26. package/src/core/index.ts +1 -1
  27. package/src/core/keywords.ts +105 -0
  28. package/src/core/modules/core/index.ts +131 -0
  29. package/src/core/{stdlib → modules/core/stdlib}/arithmetic.ts +6 -6
  30. package/src/core/{stdlib → modules/core/stdlib}/async-fns.ts +6 -6
  31. package/src/core/{stdlib → modules/core/stdlib}/atoms.ts +7 -7
  32. package/src/core/{stdlib → modules/core/stdlib}/errors.ts +4 -4
  33. package/src/core/{stdlib → modules/core/stdlib}/hof.ts +6 -6
  34. package/src/core/{stdlib → modules/core/stdlib}/lazy.ts +4 -4
  35. package/src/core/{stdlib → modules/core/stdlib}/maps-sets.ts +6 -6
  36. package/src/core/{stdlib → modules/core/stdlib}/meta.ts +5 -5
  37. package/src/core/{stdlib → modules/core/stdlib}/predicates.ts +7 -7
  38. package/src/core/modules/core/stdlib/print.ts +108 -0
  39. package/src/core/{stdlib → modules/core/stdlib}/regex.ts +5 -5
  40. package/src/core/{stdlib → modules/core/stdlib}/seq.ts +7 -7
  41. package/src/core/{stdlib → modules/core/stdlib}/strings.ts +6 -6
  42. package/src/core/{stdlib → modules/core/stdlib}/transducers.ts +6 -6
  43. package/src/core/{stdlib → modules/core/stdlib}/utils.ts +10 -10
  44. package/src/core/{stdlib → modules/core/stdlib}/vars.ts +4 -4
  45. package/src/core/{stdlib → modules/core/stdlib}/vectors.ts +6 -6
  46. package/src/core/modules/js/index.ts +402 -0
  47. package/src/core/ns-forms.ts +25 -17
  48. package/src/core/positions.ts +22 -2
  49. package/src/core/printer.ts +162 -53
  50. package/src/core/reader.ts +25 -22
  51. package/src/core/registry.ts +10 -10
  52. package/src/core/runtime.ts +23 -23
  53. package/src/core/session.ts +17 -7
  54. package/src/core/tokenizer.ts +14 -4
  55. package/src/core/transformations.ts +48 -29
  56. package/src/core/types.ts +57 -81
  57. package/src/core/core-module.ts +0 -303
  58. package/src/core/stdlib/js-namespace.ts +0 -344
@@ -1,52 +1,66 @@
1
1
  import { EvaluationError } from './errors'
2
2
  import type { CljCons, CljLazySeq } from './types'
3
- import { valueKeywords, type CljMultiMethod, type CljValue, type EvaluationContext } from './types'
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.kind === 'lazy-seq') {
18
+ while (is.lazySeq(current)) {
12
19
  const lazy = current as CljLazySeq
13
- if (lazy.realized) { current = lazy.value!; continue }
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 { kind: 'nil', value: null }
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(value: CljValue, limit: number, depth: number): { items: string[]; truncated: boolean } {
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 (current.kind === 'nil') break
32
- if (current.kind === 'lazy-seq') {
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 (current.kind === 'cons') {
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 (current.kind === 'list') {
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 (current.kind === 'vector') {
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 { return _printCtx }
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?.kind === 'number' ? len.value : null,
97
- printLevel: level?.kind === 'number' ? level.value : null,
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
- value.kind === 'list' || value.kind === 'vector' ||
106
- value.kind === 'map' || value.kind === 'set' ||
107
- value.kind === 'cons' || value.kind === 'lazy-seq'
108
- ) return '#'
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 = printLength !== null ? value.value.slice(0, printLength) : value.value
152
- const suffix = printLength !== null && value.value.length > printLength ? ' ...' : ''
153
- return `(${items.map(v => printString(v, depth + 1)).join(' ')}${suffix})`
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 = printLength !== null ? value.value.slice(0, printLength) : value.value
158
- const suffix = printLength !== null && value.value.length > printLength ? ' ...' : ''
159
- return `[${items.map(v => printString(v, depth + 1)).join(' ')}${suffix}]`
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 = printLength !== null ? value.entries.slice(0, printLength) : value.entries
164
- const suffix = printLength !== null && value.entries.length > printLength ? ' ...' : ''
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, { kind: 'symbol' as const, name: '&' }, a.restParam]
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, { kind: 'symbol' as const, name: '&' }, a.restParam]
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 = printLength !== null ? value.values.slice(0, printLength) : value.values
203
- const suffix = printLength !== null && value.values.length > printLength ? ' ...' : ''
204
- return `#{${items.map(v => printString(v, depth + 1)).join(' ')}${suffix}}`
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) return `#<Delay @${printString(value.value!, depth + 1)}>`
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 = (raw as { constructor?: { name?: string } }).constructor?.name ?? 'Object'
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, try: 0, and: 0, or: 0, cond: 0, '->': 0, '->>': 0, 'some->': 0, 'some->>': 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, 'when-not': 1, 'when-let': 1, 'when-some': 1, 'when-first': 1,
263
- if: 1, 'if-not': 1, 'if-let': 1, 'if-some': 1, while: 1,
264
- let: 1, loop: 1, binding: 1, 'with-open': 1, 'with-local-vars': 1, locking: 1,
265
- fn: 1, 'fn*': 1,
266
- def: 1, defonce: 1, ns: 1,
267
- doseq: 1, dotimes: 1, for: 1,
268
- case: 1, 'cond->': 1, 'cond->>': 1,
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, 'defn-': 2, defmacro: 2, defmethod: 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
- 'let', 'loop', 'binding', 'with-open', 'for', 'doseq', 'dotimes',
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 = BINDING_FORMS.has(name) && i === 0 && arg.kind === valueKeywords.vector
403
+ const isPairVec =
404
+ BINDING_FORMS.has(name) && i === 0 && arg.kind === valueKeywords.vector
334
405
  const argStr = isPairVec
335
- ? ppVec((arg as Extract<CljValue, { kind: 'vector' }>).value, argCol, maxWidth, true)
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') ? lastLineLen(argStr) : argCol + argStr.length - 1
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.map(a => sp(bodyIndent) + pp(a, bodyIndent, maxWidth)).join('\n')
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
- '(' + headStr + ' ' + argStrs[0] + '\n' +
367
- argStrs.slice(1).map(s => sp(argIndent) + s).join('\n') + ')'
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 '(' + headStr + '\n' + argStrs.map(s => sp(argIndent) + s).join('\n') + ')'
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(items: CljValue[], col: number, maxWidth: number, pairMode: boolean): string {
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(entries: [CljValue, CljValue][], col: number, maxWidth: number): string {
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(sp(indent) + testStr + '\n' + sp(indent + 2) + pp(items[i + 1], indent + 2, maxWidth))
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')
@@ -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 { valueKeywords, tokenKeywords, type Token } from './types'
7
- import type { CljValue, TokenKinds } from './types'
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 = { kind: 'string', value: token.value }
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 = { kind: 'number', value: token.value }
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 = { kind: 'keyword', name: `:${fullNs}/${localName}` }
55
+ val = v.keyword(`:${fullNs}/${localName}`)
50
56
  } else {
51
- val = { kind: 'keyword', name: `:${ctx.namespace}/${rest}` }
57
+ val = v.keyword(`:${ctx.namespace}/${rest}`)
52
58
  }
53
59
  } else {
54
- val = { kind: 'keyword', name: kwName }
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 { kind: valueKeywords.list, value: [v.symbol('quote'), value] }
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 { kind: valueKeywords.list, value: [v.symbol('quasiquote'), value] }
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 { kind: valueKeywords.list, value: [v.symbol('unquote'), value] }
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 (metaForm.kind === 'keyword') {
140
+ if (is.keyword(metaForm)) {
135
141
  metaEntries = [[metaForm, v.boolean(true)]]
136
- } else if (metaForm.kind === 'map') {
142
+ } else if (is.map(metaForm)) {
137
143
  metaEntries = metaForm.entries
138
- } else if (metaForm.kind === 'symbol') {
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
- target.kind === 'symbol' ||
147
- target.kind === 'list' ||
148
- target.kind === 'vector' ||
149
- target.kind === 'map'
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 = { kind: 'list', value: bodyForms }
543
+ const bodyList: CljValue = v.list(bodyForms)
541
544
 
542
545
  const { maxIndex, hasRest } = collectAnonFnParams([bodyList])
543
546
 
@@ -1,4 +1,4 @@
1
- import { isKeyword, isSymbol, isVector } from './assertions'
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 (!isVector(spec)) {
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 || !isSymbol(elements[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) => isKeyword(el) && el.name === ':as-alias'
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 (!isKeyword(kw)) {
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 || !isSymbol(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 (!isKeyword(kw)) {
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 || !isSymbol(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 || !isVector(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 (!isSymbol(sym)) {
186
+ if (!is.symbol(sym)) {
187
187
  throw new EvaluationError(':refer vector must contain only symbols', {
188
188
  spec,
189
189
  sym,
@@ -1,30 +1,30 @@
1
- import { isKeyword, isString, isSymbol, isVector } from './assertions'
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 { internVar, makeEnv, makeNamespace } from './env'
8
- import { EvaluationError } from './errors'
9
- import { v } from './factories'
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 { tokenize } from './tokenizer'
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 type { NamespaceRegistry } from './registry'
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
- isVector(spec) &&
187
+ is.vector(spec) &&
188
188
  spec.value.length > 0 &&
189
- isString(spec.value[0])
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
- isVector(spec) &&
213
+ is.vector(spec) &&
214
214
  spec.value.length > 0 &&
215
- isString(spec.value[0])
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
- isKeyword(elements[i]) &&
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 || !isSymbol(aliasSym)) {
234
+ if (!aliasSym || !is.symbol(aliasSym)) {
235
235
  throw new EvaluationError(':as expects a symbol alias', {
236
236
  spec,
237
237
  })