feat(eval): add special form desugaring and quasiquote

This commit is contained in:
dualfroz committed 2026-08-24 20:00:00 +02:00
1 parent 853d729e62
commit af0a0aca5b
1 file changed
+173
+173
View File
@@ -0,0 +1,173 @@
func sym(_ name: String) -> Value { .symbol(name) }
func listV(_ items: [Value]) -> Value { arrayToList(items) }
func quoteExpr(_ v: Value) -> Value { listV([sym("quote"), v]) }
func cadrOf(_ form: Value) throws -> Value {
let parts = try listToArray(form)
guard parts.count >= 2 else { throw schemeError("malformed form", [form]) }
return parts[1]
}
func desugarLet(_ form: Value, _ gensym: Gensym) throws -> Value {
let elems = try listToArray(form)
guard elems.count >= 3 else { throw schemeError("malformed let") }
if case .symbol(let name) = elems[1] {
let bindings = try listToArray(elems[2])
let body = Array(elems[3...])
var vars: [Value] = []
var inits: [Value] = []
for b in bindings {
let bp = try listToArray(b)
vars.append(bp[0])
inits.append(bp.count > 1 ? bp[1] : .unspecified)
}
let lambdaExpr = listV([sym("lambda"), listV(vars)] + body)
let letrecBindings = listV([listV([sym(name), lambdaExpr])])
let call = listV([sym(name)] + inits)
return listV([sym("letrec"), letrecBindings, call])
}
let bindings = try listToArray(elems[1])
let body = Array(elems[2...])
var vars: [Value] = []
var inits: [Value] = []
for b in bindings {
let bp = try listToArray(b)
vars.append(bp[0])
inits.append(bp.count > 1 ? bp[1] : .unspecified)
}
let lambdaExpr = listV([sym("lambda"), listV(vars)] + body)
return listV([lambdaExpr] + inits)
}
func desugarLetStar(_ form: Value) throws -> Value {
let elems = try listToArray(form)
let bindings = try listToArray(elems[1])
let body = Array(elems[2...])
if bindings.isEmpty {
return listV([listV([sym("lambda"), .null] + body)])
}
let first = bindings[0]
let rest = Array(bindings[1...])
let inner = listV([sym("let*"), listV(rest)] + body)
return listV([sym("let"), listV([first]), inner])
}
func desugarLetrec(_ form: Value) throws -> Value {
let elems = try listToArray(form)
let bindings = try listToArray(elems[1])
let body = Array(elems[2...])
var vars: [Value] = []
var setExprs: [Value] = []
for b in bindings {
let bp = try listToArray(b)
vars.append(bp[0])
setExprs.append(listV([sym("set!"), bp[0], bp.count > 1 ? bp[1] : .unspecified]))
}
let lambdaBody = setExprs + body
let lambdaExpr = listV([sym("lambda"), listV(vars)] + lambdaBody)
let args = vars.map { _ in Value.unspecified }
return listV([lambdaExpr] + args)
}
func desugarCond(_ form: Value, _ gensym: Gensym) throws -> Value {
let clauses = Array(try listToArray(form)[1...])
var result: Value = .unspecified
for clause in clauses.reversed() {
let parts = try listToArray(clause)
guard let test = parts.first else { throw schemeError("empty cond clause") }
if case .symbol(let s) = test, baseName(s) == "else" {
result = listV([sym("begin")] + Array(parts[1...]))
} else if parts.count >= 3, case .symbol(let arrow) = parts[1], baseName(arrow) == "=>" {
let temp = freshTemp(gensym)
result = listV([
sym("let"), listV([listV([sym(temp), test])]),
listV([sym("if"), sym(temp), listV([parts[2], sym(temp)]), result]),
])
} else if parts.count == 1 {
let temp = freshTemp(gensym)
result = listV([
sym("let"), listV([listV([sym(temp), test])]),
listV([sym("if"), sym(temp), sym(temp), result]),
])
} else {
let thenExpr = listV([sym("begin")] + Array(parts[1...]))
result = listV([sym("if"), test, thenExpr, result])
}
}
return result
}
func desugarCase(_ form: Value, _ gensym: Gensym) throws -> Value {
let parts = try listToArray(form)
let key = parts[1]
let clauses = Array(parts[2...])
let temp = freshTemp(gensym)
var condClauses: [Value] = []
for clause in clauses {
let cparts = try listToArray(clause)
if case .symbol(let s) = cparts[0], baseName(s) == "else" {
condClauses.append(listV([sym("else")] + Array(cparts[1...])))
} else {
let test = listV([sym("memv"), sym(temp), quoteExpr(cparts[0])])
condClauses.append(listV([test] + Array(cparts[1...])))
}
}
let condForm = listV([sym("cond")] + condClauses)
return listV([sym("let"), listV([listV([sym(temp), key])]), condForm])
}
func desugarAnd(_ form: Value) throws -> Value {
let parts = Array(try listToArray(form)[1...])
func andOf(_ xs: [Value]) -> Value {
if xs.isEmpty { return .bool(true) }
if xs.count == 1 { return xs[0] }
return listV([sym("if"), xs[0], andOf(Array(xs[1...])), .bool(false)])
}
return andOf(parts)
}
func desugarOr(_ form: Value, _ gensym: Gensym) throws -> Value {
let parts = Array(try listToArray(form)[1...])
func orOf(_ xs: [Value]) -> Value {
if xs.isEmpty { return .bool(false) }
if xs.count == 1 { return xs[0] }
let temp = freshTemp(gensym)
return listV([
sym("let"), listV([listV([sym(temp), xs[0]])]),
listV([sym("if"), sym(temp), sym(temp), orOf(Array(xs[1...]))]),
])
}
return orOf(parts)
}
func freshTemp(_ gensym: Gensym) -> String {
"t" + String(hygieneMark) + "d" + String(gensym.next())
}
func expandQuasi(_ template: Value, _ depth: Int) -> Value {
switch template {
case .pair(let p):
if case .symbol(let s) = p.car, baseName(s) == "unquote" {
if depth == 1 {
if case .pair(let inner) = p.cdr { return inner.car }
return .unspecified
}
return listV([sym("cons"), quoteExpr(sym("unquote")), expandQuasi(p.cdr, depth - 1)])
}
if case .symbol(let s) = p.car, baseName(s) == "quasiquote" {
return listV([sym("cons"), quoteExpr(sym("quasiquote")), expandQuasi(p.cdr, depth + 1)])
}
if case .pair(let carp) = p.car, case .symbol(let s) = carp.car, baseName(s) == "unquote-splicing", depth == 1 {
let spliced: Value
if case .pair(let inner) = carp.cdr { spliced = inner.car } else { spliced = .null }
return listV([sym("append"), spliced, expandQuasi(p.cdr, depth)])
}
return listV([sym("cons"), expandQuasi(p.car, depth), expandQuasi(p.cdr, depth)])
case .null:
return quoteExpr(.null)
case .symbol:
return quoteExpr(template)
default:
return template
}
}