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.
- package/dist-cli/conjure-js.mjs +9360 -5298
- package/dist-vite-plugin/index.mjs +9463 -5185
- package/package.json +3 -1
- package/src/bin/cli.ts +2 -2
- package/src/bin/nrepl-symbol.ts +150 -0
- package/src/bin/nrepl.ts +289 -167
- package/src/bin/version.ts +1 -1
- package/src/clojure/core.clj +757 -29
- package/src/clojure/core.clj.d.ts +75 -131
- package/src/clojure/generated/builtin-namespace-registry.ts +4 -0
- package/src/clojure/generated/clojure-core-source.ts +758 -29
- package/src/clojure/generated/clojure-set-source.ts +136 -0
- package/src/clojure/generated/clojure-walk-source.ts +72 -0
- package/src/clojure/set.clj +132 -0
- package/src/clojure/set.clj.d.ts +20 -0
- package/src/clojure/string.clj.d.ts +14 -0
- package/src/clojure/walk.clj +68 -0
- package/src/clojure/walk.clj.d.ts +7 -0
- package/src/core/assertions.ts +114 -6
- package/src/core/bootstrap.ts +337 -0
- package/src/core/conversions.ts +48 -31
- package/src/core/core-module.ts +303 -0
- package/src/core/env.ts +20 -6
- package/src/core/evaluator/apply.ts +40 -25
- package/src/core/evaluator/arity.ts +8 -8
- package/src/core/evaluator/async-evaluator.ts +565 -0
- package/src/core/evaluator/collections.ts +28 -5
- package/src/core/evaluator/destructure.ts +180 -69
- package/src/core/evaluator/dispatch.ts +12 -14
- package/src/core/evaluator/evaluate.ts +22 -20
- package/src/core/evaluator/expand.ts +45 -15
- package/src/core/evaluator/form-parsers.ts +178 -0
- package/src/core/evaluator/index.ts +7 -9
- package/src/core/evaluator/js-interop.ts +189 -0
- package/src/core/evaluator/quasiquote.ts +14 -8
- package/src/core/evaluator/recur-check.ts +6 -6
- package/src/core/evaluator/special-forms.ts +234 -191
- package/src/core/factories.ts +182 -3
- package/src/core/index.ts +54 -4
- package/src/core/module.ts +136 -0
- package/src/core/ns-forms.ts +107 -0
- package/src/core/printer.ts +371 -11
- package/src/core/reader.ts +84 -33
- package/src/core/registry.ts +209 -0
- package/src/core/runtime.ts +376 -0
- package/src/core/session.ts +253 -487
- package/src/core/stdlib/arithmetic.ts +528 -194
- package/src/core/stdlib/async-fns.ts +132 -0
- package/src/core/stdlib/atoms.ts +291 -56
- package/src/core/stdlib/errors.ts +54 -50
- package/src/core/stdlib/hof.ts +82 -166
- package/src/core/stdlib/js-namespace.ts +344 -0
- package/src/core/stdlib/lazy.ts +34 -0
- package/src/core/stdlib/maps-sets.ts +322 -0
- package/src/core/stdlib/meta.ts +61 -30
- package/src/core/stdlib/predicates.ts +325 -187
- package/src/core/stdlib/regex.ts +126 -98
- package/src/core/stdlib/seq.ts +564 -0
- package/src/core/stdlib/strings.ts +164 -135
- package/src/core/stdlib/transducers.ts +95 -100
- package/src/core/stdlib/utils.ts +292 -130
- package/src/core/stdlib/vars.ts +27 -27
- package/src/core/stdlib/vectors.ts +122 -0
- package/src/core/tokenizer.ts +2 -2
- package/src/core/transformations.ts +117 -9
- package/src/core/types.ts +98 -2
- package/src/host/node-host-module.ts +74 -0
- package/src/{vite-plugin-clj/nrepl-relay.ts → nrepl/relay.ts} +72 -11
- package/src/vite-plugin-clj/codegen.ts +87 -95
- package/src/vite-plugin-clj/index.ts +178 -23
- package/src/vite-plugin-clj/namespace-utils.ts +39 -0
- package/src/vite-plugin-clj/static-analysis.ts +211 -0
- package/src/clojure/demo.clj +0 -72
- package/src/clojure/demo.clj.d.ts +0 -0
- package/src/core/core-env.ts +0 -61
- package/src/core/stdlib/collections.ts +0 -739
- 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
|
+
}
|
package/src/core/stdlib/meta.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
// Metadata: with-meta, meta, alter-meta!
|
|
2
|
-
import {
|
|
2
|
+
import { is } from '../assertions'
|
|
3
3
|
import { EvaluationError } from '../errors'
|
|
4
|
-
import {
|
|
4
|
+
import { v } from '../factories'
|
|
5
5
|
import { printString } from '../printer'
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
CljAtom,
|
|
8
|
+
CljMap,
|
|
9
|
+
CljValue,
|
|
10
|
+
Env,
|
|
11
|
+
EvaluationContext,
|
|
12
|
+
} from '../types'
|
|
7
13
|
|
|
8
14
|
export const metaFunctions: Record<string, CljValue> = {
|
|
9
|
-
meta:
|
|
10
|
-
|
|
15
|
+
meta: v
|
|
16
|
+
.nativeFn('meta', function metaImpl(val: CljValue) {
|
|
11
17
|
if (val === undefined) {
|
|
12
18
|
throw EvaluationError.atArg('meta expects one argument', {}, 0)
|
|
13
19
|
}
|
|
@@ -21,16 +27,17 @@ export const metaFunctions: Record<string, CljValue> = {
|
|
|
21
27
|
val.kind === 'symbol' ||
|
|
22
28
|
val.kind === 'atom'
|
|
23
29
|
) {
|
|
24
|
-
return val.meta ??
|
|
30
|
+
return val.meta ?? v.nil()
|
|
25
31
|
}
|
|
26
|
-
return
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
),
|
|
31
38
|
|
|
32
|
-
'with-meta':
|
|
33
|
-
|
|
39
|
+
'with-meta': v
|
|
40
|
+
.nativeFn('with-meta', function withMetaImpl(val: CljValue, m: CljValue) {
|
|
34
41
|
if (val === undefined) {
|
|
35
42
|
throw EvaluationError.atArg('with-meta expects two arguments', {}, 0)
|
|
36
43
|
}
|
|
@@ -38,7 +45,11 @@ export const metaFunctions: Record<string, CljValue> = {
|
|
|
38
45
|
throw EvaluationError.atArg('with-meta expects two arguments', {}, 1)
|
|
39
46
|
}
|
|
40
47
|
if (m.kind !== 'map' && m.kind !== 'nil') {
|
|
41
|
-
throw EvaluationError.atArg(
|
|
48
|
+
throw EvaluationError.atArg(
|
|
49
|
+
`with-meta expects a map as second argument, got ${printString(m)}`,
|
|
50
|
+
{ m },
|
|
51
|
+
1
|
|
52
|
+
)
|
|
42
53
|
}
|
|
43
54
|
const metaSupported =
|
|
44
55
|
val.kind === 'function' ||
|
|
@@ -48,24 +59,42 @@ export const metaFunctions: Record<string, CljValue> = {
|
|
|
48
59
|
val.kind === 'map' ||
|
|
49
60
|
val.kind === 'symbol'
|
|
50
61
|
if (!metaSupported) {
|
|
51
|
-
throw EvaluationError.atArg(
|
|
62
|
+
throw EvaluationError.atArg(
|
|
63
|
+
`with-meta does not support ${val.kind}, got ${printString(val)}`,
|
|
64
|
+
{ val },
|
|
65
|
+
0
|
|
66
|
+
)
|
|
52
67
|
}
|
|
53
68
|
const meta = m.kind === 'nil' ? undefined : (m as CljMap)
|
|
54
69
|
return { ...val, meta }
|
|
55
|
-
})
|
|
56
|
-
'Returns a new value with the metadata map m applied to val.',
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
})
|
|
71
|
+
.doc('Returns a new value with the metadata map m applied to val.', [
|
|
72
|
+
['val', 'm'],
|
|
73
|
+
]),
|
|
59
74
|
|
|
60
|
-
'alter-meta!':
|
|
61
|
-
|
|
75
|
+
'alter-meta!': v
|
|
76
|
+
.nativeFnCtx(
|
|
62
77
|
'alter-meta!',
|
|
63
|
-
(
|
|
78
|
+
function alterMetaImpl(
|
|
79
|
+
ctx: EvaluationContext,
|
|
80
|
+
callEnv: Env,
|
|
81
|
+
ref: CljValue,
|
|
82
|
+
f: CljValue,
|
|
83
|
+
...args: CljValue[]
|
|
84
|
+
) {
|
|
64
85
|
if (ref === undefined) {
|
|
65
|
-
throw EvaluationError.atArg(
|
|
86
|
+
throw EvaluationError.atArg(
|
|
87
|
+
'alter-meta! expects at least two arguments',
|
|
88
|
+
{},
|
|
89
|
+
0
|
|
90
|
+
)
|
|
66
91
|
}
|
|
67
92
|
if (f === undefined) {
|
|
68
|
-
throw EvaluationError.atArg(
|
|
93
|
+
throw EvaluationError.atArg(
|
|
94
|
+
'alter-meta! expects at least two arguments',
|
|
95
|
+
{},
|
|
96
|
+
1
|
|
97
|
+
)
|
|
69
98
|
}
|
|
70
99
|
if (ref.kind !== 'var' && ref.kind !== 'atom') {
|
|
71
100
|
throw EvaluationError.atArg(
|
|
@@ -74,14 +103,14 @@ export const metaFunctions: Record<string, CljValue> = {
|
|
|
74
103
|
0
|
|
75
104
|
)
|
|
76
105
|
}
|
|
77
|
-
if (!
|
|
106
|
+
if (!is.aFunction(f)) {
|
|
78
107
|
throw EvaluationError.atArg(
|
|
79
108
|
`alter-meta! expects a function as second argument, got ${f.kind}`,
|
|
80
109
|
{},
|
|
81
110
|
1
|
|
82
111
|
)
|
|
83
112
|
}
|
|
84
|
-
const currentMeta: CljValue = ref.meta ??
|
|
113
|
+
const currentMeta: CljValue = ref.meta ?? v.nil()
|
|
85
114
|
const newMeta = ctx.applyCallable(f, [currentMeta, ...args], callEnv)
|
|
86
115
|
if (newMeta.kind !== 'map' && newMeta.kind !== 'nil') {
|
|
87
116
|
throw new EvaluationError(
|
|
@@ -89,11 +118,13 @@ export const metaFunctions: Record<string, CljValue> = {
|
|
|
89
118
|
{}
|
|
90
119
|
)
|
|
91
120
|
}
|
|
92
|
-
;(ref as CljAtom).meta =
|
|
121
|
+
;(ref as CljAtom).meta =
|
|
122
|
+
newMeta.kind === 'nil' ? undefined : (newMeta as CljMap)
|
|
93
123
|
return newMeta
|
|
94
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']]
|
|
95
129
|
),
|
|
96
|
-
"Applies f to ref's current metadata (with optional args), sets the result as the new metadata, and returns it.",
|
|
97
|
-
[['ref', 'f', '&', 'args']]
|
|
98
|
-
),
|
|
99
130
|
}
|