1
//! `NOT` and `NULL?` predicates. Both natives short-circuit at compile time
2
//! on constant operands and emit a wasm `i32_eqz` / `ref.is_null` check on
3
//! 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
1411
pub(super) fn not_fn(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
15
1411
    if args.len() != 1 {
16
        return Err(Error::Arity {
17
            name: "NOT".to_string(),
18
            expected: 1,
19
            actual: args.len(),
20
        });
21
1411
    }
22
1411
    let val = eval_value(symbols, &args[0])?;
23
1411
    if val.wasm_type().is_some() {
24
1411
        return Ok(Expr::WasmRuntime(WasmType::Bool));
25
    }
26
    Ok(bool_result(val.is_nil()))
27
1411
}
28

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

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

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

            
90
2158
pub(super) fn compile_not_to_stack(
91
2158
    ctx: &mut CompileContext,
92
2158
    emit: &mut FunctionEmitter,
93
2158
    symbols: &mut SymbolTable,
94
2158
    args: &[Expr],
95
2158
) -> Result<WasmType> {
96
2158
    if args.len() != 1 {
97
        return Err(Error::Arity {
98
            name: "NOT".to_string(),
99
            expected: 1,
100
            actual: args.len(),
101
        });
102
2158
    }
103
2158
    let val = eval_value(symbols, &args[0])?;
104
2158
    if emit_runtime_nil_test(ctx, emit, symbols, &args[0], &val)? {
105
2158
        return Ok(WasmType::Bool);
106
    }
107
    emit.i32_const(i32::from(val.is_nil()));
108
    Ok(WasmType::Bool)
109
2158
}
110

            
111
/// Emits the runtime nil test of a runtime `operand`: a bool is nil at zero,
112
/// every reference (a pair, a string, an anyref cell's null nil, …) at null,
113
/// and an index is never nil (a count of zero is still a count) — its operand
114
/// runs for effect and the test is false. Returns `false` for a constant
115
/// operand, which the caller folds.
116
5893
fn emit_runtime_nil_test(
117
5893
    ctx: &mut CompileContext,
118
5893
    emit: &mut FunctionEmitter,
119
5893
    symbols: &mut SymbolTable,
120
5893
    operand: &Expr,
121
5893
    resolved: &Expr,
122
5893
) -> Result<bool> {
123
5893
    if !resolved.is_wasm_runtime() {
124
166
        return Ok(false);
125
5727
    }
126
5727
    match compile_for_stack(ctx, emit, symbols, operand)? {
127
166
        WasmType::I32 => {
128
166
            emit.drop_value();
129
166
            emit.i32_const(0);
130
166
        }
131
2075
        WasmType::Bool => emit.i32_eqz(),
132
3486
        _ => emit.ref_is_null(),
133
    }
134
5727
    Ok(true)
135
5893
}
136

            
137
3735
pub(super) fn compile_null_p_to_stack(
138
3735
    ctx: &mut CompileContext,
139
3735
    emit: &mut FunctionEmitter,
140
3735
    symbols: &mut SymbolTable,
141
3735
    args: &[Expr],
142
3735
) -> Result<WasmType> {
143
3735
    if args.len() != 1 {
144
        return Err(Error::Arity {
145
            name: "NULL?".to_string(),
146
            expected: 1,
147
            actual: args.len(),
148
        });
149
3735
    }
150
3735
    let val = eval_value(symbols, &args[0])?;
151
3735
    if emit_runtime_nil_test(ctx, emit, symbols, &args[0], &val)? {
152
3569
        return Ok(WasmType::Bool);
153
166
    }
154
166
    emit.i32_const(i32::from(matches!(val, Expr::Nil)));
155
166
    Ok(WasmType::Bool)
156
3735
}