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

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

            
90
78246
fn num_cmp(
91
78246
    symbols: &mut SymbolTable,
92
78246
    args: &[Expr],
93
78246
    name: &str,
94
78246
    cmp: fn(&Fraction, &Fraction) -> bool,
95
78246
) -> Result<Expr> {
96
78246
    if args.is_empty() {
97
        return Err(Error::Compile(format!(
98
            "{name} requires at least 1 argument"
99
        )));
100
78246
    }
101
78246
    let resolved = resolve_all(symbols, args)?;
102
78033
    if let Some(nums) = extract_numbers(&resolved) {
103
55238
        let result = nums.windows(2).all(|w| cmp(&w[0], &w[1]));
104
55238
        return Ok(bool_result(result));
105
22795
    }
106
22795
    if has_runtime(&resolved) {
107
22795
        validate_cmp_args(&resolved, name)?;
108
22795
        return Ok(Expr::WasmRuntime(WasmType::Bool));
109
    }
110
    Err(Error::Compile(format!("{name} expects numeric arguments")))
111
78246
}
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
213
pub(in crate::compiler::native::comparison) fn eql(
135
213
    symbols: &mut SymbolTable,
136
213
    args: &[Expr],
137
213
) -> Result<Expr> {
138
213
    if args.len() != 2 {
139
        return Err(Error::Arity {
140
            name: "EQL".to_string(),
141
            expected: 2,
142
            actual: args.len(),
143
        });
144
213
    }
145
213
    let a = eval_value(symbols, &args[0])?;
146
213
    let b = eval_value(symbols, &args[1])?;
147
213
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
148
        return Ok(Expr::WasmRuntime(WasmType::Bool));
149
213
    }
150
213
    Ok(bool_result(a == b))
151
213
}
152

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

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

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

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

            
227
781
pub(in crate::compiler::native::comparison) fn compile_equal(
228
781
    ctx: &mut CompileContext,
229
781
    emit: &mut FunctionEmitter,
230
781
    symbols: &mut SymbolTable,
231
781
    args: &[Expr],
232
781
) -> Result<()> {
233
781
    compile_equal_effect(ctx, emit, symbols, args)
234
781
}
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
1846
fn compile_equal_effect(
240
1846
    ctx: &mut CompileContext,
241
1846
    emit: &mut FunctionEmitter,
242
1846
    symbols: &mut SymbolTable,
243
1846
    args: &[Expr],
244
1846
) -> Result<()> {
245
1846
    let ty = compile_equal_to_stack(ctx, emit, symbols, args)?;
246
1633
    serialize_stack_to_output(ctx, emit, ty)
247
1846
}