Lines
68.82 %
Functions
22.22 %
Branches
100 %
use super::super::context::CompileContext;
use super::super::emit::FunctionEmitter;
use super::super::expr::{compile_expr, compile_for_stack, eval_value, format_expr};
use crate::ast::{Expr, WasmType};
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
pub(super) fn list(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
let resolved: Vec<Expr> = args
.iter()
.map(|a| eval_value(symbols, a))
.collect::<Result<_>>()?;
Ok(Expr::Quote(Box::new(Expr::List(resolved))))
}
pub(super) fn cons(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 2 {
return Err(Error::Arity {
name: "CONS".to_string(),
expected: 2,
actual: args.len(),
});
let car = eval_value(symbols, &args[0])?;
let cdr = eval_value(symbols, &args[1])?;
if car.is_wasm_runtime() || cdr.is_wasm_runtime() || cdr.wasm_type() == Some(WasmType::ConsRef)
{
return Ok(Expr::WasmRuntime(WasmType::ConsRef));
match cdr {
Expr::Quote(inner) => match *inner {
Expr::List(mut elems) => {
elems.insert(0, car);
Ok(Expr::Quote(Box::new(Expr::List(elems))))
Expr::Nil => Ok(Expr::Quote(Box::new(Expr::List(vec![car])))),
other => Ok(Expr::Quote(Box::new(Expr::cons(car, other)))),
},
pub(super) fn car(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 1 {
name: "CAR".to_string(),
expected: 1,
let arg = eval_value(symbols, &args[0])?;
if arg.wasm_type() == Some(WasmType::ConsRef) {
return Ok(Expr::WasmRuntime(WasmType::I32));
match arg {
Expr::Nil => Ok(Expr::Nil),
Expr::RuntimeValue(crate::runtime::Value::Struct { name, .. }) => Ok(Expr::Symbol(name)),
Expr::List(elems) => {
if elems.is_empty() {
Ok(Expr::Nil)
} else {
Ok(elems[0].clone())
Expr::Cons(car, _) => Ok(*car),
other => Err(Error::Compile(format!(
"CAR expects a list, got {}",
format_expr(&other)
))),
pub(super) fn cdr(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "CDR".to_string(),
Expr::RuntimeValue(crate::runtime::Value::Struct { fields, .. }) => {
let tail = fields
.into_iter()
.map(|v| match v {
crate::runtime::Value::Nil => Expr::Nil,
crate::runtime::Value::Bool(b) => Expr::Bool(b),
crate::runtime::Value::Number(n) => Expr::Number(n),
crate::runtime::Value::String(s) => Expr::String(s),
crate::runtime::Value::Symbol(s) => Expr::Symbol(s),
other => Expr::RuntimeValue(other),
})
.collect::<Vec<_>>();
Ok(Expr::Quote(Box::new(Expr::List(tail))))
if elems.len() <= 1 {
Ok(Expr::Quote(Box::new(Expr::List(elems[1..].to_vec()))))
Expr::Cons(_, cdr) => Ok(*cdr),
let tail = elems[1..].to_vec();
"CDR expects a list, got {}",
pub(super) fn map_fn(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() < 2 {
name: "MAP".to_string(),
let function_arg = &args[0];
let list_args = &args[1..];
// Resolve all list arguments
let lists: Vec<Vec<Expr>> = list_args
.map(|arg| {
let resolved = eval_value(symbols, arg)?;
extract_list_elements(&resolved)
// Find the shortest list to determine iteration count
let min_len = lists.iter().map(std::vec::Vec::len).min().unwrap_or(0);
let mut results = Vec::new();
fn as_literal_arg(expr: &Expr) -> Expr {
match expr {
Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _) => {
Expr::Quote(Box::new(expr.clone()))
_ => expr.clone(),
// Apply function to corresponding elements
for i in 0..min_len {
let mut call_args = vec![function_arg.clone()];
for list in &lists {
call_args.push(as_literal_arg(&list[i]));
// Call the function with the current elements
let result = super::super::expr::call(symbols, &call_args)?;
let resolved_result = eval_value(symbols, &result)?;
results.push(resolved_result);
Ok(Expr::Quote(Box::new(Expr::List(results))))
pub(super) fn extract_list_elements(expr: &Expr) -> Result<Vec<Expr>> {
Expr::List(elems) => Ok(elems.clone()),
Expr::Nil => Ok(vec![]),
Expr::Quote(inner) => match inner.as_ref() {
"MAP expects list arguments, got quoted {}",
format_expr(other)
"MAP expects list arguments, got {}",
pub(super) fn compile_list(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
let result = list(symbols, args)?;
compile_expr(ctx, emit, symbols, &result)
pub(super) fn compile_cons(
let result = cons(symbols, args)?;
if result.wasm_type() == Some(WasmType::ConsRef) {
let ty = compile_cons_to_stack(ctx, emit, symbols, args)?;
super::super::expr::serialize_stack_to_output(ctx, emit, ty);
return Ok(());
pub(super) fn compile_car(
let result = car(symbols, args)?;
if result.wasm_type() == Some(WasmType::I32) && args.len() == 1 {
let ty = compile_car_to_stack(ctx, emit, symbols, args)?;
pub(super) fn compile_cdr(
let result = cdr(symbols, args)?;
let ty = compile_cdr_to_stack(ctx, emit, symbols, args)?;
pub(super) fn compile_cons_to_stack(
) -> Result<WasmType> {
let car_ty = compile_for_stack(ctx, emit, symbols, &args[0])?;
match car_ty {
WasmType::I32 => {}
WasmType::Ratio => {
emit.struct_get(ctx.type_idx("ratio"), 0);
emit.i32_wrap_i64();
WasmType::ConsRef | WasmType::StringRef => {
return Err(Error::Compile(
"CONS car must be i32 or ratio, not cons".to_string(),
));
let cdr_resolved = eval_value(symbols, &args[1])?;
if matches!(cdr_resolved, Expr::Nil) {
emit.ref_null(ctx.type_idx("cons"));
compile_for_stack(ctx, emit, symbols, &args[1])?;
emit.struct_new(ctx.type_idx("cons"));
Ok(WasmType::ConsRef)
pub(super) fn compile_car_to_stack(
compile_for_stack(ctx, emit, symbols, &args[0])?;
emit.struct_get(ctx.type_idx("cons"), 0);
Ok(WasmType::I32)
pub(super) fn compile_cdr_to_stack(
emit.struct_get(ctx.type_idx("cons"), 1);
pub(super) fn compile_map(
// For compilation, evaluate the map call and compile the result
let result = map_fn(symbols, args)?;