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
1700
pub(super) fn is_datum_result(result: &Expr) -> bool {
30
476
    matches!(
31
1700
        result,
32
        Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _) | Expr::Quote(_)
33
    )
34
1700
}
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
1904
pub(super) fn compile_folded_to_stack(
40
1904
    ctx: &mut CompileContext,
41
1904
    emit: &mut FunctionEmitter,
42
1904
    symbols: &mut SymbolTable,
43
1904
    folded: Expr,
44
1904
) -> Result<WasmType> {
45
1292
    match folded {
46
        // A quoted EMPTY list folds to nil — lower it as nil, not the string "()".
47
1292
        Expr::Quote(inner) if matches!(*inner, Expr::Nil) => {
48
            compile_for_stack(ctx, emit, symbols, &Expr::Nil)
49
        }
50
1292
        Expr::Quote(inner) => render_datum_string(ctx, emit, &inner),
51
272
        datum @ (Expr::Symbol(_) | Expr::List(_) | Expr::Cons(_, _)) => {
52
272
            render_datum_string(ctx, emit, &datum)
53
        }
54
340
        atom => compile_for_stack(ctx, emit, symbols, &atom),
55
    }
56
1904
}
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
1564
fn render_datum_string(
62
1564
    ctx: &mut CompileContext,
63
1564
    emit: &mut FunctionEmitter,
64
1564
    datum: &Expr,
65
1564
) -> Result<WasmType> {
66
1564
    let rendered = format_expr(datum);
67
1564
    let data_idx = ctx.add_data(rendered.as_bytes())?;
68
1564
    emit.i32_const(0);
69
1564
    emit.i32_const(rendered.len() as i32);
70
1564
    emit.array_new_data(ctx.ids.ty_i8_array, data_idx);
71
1564
    Ok(WasmType::StringRef)
72
1564
}