conjure-js 0.0.11 → 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 (80) hide show
  1. package/dist-cli/conjure-js.mjs +9336 -5028
  2. package/dist-vite-plugin/index.mjs +10455 -0
  3. package/package.json +9 -2
  4. package/src/bin/cli.ts +2 -2
  5. package/src/bin/nrepl-symbol.ts +150 -0
  6. package/src/bin/nrepl.ts +301 -157
  7. package/src/bin/version.ts +1 -1
  8. package/src/clojure/core.clj +764 -29
  9. package/src/clojure/core.clj.d.ts +76 -4
  10. package/src/clojure/demo/math.clj +5 -1
  11. package/src/clojure/generated/builtin-namespace-registry.ts +4 -0
  12. package/src/clojure/generated/clojure-core-source.ts +765 -29
  13. package/src/clojure/generated/clojure-set-source.ts +136 -0
  14. package/src/clojure/generated/clojure-walk-source.ts +72 -0
  15. package/src/clojure/set.clj +132 -0
  16. package/src/clojure/set.clj.d.ts +20 -0
  17. package/src/clojure/string.clj.d.ts +14 -0
  18. package/src/clojure/walk.clj +68 -0
  19. package/src/clojure/walk.clj.d.ts +7 -0
  20. package/src/core/assertions.ts +114 -6
  21. package/src/core/bootstrap.ts +337 -0
  22. package/src/core/conversions.ts +48 -31
  23. package/src/core/core-module.ts +303 -0
  24. package/src/core/env.ts +42 -7
  25. package/src/core/errors.ts +8 -0
  26. package/src/core/evaluator/apply.ts +40 -25
  27. package/src/core/evaluator/arity.ts +8 -8
  28. package/src/core/evaluator/async-evaluator.ts +565 -0
  29. package/src/core/evaluator/collections.ts +30 -4
  30. package/src/core/evaluator/destructure.ts +180 -69
  31. package/src/core/evaluator/dispatch.ts +24 -14
  32. package/src/core/evaluator/evaluate.ts +22 -20
  33. package/src/core/evaluator/expand.ts +45 -15
  34. package/src/core/evaluator/form-parsers.ts +178 -0
  35. package/src/core/evaluator/index.ts +7 -9
  36. package/src/core/evaluator/js-interop.ts +189 -0
  37. package/src/core/evaluator/quasiquote.ts +14 -8
  38. package/src/core/evaluator/recur-check.ts +6 -6
  39. package/src/core/evaluator/special-forms.ts +380 -173
  40. package/src/core/factories.ts +182 -3
  41. package/src/core/index.ts +55 -5
  42. package/src/core/module.ts +136 -0
  43. package/src/core/ns-forms.ts +107 -0
  44. package/src/core/positions.ts +9 -2
  45. package/src/core/printer.ts +371 -11
  46. package/src/core/reader.ts +127 -29
  47. package/src/core/registry.ts +209 -0
  48. package/src/core/runtime.ts +376 -0
  49. package/src/core/session.ts +263 -478
  50. package/src/core/stdlib/arithmetic.ts +516 -215
  51. package/src/core/stdlib/async-fns.ts +132 -0
  52. package/src/core/stdlib/atoms.ts +286 -63
  53. package/src/core/stdlib/errors.ts +54 -50
  54. package/src/core/stdlib/hof.ts +74 -173
  55. package/src/core/stdlib/js-namespace.ts +344 -0
  56. package/src/core/stdlib/lazy.ts +34 -0
  57. package/src/core/stdlib/maps-sets.ts +322 -0
  58. package/src/core/stdlib/meta.ts +109 -28
  59. package/src/core/stdlib/predicates.ts +322 -196
  60. package/src/core/stdlib/regex.ts +126 -98
  61. package/src/core/stdlib/seq.ts +564 -0
  62. package/src/core/stdlib/strings.ts +164 -135
  63. package/src/core/stdlib/transducers.ts +95 -100
  64. package/src/core/stdlib/utils.ts +283 -147
  65. package/src/core/stdlib/vars.ts +27 -27
  66. package/src/core/stdlib/vectors.ts +122 -0
  67. package/src/core/tokenizer.ts +13 -3
  68. package/src/core/transformations.ts +117 -9
  69. package/src/core/types.ts +118 -6
  70. package/src/host/node-host-module.ts +74 -0
  71. package/src/nrepl/relay.ts +432 -0
  72. package/src/vite-plugin-clj/codegen.ts +87 -95
  73. package/src/vite-plugin-clj/index.ts +242 -18
  74. package/src/vite-plugin-clj/namespace-utils.ts +39 -0
  75. package/src/vite-plugin-clj/static-analysis.ts +211 -0
  76. package/src/clojure/demo.clj +0 -63
  77. package/src/clojure/demo.clj.d.ts +0 -0
  78. package/src/core/core-env.ts +0 -60
  79. package/src/core/stdlib/collections.ts +0 -784
  80. package/src/host/node.ts +0 -55
@@ -0,0 +1,34 @@
1
+ import { is } from '../assertions'
2
+ import { v } from '../factories'
3
+ import { realizeDelay, realizeLazySeq } from '../transformations'
4
+ import type { CljValue } from '../types'
5
+
6
+ export const lazyFunctions = {
7
+ force: v
8
+ .nativeFn('force', function force(value: CljValue) {
9
+ if (is.delay(value)) return realizeDelay(value)
10
+ if (is.lazySeq(value)) return realizeLazySeq(value)
11
+ return value
12
+ })
13
+ .doc(
14
+ 'If x is a Delay or LazySeq, forces and returns the realized value. Otherwise returns x.',
15
+ [['x']]
16
+ ),
17
+ 'delay?': v
18
+ .nativeFn('delay?', function isDelayPred(value: CljValue) {
19
+ return v.boolean(is.delay(value))
20
+ })
21
+ .doc('Returns true if x is a Delay.', [['x']]),
22
+ 'lazy-seq?': v
23
+ .nativeFn('lazy-seq?', function isLazySeqPred(value: CljValue) {
24
+ return v.boolean(is.lazySeq(value))
25
+ })
26
+ .doc('Returns true if x is a LazySeq.', [['x']]),
27
+ 'realized?': v
28
+ .nativeFn('realized?', function isRealized(value: CljValue) {
29
+ if (is.delay(value)) return v.boolean(value.realized)
30
+ if (is.lazySeq(value)) return v.boolean(value.realized)
31
+ return v.boolean(false)
32
+ })
33
+ .doc('Returns true if a Delay or LazySeq has been realized.', [['x']]),
34
+ }
@@ -0,0 +1,322 @@
1
+ // Associative and set operations: hash-map, assoc, dissoc, keys, vals, zipmap,
2
+ // hash-set, set, set?, disj
3
+ //
4
+ // assoc and dissoc handle both maps and vectors (by numeric index). They live
5
+ // here because their primary semantic is associative (key→value update/remove);
6
+ // the vector branch is an edge case within the same dispatch.
7
+
8
+ import { is } from '../assertions'
9
+ import { EvaluationError } from '../errors'
10
+ import { v } from '../factories'
11
+ import { printString } from '../printer'
12
+ import { toSeq } from '../transformations'
13
+ import { type CljNumber, type CljValue } from '../types'
14
+
15
+ export const mapsSetsFunctions: Record<string, CljValue> = {
16
+ 'hash-map': v
17
+ .nativeFn('hash-map', function hashMapImpl(...kvals: CljValue[]) {
18
+ if (kvals.length === 0) {
19
+ return v.map([])
20
+ }
21
+ if (kvals.length % 2 !== 0) {
22
+ throw new EvaluationError(
23
+ `hash-map expects an even number of arguments, got ${kvals.length}`,
24
+ { args: kvals }
25
+ )
26
+ }
27
+ const entries: [CljValue, CljValue][] = []
28
+ for (let i = 0; i < kvals.length; i += 2) {
29
+ const key = kvals[i]
30
+ const value = kvals[i + 1]
31
+ entries.push([key, value])
32
+ }
33
+ return v.map(entries)
34
+ })
35
+ .doc('Returns a new hash-map containing the given key-value pairs.', [
36
+ ['&', 'kvals'],
37
+ ]),
38
+
39
+ assoc: v
40
+ .nativeFn(
41
+ 'assoc',
42
+ function assocImpl(collection: CljValue, ...args: CljValue[]) {
43
+ if (!collection) {
44
+ throw new EvaluationError(
45
+ 'assoc expects a collection as first argument',
46
+ { collection }
47
+ )
48
+ }
49
+ // nil is treated as an empty map, matching Clojure: (assoc nil :k v) => {:k v}
50
+ if (is.nil(collection)) {
51
+ collection = v.map([])
52
+ }
53
+ if (is.list(collection)) {
54
+ throw new EvaluationError(
55
+ 'assoc on lists is not supported, use vectors instead',
56
+ { collection }
57
+ )
58
+ }
59
+ if (!is.collection(collection)) {
60
+ throw EvaluationError.atArg(
61
+ `assoc expects a collection, got ${printString(collection)}`,
62
+ { collection },
63
+ 0
64
+ )
65
+ }
66
+ if (args.length < 2) {
67
+ throw new EvaluationError('assoc expects at least two arguments', {
68
+ args,
69
+ })
70
+ }
71
+ if (args.length % 2 !== 0) {
72
+ throw new EvaluationError(
73
+ 'assoc expects an even number of binding arguments',
74
+ {
75
+ args,
76
+ }
77
+ )
78
+ }
79
+ if (is.vector(collection)) {
80
+ const newValues = [...collection.value]
81
+ for (let i = 0; i < args.length; i += 2) {
82
+ const index = args[i]
83
+ if (index.kind !== 'number') {
84
+ throw EvaluationError.atArg(
85
+ `assoc on vectors expects each key argument to be a index (number), got ${printString(index)}`,
86
+ { index },
87
+ i + 1
88
+ )
89
+ }
90
+ if (index.value > newValues.length) {
91
+ throw EvaluationError.atArg(
92
+ `assoc index ${index.value} is out of bounds for vector of length ${newValues.length}`,
93
+ { index, collection },
94
+ i + 1
95
+ )
96
+ }
97
+ newValues[(index as CljNumber).value] = args[i + 1]
98
+ }
99
+ return v.vector(newValues)
100
+ }
101
+ if (is.map(collection)) {
102
+ const newEntries: [CljValue, CljValue][] = [...collection.entries]
103
+ // need to find the entry with the same key and replace it, if it doesn't exist, add it
104
+ for (let i = 0; i < args.length; i += 2) {
105
+ const key = args[i]
106
+ const value = args[i + 1]
107
+ const entryIdx = newEntries.findIndex(
108
+ function findEntryByKey(entry) {
109
+ return is.equal(entry[0], key)
110
+ }
111
+ )
112
+ if (entryIdx === -1) {
113
+ newEntries.push([key, value])
114
+ } else {
115
+ newEntries[entryIdx] = [key, value]
116
+ }
117
+ }
118
+ return v.map(newEntries)
119
+ }
120
+ throw new EvaluationError(
121
+ `unhandled collection type, got ${printString(collection)}`,
122
+ { collection }
123
+ )
124
+ }
125
+ )
126
+ .doc(
127
+ 'Associates the value val with the key k in collection. If collection is a map, returns a new map with the same mappings, otherwise returns a vector with the new value at index k.',
128
+ [['collection', '&', 'kvals']]
129
+ ),
130
+
131
+ dissoc: v
132
+ .nativeFn(
133
+ 'dissoc',
134
+ function dissocImpl(collection: CljValue, ...args: CljValue[]) {
135
+ if (!collection) {
136
+ throw new EvaluationError(
137
+ 'dissoc expects a collection as first argument',
138
+ { collection }
139
+ )
140
+ }
141
+ if (is.list(collection)) {
142
+ throw EvaluationError.atArg(
143
+ 'dissoc on lists is not supported, use vectors instead',
144
+ { collection },
145
+ 0
146
+ )
147
+ }
148
+ if (!is.collection(collection)) {
149
+ throw EvaluationError.atArg(
150
+ `dissoc expects a collection, got ${printString(collection)}`,
151
+ { collection },
152
+ 0
153
+ )
154
+ }
155
+ if (is.vector(collection)) {
156
+ if (collection.value.length === 0) {
157
+ return collection // return the empty vector
158
+ }
159
+ const newValues = [...collection.value]
160
+ for (let i = 0; i < args.length; i += 1) {
161
+ const index = args[i]
162
+ if (index.kind !== 'number') {
163
+ throw EvaluationError.atArg(
164
+ `dissoc on vectors expects each key argument to be a index (number), got ${printString(index)}`,
165
+ { index },
166
+ i + 1
167
+ )
168
+ }
169
+ if (index.value >= newValues.length) {
170
+ throw EvaluationError.atArg(
171
+ `dissoc index ${index.value} is out of bounds for vector of length ${newValues.length}`,
172
+ { index, collection },
173
+ i + 1
174
+ )
175
+ }
176
+ newValues.splice(index.value, 1)
177
+ }
178
+ return v.vector(newValues)
179
+ }
180
+ if (is.map(collection)) {
181
+ if (collection.entries.length === 0) {
182
+ return collection // return the empty map
183
+ }
184
+ const newEntries: [CljValue, CljValue][] = [...collection.entries]
185
+ for (let i = 0; i < args.length; i += 1) {
186
+ const key = args[i]
187
+ const entryIdx = newEntries.findIndex(
188
+ function findEntryByKey(entry) {
189
+ return is.equal(entry[0], key)
190
+ }
191
+ )
192
+ if (entryIdx === -1) {
193
+ return collection // not found, unchanged
194
+ }
195
+ newEntries.splice(entryIdx, 1)
196
+ }
197
+ return v.map(newEntries)
198
+ }
199
+ throw new EvaluationError(
200
+ `unhandled collection type, got ${printString(collection)}`,
201
+ { collection }
202
+ )
203
+ }
204
+ )
205
+ .doc(
206
+ 'Dissociates the key k from collection. If collection is a map, returns a new map with the same mappings, otherwise returns a vector with the value at index k removed.',
207
+ [['collection', '&', 'keys']]
208
+ ),
209
+
210
+ zipmap: v
211
+ .nativeFn('zipmap', function zipmapImpl(ks: CljValue, vs: CljValue) {
212
+ if (ks === undefined || !is.seqable(ks)) {
213
+ throw new EvaluationError(
214
+ `zipmap expects a collection or string as first argument${ks !== undefined ? `, got ${printString(ks)}` : ''}`,
215
+ { ks }
216
+ )
217
+ }
218
+ if (vs === undefined || !is.seqable(vs)) {
219
+ throw new EvaluationError(
220
+ `zipmap expects a collection or string as second argument${vs !== undefined ? `, got ${printString(vs)}` : ''}`,
221
+ { vs }
222
+ )
223
+ }
224
+ const keys = toSeq(ks)
225
+ const vals = toSeq(vs)
226
+ const len = Math.min(keys.length, vals.length)
227
+ const entries: [CljValue, CljValue][] = []
228
+ for (let i = 0; i < len; i++) {
229
+ entries.push([keys[i], vals[i]])
230
+ }
231
+ return v.map(entries)
232
+ })
233
+ .doc(
234
+ 'Returns a new map with the keys and values of the given collections.',
235
+ [['ks', 'vs']]
236
+ ),
237
+
238
+ keys: v
239
+ .nativeFn('keys', function keysImpl(m: CljValue) {
240
+ if (m === undefined || !is.map(m)) {
241
+ throw EvaluationError.atArg(
242
+ `keys expects a map${m !== undefined ? `, got ${printString(m)}` : ''}`,
243
+ { m },
244
+ 0
245
+ )
246
+ }
247
+ return v.vector(
248
+ m.entries.map(function extractKey([k]) {
249
+ return k
250
+ })
251
+ )
252
+ })
253
+ .doc('Returns a vector of the keys of the given map.', [['m']]),
254
+
255
+ vals: v
256
+ .nativeFn('vals', function valsImpl(m: CljValue) {
257
+ if (m === undefined || !is.map(m)) {
258
+ throw EvaluationError.atArg(
259
+ `vals expects a map${m !== undefined ? `, got ${printString(m)}` : ''}`,
260
+ { m },
261
+ 0
262
+ )
263
+ }
264
+ return v.vector(
265
+ m.entries.map(function extractVal([, v]) {
266
+ return v
267
+ })
268
+ )
269
+ })
270
+ .doc('Returns a vector of the values of the given map.', [['m']]),
271
+
272
+ 'hash-set': v
273
+ .nativeFn('hash-set', function hashSetImpl(...args: CljValue[]) {
274
+ const deduped: CljValue[] = []
275
+ for (const v of args) {
276
+ if (!deduped.some((existing) => is.equal(existing, v))) {
277
+ deduped.push(v)
278
+ }
279
+ }
280
+ return v.set(deduped)
281
+ })
282
+ .doc('Returns a set containing the given values.', [['&', 'xs']]),
283
+
284
+ set: v
285
+ .nativeFn('set', function setImpl(coll: CljValue) {
286
+ if (coll === undefined || coll.kind === 'nil') return v.set([])
287
+ const items = toSeq(coll)
288
+ const deduped: CljValue[] = []
289
+ for (const v of items) {
290
+ if (!deduped.some((existing) => is.equal(existing, v))) {
291
+ deduped.push(v)
292
+ }
293
+ }
294
+ return v.set(deduped)
295
+ })
296
+ .doc('Returns a set of the distinct elements of the given collection.', [
297
+ ['coll'],
298
+ ]),
299
+
300
+ 'set?': v
301
+ .nativeFn('set?', function setPredicateImpl(x: CljValue) {
302
+ return v.boolean(x !== undefined && x.kind === 'set')
303
+ })
304
+ .doc('Returns true if x is a set.', [['x']]),
305
+
306
+ disj: v
307
+ .nativeFn('disj', function disjImpl(s: CljValue, ...items: CljValue[]) {
308
+ if (s === undefined || s.kind === 'nil') return v.set([])
309
+ if (s.kind !== 'set') {
310
+ throw EvaluationError.atArg(
311
+ `disj expects a set, got ${printString(s)}`,
312
+ { s },
313
+ 0
314
+ )
315
+ }
316
+ const newValues = s.values.filter(
317
+ (v) => !items.some((item) => is.equal(item, v))
318
+ )
319
+ return v.set(newValues)
320
+ })
321
+ .doc('Returns a set with the given items removed.', [['s', '&', 'items']]),
322
+ }
@@ -1,49 +1,130 @@
1
- // Metadata: with-meta, meta
1
+ // Metadata: with-meta, meta, alter-meta!
2
+ import { is } from '../assertions'
2
3
  import { EvaluationError } from '../errors'
3
- import { cljNativeFunction, cljNil, withDoc } from '../factories'
4
+ import { v } from '../factories'
4
5
  import { printString } from '../printer'
5
- import type { CljMap, CljValue } from '../types'
6
+ import type {
7
+ CljAtom,
8
+ CljMap,
9
+ CljValue,
10
+ Env,
11
+ EvaluationContext,
12
+ } from '../types'
6
13
 
7
14
  export const metaFunctions: Record<string, CljValue> = {
8
- meta: withDoc(
9
- cljNativeFunction('meta', (val: CljValue) => {
15
+ meta: v
16
+ .nativeFn('meta', function metaImpl(val: CljValue) {
10
17
  if (val === undefined) {
11
- throw new EvaluationError('meta expects one argument', {})
18
+ throw EvaluationError.atArg('meta expects one argument', {}, 0)
12
19
  }
13
- if (val.kind === 'function' || val.kind === 'native-function') {
14
- return val.meta ?? cljNil()
20
+ if (
21
+ val.kind === 'function' ||
22
+ val.kind === 'native-function' ||
23
+ val.kind === 'var' ||
24
+ val.kind === 'list' ||
25
+ val.kind === 'vector' ||
26
+ val.kind === 'map' ||
27
+ val.kind === 'symbol' ||
28
+ val.kind === 'atom'
29
+ ) {
30
+ return val.meta ?? v.nil()
15
31
  }
16
- if (val.kind === 'var') return val.meta ?? cljNil()
17
- return cljNil()
18
- }),
19
- 'Returns the metadata map of a value, or nil if the value has no metadata.',
20
- [['val']]
21
- ),
32
+ return v.nil()
33
+ })
34
+ .doc(
35
+ 'Returns the metadata map of a value, or nil if the value has no metadata.',
36
+ [['val']]
37
+ ),
22
38
 
23
- 'with-meta': withDoc(
24
- cljNativeFunction('with-meta', (val: CljValue, m: CljValue) => {
39
+ 'with-meta': v
40
+ .nativeFn('with-meta', function withMetaImpl(val: CljValue, m: CljValue) {
25
41
  if (val === undefined) {
26
- throw new EvaluationError('with-meta expects two arguments', {})
42
+ throw EvaluationError.atArg('with-meta expects two arguments', {}, 0)
27
43
  }
28
44
  if (m === undefined) {
29
- throw new EvaluationError('with-meta expects two arguments', {})
45
+ throw EvaluationError.atArg('with-meta expects two arguments', {}, 1)
30
46
  }
31
47
  if (m.kind !== 'map' && m.kind !== 'nil') {
32
- throw new EvaluationError(
48
+ throw EvaluationError.atArg(
33
49
  `with-meta expects a map as second argument, got ${printString(m)}`,
34
- { m }
50
+ { m },
51
+ 1
35
52
  )
36
53
  }
37
- if (val.kind !== 'function' && val.kind !== 'native-function') {
38
- throw new EvaluationError(
39
- `with-meta only supports functions, got ${printString(val)}`,
40
- { val }
54
+ const metaSupported =
55
+ val.kind === 'function' ||
56
+ val.kind === 'native-function' ||
57
+ val.kind === 'list' ||
58
+ val.kind === 'vector' ||
59
+ val.kind === 'map' ||
60
+ val.kind === 'symbol'
61
+ if (!metaSupported) {
62
+ throw EvaluationError.atArg(
63
+ `with-meta does not support ${val.kind}, got ${printString(val)}`,
64
+ { val },
65
+ 0
41
66
  )
42
67
  }
43
68
  const meta = m.kind === 'nil' ? undefined : (m as CljMap)
44
69
  return { ...val, meta }
45
- }),
46
- 'Returns a new value with the metadata map m applied to val.',
47
- [['val', 'm']]
48
- ),
70
+ })
71
+ .doc('Returns a new value with the metadata map m applied to val.', [
72
+ ['val', 'm'],
73
+ ]),
74
+
75
+ 'alter-meta!': v
76
+ .nativeFnCtx(
77
+ 'alter-meta!',
78
+ function alterMetaImpl(
79
+ ctx: EvaluationContext,
80
+ callEnv: Env,
81
+ ref: CljValue,
82
+ f: CljValue,
83
+ ...args: CljValue[]
84
+ ) {
85
+ if (ref === undefined) {
86
+ throw EvaluationError.atArg(
87
+ 'alter-meta! expects at least two arguments',
88
+ {},
89
+ 0
90
+ )
91
+ }
92
+ if (f === undefined) {
93
+ throw EvaluationError.atArg(
94
+ 'alter-meta! expects at least two arguments',
95
+ {},
96
+ 1
97
+ )
98
+ }
99
+ if (ref.kind !== 'var' && ref.kind !== 'atom') {
100
+ throw EvaluationError.atArg(
101
+ `alter-meta! expects a Var or Atom as first argument, got ${ref.kind}`,
102
+ {},
103
+ 0
104
+ )
105
+ }
106
+ if (!is.aFunction(f)) {
107
+ throw EvaluationError.atArg(
108
+ `alter-meta! expects a function as second argument, got ${f.kind}`,
109
+ {},
110
+ 1
111
+ )
112
+ }
113
+ const currentMeta: CljValue = ref.meta ?? v.nil()
114
+ const newMeta = ctx.applyCallable(f, [currentMeta, ...args], callEnv)
115
+ if (newMeta.kind !== 'map' && newMeta.kind !== 'nil') {
116
+ throw new EvaluationError(
117
+ `alter-meta! function must return a map or nil, got ${newMeta.kind}`,
118
+ {}
119
+ )
120
+ }
121
+ ;(ref as CljAtom).meta =
122
+ newMeta.kind === 'nil' ? undefined : (newMeta as CljMap)
123
+ return newMeta
124
+ }
125
+ )
126
+ .doc(
127
+ "Applies f to ref's current metadata (with optional args), sets the result as the new metadata, and returns it.",
128
+ [['ref', 'f', '&', 'args']]
129
+ ),
49
130
  }