conjure-js 0.0.13 → 0.0.14
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 +2328 -2089
- package/dist-vite-plugin/index.mjs +2327 -2088
- package/package.json +1 -1
- package/src/bin/version.ts +1 -1
- package/src/core/assertions.ts +10 -3
- package/src/core/bootstrap.ts +7 -23
- package/src/core/compiler/binding.ts +164 -0
- package/src/core/compiler/callable.ts +41 -0
- package/src/core/compiler/compile-env.ts +40 -0
- package/src/core/compiler/control-flow.ts +79 -0
- package/src/core/compiler/index.ts +121 -0
- package/src/core/env.ts +4 -4
- package/src/core/errors.ts +1 -0
- package/src/core/evaluator/apply.ts +7 -3
- package/src/core/evaluator/arity.ts +16 -6
- package/src/core/evaluator/async-evaluator.ts +68 -89
- package/src/core/evaluator/collections.ts +9 -4
- package/src/core/evaluator/destructure.ts +45 -55
- package/src/core/evaluator/dispatch.ts +21 -24
- package/src/core/evaluator/evaluate.ts +14 -2
- package/src/core/evaluator/expand.ts +5 -7
- package/src/core/evaluator/js-interop.ts +46 -33
- package/src/core/evaluator/quasiquote.ts +7 -11
- package/src/core/evaluator/recur-check.ts +1 -1
- package/src/core/evaluator/special-forms.ts +18 -38
- package/src/core/index.ts +1 -1
- package/src/core/keywords.ts +105 -0
- package/src/core/modules/core/index.ts +131 -0
- package/src/core/{stdlib → modules/core/stdlib}/arithmetic.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/async-fns.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/atoms.ts +7 -7
- package/src/core/{stdlib → modules/core/stdlib}/errors.ts +4 -4
- package/src/core/{stdlib → modules/core/stdlib}/hof.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/lazy.ts +4 -4
- package/src/core/{stdlib → modules/core/stdlib}/maps-sets.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/meta.ts +5 -5
- package/src/core/{stdlib → modules/core/stdlib}/predicates.ts +7 -7
- package/src/core/modules/core/stdlib/print.ts +108 -0
- package/src/core/{stdlib → modules/core/stdlib}/regex.ts +5 -5
- package/src/core/{stdlib → modules/core/stdlib}/seq.ts +7 -7
- package/src/core/{stdlib → modules/core/stdlib}/strings.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/transducers.ts +6 -6
- package/src/core/{stdlib → modules/core/stdlib}/utils.ts +10 -10
- package/src/core/{stdlib → modules/core/stdlib}/vars.ts +4 -4
- package/src/core/{stdlib → modules/core/stdlib}/vectors.ts +6 -6
- package/src/core/modules/js/index.ts +402 -0
- package/src/core/ns-forms.ts +25 -17
- package/src/core/positions.ts +22 -2
- package/src/core/printer.ts +162 -53
- package/src/core/reader.ts +25 -22
- package/src/core/registry.ts +10 -10
- package/src/core/runtime.ts +23 -23
- package/src/core/session.ts +17 -7
- package/src/core/tokenizer.ts +14 -4
- package/src/core/transformations.ts +48 -29
- package/src/core/types.ts +57 -81
- package/src/core/core-module.ts +0 -303
- package/src/core/stdlib/js-namespace.ts +0 -344
package/src/core/session.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { builtInNamespaceSources } from '../clojure/generated/builtin-namespace-registry'
|
|
2
|
+
import { cljToJs as _cljToJs } from './conversions'
|
|
3
|
+
import { internVar, makeEnv } from './env'
|
|
2
4
|
import { CljThrownSignal, EvaluationError, ReaderError } from './errors'
|
|
3
5
|
import { createEvaluationContext, RecurSignal } from './evaluator'
|
|
4
|
-
import { internVar, makeEnv } from './env'
|
|
5
|
-
import { v } from './factories'
|
|
6
6
|
import { jsToClj } from './evaluator/js-interop'
|
|
7
|
+
import { v } from './factories'
|
|
7
8
|
import type { RuntimeModule } from './module'
|
|
8
|
-
import {
|
|
9
|
+
import { extractAliasMapFromTokens, extractNsNameFromTokens } from './ns-forms'
|
|
9
10
|
import { formatErrorContext } from './positions'
|
|
10
11
|
import { printString } from './printer'
|
|
11
12
|
import { readForms } from './reader'
|
|
12
13
|
import type { Runtime, RuntimeSnapshot } from './runtime'
|
|
13
14
|
import { createRuntime, restoreRuntime } from './runtime'
|
|
14
|
-
import { extractAliasMapFromTokens, extractNsNameFromTokens } from './ns-forms'
|
|
15
15
|
import { tokenize } from './tokenizer'
|
|
16
16
|
import type { CljNamespace, CljValue, Env } from './types'
|
|
17
17
|
|
|
@@ -57,7 +57,11 @@ export type Session = {
|
|
|
57
57
|
getNs: (namespace: string) => CljNamespace | null
|
|
58
58
|
loadFile: (source: string, nsName?: string, filePath?: string) => string
|
|
59
59
|
/** Async variant of loadFile — handles string requires ((:require ["pkg" :as X])). */
|
|
60
|
-
loadFileAsync: (
|
|
60
|
+
loadFileAsync: (
|
|
61
|
+
source: string,
|
|
62
|
+
nsName?: string,
|
|
63
|
+
filePath?: string
|
|
64
|
+
) => Promise<string>
|
|
61
65
|
evaluate: (
|
|
62
66
|
source: string,
|
|
63
67
|
opts?: { lineOffset?: number; colOffset?: number; file?: string }
|
|
@@ -142,7 +146,11 @@ function buildSessionFacade(
|
|
|
142
146
|
return runtime.loadFile(source, nsName, filePath, ctx)
|
|
143
147
|
},
|
|
144
148
|
|
|
145
|
-
async loadFileAsync(
|
|
149
|
+
async loadFileAsync(
|
|
150
|
+
source: string,
|
|
151
|
+
nsName?: string,
|
|
152
|
+
filePath?: string
|
|
153
|
+
): Promise<string> {
|
|
146
154
|
// If there is no ns declaration in the source, pre-set the namespace from
|
|
147
155
|
// the hint so the forms evaluate in the right context.
|
|
148
156
|
if (nsName) {
|
|
@@ -301,7 +309,9 @@ function buildSessionFacade(
|
|
|
301
309
|
},
|
|
302
310
|
|
|
303
311
|
cljToJs(value: CljValue): unknown {
|
|
304
|
-
return _cljToJs(value, {
|
|
312
|
+
return _cljToJs(value, {
|
|
313
|
+
applyFunction: (fn, args) => ctx.applyCallable(fn, args, makeEnv()),
|
|
314
|
+
})
|
|
305
315
|
},
|
|
306
316
|
|
|
307
317
|
evaluateForms(forms: CljValue[]): CljValue {
|
package/src/core/tokenizer.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TokenizerError } from './errors'
|
|
2
2
|
import { makeCharScanner, type CharScanner } from './scanners'
|
|
3
|
-
import { tokenKeywords, tokenSymbols
|
|
3
|
+
import { tokenKeywords, tokenSymbols } from './keywords'
|
|
4
|
+
import type { Token } from './types'
|
|
4
5
|
|
|
5
6
|
export type TokensResult = {
|
|
6
7
|
tokens: Token[]
|
|
@@ -169,9 +170,15 @@ const parseNumber = (ctx: TokenizationContext): Token => {
|
|
|
169
170
|
value += scanner.advance()! // consume '.'
|
|
170
171
|
value += scanner.consumeWhile(isNumber)
|
|
171
172
|
}
|
|
172
|
-
if (
|
|
173
|
+
if (
|
|
174
|
+
!scanner.isAtEnd() &&
|
|
175
|
+
(scanner.peek() === 'e' || scanner.peek() === 'E')
|
|
176
|
+
) {
|
|
173
177
|
value += scanner.advance()! // consume 'e' or 'E'
|
|
174
|
-
if (
|
|
178
|
+
if (
|
|
179
|
+
!scanner.isAtEnd() &&
|
|
180
|
+
(scanner.peek() === '+' || scanner.peek() === '-')
|
|
181
|
+
) {
|
|
175
182
|
value += scanner.advance()! // consume optional sign
|
|
176
183
|
}
|
|
177
184
|
const exponentDigits = scanner.consumeWhile(isNumber)
|
|
@@ -226,7 +233,10 @@ const parseMetaToken = (ctx: TokenizationContext): Token => {
|
|
|
226
233
|
return { kind: 'Meta', start, end: scanner.position() }
|
|
227
234
|
}
|
|
228
235
|
|
|
229
|
-
const parseRegexLiteral = (
|
|
236
|
+
const parseRegexLiteral = (
|
|
237
|
+
ctx: TokenizationContext,
|
|
238
|
+
start: ReturnType<typeof ctx.scanner.position>
|
|
239
|
+
): Token => {
|
|
230
240
|
const scanner = ctx.scanner
|
|
231
241
|
scanner.advance() // consume opening '"'
|
|
232
242
|
const buffer: string[] = []
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { is } from './assertions'
|
|
2
2
|
import { EvaluationError } from './errors'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { v } from './factories'
|
|
4
|
+
import { valueKeywords } from './keywords'
|
|
5
|
+
import { getPrintContext, printString } from './printer'
|
|
6
|
+
import {
|
|
7
|
+
type CljCons,
|
|
8
|
+
type CljDelay,
|
|
9
|
+
type CljLazySeq,
|
|
10
|
+
type CljValue,
|
|
11
|
+
} from './types'
|
|
6
12
|
|
|
7
13
|
export function valueToString(value: CljValue): string {
|
|
8
14
|
switch (value.kind) {
|
|
@@ -18,26 +24,36 @@ export function valueToString(value: CljValue): string {
|
|
|
18
24
|
return value.name
|
|
19
25
|
case valueKeywords.list: {
|
|
20
26
|
const { printLength } = getPrintContext()
|
|
21
|
-
const items =
|
|
22
|
-
|
|
27
|
+
const items =
|
|
28
|
+
printLength !== null ? value.value.slice(0, printLength) : value.value
|
|
29
|
+
const suffix =
|
|
30
|
+
printLength !== null && value.value.length > printLength ? ' ...' : ''
|
|
23
31
|
return `(${items.map(valueToString).join(' ')}${suffix})`
|
|
24
32
|
}
|
|
25
33
|
case valueKeywords.vector: {
|
|
26
34
|
const { printLength } = getPrintContext()
|
|
27
|
-
const items =
|
|
28
|
-
|
|
35
|
+
const items =
|
|
36
|
+
printLength !== null ? value.value.slice(0, printLength) : value.value
|
|
37
|
+
const suffix =
|
|
38
|
+
printLength !== null && value.value.length > printLength ? ' ...' : ''
|
|
29
39
|
return `[${items.map(valueToString).join(' ')}${suffix}]`
|
|
30
40
|
}
|
|
31
41
|
case valueKeywords.map: {
|
|
32
42
|
const { printLength } = getPrintContext()
|
|
33
|
-
const entries =
|
|
34
|
-
|
|
43
|
+
const entries =
|
|
44
|
+
printLength !== null
|
|
45
|
+
? value.entries.slice(0, printLength)
|
|
46
|
+
: value.entries
|
|
47
|
+
const suffix =
|
|
48
|
+
printLength !== null && value.entries.length > printLength ? ' ...' : ''
|
|
35
49
|
return `{${entries.map(([key, v]) => `${valueToString(key)} ${valueToString(v)}`).join(' ')}${suffix}}`
|
|
36
50
|
}
|
|
37
51
|
case valueKeywords.set: {
|
|
38
52
|
const { printLength } = getPrintContext()
|
|
39
|
-
const items =
|
|
40
|
-
|
|
53
|
+
const items =
|
|
54
|
+
printLength !== null ? value.values.slice(0, printLength) : value.values
|
|
55
|
+
const suffix =
|
|
56
|
+
printLength !== null && value.values.length > printLength ? ' ...' : ''
|
|
41
57
|
return `#{${items.map(valueToString).join(' ')}${suffix}}`
|
|
42
58
|
}
|
|
43
59
|
case valueKeywords.function: {
|
|
@@ -67,17 +83,20 @@ export function valueToString(value: CljValue): string {
|
|
|
67
83
|
return `${prefix}${value.pattern}`
|
|
68
84
|
}
|
|
69
85
|
case valueKeywords.delay:
|
|
70
|
-
return value.realized
|
|
86
|
+
return value.realized
|
|
87
|
+
? `#<Delay @${valueToString(value.value!)}>`
|
|
88
|
+
: '#<Delay pending>'
|
|
71
89
|
case valueKeywords.lazySeq: {
|
|
72
90
|
const realized = realizeLazySeq(value)
|
|
73
|
-
if (
|
|
91
|
+
if (is.nil(realized)) return '()'
|
|
74
92
|
return valueToString(realized)
|
|
75
93
|
}
|
|
76
94
|
case valueKeywords.cons: {
|
|
77
95
|
const items = consToArray(value)
|
|
78
96
|
const { printLength } = getPrintContext()
|
|
79
97
|
const visible = printLength !== null ? items.slice(0, printLength) : items
|
|
80
|
-
const suffix =
|
|
98
|
+
const suffix =
|
|
99
|
+
printLength !== null && items.length > printLength ? ' ...' : ''
|
|
81
100
|
return `(${visible.map(valueToString).join(' ')}${suffix})`
|
|
82
101
|
}
|
|
83
102
|
case valueKeywords.namespace:
|
|
@@ -123,27 +142,27 @@ export function realizeLazySeq(ls: CljLazySeq): CljValue {
|
|
|
123
142
|
}
|
|
124
143
|
|
|
125
144
|
export const toSeq = (collection: CljValue): CljValue[] => {
|
|
126
|
-
if (
|
|
145
|
+
if (is.list(collection)) {
|
|
127
146
|
return collection.value
|
|
128
147
|
}
|
|
129
|
-
if (
|
|
148
|
+
if (is.vector(collection)) {
|
|
130
149
|
return collection.value
|
|
131
150
|
}
|
|
132
|
-
if (
|
|
133
|
-
return collection.entries.map(([
|
|
151
|
+
if (is.map(collection)) {
|
|
152
|
+
return collection.entries.map(([key, value]) => v.vector([key, value]))
|
|
134
153
|
}
|
|
135
|
-
if (
|
|
154
|
+
if (is.set(collection)) {
|
|
136
155
|
return collection.values
|
|
137
156
|
}
|
|
138
157
|
if (collection.kind === 'string') {
|
|
139
|
-
return [...collection.value].map(
|
|
158
|
+
return [...collection.value].map(v.string)
|
|
140
159
|
}
|
|
141
|
-
if (
|
|
160
|
+
if (is.lazySeq(collection)) {
|
|
142
161
|
const realized = realizeLazySeq(collection)
|
|
143
|
-
if (
|
|
162
|
+
if (is.nil(realized)) return []
|
|
144
163
|
return toSeq(realized)
|
|
145
164
|
}
|
|
146
|
-
if (
|
|
165
|
+
if (is.cons(collection)) {
|
|
147
166
|
return consToArray(collection)
|
|
148
167
|
}
|
|
149
168
|
throw new EvaluationError(
|
|
@@ -157,21 +176,21 @@ export function consToArray(c: CljCons): CljValue[] {
|
|
|
157
176
|
const result: CljValue[] = [c.head]
|
|
158
177
|
let tail: CljValue = c.tail
|
|
159
178
|
while (true) {
|
|
160
|
-
if (
|
|
161
|
-
if (
|
|
179
|
+
if (is.nil(tail)) break
|
|
180
|
+
if (is.cons(tail)) {
|
|
162
181
|
result.push(tail.head)
|
|
163
182
|
tail = tail.tail
|
|
164
183
|
continue
|
|
165
184
|
}
|
|
166
|
-
if (
|
|
185
|
+
if (is.lazySeq(tail)) {
|
|
167
186
|
tail = realizeLazySeq(tail)
|
|
168
187
|
continue
|
|
169
188
|
}
|
|
170
|
-
if (
|
|
189
|
+
if (is.list(tail)) {
|
|
171
190
|
result.push(...tail.value)
|
|
172
191
|
break
|
|
173
192
|
}
|
|
174
|
-
if (
|
|
193
|
+
if (is.vector(tail)) {
|
|
175
194
|
result.push(...tail.value)
|
|
176
195
|
break
|
|
177
196
|
}
|
package/src/core/types.ts
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
export const valueKeywords = {
|
|
2
|
-
number: 'number',
|
|
3
|
-
string: 'string',
|
|
4
|
-
boolean: 'boolean',
|
|
5
|
-
keyword: 'keyword',
|
|
6
|
-
nil: 'nil',
|
|
7
|
-
symbol: 'symbol',
|
|
8
|
-
list: 'list',
|
|
9
|
-
vector: 'vector',
|
|
10
|
-
map: 'map',
|
|
11
|
-
function: 'function',
|
|
12
|
-
nativeFunction: 'native-function',
|
|
13
|
-
macro: 'macro',
|
|
14
|
-
multiMethod: 'multi-method',
|
|
15
|
-
atom: 'atom',
|
|
16
|
-
reduced: 'reduced',
|
|
17
|
-
volatile: 'volatile',
|
|
18
|
-
regex: 'regex',
|
|
19
|
-
var: 'var',
|
|
20
|
-
set: 'set',
|
|
21
|
-
delay: 'delay',
|
|
22
|
-
lazySeq: 'lazy-seq',
|
|
23
|
-
cons: 'cons',
|
|
24
|
-
namespace: 'namespace',
|
|
25
|
-
jsValue: 'js-value',
|
|
26
|
-
} as const
|
|
27
|
-
export type ValueKeywords = (typeof valueKeywords)[keyof typeof valueKeywords]
|
|
28
|
-
|
|
29
1
|
export type CljNumber = { kind: 'number'; value: number }
|
|
30
2
|
export type CljString = { kind: 'string'; value: string }
|
|
31
3
|
export type CljBoolean = { kind: 'boolean'; value: boolean }
|
|
@@ -34,19 +6,23 @@ export type CljNil = { kind: 'nil'; value: null }
|
|
|
34
6
|
export type CljSymbol = { kind: 'symbol'; name: string; meta?: CljMap }
|
|
35
7
|
export type CljList = { kind: 'list'; value: CljValue[]; meta?: CljMap }
|
|
36
8
|
export type CljVector = { kind: 'vector'; value: CljValue[]; meta?: CljMap }
|
|
37
|
-
export type CljMap = {
|
|
9
|
+
export type CljMap = {
|
|
10
|
+
kind: 'map'
|
|
11
|
+
entries: [CljValue, CljValue][]
|
|
12
|
+
meta?: CljMap
|
|
13
|
+
}
|
|
38
14
|
export type CljNamespace = {
|
|
39
15
|
kind: 'namespace'
|
|
40
16
|
name: string
|
|
41
|
-
vars: Map<string, CljVar>
|
|
42
|
-
aliases: Map<string, CljNamespace>
|
|
43
|
-
readerAliases: Map<string, string>
|
|
17
|
+
vars: Map<string, CljVar> // user defs from (def ...)
|
|
18
|
+
aliases: Map<string, CljNamespace> // :as namespace aliases
|
|
19
|
+
readerAliases: Map<string, string> // :as-alias reader aliases
|
|
44
20
|
}
|
|
45
21
|
|
|
46
22
|
export type Env = {
|
|
47
|
-
bindings: Map<string, CljValue>
|
|
23
|
+
bindings: Map<string, CljValue> // native fns, macros, multimethods, local values
|
|
48
24
|
outer: Env | null
|
|
49
|
-
ns?: CljNamespace
|
|
25
|
+
ns?: CljNamespace // set on namespace-root envs only
|
|
50
26
|
}
|
|
51
27
|
|
|
52
28
|
export type DestructurePattern = CljSymbol | CljVector | CljMap
|
|
@@ -55,13 +31,14 @@ export type Arity = {
|
|
|
55
31
|
params: DestructurePattern[]
|
|
56
32
|
restParam: DestructurePattern | null
|
|
57
33
|
body: CljValue[]
|
|
34
|
+
compiledBody?: CompiledExpr
|
|
58
35
|
}
|
|
59
36
|
|
|
60
37
|
export type CljFunction = {
|
|
61
38
|
kind: 'function'
|
|
62
39
|
arities: Arity[]
|
|
63
40
|
env: Env
|
|
64
|
-
name?: string
|
|
41
|
+
name?: string // set for named fn: (fn my-name [x] x)
|
|
65
42
|
meta?: CljMap
|
|
66
43
|
}
|
|
67
44
|
|
|
@@ -69,14 +46,17 @@ export type CljMacro = {
|
|
|
69
46
|
kind: 'macro'
|
|
70
47
|
arities: Arity[]
|
|
71
48
|
env: Env
|
|
72
|
-
name?: string
|
|
49
|
+
name?: string // set for named defmacro
|
|
73
50
|
}
|
|
74
51
|
|
|
75
52
|
export type CljAtom = {
|
|
76
53
|
kind: 'atom'
|
|
77
54
|
value: CljValue
|
|
78
55
|
meta?: CljMap
|
|
79
|
-
watches?: Map<
|
|
56
|
+
watches?: Map<
|
|
57
|
+
string,
|
|
58
|
+
{ key: CljValue; fn: CljValue; ctx: EvaluationContext; callEnv: Env }
|
|
59
|
+
>
|
|
80
60
|
validator?: CljValue
|
|
81
61
|
}
|
|
82
62
|
export type CljReduced = { kind: 'reduced'; value: CljValue }
|
|
@@ -96,13 +76,13 @@ export type CljLazySeq = {
|
|
|
96
76
|
kind: 'lazy-seq'
|
|
97
77
|
thunk: (() => CljValue) | null
|
|
98
78
|
realized: boolean
|
|
99
|
-
value?: CljValue
|
|
79
|
+
value?: CljValue // nil, list, cons, or another lazy-seq after realization
|
|
100
80
|
}
|
|
101
81
|
|
|
102
82
|
export type CljCons = {
|
|
103
83
|
kind: 'cons'
|
|
104
84
|
head: CljValue
|
|
105
|
-
tail: CljValue
|
|
85
|
+
tail: CljValue // can be list, vector, lazy-seq, cons, or nil
|
|
106
86
|
meta?: CljMap
|
|
107
87
|
}
|
|
108
88
|
|
|
@@ -111,8 +91,8 @@ export type CljVar = {
|
|
|
111
91
|
ns: string
|
|
112
92
|
name: string
|
|
113
93
|
value: CljValue
|
|
114
|
-
dynamic?: boolean
|
|
115
|
-
bindingStack?: CljValue[]
|
|
94
|
+
dynamic?: boolean // set when def is annotated with ^:dynamic
|
|
95
|
+
bindingStack?: CljValue[] // active dynamic bindings (push/pop by `binding`)
|
|
116
96
|
meta?: CljMap
|
|
117
97
|
}
|
|
118
98
|
|
|
@@ -234,46 +214,6 @@ export type CljValue =
|
|
|
234
214
|
| CljPending
|
|
235
215
|
| CljJsValue
|
|
236
216
|
|
|
237
|
-
/** Tokens */
|
|
238
|
-
export const tokenKeywords = {
|
|
239
|
-
LParen: 'LParen',
|
|
240
|
-
RParen: 'RParen',
|
|
241
|
-
LBracket: 'LBracket',
|
|
242
|
-
RBracket: 'RBracket',
|
|
243
|
-
LBrace: 'LBrace',
|
|
244
|
-
RBrace: 'RBrace',
|
|
245
|
-
String: 'String',
|
|
246
|
-
Number: 'Number',
|
|
247
|
-
Keyword: 'Keyword',
|
|
248
|
-
Quote: 'Quote',
|
|
249
|
-
Quasiquote: 'Quasiquote',
|
|
250
|
-
Unquote: 'Unquote',
|
|
251
|
-
UnquoteSplicing: 'UnquoteSplicing',
|
|
252
|
-
Comment: 'Comment',
|
|
253
|
-
Whitespace: 'Whitespace',
|
|
254
|
-
Symbol: 'Symbol',
|
|
255
|
-
AnonFnStart: 'AnonFnStart',
|
|
256
|
-
Deref: 'Deref',
|
|
257
|
-
Regex: 'Regex',
|
|
258
|
-
VarQuote: 'VarQuote',
|
|
259
|
-
Meta: 'Meta',
|
|
260
|
-
SetStart: 'SetStart',
|
|
261
|
-
} as const
|
|
262
|
-
export const tokenSymbols = {
|
|
263
|
-
Quote: 'quote',
|
|
264
|
-
Quasiquote: 'quasiquote',
|
|
265
|
-
Unquote: 'unquote',
|
|
266
|
-
UnquoteSplicing: 'unquote-splicing',
|
|
267
|
-
LParen: '(',
|
|
268
|
-
RParen: ')',
|
|
269
|
-
LBracket: '[',
|
|
270
|
-
RBracket: ']',
|
|
271
|
-
LBrace: '{',
|
|
272
|
-
RBrace: '}',
|
|
273
|
-
} as const
|
|
274
|
-
export type TokenSymbols = (typeof tokenSymbols)[keyof typeof tokenSymbols]
|
|
275
|
-
export type TokenKinds = keyof typeof tokenKeywords
|
|
276
|
-
|
|
277
217
|
export type Cursor = {
|
|
278
218
|
line: number
|
|
279
219
|
col: number
|
|
@@ -388,3 +328,39 @@ export type Token = (
|
|
|
388
328
|
| TokenMeta
|
|
389
329
|
| TokenSetStart
|
|
390
330
|
) & { start: Cursor; end: Cursor }
|
|
331
|
+
|
|
332
|
+
/** Compiler */
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* A compiled expression takes runtime env + ctx, returns a CljValue.
|
|
336
|
+
* Signature includes ctx even though we don't use it yet
|
|
337
|
+
* Phases 2+ (if, fn*, apply) will need it. We keep the shape fixed now.
|
|
338
|
+
*/
|
|
339
|
+
export type CompiledExpr = (env: Env, ctx: EvaluationContext) => CljValue
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* A compile function takes a node and an optional compile-time env. It returns a compiled expression
|
|
343
|
+
* when it can't compile a node, it returns null, falling back to the interpreter
|
|
344
|
+
* we have this definition here so that recursive compiler functions such as compileIf,
|
|
345
|
+
* and compileDo can reference the "root dispatcher" when compiling sub-forms.
|
|
346
|
+
* It's a clean way to prevent cyclical dependencies. Only recursive compiler fns need this.
|
|
347
|
+
*/
|
|
348
|
+
export type CompileFn = (
|
|
349
|
+
node: CljValue,
|
|
350
|
+
env: CompileEnv | null
|
|
351
|
+
) => CompiledExpr | null
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* A mutable box. Allocated at compile time.
|
|
355
|
+
* Read by compiled symbols that reference the slot binding.
|
|
356
|
+
*/
|
|
357
|
+
export type SlotRef = { value: CljValue | null }
|
|
358
|
+
|
|
359
|
+
export type CompileEnv = {
|
|
360
|
+
bindings: Map<string, SlotRef>
|
|
361
|
+
outer: CompileEnv | null
|
|
362
|
+
loop?: {
|
|
363
|
+
slots: SlotRef[]
|
|
364
|
+
recurTarget: { args: CljValue[] | null }
|
|
365
|
+
}
|
|
366
|
+
}
|