conjure-js 0.0.12 → 0.0.13

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 (77) hide show
  1. package/dist-cli/conjure-js.mjs +9360 -5298
  2. package/dist-vite-plugin/index.mjs +9463 -5185
  3. package/package.json +3 -1
  4. package/src/bin/cli.ts +2 -2
  5. package/src/bin/nrepl-symbol.ts +150 -0
  6. package/src/bin/nrepl.ts +289 -167
  7. package/src/bin/version.ts +1 -1
  8. package/src/clojure/core.clj +757 -29
  9. package/src/clojure/core.clj.d.ts +75 -131
  10. package/src/clojure/generated/builtin-namespace-registry.ts +4 -0
  11. package/src/clojure/generated/clojure-core-source.ts +758 -29
  12. package/src/clojure/generated/clojure-set-source.ts +136 -0
  13. package/src/clojure/generated/clojure-walk-source.ts +72 -0
  14. package/src/clojure/set.clj +132 -0
  15. package/src/clojure/set.clj.d.ts +20 -0
  16. package/src/clojure/string.clj.d.ts +14 -0
  17. package/src/clojure/walk.clj +68 -0
  18. package/src/clojure/walk.clj.d.ts +7 -0
  19. package/src/core/assertions.ts +114 -6
  20. package/src/core/bootstrap.ts +337 -0
  21. package/src/core/conversions.ts +48 -31
  22. package/src/core/core-module.ts +303 -0
  23. package/src/core/env.ts +20 -6
  24. package/src/core/evaluator/apply.ts +40 -25
  25. package/src/core/evaluator/arity.ts +8 -8
  26. package/src/core/evaluator/async-evaluator.ts +565 -0
  27. package/src/core/evaluator/collections.ts +28 -5
  28. package/src/core/evaluator/destructure.ts +180 -69
  29. package/src/core/evaluator/dispatch.ts +12 -14
  30. package/src/core/evaluator/evaluate.ts +22 -20
  31. package/src/core/evaluator/expand.ts +45 -15
  32. package/src/core/evaluator/form-parsers.ts +178 -0
  33. package/src/core/evaluator/index.ts +7 -9
  34. package/src/core/evaluator/js-interop.ts +189 -0
  35. package/src/core/evaluator/quasiquote.ts +14 -8
  36. package/src/core/evaluator/recur-check.ts +6 -6
  37. package/src/core/evaluator/special-forms.ts +234 -191
  38. package/src/core/factories.ts +182 -3
  39. package/src/core/index.ts +54 -4
  40. package/src/core/module.ts +136 -0
  41. package/src/core/ns-forms.ts +107 -0
  42. package/src/core/printer.ts +371 -11
  43. package/src/core/reader.ts +84 -33
  44. package/src/core/registry.ts +209 -0
  45. package/src/core/runtime.ts +376 -0
  46. package/src/core/session.ts +253 -487
  47. package/src/core/stdlib/arithmetic.ts +528 -194
  48. package/src/core/stdlib/async-fns.ts +132 -0
  49. package/src/core/stdlib/atoms.ts +291 -56
  50. package/src/core/stdlib/errors.ts +54 -50
  51. package/src/core/stdlib/hof.ts +82 -166
  52. package/src/core/stdlib/js-namespace.ts +344 -0
  53. package/src/core/stdlib/lazy.ts +34 -0
  54. package/src/core/stdlib/maps-sets.ts +322 -0
  55. package/src/core/stdlib/meta.ts +61 -30
  56. package/src/core/stdlib/predicates.ts +325 -187
  57. package/src/core/stdlib/regex.ts +126 -98
  58. package/src/core/stdlib/seq.ts +564 -0
  59. package/src/core/stdlib/strings.ts +164 -135
  60. package/src/core/stdlib/transducers.ts +95 -100
  61. package/src/core/stdlib/utils.ts +292 -130
  62. package/src/core/stdlib/vars.ts +27 -27
  63. package/src/core/stdlib/vectors.ts +122 -0
  64. package/src/core/tokenizer.ts +2 -2
  65. package/src/core/transformations.ts +117 -9
  66. package/src/core/types.ts +98 -2
  67. package/src/host/node-host-module.ts +74 -0
  68. package/src/{vite-plugin-clj/nrepl-relay.ts → nrepl/relay.ts} +72 -11
  69. package/src/vite-plugin-clj/codegen.ts +87 -95
  70. package/src/vite-plugin-clj/index.ts +178 -23
  71. package/src/vite-plugin-clj/namespace-utils.ts +39 -0
  72. package/src/vite-plugin-clj/static-analysis.ts +211 -0
  73. package/src/clojure/demo.clj +0 -72
  74. package/src/clojure/demo.clj.d.ts +0 -0
  75. package/src/core/core-env.ts +0 -61
  76. package/src/core/stdlib/collections.ts +0 -739
  77. package/src/host/node.ts +0 -55
@@ -1,28 +1,81 @@
1
- import { isEqual, isKeyword, isList, isMap, isSymbol, isVector } from '../assertions'
1
+ import { is } from '../assertions'
2
2
  import { EvaluationError } from '../errors'
3
- import { cljKeyword, cljList, cljNil, cljString, cljSymbol } from '../factories'
3
+ import {
4
+ cljKeyword,
5
+ cljList,
6
+ cljNil,
7
+ cljString,
8
+ cljSymbol,
9
+ v,
10
+ } from '../factories'
11
+ import { realizeLazySeq, consToArray } from '../transformations'
4
12
  import type { CljMap, CljValue, Env, EvaluationContext } from '../types'
5
13
 
6
14
  function toSeqSafe(value: CljValue): CljValue[] {
7
- if (value.kind === 'nil') return []
8
- if (isList(value)) return value.value
9
- if (isVector(value)) return value.value
15
+ if (is.nil(value)) return []
16
+ if (is.list(value)) return value.value
17
+ if (is.vector(value)) return value.value
18
+ if (is.lazySeq(value)) {
19
+ const realized = realizeLazySeq(value)
20
+ return toSeqSafe(realized)
21
+ }
22
+ if (is.cons(value)) return consToArray(value)
10
23
  throw new EvaluationError(
11
24
  `Cannot destructure ${value.kind} as a sequential collection`,
12
25
  { value }
13
26
  )
14
27
  }
15
28
 
16
- function findMapEntry(
17
- map: CljMap,
18
- key: CljValue
19
- ): CljValue | undefined {
20
- const entry = map.entries.find(([k]) => isEqual(k, key))
29
+ /** Return the first element of a seq-like value without full realization. */
30
+ function seqFirst(value: CljValue): CljValue {
31
+ if (is.nil(value)) return cljNil()
32
+ if (is.lazySeq(value)) {
33
+ const realized = realizeLazySeq(value)
34
+ return is.nil(realized) ? v.nil() : seqFirst(realized)
35
+ }
36
+ if (is.cons(value)) return value.head
37
+ if (is.list(value) || is.vector(value))
38
+ return value.value.length > 0 ? value.value[0] : v.nil()
39
+ return v.nil()
40
+ }
41
+
42
+ /** Return the tail of a seq-like value without full realization. */
43
+ function seqRest(value: CljValue): CljValue {
44
+ if (is.nil(value)) return v.list([])
45
+ if (is.lazySeq(value)) {
46
+ const realized = realizeLazySeq(value)
47
+ return is.nil(realized) ? v.list([]) : seqRest(realized)
48
+ }
49
+ if (is.cons(value)) return value.tail
50
+ if (is.list(value)) return v.list(value.value.slice(1))
51
+ if (is.vector(value)) return v.list(value.value.slice(1))
52
+ return v.list([])
53
+ }
54
+
55
+ /** Check if a seq-like value is empty without full realization. */
56
+ function seqIsEmpty(value: CljValue): boolean {
57
+ if (is.nil(value)) return true
58
+ if (is.lazySeq(value)) {
59
+ const realized = realizeLazySeq(value)
60
+ return seqIsEmpty(realized)
61
+ }
62
+ if (is.cons(value)) return false
63
+ if (is.list(value) || is.vector(value)) return value.value.length === 0
64
+ return true
65
+ }
66
+
67
+ /** Check if a value is lazy (lazy-seq or cons with lazy tail). */
68
+ function isLazy(value: CljValue): boolean {
69
+ return is.lazySeq(value) || is.cons(value)
70
+ }
71
+
72
+ function findMapEntry(map: CljMap, key: CljValue): CljValue | undefined {
73
+ const entry = map.entries.find(([k]) => is.equal(k, key))
21
74
  return entry ? entry[1] : undefined
22
75
  }
23
76
 
24
77
  function mapContainsKey(map: CljMap, key: CljValue): boolean {
25
- return map.entries.some(([k]) => isEqual(k, key))
78
+ return map.entries.some(([k]) => is.equal(k, key))
26
79
  }
27
80
 
28
81
  function destructureVector(
@@ -35,10 +88,12 @@ function destructureVector(
35
88
  const elems = [...pattern]
36
89
 
37
90
  // :as alias — must appear as second-to-last with a symbol after it
38
- const asIdx = elems.findIndex((e) => isKeyword(e) && e.kind === 'keyword' && e.name === ':as')
91
+ const asIdx = elems.findIndex(
92
+ (e) => is.keyword(e) && e.kind === 'keyword' && e.name === ':as'
93
+ )
39
94
  if (asIdx !== -1) {
40
95
  const asSym = elems[asIdx + 1]
41
- if (!asSym || !isSymbol(asSym)) {
96
+ if (!asSym || !is.symbol(asSym)) {
42
97
  throw new EvaluationError(':as must be followed by a symbol', { pattern })
43
98
  }
44
99
  pairs.push([asSym.name, value])
@@ -46,13 +101,15 @@ function destructureVector(
46
101
  }
47
102
 
48
103
  // & rest pattern
49
- const ampIdx = elems.findIndex((e) => isSymbol(e) && e.name === '&')
104
+ const ampIdx = elems.findIndex((e) => is.symbol(e) && e.name === '&')
50
105
  let restPattern: CljValue | null = null
51
106
  let positionalCount: number
52
107
  if (ampIdx !== -1) {
53
108
  restPattern = elems[ampIdx + 1]
54
109
  if (!restPattern) {
55
- throw new EvaluationError('& must be followed by a binding pattern', { pattern })
110
+ throw new EvaluationError('& must be followed by a binding pattern', {
111
+ pattern,
112
+ })
56
113
  }
57
114
  positionalCount = ampIdx
58
115
  elems.splice(ampIdx)
@@ -60,28 +117,60 @@ function destructureVector(
60
117
  positionalCount = elems.length
61
118
  }
62
119
 
63
- const seq = toSeqSafe(value)
120
+ // For lazy seqs, walk with first/rest to avoid full realization.
121
+ // For eager collections, use flat array for efficiency.
122
+ if (isLazy(value)) {
123
+ let current: CljValue = value
124
+ for (let i = 0; i < positionalCount; i++) {
125
+ pairs.push(...destructureBindings(elems[i], seqFirst(current), ctx, env))
126
+ current = seqRest(current)
127
+ }
128
+ if (restPattern !== null) {
129
+ // For kwargs-style map destructuring on rest, we must realize
130
+ if (is.map(restPattern) && !seqIsEmpty(current)) {
131
+ const restArgs = toSeqSafe(current)
132
+ const entries: [CljValue, CljValue][] = []
133
+ for (let i = 0; i < restArgs.length; i += 2) {
134
+ entries.push([restArgs[i], restArgs[i + 1] ?? cljNil()])
135
+ }
136
+ pairs.push(
137
+ ...destructureBindings(
138
+ restPattern,
139
+ { kind: 'map', entries },
140
+ ctx,
141
+ env
142
+ )
143
+ )
144
+ } else {
145
+ // Keep the rest as-is (still lazy) — wrap in list only if it's nil/empty
146
+ const restValue = seqIsEmpty(current) ? cljNil() : current
147
+ pairs.push(...destructureBindings(restPattern, restValue, ctx, env))
148
+ }
149
+ }
150
+ } else {
151
+ const seq = toSeqSafe(value)
64
152
 
65
- // positional bindings
66
- for (let i = 0; i < positionalCount; i++) {
67
- pairs.push(...destructureBindings(elems[i], seq[i] ?? cljNil(), ctx, env))
68
- }
153
+ // positional bindings
154
+ for (let i = 0; i < positionalCount; i++) {
155
+ pairs.push(...destructureBindings(elems[i], seq[i] ?? cljNil(), ctx, env))
156
+ }
69
157
 
70
- // rest binding
71
- if (restPattern !== null) {
72
- const restArgs = seq.slice(positionalCount)
73
- let restValue: CljValue
74
- if (isMap(restPattern) && restArgs.length > 0) {
75
- // kwargs-style: coerce flat key-value pairs into a map
76
- const entries: [CljValue, CljValue][] = []
77
- for (let i = 0; i < restArgs.length; i += 2) {
78
- entries.push([restArgs[i], restArgs[i + 1] ?? cljNil()])
158
+ // rest binding
159
+ if (restPattern !== null) {
160
+ const restArgs = seq.slice(positionalCount)
161
+ let restValue: CljValue
162
+ if (is.map(restPattern) && restArgs.length > 0) {
163
+ // kwargs-style: coerce flat key-value pairs into a map
164
+ const entries: [CljValue, CljValue][] = []
165
+ for (let i = 0; i < restArgs.length; i += 2) {
166
+ entries.push([restArgs[i], restArgs[i + 1] ?? cljNil()])
167
+ }
168
+ restValue = { kind: 'map', entries }
169
+ } else {
170
+ restValue = restArgs.length > 0 ? cljList(restArgs) : cljNil()
79
171
  }
80
- restValue = { kind: 'map', entries }
81
- } else {
82
- restValue = restArgs.length > 0 ? cljList(restArgs) : cljNil()
172
+ pairs.push(...destructureBindings(restPattern, restValue, ctx, env))
83
173
  }
84
- pairs.push(...destructureBindings(restPattern, restValue, ctx, env))
85
174
  }
86
175
 
87
176
  return pairs
@@ -96,35 +185,41 @@ function destructureMap(
96
185
  const pairs: [string, CljValue][] = []
97
186
 
98
187
  const orMapVal = findMapEntry(pattern, cljKeyword(':or'))
99
- const orMap = orMapVal && isMap(orMapVal) ? orMapVal : null
188
+ const orMap = orMapVal && is.map(orMapVal) ? orMapVal : null
100
189
  const asVal = findMapEntry(pattern, cljKeyword(':as'))
101
190
 
102
- if (!isMap(value) && value.kind !== 'nil') {
103
- throw new EvaluationError(
104
- `Cannot destructure ${value.kind} as a map`,
105
- { value, pattern }
106
- )
191
+ if (!is.map(value) && value.kind !== 'nil') {
192
+ throw new EvaluationError(`Cannot destructure ${value.kind} as a map`, {
193
+ value,
194
+ pattern,
195
+ })
107
196
  }
108
197
 
109
- const targetMap: CljMap = value.kind === 'nil'
110
- ? { kind: 'map', entries: [] }
111
- : value as CljMap
198
+ const targetMap: CljMap =
199
+ value.kind === 'nil' ? { kind: 'map', entries: [] } : (value as CljMap)
112
200
 
113
201
  for (const [k, v] of pattern.entries) {
114
- if (isKeyword(k) && k.name === ':or') continue
115
- if (isKeyword(k) && k.name === ':as') continue
202
+ if (is.keyword(k) && k.name === ':or') continue
203
+ if (is.keyword(k) && k.name === ':as') continue
116
204
 
117
205
  // :keys shorthand — lookup by keyword (supports qualified: ns/foo → :ns/foo, binds to foo)
118
- if (isKeyword(k) && k.name === ':keys') {
119
- if (!isVector(v)) {
120
- throw new EvaluationError(':keys must be followed by a vector of symbols', { pattern })
206
+ if (is.keyword(k) && k.name === ':keys') {
207
+ if (!is.vector(v)) {
208
+ throw new EvaluationError(
209
+ ':keys must be followed by a vector of symbols',
210
+ { pattern }
211
+ )
121
212
  }
122
213
  for (const sym of v.value) {
123
- if (!isSymbol(sym)) {
124
- throw new EvaluationError(':keys vector must contain symbols', { pattern, sym })
214
+ if (!is.symbol(sym)) {
215
+ throw new EvaluationError(':keys vector must contain symbols', {
216
+ pattern,
217
+ sym,
218
+ })
125
219
  }
126
220
  const slashIdx = sym.name.indexOf('/')
127
- const localName = slashIdx !== -1 ? sym.name.slice(slashIdx + 1) : sym.name
221
+ const localName =
222
+ slashIdx !== -1 ? sym.name.slice(slashIdx + 1) : sym.name
128
223
  const lookupKey = cljKeyword(':' + sym.name)
129
224
  const present = mapContainsKey(targetMap, lookupKey)
130
225
  const entry = present ? findMapEntry(targetMap, lookupKey)! : undefined
@@ -134,7 +229,8 @@ function destructureMap(
134
229
  result = entry!
135
230
  } else if (orMap) {
136
231
  const orDefault = findMapEntry(orMap, cljSymbol(localName))
137
- result = orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
232
+ result =
233
+ orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
138
234
  } else {
139
235
  result = cljNil()
140
236
  }
@@ -144,13 +240,19 @@ function destructureMap(
144
240
  }
145
241
 
146
242
  // :strs shorthand — lookup by string
147
- if (isKeyword(k) && k.name === ':strs') {
148
- if (!isVector(v)) {
149
- throw new EvaluationError(':strs must be followed by a vector of symbols', { pattern })
243
+ if (is.keyword(k) && k.name === ':strs') {
244
+ if (!is.vector(v)) {
245
+ throw new EvaluationError(
246
+ ':strs must be followed by a vector of symbols',
247
+ { pattern }
248
+ )
150
249
  }
151
250
  for (const sym of v.value) {
152
- if (!isSymbol(sym)) {
153
- throw new EvaluationError(':strs vector must contain symbols', { pattern, sym })
251
+ if (!is.symbol(sym)) {
252
+ throw new EvaluationError(':strs vector must contain symbols', {
253
+ pattern,
254
+ sym,
255
+ })
154
256
  }
155
257
  const lookupKey = cljString(sym.name)
156
258
  const present = mapContainsKey(targetMap, lookupKey)
@@ -161,7 +263,8 @@ function destructureMap(
161
263
  result = entry!
162
264
  } else if (orMap) {
163
265
  const orDefault = findMapEntry(orMap, cljSymbol(sym.name))
164
- result = orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
266
+ result =
267
+ orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
165
268
  } else {
166
269
  result = cljNil()
167
270
  }
@@ -171,13 +274,19 @@ function destructureMap(
171
274
  }
172
275
 
173
276
  // :syms shorthand — lookup by symbol
174
- if (isKeyword(k) && k.name === ':syms') {
175
- if (!isVector(v)) {
176
- throw new EvaluationError(':syms must be followed by a vector of symbols', { pattern })
277
+ if (is.keyword(k) && k.name === ':syms') {
278
+ if (!is.vector(v)) {
279
+ throw new EvaluationError(
280
+ ':syms must be followed by a vector of symbols',
281
+ { pattern }
282
+ )
177
283
  }
178
284
  for (const sym of v.value) {
179
- if (!isSymbol(sym)) {
180
- throw new EvaluationError(':syms vector must contain symbols', { pattern, sym })
285
+ if (!is.symbol(sym)) {
286
+ throw new EvaluationError(':syms vector must contain symbols', {
287
+ pattern,
288
+ sym,
289
+ })
181
290
  }
182
291
  const lookupKey = cljSymbol(sym.name)
183
292
  const present = mapContainsKey(targetMap, lookupKey)
@@ -188,7 +297,8 @@ function destructureMap(
188
297
  result = entry!
189
298
  } else if (orMap) {
190
299
  const orDefault = findMapEntry(orMap, cljSymbol(sym.name))
191
- result = orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
300
+ result =
301
+ orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
192
302
  } else {
193
303
  result = cljNil()
194
304
  }
@@ -205,9 +315,10 @@ function destructureMap(
205
315
  let boundVal: CljValue
206
316
  if (present) {
207
317
  boundVal = entry!
208
- } else if (orMap && isSymbol(k)) {
318
+ } else if (orMap && is.symbol(k)) {
209
319
  const orDefault = findMapEntry(orMap, cljSymbol(k.name))
210
- boundVal = orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
320
+ boundVal =
321
+ orDefault !== undefined ? ctx.evaluate(orDefault, env) : cljNil()
211
322
  } else {
212
323
  boundVal = cljNil()
213
324
  }
@@ -215,7 +326,7 @@ function destructureMap(
215
326
  }
216
327
 
217
328
  // :as alias
218
- if (asVal && isSymbol(asVal)) {
329
+ if (asVal && is.symbol(asVal)) {
219
330
  pairs.push([asVal.name, value])
220
331
  }
221
332
 
@@ -228,15 +339,15 @@ export function destructureBindings(
228
339
  ctx: EvaluationContext,
229
340
  env: Env
230
341
  ): [string, CljValue][] {
231
- if (isSymbol(pattern)) {
342
+ if (is.symbol(pattern)) {
232
343
  return [[pattern.name, value]]
233
344
  }
234
345
 
235
- if (isVector(pattern)) {
346
+ if (is.vector(pattern)) {
236
347
  return destructureVector(pattern.value, value, ctx, env)
237
348
  }
238
349
 
239
- if (isMap(pattern)) {
350
+ if (is.map(pattern)) {
240
351
  return destructureMap(pattern, value, ctx, env)
241
352
  }
242
353
 
@@ -1,10 +1,4 @@
1
- import {
2
- isCallable,
3
- isEqual,
4
- isMultiMethod,
5
- isSpecialForm,
6
- isSymbol,
7
- } from '../assertions'
1
+ import { is } from '../assertions'
8
2
  import { EvaluationError } from '../errors'
9
3
  import { printString } from '../printer'
10
4
  import { getPos } from '../positions'
@@ -26,7 +20,7 @@ function dispatchMultiMethod(
26
20
  ): CljValue {
27
21
  const dispatchVal = ctx.applyFunction(mm.dispatchFn, args, env)
28
22
  const method = mm.methods.find(({ dispatchVal: dv }) =>
29
- isEqual(dv, dispatchVal)
23
+ is.equal(dv, dispatchVal)
30
24
  )
31
25
  if (method) return ctx.applyFunction(method.fn, args, env)
32
26
  if (mm.defaultMethod) return ctx.applyFunction(mm.defaultMethod, args, env)
@@ -49,23 +43,23 @@ export function evaluateList(
49
43
  ctx: EvaluationContext
50
44
  ): CljValue {
51
45
  if (list.value.length === 0) {
52
- throw new EvaluationError('Unexpected empty list', { list, env })
46
+ return list
53
47
  }
54
48
  const first = list.value[0]
55
49
 
56
- if (isSpecialForm(first)) {
50
+ if (is.specialForm(first)) {
57
51
  return evaluateSpecialForm(first.name, list, env, ctx)
58
52
  }
59
53
 
60
54
  const evaledFirst = ctx.evaluate(first, env)
61
55
 
62
- if (isMultiMethod(evaledFirst)) {
56
+ if (is.multiMethod(evaledFirst)) {
63
57
  const args = list.value.slice(1).map((v) => ctx.evaluate(v, env))
64
58
  return dispatchMultiMethod(evaledFirst, args, ctx, env)
65
59
  }
66
60
 
67
- if (!isCallable(evaledFirst)) {
68
- const name = isSymbol(first) ? first.name : printString(first)
61
+ if (!is.callable(evaledFirst)) {
62
+ const name = is.symbol(first) ? first.name : printString(first)
69
63
  throw new EvaluationError(`${name} is not callable`, { list, env })
70
64
  }
71
65
 
@@ -73,7 +67,11 @@ export function evaluateList(
73
67
  try {
74
68
  return ctx.applyCallable(evaledFirst, args, env)
75
69
  } catch (e) {
76
- if (e instanceof EvaluationError && e.data?.argIndex !== undefined && !e.pos) {
70
+ if (
71
+ e instanceof EvaluationError &&
72
+ e.data?.argIndex !== undefined &&
73
+ !e.pos
74
+ ) {
77
75
  const argForm = list.value[(e.data.argIndex as number) + 1]
78
76
  if (argForm) {
79
77
  const pos = getPos(argForm)
@@ -1,11 +1,11 @@
1
- import { getNamespaceEnv, getRootEnv, lookup } from '../env'
1
+ import { derefValue, getNamespaceEnv, lookup } from '../env'
2
2
  import { EvaluationError } from '../errors'
3
- import { cljNil } from '../factories'
3
+ import { v } from '../factories'
4
4
  import { getPos } from '../positions'
5
5
  import { valueKeywords } from '../types'
6
6
 
7
7
  import type { CljValue, Env, EvaluationContext } from '../types'
8
- import { evaluateMap, evaluateVector } from './collections'
8
+ import { evaluateMap, evaluateSet, evaluateVector } from './collections'
9
9
  import { evaluateList } from './dispatch'
10
10
 
11
11
  export type EvaluationMeasurement = {
@@ -34,6 +34,10 @@ export function evaluateWithContext(
34
34
  case valueKeywords.multiMethod:
35
35
  case valueKeywords.boolean:
36
36
  case valueKeywords.regex:
37
+ case valueKeywords.delay:
38
+ case valueKeywords.lazySeq:
39
+ case valueKeywords.cons:
40
+ case valueKeywords.namespace:
37
41
  return expr
38
42
  case valueKeywords.symbol: {
39
43
  const slashIdx = expr.name.indexOf('/')
@@ -41,27 +45,23 @@ export function evaluateWithContext(
41
45
  const alias = expr.name.slice(0, slashIdx)
42
46
  const sym = expr.name.slice(slashIdx + 1)
43
47
  const nsEnv = getNamespaceEnv(env)
44
- // Try alias lookup (CljNamespace) first
45
- const aliasCljNs = nsEnv.ns?.aliases.get(alias)
46
- if (aliasCljNs) {
47
- const v = aliasCljNs.vars.get(sym)
48
- if (v === undefined) {
49
- throw new EvaluationError(`Symbol ${expr.name} not found`, {
50
- symbol: expr.name,
51
- env,
52
- })
53
- }
54
- return v.value
55
- }
56
- // Fall back to full namespace Env chain (handles clojure.core/sym etc.)
57
- const targetEnv = getRootEnv(env).resolveNs?.(alias) ?? null
58
- if (!targetEnv) {
48
+ // Resolve alias: local :as alias first, then full namespace name
49
+ const targetNs =
50
+ nsEnv.ns?.aliases.get(alias) ?? ctx.resolveNs(alias) ?? null
51
+ if (!targetNs) {
59
52
  throw new EvaluationError(`No such namespace or alias: ${alias}`, {
60
53
  symbol: expr.name,
61
54
  env,
62
55
  })
63
56
  }
64
- return lookup(sym, targetEnv)
57
+ const v = targetNs.vars.get(sym)
58
+ if (v === undefined) {
59
+ throw new EvaluationError(`Symbol ${expr.name} not found`, {
60
+ symbol: expr.name,
61
+ env,
62
+ })
63
+ }
64
+ return derefValue(v)
65
65
  }
66
66
  return lookup(expr.name, env)
67
67
  }
@@ -69,6 +69,8 @@ export function evaluateWithContext(
69
69
  return evaluateVector(expr, env, ctx)
70
70
  case valueKeywords.map:
71
71
  return evaluateMap(expr, env, ctx)
72
+ case valueKeywords.set:
73
+ return evaluateSet(expr, env, ctx)
72
74
  case valueKeywords.list:
73
75
  return evaluateList(expr, env, ctx)
74
76
  default:
@@ -88,7 +90,7 @@ export function evaluateFormsWithContext(
88
90
  env: Env,
89
91
  ctx: EvaluationContext
90
92
  ): CljValue {
91
- let result: CljValue = cljNil()
93
+ let result: CljValue = v.nil()
92
94
  for (const form of forms) {
93
95
  result = ctx.evaluate(form, env)
94
96
  }
@@ -1,5 +1,5 @@
1
- import { isList, isMap, isMacro, isSymbol, isVector } from '../assertions'
2
- import { tryLookup } from '../env'
1
+ import { is } from '../assertions'
2
+ import { derefValue, getNamespaceEnv, tryLookup } from '../env'
3
3
  import { cljList, cljMap, cljVector } from '../factories'
4
4
  import type { CljValue, Env, EvaluationContext } from '../types'
5
5
 
@@ -25,13 +25,17 @@ export function macroExpandAllWithContext(
25
25
  ctx: EvaluationContext
26
26
  ): CljValue {
27
27
  // Vectors: expand each element (covers [x (some-macro ...)] in let bindings etc.)
28
- if (isVector(form)) {
29
- const expanded = form.value.map((sub) => macroExpandAllWithContext(sub, env, ctx))
30
- return expanded.every((e, i) => e === form.value[i]) ? form : cljVector(expanded)
28
+ if (is.vector(form)) {
29
+ const expanded = form.value.map((sub) =>
30
+ macroExpandAllWithContext(sub, env, ctx)
31
+ )
32
+ return expanded.every((e, i) => e === form.value[i])
33
+ ? form
34
+ : cljVector(expanded)
31
35
  }
32
36
 
33
37
  // Maps: expand each key and value
34
- if (isMap(form)) {
38
+ if (is.map(form)) {
35
39
  const expanded = form.entries.map(
36
40
  ([k, v]) =>
37
41
  [
@@ -39,13 +43,15 @@ export function macroExpandAllWithContext(
39
43
  macroExpandAllWithContext(v, env, ctx),
40
44
  ] as [CljValue, CljValue]
41
45
  )
42
- return expanded.every(([k, v], i) => k === form.entries[i][0] && v === form.entries[i][1])
46
+ return expanded.every(
47
+ ([k, v], i) => k === form.entries[i][0] && v === form.entries[i][1]
48
+ )
43
49
  ? form
44
50
  : cljMap(expanded)
45
51
  }
46
52
 
47
53
  // Atoms (number, string, boolean, keyword, nil, symbol, regex, functions, etc.)
48
- if (!isList(form)) return form
54
+ if (!is.list(form)) return form
49
55
 
50
56
  // Empty list
51
57
  if (form.value.length === 0) return form
@@ -53,9 +59,13 @@ export function macroExpandAllWithContext(
53
59
  const first = form.value[0]
54
60
 
55
61
  // Non-symbol head (e.g. anonymous fn call `((fn [x] x) 5)`): expand all sub-forms
56
- if (!isSymbol(first)) {
57
- const expanded = form.value.map((sub) => macroExpandAllWithContext(sub, env, ctx))
58
- return expanded.every((e, i) => e === form.value[i]) ? form : cljList(expanded)
62
+ if (!is.symbol(first)) {
63
+ const expanded = form.value.map((sub) =>
64
+ macroExpandAllWithContext(sub, env, ctx)
65
+ )
66
+ return expanded.every((e, i) => e === form.value[i])
67
+ ? form
68
+ : cljList(expanded)
59
69
  }
60
70
 
61
71
  const name = first.name
@@ -66,14 +76,34 @@ export function macroExpandAllWithContext(
66
76
  // Check whether the head resolves to a macro in the current env.
67
77
  // tryLookup returns undefined for unknown symbols (forward refs, fn params, etc.)
68
78
  // avoiding the try/catch exception path entirely.
69
- const macroOrUnknown = tryLookup(name, env)
70
- if (macroOrUnknown !== undefined && isMacro(macroOrUnknown)) {
79
+ // For qualified symbols like clojure.core/when-let or aliased m/my-macro,
80
+ // resolve via local :as alias first, then full namespace name.
81
+ let macroOrUnknown: CljValue | undefined
82
+ const slashIdx = name.indexOf('/')
83
+ if (slashIdx > 0 && slashIdx < name.length - 1) {
84
+ const nsPrefix = name.slice(0, slashIdx)
85
+ const localName = name.slice(slashIdx + 1)
86
+ const nsEnv = getNamespaceEnv(env)
87
+ const targetNs =
88
+ nsEnv.ns?.aliases.get(nsPrefix) ?? ctx.resolveNs(nsPrefix) ?? null
89
+ if (targetNs) {
90
+ const v = targetNs.vars.get(localName)
91
+ macroOrUnknown = v !== undefined ? derefValue(v) : undefined
92
+ }
93
+ } else {
94
+ macroOrUnknown = tryLookup(name, env)
95
+ }
96
+ if (macroOrUnknown !== undefined && is.macro(macroOrUnknown)) {
71
97
  const expanded = ctx.applyMacro(macroOrUnknown, form.value.slice(1))
72
98
  // Keep expanding until no more macros at the top level
73
99
  return macroExpandAllWithContext(expanded, env, ctx)
74
100
  }
75
101
 
76
102
  // Special forms and function calls: expand all sub-forms
77
- const expanded = form.value.map((sub) => macroExpandAllWithContext(sub, env, ctx))
78
- return expanded.every((e, i) => e === form.value[i]) ? form : cljList(expanded)
103
+ const expanded = form.value.map((sub) =>
104
+ macroExpandAllWithContext(sub, env, ctx)
105
+ )
106
+ return expanded.every((e, i) => e === form.value[i])
107
+ ? form
108
+ : cljList(expanded)
79
109
  }