Lines
96.67 %
Functions
20 %
Branches
100 %
//! Shared lowering for a const-folded list-native result at value position.
//!
//! CAR/CDR/REVERSE/CONS all const-fold a constant (or quoted) list argument on
//! the eval path; the folded result may be a self-evaluating atom, a quoted
//! tail, or a bare datum (`Symbol` / `List` / `Cons`) with no runtime value.
//! Their stack handlers used to assume a runtime `PairRef` and reject the
//! folded datum, so e.g. `(reverse '(1 2 3))` or `(car '(x y))` compiled on the
//! codegen path but trapped on the eval-with-type (rpc Session) path. Routing
//! the folded result through here renders a datum to its printed form as a
//! clean `StringRef` — matching how the codegen/effect path serializes a quoted
//! datum and how `nomi-eval` decodes one — while atoms lower as themselves.
//! This is kept local to the list natives rather than added to
//! `compile_for_stack` so it does not change how other natives (e.g. `append`,
//! which deliberately rejects a quoted symbol-list at stack position) treat a
//! quoted datum.
use crate::ast::{Expr, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::{compile_for_stack, format_expr};
use crate::error::Result;
use crate::runtime::SymbolTable;
/// Whether a const-folded result is quoted DATA (must render as a datum string)
/// rather than code/an atom: a bare `Symbol` / `List` / `Cons` extracted from a
/// quoted source, or a `Quote`-wrapped value.
#[must_use]
pub(super) fn is_datum_result(result: &Expr) -> bool {
matches!(
result,
Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _) | Expr::Quote(_)
)
}
/// Lowers a const-folded list-native result to a single clean stack value: a
/// quoted-empty list → nil; a quoted/bare datum → its printed form as a
/// `StringRef`; any self-evaluating atom → itself via `compile_for_stack`.
pub(super) fn compile_folded_to_stack(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
folded: Expr,
) -> Result<WasmType> {
match folded {
// A quoted EMPTY list folds to nil — lower it as nil, not the string "()".
Expr::Quote(inner) if matches!(*inner, Expr::Nil) => {
compile_for_stack(ctx, emit, symbols, &Expr::Nil)
Expr::Quote(inner) => render_datum_string(ctx, emit, &inner),
datum @ (Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _)) => {
render_datum_string(ctx, emit, &datum)
atom => compile_for_stack(ctx, emit, symbols, &atom),
/// Pushes the printed form of a datum as a clean `StringRef` (`i8_array` from a
/// data segment) — the same shape a string literal lowers to, no debug-output
/// side effect.
fn render_datum_string(
datum: &Expr,
let rendered = format_expr(datum);
let data_idx = ctx.add_data(rendered.as_bytes())?;
emit.i32_const(0);
emit.i32_const(rendered.len() as i32);
emit.array_new_data(ctx.ids.ty_i8_array, data_idx);
Ok(WasmType::StringRef)