1
//! Eval handlers + the effect-path compile wrappers. Eval handlers
2
//! constant-fold over `Expr::Number` args; the compile wrappers
3
//! detect the const-fold path inline and only re-enter `to_stack`
4
//! when at least one arg is a runtime value.
5

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

            
13
use super::super::shared::{
14
    bool_result, emit_bool, extract_numbers, has_runtime, resolve_all, validate_cmp_args,
15
};
16
use super::to_stack::{compile_cmp_to_stack_by_name, compile_equal_to_stack, compile_neq_to_stack};
17

            
18
51498
pub(in crate::compiler::native::comparison) fn eval_eq(
19
51498
    s: &mut SymbolTable,
20
51498
    args: &[Expr],
21
51498
) -> Result<Expr> {
22
51498
    num_cmp(s, args, "=", |a, b| a == b)
23
51498
}
24
2246
pub(in crate::compiler::native::comparison) fn eval_lt(
25
2246
    s: &mut SymbolTable,
26
2246
    args: &[Expr],
27
2246
) -> Result<Expr> {
28
2246
    num_cmp(s, args, "<", |a, b| a < b)
29
2246
}
30
1568
pub(in crate::compiler::native::comparison) fn eval_gt(
31
1568
    s: &mut SymbolTable,
32
1568
    args: &[Expr],
33
1568
) -> Result<Expr> {
34
1568
    num_cmp(s, args, ">", |a, b| a > b)
35
1568
}
36
10472
pub(in crate::compiler::native::comparison) fn eval_le(
37
10472
    s: &mut SymbolTable,
38
10472
    args: &[Expr],
39
10472
) -> Result<Expr> {
40
10472
    num_cmp(s, args, "<=", |a, b| a <= b)
41
10472
}
42
9208
pub(in crate::compiler::native::comparison) fn eval_ge(
43
9208
    s: &mut SymbolTable,
44
9208
    args: &[Expr],
45
9208
) -> Result<Expr> {
46
9208
    num_cmp(s, args, ">=", |a, b| a >= b)
47
9208
}
48

            
49
2244
pub(in crate::compiler::native::comparison) fn compile_eq(
50
2244
    ctx: &mut CompileContext,
51
2244
    emit: &mut FunctionEmitter,
52
2244
    s: &mut SymbolTable,
53
2244
    args: &[Expr],
54
2244
) -> Result<()> {
55
2244
    compile_num_cmp(ctx, emit, s, args, "=", |a, b| a == b)
56
2244
}
57
612
pub(in crate::compiler::native::comparison) fn compile_lt(
58
612
    ctx: &mut CompileContext,
59
612
    emit: &mut FunctionEmitter,
60
612
    s: &mut SymbolTable,
61
612
    args: &[Expr],
62
612
) -> Result<()> {
63
612
    compile_num_cmp(ctx, emit, s, args, "<", |a, b| a < b)
64
612
}
65
272
pub(in crate::compiler::native::comparison) fn compile_gt(
66
272
    ctx: &mut CompileContext,
67
272
    emit: &mut FunctionEmitter,
68
272
    s: &mut SymbolTable,
69
272
    args: &[Expr],
70
272
) -> Result<()> {
71
408
    compile_num_cmp(ctx, emit, s, args, ">", |a, b| a > b)
72
272
}
73
272
pub(in crate::compiler::native::comparison) fn compile_le(
74
272
    ctx: &mut CompileContext,
75
272
    emit: &mut FunctionEmitter,
76
272
    s: &mut SymbolTable,
77
272
    args: &[Expr],
78
272
) -> Result<()> {
79
340
    compile_num_cmp(ctx, emit, s, args, "<=", |a, b| a <= b)
80
272
}
81
272
pub(in crate::compiler::native::comparison) fn compile_ge(
82
272
    ctx: &mut CompileContext,
83
272
    emit: &mut FunctionEmitter,
84
272
    s: &mut SymbolTable,
85
272
    args: &[Expr],
86
272
) -> Result<()> {
87
272
    compile_num_cmp(ctx, emit, s, args, ">=", |a, b| a >= b)
88
272
}
89

            
90
74992
fn num_cmp(
91
74992
    symbols: &mut SymbolTable,
92
74992
    args: &[Expr],
93
74992
    name: &str,
94
74992
    cmp: fn(&Fraction, &Fraction) -> bool,
95
74992
) -> Result<Expr> {
96
74992
    if args.is_empty() {
97
        return Err(Error::Compile(format!(
98
            "{name} requires at least 1 argument"
99
        )));
100
74992
    }
101
74992
    let resolved = resolve_all(symbols, args)?;
102
74788
    if let Some(nums) = extract_numbers(&resolved) {
103
52904
        let result = nums.windows(2).all(|w| cmp(&w[0], &w[1]));
104
52904
        return Ok(bool_result(result));
105
21884
    }
106
21884
    if has_runtime(&resolved) {
107
21884
        validate_cmp_args(&resolved, name)?;
108
21884
        return Ok(Expr::WasmRuntime(WasmType::Bool));
109
    }
110
    Err(Error::Compile(format!("{name} expects numeric arguments")))
111
74992
}
112

            
113
pub(in crate::compiler::native::comparison) fn num_neq(
114
    symbols: &mut SymbolTable,
115
    args: &[Expr],
116
) -> Result<Expr> {
117
    if args.is_empty() {
118
        return Err(Error::Compile(
119
            "/= requires at least 1 argument".to_string(),
120
        ));
121
    }
122
    let resolved = resolve_all(symbols, args)?;
123
    if let Some(nums) = extract_numbers(&resolved) {
124
        let result = (0..nums.len()).all(|i| (i + 1..nums.len()).all(|j| nums[i] != nums[j]));
125
        return Ok(bool_result(result));
126
    }
127
    if has_runtime(&resolved) {
128
        validate_cmp_args(&resolved, "/=")?;
129
        return Ok(Expr::WasmRuntime(WasmType::Bool));
130
    }
131
    Err(Error::Compile("/= expects numeric arguments".to_string()))
132
}
133

            
134
204
pub(in crate::compiler::native::comparison) fn eql(
135
204
    symbols: &mut SymbolTable,
136
204
    args: &[Expr],
137
204
) -> Result<Expr> {
138
204
    if args.len() != 2 {
139
        return Err(Error::Arity {
140
            name: "EQL".to_string(),
141
            expected: 2,
142
            actual: args.len(),
143
        });
144
204
    }
145
204
    let a = eval_value(symbols, &args[0])?;
146
204
    let b = eval_value(symbols, &args[1])?;
147
204
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
148
        return Ok(Expr::WasmRuntime(WasmType::Bool));
149
204
    }
150
204
    Ok(bool_result(a == b))
151
204
}
152

            
153
272
pub(in crate::compiler::native::comparison) fn equal(
154
272
    symbols: &mut SymbolTable,
155
272
    args: &[Expr],
156
272
) -> Result<Expr> {
157
272
    if args.len() != 2 {
158
        return Err(Error::Arity {
159
            name: "EQUAL".to_string(),
160
            expected: 2,
161
            actual: args.len(),
162
        });
163
272
    }
164
272
    let a = eval_value(symbols, &args[0])?;
165
272
    let b = eval_value(symbols, &args[1])?;
166
272
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
167
272
        return Ok(Expr::WasmRuntime(WasmType::Bool));
168
    }
169
    Ok(bool_result(a == b))
170
272
}
171

            
172
3672
fn compile_num_cmp(
173
3672
    ctx: &mut CompileContext,
174
3672
    emit: &mut FunctionEmitter,
175
3672
    symbols: &mut SymbolTable,
176
3672
    args: &[Expr],
177
3672
    name: &str,
178
3672
    cmp: fn(&Fraction, &Fraction) -> bool,
179
3672
) -> Result<()> {
180
3672
    if args.is_empty() {
181
        return Err(Error::Compile(format!(
182
            "{name} requires at least 1 argument"
183
        )));
184
3672
    }
185
3672
    let resolved = resolve_all(symbols, args)?;
186
3672
    if let Some(nums) = extract_numbers(&resolved) {
187
2040
        let result = nums.windows(2).all(|w| cmp(&w[0], &w[1]));
188
1088
        emit_bool(ctx, emit, result);
189
1088
        return Ok(());
190
2584
    }
191
2584
    let ty = compile_cmp_to_stack_by_name(ctx, emit, symbols, args, name, cmp)?;
192
2244
    serialize_stack_to_output(ctx, emit, ty)?;
193
2244
    Ok(())
194
3672
}
195

            
196
340
pub(in crate::compiler::native::comparison) fn compile_num_neq(
197
340
    ctx: &mut CompileContext,
198
340
    emit: &mut FunctionEmitter,
199
340
    symbols: &mut SymbolTable,
200
340
    args: &[Expr],
201
340
) -> Result<()> {
202
340
    if args.is_empty() {
203
        return Err(Error::Compile(
204
            "/= requires at least 1 argument".to_string(),
205
        ));
206
340
    }
207
340
    let resolved = resolve_all(symbols, args)?;
208
340
    if let Some(nums) = extract_numbers(&resolved) {
209
476
        let result = (0..nums.len()).all(|i| (i + 1..nums.len()).all(|j| nums[i] != nums[j]));
210
272
        emit_bool(ctx, emit, result);
211
272
        return Ok(());
212
68
    }
213
68
    let ty = compile_neq_to_stack(ctx, emit, symbols, args)?;
214
68
    serialize_stack_to_output(ctx, emit, ty)?;
215
68
    Ok(())
216
340
}
217

            
218
1020
pub(in crate::compiler::native::comparison) fn compile_eql(
219
1020
    ctx: &mut CompileContext,
220
1020
    emit: &mut FunctionEmitter,
221
1020
    symbols: &mut SymbolTable,
222
1020
    args: &[Expr],
223
1020
) -> Result<()> {
224
1020
    compile_equal_effect(ctx, emit, symbols, args)
225
1020
}
226

            
227
748
pub(in crate::compiler::native::comparison) fn compile_equal(
228
748
    ctx: &mut CompileContext,
229
748
    emit: &mut FunctionEmitter,
230
748
    symbols: &mut SymbolTable,
231
748
    args: &[Expr],
232
748
) -> Result<()> {
233
748
    compile_equal_effect(ctx, emit, symbols, args)
234
748
}
235

            
236
/// Effect-position generic equality: compile through the value path, then
237
/// serialize. Shared by `EQL` / `EQUAL` / their `?`-spelled aliases — all the
238
/// same structural comparison, distinct only in name (Scheme vs CL).
239
1768
fn compile_equal_effect(
240
1768
    ctx: &mut CompileContext,
241
1768
    emit: &mut FunctionEmitter,
242
1768
    symbols: &mut SymbolTable,
243
1768
    args: &[Expr],
244
1768
) -> Result<()> {
245
1768
    let ty = compile_equal_to_stack(ctx, emit, symbols, args)?;
246
1564
    serialize_stack_to_output(ctx, emit, ty)
247
1768
}