1
//! `NOT` and `NULL?` predicates plus the shared `is_truthy` helper.
2
//! Both natives short-circuit at compile time on constant operands
3
//! and emit a wasm `i32_eqz` / `ref.is_null` check on runtime values.
4

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

            
12
use super::shared::{bool_result, emit_bool};
13

            
14
204
fn is_truthy(expr: &Expr) -> bool {
15
204
    !matches!(expr, Expr::Nil | Expr::Bool(false))
16
204
}
17

            
18
1166
pub(super) fn not_fn(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
19
1166
    if args.len() != 1 {
20
        return Err(Error::Arity {
21
            name: "NOT".to_string(),
22
            expected: 1,
23
            actual: args.len(),
24
        });
25
1166
    }
26
1166
    let val = eval_value(symbols, &args[0])?;
27
1166
    if val.wasm_type().is_some() {
28
1166
        return Ok(Expr::WasmRuntime(WasmType::Bool));
29
    }
30
    Ok(bool_result(!is_truthy(&val)))
31
1166
}
32

            
33
1646
pub(super) fn null_p(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
34
1646
    if args.len() != 1 {
35
        return Err(Error::Arity {
36
            name: "NULL?".to_string(),
37
            expected: 1,
38
            actual: args.len(),
39
        });
40
1646
    }
41
1646
    let val = eval_value(symbols, &args[0])?;
42
1646
    if val.wasm_type().is_some() {
43
1646
        return Ok(Expr::WasmRuntime(WasmType::Bool));
44
    }
45
    Ok(bool_result(matches!(val, Expr::Nil)))
46
1646
}
47

            
48
476
pub(super) fn compile_not(
49
476
    ctx: &mut CompileContext,
50
476
    emit: &mut FunctionEmitter,
51
476
    symbols: &mut SymbolTable,
52
476
    args: &[Expr],
53
476
) -> Result<()> {
54
476
    if args.len() != 1 {
55
        return Err(Error::Arity {
56
            name: "NOT".to_string(),
57
            expected: 1,
58
            actual: args.len(),
59
        });
60
476
    }
61
476
    let val = eval_value(symbols, &args[0])?;
62
476
    if val.wasm_type().is_some() {
63
272
        compile_not_to_stack(ctx, emit, symbols, args)?;
64
272
        serialize_stack_to_output(ctx, emit, WasmType::Bool)?;
65
272
        return Ok(());
66
204
    }
67
204
    emit_bool(ctx, emit, !is_truthy(&val));
68
204
    Ok(())
69
476
}
70

            
71
272
pub(super) fn compile_null_p(
72
272
    ctx: &mut CompileContext,
73
272
    emit: &mut FunctionEmitter,
74
272
    symbols: &mut SymbolTable,
75
272
    args: &[Expr],
76
272
) -> Result<()> {
77
272
    if args.len() != 1 {
78
        return Err(Error::Arity {
79
            name: "NULL?".to_string(),
80
            expected: 1,
81
            actual: args.len(),
82
        });
83
272
    }
84
272
    let val = eval_value(symbols, &args[0])?;
85
272
    if val.wasm_type().is_some() {
86
68
        compile_null_p_to_stack(ctx, emit, symbols, args)?;
87
68
        serialize_stack_to_output(ctx, emit, WasmType::Bool)?;
88
68
        return Ok(());
89
204
    }
90
204
    emit_bool(ctx, emit, matches!(val, Expr::Nil));
91
204
    Ok(())
92
272
}
93

            
94
1576
pub(super) fn compile_not_to_stack(
95
1576
    ctx: &mut CompileContext,
96
1576
    emit: &mut FunctionEmitter,
97
1576
    symbols: &mut SymbolTable,
98
1576
    args: &[Expr],
99
1576
) -> Result<WasmType> {
100
1576
    if args.len() != 1 {
101
        return Err(Error::Arity {
102
            name: "NOT".to_string(),
103
            expected: 1,
104
            actual: args.len(),
105
        });
106
1576
    }
107
1576
    let val = eval_value(symbols, &args[0])?;
108
1576
    match val.wasm_type() {
109
        Some(WasmType::PairRef(_) | WasmType::StringRef | WasmType::EntityRef(_)) => {
110
            compile_for_stack(ctx, emit, symbols, &args[0])?;
111
            emit.ref_is_null();
112
            return Ok(WasmType::Bool);
113
        }
114
        Some(WasmType::I32 | WasmType::Bool) => {
115
1576
            compile_for_stack(ctx, emit, symbols, &args[0])?;
116
1576
            emit.i32_eqz();
117
1576
            return Ok(WasmType::Bool);
118
        }
119
        _ => {}
120
    }
121
    emit.i32_const(i32::from(!is_truthy(&val)));
122
    Ok(WasmType::Bool)
123
1576
}
124

            
125
1714
pub(super) fn compile_null_p_to_stack(
126
1714
    ctx: &mut CompileContext,
127
1714
    emit: &mut FunctionEmitter,
128
1714
    symbols: &mut SymbolTable,
129
1714
    args: &[Expr],
130
1714
) -> Result<WasmType> {
131
1714
    if args.len() != 1 {
132
        return Err(Error::Arity {
133
            name: "NULL?".to_string(),
134
            expected: 1,
135
            actual: args.len(),
136
        });
137
1714
    }
138
1714
    let val = eval_value(symbols, &args[0])?;
139
1714
    match val.wasm_type() {
140
        Some(WasmType::PairRef(_) | WasmType::StringRef | WasmType::EntityRef(_)) => {
141
1578
            compile_for_stack(ctx, emit, symbols, &args[0])?;
142
1578
            emit.ref_is_null();
143
1578
            return Ok(WasmType::Bool);
144
        }
145
        Some(WasmType::I32 | WasmType::Bool) => {
146
            compile_for_stack(ctx, emit, symbols, &args[0])?;
147
            emit.i32_eqz();
148
            return Ok(WasmType::Bool);
149
        }
150
136
        _ => {}
151
    }
152
136
    emit.i32_const(i32::from(matches!(val, Expr::Nil)));
153
136
    Ok(WasmType::Bool)
154
1714
}