114 lines
3.8 KiB
Rust
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())),
|
|
}
|
|
}
|