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
@@ -1,68 +1,67 @@
1
1
  // Miscellaneous utilities: str, type, gensym, eval, macroexpand-1, macroexpand,
2
2
  // namespace, name, keyword
3
- import { isKeyword, isList, isMacro, isSymbol, isTruthy } from '../assertions'
4
- import { getRootEnv, tryLookup } from '../env'
3
+ import { is } from '../assertions'
4
+ import { tryLookup } from '../env'
5
5
  import { EvaluationError } from '../errors'
6
- import {
7
- cljBoolean,
8
- cljKeyword,
9
- cljNativeFunction,
10
- cljNativeFunctionWithContext,
11
- cljNil,
12
- cljString,
13
- cljSymbol,
14
- withDoc,
15
- } from '../factories'
6
+ import { v } from '../factories'
16
7
  import { makeGensym } from '../gensym'
17
- import { joinLines, printString } from '../printer'
8
+ import { buildPrintContext, joinLines, prettyPrintString, printString, withPrintContext } from '../printer'
9
+ import { readForms } from '../reader'
10
+ import { tokenize } from '../tokenizer'
18
11
  import { valueToString } from '../transformations'
19
12
  import type { CljValue, Env, EvaluationContext } from '../types'
20
13
 
21
14
  export const utilFunctions: Record<string, CljValue> = {
22
- str: withDoc(
23
- cljNativeFunction('str', (...args: CljValue[]) => {
24
- return cljString(args.map(valueToString).join(''))
25
- }),
26
- 'Returns a concatenated string representation of the given values.',
27
- [['&', 'args']]
28
- ),
29
- subs: withDoc(
30
- cljNativeFunction(
15
+ str: v
16
+ .nativeFn('str', function strImpl(...args: CljValue[]) {
17
+ return v.string(
18
+ args.map((v) => (v.kind === 'nil' ? '' : valueToString(v))).join('')
19
+ )
20
+ })
21
+ .doc('Returns a concatenated string representation of the given values.', [
22
+ ['&', 'args'],
23
+ ]),
24
+ subs: v
25
+ .nativeFn(
31
26
  'subs',
32
- (s: CljValue, start: CljValue, end?: CljValue) => {
27
+ function subsImpl(s: CljValue, start: CljValue, end?: CljValue) {
33
28
  if (s === undefined || s.kind !== 'string') {
34
- throw new EvaluationError(
29
+ throw EvaluationError.atArg(
35
30
  `subs expects a string as first argument${s !== undefined ? `, got ${printString(s)}` : ''}`,
36
- { s }
31
+ { s },
32
+ 0
37
33
  )
38
34
  }
39
35
  if (start === undefined || start.kind !== 'number') {
40
- throw new EvaluationError(
36
+ throw EvaluationError.atArg(
41
37
  `subs expects a number as second argument${start !== undefined ? `, got ${printString(start)}` : ''}`,
42
- { start }
38
+ { start },
39
+ 1
43
40
  )
44
41
  }
45
42
  if (end !== undefined && end.kind !== 'number') {
46
- throw new EvaluationError(
43
+ throw EvaluationError.atArg(
47
44
  `subs expects a number as optional third argument${end !== undefined ? `, got ${printString(end)}` : ''}`,
48
- { end }
45
+ { end },
46
+ 2
49
47
  )
50
48
  }
51
49
  const from = start.value
52
50
  const to = end?.value
53
- return cljString(
51
+ return v.string(
54
52
  to === undefined ? s.value.slice(from) : s.value.slice(from, to)
55
53
  )
56
54
  }
55
+ )
56
+ .doc(
57
+ 'Returns the substring of s beginning at start, and optionally ending before end.',
58
+ [
59
+ ['s', 'start'],
60
+ ['s', 'start', 'end'],
61
+ ]
57
62
  ),
58
- 'Returns the substring of s beginning at start, and optionally ending before end.',
59
- [
60
- ['s', 'start'],
61
- ['s', 'start', 'end'],
62
- ]
63
- ),
64
- type: withDoc(
65
- cljNativeFunction('type', (x: CljValue) => {
63
+ type: v
64
+ .nativeFn('type', function typeImpl(x: CljValue) {
66
65
  if (x === undefined) {
67
66
  throw new EvaluationError('type expects an argument', { x })
68
67
  }
@@ -85,204 +84,341 @@ export const utilFunctions: Record<string, CljValue> = {
85
84
  if (!name) {
86
85
  throw new EvaluationError(`type: unhandled kind ${x.kind}`, { x })
87
86
  }
88
- return cljKeyword(name)
89
- }),
90
- 'Returns a keyword representing the type of the given value.',
91
- [['x']]
92
- ),
93
- gensym: withDoc(
94
- cljNativeFunction('gensym', (...args: CljValue[]) => {
87
+ return v.keyword(name)
88
+ })
89
+ .doc('Returns a keyword representing the type of the given value.', [
90
+ ['x'],
91
+ ]),
92
+ gensym: v
93
+ .nativeFn('gensym', function gensymImpl(...args: CljValue[]) {
95
94
  if (args.length > 1) {
96
95
  throw new EvaluationError('gensym takes 0 or 1 arguments', { args })
97
96
  }
98
97
  const prefix = args[0]
99
98
  if (prefix !== undefined && prefix.kind !== 'string') {
100
- throw new EvaluationError(
99
+ throw EvaluationError.atArg(
101
100
  `gensym prefix must be a string${prefix !== undefined ? `, got ${printString(prefix)}` : ''}`,
102
- { prefix }
101
+ { prefix },
102
+ 0
103
103
  )
104
104
  }
105
105
  const p = prefix?.kind === 'string' ? prefix.value : 'G'
106
- return cljSymbol(makeGensym(p))
107
- }),
108
- 'Returns a unique symbol with the given prefix. Defaults to "G" if no prefix is provided.',
109
- [[], ['prefix']]
110
- ),
111
- eval: withDoc(
112
- cljNativeFunctionWithContext(
106
+ return v.symbol(makeGensym(p))
107
+ })
108
+ .doc(
109
+ 'Returns a unique symbol with the given prefix. Defaults to "G" if no prefix is provided.',
110
+ [[], ['prefix']]
111
+ ),
112
+ eval: v
113
+ .nativeFnCtx(
113
114
  'eval',
114
- (ctx: EvaluationContext, callEnv: Env, form: CljValue | undefined) => {
115
+ function evalImpl(
116
+ ctx: EvaluationContext,
117
+ callEnv: Env,
118
+ form: CljValue | undefined
119
+ ) {
115
120
  if (form === undefined) {
116
121
  throw new EvaluationError('eval expects a form as argument', {
117
122
  form,
118
123
  })
119
124
  }
120
- const rootEnv = getRootEnv(callEnv)
121
- const expanded = ctx.expandAll(form, rootEnv)
122
- return ctx.evaluate(expanded, rootEnv)
125
+ const expanded = ctx.expandAll(form, callEnv)
126
+ return ctx.evaluate(expanded, callEnv)
123
127
  }
128
+ )
129
+ .doc(
130
+ 'Evaluates the given form in the global environment and returns the result.',
131
+ [['form']]
124
132
  ),
125
- 'Evaluates the given form in the global environment and returns the result.',
126
- [['form']]
127
- ),
128
133
 
129
- 'macroexpand-1': withDoc(
130
- cljNativeFunctionWithContext(
134
+ 'macroexpand-1': v
135
+ .nativeFnCtx(
131
136
  'macroexpand-1',
132
- (ctx: EvaluationContext, callEnv: Env, form: CljValue) => {
133
- if (!isList(form) || form.value.length === 0) return form
137
+ function macroexpand1Impl(
138
+ ctx: EvaluationContext,
139
+ callEnv: Env,
140
+ form: CljValue
141
+ ) {
142
+ if (!is.list(form) || form.value.length === 0) return form
134
143
  const head = form.value[0]
135
- if (!isSymbol(head)) return form
136
- const macroValue = tryLookup(head.name, getRootEnv(callEnv))
144
+ if (!is.symbol(head)) return form
145
+ const macroValue = tryLookup(head.name, callEnv)
137
146
  if (macroValue === undefined) return form
138
- if (!isMacro(macroValue)) return form
147
+ if (!is.macro(macroValue)) return form
139
148
  return ctx.applyMacro(macroValue, form.value.slice(1))
140
149
  }
150
+ )
151
+ .doc(
152
+ 'If the head of the form is a macro, expands it and returns the resulting forms. Otherwise, returns the form unchanged.',
153
+ [['form']]
141
154
  ),
142
- 'If the head of the form is a macro, expands it and returns the resulting forms. Otherwise, returns the form unchanged.',
143
- [['form']]
144
- ),
145
155
 
146
- macroexpand: withDoc(
147
- cljNativeFunctionWithContext(
156
+ macroexpand: v
157
+ .nativeFnCtx(
148
158
  'macroexpand',
149
- (ctx: EvaluationContext, callEnv: Env, form: CljValue) => {
150
- const rootEnv = getRootEnv(callEnv)
159
+ function macroexpandImpl(
160
+ ctx: EvaluationContext,
161
+ callEnv: Env,
162
+ form: CljValue
163
+ ) {
151
164
  let current = form
152
165
  while (true) {
153
- if (!isList(current) || current.value.length === 0) return current
166
+ if (!is.list(current) || current.value.length === 0) return current
154
167
  const head = current.value[0]
155
- if (!isSymbol(head)) return current
156
- const macroValue = tryLookup(head.name, rootEnv)
168
+ if (!is.symbol(head)) return current
169
+ const macroValue = tryLookup(head.name, callEnv)
157
170
  if (macroValue === undefined) return current
158
- if (!isMacro(macroValue)) return current
171
+ if (!is.macro(macroValue)) return current
159
172
  current = ctx.applyMacro(macroValue, current.value.slice(1))
160
173
  }
161
174
  }
175
+ )
176
+ .doc(
177
+ joinLines([
178
+ 'Expands all macros until the expansion is stable (head is no longer a macro)',
179
+ '',
180
+ 'Note neither macroexpand-1 nor macroexpand will expand macros in sub-forms',
181
+ ]),
182
+ [['form']]
162
183
  ),
163
- joinLines([
164
- 'Expands all macros until the expansion is stable (head is no longer a macro)',
165
- '',
166
- 'Note neither macroexpand-1 nor macroexpand will expand macros in sub-forms',
167
- ]),
168
- [['form']]
169
- ),
170
184
 
171
- 'macroexpand-all': withDoc(
172
- cljNativeFunctionWithContext(
185
+ 'macroexpand-all': v
186
+ .nativeFnCtx(
173
187
  'macroexpand-all',
174
- (ctx: EvaluationContext, callEnv: Env, form: CljValue) =>
175
- ctx.expandAll(form, getRootEnv(callEnv))
188
+ function macroexpandAllImpl(
189
+ ctx: EvaluationContext,
190
+ callEnv: Env,
191
+ form: CljValue
192
+ ) {
193
+ return ctx.expandAll(form, callEnv)
194
+ }
195
+ )
196
+ .doc(
197
+ joinLines([
198
+ 'Fully expands all macros in a form recursively — including in sub-forms.',
199
+ '',
200
+ 'Unlike macroexpand, this descends into every sub-expression.',
201
+ 'Expansion stops at quote/quasiquote boundaries and fn/loop bodies.',
202
+ ]),
203
+ [['form']]
176
204
  ),
177
- joinLines([
178
- 'Fully expands all macros in a form recursively — including in sub-forms.',
179
- '',
180
- 'Unlike macroexpand, this descends into every sub-expression.',
181
- 'Expansion stops at quote/quasiquote boundaries and fn/loop bodies.',
182
- ]),
183
- [['form']]
184
- ),
185
205
 
186
206
  // Returns the namespace string of a qualified keyword or symbol, or nil.
187
207
  // (namespace :user/foo) => "user"
188
208
  // (namespace :foo) => nil
189
209
  // (namespace 'user/foo) => "user"
190
- namespace: withDoc(
191
- cljNativeFunction('namespace', (x: CljValue) => {
210
+ namespace: v
211
+ .nativeFn('namespace', function namespaceImpl(x: CljValue) {
192
212
  if (x === undefined) {
193
- throw new EvaluationError('namespace expects an argument', { x })
213
+ throw EvaluationError.atArg('namespace expects an argument', { x }, 0)
194
214
  }
195
215
  let raw: string | undefined
196
- if (isKeyword(x)) {
216
+ if (is.keyword(x)) {
197
217
  // keyword name format: ":ns/local" or ":local"
198
218
  raw = x.name.slice(1) // strip leading ":"
199
- } else if (isSymbol(x)) {
219
+ } else if (is.symbol(x)) {
200
220
  raw = x.name
201
221
  } else {
202
- throw new EvaluationError(
222
+ throw EvaluationError.atArg(
203
223
  `namespace expects a keyword or symbol, got ${printString(x)}`,
204
- { x }
224
+ { x },
225
+ 0
205
226
  )
206
227
  }
207
228
  const slashIdx = raw.indexOf('/')
208
- if (slashIdx <= 0) return cljNil()
209
- return cljString(raw.slice(0, slashIdx))
210
- }),
211
- 'Returns the namespace string of a qualified keyword or symbol, or nil if the argument is not qualified.',
212
- [['x']]
213
- ),
229
+ if (slashIdx <= 0) return v.nil()
230
+ return v.string(raw.slice(0, slashIdx))
231
+ })
232
+ .doc(
233
+ 'Returns the namespace string of a qualified keyword or symbol, or nil if the argument is not qualified.',
234
+ [['x']]
235
+ ),
214
236
 
215
237
  // Returns the local name of a keyword or symbol as a string.
216
238
  // (name :user/foo) => "foo"
217
239
  // (name :foo) => "foo"
218
240
  // (name 'user/foo) => "foo"
219
- name: withDoc(
220
- cljNativeFunction('name', (x: CljValue) => {
241
+ name: v
242
+ .nativeFn('name', function nameImpl(x: CljValue) {
221
243
  if (x === undefined) {
222
- throw new EvaluationError('name expects an argument', { x })
244
+ throw EvaluationError.atArg('name expects an argument', { x }, 0)
223
245
  }
224
246
  let raw: string | undefined
225
- if (isKeyword(x)) {
247
+ if (is.keyword(x)) {
226
248
  raw = x.name.slice(1) // strip leading ":"
227
- } else if (isSymbol(x)) {
249
+ } else if (is.symbol(x)) {
228
250
  raw = x.name
229
251
  } else if (x.kind === 'string') {
230
252
  return x
231
253
  } else {
232
- throw new EvaluationError(
254
+ throw EvaluationError.atArg(
233
255
  `name expects a keyword, symbol, or string, got ${printString(x)}`,
234
- { x }
256
+ { x },
257
+ 0
235
258
  )
236
259
  }
237
260
  const slashIdx = raw.indexOf('/')
238
- return cljString(slashIdx >= 0 ? raw.slice(slashIdx + 1) : raw)
239
- }),
240
- 'Returns the local name of a qualified keyword or symbol, or the string value if the argument is a string.',
241
- [['x']]
242
- ),
261
+ return v.string(slashIdx >= 0 ? raw.slice(slashIdx + 1) : raw)
262
+ })
263
+ .doc(
264
+ 'Returns the local name of a qualified keyword or symbol, or the string value if the argument is a string.',
265
+ [['x']]
266
+ ),
243
267
 
244
268
  // Constructs a keyword.
245
269
  // (keyword "foo") => :foo
246
270
  // (keyword "user" "foo") => :user/foo
247
- keyword: withDoc(
248
- cljNativeFunction('keyword', (...args: CljValue[]) => {
271
+ keyword: v
272
+ .nativeFn('keyword', function keywordImpl(...args: CljValue[]) {
249
273
  if (args.length === 0 || args.length > 2) {
250
274
  throw new EvaluationError('keyword expects 1 or 2 string arguments', {
251
275
  args,
252
276
  })
253
277
  }
254
278
  if (args[0].kind !== 'string') {
255
- throw new EvaluationError(
279
+ throw EvaluationError.atArg(
256
280
  `keyword expects a string, got ${printString(args[0])}`,
257
- { args }
281
+ { args },
282
+ 0
258
283
  )
259
284
  }
260
285
  if (args.length === 1) {
261
- return cljKeyword(`:${args[0].value}`)
286
+ return v.keyword(`:${args[0].value}`)
262
287
  }
263
288
  if (args[1].kind !== 'string') {
264
- throw new EvaluationError(
289
+ throw EvaluationError.atArg(
265
290
  `keyword second argument must be a string, got ${printString(args[1])}`,
266
- { args }
291
+ { args },
292
+ 1
293
+ )
294
+ }
295
+ return v.keyword(`:${args[0].value}/${args[1].value}`)
296
+ })
297
+ .doc(
298
+ joinLines([
299
+ 'Constructs a keyword with the given name and namespace strings. Returns a keyword value.',
300
+ '',
301
+ 'Note: do not use : in the keyword strings, it will be added automatically.',
302
+ 'e.g. (keyword "foo") => :foo',
303
+ ]),
304
+ [['name'], ['ns', 'name']]
305
+ ),
306
+
307
+ boolean: v
308
+ .nativeFn('boolean', function booleanImpl(x: CljValue) {
309
+ if (x === undefined) return v.boolean(false)
310
+ return v.boolean(is.truthy(x))
311
+ })
312
+ .doc('Coerces to boolean. Everything is true except false and nil.', [
313
+ ['x'],
314
+ ]),
315
+
316
+ 'clojure-version': v
317
+ .nativeFn('clojure-version', function clojureVersionImpl() {
318
+ return v.string('1.12.0')
319
+ })
320
+ .doc('Returns a string describing the current Clojure version.', [[]]),
321
+
322
+ 'pr-str': v
323
+ .nativeFnCtx('pr-str', function prStrImpl(ctx: EvaluationContext, _callEnv, ...args: CljValue[]) {
324
+ return withPrintContext(buildPrintContext(ctx), () =>
325
+ v.string(args.map(printString).join(' '))
326
+ )
327
+ })
328
+ .doc(
329
+ 'Returns a readable string representation of the given values (strings are quoted).',
330
+ [['&', 'args']]
331
+ ),
332
+
333
+ 'pretty-print-str': v
334
+ .nativeFnCtx(
335
+ 'pretty-print-str',
336
+ function prettyPrintStrImpl(ctx: EvaluationContext, _callEnv, ...args: CljValue[]) {
337
+ if (args.length === 0) return v.string('')
338
+ const form = args[0]
339
+ const widthArg = args[1]
340
+ const maxWidth =
341
+ widthArg !== undefined && widthArg.kind === 'number'
342
+ ? widthArg.value
343
+ : 80
344
+ return withPrintContext(buildPrintContext(ctx), () =>
345
+ v.string(prettyPrintString(form, maxWidth))
267
346
  )
268
347
  }
269
- return cljKeyword(`:${args[0].value}/${args[1].value}`)
270
- }),
271
- joinLines([
272
- 'Constructs a keyword with the given name and namespace strings. Returns a keyword value.',
273
- '',
274
- 'Note: do not use : in the keyword strings, it will be added automatically.',
275
- 'e.g. (keyword "foo") => :foo',
348
+ )
349
+ .doc('Returns a pretty-printed string representation of form.', [
350
+ ['form'],
351
+ ['form', 'max-width'],
276
352
  ]),
277
- [['name'], ['ns', 'name']]
278
- ),
279
353
 
280
- boolean: withDoc(
281
- cljNativeFunction('boolean', (x: CljValue) => {
282
- if (x === undefined) return cljBoolean(false)
283
- return cljBoolean(isTruthy(x))
284
- }),
285
- 'Coerces to boolean. Everything is true except false and nil.',
286
- [['x']]
287
- ),
354
+ 'read-string': v
355
+ .nativeFn('read-string', function readStringImpl(s: CljValue) {
356
+ if (s === undefined || s.kind !== 'string') {
357
+ throw EvaluationError.atArg(
358
+ `read-string expects a string${s !== undefined ? `, got ${printString(s)}` : ''}`,
359
+ { s },
360
+ 0
361
+ )
362
+ }
363
+ const tokens = tokenize(s.value)
364
+ const forms = readForms(tokens)
365
+ if (forms.length === 0) return v.nil()
366
+ return forms[0]
367
+ })
368
+ .doc(
369
+ 'Reads one object from the string s. Returns nil if string is empty.',
370
+ [['s']]
371
+ ),
372
+
373
+ 'prn-str': v
374
+ .nativeFnCtx('prn-str', function prnStrImpl(ctx: EvaluationContext, _callEnv, ...args: CljValue[]) {
375
+ return withPrintContext(buildPrintContext(ctx), () =>
376
+ v.string(args.map(printString).join(' ') + '\n')
377
+ )
378
+ })
379
+ .doc('pr-str to a string, followed by a newline.', [['&', 'args']]),
380
+
381
+ 'print-str': v
382
+ .nativeFnCtx('print-str', function printStrImpl(ctx: EvaluationContext, _callEnv, ...args: CljValue[]) {
383
+ return withPrintContext(buildPrintContext(ctx), () =>
384
+ v.string(args.map(valueToString).join(' '))
385
+ )
386
+ })
387
+ .doc('print to a string (human-readable, no quotes on strings).', [
388
+ ['&', 'args'],
389
+ ]),
390
+
391
+ 'println-str': v
392
+ .nativeFn('println-str', function printlnStrImpl(...args: CljValue[]) {
393
+ return v.string(args.map(valueToString).join(' ') + '\n')
394
+ })
395
+ .doc('println to a string.', [['&', 'args']]),
396
+
397
+ symbol: v
398
+ .nativeFn('symbol', function symbolImpl(...args: CljValue[]) {
399
+ if (args.length === 0 || args.length > 2) {
400
+ throw new EvaluationError('symbol expects 1 or 2 string arguments', {
401
+ args,
402
+ })
403
+ }
404
+ if (args.length === 1) {
405
+ if (is.symbol(args[0])) return args[0]
406
+ if (args[0].kind !== 'string') {
407
+ throw EvaluationError.atArg(
408
+ `symbol expects a string, got ${printString(args[0])}`,
409
+ { args },
410
+ 0
411
+ )
412
+ }
413
+ return v.symbol(args[0].value)
414
+ }
415
+ if (args[0].kind !== 'string' || args[1].kind !== 'string') {
416
+ throw new EvaluationError('symbol expects string arguments', { args })
417
+ }
418
+ return v.symbol(`${args[0].value}/${args[1].value}`)
419
+ })
420
+ .doc('Returns a Symbol with the given namespace and name.', [
421
+ ['name'],
422
+ ['ns', 'name'],
423
+ ]),
288
424
  }
@@ -1,42 +1,41 @@
1
- import { isAFunction, isVar } from '../assertions'
1
+ import { is } from '../assertions'
2
2
  import { EvaluationError } from '../errors'
3
- import {
4
- cljBoolean,
5
- cljNativeFunction,
6
- cljNativeFunctionWithContext,
7
- withDoc,
8
- } from '../factories'
3
+ import { v } from '../factories'
9
4
  import type { CljValue } from '../types'
10
5
 
11
6
  export const varFunctions: Record<string, CljValue> = {
12
- 'var?': withDoc(
13
- cljNativeFunction('var?', (x: CljValue) => cljBoolean(isVar(x))),
14
- 'Returns true if x is a Var.',
15
- [['x']]
16
- ),
7
+ 'var?': v
8
+ .nativeFn('var?', function isVarImpl(x: CljValue) {
9
+ return v.boolean(is.var(x))
10
+ })
11
+ .doc('Returns true if x is a Var.', [['x']]),
17
12
 
18
- 'var-get': withDoc(
19
- cljNativeFunction('var-get', (x: CljValue) => {
20
- if (!isVar(x)) {
13
+ 'var-get': v
14
+ .nativeFn('var-get', function varGetImpl(x: CljValue) {
15
+ if (!is.var(x)) {
21
16
  throw new EvaluationError(`var-get expects a Var, got ${x.kind}`, { x })
22
17
  }
23
18
  return x.value
24
- }),
25
- 'Returns the value in the Var object.',
26
- [['x']]
27
- ),
19
+ })
20
+ .doc('Returns the value in the Var object.', [['x']]),
28
21
 
29
- 'alter-var-root': withDoc(
30
- cljNativeFunctionWithContext(
22
+ 'alter-var-root': v
23
+ .nativeFnCtx(
31
24
  'alter-var-root',
32
- (ctx, callEnv, varVal: CljValue, f: CljValue, ...args: CljValue[]) => {
33
- if (!isVar(varVal)) {
25
+ function alterVarRootImpl(
26
+ ctx,
27
+ callEnv,
28
+ varVal: CljValue,
29
+ f: CljValue,
30
+ ...args: CljValue[]
31
+ ) {
32
+ if (!is.var(varVal)) {
34
33
  throw new EvaluationError(
35
34
  `alter-var-root expects a Var as its first argument, got ${varVal.kind}`,
36
35
  { varVal }
37
36
  )
38
37
  }
39
- if (!isAFunction(f)) {
38
+ if (!is.aFunction(f)) {
40
39
  throw new EvaluationError(
41
40
  `alter-var-root expects a function as its second argument, got ${f.kind}`,
42
41
  { f }
@@ -46,8 +45,9 @@ export const varFunctions: Record<string, CljValue> = {
46
45
  varVal.value = newVal
47
46
  return newVal
48
47
  }
48
+ )
49
+ .doc(
50
+ 'Atomically alters the root binding of var v by applying f to its current value plus any additional args.',
51
+ [['v', 'f', '&', 'args']]
49
52
  ),
50
- 'Atomically alters the root binding of var v by applying f to its current value plus any additional args.',
51
- [['v', 'f', '&', 'args']]
52
- ),
53
53
  }