Lines
58.04 %
Functions
13.33 %
Branches
100 %
use crate::ast::Expr;
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
use super::super::context::CompileContext;
use super::super::emit::FunctionEmitter;
use super::super::expr::{compile_call, compile_string, eval_value, format_expr};
use super::binding::parse_lambda_params;
pub(super) fn compile_lambda_form(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
args: &[Expr],
) -> Result<()> {
let result = lambda(args)?;
compile_string(ctx, emit, &format_expr(&result));
Ok(())
}
pub(super) fn compile_function_form(
symbols: &mut SymbolTable,
let result = function_form(symbols, args)?;
pub(super) 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)
pub(super) 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)
pub(super) 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()
} else {
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)))
pub(super) fn function_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 1 {
return Err(Error::Arity {
name: "FUNCTION".to_string(),
expected: 1,
actual: args.len(),
});
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(),
)),
pub(super) fn funcall(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
tracing::debug!(args = args.len(), "compiling funcall");
super::super::expr::call(symbols, args)
pub(super) fn apply(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
super::super::expr::call(symbols, &all_args)
pub(super) fn resolve_apply_designator(symbols: &mut SymbolTable, arg: &Expr) -> Result<Expr> {
match arg {
let sym = symbols
.ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
if sym.value().is_some() {
eval_value(symbols, arg)
Ok(Expr::Symbol(name.clone()))
_ => eval_value(symbols, arg),