1
use super::super::context::CompileContext;
2
use super::super::emit::FunctionEmitter;
3
use super::super::expr::{
4
    compile_bool, compile_for_stack, compile_nil, eval_value, format_expr,
5
    serialize_stack_to_output,
6
};
7
use crate::ast::{Expr, Fraction, WasmType};
8
use crate::error::{Error, Result};
9
use crate::runtime::SymbolTable;
10

            
11
14784
pub(super) fn bool_result(value: bool) -> Expr {
12
14784
    if value { Expr::Bool(true) } else { Expr::Nil }
13
14784
}
14

            
15
1100
fn emit_bool(ctx: &mut CompileContext, emit: &mut FunctionEmitter, value: bool) {
16
1100
    if value {
17
616
        compile_bool(ctx, emit, true);
18
616
    } else {
19
484
        compile_nil(ctx, emit);
20
484
    }
21
1100
}
22

            
23
4182
fn has_runtime(resolved: &[Expr]) -> bool {
24
4182
    resolved.iter().any(crate::ast::Expr::is_wasm_runtime)
25
4182
}
26

            
27
25078
fn resolve_all(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Vec<Expr>> {
28
50684
    args.iter().map(|arg| eval_value(symbols, arg)).collect()
29
25078
}
30

            
31
25078
fn extract_numbers(resolved: &[Expr]) -> Option<Vec<Fraction>> {
32
25078
    resolved
33
25078
        .iter()
34
43368
        .map(|e| match e {
35
33602
            Expr::Number(n) => Some(*n),
36
9766
            _ => None,
37
43368
        })
38
25078
        .collect()
39
25078
}
40

            
41
8974
fn validate_cmp_args(resolved: &[Expr], name: &str) -> Result<WasmType> {
42
8974
    let mut seen_ratio = false;
43
8974
    let mut seen_i32 = false;
44
8974
    let mut seen_int_const = false;
45
17904
    for r in resolved {
46
6926
        match r {
47
6926
            Expr::Number(n) if *n.denom() == 1 => seen_int_const = true,
48
            Expr::Number(_) => seen_ratio = true,
49
10978
            _ if r.wasm_type() == Some(WasmType::Ratio) => seen_ratio = true,
50
7440
            _ if r.wasm_type() == Some(WasmType::I32) => seen_i32 = true,
51
88
            other => {
52
88
                return Err(Error::Compile(format!(
53
88
                    "{name} expects numeric arguments, got {}",
54
88
                    format_expr(other)
55
88
                )));
56
            }
57
        }
58
    }
59
    // When mixing I32 and Ratio, promote I32 to Ratio via ratio_from_i64
60
8886
    if seen_ratio && seen_i32 {
61
980
        return Ok(WasmType::Ratio);
62
7906
    }
63
7906
    if seen_i32 || (seen_int_const && !seen_ratio) {
64
5568
        Ok(WasmType::I32)
65
    } else {
66
2338
        Ok(WasmType::Ratio)
67
    }
68
8974
}
69

            
70
/// Push a value onto the WASM stack as an i32.
71
/// Handles WasmRuntime(I32), `WasmLocal`(_, I32), integer constants, and Bool/Nil.
72
4716
fn compile_for_stack_i32(
73
4716
    ctx: &mut CompileContext,
74
4716
    emit: &mut FunctionEmitter,
75
4716
    symbols: &mut SymbolTable,
76
4716
    expr: &Expr,
77
4716
) -> Result<()> {
78
4716
    let resolved = eval_value(symbols, expr)?;
79
1554
    match &resolved {
80
        Expr::WasmRuntime(WasmType::I32) => {
81
2358
            compile_for_stack(ctx, emit, symbols, expr)?;
82
        }
83
        Expr::WasmLocal(_, WasmType::I32) => {
84
804
            compile_for_stack(ctx, emit, symbols, expr)?;
85
        }
86
1554
        Expr::Number(n) if *n.denom() == 1 => {
87
1554
            emit.i32_const(*n.numer() as i32);
88
1554
        }
89
        Expr::Bool(b) => emit.i32_const(i32::from(*b)),
90
        Expr::Nil => emit.i32_const(0),
91
        _ => {
92
            return Err(Error::Compile(format!(
93
                "cannot compile {} as i32",
94
                format_expr(expr)
95
            )));
96
        }
97
    }
98
4716
    Ok(())
99
4716
}
100

            
101
// --- Eval functions ---
102

            
103
18834
pub(super) fn num_cmp(
104
18834
    symbols: &mut SymbolTable,
105
18834
    args: &[Expr],
106
18834
    name: &str,
107
18834
    cmp: fn(&Fraction, &Fraction) -> bool,
108
18834
) -> Result<Expr> {
109
18834
    if args.is_empty() {
110
        return Err(Error::Compile(format!(
111
            "{name} requires at least 1 argument"
112
        )));
113
18834
    }
114
18834
    let resolved = resolve_all(symbols, args)?;
115
18834
    if let Some(nums) = extract_numbers(&resolved) {
116
14652
        let result = nums.windows(2).all(|w| cmp(&w[0], &w[1]));
117
14652
        return Ok(bool_result(result));
118
4182
    }
119
4182
    if has_runtime(&resolved) {
120
4182
        validate_cmp_args(&resolved, name)?;
121
4182
        return Ok(Expr::WasmRuntime(WasmType::I32));
122
    }
123
    Err(Error::Compile(format!("{name} expects numeric arguments")))
124
18834
}
125

            
126
pub(super) fn num_neq(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
127
    if args.is_empty() {
128
        return Err(Error::Compile(
129
            "/= requires at least 1 argument".to_string(),
130
        ));
131
    }
132
    let resolved = resolve_all(symbols, args)?;
133
    if let Some(nums) = extract_numbers(&resolved) {
134
        let result = (0..nums.len()).all(|i| (i + 1..nums.len()).all(|j| nums[i] != nums[j]));
135
        return Ok(bool_result(result));
136
    }
137
    if has_runtime(&resolved) {
138
        validate_cmp_args(&resolved, "/=")?;
139
        return Ok(Expr::WasmRuntime(WasmType::I32));
140
    }
141
    Err(Error::Compile("/= expects numeric arguments".to_string()))
142
}
143

            
144
132
pub(super) fn eql(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
145
132
    if args.len() != 2 {
146
        return Err(Error::Arity {
147
            name: "EQL".to_string(),
148
            expected: 2,
149
            actual: args.len(),
150
        });
151
132
    }
152
132
    let a = eval_value(symbols, &args[0])?;
153
132
    let b = eval_value(symbols, &args[1])?;
154
132
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
155
        return Ok(Expr::WasmRuntime(WasmType::I32));
156
132
    }
157
132
    Ok(bool_result(a == b))
158
132
}
159

            
160
pub(super) fn equal(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
161
    if args.len() != 2 {
162
        return Err(Error::Arity {
163
            name: "EQUAL".to_string(),
164
            expected: 2,
165
            actual: args.len(),
166
        });
167
    }
168
    let a = eval_value(symbols, &args[0])?;
169
    let b = eval_value(symbols, &args[1])?;
170
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
171
        return Ok(Expr::WasmRuntime(WasmType::I32));
172
    }
173
    Ok(bool_result(a == b))
174
}
175

            
176
// --- Compile functions (serializing) ---
177

            
178
1364
pub(super) fn compile_num_cmp(
179
1364
    ctx: &mut CompileContext,
180
1364
    emit: &mut FunctionEmitter,
181
1364
    symbols: &mut SymbolTable,
182
1364
    args: &[Expr],
183
1364
    name: &str,
184
1364
    cmp: fn(&Fraction, &Fraction) -> bool,
185
1364
) -> Result<()> {
186
1364
    if args.is_empty() {
187
        return Err(Error::Compile(format!(
188
            "{name} requires at least 1 argument"
189
        )));
190
1364
    }
191
1364
    let resolved = resolve_all(symbols, args)?;
192
1364
    if let Some(nums) = extract_numbers(&resolved) {
193
924
        let result = nums.windows(2).all(|w| cmp(&w[0], &w[1]));
194
572
        emit_bool(ctx, emit, result);
195
572
        return Ok(());
196
792
    }
197
792
    let ty = compile_cmp_to_stack_by_name(ctx, emit, symbols, args, name, cmp)?;
198
704
    serialize_stack_to_output(ctx, emit, ty);
199
704
    Ok(())
200
1364
}
201

            
202
88
pub(super) fn compile_num_neq(
203
88
    ctx: &mut CompileContext,
204
88
    emit: &mut FunctionEmitter,
205
88
    symbols: &mut SymbolTable,
206
88
    args: &[Expr],
207
88
) -> Result<()> {
208
88
    if args.is_empty() {
209
        return Err(Error::Compile(
210
            "/= requires at least 1 argument".to_string(),
211
        ));
212
88
    }
213
88
    let resolved = resolve_all(symbols, args)?;
214
88
    if let Some(nums) = extract_numbers(&resolved) {
215
220
        let result = (0..nums.len()).all(|i| (i + 1..nums.len()).all(|j| nums[i] != nums[j]));
216
88
        emit_bool(ctx, emit, result);
217
88
        return Ok(());
218
    }
219
    let ty = compile_neq_to_stack(ctx, emit, symbols, args)?;
220
    serialize_stack_to_output(ctx, emit, ty);
221
    Ok(())
222
88
}
223

            
224
352
pub(super) fn compile_eql(
225
352
    ctx: &mut CompileContext,
226
352
    emit: &mut FunctionEmitter,
227
352
    symbols: &mut SymbolTable,
228
352
    args: &[Expr],
229
352
) -> Result<()> {
230
352
    if args.len() != 2 {
231
88
        return Err(Error::Arity {
232
88
            name: "EQL".to_string(),
233
88
            expected: 2,
234
88
            actual: args.len(),
235
88
        });
236
264
    }
237
264
    let a = eval_value(symbols, &args[0])?;
238
264
    let b = eval_value(symbols, &args[1])?;
239
264
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
240
        let ty = compile_eq_to_stack(ctx, emit, symbols, args)?;
241
        serialize_stack_to_output(ctx, emit, ty);
242
        return Ok(());
243
264
    }
244
264
    emit_bool(ctx, emit, a == b);
245
264
    Ok(())
246
352
}
247

            
248
220
pub(super) fn compile_equal(
249
220
    ctx: &mut CompileContext,
250
220
    emit: &mut FunctionEmitter,
251
220
    symbols: &mut SymbolTable,
252
220
    args: &[Expr],
253
220
) -> Result<()> {
254
220
    if args.len() != 2 {
255
44
        return Err(Error::Arity {
256
44
            name: "EQUAL".to_string(),
257
44
            expected: 2,
258
44
            actual: args.len(),
259
44
        });
260
176
    }
261
176
    let a = eval_value(symbols, &args[0])?;
262
176
    let b = eval_value(symbols, &args[1])?;
263
176
    if a.is_wasm_runtime() || b.is_wasm_runtime() {
264
        let ty = compile_eq_to_stack(ctx, emit, symbols, args)?;
265
        serialize_stack_to_output(ctx, emit, ty);
266
        return Ok(());
267
176
    }
268
176
    emit_bool(ctx, emit, a == b);
269
176
    Ok(())
270
220
}
271

            
272
// --- Stack compile functions (for sub-expressions) ---
273

            
274
1058
fn ratio_cmp_helper(name: &str) -> &str {
275
1058
    match name {
276
1058
        "=" => "ratio_eq",
277
44
        "<" => "ratio_lt",
278
        _ => unreachable!("unsupported comparison: {name}"),
279
    }
280
1058
}
281

            
282
/// Push a value onto the WASM stack as a Ratio, promoting I32 if necessary.
283
4692
fn compile_for_stack_as_ratio(
284
4692
    ctx: &mut CompileContext,
285
4692
    emit: &mut FunctionEmitter,
286
4692
    symbols: &mut SymbolTable,
287
4692
    expr: &Expr,
288
4692
) -> Result<()> {
289
4692
    let ty = compile_for_stack(ctx, emit, symbols, expr)?;
290
4692
    match ty {
291
3712
        WasmType::Ratio => Ok(()),
292
        WasmType::I32 => {
293
980
            emit.i64_extend_i32_u();
294
980
            emit.call(ctx.func("ratio_from_i64"));
295
980
            Ok(())
296
        }
297
        WasmType::ConsRef | WasmType::StringRef => Err(Error::Compile(
298
            "cannot use cons cell in numeric comparison".to_string(),
299
        )),
300
    }
301
4692
}
302

            
303
2358
fn emit_i32_cmp(emit: &mut FunctionEmitter, name: &str) {
304
2358
    match name {
305
2358
        "=" => emit.i32_eq(),
306
        "<" => emit.i32_lt_s(),
307
        _ => unreachable!("unsupported i32 comparison: {name}"),
308
    }
309
2358
}
310

            
311
3416
pub(super) fn compile_eq_to_stack(
312
3416
    ctx: &mut CompileContext,
313
3416
    emit: &mut FunctionEmitter,
314
3416
    symbols: &mut SymbolTable,
315
3416
    args: &[Expr],
316
3416
) -> Result<WasmType> {
317
3416
    compile_cmp_to_stack(ctx, emit, symbols, args, "=", |a, b| a == b)
318
3416
}
319

            
320
pub(super) fn compile_neq_to_stack(
321
    ctx: &mut CompileContext,
322
    emit: &mut FunctionEmitter,
323
    symbols: &mut SymbolTable,
324
    args: &[Expr],
325
) -> Result<WasmType> {
326
    if args.is_empty() {
327
        return Err(Error::Compile(
328
            "/= requires at least 1 argument".to_string(),
329
        ));
330
    }
331
    let resolved = resolve_all(symbols, args)?;
332
    if let Some(nums) = extract_numbers(&resolved) {
333
        let result = (0..nums.len()).all(|i| (i + 1..nums.len()).all(|j| nums[i] != nums[j]));
334
        emit.i32_const(i32::from(result));
335
        return Ok(WasmType::I32);
336
    }
337
    if args.len() == 2 {
338
        let arg_ty = validate_cmp_args(&resolved, "/=")?;
339
        match arg_ty {
340
            WasmType::Ratio => {
341
                compile_for_stack_as_ratio(ctx, emit, symbols, &args[0])?;
342
                compile_for_stack_as_ratio(ctx, emit, symbols, &args[1])?;
343
                emit.call(ctx.func("ratio_eq"));
344
                emit.i32_eqz();
345
                return Ok(WasmType::I32);
346
            }
347
            WasmType::I32 => {
348
                compile_for_stack_i32(ctx, emit, symbols, &args[0])?;
349
                compile_for_stack_i32(ctx, emit, symbols, &args[1])?;
350
                emit.i32_ne();
351
                return Ok(WasmType::I32);
352
            }
353
            WasmType::ConsRef | WasmType::StringRef => {
354
                return Err(Error::Compile(
355
                    "cannot compare cons cells with /=".to_string(),
356
                ));
357
            }
358
        }
359
    }
360
    Err(Error::Compile(
361
        "/= with runtime args requires exactly 2 arguments".to_string(),
362
    ))
363
}
364

            
365
88
pub(super) fn compile_lt_to_stack(
366
88
    ctx: &mut CompileContext,
367
88
    emit: &mut FunctionEmitter,
368
88
    symbols: &mut SymbolTable,
369
88
    args: &[Expr],
370
88
) -> Result<WasmType> {
371
88
    compile_cmp_to_stack(ctx, emit, symbols, args, "<", |a, b| a < b)
372
88
}
373

            
374
88
pub(super) fn compile_gt_to_stack(
375
88
    ctx: &mut CompileContext,
376
88
    emit: &mut FunctionEmitter,
377
88
    symbols: &mut SymbolTable,
378
88
    args: &[Expr],
379
88
) -> Result<WasmType> {
380
    // a > b ≡ b < a
381
88
    compile_cmp_to_stack_reversed(ctx, emit, symbols, args, ">", |a, b| a > b)
382
88
}
383

            
384
pub(super) fn compile_le_to_stack(
385
    ctx: &mut CompileContext,
386
    emit: &mut FunctionEmitter,
387
    symbols: &mut SymbolTable,
388
    args: &[Expr],
389
) -> Result<WasmType> {
390
    // a <= b ≡ !(b < a)
391
    if args.len() != 2 {
392
        return Err(Error::Compile(
393
            "<= requires exactly 2 arguments for runtime comparison".to_string(),
394
        ));
395
    }
396
    let resolved = resolve_all(symbols, args)?;
397
    if let Some(nums) = extract_numbers(&resolved) {
398
        emit.i32_const(i32::from(nums[0] <= nums[1]));
399
        return Ok(WasmType::I32);
400
    }
401
    let arg_ty = validate_cmp_args(&resolved, "<=")?;
402
    match arg_ty {
403
        WasmType::Ratio => {
404
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[1])?;
405
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[0])?;
406
            emit.call(ctx.func("ratio_lt"));
407
            emit.i32_eqz();
408
            Ok(WasmType::I32)
409
        }
410
        WasmType::I32 => {
411
            compile_for_stack_i32(ctx, emit, symbols, &args[0])?;
412
            compile_for_stack_i32(ctx, emit, symbols, &args[1])?;
413
            emit.i32_le_s();
414
            Ok(WasmType::I32)
415
        }
416
        WasmType::ConsRef | WasmType::StringRef => Err(Error::Compile(
417
            "cannot compare cons cells with <=".to_string(),
418
        )),
419
    }
420
}
421

            
422
1200
pub(super) fn compile_ge_to_stack(
423
1200
    ctx: &mut CompileContext,
424
1200
    emit: &mut FunctionEmitter,
425
1200
    symbols: &mut SymbolTable,
426
1200
    args: &[Expr],
427
1200
) -> Result<WasmType> {
428
    // a >= b ≡ !(a < b)
429
1200
    if args.len() != 2 {
430
        return Err(Error::Compile(
431
            ">= requires exactly 2 arguments for runtime comparison".to_string(),
432
        ));
433
1200
    }
434
1200
    let resolved = resolve_all(symbols, args)?;
435
1200
    if let Some(nums) = extract_numbers(&resolved) {
436
        emit.i32_const(i32::from(nums[0] >= nums[1]));
437
        return Ok(WasmType::I32);
438
1200
    }
439
1200
    let arg_ty = validate_cmp_args(&resolved, ">=")?;
440
1200
    match arg_ty {
441
        WasmType::Ratio => {
442
1200
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[0])?;
443
1200
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[1])?;
444
1200
            emit.call(ctx.func("ratio_lt"));
445
1200
            emit.i32_eqz();
446
1200
            Ok(WasmType::I32)
447
        }
448
        WasmType::I32 => {
449
            compile_for_stack_i32(ctx, emit, symbols, &args[0])?;
450
            compile_for_stack_i32(ctx, emit, symbols, &args[1])?;
451
            emit.i32_ge_s();
452
            Ok(WasmType::I32)
453
        }
454
        WasmType::ConsRef | WasmType::StringRef => Err(Error::Compile(
455
            "cannot compare cons cells with >=".to_string(),
456
        )),
457
    }
458
1200
}
459

            
460
792
fn compile_cmp_to_stack_by_name(
461
792
    ctx: &mut CompileContext,
462
792
    emit: &mut FunctionEmitter,
463
792
    symbols: &mut SymbolTable,
464
792
    args: &[Expr],
465
792
    name: &str,
466
792
    const_cmp: fn(&Fraction, &Fraction) -> bool,
467
792
) -> Result<WasmType> {
468
792
    match name {
469
792
        "=" => compile_eq_to_stack(ctx, emit, symbols, args),
470
132
        "<" => compile_lt_to_stack(ctx, emit, symbols, args),
471
44
        ">" => compile_gt_to_stack(ctx, emit, symbols, args),
472
        "<=" => compile_le_to_stack(ctx, emit, symbols, args),
473
        ">=" => compile_ge_to_stack(ctx, emit, symbols, args),
474
        _ => compile_cmp_to_stack(ctx, emit, symbols, args, name, const_cmp),
475
    }
476
792
}
477

            
478
3504
fn compile_cmp_to_stack(
479
3504
    ctx: &mut CompileContext,
480
3504
    emit: &mut FunctionEmitter,
481
3504
    symbols: &mut SymbolTable,
482
3504
    args: &[Expr],
483
3504
    name: &str,
484
3504
    const_cmp: fn(&Fraction, &Fraction) -> bool,
485
3504
) -> Result<WasmType> {
486
3504
    if args.is_empty() {
487
        return Err(Error::Compile(format!(
488
            "{name} requires at least 1 argument"
489
        )));
490
3504
    }
491
3504
    let resolved = resolve_all(symbols, args)?;
492
3504
    if let Some(nums) = extract_numbers(&resolved) {
493
        let result = nums.windows(2).all(|w| const_cmp(&w[0], &w[1]));
494
        emit.i32_const(i32::from(result));
495
        return Ok(WasmType::I32);
496
3504
    }
497
3504
    if args.len() != 2 {
498
        return Err(Error::Compile(format!(
499
            "{name} with runtime args requires exactly 2 arguments"
500
        )));
501
3504
    }
502
3504
    let arg_ty = validate_cmp_args(&resolved, name)?;
503
3416
    match arg_ty {
504
        WasmType::Ratio => {
505
1058
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[0])?;
506
1058
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[1])?;
507
1058
            emit.call(ctx.func(ratio_cmp_helper(name)));
508
1058
            Ok(WasmType::I32)
509
        }
510
        WasmType::I32 => {
511
2358
            compile_for_stack_i32(ctx, emit, symbols, &args[0])?;
512
2358
            compile_for_stack_i32(ctx, emit, symbols, &args[1])?;
513
2358
            emit_i32_cmp(emit, name);
514
2358
            Ok(WasmType::I32)
515
        }
516
        WasmType::ConsRef | WasmType::StringRef => Err(Error::Compile(format!(
517
            "cannot compare cons cells with {name}"
518
        ))),
519
    }
520
3504
}
521

            
522
88
fn compile_cmp_to_stack_reversed(
523
88
    ctx: &mut CompileContext,
524
88
    emit: &mut FunctionEmitter,
525
88
    symbols: &mut SymbolTable,
526
88
    args: &[Expr],
527
88
    name: &str,
528
88
    const_cmp: fn(&Fraction, &Fraction) -> bool,
529
88
) -> Result<WasmType> {
530
88
    if args.is_empty() {
531
        return Err(Error::Compile(format!(
532
            "{name} requires at least 1 argument"
533
        )));
534
88
    }
535
88
    let resolved = resolve_all(symbols, args)?;
536
88
    if let Some(nums) = extract_numbers(&resolved) {
537
        let result = nums.windows(2).all(|w| const_cmp(&w[0], &w[1]));
538
        emit.i32_const(i32::from(result));
539
        return Ok(WasmType::I32);
540
88
    }
541
88
    if args.len() != 2 {
542
        return Err(Error::Compile(format!(
543
            "{name} with runtime args requires exactly 2 arguments"
544
        )));
545
88
    }
546
88
    let arg_ty = validate_cmp_args(&resolved, name)?;
547
88
    match arg_ty {
548
        WasmType::ConsRef | WasmType::StringRef => Err(Error::Compile(format!(
549
            "cannot compare cons cells with {name}"
550
        ))),
551
        WasmType::Ratio => {
552
            // a > b ≡ b < a
553
88
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[1])?;
554
88
            compile_for_stack_as_ratio(ctx, emit, symbols, &args[0])?;
555
88
            emit.call(ctx.func("ratio_lt"));
556
88
            Ok(WasmType::I32)
557
        }
558
        WasmType::I32 => {
559
            compile_for_stack_i32(ctx, emit, symbols, &args[0])?;
560
            compile_for_stack_i32(ctx, emit, symbols, &args[1])?;
561
            emit.i32_gt_s();
562
            Ok(WasmType::I32)
563
        }
564
    }
565
88
}
566

            
567
// --- NOT / NULL? ---
568

            
569
fn is_truthy(expr: &Expr) -> bool {
570
    !matches!(expr, Expr::Nil | Expr::Bool(false))
571
}
572

            
573
1072
pub(super) fn not_fn(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
574
1072
    if args.len() != 1 {
575
        return Err(Error::Arity {
576
            name: "NOT".to_string(),
577
            expected: 1,
578
            actual: args.len(),
579
        });
580
1072
    }
581
1072
    let val = eval_value(symbols, &args[0])?;
582
1072
    if val.wasm_type().is_some() {
583
1072
        return Ok(Expr::WasmRuntime(WasmType::I32));
584
    }
585
    Ok(bool_result(!is_truthy(&val)))
586
1072
}
587

            
588
1340
pub(super) fn null_p(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
589
1340
    if args.len() != 1 {
590
        return Err(Error::Arity {
591
            name: "NULL?".to_string(),
592
            expected: 1,
593
            actual: args.len(),
594
        });
595
1340
    }
596
1340
    let val = eval_value(symbols, &args[0])?;
597
1340
    if val.wasm_type().is_some() {
598
1340
        return Ok(Expr::WasmRuntime(WasmType::I32));
599
    }
600
    Ok(bool_result(matches!(val, Expr::Nil)))
601
1340
}
602

            
603
44
pub(super) fn compile_not(
604
44
    ctx: &mut CompileContext,
605
44
    emit: &mut FunctionEmitter,
606
44
    symbols: &mut SymbolTable,
607
44
    args: &[Expr],
608
44
) -> Result<()> {
609
44
    if args.len() != 1 {
610
        return Err(Error::Arity {
611
            name: "NOT".to_string(),
612
            expected: 1,
613
            actual: args.len(),
614
        });
615
44
    }
616
44
    let val = eval_value(symbols, &args[0])?;
617
44
    if val.wasm_type().is_some() {
618
44
        compile_not_to_stack(ctx, emit, symbols, args)?;
619
44
        serialize_stack_to_output(ctx, emit, WasmType::I32);
620
44
        return Ok(());
621
    }
622
    emit_bool(ctx, emit, !is_truthy(&val));
623
    Ok(())
624
44
}
625

            
626
44
pub(super) fn compile_null_p(
627
44
    ctx: &mut CompileContext,
628
44
    emit: &mut FunctionEmitter,
629
44
    symbols: &mut SymbolTable,
630
44
    args: &[Expr],
631
44
) -> Result<()> {
632
44
    if args.len() != 1 {
633
        return Err(Error::Arity {
634
            name: "NULL?".to_string(),
635
            expected: 1,
636
            actual: args.len(),
637
        });
638
44
    }
639
44
    let val = eval_value(symbols, &args[0])?;
640
44
    if val.wasm_type().is_some() {
641
44
        compile_null_p_to_stack(ctx, emit, symbols, args)?;
642
44
        serialize_stack_to_output(ctx, emit, WasmType::I32);
643
44
        return Ok(());
644
    }
645
    emit_bool(ctx, emit, matches!(val, Expr::Nil));
646
    Ok(())
647
44
}
648

            
649
848
pub(super) fn compile_not_to_stack(
650
848
    ctx: &mut CompileContext,
651
848
    emit: &mut FunctionEmitter,
652
848
    symbols: &mut SymbolTable,
653
848
    args: &[Expr],
654
848
) -> Result<WasmType> {
655
848
    if args.len() != 1 {
656
        return Err(Error::Arity {
657
            name: "NOT".to_string(),
658
            expected: 1,
659
            actual: args.len(),
660
        });
661
848
    }
662
848
    let val = eval_value(symbols, &args[0])?;
663
848
    match val.wasm_type() {
664
        Some(WasmType::ConsRef | WasmType::StringRef) => {
665
            compile_for_stack(ctx, emit, symbols, &args[0])?;
666
            emit.ref_is_null();
667
            return Ok(WasmType::I32);
668
        }
669
        Some(WasmType::I32) => {
670
848
            compile_for_stack(ctx, emit, symbols, &args[0])?;
671
848
            emit.i32_eqz();
672
848
            return Ok(WasmType::I32);
673
        }
674
        _ => {}
675
    }
676
    emit.i32_const(i32::from(!is_truthy(&val)));
677
    Ok(WasmType::I32)
678
848
}
679

            
680
982
pub(super) fn compile_null_p_to_stack(
681
982
    ctx: &mut CompileContext,
682
982
    emit: &mut FunctionEmitter,
683
982
    symbols: &mut SymbolTable,
684
982
    args: &[Expr],
685
982
) -> Result<WasmType> {
686
982
    if args.len() != 1 {
687
        return Err(Error::Arity {
688
            name: "NULL?".to_string(),
689
            expected: 1,
690
            actual: args.len(),
691
        });
692
982
    }
693
982
    let val = eval_value(symbols, &args[0])?;
694
982
    match val.wasm_type() {
695
        Some(WasmType::ConsRef | WasmType::StringRef) => {
696
982
            compile_for_stack(ctx, emit, symbols, &args[0])?;
697
982
            emit.ref_is_null();
698
982
            return Ok(WasmType::I32);
699
        }
700
        Some(WasmType::I32) => {
701
            compile_for_stack(ctx, emit, symbols, &args[0])?;
702
            emit.i32_eqz();
703
            return Ok(WasmType::I32);
704
        }
705
        _ => {}
706
    }
707
    emit.i32_const(i32::from(matches!(val, Expr::Nil)));
708
    Ok(WasmType::I32)
709
982
}