1
//! `REVERSE` — produce the input list in reversed order. Constant-folds
2
//! when the input is a compile-time list literal; for runtime
3
//! `PairRef(elem)` chains, walks the input cell-by-cell building a fresh
4
//! reversed chain via the `pair_new` helper.
5

            
6
use crate::ast::{Expr, PairElement, WasmType};
7
use crate::compiler::context::CompileContext;
8
use crate::compiler::emit::FunctionEmitter;
9
use crate::compiler::expr::{
10
    compile_expr, compile_for_stack, eval_value, format_expr, serialize_stack_to_output,
11
};
12
use crate::error::{Error, Result};
13
use crate::runtime::SymbolTable;
14

            
15
use super::datum::{compile_folded_to_stack, is_datum_result};
16

            
17
747
pub(super) fn reverse(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
18
747
    if args.len() != 1 {
19
        return Err(Error::Arity {
20
            name: "REVERSE".to_string(),
21
            expected: 1,
22
            actual: args.len(),
23
        });
24
747
    }
25
747
    let arg = eval_value(symbols, &args[0])?;
26
747
    if let Some(WasmType::PairRef(elem)) = arg.wasm_type() {
27
83
        return Ok(Expr::WasmRuntime(WasmType::PairRef(elem)));
28
664
    }
29
664
    let elements = match arg {
30
166
        Expr::Nil => return Ok(Expr::Nil),
31
        Expr::List(elems) => elems,
32
498
        Expr::Quote(inner) => match *inner {
33
498
            Expr::List(elems) => elems,
34
            Expr::Nil => return Ok(Expr::Nil),
35
            other => {
36
                return Err(Error::Compile(format!(
37
                    "REVERSE expects a list, got {}",
38
                    format_expr(&other)
39
                )));
40
            }
41
        },
42
        other => {
43
            return Err(Error::Compile(format!(
44
                "REVERSE expects a list, got {}",
45
                format_expr(&other)
46
            )));
47
        }
48
    };
49
498
    let reversed: Vec<Expr> = elements.into_iter().rev().collect();
50
498
    Ok(Expr::quoted_list(reversed))
51
747
}
52

            
53
415
pub(super) fn compile_reverse(
54
415
    ctx: &mut CompileContext,
55
415
    emit: &mut FunctionEmitter,
56
415
    symbols: &mut SymbolTable,
57
415
    args: &[Expr],
58
415
) -> Result<()> {
59
415
    let folded = reverse(symbols, args)?;
60
415
    if matches!(folded.wasm_type(), Some(WasmType::PairRef(_))) {
61
83
        let ty = compile_reverse_to_stack(ctx, emit, symbols, args)?;
62
83
        serialize_stack_to_output(ctx, emit, ty)?;
63
83
        return Ok(());
64
332
    }
65
    // A fully-constant (reverse '(…)) folds to a quoted reversed list — render
66
    // it as a datum so both compile surfaces agree (it used to trap on the
67
    // eval-with-type stack path with no runtime pair).
68
332
    if is_datum_result(&folded) {
69
249
        let ty = compile_folded_to_stack(ctx, emit, symbols, folded)?;
70
249
        return serialize_stack_to_output(ctx, emit, ty);
71
83
    }
72
83
    compile_expr(ctx, emit, symbols, &folded)
73
415
}
74

            
75
332
pub(super) fn compile_reverse_to_stack(
76
332
    ctx: &mut CompileContext,
77
332
    emit: &mut FunctionEmitter,
78
332
    symbols: &mut SymbolTable,
79
332
    args: &[Expr],
80
332
) -> Result<WasmType> {
81
332
    if args.len() != 1 {
82
        return Err(Error::Arity {
83
            name: "REVERSE".to_string(),
84
            expected: 1,
85
            actual: args.len(),
86
        });
87
332
    }
88
332
    let resolved = eval_value(symbols, &args[0])?;
89
332
    let elem = match resolved.wasm_type() {
90
83
        Some(WasmType::PairRef(e)) => e,
91
        _ => {
92
            // Not a runtime pair: const-fold and render the reversed datum,
93
            // matching the effect path (and CAR/CDR) so a constant or
94
            // runtime-builder list at value position lowers instead of trapping.
95
249
            let folded = reverse(symbols, args)?;
96
249
            return compile_folded_to_stack(ctx, emit, symbols, folded);
97
        }
98
    };
99
83
    emit_reverse_loop(ctx, emit, symbols, &args[0], elem)?;
100
83
    Ok(WasmType::PairRef(elem))
101
332
}
102

            
103
/// Walks `list_expr` cell by cell, prepending each car onto a fresh
104
/// accumulator chain. The accumulator is initialised to a null `$pair`
105
/// ref and rebuilt every iteration via `pair_new(car, acc)`. After the
106
/// loop, the accumulator holds the input reversed and is left on the
107
/// wasm stack.
108
1411
pub(in crate::compiler::native::list) fn emit_reverse_loop(
109
1411
    ctx: &mut CompileContext,
110
1411
    emit: &mut FunctionEmitter,
111
1411
    symbols: &mut SymbolTable,
112
1411
    list_expr: &Expr,
113
1411
    elem: PairElement,
114
1411
) -> Result<()> {
115
1411
    let pair_idx = ctx.ids.ty_pair;
116
1411
    let pair_local = ctx.alloc_local(WasmType::PairRef(elem))?;
117
1411
    let acc_local = ctx.alloc_local(WasmType::PairRef(elem))?;
118

            
119
1411
    compile_for_stack(ctx, emit, symbols, list_expr)?;
120
1411
    emit.local_set(pair_local);
121

            
122
1411
    emit.ref_null(pair_idx);
123
1411
    emit.local_set(acc_local);
124

            
125
1411
    emit.block_start();
126
1411
    emit.loop_start();
127

            
128
1411
    emit.local_get(pair_local);
129
1411
    emit.ref_is_null();
130
1411
    emit.br_if(1);
131

            
132
    // pair_new expects (anyref car, ref null $pair cdr). The car comes
133
    // straight off the input pair as anyref — no downcast needed since
134
    // we're rebuilding into a fresh pair of the same element type.
135
1411
    emit.local_get(pair_local);
136
1411
    emit.struct_get(pair_idx, 0);
137
1411
    emit.local_get(acc_local);
138
1411
    emit.call(ctx.ids.pair_new);
139
1411
    emit.local_set(acc_local);
140

            
141
1411
    emit.local_get(pair_local);
142
1411
    emit.struct_get(pair_idx, 1);
143
1411
    emit.local_set(pair_local);
144

            
145
1411
    emit.br(0);
146
1411
    emit.block_end();
147
1411
    emit.block_end();
148

            
149
1411
    emit.local_get(acc_local);
150
1411
    Ok(())
151
1411
}