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
|
@@ -1,260 +1,349 @@
|
|
|
1
|
+
import { is } from '../assertions'
|
|
1
2
|
import { EvaluationError } from '../errors'
|
|
2
|
-
import {
|
|
3
|
-
import { isEqual } from '../assertions'
|
|
3
|
+
import { v } from '../factories'
|
|
4
4
|
import { printString } from '../printer'
|
|
5
|
-
import
|
|
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
|
-
'+':
|
|
9
|
-
|
|
9
|
+
'+': v
|
|
10
|
+
.nativeFn('+', function add(...nums: CljValue[]) {
|
|
10
11
|
if (nums.length === 0) {
|
|
11
|
-
return
|
|
12
|
+
return v.number(0)
|
|
12
13
|
}
|
|
13
|
-
const badIdx = nums.findIndex((a)
|
|
14
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
15
|
+
return a.kind !== 'number'
|
|
16
|
+
})
|
|
14
17
|
if (badIdx !== -1) {
|
|
15
|
-
throw EvaluationError.atArg(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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)
|
|
39
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
40
|
+
return a.kind !== 'number'
|
|
41
|
+
})
|
|
33
42
|
if (badIdx !== -1) {
|
|
34
|
-
throw EvaluationError.atArg(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
})
|
|
53
|
+
.doc(
|
|
54
|
+
'Returns the difference of the arguments. Throws on non-number arguments.',
|
|
55
|
+
[['&', 'nums']]
|
|
56
|
+
),
|
|
43
57
|
|
|
44
|
-
'*':
|
|
45
|
-
|
|
58
|
+
'*': v
|
|
59
|
+
.nativeFn('*', function multiply(...nums: CljValue[]) {
|
|
46
60
|
if (nums.length === 0) {
|
|
47
|
-
return
|
|
61
|
+
return v.number(1)
|
|
48
62
|
}
|
|
49
|
-
const badIdx = nums.findIndex((a)
|
|
63
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
64
|
+
return a.kind !== 'number'
|
|
65
|
+
})
|
|
50
66
|
if (badIdx !== -1) {
|
|
51
|
-
throw EvaluationError.atArg(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
})
|
|
77
|
+
.doc(
|
|
78
|
+
'Returns the product of the arguments. Throws on non-number arguments.',
|
|
79
|
+
[['&', 'nums']]
|
|
80
|
+
),
|
|
60
81
|
|
|
61
|
-
'/':
|
|
62
|
-
|
|
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)
|
|
89
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
90
|
+
return a.kind !== 'number'
|
|
91
|
+
})
|
|
69
92
|
if (badIdx !== -1) {
|
|
70
|
-
throw EvaluationError.atArg(
|
|
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
|
|
105
|
+
return v.number((acc as CljNumber).value / (arg as CljNumber).value)
|
|
79
106
|
}, nums[0] as CljNumber)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
'>':
|
|
86
|
-
|
|
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)
|
|
120
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
121
|
+
return a.kind !== 'number'
|
|
122
|
+
})
|
|
93
123
|
if (badIdx !== -1) {
|
|
94
|
-
throw EvaluationError.atArg(
|
|
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
|
|
132
|
+
return v.boolean(false)
|
|
99
133
|
}
|
|
100
134
|
}
|
|
101
|
-
return
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
'<':
|
|
108
|
-
|
|
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)
|
|
149
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
150
|
+
return a.kind !== 'number'
|
|
151
|
+
})
|
|
115
152
|
if (badIdx !== -1) {
|
|
116
|
-
throw EvaluationError.atArg(
|
|
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
|
|
161
|
+
return v.boolean(false)
|
|
121
162
|
}
|
|
122
163
|
}
|
|
123
|
-
return
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
'>=':
|
|
130
|
-
|
|
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)
|
|
178
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
179
|
+
return a.kind !== 'number'
|
|
180
|
+
})
|
|
137
181
|
if (badIdx !== -1) {
|
|
138
|
-
throw EvaluationError.atArg(
|
|
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
|
|
190
|
+
return v.boolean(false)
|
|
143
191
|
}
|
|
144
192
|
}
|
|
145
|
-
return
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
'<=':
|
|
152
|
-
|
|
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)
|
|
207
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
208
|
+
return a.kind !== 'number'
|
|
209
|
+
})
|
|
159
210
|
if (badIdx !== -1) {
|
|
160
|
-
throw EvaluationError.atArg(
|
|
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
|
|
219
|
+
return v.boolean(false)
|
|
165
220
|
}
|
|
166
221
|
}
|
|
167
|
-
return
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
'=':
|
|
174
|
-
|
|
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 (!
|
|
182
|
-
return
|
|
237
|
+
if (!is.equal(vals[i], vals[i - 1])) {
|
|
238
|
+
return v.boolean(false)
|
|
183
239
|
}
|
|
184
240
|
}
|
|
185
|
-
return
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
|
|
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:
|
|
192
|
-
|
|
248
|
+
inc: v
|
|
249
|
+
.nativeFn('inc', function increment(x: CljValue) {
|
|
193
250
|
if (x === undefined || x.kind !== 'number') {
|
|
194
|
-
throw EvaluationError.atArg(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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:
|
|
203
|
-
|
|
264
|
+
dec: v
|
|
265
|
+
.nativeFn('dec', function decrement(x: CljValue) {
|
|
204
266
|
if (x === undefined || x.kind !== 'number') {
|
|
205
|
-
throw EvaluationError.atArg(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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:
|
|
214
|
-
|
|
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)
|
|
287
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
288
|
+
return a.kind !== 'number'
|
|
289
|
+
})
|
|
221
290
|
if (badIdx !== -1) {
|
|
222
|
-
throw EvaluationError.atArg(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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:
|
|
233
|
-
|
|
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)
|
|
313
|
+
const badIdx = nums.findIndex(function isNotNumber(a) {
|
|
314
|
+
return a.kind !== 'number'
|
|
315
|
+
})
|
|
240
316
|
if (badIdx !== -1) {
|
|
241
|
-
throw EvaluationError.atArg(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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:
|
|
252
|
-
|
|
332
|
+
mod: v
|
|
333
|
+
.nativeFn('mod', function modulo(n: CljValue, d: CljValue) {
|
|
253
334
|
if (n === undefined || n.kind !== 'number') {
|
|
254
|
-
throw EvaluationError.atArg(
|
|
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(
|
|
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
|
|
355
|
+
return v.number(
|
|
267
356
|
result < 0 ? result + Math.abs((d as CljNumber).value) : result
|
|
268
357
|
)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
|
|
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?':
|
|
275
|
-
|
|
364
|
+
'even?': v
|
|
365
|
+
.nativeFn('even?', function isEven(n: CljValue) {
|
|
276
366
|
if (n === undefined || n.kind !== 'number') {
|
|
277
|
-
throw EvaluationError.atArg(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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?':
|
|
286
|
-
|
|
379
|
+
'odd?': v
|
|
380
|
+
.nativeFn('odd?', function isOdd(n: CljValue) {
|
|
287
381
|
if (n === undefined || n.kind !== 'number') {
|
|
288
|
-
throw EvaluationError.atArg(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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?':
|
|
297
|
-
|
|
394
|
+
'pos?': v
|
|
395
|
+
.nativeFn('pos?', function isPositive(n: CljValue) {
|
|
298
396
|
if (n === undefined || n.kind !== 'number') {
|
|
299
|
-
throw EvaluationError.atArg(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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?':
|
|
308
|
-
|
|
410
|
+
'neg?': v
|
|
411
|
+
.nativeFn('neg?', function isNegative(n: CljValue) {
|
|
309
412
|
if (n === undefined || n.kind !== 'number') {
|
|
310
|
-
throw EvaluationError.atArg(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
319
|
-
|
|
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(`
|
|
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
|
|
324
|
-
})
|
|
325
|
-
'
|
|
326
|
-
|
|
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
|
}
|