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
85710
pub fn compile_program(
73
85710
    ctx: &mut CompileContext,
74
85710
    emit: &mut FunctionEmitter,
75
85710
    symbols: &mut SymbolTable,
76
85710
    program: &Program,
77
85710
) -> Result<()> {
78
85710
    ctx.serializer().begin_output(emit);
79
85710
    if program.exprs.is_empty() {
80
1
        compile_nil(ctx, emit);
81
1
        return Ok(());
82
85709
    }
83
117801
    let has_trigger = program.exprs.iter().any(|e| {
84
1207
        matches!(
85
114526
            e,
86
114526
            Expr::List(elems)
87
114526
                if matches!(elems.first(), Some(Expr::Symbol(name)) if name == "DEFUN")
88
5682
                && matches!(elems.get(1), Some(Expr::Symbol(name)) if name == "SHOULD-APPLY")
89
        )
90
117801
    });
91
85709
    if has_trigger {
92
4615
        for expr in &program.exprs {
93
4615
            compile_for_effect(ctx, emit, symbols, expr)?;
94
        }
95
1207
        Ok(())
96
    } else {
97
84502
        for expr in &program.exprs[..program.exprs.len() - 1] {
98
32092
            compile_for_effect(ctx, emit, symbols, expr)?;
99
        }
100
84360
        compile_expr(ctx, emit, symbols, program.exprs.last().unwrap())
101
    }
102
85710
}
103

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

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