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,260 +1,349 @@
1
+ import { is } from '../assertions'
1
2
  import { EvaluationError } from '../errors'
2
- import { cljBoolean, cljNativeFunction, cljNumber, withDoc } from '../factories'
3
- import { isEqual } from '../assertions'
3
+ import { v } from '../factories'
4
4
  import { printString } from '../printer'
5
- import type { CljNumber, CljValue } from '../types'
5
+ import { toSeq } from '../transformations'
6
+ import type { CljList, CljNumber, CljValue, CljVector } from '../types'
6
7
 
7
8
  export const arithmeticFunctions: Record<string, CljValue> = {
8
- '+': withDoc(
9
- cljNativeFunction('+', (...nums: CljValue[]) => {
9
+ '+': v
10
+ .nativeFn('+', function add(...nums: CljValue[]) {
10
11
  if (nums.length === 0) {
11
- return cljNumber(0)
12
+ return v.number(0)
12
13
  }
13
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
14
+ const badIdx = nums.findIndex(function isNotNumber(a) {
15
+ return a.kind !== 'number'
16
+ })
14
17
  if (badIdx !== -1) {
15
- throw EvaluationError.atArg('+ expects all arguments to be numbers', { args: nums }, badIdx)
16
- }
17
- return nums.reduce((acc, arg) => {
18
- return cljNumber((acc as CljNumber).value + (arg as CljNumber).value)
19
- }, cljNumber(0))
20
- }),
21
- 'Returns the sum of the arguments. Throws on non-number arguments.',
22
- [['&', 'nums']]
23
- ),
24
-
25
- '-': withDoc(
26
- cljNativeFunction('-', (...nums: CljValue[]) => {
18
+ throw EvaluationError.atArg(
19
+ '+ expects all arguments to be numbers',
20
+ { args: nums },
21
+ badIdx
22
+ )
23
+ }
24
+ return nums.reduce(function sumNumbers(acc, arg) {
25
+ return v.number((acc as CljNumber).value + (arg as CljNumber).value)
26
+ }, v.number(0))
27
+ })
28
+ .doc('Returns the sum of the arguments. Throws on non-number arguments.', [
29
+ ['&', 'nums'],
30
+ ]),
31
+
32
+ '-': v
33
+ .nativeFn('-', function subtract(...nums: CljValue[]) {
27
34
  if (nums.length === 0) {
28
35
  throw new EvaluationError('- expects at least one argument', {
29
36
  args: nums,
30
37
  })
31
38
  }
32
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
39
+ const badIdx = nums.findIndex(function isNotNumber(a) {
40
+ return a.kind !== 'number'
41
+ })
33
42
  if (badIdx !== -1) {
34
- throw EvaluationError.atArg('- expects all arguments to be numbers', { args: nums }, badIdx)
35
- }
36
- return nums.slice(1).reduce((acc, arg) => {
37
- return cljNumber((acc as CljNumber).value - (arg as CljNumber).value)
43
+ throw EvaluationError.atArg(
44
+ '- expects all arguments to be numbers',
45
+ { args: nums },
46
+ badIdx
47
+ )
48
+ }
49
+ return nums.slice(1).reduce(function subtractNumbers(acc, arg) {
50
+ return v.number((acc as CljNumber).value - (arg as CljNumber).value)
38
51
  }, nums[0] as CljNumber)
39
- }),
40
- 'Returns the difference of the arguments. Throws on non-number arguments.',
41
- [['&', 'nums']]
42
- ),
52
+ })
53
+ .doc(
54
+ 'Returns the difference of the arguments. Throws on non-number arguments.',
55
+ [['&', 'nums']]
56
+ ),
43
57
 
44
- '*': withDoc(
45
- cljNativeFunction('*', (...nums: CljValue[]) => {
58
+ '*': v
59
+ .nativeFn('*', function multiply(...nums: CljValue[]) {
46
60
  if (nums.length === 0) {
47
- return cljNumber(1)
61
+ return v.number(1)
48
62
  }
49
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
63
+ const badIdx = nums.findIndex(function isNotNumber(a) {
64
+ return a.kind !== 'number'
65
+ })
50
66
  if (badIdx !== -1) {
51
- throw EvaluationError.atArg('* expects all arguments to be numbers', { args: nums }, badIdx)
52
- }
53
- return nums.slice(1).reduce((acc, arg) => {
54
- return cljNumber((acc as CljNumber).value * (arg as CljNumber).value)
67
+ throw EvaluationError.atArg(
68
+ '* expects all arguments to be numbers',
69
+ { args: nums },
70
+ badIdx
71
+ )
72
+ }
73
+ return nums.slice(1).reduce(function multiplyNumbers(acc, arg) {
74
+ return v.number((acc as CljNumber).value * (arg as CljNumber).value)
55
75
  }, nums[0] as CljNumber)
56
- }),
57
- 'Returns the product of the arguments. Throws on non-number arguments.',
58
- [['&', 'nums']]
59
- ),
76
+ })
77
+ .doc(
78
+ 'Returns the product of the arguments. Throws on non-number arguments.',
79
+ [['&', 'nums']]
80
+ ),
60
81
 
61
- '/': withDoc(
62
- cljNativeFunction('/', (...nums: CljValue[]) => {
82
+ '/': v
83
+ .nativeFn('/', function divide(...nums: CljValue[]) {
63
84
  if (nums.length === 0) {
64
85
  throw new EvaluationError('/ expects at least one argument', {
65
86
  args: nums,
66
87
  })
67
88
  }
68
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
89
+ const badIdx = nums.findIndex(function isNotNumber(a) {
90
+ return a.kind !== 'number'
91
+ })
69
92
  if (badIdx !== -1) {
70
- throw EvaluationError.atArg('/ expects all arguments to be numbers', { args: nums }, badIdx)
93
+ throw EvaluationError.atArg(
94
+ '/ expects all arguments to be numbers',
95
+ { args: nums },
96
+ badIdx
97
+ )
71
98
  }
72
- return nums.slice(1).reduce((acc, arg, reduceIdx) => {
99
+ return nums.slice(1).reduce(function divideNumbers(acc, arg, reduceIdx) {
73
100
  if ((arg as CljNumber).value === 0) {
74
101
  const err = new EvaluationError('division by zero', { args: nums })
75
102
  err.data = { argIndex: reduceIdx + 1 }
76
103
  throw err
77
104
  }
78
- return cljNumber((acc as CljNumber).value / (arg as CljNumber).value)
105
+ return v.number((acc as CljNumber).value / (arg as CljNumber).value)
79
106
  }, nums[0] as CljNumber)
80
- }),
81
- 'Returns the quotient of the arguments. Throws on non-number arguments or division by zero.',
82
- [['&', 'nums']]
83
- ),
107
+ })
108
+ .doc(
109
+ 'Returns the quotient of the arguments. Throws on non-number arguments or division by zero.',
110
+ [['&', 'nums']]
111
+ ),
84
112
 
85
- '>': withDoc(
86
- cljNativeFunction('>', (...nums: CljValue[]) => {
113
+ '>': v
114
+ .nativeFn('>', function greaterThan(...nums: CljValue[]) {
87
115
  if (nums.length < 2) {
88
116
  throw new EvaluationError('> expects at least two arguments', {
89
117
  args: nums,
90
118
  })
91
119
  }
92
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
120
+ const badIdx = nums.findIndex(function isNotNumber(a) {
121
+ return a.kind !== 'number'
122
+ })
93
123
  if (badIdx !== -1) {
94
- throw EvaluationError.atArg('> expects all arguments to be numbers', { args: nums }, badIdx)
124
+ throw EvaluationError.atArg(
125
+ '> expects all arguments to be numbers',
126
+ { args: nums },
127
+ badIdx
128
+ )
95
129
  }
96
130
  for (let i = 1; i < nums.length; i++) {
97
131
  if ((nums[i] as CljNumber).value >= (nums[i - 1] as CljNumber).value) {
98
- return cljBoolean(false)
132
+ return v.boolean(false)
99
133
  }
100
134
  }
101
- return cljBoolean(true)
102
- }),
103
- 'Compares adjacent arguments left to right, returns true if all values are in ascending order, false otherwise.',
104
- [['&', 'nums']]
105
- ),
135
+ return v.boolean(true)
136
+ })
137
+ .doc(
138
+ 'Compares adjacent arguments left to right, returns true if all values are in ascending order, false otherwise.',
139
+ [['&', 'nums']]
140
+ ),
106
141
 
107
- '<': withDoc(
108
- cljNativeFunction('<', (...nums: CljValue[]) => {
142
+ '<': v
143
+ .nativeFn('<', function lessThan(...nums: CljValue[]) {
109
144
  if (nums.length < 2) {
110
145
  throw new EvaluationError('< expects at least two arguments', {
111
146
  args: nums,
112
147
  })
113
148
  }
114
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
149
+ const badIdx = nums.findIndex(function isNotNumber(a) {
150
+ return a.kind !== 'number'
151
+ })
115
152
  if (badIdx !== -1) {
116
- throw EvaluationError.atArg('< expects all arguments to be numbers', { args: nums }, badIdx)
153
+ throw EvaluationError.atArg(
154
+ '< expects all arguments to be numbers',
155
+ { args: nums },
156
+ badIdx
157
+ )
117
158
  }
118
159
  for (let i = 1; i < nums.length; i++) {
119
160
  if ((nums[i] as CljNumber).value <= (nums[i - 1] as CljNumber).value) {
120
- return cljBoolean(false)
161
+ return v.boolean(false)
121
162
  }
122
163
  }
123
- return cljBoolean(true)
124
- }),
125
- 'Compares adjacent arguments left to right, returns true if all values are in descending order, false otherwise.',
126
- [['&', 'nums']]
127
- ),
164
+ return v.boolean(true)
165
+ })
166
+ .doc(
167
+ 'Compares adjacent arguments left to right, returns true if all values are in descending order, false otherwise.',
168
+ [['&', 'nums']]
169
+ ),
128
170
 
129
- '>=': withDoc(
130
- cljNativeFunction('>=', (...nums: CljValue[]) => {
171
+ '>=': v
172
+ .nativeFn('>=', function greaterThanOrEqual(...nums: CljValue[]) {
131
173
  if (nums.length < 2) {
132
174
  throw new EvaluationError('>= expects at least two arguments', {
133
175
  args: nums,
134
176
  })
135
177
  }
136
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
178
+ const badIdx = nums.findIndex(function isNotNumber(a) {
179
+ return a.kind !== 'number'
180
+ })
137
181
  if (badIdx !== -1) {
138
- throw EvaluationError.atArg('>= expects all arguments to be numbers', { args: nums }, badIdx)
182
+ throw EvaluationError.atArg(
183
+ '>= expects all arguments to be numbers',
184
+ { args: nums },
185
+ badIdx
186
+ )
139
187
  }
140
188
  for (let i = 1; i < nums.length; i++) {
141
189
  if ((nums[i] as CljNumber).value > (nums[i - 1] as CljNumber).value) {
142
- return cljBoolean(false)
190
+ return v.boolean(false)
143
191
  }
144
192
  }
145
- return cljBoolean(true)
146
- }),
147
- 'Compares adjacent arguments left to right, returns true if all comparisons returns true for greater than or equal to checks, false otherwise.',
148
- [['&', 'nums']]
149
- ),
193
+ return v.boolean(true)
194
+ })
195
+ .doc(
196
+ 'Compares adjacent arguments left to right, returns true if all comparisons returns true for greater than or equal to checks, false otherwise.',
197
+ [['&', 'nums']]
198
+ ),
150
199
 
151
- '<=': withDoc(
152
- cljNativeFunction('<=', (...nums: CljValue[]) => {
200
+ '<=': v
201
+ .nativeFn('<=', function lessThanOrEqual(...nums: CljValue[]) {
153
202
  if (nums.length < 2) {
154
203
  throw new EvaluationError('<= expects at least two arguments', {
155
204
  args: nums,
156
205
  })
157
206
  }
158
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
207
+ const badIdx = nums.findIndex(function isNotNumber(a) {
208
+ return a.kind !== 'number'
209
+ })
159
210
  if (badIdx !== -1) {
160
- throw EvaluationError.atArg('<= expects all arguments to be numbers', { args: nums }, badIdx)
211
+ throw EvaluationError.atArg(
212
+ '<= expects all arguments to be numbers',
213
+ { args: nums },
214
+ badIdx
215
+ )
161
216
  }
162
217
  for (let i = 1; i < nums.length; i++) {
163
218
  if ((nums[i] as CljNumber).value < (nums[i - 1] as CljNumber).value) {
164
- return cljBoolean(false)
219
+ return v.boolean(false)
165
220
  }
166
221
  }
167
- return cljBoolean(true)
168
- }),
169
- 'Compares adjacent arguments left to right, returns true if all comparisons returns true for less than or equal to checks, false otherwise.',
170
- [['&', 'nums']]
171
- ),
222
+ return v.boolean(true)
223
+ })
224
+ .doc(
225
+ 'Compares adjacent arguments left to right, returns true if all comparisons returns true for less than or equal to checks, false otherwise.',
226
+ [['&', 'nums']]
227
+ ),
172
228
 
173
- '=': withDoc(
174
- cljNativeFunction('=', (...vals: CljValue[]) => {
229
+ '=': v
230
+ .nativeFn('=', function equals(...vals: CljValue[]) {
175
231
  if (vals.length < 2) {
176
232
  throw new EvaluationError('= expects at least two arguments', {
177
233
  args: vals,
178
234
  })
179
235
  }
180
236
  for (let i = 1; i < vals.length; i++) {
181
- if (!isEqual(vals[i], vals[i - 1])) {
182
- return cljBoolean(false)
237
+ if (!is.equal(vals[i], vals[i - 1])) {
238
+ return v.boolean(false)
183
239
  }
184
240
  }
185
- return cljBoolean(true)
186
- }),
187
- 'Compares adjacent arguments left to right, returns true if all values are structurally equal, false otherwise.',
188
- [['&', 'vals']]
189
- ),
241
+ return v.boolean(true)
242
+ })
243
+ .doc(
244
+ 'Compares adjacent arguments left to right, returns true if all values are structurally equal, false otherwise.',
245
+ [['&', 'vals']]
246
+ ),
190
247
 
191
- inc: withDoc(
192
- cljNativeFunction('inc', (x: CljValue) => {
248
+ inc: v
249
+ .nativeFn('inc', function increment(x: CljValue) {
193
250
  if (x === undefined || x.kind !== 'number') {
194
- throw EvaluationError.atArg(`inc expects a number${x !== undefined ? `, got ${printString(x)}` : ''}`, { x }, 0)
195
- }
196
- return cljNumber((x as CljNumber).value + 1)
197
- }),
198
- 'Returns the argument incremented by 1. Throws on non-number arguments.',
199
- [['x']]
200
- ),
251
+ throw EvaluationError.atArg(
252
+ `inc expects a number${x !== undefined ? `, got ${printString(x)}` : ''}`,
253
+ { x },
254
+ 0
255
+ )
256
+ }
257
+ return v.number((x as CljNumber).value + 1)
258
+ })
259
+ .doc(
260
+ 'Returns the argument incremented by 1. Throws on non-number arguments.',
261
+ [['x']]
262
+ ),
201
263
 
202
- dec: withDoc(
203
- cljNativeFunction('dec', (x: CljValue) => {
264
+ dec: v
265
+ .nativeFn('dec', function decrement(x: CljValue) {
204
266
  if (x === undefined || x.kind !== 'number') {
205
- throw EvaluationError.atArg(`dec expects a number${x !== undefined ? `, got ${printString(x)}` : ''}`, { x }, 0)
206
- }
207
- return cljNumber((x as CljNumber).value - 1)
208
- }),
209
- 'Returns the argument decremented by 1. Throws on non-number arguments.',
210
- [['x']]
211
- ),
267
+ throw EvaluationError.atArg(
268
+ `dec expects a number${x !== undefined ? `, got ${printString(x)}` : ''}`,
269
+ { x },
270
+ 0
271
+ )
272
+ }
273
+ return v.number((x as CljNumber).value - 1)
274
+ })
275
+ .doc(
276
+ 'Returns the argument decremented by 1. Throws on non-number arguments.',
277
+ [['x']]
278
+ ),
212
279
 
213
- max: withDoc(
214
- cljNativeFunction('max', (...nums: CljValue[]) => {
280
+ max: v
281
+ .nativeFn('max', function maximum(...nums: CljValue[]) {
215
282
  if (nums.length === 0) {
216
283
  throw new EvaluationError('max expects at least one argument', {
217
284
  args: nums,
218
285
  })
219
286
  }
220
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
287
+ const badIdx = nums.findIndex(function isNotNumber(a) {
288
+ return a.kind !== 'number'
289
+ })
221
290
  if (badIdx !== -1) {
222
- throw EvaluationError.atArg('max expects all arguments to be numbers', { args: nums }, badIdx)
223
- }
224
- return nums.reduce((best, arg) =>
225
- (arg as CljNumber).value > (best as CljNumber).value ? arg : best
226
- )
227
- }),
228
- 'Returns the largest of the arguments. Throws on non-number arguments.',
229
- [['&', 'nums']]
230
- ),
291
+ throw EvaluationError.atArg(
292
+ 'max expects all arguments to be numbers',
293
+ { args: nums },
294
+ badIdx
295
+ )
296
+ }
297
+ return nums.reduce(function findMax(best, arg) {
298
+ return (arg as CljNumber).value > (best as CljNumber).value ? arg : best
299
+ })
300
+ })
301
+ .doc(
302
+ 'Returns the largest of the arguments. Throws on non-number arguments.',
303
+ [['&', 'nums']]
304
+ ),
231
305
 
232
- min: withDoc(
233
- cljNativeFunction('min', (...nums: CljValue[]) => {
306
+ min: v
307
+ .nativeFn('min', function minimum(...nums: CljValue[]) {
234
308
  if (nums.length === 0) {
235
309
  throw new EvaluationError('min expects at least one argument', {
236
310
  args: nums,
237
311
  })
238
312
  }
239
- const badIdx = nums.findIndex((a) => a.kind !== 'number')
313
+ const badIdx = nums.findIndex(function isNotNumber(a) {
314
+ return a.kind !== 'number'
315
+ })
240
316
  if (badIdx !== -1) {
241
- throw EvaluationError.atArg('min expects all arguments to be numbers', { args: nums }, badIdx)
242
- }
243
- return nums.reduce((best, arg) =>
244
- (arg as CljNumber).value < (best as CljNumber).value ? arg : best
245
- )
246
- }),
247
- 'Returns the smallest of the arguments. Throws on non-number arguments.',
248
- [['&', 'nums']]
249
- ),
317
+ throw EvaluationError.atArg(
318
+ 'min expects all arguments to be numbers',
319
+ { args: nums },
320
+ badIdx
321
+ )
322
+ }
323
+ return nums.reduce(function findMin(best, arg) {
324
+ return (arg as CljNumber).value < (best as CljNumber).value ? arg : best
325
+ })
326
+ })
327
+ .doc(
328
+ 'Returns the smallest of the arguments. Throws on non-number arguments.',
329
+ [['&', 'nums']]
330
+ ),
250
331
 
251
- mod: withDoc(
252
- cljNativeFunction('mod', (n: CljValue, d: CljValue) => {
332
+ mod: v
333
+ .nativeFn('mod', function modulo(n: CljValue, d: CljValue) {
253
334
  if (n === undefined || n.kind !== 'number') {
254
- throw EvaluationError.atArg(`mod expects a number as first argument${n !== undefined ? `, got ${printString(n)}` : ''}`, { n }, 0)
335
+ throw EvaluationError.atArg(
336
+ `mod expects a number as first argument${n !== undefined ? `, got ${printString(n)}` : ''}`,
337
+ { n },
338
+ 0
339
+ )
255
340
  }
256
341
  if (d === undefined || d.kind !== 'number') {
257
- throw EvaluationError.atArg(`mod expects a number as second argument${d !== undefined ? `, got ${printString(d)}` : ''}`, { d }, 1)
342
+ throw EvaluationError.atArg(
343
+ `mod expects a number as second argument${d !== undefined ? `, got ${printString(d)}` : ''}`,
344
+ { d },
345
+ 1
346
+ )
258
347
  }
259
348
  if ((d as CljNumber).value === 0) {
260
349
  const err = new EvaluationError('mod: division by zero', { n, d })
@@ -263,66 +352,311 @@ export const arithmeticFunctions: Record<string, CljValue> = {
263
352
  }
264
353
  // Clojure mod always returns non-negative when divisor is positive
265
354
  const result = (n as CljNumber).value % (d as CljNumber).value
266
- return cljNumber(
355
+ return v.number(
267
356
  result < 0 ? result + Math.abs((d as CljNumber).value) : result
268
357
  )
269
- }),
270
- 'Returns the remainder of the first argument divided by the second argument. Throws on non-number arguments or division by zero.',
271
- [['n', 'd']]
272
- ),
358
+ })
359
+ .doc(
360
+ 'Returns the remainder of the first argument divided by the second argument. Throws on non-number arguments or division by zero.',
361
+ [['n', 'd']]
362
+ ),
273
363
 
274
- 'even?': withDoc(
275
- cljNativeFunction('even?', (n: CljValue) => {
364
+ 'even?': v
365
+ .nativeFn('even?', function isEven(n: CljValue) {
276
366
  if (n === undefined || n.kind !== 'number') {
277
- throw EvaluationError.atArg(`even? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`, { n }, 0)
278
- }
279
- return cljBoolean((n as CljNumber).value % 2 === 0)
280
- }),
281
- 'Returns true if the argument is an even number, false otherwise.',
282
- [['n']]
283
- ),
367
+ throw EvaluationError.atArg(
368
+ `even? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`,
369
+ { n },
370
+ 0
371
+ )
372
+ }
373
+ return v.boolean((n as CljNumber).value % 2 === 0)
374
+ })
375
+ .doc('Returns true if the argument is an even number, false otherwise.', [
376
+ ['n'],
377
+ ]),
284
378
 
285
- 'odd?': withDoc(
286
- cljNativeFunction('odd?', (n: CljValue) => {
379
+ 'odd?': v
380
+ .nativeFn('odd?', function isOdd(n: CljValue) {
287
381
  if (n === undefined || n.kind !== 'number') {
288
- throw EvaluationError.atArg(`odd? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`, { n }, 0)
289
- }
290
- return cljBoolean(Math.abs((n as CljNumber).value) % 2 !== 0)
291
- }),
292
- 'Returns true if the argument is an odd number, false otherwise.',
293
- [['n']]
294
- ),
382
+ throw EvaluationError.atArg(
383
+ `odd? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`,
384
+ { n },
385
+ 0
386
+ )
387
+ }
388
+ return v.boolean(Math.abs((n as CljNumber).value) % 2 !== 0)
389
+ })
390
+ .doc('Returns true if the argument is an odd number, false otherwise.', [
391
+ ['n'],
392
+ ]),
295
393
 
296
- 'pos?': withDoc(
297
- cljNativeFunction('pos?', (n: CljValue) => {
394
+ 'pos?': v
395
+ .nativeFn('pos?', function isPositive(n: CljValue) {
298
396
  if (n === undefined || n.kind !== 'number') {
299
- throw EvaluationError.atArg(`pos? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`, { n }, 0)
300
- }
301
- return cljBoolean((n as CljNumber).value > 0)
302
- }),
303
- 'Returns true if the argument is a positive number, false otherwise.',
304
- [['n']]
305
- ),
397
+ throw EvaluationError.atArg(
398
+ `pos? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`,
399
+ { n },
400
+ 0
401
+ )
402
+ }
403
+ return v.boolean((n as CljNumber).value > 0)
404
+ })
405
+ .doc(
406
+ 'Returns true if the argument is a positive number, false otherwise.',
407
+ [['n']]
408
+ ),
306
409
 
307
- 'neg?': withDoc(
308
- cljNativeFunction('neg?', (n: CljValue) => {
410
+ 'neg?': v
411
+ .nativeFn('neg?', function isNegative(n: CljValue) {
309
412
  if (n === undefined || n.kind !== 'number') {
310
- throw EvaluationError.atArg(`neg? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`, { n }, 0)
311
- }
312
- return cljBoolean((n as CljNumber).value < 0)
313
- }),
314
- 'Returns true if the argument is a negative number, false otherwise.',
315
- [['n']]
316
- ),
413
+ throw EvaluationError.atArg(
414
+ `neg? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`,
415
+ { n },
416
+ 0
417
+ )
418
+ }
419
+ return v.boolean((n as CljNumber).value < 0)
420
+ })
421
+ .doc(
422
+ 'Returns true if the argument is a negative number, false otherwise.',
423
+ [['n']]
424
+ ),
425
+
426
+ 'zero?': v
427
+ .nativeFn('zero?', function isZero(n: CljValue) {
428
+ if (n === undefined || n.kind !== 'number') {
429
+ throw EvaluationError.atArg(
430
+ `zero? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`,
431
+ { n },
432
+ 0
433
+ )
434
+ }
435
+ return v.boolean((n as CljNumber).value === 0)
436
+ })
437
+ .doc('Returns true if the argument is zero, false otherwise.', [['n']]),
438
+
439
+ abs: v
440
+ .nativeFn('abs', function absImpl(n: CljValue) {
441
+ if (n === undefined || n.kind !== 'number') {
442
+ throw EvaluationError.atArg(
443
+ `abs expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`,
444
+ { n },
445
+ 0
446
+ )
447
+ }
448
+ return v.number(Math.abs((n as CljNumber).value))
449
+ })
450
+ .doc('Returns the absolute value of a.', [['a']]),
451
+
452
+ quot: v
453
+ .nativeFn('quot', function quotImpl(num: CljValue, div: CljValue) {
454
+ if (num === undefined || num.kind !== 'number') {
455
+ throw EvaluationError.atArg(
456
+ `quot expects a number as first argument`,
457
+ { num },
458
+ 0
459
+ )
460
+ }
461
+ if (div === undefined || div.kind !== 'number') {
462
+ throw EvaluationError.atArg(
463
+ `quot expects a number as second argument`,
464
+ { div },
465
+ 1
466
+ )
467
+ }
468
+ if ((div as CljNumber).value === 0) {
469
+ throw new EvaluationError('quot: division by zero', { num, div })
470
+ }
471
+ return v.number(
472
+ Math.trunc((num as CljNumber).value / (div as CljNumber).value)
473
+ )
474
+ })
475
+ .doc('quot[ient] of dividing numerator by denominator.', [['num', 'div']]),
476
+
477
+ rem: v
478
+ .nativeFn('rem', function remImpl(num: CljValue, div: CljValue) {
479
+ if (num === undefined || num.kind !== 'number') {
480
+ throw EvaluationError.atArg(
481
+ `rem expects a number as first argument`,
482
+ { num },
483
+ 0
484
+ )
485
+ }
486
+ if (div === undefined || div.kind !== 'number') {
487
+ throw EvaluationError.atArg(
488
+ `rem expects a number as second argument`,
489
+ { div },
490
+ 1
491
+ )
492
+ }
493
+ if ((div as CljNumber).value === 0) {
494
+ throw new EvaluationError('rem: division by zero', { num, div })
495
+ }
496
+ return v.number((num as CljNumber).value % (div as CljNumber).value)
497
+ })
498
+ .doc('remainder of dividing numerator by denominator.', [['num', 'div']]),
317
499
 
318
- 'zero?': withDoc(
319
- cljNativeFunction('zero?', (n: CljValue) => {
500
+ rand: v
501
+ .nativeFn('rand', function randImpl(...args: CljValue[]) {
502
+ if (args.length === 0) return v.number(Math.random())
503
+ if (args[0].kind !== 'number') {
504
+ throw EvaluationError.atArg(`rand expects a number`, { n: args[0] }, 0)
505
+ }
506
+ return v.number(Math.random() * (args[0] as CljNumber).value)
507
+ })
508
+ .doc(
509
+ 'Returns a random floating point number between 0 (inclusive) and n (default 1) (exclusive).',
510
+ [[], ['n']]
511
+ ),
512
+
513
+ 'rand-int': v
514
+ .nativeFn('rand-int', function randIntImpl(n: CljValue) {
320
515
  if (n === undefined || n.kind !== 'number') {
321
- throw EvaluationError.atArg(`zero? expects a number${n !== undefined ? `, got ${printString(n)}` : ''}`, { n }, 0)
516
+ throw EvaluationError.atArg(`rand-int expects a number`, { n }, 0)
517
+ }
518
+ return v.number(Math.floor(Math.random() * (n as CljNumber).value))
519
+ })
520
+ .doc('Returns a random integer between 0 (inclusive) and n (exclusive).', [
521
+ ['n'],
522
+ ]),
523
+
524
+ 'rand-nth': v
525
+ .nativeFn('rand-nth', function randNthImpl(coll: CljValue) {
526
+ if (coll === undefined || (!is.list(coll) && !is.vector(coll))) {
527
+ throw EvaluationError.atArg(
528
+ `rand-nth expects a list or vector`,
529
+ { coll },
530
+ 0
531
+ )
532
+ }
533
+ const items = (coll as CljList | CljVector).value
534
+ if (items.length === 0) {
535
+ throw new EvaluationError('rand-nth called on empty collection', {
536
+ coll,
537
+ })
322
538
  }
323
- return cljBoolean((n as CljNumber).value === 0)
324
- }),
325
- 'Returns true if the argument is zero, false otherwise.',
326
- [['n']]
327
- ),
539
+ return items[Math.floor(Math.random() * items.length)]
540
+ })
541
+ .doc('Return a random element of the (sequential) collection.', [['coll']]),
542
+
543
+ shuffle: v
544
+ .nativeFn('shuffle', function shuffleImpl(coll: CljValue) {
545
+ if (coll === undefined || coll.kind === 'nil') return v.vector([])
546
+ if (!is.seqable(coll)) {
547
+ throw EvaluationError.atArg(
548
+ `shuffle expects a collection, got ${printString(coll)}`,
549
+ { coll },
550
+ 0
551
+ )
552
+ }
553
+ const arr = [...toSeq(coll)]
554
+ for (let i = arr.length - 1; i > 0; i--) {
555
+ const j = Math.floor(Math.random() * (i + 1))
556
+ ;[arr[i], arr[j]] = [arr[j], arr[i]]
557
+ }
558
+ return v.vector(arr)
559
+ })
560
+ .doc('Return a random permutation of coll.', [['coll']]),
561
+
562
+ 'bit-and': v
563
+ .nativeFn('bit-and', function bitAndImpl(x: CljValue, y: CljValue) {
564
+ if (x?.kind !== 'number')
565
+ throw EvaluationError.atArg('bit-and expects numbers', { x }, 0)
566
+ if (y?.kind !== 'number')
567
+ throw EvaluationError.atArg('bit-and expects numbers', { y }, 1)
568
+ return v.number((x as CljNumber).value & (y as CljNumber).value)
569
+ })
570
+ .doc('Bitwise and', [['x', 'y']]),
571
+
572
+ 'bit-or': v
573
+ .nativeFn('bit-or', function bitOrImpl(x: CljValue, y: CljValue) {
574
+ if (x?.kind !== 'number')
575
+ throw EvaluationError.atArg('bit-or expects numbers', { x }, 0)
576
+ if (y?.kind !== 'number')
577
+ throw EvaluationError.atArg('bit-or expects numbers', { y }, 1)
578
+ return v.number((x as CljNumber).value | (y as CljNumber).value)
579
+ })
580
+ .doc('Bitwise or', [['x', 'y']]),
581
+
582
+ 'bit-xor': v
583
+ .nativeFn('bit-xor', function bitXorImpl(x: CljValue, y: CljValue) {
584
+ if (x?.kind !== 'number')
585
+ throw EvaluationError.atArg('bit-xor expects numbers', { x }, 0)
586
+ if (y?.kind !== 'number')
587
+ throw EvaluationError.atArg('bit-xor expects numbers', { y }, 1)
588
+ return v.number((x as CljNumber).value ^ (y as CljNumber).value)
589
+ })
590
+ .doc('Bitwise exclusive or', [['x', 'y']]),
591
+
592
+ 'bit-not': v
593
+ .nativeFn('bit-not', function bitNotImpl(x: CljValue) {
594
+ if (x?.kind !== 'number')
595
+ throw EvaluationError.atArg('bit-not expects a number', { x }, 0)
596
+ return v.number(~(x as CljNumber).value)
597
+ })
598
+ .doc('Bitwise complement', [['x']]),
599
+
600
+ 'bit-shift-left': v
601
+ .nativeFn(
602
+ 'bit-shift-left',
603
+ function bitShiftLeftImpl(x: CljValue, n: CljValue) {
604
+ if (x?.kind !== 'number')
605
+ throw EvaluationError.atArg(
606
+ 'bit-shift-left expects numbers',
607
+ { x },
608
+ 0
609
+ )
610
+ if (n?.kind !== 'number')
611
+ throw EvaluationError.atArg(
612
+ 'bit-shift-left expects numbers',
613
+ { n },
614
+ 1
615
+ )
616
+ return v.number((x as CljNumber).value << (n as CljNumber).value)
617
+ }
618
+ )
619
+ .doc('Bitwise shift left', [['x', 'n']]),
620
+
621
+ 'bit-shift-right': v
622
+ .nativeFn(
623
+ 'bit-shift-right',
624
+ function bitShiftRightImpl(x: CljValue, n: CljValue) {
625
+ if (x?.kind !== 'number')
626
+ throw EvaluationError.atArg(
627
+ 'bit-shift-right expects numbers',
628
+ { x },
629
+ 0
630
+ )
631
+ if (n?.kind !== 'number')
632
+ throw EvaluationError.atArg(
633
+ 'bit-shift-right expects numbers',
634
+ { n },
635
+ 1
636
+ )
637
+ return v.number((x as CljNumber).value >> (n as CljNumber).value)
638
+ }
639
+ )
640
+ .doc('Bitwise shift right', [['x', 'n']]),
641
+
642
+ 'unsigned-bit-shift-right': v
643
+ .nativeFn(
644
+ 'unsigned-bit-shift-right',
645
+ function unsignedBitShiftRightImpl(x: CljValue, n: CljValue) {
646
+ if (x?.kind !== 'number')
647
+ throw EvaluationError.atArg(
648
+ 'unsigned-bit-shift-right expects numbers',
649
+ { x },
650
+ 0
651
+ )
652
+ if (n?.kind !== 'number')
653
+ throw EvaluationError.atArg(
654
+ 'unsigned-bit-shift-right expects numbers',
655
+ { n },
656
+ 1
657
+ )
658
+ return v.number((x as CljNumber).value >>> (n as CljNumber).value)
659
+ }
660
+ )
661
+ .doc('Bitwise shift right, without sign-extension', [['x', 'n']]),
328
662
  }