1
//! Expression-level compile + eval dispatch.
2
//!
3
//! Split into topic-focused submodules so each file stays under the
4
//! ~500-line CLAUDE.md guideline:
5
//! - [`atoms`] — primitive emit (nil, bool, number, string, symbol,
6
//!   push_ratio) and the `compile_text` plumbing they share.
7
//! - [`compile`] — the generic `compile_expr` / `compile_quoted_expr`
8
//!   dispatch + `serialize_stack_to_output`.
9
//! - [`effect`] — effect-position codegen (`compile_for_effect` plus
10
//!   the SETF / IF specialisations it owns).
11
//! - [`stack`] — stack-position codegen (`compile_for_stack` and the
12
//!   ratio/numeric refinements).
13
//! - [`call`] — function call dispatch (symbol calls, lambda calls,
14
//!   lambda-param binding).
15
//! - [`eval`] — eval-only path (`call`, `eval_value`, `dispatch_symbol`,
16
//!   macro expansion, lambda-call eval, `resolve_arg`).
17
//! - [`quasiquote`] — quasiquote / unquote expansion.
18
//! - [`format`] — debug/display formatting for Expr and RuntimeValue.
19
//!
20
//! `LOCAL_*` indices, `compile_program`, and the two `compile_body*`
21
//! helpers stay here in `mod.rs` since they're the cross-submodule
22
//! glue every entry point lands on.
23

            
24
mod atoms;
25
mod call;
26
mod compile;
27
mod effect;
28
mod eval;
29
mod format;
30
mod quasiquote;
31
mod stack;
32

            
33
use crate::ast::{Expr, Program, WasmType};
34
use crate::error::Result;
35
use crate::runtime::SymbolTable;
36

            
37
use super::context::CompileContext;
38
use super::emit::FunctionEmitter;
39

            
40
// Reserved local-pool indices the compiler relies on across codegen
41
// paths. The output-base + GC-array slots come first so they're
42
// available before the `next_local` allocator kicks in. See
43
// `CompileContext::build_locals_declaration` for the matching wasm-
44
// side declaration.
45
pub const LOCAL_GC_ARR: u32 = 0;
46
pub const LOCAL_IDX: u32 = 1;
47
pub const LOCAL_OUTPUT_BASE: u32 = 2;
48
pub const LOCAL_TEMP_RATIO: u32 = 3;
49
pub const LOCAL_TEMP_I32: u32 = 4;
50
/// Absolute address of the entity currently being written to the output
51
/// buffer (`output_base + next_write_pos`). Every output writer positions its
52
/// entity header + data fields relative to this so writes APPEND at the runtime
53
/// `next_write_pos` — the single output protocol that lets compile-time-known
54
/// writes (a program result) and dynamic-count writes (a `create-tag` loop)
55
/// share one buffer without overwriting each other.
56
pub const LOCAL_ENTITY_BASE: u32 = 5;
57

            
58
// Submodule re-exports. Keeps `super::expr::<name>` callers stable
59
// across the refactor so neighbouring modules (`native`, `special`,
60
// the compiler root) don't have to learn the submodule layout.
61
pub(super) use atoms::{compile_bool, compile_nil, compile_string, emit_nil_default, push_ratio};
62
pub(super) use call::compile_call;
63
pub(super) use compile::{compile_expr, compile_quoted_expr, serialize_stack_to_output};
64
pub(super) use effect::compile_for_effect;
65
pub(super) use stack::{
66
    classify_stack_type, compile_call_for_stack, compile_for_stack, compile_for_stack_as,
67
};
68

            
69
pub(crate) use eval::{call, eval_value, expand_macro, resolve_arg};
70
pub(crate) use format::format_expr;
71

            
72
82093
pub fn compile_program(
73
82093
    ctx: &mut CompileContext,
74
82093
    emit: &mut FunctionEmitter,
75
82093
    symbols: &mut SymbolTable,
76
82093
    program: &Program,
77
82093
) -> Result<()> {
78
82093
    ctx.serializer().begin_output(emit);
79
82093
    if program.exprs.is_empty() {
80
1
        compile_nil(ctx, emit);
81
1
        return Ok(());
82
82092
    }
83
112828
    let has_trigger = program.exprs.iter().any(|e| {
84
1160
        matches!(
85
109691
            e,
86
109691
            Expr::List(elems)
87
109691
                if matches!(elems.first(), Some(Expr::Symbol(name)) if name == "DEFUN")
88
5446
                && matches!(elems.get(1), Some(Expr::Symbol(name)) if name == "SHOULD-APPLY")
89
        )
90
112828
    });
91
82092
    if has_trigger {
92
4446
        for expr in &program.exprs {
93
4446
            compile_for_effect(ctx, emit, symbols, expr)?;
94
        }
95
1160
        Ok(())
96
    } else {
97
80932
        for expr in &program.exprs[..program.exprs.len() - 1] {
98
30736
            compile_for_effect(ctx, emit, symbols, expr)?;
99
        }
100
80796
        compile_expr(ctx, emit, symbols, program.exprs.last().unwrap())
101
    }
102
82093
}
103

            
104
22032
pub(super) fn compile_body(
105
22032
    ctx: &mut CompileContext,
106
22032
    emit: &mut FunctionEmitter,
107
22032
    symbols: &mut SymbolTable,
108
22032
    body: &[Expr],
109
22032
) -> Result<()> {
110
22032
    for expr in &body[..body.len() - 1] {
111
4760
        compile_for_effect(ctx, emit, symbols, expr)?;
112
    }
113
22032
    compile_expr(ctx, emit, symbols, body.last().unwrap())
114
22032
}
115

            
116
4716
pub(super) fn compile_body_for_stack(
117
4716
    ctx: &mut CompileContext,
118
4716
    emit: &mut FunctionEmitter,
119
4716
    symbols: &mut SymbolTable,
120
4716
    body: &[Expr],
121
4716
) -> Result<WasmType> {
122
6348
    for expr in &body[..body.len() - 1] {
123
6332
        compile_for_effect(ctx, emit, symbols, expr)?;
124
    }
125
4716
    compile_for_stack(ctx, emit, symbols, body.last().unwrap())
126
4716
}