Lines
100 %
Functions
45.24 %
Branches
//! Primitive emit helpers — nil, bool, number, string, symbol — plus
//! the `push_ratio` numeric kernel and the `compile_text` machinery
//! `compile_string` and `compile_symbol` share.
use scripting_format::ValueType;
use crate::ast::WasmType;
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::layout::GcLocals;
use crate::error::{Error, Result};
use super::{LOCAL_GC_ARR, LOCAL_IDX};
pub(in crate::compiler) fn compile_nil(ctx: &mut CompileContext, emit: &mut FunctionEmitter) {
ctx.serializer().write_debug_nil(emit);
}
/// Emits a typed zero/null stack value for `ty` — the canonical nil for a
/// binding/accumulator declared with that wasm type. Every type with a valid
/// nil produces one; `Commodity` is the sole exception (a money zero has no
/// currency), so it errors rather than fabricate a currency-less amount.
pub(in crate::compiler) fn emit_nil_default(
ctx: &CompileContext,
emit: &mut FunctionEmitter,
ty: WasmType,
) -> Result<()> {
match ty {
WasmType::I32 | WasmType::Bool => emit.i32_const(0),
WasmType::Ratio => push_ratio(ctx, emit, 0, 1),
WasmType::StringRef => emit.ref_null(ctx.ids.ty_i8_array),
WasmType::PairRef(_) => emit.ref_null(ctx.ids.ty_pair),
WasmType::EntityRef(kind) => emit.ref_null(ctx.ids.entity_type(kind)),
WasmType::Closure(sig) => emit.ref_null(ctx.closure_sig(sig).closure_type_idx),
WasmType::AnyRef => emit.ref_null_any(),
WasmType::Commodity => {
return Err(Error::Compile(
"cannot nil-initialize a commodity-bearing value: a money zero \
has no currency"
.to_string(),
));
Ok(())
pub(in crate::compiler) fn compile_bool(
ctx: &mut CompileContext,
value: bool,
) {
ctx.serializer().write_debug_bool(emit, value);
pub(in crate::compiler) fn compile_number(
numer: i64,
denom: i64,
ctx.serializer().write_debug_number(emit, numer, denom);
fn compile_text(
value_type: ValueType,
s: &str,
let data_idx = ctx.add_data(s.as_bytes())?;
let gc = GcLocals {
type_idx: ctx.ids.ty_i8_array,
arr: LOCAL_GC_ARR,
idx: LOCAL_IDX,
};
ctx.serializer()
.write_debug_string_gc(emit, value_type, data_idx, s.len() as u32, &gc);
pub(in crate::compiler) fn compile_string(
compile_text(ctx, emit, ValueType::String, s)
pub(in crate::compiler) fn compile_symbol(
compile_text(ctx, emit, ValueType::Symbol, s)
pub(in crate::compiler) fn push_ratio(
emit.i64_const(numer);
emit.i64_const(denom);
emit.call(ctx.ids.ratio_new);
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{EntityKind, PairElement, WasmType};
/// `emit_nil_default` produces a typed default for every wasm type that has
/// a valid nil, and errors only for `Commodity` (a money zero has no
/// currency). Covers all match arms, including the defensive `Commodity`
/// guard that no current script can reach (no Commodity-producing literal).
#[test]
fn emit_nil_default_errors_only_for_commodity() {
let mut ctx = CompileContext::new().expect("ctx");
let sig = ctx
.intern_closure_signature(&[], WasmType::Ratio)
.expect("intern closure sig");
let with_nil = [
WasmType::I32,
WasmType::Bool,
WasmType::Ratio,
WasmType::StringRef,
WasmType::PairRef(PairElement::I32),
WasmType::AnyRef,
WasmType::EntityRef(EntityKind::Split),
WasmType::Closure(sig),
];
for ty in with_nil {
let mut emit = FunctionEmitter::new();
emit_nil_default(&ctx, &mut emit, ty)
.unwrap_or_else(|e| panic!("emit_nil_default({ty:?}) should succeed: {e}"));
assert!(
emit_nil_default(&ctx, &mut emit, WasmType::Commodity).is_err(),
"a commodity-bearing value has no valid nil default"
);