Lines
90.38 %
Functions
35 %
Branches
100 %
//! `LET` / `LET*` eval-only handlers plus `eval_body` — the body
//! sequencing shared by every binding form's tail. `eval_body` is the
//! piece that walks the body for accumulator setf chains so the
//! promoted runtime type matches the codegen path's `PairElement`.
use crate::ast::{Expr, WasmType};
use crate::compiler::expr::eval_value;
use crate::error::{Error, Result};
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
use super::infer::infer_runtime_type_with_body;
use super::parse::parse_bindings;
pub(super) fn let_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() < 2 {
return Err(Error::Compile(
"LET requires a bindings list and at least one body form".to_string(),
));
}
let bindings = parse_bindings("LET", &args[0])?;
let resolved: Vec<(String, Expr)> = bindings
.into_iter()
.map(|(name, init)| {
let val = match init {
Some(expr) => eval_value(symbols, &expr)?,
None => Expr::Nil,
};
Ok((name, val))
})
.collect::<Result<_>>()?;
let mut local_symbols = symbols.clone();
for (name, val) in resolved {
local_symbols.define(Symbol::new(&name, SymbolKind::Variable).with_value(val));
eval_body(&mut local_symbols, &args[1..])
pub(super) fn let_star(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
"LET* requires a bindings list and at least one body form".to_string(),
let bindings = parse_bindings("LET*", &args[0])?;
for (name, init) in bindings {
Some(expr) => eval_value(&mut local_symbols, &expr)?,
pub(in crate::compiler) fn eval_body(symbols: &mut SymbolTable, body: &[Expr]) -> Result<Expr> {
let mut has_runtime = false;
for expr in &body[..body.len() - 1] {
let val = eval_value(symbols, expr)?;
if val.is_wasm_runtime() {
has_runtime = true;
let result_expr = body.last().unwrap();
let result = eval_value(symbols, result_expr)?;
if has_runtime && !result.is_wasm_runtime() {
// When the body's last form returns an accumulator (a let-bound
// Symbol the body sets via `(setf <name> (cons V <name>))`),
// walk the body for that setf and infer the PairElement from
// the cons car so the promoted runtime type matches the
// PairRef shape the codegen path actually emits. Without this,
// the fallback `PairRef(I32)` disagrees with codegen when the
// car is a Ratio/Commodity, leading to a ref.cast mismatch in
// downstream consumers.
let ty: WasmType = infer_runtime_type_with_body(&result, result_expr, body, symbols);
return Ok(Expr::WasmRuntime(ty));
Ok(result)