Lines
71.76 %
Functions
33.33 %
Branches
100 %
//! `NOT` and `NULL?` predicates. Both natives short-circuit at compile time
//! on constant operands and emit a wasm `i32_eqz` / `ref.is_null` check on
//! runtime values.
use crate::ast::{Expr, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::{compile_for_stack, eval_value, serialize_stack_to_output};
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
use super::shared::{bool_result, emit_bool};
pub(super) fn not_fn(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 1 {
return Err(Error::Arity {
name: "NOT".to_string(),
expected: 1,
actual: args.len(),
});
}
let val = eval_value(symbols, &args[0])?;
if val.wasm_type().is_some() {
return Ok(Expr::WasmRuntime(WasmType::Bool));
Ok(bool_result(val.is_nil()))
pub(super) fn null_p(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "NULL?".to_string(),
Ok(bool_result(matches!(val, Expr::Nil)))
pub(super) fn compile_not(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
compile_not_to_stack(ctx, emit, symbols, args)?;
serialize_stack_to_output(ctx, emit, WasmType::Bool)?;
return Ok(());
emit_bool(ctx, emit, val.is_nil());
Ok(())
pub(super) fn compile_null_p(
compile_null_p_to_stack(ctx, emit, symbols, args)?;
emit_bool(ctx, emit, matches!(val, Expr::Nil));
pub(super) fn compile_not_to_stack(
) -> Result<WasmType> {
if emit_runtime_nil_test(ctx, emit, symbols, &args[0], &val)? {
return Ok(WasmType::Bool);
emit.i32_const(i32::from(val.is_nil()));
Ok(WasmType::Bool)
/// Emits the runtime nil test of a runtime `operand`: a bool is nil at zero,
/// every reference (a pair, a string, an anyref cell's null nil, …) at null,
/// and an index is never nil (a count of zero is still a count) — its operand
/// runs for effect and the test is false. Returns `false` for a constant
/// operand, which the caller folds.
fn emit_runtime_nil_test(
operand: &Expr,
resolved: &Expr,
) -> Result<bool> {
if !resolved.is_wasm_runtime() {
return Ok(false);
match compile_for_stack(ctx, emit, symbols, operand)? {
WasmType::I32 => {
emit.drop_value();
emit.i32_const(0);
WasmType::Bool => emit.i32_eqz(),
_ => emit.ref_is_null(),
Ok(true)
pub(super) fn compile_null_p_to_stack(
emit.i32_const(i32::from(matches!(val, Expr::Nil)));