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,228 +1,366 @@
1
1
  // Predicates & logical: nil?, true?, false?, truthy?, falsy?, not, not=,
2
2
  // number?, string?, boolean?, vector?, list?, map?, keyword?, symbol?, fn?,
3
3
  // coll?, some, every?
4
- import {
5
- isAFunction,
6
- isCollection,
7
- isEqual,
8
- isFalsy,
9
- isKeyword,
10
- isSeqable,
11
- isSymbol,
12
- isTruthy,
13
- isList,
14
- isVector,
15
- isMap,
16
- } from '../assertions'
17
- import { applyFunction } from '../evaluator'
4
+ import { is } from '../assertions'
18
5
  import { EvaluationError } from '../errors'
19
- import { cljBoolean, cljNativeFunction, cljNil, withDoc } from '../factories'
6
+ import { v } from '../factories'
20
7
  import { printString } from '../printer'
21
8
  import { toSeq } from '../transformations'
22
- import type { CljValue } from '../types'
9
+ import type { CljNumber, CljValue, Env, EvaluationContext } from '../types'
23
10
 
24
11
  export const predicateFunctions: Record<string, CljValue> = {
25
- 'nil?': withDoc(
26
- cljNativeFunction('nil?', (arg: CljValue) => {
27
- return cljBoolean(arg.kind === 'nil')
28
- }),
29
- 'Returns true if the value is nil, false otherwise.',
30
- [['arg']]
31
- ),
32
- 'true?': withDoc(
33
- cljNativeFunction('true?', (arg: CljValue) => {
12
+ 'nil?': v
13
+ .nativeFn('nil?', function nilPredImpl(arg: CljValue) {
14
+ return v.boolean(arg.kind === 'nil')
15
+ })
16
+ .doc('Returns true if the value is nil, false otherwise.', [['arg']]),
17
+ 'true?': v
18
+ .nativeFn('true?', function truePredImpl(arg: CljValue) {
34
19
  // returns true if the value is a boolean and true
35
20
  if (arg.kind !== 'boolean') {
36
- return cljBoolean(false)
21
+ return v.boolean(false)
37
22
  }
38
- return cljBoolean(arg.value === true)
39
- }),
40
- 'Returns true if the value is a boolean and true, false otherwise.',
41
- [['arg']]
42
- ),
43
- 'false?': withDoc(
44
- cljNativeFunction('false?', (arg: CljValue) => {
23
+ return v.boolean(arg.value === true)
24
+ })
25
+ .doc('Returns true if the value is a boolean and true, false otherwise.', [
26
+ ['arg'],
27
+ ]),
28
+ 'false?': v
29
+ .nativeFn('false?', function falsePredImpl(arg: CljValue) {
45
30
  // returns true if the value is a boolean and false
46
31
  if (arg.kind !== 'boolean') {
47
- return cljBoolean(false)
32
+ return v.boolean(false)
48
33
  }
49
- return cljBoolean(arg.value === false)
50
- }),
51
- 'Returns true if the value is a boolean and false, false otherwise.',
52
- [['arg']]
53
- ),
54
- 'truthy?': withDoc(
55
- cljNativeFunction('truthy?', (arg: CljValue) => {
56
- return cljBoolean(isTruthy(arg))
57
- }),
58
- 'Returns true if the value is not nil or false, false otherwise.',
59
- [['arg']]
60
- ),
61
- 'falsy?': withDoc(
62
- cljNativeFunction('falsy?', (arg: CljValue) => {
63
- return cljBoolean(isFalsy(arg))
64
- }),
65
- 'Returns true if the value is nil or false, false otherwise.',
66
- [['arg']]
67
- ),
68
- // not: withDoc(
69
- // cljNativeFunction('not', (arg: CljValue) => {
70
- // return cljBoolean(!isTruthy(arg))
71
- // }),
72
- // 'Returns the negation of the truthiness of the value.',
73
- // [['arg']]
74
- // ),
75
- 'not=': withDoc(
76
- cljNativeFunction('not=', (...vals: CljValue[]) => {
34
+ return v.boolean(arg.value === false)
35
+ })
36
+ .doc('Returns true if the value is a boolean and false, false otherwise.', [
37
+ ['arg'],
38
+ ]),
39
+ 'truthy?': v
40
+ .nativeFn('truthy?', function truthyPredImpl(arg: CljValue) {
41
+ return v.boolean(is.truthy(arg))
42
+ })
43
+ .doc('Returns true if the value is not nil or false, false otherwise.', [
44
+ ['arg'],
45
+ ]),
46
+ 'falsy?': v
47
+ .nativeFn('falsy?', function falsyPredImpl(arg: CljValue) {
48
+ return v.boolean(is.falsy(arg))
49
+ })
50
+ .doc('Returns true if the value is nil or false, false otherwise.', [
51
+ ['arg'],
52
+ ]),
53
+ 'not=': v
54
+ .nativeFn('not=', function notEqualImpl(...vals: CljValue[]) {
77
55
  if (vals.length < 2) {
78
56
  throw new EvaluationError('not= expects at least two arguments', {
79
57
  args: vals,
80
58
  })
81
59
  }
82
60
  for (let i = 1; i < vals.length; i++) {
83
- if (!isEqual(vals[i], vals[i - 1])) {
84
- return cljBoolean(true)
61
+ if (!is.equal(vals[i], vals[i - 1])) {
62
+ return v.boolean(true)
85
63
  }
86
64
  }
87
- return cljBoolean(false)
88
- }),
89
- 'Returns true if any two adjacent arguments are not equal, false otherwise.',
90
- [['&', 'vals']]
91
- ),
92
- 'number?': withDoc(
93
- cljNativeFunction('number?', (x: CljValue) =>
94
- cljBoolean(x !== undefined && x.kind === 'number')
65
+ return v.boolean(false)
66
+ })
67
+ .doc(
68
+ 'Returns true if any two adjacent arguments are not equal, false otherwise.',
69
+ [['&', 'vals']]
95
70
  ),
96
- 'Returns true if the value is a number, false otherwise.',
97
- [['x']]
98
- ),
71
+ 'number?': v
72
+ .nativeFn('number?', function numberPredImpl(x: CljValue) {
73
+ return v.boolean(x !== undefined && x.kind === 'number')
74
+ })
75
+ .doc('Returns true if the value is a number, false otherwise.', [['x']]),
99
76
 
100
- 'string?': withDoc(
101
- cljNativeFunction('string?', (x: CljValue) =>
102
- cljBoolean(x !== undefined && x.kind === 'string')
103
- ),
104
- 'Returns true if the value is a string, false otherwise.',
105
- [['x']]
106
- ),
77
+ 'string?': v
78
+ .nativeFn('string?', function stringPredImpl(x: CljValue) {
79
+ return v.boolean(x !== undefined && is.string(x))
80
+ })
81
+ .doc('Returns true if the value is a string, false otherwise.', [['x']]),
107
82
 
108
- 'boolean?': withDoc(
109
- cljNativeFunction('boolean?', (x: CljValue) =>
110
- cljBoolean(x !== undefined && x.kind === 'boolean')
111
- ),
112
- 'Returns true if the value is a boolean, false otherwise.',
113
- [['x']]
114
- ),
83
+ 'boolean?': v
84
+ .nativeFn('boolean?', function booleanPredImpl(x: CljValue) {
85
+ return v.boolean(x !== undefined && x.kind === 'boolean')
86
+ })
87
+ .doc('Returns true if the value is a boolean, false otherwise.', [['x']]),
115
88
 
116
- 'vector?': withDoc(
117
- cljNativeFunction('vector?', (x: CljValue) =>
118
- cljBoolean(x !== undefined && isVector(x))
119
- ),
120
- 'Returns true if the value is a vector, false otherwise.',
121
- [['x']]
122
- ),
123
-
124
- 'list?': withDoc(
125
- cljNativeFunction('list?', (x: CljValue) =>
126
- cljBoolean(x !== undefined && isList(x))
127
- ),
128
- 'Returns true if the value is a list, false otherwise.',
129
- [['x']]
130
- ),
89
+ 'vector?': v
90
+ .nativeFn('vector?', function vectorPredImpl(x: CljValue) {
91
+ return v.boolean(x !== undefined && is.vector(x))
92
+ })
93
+ .doc('Returns true if the value is a vector, false otherwise.', [['x']]),
131
94
 
132
- 'map?': withDoc(
133
- cljNativeFunction('map?', (x: CljValue) =>
134
- cljBoolean(x !== undefined && isMap(x))
135
- ),
136
- 'Returns true if the value is a map, false otherwise.',
137
- [['x']]
138
- ),
95
+ 'list?': v
96
+ .nativeFn('list?', function listPredImpl(x: CljValue) {
97
+ return v.boolean(x !== undefined && is.list(x))
98
+ })
99
+ .doc('Returns true if the value is a list, false otherwise.', [['x']]),
139
100
 
140
- 'keyword?': withDoc(
141
- cljNativeFunction('keyword?', (x: CljValue) =>
142
- cljBoolean(x !== undefined && isKeyword(x))
143
- ),
144
- 'Returns true if the value is a keyword, false otherwise.',
145
- [['x']]
146
- ),
101
+ 'map?': v
102
+ .nativeFn('map?', function mapPredImpl(x: CljValue) {
103
+ return v.boolean(x !== undefined && is.map(x))
104
+ })
105
+ .doc('Returns true if the value is a map, false otherwise.', [['x']]),
147
106
 
148
- 'qualified-keyword?': withDoc(
149
- cljNativeFunction('qualified-keyword?', (x: CljValue) =>
150
- cljBoolean(x !== undefined && isKeyword(x) && x.name.includes('/'))
151
- ),
152
- 'Returns true if the value is a qualified keyword, false otherwise.',
153
- [['x']]
154
- ),
107
+ 'keyword?': v
108
+ .nativeFn('keyword?', function keywordPredImpl(x: CljValue) {
109
+ return v.boolean(x !== undefined && is.keyword(x))
110
+ })
111
+ .doc('Returns true if the value is a keyword, false otherwise.', [['x']]),
155
112
 
156
- 'symbol?': withDoc(
157
- cljNativeFunction('symbol?', (x: CljValue) =>
158
- cljBoolean(x !== undefined && isSymbol(x))
159
- ),
160
- 'Returns true if the value is a symbol, false otherwise.',
161
- [['x']]
162
- ),
113
+ 'qualified-keyword?': v
114
+ .nativeFn(
115
+ 'qualified-keyword?',
116
+ function qualifiedKeywordPredImpl(x: CljValue) {
117
+ return v.boolean(
118
+ x !== undefined && is.keyword(x) && x.name.includes('/')
119
+ )
120
+ }
121
+ )
122
+ .doc('Returns true if the value is a qualified keyword, false otherwise.', [
123
+ ['x'],
124
+ ]),
163
125
 
164
- 'qualified-symbol?': withDoc(
165
- cljNativeFunction('qualified-symbol?', (x: CljValue) =>
166
- cljBoolean(x !== undefined && isSymbol(x) && x.name.includes('/'))
167
- ),
168
- 'Returns true if the value is a qualified symbol, false otherwise.',
169
- [['x']]
170
- ),
126
+ 'symbol?': v
127
+ .nativeFn('symbol?', function symbolPredImpl(x: CljValue) {
128
+ return v.boolean(x !== undefined && is.symbol(x))
129
+ })
130
+ .doc('Returns true if the value is a symbol, false otherwise.', [['x']]),
171
131
 
172
- 'fn?': withDoc(
173
- cljNativeFunction('fn?', (x: CljValue) =>
174
- cljBoolean(x !== undefined && isAFunction(x))
175
- ),
176
- 'Returns true if the value is a function, false otherwise.',
177
- [['x']]
178
- ),
132
+ 'namespace?': v
133
+ .nativeFn('namespace?', function namespaceQImpl(x: CljValue) {
134
+ return v.boolean(x !== undefined && x.kind === 'namespace')
135
+ })
136
+ .doc('Returns true if x is a namespace.', [['x']]),
179
137
 
180
- 'coll?': withDoc(
181
- cljNativeFunction('coll?', (x: CljValue) =>
182
- cljBoolean(x !== undefined && isCollection(x))
183
- ),
184
- 'Returns true if the value is a collection, false otherwise.',
185
- [['x']]
186
- ),
187
- some: withDoc(
188
- cljNativeFunction('some', (pred: CljValue, coll: CljValue): CljValue => {
189
- if (pred === undefined || !isAFunction(pred)) {
190
- throw EvaluationError.atArg(`some expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ''}`, { pred }, 0)
191
- }
192
- if (coll === undefined) {
193
- return cljNil()
194
- }
195
- if (!isSeqable(coll)) {
196
- throw EvaluationError.atArg(`some expects a collection or string as second argument, got ${printString(coll)}`, { coll }, 1)
138
+ 'qualified-symbol?': v
139
+ .nativeFn(
140
+ 'qualified-symbol?',
141
+ function qualifiedSymbolPredImpl(x: CljValue) {
142
+ return v.boolean(
143
+ x !== undefined && is.symbol(x) && x.name.includes('/')
144
+ )
197
145
  }
198
- for (const item of toSeq(coll)) {
199
- const result = applyFunction(pred, [item])
200
- if (isTruthy(result)) {
201
- return result
146
+ )
147
+ .doc('Returns true if the value is a qualified symbol, false otherwise.', [
148
+ ['x'],
149
+ ]),
150
+
151
+ 'fn?': v
152
+ .nativeFn('fn?', function fnPredImpl(x: CljValue) {
153
+ return v.boolean(x !== undefined && is.aFunction(x))
154
+ })
155
+ .doc('Returns true if the value is a function, false otherwise.', [['x']]),
156
+
157
+ 'coll?': v
158
+ .nativeFn('coll?', function collPredImpl(x: CljValue) {
159
+ return v.boolean(x !== undefined && is.collection(x))
160
+ })
161
+ .doc('Returns true if the value is a collection, false otherwise.', [
162
+ ['x'],
163
+ ]),
164
+ some: v
165
+ .nativeFnCtx(
166
+ 'some',
167
+ function someImpl(
168
+ ctx: EvaluationContext,
169
+ callEnv: Env,
170
+ pred: CljValue,
171
+ coll: CljValue
172
+ ): CljValue {
173
+ if (pred === undefined || !is.aFunction(pred)) {
174
+ throw EvaluationError.atArg(
175
+ `some expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ''}`,
176
+ { pred },
177
+ 0
178
+ )
179
+ }
180
+ if (coll === undefined) {
181
+ return v.nil()
182
+ }
183
+ if (!is.seqable(coll)) {
184
+ throw EvaluationError.atArg(
185
+ `some expects a collection or string as second argument, got ${printString(coll)}`,
186
+ { coll },
187
+ 1
188
+ )
189
+ }
190
+ for (const item of toSeq(coll)) {
191
+ const result = ctx.applyFunction(pred, [item], callEnv)
192
+ if (is.truthy(result)) {
193
+ return result
194
+ }
202
195
  }
196
+ return v.nil()
203
197
  }
204
- return cljNil()
205
- }),
206
- 'Returns the first truthy result of applying pred to each item in coll, or nil if no item satisfies pred.',
207
- [['pred', 'coll']]
208
- ),
209
-
210
- 'every?': withDoc(
211
- cljNativeFunction('every?', (pred: CljValue, coll: CljValue): CljValue => {
212
- if (pred === undefined || !isAFunction(pred)) {
213
- throw EvaluationError.atArg(`every? expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ''}`, { pred }, 0)
198
+ )
199
+ .doc(
200
+ 'Returns the first truthy result of applying pred to each item in coll, or nil if no item satisfies pred.',
201
+ [['pred', 'coll']]
202
+ ),
203
+
204
+ 'every?': v
205
+ .nativeFnCtx(
206
+ 'every?',
207
+ function everyPredImpl(
208
+ ctx: EvaluationContext,
209
+ callEnv: Env,
210
+ pred: CljValue,
211
+ coll: CljValue
212
+ ): CljValue {
213
+ if (pred === undefined || !is.aFunction(pred)) {
214
+ throw EvaluationError.atArg(
215
+ `every? expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ''}`,
216
+ { pred },
217
+ 0
218
+ )
219
+ }
220
+ if (coll === undefined || !is.seqable(coll)) {
221
+ throw EvaluationError.atArg(
222
+ `every? expects a collection or string as second argument${coll !== undefined ? `, got ${printString(coll)}` : ''}`,
223
+ { coll },
224
+ 1
225
+ )
226
+ }
227
+ for (const item of toSeq(coll)) {
228
+ if (is.falsy(ctx.applyFunction(pred, [item], callEnv))) {
229
+ return v.boolean(false)
230
+ }
231
+ }
232
+ return v.boolean(true)
214
233
  }
215
- if (coll === undefined || !isSeqable(coll)) {
216
- throw EvaluationError.atArg(`every? expects a collection or string as second argument${coll !== undefined ? `, got ${printString(coll)}` : ''}`, { coll }, 1)
234
+ )
235
+ .doc('Returns true if all items in coll satisfy pred, false otherwise.', [
236
+ ['pred', 'coll'],
237
+ ]),
238
+
239
+ 'identical?': v
240
+ .nativeFn(
241
+ 'identical?',
242
+ function identicalPredImpl(x: CljValue, y: CljValue) {
243
+ return v.boolean(x === y)
217
244
  }
218
- for (const item of toSeq(coll)) {
219
- if (isFalsy(applyFunction(pred, [item]))) {
220
- return cljBoolean(false)
245
+ )
246
+ .doc('Tests if 2 arguments are the same object (reference equality).', [
247
+ ['x', 'y'],
248
+ ]),
249
+
250
+ 'seqable?': v
251
+ .nativeFn('seqable?', function seqablePredImpl(x: CljValue) {
252
+ return v.boolean(x !== undefined && is.seqable(x))
253
+ })
254
+ .doc('Return true if the seq function is supported for x.', [['x']]),
255
+
256
+ 'sequential?': v
257
+ .nativeFn('sequential?', function sequentialPredImpl(x: CljValue) {
258
+ return v.boolean(x !== undefined && (is.list(x) || is.vector(x)))
259
+ })
260
+ .doc('Returns true if coll is a sequential collection (list or vector).', [
261
+ ['coll'],
262
+ ]),
263
+
264
+ 'associative?': v
265
+ .nativeFn('associative?', function associativePredImpl(x: CljValue) {
266
+ return v.boolean(x !== undefined && (is.map(x) || is.vector(x)))
267
+ })
268
+ .doc('Returns true if coll implements Associative (map or vector).', [
269
+ ['coll'],
270
+ ]),
271
+
272
+ 'counted?': v
273
+ .nativeFn('counted?', function countedPredImpl(x: CljValue) {
274
+ return v.boolean(
275
+ x !== undefined &&
276
+ (is.list(x) ||
277
+ is.vector(x) ||
278
+ is.map(x) ||
279
+ x.kind === 'set' ||
280
+ is.string(x))
281
+ )
282
+ })
283
+ .doc('Returns true if coll implements count in constant time.', [['coll']]),
284
+
285
+ 'int?': v
286
+ .nativeFn('int?', function intPredImpl(x: CljValue) {
287
+ return v.boolean(
288
+ x !== undefined &&
289
+ x.kind === 'number' &&
290
+ Number.isInteger((x as import('../types').CljNumber).value)
291
+ )
292
+ })
293
+ .doc('Return true if x is a fixed precision integer.', [['x']]),
294
+
295
+ 'double?': v
296
+ .nativeFn('double?', function doublePredImpl(x: CljValue) {
297
+ return v.boolean(x !== undefined && x.kind === 'number')
298
+ })
299
+ .doc('Return true if x is a Double (all numbers in JS are doubles).', [
300
+ ['x'],
301
+ ]),
302
+
303
+ 'NaN?': v
304
+ .nativeFn('NaN?', function nanPredImpl(x: CljValue) {
305
+ return v.boolean(
306
+ x !== undefined && x.kind === 'number' && isNaN((x as CljNumber).value)
307
+ )
308
+ })
309
+ .doc('Returns true if num is NaN, else false.', [['num']]),
310
+
311
+ 'infinite?': v
312
+ .nativeFn('infinite?', function infinitePredImpl(x: CljValue) {
313
+ return v.boolean(
314
+ x !== undefined &&
315
+ x.kind === 'number' &&
316
+ !isFinite((x as CljNumber).value)
317
+ )
318
+ })
319
+ .doc('Returns true if num is positive or negative infinity, else false.', [
320
+ ['num'],
321
+ ]),
322
+
323
+ compare: v
324
+ .nativeFn(
325
+ 'compare',
326
+ function compareImpl(x: CljValue, y: CljValue): CljValue {
327
+ if (is.nil(x) && is.nil(y)) return v.number(0)
328
+ if (is.nil(x)) return v.number(-1)
329
+ if (is.nil(y)) return v.number(1)
330
+ if (is.number(x) && is.number(y)) {
331
+ return v.number(
332
+ (x as CljNumber).value < (y as CljNumber).value
333
+ ? -1
334
+ : (x as CljNumber).value > (y as CljNumber).value
335
+ ? 1
336
+ : 0
337
+ )
221
338
  }
339
+ if (is.string(x) && is.string(y)) {
340
+ return v.number(x.value < y.value ? -1 : x.value > y.value ? 1 : 0)
341
+ }
342
+ if (is.keyword(x) && is.keyword(y)) {
343
+ return v.number(x.name < y.name ? -1 : x.name > y.name ? 1 : 0)
344
+ }
345
+ throw new EvaluationError(
346
+ `compare: cannot compare ${printString(x)} to ${printString(y)}`,
347
+ { x, y }
348
+ )
349
+ }
350
+ )
351
+ .doc('Comparator. Returns a negative number, zero, or a positive number.', [
352
+ ['x', 'y'],
353
+ ]),
354
+
355
+ hash: v
356
+ .nativeFn('hash', function hashImpl(x: CljValue) {
357
+ // Simple hash — consistent within a session, not cryptographic
358
+ const s = printString(x)
359
+ let h = 0
360
+ for (let i = 0; i < s.length; i++) {
361
+ h = (Math.imul(31, h) + s.charCodeAt(i)) | 0
222
362
  }
223
- return cljBoolean(true)
224
- }),
225
- 'Returns true if all items in coll satisfy pred, false otherwise.',
226
- [['pred', 'coll']]
227
- ),
363
+ return v.number(h)
364
+ })
365
+ .doc('Returns the hash code of its argument.', [['x']]),
228
366
  }