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,58 @@
|
|
|
1
|
+
import { define, makeEnv } from './env'
|
|
2
|
+
import { cljNativeFunction, cljNil } from './factories'
|
|
3
|
+
import { arithmeticFunctions } from './stdlib/arithmetic'
|
|
4
|
+
import { atomFunctions } from './stdlib/atoms'
|
|
5
|
+
import { collectionFunctions } from './stdlib/collections'
|
|
6
|
+
import { errorFunctions } from './stdlib/errors'
|
|
7
|
+
import { hofFunctions } from './stdlib/hof'
|
|
8
|
+
import { metaFunctions } from './stdlib/meta'
|
|
9
|
+
import { predicateFunctions } from './stdlib/predicates'
|
|
10
|
+
import { regexFunctions } from './stdlib/regex'
|
|
11
|
+
import { stringFunctions } from './stdlib/strings'
|
|
12
|
+
import { transducerFunctions } from './stdlib/transducers'
|
|
13
|
+
import { utilFunctions } from './stdlib/utils'
|
|
14
|
+
import { valueToString } from './transformations'
|
|
15
|
+
import { type CljValue, type Env } from './types'
|
|
16
|
+
|
|
17
|
+
const nativeFunctions = {
|
|
18
|
+
...arithmeticFunctions,
|
|
19
|
+
...atomFunctions,
|
|
20
|
+
...collectionFunctions,
|
|
21
|
+
...errorFunctions,
|
|
22
|
+
...predicateFunctions,
|
|
23
|
+
...hofFunctions,
|
|
24
|
+
...metaFunctions,
|
|
25
|
+
...transducerFunctions,
|
|
26
|
+
...regexFunctions,
|
|
27
|
+
...stringFunctions,
|
|
28
|
+
...utilFunctions,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function loadCoreFunctions(env: Env, output?: (text: string) => void) {
|
|
32
|
+
for (const [key, value] of Object.entries(nativeFunctions)) {
|
|
33
|
+
define(key, value, env)
|
|
34
|
+
}
|
|
35
|
+
const emit = output ?? ((text: string) => console.log(text))
|
|
36
|
+
define(
|
|
37
|
+
'println',
|
|
38
|
+
cljNativeFunction('println', (...args: CljValue[]) => {
|
|
39
|
+
emit(args.map(valueToString).join(' '))
|
|
40
|
+
return cljNil()
|
|
41
|
+
}),
|
|
42
|
+
env
|
|
43
|
+
)
|
|
44
|
+
define(
|
|
45
|
+
'print',
|
|
46
|
+
cljNativeFunction('print', (...args: CljValue[]) => {
|
|
47
|
+
emit(args.map(valueToString).join(' '))
|
|
48
|
+
return cljNil()
|
|
49
|
+
}),
|
|
50
|
+
env
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function makeCoreEnv(output?: (text: string) => void): Env {
|
|
55
|
+
const env = makeEnv()
|
|
56
|
+
loadCoreFunctions(env, output)
|
|
57
|
+
return env
|
|
58
|
+
}
|
package/src/core/env.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { EvaluationError } from './errors'
|
|
2
|
+
import type { CljValue, Env } from './types'
|
|
3
|
+
|
|
4
|
+
class EnvError extends Error {
|
|
5
|
+
context: any
|
|
6
|
+
constructor(message: string, context: any) {
|
|
7
|
+
super(message)
|
|
8
|
+
this.context = context
|
|
9
|
+
this.name = 'EnvError'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function makeEnv(outer?: Env): Env {
|
|
14
|
+
return {
|
|
15
|
+
bindings: new Map(),
|
|
16
|
+
outer: outer ?? null,
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function lookup(name: string, env: Env): CljValue {
|
|
21
|
+
let current = env as Env | null
|
|
22
|
+
while (current) {
|
|
23
|
+
if (current.bindings.has(name)) {
|
|
24
|
+
return current.bindings.get(name)!
|
|
25
|
+
}
|
|
26
|
+
current = current.outer
|
|
27
|
+
}
|
|
28
|
+
throw new EvaluationError(`Symbol ${name} not found`, { name })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function tryLookup(name: string, env: Env): CljValue | undefined {
|
|
32
|
+
let current = env as Env | null
|
|
33
|
+
while (current) {
|
|
34
|
+
if (current.bindings.has(name)) {
|
|
35
|
+
return current.bindings.get(name)!
|
|
36
|
+
}
|
|
37
|
+
current = current.outer
|
|
38
|
+
}
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function define(name: string, value: CljValue, env: Env) {
|
|
43
|
+
env.bindings.set(name, value)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function extend(params: string[], args: CljValue[], outer: Env): Env {
|
|
47
|
+
if (params.length !== args.length) {
|
|
48
|
+
throw new EnvError('Number of parameters and arguments must match', {
|
|
49
|
+
params,
|
|
50
|
+
args,
|
|
51
|
+
outer,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
const env = makeEnv(outer)
|
|
55
|
+
for (let i = 0; i < params.length; i++) {
|
|
56
|
+
define(params[i], args[i], env)
|
|
57
|
+
}
|
|
58
|
+
return env
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getRootEnv(env: Env): Env {
|
|
62
|
+
let current = env as Env | null
|
|
63
|
+
while (current?.outer) {
|
|
64
|
+
current = current.outer
|
|
65
|
+
}
|
|
66
|
+
return current!
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getNamespaceEnv(env: Env): Env {
|
|
70
|
+
let current: Env | null = env
|
|
71
|
+
while (current) {
|
|
72
|
+
if (current.namespace) return current
|
|
73
|
+
current = current.outer
|
|
74
|
+
}
|
|
75
|
+
// fallback for un-namespaced envs for backwards compact
|
|
76
|
+
// eventually we'll remove this and require all envs to be namespaced
|
|
77
|
+
return getRootEnv(env)
|
|
78
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CljValue, Pos } from './types'
|
|
2
|
+
|
|
3
|
+
export class TokenizerError extends Error {
|
|
4
|
+
context: unknown
|
|
5
|
+
constructor(message: string, context: unknown) {
|
|
6
|
+
super(message)
|
|
7
|
+
this.name = 'TokenizerError'
|
|
8
|
+
this.context = context
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class ReaderError extends Error {
|
|
13
|
+
context: unknown
|
|
14
|
+
pos?: Pos
|
|
15
|
+
constructor(message: string, context: unknown, pos?: Pos) {
|
|
16
|
+
super(message)
|
|
17
|
+
this.name = 'ReaderError'
|
|
18
|
+
this.context = context
|
|
19
|
+
this.pos = pos
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class EvaluationError extends Error {
|
|
24
|
+
context: unknown
|
|
25
|
+
pos?: Pos
|
|
26
|
+
constructor(message: string, context: unknown, pos?: Pos) {
|
|
27
|
+
super(message)
|
|
28
|
+
this.name = 'EvaluationError'
|
|
29
|
+
this.context = context
|
|
30
|
+
this.pos = pos
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class CljThrownSignal {
|
|
35
|
+
value: CljValue
|
|
36
|
+
constructor(value: CljValue) {
|
|
37
|
+
this.value = value
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isAFunction, isEqual, isKeyword, isMap } from '../assertions'
|
|
2
|
+
import { EvaluationError } from '../errors'
|
|
3
|
+
import { cljNil } from '../factories'
|
|
4
|
+
import { printString } from '../printer'
|
|
5
|
+
import type {
|
|
6
|
+
CljFunction,
|
|
7
|
+
CljMacro,
|
|
8
|
+
CljNativeFunction,
|
|
9
|
+
CljValue,
|
|
10
|
+
Env,
|
|
11
|
+
EvaluationContext,
|
|
12
|
+
} from '../types'
|
|
13
|
+
import { bindParams, RecurSignal, resolveArity } from './arity'
|
|
14
|
+
|
|
15
|
+
export function applyFunctionWithContext(
|
|
16
|
+
fn: CljFunction | CljNativeFunction,
|
|
17
|
+
args: CljValue[],
|
|
18
|
+
ctx: EvaluationContext,
|
|
19
|
+
callEnv: Env
|
|
20
|
+
): CljValue {
|
|
21
|
+
if (fn.kind === 'native-function') {
|
|
22
|
+
// New path, native fns receive evaluation context as first argument
|
|
23
|
+
if (fn.fnWithContext) {
|
|
24
|
+
return fn.fnWithContext(ctx, callEnv, ...args)
|
|
25
|
+
}
|
|
26
|
+
return fn.fn(...args)
|
|
27
|
+
}
|
|
28
|
+
if (fn.kind === 'function') {
|
|
29
|
+
const arity = resolveArity(fn.arities, args.length)
|
|
30
|
+
let currentArgs = args
|
|
31
|
+
while (true) {
|
|
32
|
+
const localEnv = bindParams(
|
|
33
|
+
arity.params,
|
|
34
|
+
arity.restParam,
|
|
35
|
+
currentArgs,
|
|
36
|
+
fn.env,
|
|
37
|
+
ctx,
|
|
38
|
+
callEnv
|
|
39
|
+
)
|
|
40
|
+
try {
|
|
41
|
+
return ctx.evaluateForms(arity.body, localEnv)
|
|
42
|
+
} catch (e) {
|
|
43
|
+
if (e instanceof RecurSignal) {
|
|
44
|
+
currentArgs = e.args
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
throw e
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new EvaluationError(
|
|
53
|
+
`${(fn as CljValue).kind} is not a callable function`,
|
|
54
|
+
{
|
|
55
|
+
fn,
|
|
56
|
+
args,
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Invokes any IFn value — functions, native functions, keywords, and maps.
|
|
63
|
+
* Used by comp, partial, and any other HOF that needs to call an arbitrary
|
|
64
|
+
* callable without going through the full list-evaluation dispatch.
|
|
65
|
+
*/
|
|
66
|
+
export function applyCallableWithContext(
|
|
67
|
+
fn: CljValue,
|
|
68
|
+
args: CljValue[],
|
|
69
|
+
ctx: EvaluationContext,
|
|
70
|
+
callEnv: Env
|
|
71
|
+
): CljValue {
|
|
72
|
+
if (isAFunction(fn)) {
|
|
73
|
+
return applyFunctionWithContext(fn, args, ctx, callEnv)
|
|
74
|
+
}
|
|
75
|
+
if (isKeyword(fn)) {
|
|
76
|
+
const target = args[0]
|
|
77
|
+
const defaultVal = args.length > 1 ? args[1] : cljNil()
|
|
78
|
+
if (isMap(target)) {
|
|
79
|
+
const entry = target.entries.find(([k]) => isEqual(k, fn))
|
|
80
|
+
return entry ? entry[1] : defaultVal
|
|
81
|
+
}
|
|
82
|
+
return defaultVal
|
|
83
|
+
}
|
|
84
|
+
if (isMap(fn)) {
|
|
85
|
+
if (args.length === 0) {
|
|
86
|
+
throw new EvaluationError('Map used as function requires at least one argument', { fn, args })
|
|
87
|
+
}
|
|
88
|
+
const key = args[0]
|
|
89
|
+
const defaultVal = args.length > 1 ? args[1] : cljNil()
|
|
90
|
+
const entry = fn.entries.find(([k]) => isEqual(k, key))
|
|
91
|
+
return entry ? entry[1] : defaultVal
|
|
92
|
+
}
|
|
93
|
+
throw new EvaluationError(`${printString(fn)} is not a callable value`, {
|
|
94
|
+
fn,
|
|
95
|
+
args,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function applyMacroWithContext(
|
|
100
|
+
macro: CljMacro,
|
|
101
|
+
rawArgs: CljValue[],
|
|
102
|
+
ctx: EvaluationContext
|
|
103
|
+
): CljValue {
|
|
104
|
+
const arity = resolveArity(macro.arities, rawArgs.length)
|
|
105
|
+
const localEnv = bindParams(
|
|
106
|
+
arity.params,
|
|
107
|
+
arity.restParam,
|
|
108
|
+
rawArgs,
|
|
109
|
+
macro.env,
|
|
110
|
+
ctx,
|
|
111
|
+
macro.env
|
|
112
|
+
)
|
|
113
|
+
return ctx.evaluateForms(arity.body, localEnv)
|
|
114
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { isList, isMap, isSymbol, isVector } from '../assertions'
|
|
2
|
+
import { extend } from '../env'
|
|
3
|
+
import { EvaluationError } from '../errors'
|
|
4
|
+
import { cljList, cljNil } from '../factories'
|
|
5
|
+
import type {
|
|
6
|
+
Arity,
|
|
7
|
+
CljValue,
|
|
8
|
+
CljVector,
|
|
9
|
+
DestructurePattern,
|
|
10
|
+
Env,
|
|
11
|
+
EvaluationContext,
|
|
12
|
+
} from '../types'
|
|
13
|
+
import { destructureBindings } from './destructure'
|
|
14
|
+
|
|
15
|
+
export class RecurSignal {
|
|
16
|
+
args: CljValue[]
|
|
17
|
+
constructor(args: CljValue[]) {
|
|
18
|
+
this.args = args
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function parseParamVector(
|
|
23
|
+
args: CljVector,
|
|
24
|
+
env: Env
|
|
25
|
+
): { params: DestructurePattern[]; restParam: DestructurePattern | null } {
|
|
26
|
+
const ampIdx = args.value.findIndex((a) => isSymbol(a) && a.name === '&')
|
|
27
|
+
let params: DestructurePattern[] = []
|
|
28
|
+
let restParam: DestructurePattern | null = null
|
|
29
|
+
if (ampIdx === -1) {
|
|
30
|
+
params = args.value as DestructurePattern[]
|
|
31
|
+
} else {
|
|
32
|
+
const ampsCount = args.value.filter(
|
|
33
|
+
(a) => isSymbol(a) && a.name === '&'
|
|
34
|
+
).length
|
|
35
|
+
if (ampsCount > 1) {
|
|
36
|
+
throw new EvaluationError('& can only appear once', { args, env })
|
|
37
|
+
}
|
|
38
|
+
if (ampIdx !== args.value.length - 2) {
|
|
39
|
+
throw new EvaluationError('& must be second-to-last argument', {
|
|
40
|
+
args,
|
|
41
|
+
env,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
params = args.value.slice(0, ampIdx) as DestructurePattern[]
|
|
45
|
+
restParam = args.value[ampIdx + 1] as DestructurePattern
|
|
46
|
+
}
|
|
47
|
+
return { params, restParam }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function parseArities(forms: CljValue[], env: Env): Arity[] {
|
|
51
|
+
if (forms.length === 0) {
|
|
52
|
+
throw new EvaluationError(
|
|
53
|
+
'fn/defmacro requires at least a parameter vector',
|
|
54
|
+
{
|
|
55
|
+
forms,
|
|
56
|
+
env,
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (isVector(forms[0])) {
|
|
62
|
+
const paramVec = forms[0]
|
|
63
|
+
const { params, restParam } = parseParamVector(paramVec, env)
|
|
64
|
+
return [{ params, restParam, body: forms.slice(1) }]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (isList(forms[0])) {
|
|
68
|
+
const arities: Arity[] = []
|
|
69
|
+
for (const form of forms) {
|
|
70
|
+
if (!isList(form) || form.value.length === 0) {
|
|
71
|
+
throw new EvaluationError(
|
|
72
|
+
'Multi-arity clause must be a list starting with a parameter vector',
|
|
73
|
+
{ form, env }
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
const paramVec = form.value[0]
|
|
77
|
+
if (!isVector(paramVec)) {
|
|
78
|
+
throw new EvaluationError(
|
|
79
|
+
'First element of arity clause must be a parameter vector',
|
|
80
|
+
{ paramVec, env }
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
const { params, restParam } = parseParamVector(paramVec, env)
|
|
84
|
+
arities.push({ params, restParam, body: form.value.slice(1) })
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const variadicCount = arities.filter((a) => a.restParam !== null).length
|
|
88
|
+
if (variadicCount > 1) {
|
|
89
|
+
throw new EvaluationError(
|
|
90
|
+
'At most one variadic arity is allowed per function',
|
|
91
|
+
{ forms, env }
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return arities
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw new EvaluationError(
|
|
99
|
+
'fn/defmacro expects a parameter vector or arity clauses',
|
|
100
|
+
{ forms, env }
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function bindParams(
|
|
105
|
+
params: DestructurePattern[],
|
|
106
|
+
restParam: DestructurePattern | null,
|
|
107
|
+
args: CljValue[],
|
|
108
|
+
outerEnv: Env,
|
|
109
|
+
ctx: EvaluationContext,
|
|
110
|
+
bindEnv: Env
|
|
111
|
+
): Env {
|
|
112
|
+
if (restParam === null) {
|
|
113
|
+
if (args.length !== params.length) {
|
|
114
|
+
throw new EvaluationError(
|
|
115
|
+
`Arguments length mismatch: fn accepts ${params.length} arguments, but ${args.length} were provided`,
|
|
116
|
+
{ params, args, outerEnv }
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
if (args.length < params.length) {
|
|
121
|
+
throw new EvaluationError(
|
|
122
|
+
`Arguments length mismatch: fn expects at least ${params.length} arguments, but ${args.length} were provided`,
|
|
123
|
+
{ params, args, outerEnv }
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const allPairs: [string, CljValue][] = []
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < params.length; i++) {
|
|
131
|
+
allPairs.push(...destructureBindings(params[i], args[i], ctx, bindEnv))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (restParam !== null) {
|
|
135
|
+
const restArgs = args.slice(params.length)
|
|
136
|
+
let restValue: CljValue
|
|
137
|
+
if (isMap(restParam) && restArgs.length > 0) {
|
|
138
|
+
const entries: [CljValue, CljValue][] = []
|
|
139
|
+
for (let i = 0; i < restArgs.length; i += 2) {
|
|
140
|
+
entries.push([restArgs[i], restArgs[i + 1] ?? cljNil()])
|
|
141
|
+
}
|
|
142
|
+
restValue = { kind: 'map', entries }
|
|
143
|
+
} else {
|
|
144
|
+
restValue = restArgs.length > 0 ? cljList(restArgs) : cljNil()
|
|
145
|
+
}
|
|
146
|
+
allPairs.push(...destructureBindings(restParam, restValue, ctx, bindEnv))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return extend(
|
|
150
|
+
allPairs.map(([n]) => n),
|
|
151
|
+
allPairs.map(([, v]) => v),
|
|
152
|
+
outerEnv
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function resolveArity(arities: Arity[], argCount: number): Arity {
|
|
157
|
+
const exactMatch = arities.find(
|
|
158
|
+
(a) => a.restParam === null && a.params.length === argCount
|
|
159
|
+
)
|
|
160
|
+
if (exactMatch) return exactMatch
|
|
161
|
+
|
|
162
|
+
const variadicMatch = arities.find(
|
|
163
|
+
(a) => a.restParam !== null && argCount >= a.params.length
|
|
164
|
+
)
|
|
165
|
+
if (variadicMatch) return variadicMatch
|
|
166
|
+
|
|
167
|
+
const counts = arities.map((a) =>
|
|
168
|
+
a.restParam ? `${a.params.length}+` : `${a.params.length}`
|
|
169
|
+
)
|
|
170
|
+
throw new EvaluationError(
|
|
171
|
+
`No matching arity for ${argCount} arguments. Available arities: ${counts.join(', ')}`,
|
|
172
|
+
{ arities, argCount }
|
|
173
|
+
)
|
|
174
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cljMap, cljVector } from '../factories'
|
|
2
|
+
import type { CljMap, CljValue, CljVector, EvaluationContext } from '../types'
|
|
3
|
+
import type { Env } from '../types'
|
|
4
|
+
|
|
5
|
+
export function evaluateVector(
|
|
6
|
+
vector: CljVector,
|
|
7
|
+
env: Env,
|
|
8
|
+
ctx: EvaluationContext
|
|
9
|
+
): CljValue {
|
|
10
|
+
return cljVector(vector.value.map((v) => ctx.evaluate(v, env)))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function evaluateMap(
|
|
14
|
+
map: CljMap,
|
|
15
|
+
env: Env,
|
|
16
|
+
ctx: EvaluationContext
|
|
17
|
+
): CljValue {
|
|
18
|
+
let entries: [CljValue, CljValue][] = []
|
|
19
|
+
for (const [key, value] of map.entries) {
|
|
20
|
+
const evaluatedKey = ctx.evaluate(key, env)
|
|
21
|
+
const evaluatedValue = ctx.evaluate(value, env)
|
|
22
|
+
entries.push([evaluatedKey, evaluatedValue])
|
|
23
|
+
}
|
|
24
|
+
return cljMap(entries)
|
|
25
|
+
}
|