1
//! Shared lowering for a const-folded list-native result at value position.
2
//!
3
//! CAR/CDR/REVERSE/CONS all const-fold a constant (or quoted) list argument on
4
//! the eval path; the folded result may be a self-evaluating atom, a quoted
5
//! tail, or a bare datum (`Symbol` / `List` / `Cons`) with no runtime value.
6
//! Their stack handlers used to assume a runtime `PairRef` and reject the
7
//! folded datum, so e.g. `(reverse '(1 2 3))` or `(car '(x y))` compiled on the
8
//! codegen path but trapped on the eval-with-type (rpc Session) path. Routing
9
//! the folded result through here renders a datum to its printed form as a
10
//! clean `StringRef` — matching how the codegen/effect path serializes a quoted
11
//! datum and how `nomi-eval` decodes one — while atoms lower as themselves.
12
//!
13
//! This is kept local to the list natives rather than added to
14
//! `compile_for_stack` so it does not change how other natives (e.g. `append`,
15
//! which deliberately rejects a quoted symbol-list at stack position) treat a
16
//! quoted datum.
17

            
18
use crate::ast::{Expr, WasmType};
19
use crate::compiler::context::CompileContext;
20
use crate::compiler::emit::FunctionEmitter;
21
use crate::compiler::expr::{compile_for_stack, format_expr};
22
use crate::error::Result;
23
use crate::runtime::SymbolTable;
24

            
25
/// Whether a const-folded result is quoted DATA (must render as a datum string)
26
/// rather than code/an atom: a bare `Symbol` / `List` / `Cons` extracted from a
27
/// quoted source, or a `Quote`-wrapped value.
28
#[must_use]
29
1775
pub(super) fn is_datum_result(result: &Expr) -> bool {
30
497
    matches!(
31
1775
        result,
32
        Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _) | Expr::Quote(_)
33
    )
34
1775
}
35

            
36
/// Lowers a const-folded list-native result to a single clean stack value: a
37
/// quoted-empty list → nil; a quoted/bare datum → its printed form as a
38
/// `StringRef`; any self-evaluating atom → itself via `compile_for_stack`.
39
1988
pub(super) fn compile_folded_to_stack(
40
1988
    ctx: &mut CompileContext,
41
1988
    emit: &mut FunctionEmitter,
42
1988
    symbols: &mut SymbolTable,
43
1988
    folded: Expr,
44
1988
) -> Result<WasmType> {
45
1349
    match folded {
46
        // A quoted EMPTY list folds to nil — lower it as nil, not the string "()".
47
1349
        Expr::Quote(inner) if matches!(*inner, Expr::Nil) => {
48
            compile_for_stack(ctx, emit, symbols, &Expr::Nil)
49
        }
50
1349
        Expr::Quote(inner) => render_datum_string(ctx, emit, &inner),
51
284
        datum @ (Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _)) => {
52
284
            render_datum_string(ctx, emit, &datum)
53
        }
54
355
        atom => compile_for_stack(ctx, emit, symbols, &atom),
55
    }
56
1988
}
57

            
58
/// Pushes the printed form of a datum as a clean `StringRef` (`i8_array` from a
59
/// data segment) — the same shape a string literal lowers to, no debug-output
60
/// side effect.
61
1633
fn render_datum_string(
62
1633
    ctx: &mut CompileContext,
63
1633
    emit: &mut FunctionEmitter,
64
1633
    datum: &Expr,
65
1633
) -> Result<WasmType> {
66
1633
    let rendered = format_expr(datum);
67
1633
    let data_idx = ctx.add_data(rendered.as_bytes())?;
68
1633
    emit.i32_const(0);
69
1633
    emit.i32_const(rendered.len() as i32);
70
1633
    emit.array_new_data(ctx.ids.ty_i8_array, data_idx);
71
1633
    Ok(WasmType::StringRef)
72
1633
}