Lines
58.8 %
Functions
24.19 %
Branches
100 %
use tracing::debug;
use crate::ast::{Expr, LambdaParams};
use crate::error::{Error, Result};
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
use super::context::CompileContext;
use super::emit::FunctionEmitter;
use super::expr::{
compile_body, compile_call, compile_expr, compile_for_effect, compile_nil, compile_quoted_expr,
compile_string, eval_value, format_expr, resolve_arg,
};
pub fn call(symbols: &mut SymbolTable, name: &str, args: &[Expr]) -> Result<Expr> {
debug!(special_form = %name, args = args.len(), "calling special form");
match name {
"QUOTE" => quote(args),
"DEFUN" => defun(symbols, args),
"LAMBDA" => lambda(args),
"FUNCTION" => function_form(symbols, args),
"DEFVAR" => defvar(symbols, args),
"DEFPARAMETER" => defparameter(symbols, args),
"DESCRIBE" => describe(symbols, args),
"FUNCALL" => funcall(symbols, args),
"APPLY" => apply(symbols, args),
"BEGIN" => begin_form(symbols, args),
"AND" => and_form(symbols, args),
"OR" => or_form(symbols, args),
"IF" => if_form(symbols, args),
"COND" => cond_form(symbols, args),
"LET" => let_form(symbols, args),
"LET*" => let_star(symbols, args),
"DO" => do_form(symbols, args),
"DO*" => do_star_form(symbols, args),
"DEFMACRO" => defmacro(symbols, args),
"MACROEXPAND-1" => macroexpand_1(symbols, args),
"MACROEXPAND" => macroexpand(symbols, args),
"COMPILE" => compile_form(symbols, args),
"EVAL" => eval_form(symbols, args),
"LABELS" => labels_form(symbols, args),
"DOLIST" => dolist_form(symbols, args),
"DEFSTRUCT" => defstruct(symbols, args),
"SETF" => setf(symbols, args),
_ => Err(Error::Compile(format!(
"special form '{name}' not yet implemented"
))),
}
// WASM compile dispatch
pub(super) fn compile(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
name: &str,
args: &[Expr],
) -> Result<()> {
debug!(special_form = %name, args = args.len(), "compiling special form");
"QUOTE" => compile_quote(ctx, emit, args),
"DEFUN" => compile_defun(ctx, emit, symbols, args),
"LAMBDA" => compile_lambda_form(ctx, emit, args),
"FUNCTION" => compile_function_form(ctx, emit, symbols, args),
"DEFVAR" => compile_defvar(ctx, emit, symbols, args),
"DEFPARAMETER" => compile_defparam(ctx, emit, symbols, args),
"DESCRIBE" => compile_describe(ctx, emit, symbols, args),
"FUNCALL" => compile_funcall(ctx, emit, symbols, args),
"APPLY" => compile_apply_form(ctx, emit, symbols, args),
"BEGIN" => compile_begin(ctx, emit, symbols, args),
"AND" => compile_and(ctx, emit, symbols, args),
"OR" => compile_or(ctx, emit, symbols, args),
"IF" => compile_if(ctx, emit, symbols, args),
"COND" => compile_cond(ctx, emit, symbols, args),
"LET" => compile_let(ctx, emit, symbols, args),
"LET*" => compile_let_star(ctx, emit, symbols, args),
"DO" => compile_do(ctx, emit, symbols, args),
"DO*" => compile_do_star(ctx, emit, symbols, args),
"DEFMACRO" => compile_defmacro_form(ctx, emit, symbols, args),
"MACROEXPAND-1" => compile_macroexpand_1_form(ctx, emit, symbols, args),
"MACROEXPAND" => compile_macroexpand_form(ctx, emit, symbols, args),
"COMPILE" => compile_compile_form(ctx, emit, symbols, args),
"EVAL" => compile_eval_form(ctx, emit, symbols, args),
"LABELS" => compile_labels(ctx, emit, symbols, args),
"DOLIST" => compile_dolist(ctx, emit, symbols, args),
"DEFSTRUCT" => compile_defstruct(ctx, emit, symbols, args),
"SETF" => compile_setf(ctx, emit, symbols, args),
fn compile_quote(
if args.len() != 1 {
return Err(Error::Arity {
name: "quote".to_string(),
expected: 1,
actual: args.len(),
});
compile_quoted_expr(ctx, emit, &args[0])
fn compile_defun(
let result = defun(symbols, args)?;
compile_expr(ctx, emit, symbols, &result)
fn compile_lambda_form(
let result = lambda(args)?;
compile_string(ctx, emit, &format_expr(&result));
Ok(())
fn compile_function_form(
let result = function_form(symbols, args)?;
fn compile_defvar(
let result = defvar(symbols, args)?;
fn compile_defparam(
let result = defparameter(symbols, args)?;
fn compile_describe(
let result = describe(symbols, args)?;
fn compile_funcall(
if args.is_empty() {
return Err(Error::Compile(
"funcall requires at least a function argument".to_string(),
));
compile_call(ctx, emit, symbols, args)
fn compile_apply_form(
if args.len() < 2 {
"apply requires at least a function and one argument".to_string(),
let leading = &args[..args.len() - 1];
let last = eval_value(symbols, &args[args.len() - 1])?;
let spread = match &last {
Expr::Quote(inner) => match inner.as_ref() {
Expr::List(elems) => elems.clone(),
Expr::Nil => vec![],
_ => {
"apply: last argument must be a quoted list".to_string(),
},
let mut all_args: Vec<Expr> = Vec::with_capacity(leading.len() + spread.len());
all_args.push(resolve_apply_designator(symbols, &leading[0])?);
for arg in &leading[1..] {
all_args.push(eval_value(symbols, arg)?);
all_args.extend(spread);
compile_call(ctx, emit, symbols, &all_args)
fn compile_if(
if args.len() < 2 || args.len() > 3 {
"IF requires a test, a then-form, and an optional else-form".to_string(),
let test = eval_value(symbols, &args[0])?;
if is_truthy(&test) {
compile_expr(ctx, emit, symbols, &args[1])
} else if args.len() == 3 {
compile_expr(ctx, emit, symbols, &args[2])
} else {
compile_nil(emit);
fn compile_begin(
return Ok(());
compile_body(ctx, emit, symbols, args)
fn compile_and(
return compile_expr(ctx, emit, symbols, &Expr::Bool(true));
let mut last = Expr::Bool(true);
for arg in args {
let value = eval_value(symbols, arg)?;
if !is_truthy(&value) {
return compile_expr(ctx, emit, symbols, &value);
last = value;
compile_expr(ctx, emit, symbols, &last)
fn compile_or(
let mut last = Expr::Nil;
if is_truthy(&value) {
fn compile_cond(
for clause in args {
let elems = clause.as_list().ok_or_else(|| {
Error::Compile(format!("COND: clause must be a list, got {clause:?}"))
})?;
if elems.is_empty() {
return Err(Error::Compile("COND: empty clause".to_string()));
let test = eval_value(symbols, &elems[0])?;
if elems.len() == 1 {
return compile_expr(ctx, emit, symbols, &test);
return compile_body(ctx, emit, symbols, &elems[1..]);
fn compile_let(
"LET requires a bindings list and at least one body form".to_string(),
let bindings = parse_bindings("LET", &args[0])?;
let resolved: Vec<(String, Expr)> = bindings
.into_iter()
.map(|(name, init)| {
let val = match init {
Some(expr) => eval_value(symbols, &expr)?,
None => Expr::Nil,
Ok((name, val))
})
.collect::<Result<_>>()?;
let mut local = symbols.clone();
for (name, val) in resolved {
local.define(Symbol::new(&name, SymbolKind::Variable).with_value(val));
compile_body(ctx, emit, &mut local, &args[1..])
fn compile_let_star(
"LET* requires a bindings list and at least one body form".to_string(),
let bindings = parse_bindings("LET*", &args[0])?;
for (name, init) in bindings {
Some(expr) => eval_value(&mut local, &expr)?,
fn compile_do(
"DO requires a variable list and an end clause".to_string(),
let vars = parse_do_vars("DO", &args[0])?;
let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
let end_test = end_test.clone();
let result_forms: Vec<Expr> = result_forms.to_vec();
let body = &args[2..];
let init_values: Vec<(String, Expr, Option<Expr>)> = vars
.map(|v| {
let val = match v.init {
Ok((v.name, val, v.step))
let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
for (name, val, step) in &init_values {
local.define(Symbol::new(name, SymbolKind::Variable).with_value(val.clone()));
stepped.push((name.clone(), step.clone()));
loop {
let test = eval_value(&mut local, &end_test)?;
return if result_forms.is_empty() {
compile_body(ctx, emit, &mut local, &result_forms)
for expr in body {
compile_for_effect(ctx, emit, &mut local, expr)?;
let new_values: Vec<(&str, Expr)> = stepped
.iter()
.filter_map(|(name, step)| {
step.as_ref()
.map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
for (name, val) in new_values {
local
.lookup_mut(name)
.expect("DO variable must exist")
.set_value(val);
fn compile_do_star(
"DO* requires a variable list and an end clause".to_string(),
let vars = parse_do_vars("DO*", &args[0])?;
let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
for v in vars {
local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
stepped.push((v.name, v.step));
for (name, step) in &stepped {
if let Some(s) = step {
let val = eval_value(&mut local, s)?;
.expect("DO* variable must exist")
fn compile_defmacro_form(
let result = defmacro(symbols, args)?;
fn compile_macroexpand_1_form(
let result = macroexpand_1(symbols, args)?;
fn compile_macroexpand_form(
let result = macroexpand(symbols, args)?;
fn compile_compile_form(
let result = compile_form(symbols, args)?;
fn compile_eval_form(
name: "eval".to_string(),
let resolved = resolve_arg(symbols, &args[0])?;
match &resolved {
Expr::Quote(inner) => compile_expr(ctx, emit, symbols, inner),
_ => compile_expr(ctx, emit, symbols, &resolved),
// Eval dispatch
fn quote(args: &[Expr]) -> Result<Expr> {
Ok(args[0].clone())
fn lambda(args: &[Expr]) -> Result<Expr> {
"LAMBDA requires a parameter list and body".to_string(),
let params = parse_lambda_params("LAMBDA", &args[0])?;
let body = if args.len() == 2 {
args[1].clone()
let mut forms = Vec::with_capacity(args.len());
forms.push(Expr::Symbol("BEGIN".to_string()));
forms.extend_from_slice(&args[1..]);
Expr::List(forms)
Ok(Expr::Lambda(params, Box::new(body)))
fn defun(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() < 3 {
"DEFUN requires a name, parameter list, and body".to_string(),
let name = match &args[0] {
Expr::Symbol(s) => s.clone(),
other => {
return Err(Error::Compile(format!(
"DEFUN: expected symbol name, got {other:?}"
)));
let params = parse_lambda_params("DEFUN", &args[1])?;
let (doc, body_idx) = match args.get(2) {
Some(Expr::String(s)) if args.len() > 3 => (Some(s.clone()), 3),
_ => (None, 2),
if body_idx >= args.len() {
return Err(Error::Compile("DEFUN: missing body".to_string()));
let body = if args.len() == body_idx + 1 {
args[body_idx].clone()
let mut forms = Vec::with_capacity(args.len() - body_idx + 1);
forms.extend_from_slice(&args[body_idx..]);
let lambda = Expr::Lambda(params, Box::new(body));
if let Some(sym) = symbols.lookup_mut(&name) {
sym.set_function(lambda);
if let Some(d) = doc {
sym.set_doc(d);
let mut sym = Symbol::new(&name, SymbolKind::Variable).with_function(lambda);
sym = sym.with_doc(d);
symbols.define(sym);
Ok(Expr::Quote(Box::new(Expr::Symbol(name))))
fn function_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "FUNCTION".to_string(),
match &args[0] {
Expr::Symbol(name) => {
let func = symbols
.lookup(name)
.and_then(|s| s.function().cloned())
.ok_or_else(|| Error::Compile(format!("'{name}' has no function definition")))?;
Ok(func)
Expr::List(elems) if matches!(elems.first(), Some(Expr::Symbol(s)) if s == "LAMBDA") => {
lambda(&elems[1..])
_ => Err(Error::Compile(
"FUNCTION: argument must be a symbol or lambda expression".to_string(),
)),
fn defvar(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
"DEFVAR requires at least a name".to_string(),
"DEFVAR: expected symbol name, got {other:?}"
let initial = args.get(1).map(|e| eval_value(symbols, e)).transpose()?;
let doc = match args.get(2) {
Some(Expr::String(s)) => Some(s.clone()),
_ => None,
if sym.value().is_none()
&& let Some(val) = initial
{
sym.set_value(val);
let mut sym = Symbol::new(&name, SymbolKind::Variable);
if let Some(val) = initial {
sym = sym.with_value(val);
fn defparameter(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
"DEFPARAMETER requires a name and initial value".to_string(),
"DEFPARAMETER: expected symbol name, got {other:?}"
let value = eval_value(symbols, &args[1])?;
sym.set_value(value);
let mut sym = Symbol::new(&name, SymbolKind::Variable).with_value(value);
fn describe(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "DESCRIBE".to_string(),
"DESCRIBE: argument must be a symbol".to_string(),
let sym = symbols
.lookup(&name)
.ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
let mut lines = Vec::new();
let kind_str = match sym.kind() {
SymbolKind::Macro => "a macro",
_ if sym.function().is_some() => "a function",
SymbolKind::Variable => "a variable",
SymbolKind::Operator => "an operator",
SymbolKind::Native => "a native function",
SymbolKind::SpecialForm => "a special form",
SymbolKind::Function => "a function",
lines.push(format!("{name} is {kind_str}"));
if let Some(func) = sym.function() {
lines.push(format!(" Lambda: {}", format_expr(func)));
if let Some(val) = sym.value() {
let type_name = match val {
Expr::Nil | Expr::Bool(false) => "Nil",
Expr::Bool(true) => "Bool",
Expr::Number(_) => "Number",
Expr::String(_) => "String",
Expr::Symbol(_) => "Symbol",
Expr::Lambda(_, _) => "Lambda",
_ => "Compound",
lines.push(format!(" Value: {} ({})", format_expr(val), type_name));
if let Some(doc) = sym.doc() {
lines.push(format!(" Documentation: \"{doc}\""));
Ok(Expr::String(lines.join("\n")))
fn funcall(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
debug!(args = args.len(), "compiling funcall");
super::expr::call(symbols, args)
fn apply(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
super::expr::call(symbols, &all_args)
fn resolve_apply_designator(symbols: &mut SymbolTable, arg: &Expr) -> Result<Expr> {
match arg {
if sym.value().is_some() {
eval_value(symbols, arg)
Ok(Expr::Symbol(name.clone()))
_ => eval_value(symbols, arg),
fn compile_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "compile".to_string(),
Expr::Symbol(s) => s,
"compile: argument must be a quoted symbol".to_string(),
Expr::Symbol(_) => {
Expr::Symbol(s) => {
return compile_form_with_name(symbols, s);
compile_form_with_name(symbols, name)
fn compile_form_with_name(symbols: &mut SymbolTable, name: &str) -> Result<Expr> {
debug!(function = %name, "compiling compile");
.ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
if sym.function().is_none() {
"compile: '{name}' is not a function"
Ok(Expr::Quote(Box::new(Expr::Symbol(name.to_string()))))
fn eval_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
let resolved = eval_value(symbols, &args[0])?;
debug!(expr = ?resolved, "compiling eval");
Expr::Quote(inner) => eval_value(symbols, inner),
_ => eval_value(symbols, &resolved),
fn is_truthy(expr: &Expr) -> bool {
!matches!(expr, Expr::Nil | Expr::Bool(false))
fn if_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
eval_value(symbols, &args[1])
eval_value(symbols, &args[2])
Ok(Expr::Nil)
fn begin_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
return Ok(Expr::Nil);
eval_body(symbols, args)
fn and_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
return Ok(Expr::Bool(true));
return Ok(value);
Ok(last)
fn or_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
fn cond_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
return Ok(test);
return eval_body(symbols, &elems[1..]);
fn eval_body(symbols: &mut SymbolTable, body: &[Expr]) -> Result<Expr> {
for expr in &body[..body.len() - 1] {
eval_value(symbols, expr)?;
eval_value(symbols, body.last().unwrap())
fn let_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
let mut local_symbols = symbols.clone();
local_symbols.define(Symbol::new(&name, SymbolKind::Variable).with_value(val));
eval_body(&mut local_symbols, &args[1..])
fn let_star(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
Some(expr) => eval_value(&mut local_symbols, &expr)?,
fn parse_bindings(context: &str, expr: &Expr) -> Result<Vec<(String, Option<Expr>)>> {
let list = match expr {
Expr::List(elems) => elems,
"{context}: expected bindings list, got {expr:?}"
list.iter()
.map(|binding| match binding {
Expr::Symbol(name) => Ok((name.clone(), None)),
Expr::List(elems) if elems.len() == 1 => {
let name = elems[0].as_symbol().ok_or_else(|| {
Error::Compile(format!(
"{context}: binding name must be a symbol, got {:?}",
elems[0]
))
Ok((name.to_string(), None))
Expr::List(elems) if elems.len() == 2 => {
Ok((name.to_string(), Some(elems[1].clone())))
"{context}: malformed binding: {binding:?}"
.collect()
struct DoVar {
name: String,
init: Option<Expr>,
step: Option<Expr>,
fn parse_do_vars(context: &str, expr: &Expr) -> Result<Vec<DoVar>> {
"{context}: expected variable list, got {expr:?}"
.map(|spec| match spec {
Expr::Symbol(name) => Ok(DoVar {
name: name.clone(),
init: None,
step: None,
}),
Expr::List(elems) if !elems.is_empty() && elems.len() <= 3 => {
"{context}: variable name must be a symbol, got {:?}",
Ok(DoVar {
name: name.to_string(),
init: elems.get(1).cloned(),
step: elems.get(2).cloned(),
"{context}: malformed variable spec: {spec:?}"
fn parse_end_clause<'a>(context: &str, expr: &'a Expr) -> Result<(&'a Expr, &'a [Expr])> {
let elems = expr.as_list().ok_or_else(|| {
"{context}: end clause must be a list, got {expr:?}"
"{context}: end clause must have a test form"
Ok((&elems[0], &elems[1..]))
fn do_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
eval_body(&mut local, &result_forms)
eval_value(&mut local, expr)?;
fn do_star_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
fn parse_param_list(context: &str, expr: &Expr) -> Result<Vec<String>> {
let params = parse_lambda_params(context, expr)?;
if !params.optional.is_empty()
|| params.rest.is_some()
|| !params.key.is_empty()
|| !params.aux.is_empty()
"{context}: lambda list keywords not supported in this context"
Ok(params.required)
fn parse_lambda_params(context: &str, expr: &Expr) -> Result<LambdaParams> {
match expr {
Expr::List(ps) => {
let mut params = LambdaParams::simple(Vec::new());
let mut state = ParseState::Required;
for param in ps {
if let Some(sym) = param.as_symbol() {
if sym.eq_ignore_ascii_case("&optional") {
if matches!(
state,
ParseState::Optional
| ParseState::Rest
| ParseState::Key
| ParseState::Aux
) {
"{context}: &optional after {}",
state.name()
state = ParseState::Optional;
continue;
if sym.eq_ignore_ascii_case("&rest") {
if matches!(state, ParseState::Rest | ParseState::Key | ParseState::Aux) {
"{context}: &rest after {}",
state = ParseState::Rest;
if sym.eq_ignore_ascii_case("&key") {
if matches!(state, ParseState::Key | ParseState::Aux) {
"{context}: &key after {}",
state = ParseState::Key;
if sym.eq_ignore_ascii_case("&aux") {
if matches!(state, ParseState::Aux) {
return Err(Error::Compile(format!("{context}: &aux after aux")));
state = ParseState::Aux;
match state {
ParseState::Required => {
let name = param
.as_symbol()
.ok_or_else(|| {
"{context}: required parameter must be a symbol, got {param:?}"
})?
.to_string();
params.required.push(name);
ParseState::Optional => {
let (name, default) = parse_optional_param(context, param)?;
params.optional.push((name, default));
ParseState::Rest => {
"{context}: &rest parameter must be a symbol, got {param:?}"
if params.rest.is_some() {
"{context}: multiple &rest parameters"
params.rest = Some(name);
state = ParseState::PostRest;
ParseState::PostRest => {
"{context}: parameters after &rest must use &key or &aux"
ParseState::Key => {
params.key.push((name, default));
ParseState::Aux => {
params.aux.push((name, default));
Ok(params)
other => Err(Error::Compile(format!(
"{context}: expected parameter list, got {other:?}"
#[derive(Debug, Clone)]
enum ParseState {
Required,
Optional,
Rest,
PostRest,
Key,
Aux,
impl ParseState {
fn name(&self) -> &'static str {
match self {
ParseState::Required => "required",
ParseState::Optional => "&optional",
ParseState::Rest => "&rest",
ParseState::PostRest => "post-&rest",
ParseState::Key => "&key",
ParseState::Aux => "&aux",
fn parse_optional_param(context: &str, param: &Expr) -> Result<(String, Option<Expr>)> {
match param {
let name = elems[0]
"{context}: parameter name must be a symbol, got {:?}",
Ok((name, Some(elems[1].clone())))
"{context}: optional parameter must be a symbol or (symbol default), got {param:?}"
// Macros
fn defmacro(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
"DEFMACRO requires a name, parameter list, and body".to_string(),
"DEFMACRO: expected symbol name, got {other:?}"
let params = parse_lambda_params("DEFMACRO", &args[1])?;
if !params.aux.is_empty() {
"DEFMACRO: &aux is not yet supported".to_string(),
return Err(Error::Compile("DEFMACRO: missing body".to_string()));
let mut sym = Symbol::new(&name, SymbolKind::Macro).with_function(lambda);
fn macro_head_name(expr: &Expr) -> Option<&str> {
Expr::Symbol(name) => Some(name),
fn macroexpand_1_impl(symbols: &mut SymbolTable, form: &Expr) -> Result<Expr> {
match form {
Expr::List(elems) if !elems.is_empty() => {
if let Some(name) = macro_head_name(&elems[0])
&& let Some(sym) = symbols.lookup(name)
&& sym.kind() == SymbolKind::Macro
&& let Some(Expr::Lambda(params, body)) = sym.function().cloned()
return super::expr::expand_macro(symbols, ¶ms, &body, &elems[1..]);
Ok(form.clone())
_ => Ok(form.clone()),
fn macroexpand_1(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "MACROEXPAND-1".to_string(),
let form = resolve_arg(symbols, &args[0])?;
macroexpand_1_impl(symbols, &form)
fn macroexpand(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "MACROEXPAND".to_string(),
let mut form = resolve_arg(symbols, &args[0])?;
let expanded = macroexpand_1_impl(symbols, &form)?;
if expanded == form {
break;
form = expanded;
Ok(form)
// Labels
struct LabelDef {
params: Vec<String>,
body: Expr,
fn parse_labels_defs(args: &[Expr]) -> Result<(Vec<LabelDef>, &[Expr])> {
"LABELS requires a definitions list and at least one body form".to_string(),
let defs_list = args[0].as_list().ok_or_else(|| {
"LABELS: expected definitions list, got {:?}",
args[0]
let defs = defs_list
.map(|def| {
let elems = def.as_list().ok_or_else(|| {
Error::Compile(format!("LABELS: expected function definition, got {def:?}"))
if elems.len() < 3 {
"LABELS: each definition needs a name, params, and body".to_string(),
"LABELS: expected function name, got {:?}",
let params = parse_param_list("LABELS", &elems[1])?;
let body = if elems.len() == 3 {
elems[2].clone()
let mut forms = Vec::with_capacity(elems.len() - 1);
forms.extend_from_slice(&elems[2..]);
Ok(LabelDef { name, params, body })
.collect::<Result<Vec<_>>>()?;
Ok((defs, &args[1..]))
fn define_labels(symbols: &mut SymbolTable, defs: Vec<LabelDef>) -> SymbolTable {
for def in defs {
let lambda = Expr::Lambda(LambdaParams::simple(def.params), Box::new(def.body));
local.define(Symbol::new(&def.name, SymbolKind::Function).with_function(lambda));
fn labels_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
let (defs, body) = parse_labels_defs(args)?;
let mut local = define_labels(symbols, defs);
eval_body(&mut local, body)
fn compile_labels(
compile_body(ctx, emit, &mut local, body)
// DOLIST implementation
fn dolist_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
"DOLIST requires a variable specification and at least one body form".to_string(),
let var_spec = args[0].as_list().ok_or_else(|| {
"DOLIST: expected variable specification list, got {:?}",
if var_spec.len() < 2 || var_spec.len() > 3 {
"DOLIST: variable specification must be (var list) or (var list result)".to_string(),
let var_name = var_spec[0].as_symbol().ok_or_else(|| {
"DOLIST: variable must be a symbol, got {:?}",
var_spec[0]
let list_expr = &var_spec[1];
let result_expr = var_spec.get(2).cloned();
let body = &args[1..];
// Evaluate the list expression
let list_value = eval_value(symbols, list_expr)?;
let elements = match &list_value {
"DOLIST: list expression must evaluate to a list".to_string(),
let saved_loop_var = symbols.lookup(var_name).cloned();
for element in elements {
symbols.define(Symbol::new(var_name, SymbolKind::Variable).with_value(element));
if let Some(saved) = saved_loop_var {
symbols.define(saved);
symbols.remove(var_name);
// Return result expression if provided, otherwise nil
if let Some(result) = result_expr {
eval_value(symbols, &result)
fn compile_dolist(
compile_for_effect(ctx, emit, symbols, expr)?;
// Compile result expression if provided, otherwise nil
// DEFSTRUCT implementation
fn defstruct(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
"DEFSTRUCT requires a structure name".to_string(),
let name = args[0].as_symbol().ok_or_else(|| {
"DEFSTRUCT: expected structure name, got {:?}",
let fields: Vec<String> = args[1..]
.map(|arg| {
arg.as_symbol()
.map(std::string::ToString::to_string)
Error::Compile(format!("DEFSTRUCT: expected field name, got {arg:?}"))
symbols.define_struct_fields(name.to_string(), fields.clone());
// Generate constructor function: make-<name>
let constructor_name = format!("MAKE-{name}");
let constructor_params = LambdaParams {
required: Vec::new(),
optional: Vec::new(),
rest: None,
key: fields.iter().map(|field| (field.clone(), None)).collect(),
aux: Vec::new(),
let constructor_body = {
// Build a list to collect field values from keyword arguments
// The &key parameters will become local variables, so we just reference them directly
let field_values: Vec<Expr> = fields
.map(|field| Expr::Symbol(field.clone()))
.collect();
Expr::List(vec![
Expr::Symbol("MAKE-STRUCT-INSTANCE".to_string()),
Expr::Quote(Box::new(Expr::String(name.to_string()))),
Expr::Quote(Box::new(Expr::List(
fields.iter().map(|f| Expr::String(f.clone())).collect(),
Expr::List(
vec![Expr::Symbol("LIST".to_string())]
.chain(field_values)
.collect(),
),
])
let constructor = Expr::Lambda(constructor_params, Box::new(constructor_body));
symbols.define(
Symbol::new(&constructor_name, SymbolKind::Function).with_function(constructor.clone()),
);
// Generate accessor functions: <name>-<field>
for field in &fields {
let accessor_name = format!("{name}-{field}");
let accessor_params = LambdaParams::simple(vec!["INSTANCE".to_string()]);
let accessor_body = Expr::List(vec![
Expr::Symbol("STRUCT-FIELD".to_string()),
Expr::Symbol("INSTANCE".to_string()),
Expr::Quote(Box::new(Expr::String(field.clone()))),
]);
let accessor = Expr::Lambda(accessor_params, Box::new(accessor_body));
symbols.define(Symbol::new(&accessor_name, SymbolKind::Function).with_function(accessor));
// Generate setf function: (setf <name>-<field>)
let setf_accessor_name = format!("(SETF {accessor_name})");
let setf_params =
LambdaParams::simple(vec!["INSTANCE".to_string(), "NEW-VALUE".to_string()]);
let setf_body = Expr::List(vec![
Expr::Symbol("STRUCT-SET-FIELD".to_string()),
Expr::Symbol("NEW-VALUE".to_string()),
let setf_function = Expr::Lambda(setf_params, Box::new(setf_body));
Symbol::new(&setf_accessor_name, SymbolKind::Function).with_function(setf_function),
// Generate predicate function: <name>-p
let predicate_name = format!("{name}-P");
let predicate_params = LambdaParams::simple(vec!["OBJECT".to_string()]);
let predicate_body = Expr::List(vec![
Expr::Symbol("STRUCT-P".to_string()),
Expr::Symbol("OBJECT".to_string()),
let predicate = Expr::Lambda(predicate_params, Box::new(predicate_body));
symbols.define(Symbol::new(&predicate_name, SymbolKind::Function).with_function(predicate));
fn compile_defstruct(
let result = defstruct(symbols, args)?;
// SETF implementation - generalized assignment
fn setf(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if !args.len().is_multiple_of(2) {
"SETF requires an even number of arguments (place value pairs)".to_string(),
// Process each place-value pair
let mut last_value = Expr::Nil;
for pair in args.chunks(2) {
let place = &pair[0];
let value = eval_value(symbols, &pair[1])?;
last_value = set_place(symbols, place, value)?;
Ok(last_value)
fn set_place(symbols: &mut SymbolTable, place: &Expr, value: Expr) -> Result<Expr> {
match place {
// Simple variable assignment
if let Some(sym) = symbols.lookup_mut(name) {
sym.set_value(value.clone());
// Define new variable
symbols.define(Symbol::new(name, SymbolKind::Variable).with_value(value.clone()));
Ok(value)
// Function call - check if it's a struct accessor
Expr::List(exprs) if !exprs.is_empty() => {
if let Expr::Symbol(func_name) = &exprs[0] {
// Look for setf function for this accessor
let setf_name = format!("(SETF {func_name})");
if symbols.contains(&setf_name) {
// Call the setf function with the arguments and the new value
let mut setf_args = exprs[1..].to_vec();
setf_args.push(value.clone());
let call_args: Vec<Expr> = [Expr::Symbol(setf_name)]
.chain(setf_args)
super::expr::call(symbols, &call_args)
Err(Error::Compile(format!(
"No setf method defined for accessor '{func_name}'"
)))
Err(Error::Compile(
"Invalid place in SETF - expected function call".to_string(),
"Invalid place in SETF: {}",
format_expr(place)
fn compile_setf(
let result = setf(symbols, args)?;