Add Scheme forms: named let, letrec, dynamic-wind, three-tier equality
Spec (eval.sx, primitives.sx): - Named let: (let loop ((i 0)) body) — self-recursive lambda with TCO - letrec: mutually recursive local bindings with closure patching - dynamic-wind: entry/exit guards with wind stack for future continuations - eq?/eqv?/equal?: identity, atom-value, and deep structural equality Implementation (evaluator.py, async_eval.py, primitives.py): - Both sync and async evaluators implement all four forms - 33 new tests covering all forms including TCO at 10k depth Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -279,6 +279,10 @@ async def _asf_or(expr, env, ctx):
|
||||
|
||||
|
||||
async def _asf_let(expr, env, ctx):
|
||||
# Named let: (let name ((x 0) ...) body)
|
||||
if isinstance(expr[1], Symbol):
|
||||
return await _asf_named_let(expr, env, ctx)
|
||||
|
||||
bindings = expr[1]
|
||||
local = dict(env)
|
||||
if isinstance(bindings, list):
|
||||
@@ -299,6 +303,98 @@ async def _asf_let(expr, env, ctx):
|
||||
return NIL
|
||||
|
||||
|
||||
async def _asf_named_let(expr, env, ctx):
|
||||
"""Async named let: (let name ((x 0) ...) body)"""
|
||||
loop_name = expr[1].name
|
||||
bindings = expr[2]
|
||||
body = expr[3:]
|
||||
|
||||
params: list[str] = []
|
||||
inits: list = []
|
||||
|
||||
if isinstance(bindings, list):
|
||||
if bindings and isinstance(bindings[0], list):
|
||||
for binding in bindings:
|
||||
var = binding[0]
|
||||
params.append(var.name if isinstance(var, Symbol) else var)
|
||||
inits.append(binding[1])
|
||||
elif len(bindings) % 2 == 0:
|
||||
for i in range(0, len(bindings), 2):
|
||||
var = bindings[i]
|
||||
params.append(var.name if isinstance(var, Symbol) else var)
|
||||
inits.append(bindings[i + 1])
|
||||
|
||||
loop_body = body[0] if len(body) == 1 else [Symbol("begin")] + list(body)
|
||||
loop_fn = Lambda(params, loop_body, dict(env), name=loop_name)
|
||||
loop_fn.closure[loop_name] = loop_fn
|
||||
|
||||
init_vals = [await _async_trampoline(await _async_eval(init, env, ctx)) for init in inits]
|
||||
return await _async_call_lambda(loop_fn, init_vals, env, ctx)
|
||||
|
||||
|
||||
async def _asf_letrec(expr, env, ctx):
|
||||
"""Async letrec: (letrec ((name1 val1) ...) body)"""
|
||||
bindings = expr[1]
|
||||
local = dict(env)
|
||||
|
||||
names: list[str] = []
|
||||
val_exprs: list = []
|
||||
|
||||
if isinstance(bindings, list):
|
||||
if bindings and isinstance(bindings[0], list):
|
||||
for binding in bindings:
|
||||
var = binding[0]
|
||||
vname = var.name if isinstance(var, Symbol) else var
|
||||
names.append(vname)
|
||||
val_exprs.append(binding[1])
|
||||
local[vname] = NIL
|
||||
elif len(bindings) % 2 == 0:
|
||||
for i in range(0, len(bindings), 2):
|
||||
var = bindings[i]
|
||||
vname = var.name if isinstance(var, Symbol) else var
|
||||
names.append(vname)
|
||||
val_exprs.append(bindings[i + 1])
|
||||
local[vname] = NIL
|
||||
|
||||
values = [await _async_trampoline(await _async_eval(ve, local, ctx)) for ve in val_exprs]
|
||||
for name, val in zip(names, values):
|
||||
local[name] = val
|
||||
for val in values:
|
||||
if isinstance(val, Lambda):
|
||||
for name in names:
|
||||
val.closure[name] = local[name]
|
||||
|
||||
for body_expr in expr[2:-1]:
|
||||
await _async_trampoline(await _async_eval(body_expr, local, ctx))
|
||||
if len(expr) > 2:
|
||||
return _AsyncThunk(expr[-1], local, ctx)
|
||||
return NIL
|
||||
|
||||
|
||||
async def _asf_dynamic_wind(expr, env, ctx):
|
||||
"""Async dynamic-wind: (dynamic-wind before body after)"""
|
||||
before = await _async_trampoline(await _async_eval(expr[1], env, ctx))
|
||||
body_fn = await _async_trampoline(await _async_eval(expr[2], env, ctx))
|
||||
after = await _async_trampoline(await _async_eval(expr[3], env, ctx))
|
||||
|
||||
async def _call_thunk(fn):
|
||||
if isinstance(fn, Lambda):
|
||||
return await _async_trampoline(await _async_call_lambda(fn, [], env, ctx))
|
||||
if callable(fn):
|
||||
r = fn()
|
||||
if inspect.iscoroutine(r):
|
||||
return await r
|
||||
return r
|
||||
raise EvalError(f"dynamic-wind: expected thunk, got {type(fn).__name__}")
|
||||
|
||||
await _call_thunk(before)
|
||||
try:
|
||||
result = await _call_thunk(body_fn)
|
||||
finally:
|
||||
await _call_thunk(after)
|
||||
return result
|
||||
|
||||
|
||||
async def _asf_lambda(expr, env, ctx):
|
||||
params_expr = expr[1]
|
||||
param_names = []
|
||||
@@ -467,6 +563,7 @@ _ASYNC_SPECIAL_FORMS: dict[str, Any] = {
|
||||
"or": _asf_or,
|
||||
"let": _asf_let,
|
||||
"let*": _asf_let,
|
||||
"letrec": _asf_letrec,
|
||||
"lambda": _asf_lambda,
|
||||
"fn": _asf_lambda,
|
||||
"define": _asf_define,
|
||||
@@ -481,6 +578,7 @@ _ASYNC_SPECIAL_FORMS: dict[str, Any] = {
|
||||
"quasiquote": _asf_quasiquote,
|
||||
"->": _asf_thread_first,
|
||||
"set!": _asf_set_bang,
|
||||
"dynamic-wind": _asf_dynamic_wind,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user