Files
lysp/src/vm/macros.rs
T

114 lines
3.8 KiB
Rust

use std::rc::Rc;
use crate::{
error::MachineErrorAt,
vm::{
env::{Environment, Macro},
machine::Machine,
value::{ClosureValue, ConsCell, Keyword, Value},
},
};
pub trait MacroExpand: Sized {
fn macro_expand(
&self,
vm: &mut Machine,
env: &Rc<Environment>,
tail: bool,
) -> Result<Self, MachineErrorAt>;
}
impl MacroExpand for Value {
fn macro_expand(
&self,
vm: &mut Machine,
env: &Rc<Environment>,
tail: bool,
) -> Result<Self, MachineErrorAt> {
match self {
Self::Nil
| Self::Identifier(_)
| Self::Number(_)
| Self::Boolean(_)
| Self::String(_)
| Self::Keyword(_)
// | Self::OpaqueValue(_)
// | Self::BytecodeFunction(_)
| Self::Quote(_)
| Self::Closure(_)
| Self::Function(_)
| Self::NativeFunction(_)
| Self::NativeValue(_)
| Self::Vector(_)
| Self::Unquote(_) => Ok(self.clone()),
// | Self::NativeFunction(_) => Ok(self.clone()),
Self::Cons(cons) => {
let ConsCell(car, cdr) = cons.as_ref();
let car = car.macro_expand(vm, env, false)?;
let cdr = cdr.macro_expand(vm, env, true)?;
if tail {
let cons = Rc::new(ConsCell(car, cdr.clone()));
return Ok(Self::Cons(cons));
}
let (Self::Identifier(identifier), Some(args)) = (&car, cdr.as_proper_list())
else {
let cons = Rc::new(ConsCell(car, cdr.clone()));
return Ok(Self::Cons(cons));
};
let Some(mac) = env.global_macro(identifier) else {
// eprintln!("{identifier} is not a macro");
let cons = Rc::new(ConsCell(car, cdr.clone()));
return Ok(Self::Cons(cons));
};
match mac {
Macro::Native(native) => {
let result = native.invoke(vm, env, &args[..]).expect("TODO");
Ok(result)
}
Macro::Bytecode(bytecode) => {
let closure = ClosureValue { function: bytecode.clone(), upvalues: vec![] };
let result = vm.evaluate_closure_args(env, closure, &args[..])?;
Ok(result)
}
}
}
Self::Quasi(value) => {
let value = expand_quasiquote(value);
value.macro_expand(vm, env, false)
}
}
}
}
fn expand_quasiquote(value: &Value) -> Value {
match value {
Value::Nil => Value::Nil,
Value::Unquote(inner) => inner.as_ref().clone(),
Value::Cons(cons) => {
// x . y -> (cons <exp-car> <exp-cdr>)
let ConsCell(car, cdr) = cons.as_ref();
let exp_car = expand_quasiquote(car);
let exp_cdr = expand_quasiquote(cdr);
Value::Identifier("cons".into()).cons(exp_car.cons(exp_cdr.cons(Value::Nil)))
}
Value::Quote(value) => {
// (cons 'quote inner)
let cons = Value::Identifier("cons".into());
let quote_kw = Value::Quote(Rc::new(Value::Keyword(Keyword::Quote)));
let quote_nil = Value::Quote(Rc::new(Value::Nil));
let exp_inner = expand_quasiquote(value);
let cons_inner = cons
.clone()
.cons(exp_inner.cons(quote_nil.clone().cons(Value::Nil)));
cons.cons(quote_kw.cons(cons_inner.cons(Value::Nil)))
}
_ => Value::Quote(Rc::new(value.clone())),
}
}