Lines
79.72 %
Functions
22.29 %
Branches
100 %
//! Eval handlers + the effect-path compile wrappers. Eval handlers
//! constant-fold over `Expr::Number` args; the compile wrappers
//! detect the const-fold path inline and only re-enter `to_stack`
//! when at least one arg is a runtime value.
use crate::ast::{Expr, Fraction, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::{eval_value, serialize_stack_to_output};
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
use super::super::shared::{
bool_result, emit_bool, extract_numbers, has_runtime, resolve_all, validate_cmp_args,
};
use super::to_stack::{compile_cmp_to_stack_by_name, compile_equal_to_stack, compile_neq_to_stack};
pub(in crate::compiler::native::comparison) fn eval_eq(
s: &mut SymbolTable,
args: &[Expr],
) -> Result<Expr> {
num_cmp(s, args, "=", |a, b| a == b)
}
pub(in crate::compiler::native::comparison) fn eval_lt(
num_cmp(s, args, "<", |a, b| a < b)
pub(in crate::compiler::native::comparison) fn eval_gt(
num_cmp(s, args, ">", |a, b| a > b)
pub(in crate::compiler::native::comparison) fn eval_le(
num_cmp(s, args, "<=", |a, b| a <= b)
pub(in crate::compiler::native::comparison) fn eval_ge(
num_cmp(s, args, ">=", |a, b| a >= b)
pub(in crate::compiler::native::comparison) fn compile_eq(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
) -> Result<()> {
compile_num_cmp(ctx, emit, s, args, "=", |a, b| a == b)
pub(in crate::compiler::native::comparison) fn compile_lt(
compile_num_cmp(ctx, emit, s, args, "<", |a, b| a < b)
pub(in crate::compiler::native::comparison) fn compile_gt(
compile_num_cmp(ctx, emit, s, args, ">", |a, b| a > b)
pub(in crate::compiler::native::comparison) fn compile_le(
compile_num_cmp(ctx, emit, s, args, "<=", |a, b| a <= b)
pub(in crate::compiler::native::comparison) fn compile_ge(
compile_num_cmp(ctx, emit, s, args, ">=", |a, b| a >= b)
fn num_cmp(
symbols: &mut SymbolTable,
name: &str,
cmp: fn(&Fraction, &Fraction) -> bool,
if args.is_empty() {
return Err(Error::Compile(format!(
"{name} requires at least 1 argument"
)));
let resolved = resolve_all(symbols, args)?;
if let Some(nums) = extract_numbers(&resolved) {
let result = nums.windows(2).all(|w| cmp(&w[0], &w[1]));
return Ok(bool_result(result));
if has_runtime(&resolved) {
validate_cmp_args(&resolved, name)?;
return Ok(Expr::WasmRuntime(WasmType::Bool));
Err(Error::Compile(format!("{name} expects numeric arguments")))
pub(in crate::compiler::native::comparison) fn num_neq(
return Err(Error::Compile(
"/= requires at least 1 argument".to_string(),
));
let result = (0..nums.len()).all(|i| (i + 1..nums.len()).all(|j| nums[i] != nums[j]));
validate_cmp_args(&resolved, "/=")?;
Err(Error::Compile("/= expects numeric arguments".to_string()))
pub(in crate::compiler::native::comparison) fn eql(
if args.len() != 2 {
return Err(Error::Arity {
name: "EQL".to_string(),
expected: 2,
actual: args.len(),
});
let a = eval_value(symbols, &args[0])?;
let b = eval_value(symbols, &args[1])?;
if a.is_wasm_runtime() || b.is_wasm_runtime() {
Ok(bool_result(a == b))
pub(in crate::compiler::native::comparison) fn equal(
name: "EQUAL".to_string(),
fn compile_num_cmp(
emit_bool(ctx, emit, result);
return Ok(());
let ty = compile_cmp_to_stack_by_name(ctx, emit, symbols, args, name, cmp)?;
serialize_stack_to_output(ctx, emit, ty)?;
Ok(())
pub(in crate::compiler::native::comparison) fn compile_num_neq(
let ty = compile_neq_to_stack(ctx, emit, symbols, args)?;
pub(in crate::compiler::native::comparison) fn compile_eql(
compile_equal_effect(ctx, emit, symbols, args)
pub(in crate::compiler::native::comparison) fn compile_equal(
/// Effect-position generic equality: compile through the value path, then
/// serialize. Shared by `EQL` / `EQUAL` / their `?`-spelled aliases — all the
/// same structural comparison, distinct only in name (Scheme vs CL).
fn compile_equal_effect(
let ty = compile_equal_to_stack(ctx, emit, symbols, args)?;
serialize_stack_to_output(ctx, emit, ty)