conjure-js 0.0.11 → 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 +9336 -5028
- package/dist-vite-plugin/index.mjs +10455 -0
- package/package.json +9 -2
- package/src/bin/cli.ts +2 -2
- package/src/bin/nrepl-symbol.ts +150 -0
- package/src/bin/nrepl.ts +301 -157
- package/src/bin/version.ts +1 -1
- package/src/clojure/core.clj +764 -29
- package/src/clojure/core.clj.d.ts +76 -4
- package/src/clojure/demo/math.clj +5 -1
- package/src/clojure/generated/builtin-namespace-registry.ts +4 -0
- package/src/clojure/generated/clojure-core-source.ts +765 -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 +42 -7
- package/src/core/errors.ts +8 -0
- 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 +30 -4
- package/src/core/evaluator/destructure.ts +180 -69
- package/src/core/evaluator/dispatch.ts +24 -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 +380 -173
- package/src/core/factories.ts +182 -3
- package/src/core/index.ts +55 -5
- package/src/core/module.ts +136 -0
- package/src/core/ns-forms.ts +107 -0
- package/src/core/positions.ts +9 -2
- package/src/core/printer.ts +371 -11
- package/src/core/reader.ts +127 -29
- package/src/core/registry.ts +209 -0
- package/src/core/runtime.ts +376 -0
- package/src/core/session.ts +263 -478
- package/src/core/stdlib/arithmetic.ts +516 -215
- package/src/core/stdlib/async-fns.ts +132 -0
- package/src/core/stdlib/atoms.ts +286 -63
- package/src/core/stdlib/errors.ts +54 -50
- package/src/core/stdlib/hof.ts +74 -173
- 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 +109 -28
- package/src/core/stdlib/predicates.ts +322 -196
- 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 +283 -147
- package/src/core/stdlib/vars.ts +27 -27
- package/src/core/stdlib/vectors.ts +122 -0
- package/src/core/tokenizer.ts +13 -3
- package/src/core/transformations.ts +117 -9
- package/src/core/types.ts +118 -6
- package/src/host/node-host-module.ts +74 -0
- package/src/nrepl/relay.ts +432 -0
- package/src/vite-plugin-clj/codegen.ts +87 -95
- package/src/vite-plugin-clj/index.ts +242 -18
- 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 -63
- package/src/clojure/demo.clj.d.ts +0 -0
- package/src/core/core-env.ts +0 -60
- package/src/core/stdlib/collections.ts +0 -784
- package/src/host/node.ts +0 -55
package/src/core/stdlib/utils.ts
CHANGED
|
@@ -1,68 +1,67 @@
|
|
|
1
1
|
// Miscellaneous utilities: str, type, gensym, eval, macroexpand-1, macroexpand,
|
|
2
2
|
// namespace, name, keyword
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { is } from '../assertions'
|
|
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
|
|
29
|
+
throw EvaluationError.atArg(
|
|
35
30
|
`subs expects a string as first argument${s !== undefined ? `, got ${printString(s)}` : ''}`,
|
|
36
|
-
{ s }
|
|
31
|
+
{ s },
|
|
32
|
+
0
|
|
37
33
|
)
|
|
38
34
|
}
|
|
39
35
|
if (start === undefined || start.kind !== 'number') {
|
|
40
|
-
throw
|
|
36
|
+
throw EvaluationError.atArg(
|
|
41
37
|
`subs expects a number as second argument${start !== undefined ? `, got ${printString(start)}` : ''}`,
|
|
42
|
-
{ start }
|
|
38
|
+
{ start },
|
|
39
|
+
1
|
|
43
40
|
)
|
|
44
41
|
}
|
|
45
42
|
if (end !== undefined && end.kind !== 'number') {
|
|
46
|
-
throw
|
|
43
|
+
throw EvaluationError.atArg(
|
|
47
44
|
`subs expects a number as optional third argument${end !== undefined ? `, got ${printString(end)}` : ''}`,
|
|
48
|
-
{ end }
|
|
45
|
+
{ end },
|
|
46
|
+
2
|
|
49
47
|
)
|
|
50
48
|
}
|
|
51
49
|
const from = start.value
|
|
52
50
|
const to = end?.value
|
|
53
|
-
return
|
|
51
|
+
return v.string(
|
|
54
52
|
to === undefined ? s.value.slice(from) : s.value.slice(from, to)
|
|
55
53
|
)
|
|
56
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
|
+
]
|
|
57
62
|
),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
['s', 'start'],
|
|
61
|
-
['s', 'start', 'end'],
|
|
62
|
-
]
|
|
63
|
-
),
|
|
64
|
-
type: withDoc(
|
|
65
|
-
cljNativeFunction('type', (x: CljValue) => {
|
|
63
|
+
type: v
|
|
64
|
+
.nativeFn('type', function typeImpl(x: CljValue) {
|
|
66
65
|
if (x === undefined) {
|
|
67
66
|
throw new EvaluationError('type expects an argument', { x })
|
|
68
67
|
}
|
|
@@ -85,204 +84,341 @@ export const utilFunctions: Record<string, CljValue> = {
|
|
|
85
84
|
if (!name) {
|
|
86
85
|
throw new EvaluationError(`type: unhandled kind ${x.kind}`, { x })
|
|
87
86
|
}
|
|
88
|
-
return
|
|
89
|
-
})
|
|
90
|
-
'Returns a keyword representing the type of the given value.',
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
gensym:
|
|
94
|
-
|
|
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[]) {
|
|
95
94
|
if (args.length > 1) {
|
|
96
95
|
throw new EvaluationError('gensym takes 0 or 1 arguments', { args })
|
|
97
96
|
}
|
|
98
97
|
const prefix = args[0]
|
|
99
98
|
if (prefix !== undefined && prefix.kind !== 'string') {
|
|
100
|
-
throw
|
|
99
|
+
throw EvaluationError.atArg(
|
|
101
100
|
`gensym prefix must be a string${prefix !== undefined ? `, got ${printString(prefix)}` : ''}`,
|
|
102
|
-
{ prefix }
|
|
101
|
+
{ prefix },
|
|
102
|
+
0
|
|
103
103
|
)
|
|
104
104
|
}
|
|
105
105
|
const p = prefix?.kind === 'string' ? prefix.value : 'G'
|
|
106
|
-
return
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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(
|
|
113
114
|
'eval',
|
|
114
|
-
(
|
|
115
|
+
function evalImpl(
|
|
116
|
+
ctx: EvaluationContext,
|
|
117
|
+
callEnv: Env,
|
|
118
|
+
form: CljValue | undefined
|
|
119
|
+
) {
|
|
115
120
|
if (form === undefined) {
|
|
116
121
|
throw new EvaluationError('eval expects a form as argument', {
|
|
117
122
|
form,
|
|
118
123
|
})
|
|
119
124
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
return ctx.evaluate(expanded, rootEnv)
|
|
125
|
+
const expanded = ctx.expandAll(form, callEnv)
|
|
126
|
+
return ctx.evaluate(expanded, callEnv)
|
|
123
127
|
}
|
|
128
|
+
)
|
|
129
|
+
.doc(
|
|
130
|
+
'Evaluates the given form in the global environment and returns the result.',
|
|
131
|
+
[['form']]
|
|
124
132
|
),
|
|
125
|
-
'Evaluates the given form in the global environment and returns the result.',
|
|
126
|
-
[['form']]
|
|
127
|
-
),
|
|
128
133
|
|
|
129
|
-
'macroexpand-1':
|
|
130
|
-
|
|
134
|
+
'macroexpand-1': v
|
|
135
|
+
.nativeFnCtx(
|
|
131
136
|
'macroexpand-1',
|
|
132
|
-
(
|
|
133
|
-
|
|
137
|
+
function macroexpand1Impl(
|
|
138
|
+
ctx: EvaluationContext,
|
|
139
|
+
callEnv: Env,
|
|
140
|
+
form: CljValue
|
|
141
|
+
) {
|
|
142
|
+
if (!is.list(form) || form.value.length === 0) return form
|
|
134
143
|
const head = form.value[0]
|
|
135
|
-
if (!
|
|
136
|
-
const macroValue = tryLookup(head.name,
|
|
144
|
+
if (!is.symbol(head)) return form
|
|
145
|
+
const macroValue = tryLookup(head.name, callEnv)
|
|
137
146
|
if (macroValue === undefined) return form
|
|
138
|
-
if (!
|
|
147
|
+
if (!is.macro(macroValue)) return form
|
|
139
148
|
return ctx.applyMacro(macroValue, form.value.slice(1))
|
|
140
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']]
|
|
141
154
|
),
|
|
142
|
-
'If the head of the form is a macro, expands it and returns the resulting forms. Otherwise, returns the form unchanged.',
|
|
143
|
-
[['form']]
|
|
144
|
-
),
|
|
145
155
|
|
|
146
|
-
macroexpand:
|
|
147
|
-
|
|
156
|
+
macroexpand: v
|
|
157
|
+
.nativeFnCtx(
|
|
148
158
|
'macroexpand',
|
|
149
|
-
(
|
|
150
|
-
|
|
159
|
+
function macroexpandImpl(
|
|
160
|
+
ctx: EvaluationContext,
|
|
161
|
+
callEnv: Env,
|
|
162
|
+
form: CljValue
|
|
163
|
+
) {
|
|
151
164
|
let current = form
|
|
152
165
|
while (true) {
|
|
153
|
-
if (!
|
|
166
|
+
if (!is.list(current) || current.value.length === 0) return current
|
|
154
167
|
const head = current.value[0]
|
|
155
|
-
if (!
|
|
156
|
-
const macroValue = tryLookup(head.name,
|
|
168
|
+
if (!is.symbol(head)) return current
|
|
169
|
+
const macroValue = tryLookup(head.name, callEnv)
|
|
157
170
|
if (macroValue === undefined) return current
|
|
158
|
-
if (!
|
|
171
|
+
if (!is.macro(macroValue)) return current
|
|
159
172
|
current = ctx.applyMacro(macroValue, current.value.slice(1))
|
|
160
173
|
}
|
|
161
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']]
|
|
162
183
|
),
|
|
163
|
-
joinLines([
|
|
164
|
-
'Expands all macros until the expansion is stable (head is no longer a macro)',
|
|
165
|
-
'',
|
|
166
|
-
'Note neither macroexpand-1 nor macroexpand will expand macros in sub-forms',
|
|
167
|
-
]),
|
|
168
|
-
[['form']]
|
|
169
|
-
),
|
|
170
184
|
|
|
171
|
-
'macroexpand-all':
|
|
172
|
-
|
|
185
|
+
'macroexpand-all': v
|
|
186
|
+
.nativeFnCtx(
|
|
173
187
|
'macroexpand-all',
|
|
174
|
-
(
|
|
175
|
-
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']]
|
|
176
204
|
),
|
|
177
|
-
joinLines([
|
|
178
|
-
'Fully expands all macros in a form recursively — including in sub-forms.',
|
|
179
|
-
'',
|
|
180
|
-
'Unlike macroexpand, this descends into every sub-expression.',
|
|
181
|
-
'Expansion stops at quote/quasiquote boundaries and fn/loop bodies.',
|
|
182
|
-
]),
|
|
183
|
-
[['form']]
|
|
184
|
-
),
|
|
185
205
|
|
|
186
206
|
// Returns the namespace string of a qualified keyword or symbol, or nil.
|
|
187
207
|
// (namespace :user/foo) => "user"
|
|
188
208
|
// (namespace :foo) => nil
|
|
189
209
|
// (namespace 'user/foo) => "user"
|
|
190
|
-
namespace:
|
|
191
|
-
|
|
210
|
+
namespace: v
|
|
211
|
+
.nativeFn('namespace', function namespaceImpl(x: CljValue) {
|
|
192
212
|
if (x === undefined) {
|
|
193
|
-
throw
|
|
213
|
+
throw EvaluationError.atArg('namespace expects an argument', { x }, 0)
|
|
194
214
|
}
|
|
195
215
|
let raw: string | undefined
|
|
196
|
-
if (
|
|
216
|
+
if (is.keyword(x)) {
|
|
197
217
|
// keyword name format: ":ns/local" or ":local"
|
|
198
218
|
raw = x.name.slice(1) // strip leading ":"
|
|
199
|
-
} else if (
|
|
219
|
+
} else if (is.symbol(x)) {
|
|
200
220
|
raw = x.name
|
|
201
221
|
} else {
|
|
202
|
-
throw
|
|
222
|
+
throw EvaluationError.atArg(
|
|
203
223
|
`namespace expects a keyword or symbol, got ${printString(x)}`,
|
|
204
|
-
{ x }
|
|
224
|
+
{ x },
|
|
225
|
+
0
|
|
205
226
|
)
|
|
206
227
|
}
|
|
207
228
|
const slashIdx = raw.indexOf('/')
|
|
208
|
-
if (slashIdx <= 0) return
|
|
209
|
-
return
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
+
),
|
|
214
236
|
|
|
215
237
|
// Returns the local name of a keyword or symbol as a string.
|
|
216
238
|
// (name :user/foo) => "foo"
|
|
217
239
|
// (name :foo) => "foo"
|
|
218
240
|
// (name 'user/foo) => "foo"
|
|
219
|
-
name:
|
|
220
|
-
|
|
241
|
+
name: v
|
|
242
|
+
.nativeFn('name', function nameImpl(x: CljValue) {
|
|
221
243
|
if (x === undefined) {
|
|
222
|
-
throw
|
|
244
|
+
throw EvaluationError.atArg('name expects an argument', { x }, 0)
|
|
223
245
|
}
|
|
224
246
|
let raw: string | undefined
|
|
225
|
-
if (
|
|
247
|
+
if (is.keyword(x)) {
|
|
226
248
|
raw = x.name.slice(1) // strip leading ":"
|
|
227
|
-
} else if (
|
|
249
|
+
} else if (is.symbol(x)) {
|
|
228
250
|
raw = x.name
|
|
229
251
|
} else if (x.kind === 'string') {
|
|
230
252
|
return x
|
|
231
253
|
} else {
|
|
232
|
-
throw
|
|
254
|
+
throw EvaluationError.atArg(
|
|
233
255
|
`name expects a keyword, symbol, or string, got ${printString(x)}`,
|
|
234
|
-
{ x }
|
|
256
|
+
{ x },
|
|
257
|
+
0
|
|
235
258
|
)
|
|
236
259
|
}
|
|
237
260
|
const slashIdx = raw.indexOf('/')
|
|
238
|
-
return
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
),
|
|
243
267
|
|
|
244
268
|
// Constructs a keyword.
|
|
245
269
|
// (keyword "foo") => :foo
|
|
246
270
|
// (keyword "user" "foo") => :user/foo
|
|
247
|
-
keyword:
|
|
248
|
-
|
|
271
|
+
keyword: v
|
|
272
|
+
.nativeFn('keyword', function keywordImpl(...args: CljValue[]) {
|
|
249
273
|
if (args.length === 0 || args.length > 2) {
|
|
250
274
|
throw new EvaluationError('keyword expects 1 or 2 string arguments', {
|
|
251
275
|
args,
|
|
252
276
|
})
|
|
253
277
|
}
|
|
254
278
|
if (args[0].kind !== 'string') {
|
|
255
|
-
throw
|
|
279
|
+
throw EvaluationError.atArg(
|
|
256
280
|
`keyword expects a string, got ${printString(args[0])}`,
|
|
257
|
-
{ args }
|
|
281
|
+
{ args },
|
|
282
|
+
0
|
|
258
283
|
)
|
|
259
284
|
}
|
|
260
285
|
if (args.length === 1) {
|
|
261
|
-
return
|
|
286
|
+
return v.keyword(`:${args[0].value}`)
|
|
262
287
|
}
|
|
263
288
|
if (args[1].kind !== 'string') {
|
|
264
|
-
throw
|
|
289
|
+
throw EvaluationError.atArg(
|
|
265
290
|
`keyword second argument must be a string, got ${printString(args[1])}`,
|
|
266
|
-
{ args }
|
|
291
|
+
{ args },
|
|
292
|
+
1
|
|
293
|
+
)
|
|
294
|
+
}
|
|
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'],
|
|
314
|
+
]),
|
|
315
|
+
|
|
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))
|
|
267
346
|
)
|
|
268
347
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
'
|
|
273
|
-
'',
|
|
274
|
-
'Note: do not use : in the keyword strings, it will be added automatically.',
|
|
275
|
-
'e.g. (keyword "foo") => :foo',
|
|
348
|
+
)
|
|
349
|
+
.doc('Returns a pretty-printed string representation of form.', [
|
|
350
|
+
['form'],
|
|
351
|
+
['form', 'max-width'],
|
|
276
352
|
]),
|
|
277
|
-
[['name'], ['ns', 'name']]
|
|
278
|
-
),
|
|
279
353
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
+
]),
|
|
288
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
|
}
|