conjure-js 0.0.1
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/conjure +0 -0
- package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/assets/editor.worker-CdQrwHl8.js +26 -0
- package/dist/assets/main-A7ZMId9A.css +1 -0
- package/dist/assets/main-CmI-7epE.js +3137 -0
- package/dist/index.html +195 -0
- package/dist/vite.svg +1 -0
- package/package.json +68 -0
- package/src/bin/__fixtures__/smoke/app/lib.clj +4 -0
- package/src/bin/__fixtures__/smoke/app/main.clj +4 -0
- package/src/bin/__fixtures__/smoke/repl-smoke.ts +12 -0
- package/src/bin/bencode.ts +205 -0
- package/src/bin/cli.ts +250 -0
- package/src/bin/nrepl-utils.ts +59 -0
- package/src/bin/nrepl.ts +393 -0
- package/src/bin/version.ts +4 -0
- package/src/clojure/core.clj +620 -0
- package/src/clojure/core.clj.d.ts +189 -0
- package/src/clojure/demo/math.clj +16 -0
- package/src/clojure/demo/math.clj.d.ts +4 -0
- package/src/clojure/demo.clj +42 -0
- package/src/clojure/demo.clj.d.ts +0 -0
- package/src/clojure/generated/builtin-namespace-registry.ts +14 -0
- package/src/clojure/generated/clojure-core-source.ts +623 -0
- package/src/clojure/generated/clojure-string-source.ts +196 -0
- package/src/clojure/string.clj +192 -0
- package/src/clojure/string.clj.d.ts +25 -0
- package/src/core/assertions.ts +134 -0
- package/src/core/conversions.ts +108 -0
- package/src/core/core-env.ts +58 -0
- package/src/core/env.ts +78 -0
- package/src/core/errors.ts +39 -0
- package/src/core/evaluator/apply.ts +114 -0
- package/src/core/evaluator/arity.ts +174 -0
- package/src/core/evaluator/collections.ts +25 -0
- package/src/core/evaluator/destructure.ts +247 -0
- package/src/core/evaluator/dispatch.ts +73 -0
- package/src/core/evaluator/evaluate.ts +100 -0
- package/src/core/evaluator/expand.ts +79 -0
- package/src/core/evaluator/index.ts +72 -0
- package/src/core/evaluator/quasiquote.ts +87 -0
- package/src/core/evaluator/recur-check.ts +109 -0
- package/src/core/evaluator/special-forms.ts +517 -0
- package/src/core/factories.ts +155 -0
- package/src/core/gensym.ts +9 -0
- package/src/core/index.ts +76 -0
- package/src/core/positions.ts +38 -0
- package/src/core/printer.ts +86 -0
- package/src/core/reader.ts +559 -0
- package/src/core/scanners.ts +93 -0
- package/src/core/session.ts +610 -0
- package/src/core/stdlib/arithmetic.ts +361 -0
- package/src/core/stdlib/atoms.ts +88 -0
- package/src/core/stdlib/collections.ts +784 -0
- package/src/core/stdlib/errors.ts +81 -0
- package/src/core/stdlib/hof.ts +307 -0
- package/src/core/stdlib/meta.ts +48 -0
- package/src/core/stdlib/predicates.ts +240 -0
- package/src/core/stdlib/regex.ts +238 -0
- package/src/core/stdlib/strings.ts +311 -0
- package/src/core/stdlib/transducers.ts +256 -0
- package/src/core/stdlib/utils.ts +287 -0
- package/src/core/tokenizer.ts +437 -0
- package/src/core/transformations.ts +75 -0
- package/src/core/types.ts +258 -0
- package/src/main.ts +1 -0
- package/src/monaco-esm.d.ts +7 -0
- package/src/playground/clojure-tokens.ts +67 -0
- package/src/playground/editor.worker.ts +5 -0
- package/src/playground/find-form.ts +138 -0
- package/src/playground/playground.ts +342 -0
- package/src/playground/samples/00-welcome.clj +385 -0
- package/src/playground/samples/01-collections.clj +191 -0
- package/src/playground/samples/02-higher-order-functions.clj +215 -0
- package/src/playground/samples/03-destructuring.clj +194 -0
- package/src/playground/samples/04-strings-and-regex.clj +202 -0
- package/src/playground/samples/05-error-handling.clj +212 -0
- package/src/repl/repl.ts +116 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { CljThrownSignal, EvaluationError } from '../errors'
|
|
2
|
+
import { cljKeyword, cljMap, cljNativeFunction, cljNil, withDoc } from '../factories'
|
|
3
|
+
import { isKeyword, isMap } from '../assertions'
|
|
4
|
+
import type { CljValue } from '../types'
|
|
5
|
+
|
|
6
|
+
export const errorFunctions = {
|
|
7
|
+
throw: withDoc(
|
|
8
|
+
cljNativeFunction('throw', (...args: CljValue[]) => {
|
|
9
|
+
if (args.length !== 1) {
|
|
10
|
+
throw new EvaluationError(
|
|
11
|
+
`throw requires exactly 1 argument, got ${args.length}`,
|
|
12
|
+
{ args }
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
throw new CljThrownSignal(args[0])
|
|
16
|
+
}),
|
|
17
|
+
'Throws a value as an exception. The value may be any CljValue; maps are idiomatic.',
|
|
18
|
+
[['value']]
|
|
19
|
+
),
|
|
20
|
+
|
|
21
|
+
'ex-info': withDoc(
|
|
22
|
+
cljNativeFunction('ex-info', (...args: CljValue[]) => {
|
|
23
|
+
if (args.length < 2) {
|
|
24
|
+
throw new EvaluationError(
|
|
25
|
+
`ex-info requires at least 2 arguments, got ${args.length}`,
|
|
26
|
+
{ args }
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
const [msg, data, cause] = args
|
|
30
|
+
if (msg.kind !== 'string') {
|
|
31
|
+
throw new EvaluationError(
|
|
32
|
+
'ex-info: first argument must be a string',
|
|
33
|
+
{ msg }
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
const entries: [CljValue, CljValue][] = [
|
|
37
|
+
[cljKeyword(':message'), msg],
|
|
38
|
+
[cljKeyword(':data'), data],
|
|
39
|
+
]
|
|
40
|
+
if (cause !== undefined) {
|
|
41
|
+
entries.push([cljKeyword(':cause'), cause])
|
|
42
|
+
}
|
|
43
|
+
return cljMap(entries)
|
|
44
|
+
}),
|
|
45
|
+
'Creates an error map with :message and :data keys. Optionally accepts a :cause.',
|
|
46
|
+
[['msg', 'data'], ['msg', 'data', 'cause']]
|
|
47
|
+
),
|
|
48
|
+
|
|
49
|
+
'ex-message': withDoc(
|
|
50
|
+
cljNativeFunction('ex-message', (...args: CljValue[]) => {
|
|
51
|
+
const [e] = args
|
|
52
|
+
if (!isMap(e)) return cljNil()
|
|
53
|
+
const entry = e.entries.find(([k]) => isKeyword(k) && k.name === ':message')
|
|
54
|
+
return entry ? entry[1] : cljNil()
|
|
55
|
+
}),
|
|
56
|
+
'Returns the :message of an error map, or nil.',
|
|
57
|
+
[['e']]
|
|
58
|
+
),
|
|
59
|
+
|
|
60
|
+
'ex-data': withDoc(
|
|
61
|
+
cljNativeFunction('ex-data', (...args: CljValue[]) => {
|
|
62
|
+
const [e] = args
|
|
63
|
+
if (!isMap(e)) return cljNil()
|
|
64
|
+
const entry = e.entries.find(([k]) => isKeyword(k) && k.name === ':data')
|
|
65
|
+
return entry ? entry[1] : cljNil()
|
|
66
|
+
}),
|
|
67
|
+
'Returns the :data map of an error map, or nil.',
|
|
68
|
+
[['e']]
|
|
69
|
+
),
|
|
70
|
+
|
|
71
|
+
'ex-cause': withDoc(
|
|
72
|
+
cljNativeFunction('ex-cause', (...args: CljValue[]) => {
|
|
73
|
+
const [e] = args
|
|
74
|
+
if (!isMap(e)) return cljNil()
|
|
75
|
+
const entry = e.entries.find(([k]) => isKeyword(k) && k.name === ':cause')
|
|
76
|
+
return entry ? entry[1] : cljNil()
|
|
77
|
+
}),
|
|
78
|
+
'Returns the :cause of an error map, or nil.',
|
|
79
|
+
[['e']]
|
|
80
|
+
),
|
|
81
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// Higher-order functions: map, filter, reduce, apply, partial, comp,
|
|
2
|
+
// map-indexed, identity
|
|
3
|
+
import {
|
|
4
|
+
isAFunction,
|
|
5
|
+
isCallable,
|
|
6
|
+
isNil,
|
|
7
|
+
isReduced,
|
|
8
|
+
isSeqable,
|
|
9
|
+
} from '../assertions'
|
|
10
|
+
import { EvaluationError } from '../errors'
|
|
11
|
+
import {
|
|
12
|
+
cljNativeFunction,
|
|
13
|
+
cljNativeFunctionWithContext,
|
|
14
|
+
withDoc,
|
|
15
|
+
} from '../factories'
|
|
16
|
+
import { printString } from '../printer'
|
|
17
|
+
import { toSeq } from '../transformations'
|
|
18
|
+
import type { CljValue, Env, EvaluationContext } from '../types'
|
|
19
|
+
|
|
20
|
+
export const hofFunctions: Record<string, CljValue> = {
|
|
21
|
+
// map: cljNativeFunctionWithContext(
|
|
22
|
+
// 'map',
|
|
23
|
+
// (
|
|
24
|
+
// ctx: EvaluationContext,
|
|
25
|
+
// fn: CljValue | undefined,
|
|
26
|
+
// collection: CljValue | undefined
|
|
27
|
+
// ): CljValue => {
|
|
28
|
+
// if (fn === undefined) {
|
|
29
|
+
// throw new EvaluationError(
|
|
30
|
+
// `map expects a function as first argument, got nil`,
|
|
31
|
+
// { fn }
|
|
32
|
+
// )
|
|
33
|
+
// }
|
|
34
|
+
// if (!isAFunction(fn)) {
|
|
35
|
+
// throw new EvaluationError(
|
|
36
|
+
// `map expects a function as first argument, got ${printString(fn)}`,
|
|
37
|
+
// { fn }
|
|
38
|
+
// )
|
|
39
|
+
// }
|
|
40
|
+
// if (collection === undefined) {
|
|
41
|
+
// return cljNil()
|
|
42
|
+
// }
|
|
43
|
+
// if (!isCollection(collection)) {
|
|
44
|
+
// throw new EvaluationError(
|
|
45
|
+
// `map expects a collection, got ${printString(collection)}`,
|
|
46
|
+
// { collection }
|
|
47
|
+
// )
|
|
48
|
+
// }
|
|
49
|
+
|
|
50
|
+
// const wrap = isVector(collection) ? cljVector : cljList
|
|
51
|
+
// return wrap(
|
|
52
|
+
// toSeq(collection).map((item) => ctx.applyFunction(fn, [item]))
|
|
53
|
+
// )
|
|
54
|
+
// }
|
|
55
|
+
// ),
|
|
56
|
+
// filter: cljNativeFunctionWithContext(
|
|
57
|
+
// 'filter',
|
|
58
|
+
// (
|
|
59
|
+
// ctx: EvaluationContext,
|
|
60
|
+
// fn: CljValue | undefined,
|
|
61
|
+
// collection: CljValue | undefined
|
|
62
|
+
// ): CljValue => {
|
|
63
|
+
// if (fn === undefined) {
|
|
64
|
+
// throw new EvaluationError(
|
|
65
|
+
// `filter expects a function as first argument, got nil`,
|
|
66
|
+
// { fn }
|
|
67
|
+
// )
|
|
68
|
+
// }
|
|
69
|
+
// if (!isAFunction(fn)) {
|
|
70
|
+
// throw new EvaluationError(
|
|
71
|
+
// `filter expects a function as first argument, got ${printString(fn)}`,
|
|
72
|
+
// { fn }
|
|
73
|
+
// )
|
|
74
|
+
// }
|
|
75
|
+
// if (collection === undefined) {
|
|
76
|
+
// return cljNil()
|
|
77
|
+
// }
|
|
78
|
+
// if (!isCollection(collection)) {
|
|
79
|
+
// throw new EvaluationError(
|
|
80
|
+
// `filter expects a collection, got ${printString(collection)}`,
|
|
81
|
+
// { collection }
|
|
82
|
+
// )
|
|
83
|
+
// }
|
|
84
|
+
|
|
85
|
+
// const wrap = isVector(collection) ? cljVector : cljList
|
|
86
|
+
// return wrap(
|
|
87
|
+
// toSeq(collection).filter((item) =>
|
|
88
|
+
// isTruthy(ctx.applyFunction(fn, [item]))
|
|
89
|
+
// )
|
|
90
|
+
// )
|
|
91
|
+
// }
|
|
92
|
+
// ),
|
|
93
|
+
reduce: withDoc(
|
|
94
|
+
cljNativeFunctionWithContext(
|
|
95
|
+
'reduce',
|
|
96
|
+
(
|
|
97
|
+
ctx: EvaluationContext,
|
|
98
|
+
callEnv: Env,
|
|
99
|
+
fn: CljValue,
|
|
100
|
+
...rest: CljValue[]
|
|
101
|
+
) => {
|
|
102
|
+
if (fn === undefined || !isAFunction(fn)) {
|
|
103
|
+
throw new EvaluationError(
|
|
104
|
+
`reduce expects a function as first argument${fn !== undefined ? `, got ${printString(fn)}` : ''}`,
|
|
105
|
+
{ fn }
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
if (rest.length === 0 || rest.length > 2) {
|
|
109
|
+
throw new EvaluationError(
|
|
110
|
+
'reduce expects 2 or 3 arguments: (reduce f coll) or (reduce f init coll)',
|
|
111
|
+
{ fn }
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const hasInit = rest.length === 2
|
|
116
|
+
const init: CljValue | undefined = hasInit ? rest[0] : undefined
|
|
117
|
+
const collection = hasInit ? rest[1] : rest[0]
|
|
118
|
+
|
|
119
|
+
// nil is treated as an empty collection (matches Clojure semantics)
|
|
120
|
+
if (collection.kind === 'nil') {
|
|
121
|
+
if (!hasInit) {
|
|
122
|
+
throw new EvaluationError(
|
|
123
|
+
'reduce called on empty collection with no initial value',
|
|
124
|
+
{ fn }
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
return init!
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!isSeqable(collection)) {
|
|
131
|
+
throw new EvaluationError(
|
|
132
|
+
`reduce expects a collection or string, got ${printString(collection)}`,
|
|
133
|
+
{ collection }
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const items = toSeq(collection)
|
|
138
|
+
|
|
139
|
+
if (!hasInit) {
|
|
140
|
+
if (items.length === 0) {
|
|
141
|
+
throw new EvaluationError(
|
|
142
|
+
'reduce called on empty collection with no initial value',
|
|
143
|
+
{ fn }
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
if (items.length === 1) return items[0]
|
|
147
|
+
let acc = items[0]
|
|
148
|
+
for (let i = 1; i < items.length; i++) {
|
|
149
|
+
const result = ctx.applyFunction(fn, [acc, items[i]], callEnv)
|
|
150
|
+
if (isReduced(result)) return result.value
|
|
151
|
+
acc = result
|
|
152
|
+
}
|
|
153
|
+
return acc
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let acc = init!
|
|
157
|
+
for (const item of items) {
|
|
158
|
+
const result = ctx.applyFunction(fn, [acc, item], callEnv)
|
|
159
|
+
if (isReduced(result)) return result.value
|
|
160
|
+
acc = result
|
|
161
|
+
}
|
|
162
|
+
return acc
|
|
163
|
+
}
|
|
164
|
+
),
|
|
165
|
+
'Reduces a collection to a single value by iteratively applying f. (reduce f coll) or (reduce f init coll).',
|
|
166
|
+
[
|
|
167
|
+
['f', 'coll'],
|
|
168
|
+
['f', 'val', 'coll'],
|
|
169
|
+
]
|
|
170
|
+
),
|
|
171
|
+
|
|
172
|
+
apply: withDoc(
|
|
173
|
+
cljNativeFunctionWithContext(
|
|
174
|
+
'apply',
|
|
175
|
+
(
|
|
176
|
+
ctx: EvaluationContext,
|
|
177
|
+
callEnv: Env,
|
|
178
|
+
fn: CljValue | undefined,
|
|
179
|
+
...rest: CljValue[]
|
|
180
|
+
) => {
|
|
181
|
+
if (fn === undefined || !isCallable(fn)) {
|
|
182
|
+
throw new EvaluationError(
|
|
183
|
+
`apply expects a callable as first argument${fn !== undefined ? `, got ${printString(fn)}` : ''}`,
|
|
184
|
+
{ fn }
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
if (rest.length === 0) {
|
|
188
|
+
throw new EvaluationError('apply expects at least 2 arguments', {
|
|
189
|
+
fn,
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
const lastArg = rest[rest.length - 1]
|
|
193
|
+
if (!isNil(lastArg) && !isSeqable(lastArg)) {
|
|
194
|
+
throw new EvaluationError(
|
|
195
|
+
`apply expects a collection or string as last argument, got ${printString(lastArg)}`,
|
|
196
|
+
{ lastArg }
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const args = [
|
|
201
|
+
...rest.slice(0, -1),
|
|
202
|
+
...(isNil(lastArg) ? [] : toSeq(lastArg)),
|
|
203
|
+
]
|
|
204
|
+
return ctx.applyCallable(fn, args, callEnv)
|
|
205
|
+
}
|
|
206
|
+
),
|
|
207
|
+
'Calls f with the elements of the last argument (a collection) as its arguments, optionally prepended by fixed args.',
|
|
208
|
+
[
|
|
209
|
+
['f', 'args'],
|
|
210
|
+
['f', '&', 'args'],
|
|
211
|
+
]
|
|
212
|
+
),
|
|
213
|
+
|
|
214
|
+
partial: withDoc(
|
|
215
|
+
cljNativeFunction('partial', (fn: CljValue, ...preArgs: CljValue[]) => {
|
|
216
|
+
if (fn === undefined || !isCallable(fn)) {
|
|
217
|
+
throw new EvaluationError(
|
|
218
|
+
`partial expects a callable as first argument${fn !== undefined ? `, got ${printString(fn)}` : ''}`,
|
|
219
|
+
{ fn }
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
const capturedFn = fn
|
|
223
|
+
return cljNativeFunctionWithContext(
|
|
224
|
+
'partial',
|
|
225
|
+
(ctx: EvaluationContext, callEnv: Env, ...moreArgs: CljValue[]) => {
|
|
226
|
+
return ctx.applyCallable(
|
|
227
|
+
capturedFn,
|
|
228
|
+
[...preArgs, ...moreArgs],
|
|
229
|
+
callEnv
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
}),
|
|
234
|
+
'Returns a function that calls f with pre-applied args prepended to any additional arguments.',
|
|
235
|
+
[['f', '&', 'args']]
|
|
236
|
+
),
|
|
237
|
+
|
|
238
|
+
comp: withDoc(
|
|
239
|
+
cljNativeFunction('comp', (...fns: CljValue[]) => {
|
|
240
|
+
if (fns.length === 0) {
|
|
241
|
+
return cljNativeFunction('identity', (x: CljValue) => x)
|
|
242
|
+
}
|
|
243
|
+
if (fns.some((f) => !isCallable(f))) {
|
|
244
|
+
throw new EvaluationError(
|
|
245
|
+
'comp expects functions or other callable values (keywords, maps)',
|
|
246
|
+
{ fns }
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
const capturedFns = fns
|
|
250
|
+
return cljNativeFunctionWithContext(
|
|
251
|
+
'composed',
|
|
252
|
+
(ctx: EvaluationContext, callEnv: Env, ...args: CljValue[]) => {
|
|
253
|
+
let result = ctx.applyCallable(
|
|
254
|
+
capturedFns[capturedFns.length - 1],
|
|
255
|
+
args,
|
|
256
|
+
callEnv
|
|
257
|
+
)
|
|
258
|
+
for (let i = capturedFns.length - 2; i >= 0; i--) {
|
|
259
|
+
result = ctx.applyCallable(capturedFns[i], [result], callEnv)
|
|
260
|
+
}
|
|
261
|
+
return result
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
}),
|
|
265
|
+
'Returns the composition of fns, applied right-to-left. (comp f g) is equivalent to (fn [x] (f (g x))). Accepts any callable: functions, keywords, and maps.',
|
|
266
|
+
[[], ['f'], ['f', 'g'], ['f', 'g', '&', 'fns']]
|
|
267
|
+
),
|
|
268
|
+
|
|
269
|
+
// 'map-indexed': cljNativeFunctionWithContext(
|
|
270
|
+
// 'map-indexed',
|
|
271
|
+
// (ctx: EvaluationContext, fn: CljValue, coll: CljValue): CljValue => {
|
|
272
|
+
// if (fn === undefined || !isAFunction(fn)) {
|
|
273
|
+
// throw new EvaluationError(
|
|
274
|
+
// `map-indexed expects a function as first argument${fn !== undefined ? `, got ${printString(fn)}` : ''}`,
|
|
275
|
+
// { fn }
|
|
276
|
+
// )
|
|
277
|
+
// }
|
|
278
|
+
// if (coll === undefined || !isCollection(coll)) {
|
|
279
|
+
// throw new EvaluationError(
|
|
280
|
+
// `map-indexed expects a collection as second argument${coll !== undefined ? `, got ${printString(coll)}` : ''}`,
|
|
281
|
+
// { coll }
|
|
282
|
+
// )
|
|
283
|
+
// }
|
|
284
|
+
// const items = toSeq(coll)
|
|
285
|
+
// const wrap = isVector(coll) ? cljVector : cljList
|
|
286
|
+
// return wrap(
|
|
287
|
+
// items.map((item, idx) =>
|
|
288
|
+
// ctx.applyFunction(fn as CljFunction | CljNativeFunction, [
|
|
289
|
+
// cljNumber(idx),
|
|
290
|
+
// item,
|
|
291
|
+
// ])
|
|
292
|
+
// )
|
|
293
|
+
// )
|
|
294
|
+
// }
|
|
295
|
+
// ),
|
|
296
|
+
|
|
297
|
+
identity: withDoc(
|
|
298
|
+
cljNativeFunction('identity', (x: CljValue) => {
|
|
299
|
+
if (x === undefined) {
|
|
300
|
+
throw new EvaluationError('identity expects one argument', {})
|
|
301
|
+
}
|
|
302
|
+
return x
|
|
303
|
+
}),
|
|
304
|
+
'Returns its single argument unchanged.',
|
|
305
|
+
[['x']]
|
|
306
|
+
),
|
|
307
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Metadata: with-meta, meta
|
|
2
|
+
import { EvaluationError } from '../errors'
|
|
3
|
+
import { cljNativeFunction, cljNil, withDoc } from '../factories'
|
|
4
|
+
import { printString } from '../printer'
|
|
5
|
+
import type { CljMap, CljValue } from '../types'
|
|
6
|
+
|
|
7
|
+
export const metaFunctions: Record<string, CljValue> = {
|
|
8
|
+
meta: withDoc(
|
|
9
|
+
cljNativeFunction('meta', (val: CljValue) => {
|
|
10
|
+
if (val === undefined) {
|
|
11
|
+
throw new EvaluationError('meta expects one argument', {})
|
|
12
|
+
}
|
|
13
|
+
if (val.kind === 'function' || val.kind === 'native-function') {
|
|
14
|
+
return val.meta ?? cljNil()
|
|
15
|
+
}
|
|
16
|
+
return cljNil()
|
|
17
|
+
}),
|
|
18
|
+
'Returns the metadata map of a value, or nil if the value has no metadata.',
|
|
19
|
+
[['val']]
|
|
20
|
+
),
|
|
21
|
+
|
|
22
|
+
'with-meta': withDoc(
|
|
23
|
+
cljNativeFunction('with-meta', (val: CljValue, m: CljValue) => {
|
|
24
|
+
if (val === undefined) {
|
|
25
|
+
throw new EvaluationError('with-meta expects two arguments', {})
|
|
26
|
+
}
|
|
27
|
+
if (m === undefined) {
|
|
28
|
+
throw new EvaluationError('with-meta expects two arguments', {})
|
|
29
|
+
}
|
|
30
|
+
if (m.kind !== 'map' && m.kind !== 'nil') {
|
|
31
|
+
throw new EvaluationError(
|
|
32
|
+
`with-meta expects a map as second argument, got ${printString(m)}`,
|
|
33
|
+
{ m }
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
if (val.kind !== 'function' && val.kind !== 'native-function') {
|
|
37
|
+
throw new EvaluationError(
|
|
38
|
+
`with-meta only supports functions, got ${printString(val)}`,
|
|
39
|
+
{ val }
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
const meta = m.kind === 'nil' ? undefined : (m as CljMap)
|
|
43
|
+
return { ...val, meta }
|
|
44
|
+
}),
|
|
45
|
+
'Returns a new value with the metadata map m applied to val.',
|
|
46
|
+
[['val', 'm']]
|
|
47
|
+
),
|
|
48
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Predicates & logical: nil?, true?, false?, truthy?, falsy?, not, not=,
|
|
2
|
+
// number?, string?, boolean?, vector?, list?, map?, keyword?, symbol?, fn?,
|
|
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'
|
|
18
|
+
import { EvaluationError } from '../errors'
|
|
19
|
+
import { cljBoolean, cljNativeFunction, cljNil, withDoc } from '../factories'
|
|
20
|
+
import { printString } from '../printer'
|
|
21
|
+
import { toSeq } from '../transformations'
|
|
22
|
+
import type { CljValue } from '../types'
|
|
23
|
+
|
|
24
|
+
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) => {
|
|
34
|
+
// returns true if the value is a boolean and true
|
|
35
|
+
if (arg.kind !== 'boolean') {
|
|
36
|
+
return cljBoolean(false)
|
|
37
|
+
}
|
|
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) => {
|
|
45
|
+
// returns true if the value is a boolean and false
|
|
46
|
+
if (arg.kind !== 'boolean') {
|
|
47
|
+
return cljBoolean(false)
|
|
48
|
+
}
|
|
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[]) => {
|
|
77
|
+
if (vals.length < 2) {
|
|
78
|
+
throw new EvaluationError('not= expects at least two arguments', {
|
|
79
|
+
args: vals,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
for (let i = 1; i < vals.length; i++) {
|
|
83
|
+
if (!isEqual(vals[i], vals[i - 1])) {
|
|
84
|
+
return cljBoolean(true)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
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')
|
|
95
|
+
),
|
|
96
|
+
'Returns true if the value is a number, false otherwise.',
|
|
97
|
+
[['x']]
|
|
98
|
+
),
|
|
99
|
+
|
|
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
|
+
),
|
|
107
|
+
|
|
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
|
+
),
|
|
115
|
+
|
|
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
|
+
),
|
|
131
|
+
|
|
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
|
+
),
|
|
139
|
+
|
|
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
|
+
),
|
|
147
|
+
|
|
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
|
+
),
|
|
155
|
+
|
|
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
|
+
),
|
|
163
|
+
|
|
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
|
+
),
|
|
171
|
+
|
|
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
|
+
),
|
|
179
|
+
|
|
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 new EvaluationError(
|
|
191
|
+
`some expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ''}`,
|
|
192
|
+
{ pred }
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
if (coll === undefined) {
|
|
196
|
+
return cljNil()
|
|
197
|
+
}
|
|
198
|
+
if (!isSeqable(coll)) {
|
|
199
|
+
throw new EvaluationError(
|
|
200
|
+
`some expects a collection or string as second argument, got ${printString(coll)}`,
|
|
201
|
+
{ coll }
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
for (const item of toSeq(coll)) {
|
|
205
|
+
const result = applyFunction(pred, [item])
|
|
206
|
+
if (isTruthy(result)) {
|
|
207
|
+
return result
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return cljNil()
|
|
211
|
+
}),
|
|
212
|
+
'Returns the first truthy result of applying pred to each item in coll, or nil if no item satisfies pred.',
|
|
213
|
+
[['pred', 'coll']]
|
|
214
|
+
),
|
|
215
|
+
|
|
216
|
+
'every?': withDoc(
|
|
217
|
+
cljNativeFunction('every?', (pred: CljValue, coll: CljValue): CljValue => {
|
|
218
|
+
if (pred === undefined || !isAFunction(pred)) {
|
|
219
|
+
throw new EvaluationError(
|
|
220
|
+
`every? expects a function as first argument${pred !== undefined ? `, got ${printString(pred)}` : ''}`,
|
|
221
|
+
{ pred }
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
if (coll === undefined || !isSeqable(coll)) {
|
|
225
|
+
throw new EvaluationError(
|
|
226
|
+
`every? expects a collection or string as second argument${coll !== undefined ? `, got ${printString(coll)}` : ''}`,
|
|
227
|
+
{ coll }
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
for (const item of toSeq(coll)) {
|
|
231
|
+
if (isFalsy(applyFunction(pred, [item]))) {
|
|
232
|
+
return cljBoolean(false)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return cljBoolean(true)
|
|
236
|
+
}),
|
|
237
|
+
'Returns true if all items in coll satisfy pred, false otherwise.',
|
|
238
|
+
[['pred', 'coll']]
|
|
239
|
+
),
|
|
240
|
+
}
|