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