use std::collections::HashMap;
use crate::{
Error, TulispContext, TulispObject, TulispValue, bytecode::Instruction, eval::macroexpand,
};
use super::compiler::compile_expr;
mod arithmetic_operations;
mod common;
mod comparison_of_numbers;
mod conditionals;
mod lambda;
mod list_elements;
mod other_functions;
mod plist;
mod setting;
type FnCallCompiler =
fn(&mut TulispContext, &TulispObject, &TulispObject) -> Result<Vec<Instruction>, Error>;
pub(crate) struct VMCompilers {
pub functions: HashMap<usize, FnCallCompiler>,
}
macro_rules! map_fn_call_compilers {
($ctx:ident, $functions: ident, $(($name:literal, $compiler:path),)+) => {
$(
$functions.insert(
$ctx.intern($name).addr_as_usize(),
$compiler as FnCallCompiler,
);
)+
};
}
impl VMCompilers {
pub fn new(ctx: &mut TulispContext) -> Self {
let mut functions = HashMap::new();
map_fn_call_compilers! {
ctx, functions,
("<=", comparison_of_numbers::compile_fn_le),
("<", comparison_of_numbers::compile_fn_lt),
(">=", comparison_of_numbers::compile_fn_ge),
(">", comparison_of_numbers::compile_fn_gt),
("eq", comparison_of_numbers::compile_fn_eq),
("equal", comparison_of_numbers::compile_fn_equal),
("+", arithmetic_operations::compile_fn_plus),
("-", arithmetic_operations::compile_fn_minus),
("*", arithmetic_operations::compile_fn_mul),
("/", arithmetic_operations::compile_fn_div),
("load", other_functions::compile_fn_load_file),
("print", other_functions::compile_fn_print),
("quote", other_functions::compile_fn_quote),
("defun", other_functions::compile_fn_defun),
("lambda", lambda::compile_fn_lambda),
("funcall", lambda::compile_fn_funcall),
("apply", lambda::compile_fn_apply),
("progn", other_functions::compile_fn_progn),
("let", setting::compile_fn_let_star),
("let*", setting::compile_fn_let_star),
("setq", setting::compile_fn_setq),
("set", setting::compile_fn_set),
("cons", other_functions::compile_fn_cons),
("list", other_functions::compile_fn_list),
("append", other_functions::compile_fn_append),
("plist-get", plist::compile_fn_plist_get),
("car", list_elements::compile_fn_cxr),
("cdr", list_elements::compile_fn_cxr),
("caar", list_elements::compile_fn_cxr),
("cadr", list_elements::compile_fn_cxr),
("cdar", list_elements::compile_fn_cxr),
("cddr", list_elements::compile_fn_cxr),
("caaar", list_elements::compile_fn_cxr),
("caadr", list_elements::compile_fn_cxr),
("cadar", list_elements::compile_fn_cxr),
("caddr", list_elements::compile_fn_cxr),
("cdaar", list_elements::compile_fn_cxr),
("cdadr", list_elements::compile_fn_cxr),
("cddar", list_elements::compile_fn_cxr),
("cdddr", list_elements::compile_fn_cxr),
("caaaar", list_elements::compile_fn_cxr),
("caaadr", list_elements::compile_fn_cxr),
("caadar", list_elements::compile_fn_cxr),
("caaddr", list_elements::compile_fn_cxr),
("cadaar", list_elements::compile_fn_cxr),
("cadadr", list_elements::compile_fn_cxr),
("caddar", list_elements::compile_fn_cxr),
("cadddr", list_elements::compile_fn_cxr),
("cdaaar", list_elements::compile_fn_cxr),
("cdaadr", list_elements::compile_fn_cxr),
("cdadar", list_elements::compile_fn_cxr),
("cdaddr", list_elements::compile_fn_cxr),
("cddaar", list_elements::compile_fn_cxr),
("cddadr", list_elements::compile_fn_cxr),
("cdddar", list_elements::compile_fn_cxr),
("cddddr", list_elements::compile_fn_cxr),
("if", conditionals::compile_fn_if),
("cond", conditionals::compile_fn_cond),
("while", conditionals::compile_fn_while),
("dolist", conditionals::compile_fn_dolist),
("dotimes", conditionals::compile_fn_dotimes),
("and", conditionals::compile_fn_and),
("or", conditionals::compile_fn_or),
("not", conditionals::compile_fn_not),
("defmacro", other_functions::compile_fn_noop),
}
VMCompilers { functions }
}
}
pub(super) fn compile_form(
ctx: &mut TulispContext,
form: &TulispObject,
) -> Result<Vec<Instruction>, Error> {
let name = form.car()?;
let args = form.cdr()?;
if let Some(compiler) = ctx
.compiler
.as_ref()
.unwrap()
.vm_compilers
.functions
.get(&name.addr_as_usize())
{
return compiler(ctx, &name, &args);
}
if let Ok(func) = ctx.eval(&name) {
match &*func.inner_ref() {
(TulispValue::Func(func), _) => {
let compiler = ctx.compiler.as_mut().unwrap();
return Ok(vec![
Instruction::Push(args.clone()),
Instruction::RustCall {
name: name.clone(),
form: form.clone(),
func: func.clone(),
keep_result: compiler.keep_result,
},
]);
}
(TulispValue::Defun { call, arity }, _) => {
let call = call.clone();
let arity = arity.clone();
drop(func.inner_ref());
let mut result = Vec::new();
let mut args_count = 0usize;
let mut rest = args.clone();
while rest.consp() {
let arg = rest.car()?;
result.append(&mut super::compiler::compile_expr_keep_result(ctx, &arg)?);
args_count += 1;
rest = rest.cdr()?;
}
if args_count < arity.required {
return Err(
crate::Error::missing_argument("Too few arguments".to_string())
.with_trace(form.clone()),
);
}
if !arity.has_rest && args_count > arity.required + arity.optional {
return Err(
crate::Error::invalid_argument("Too many arguments".to_string())
.with_trace(form.clone()),
);
}
let keep_result = ctx.compiler.as_ref().unwrap().keep_result;
result.push(Instruction::RustCallTyped {
name: name.clone(),
form: form.clone(),
call,
args_count,
keep_result,
});
return Ok(result);
}
(TulispValue::Defmacro { .. }, _) | (TulispValue::Macro(..), _) => {
let form = macroexpand(ctx, form.clone())?;
return compile_expr(ctx, &form);
}
_ => {}
}
}
other_functions::compile_fn_defun_call(ctx, &name, &args)
}