use super::*;
use wasm_encoder::BlockType;
pub(super) fn emit_str_alloc(ctx: &mut WasmGenCtx, len_i32: u32) -> (u32, u32) {
let padded = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(len_i32));
ctx.emit(Instruction::I32Const(7));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(-8i32)); ctx.emit(Instruction::I32And);
ctx.emit(Instruction::LocalSet(padded));
let payload = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::LocalGet(padded));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(payload));
let ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc_dynamic(ctx, payload, ptr, "string");
ctx.emit(Instruction::LocalGet(ptr));
ctx.ptr_to_f64(len_i32);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let content_base = ctx.i32_offset(ptr, 8);
(ptr, content_base)
}
pub(super) fn emit_str_count(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&str:count")?;
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::F64Load(mem_arg_f64(0))); Ok(())
}
pub(super) fn emit_str_empty(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "str-empty?")?;
ctx.emit(f64_const(1.0));
ctx.emit(f64_const(0.0));
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::F64Eq);
ctx.emit(Instruction::Select);
Ok(())
}
pub(super) fn emit_str_concat(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&str:concat")?;
emit_expr(ctx, &args[0])?;
let f_a = ctx.alloc_local_typed(ValType::F64);
ctx.emit(Instruction::LocalSet(f_a));
let ptr_a = emit_turn_string_from_local(ctx, f_a);
emit_expr(ctx, &args[1])?;
let f_b = ctx.alloc_local_typed(ValType::F64);
ctx.emit(Instruction::LocalSet(f_b));
let ptr_b = emit_turn_string_from_local(ctx, f_b);
let len_a = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr_a));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(len_a));
let len_b = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr_b));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(len_b));
let len_c = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(len_a));
ctx.emit(Instruction::LocalGet(len_b));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(len_c));
let (ptr_c, dst_c) = emit_str_alloc(ctx, len_c);
let src_a = ctx.i32_offset(ptr_a, 8);
ctx.emit(Instruction::LocalGet(dst_c));
ctx.emit(Instruction::LocalGet(src_a));
ctx.emit(Instruction::LocalGet(len_a));
ctx.emit(Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 });
let dst_b = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_c));
ctx.emit(Instruction::LocalGet(len_a));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_b));
let src_b = ctx.i32_offset(ptr_b, 8);
ctx.emit(Instruction::LocalGet(dst_b));
ctx.emit(Instruction::LocalGet(src_b));
ctx.emit(Instruction::LocalGet(len_b));
ctx.emit(Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 });
ctx.ptr_to_f64(ptr_c);
Ok(())
}
pub(super) fn emit_str_nth(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&str:nth")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let idx = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64S);
ctx.emit(Instruction::LocalSet(idx));
let len = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(len));
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32LtS);
ctx.emit(Instruction::If(BlockType::Result(ValType::F64)));
ctx.emit(Instruction::F64Const(Ieee64::from(0.0f64)));
ctx.emit(Instruction::Else);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::LocalGet(len));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::If(BlockType::Result(ValType::F64)));
ctx.emit(Instruction::F64Const(Ieee64::from(0.0f64)));
ctx.emit(Instruction::Else);
let one = ctx.alloc_i32(1);
let (ptr_b, dst_b) = emit_str_alloc(ctx, one);
let byte = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Load8U(mem_arg_byte(0)));
ctx.emit(Instruction::LocalSet(byte));
ctx.emit(Instruction::LocalGet(dst_b));
ctx.emit(Instruction::LocalGet(byte));
ctx.emit(Instruction::I32Store8(mem_arg_byte(0)));
ctx.ptr_to_f64(ptr_b);
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
Ok(())
}
pub(super) fn emit_str_first(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&str:first")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let len = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(len));
ctx.emit(Instruction::LocalGet(len));
ctx.emit(Instruction::I32Eqz);
ctx.emit(Instruction::If(BlockType::Result(ValType::F64)));
ctx.emit(Instruction::F64Const(Ieee64::from(0.0f64)));
ctx.emit(Instruction::Else);
let one = ctx.alloc_i32(1);
let (ptr_b, dst_b) = emit_str_alloc(ctx, one);
let byte = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(8))); ctx.emit(Instruction::LocalSet(byte));
ctx.emit(Instruction::LocalGet(dst_b));
ctx.emit(Instruction::LocalGet(byte));
ctx.emit(Instruction::I32Store8(mem_arg_byte(0)));
ctx.ptr_to_f64(ptr_b);
ctx.emit(Instruction::End); Ok(())
}
pub(super) fn emit_str_rest(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&str:rest")?;
let ptr_a = emit_ptr_to_i32(ctx, &args[0])?;
let old_len = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr_a));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(old_len));
let new_len = ctx.i32_offset(old_len, -1);
let (ptr_b, dst_b) = emit_str_alloc(ctx, new_len);
let src = ctx.i32_offset(ptr_a, 9);
ctx.emit(Instruction::LocalGet(dst_b));
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalGet(new_len));
ctx.emit(Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 });
ctx.ptr_to_f64(ptr_b);
Ok(())
}
pub(super) fn emit_str_slice(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 3 && args.len() != 2 {
return Err("&str:slice expects 2 or 3 args (str, start[, end])".into());
}
let ptr_a = emit_ptr_to_i32(ctx, &args[0])?;
let start = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(start));
let end = ctx.alloc_local_typed(ValType::I32);
if args.len() == 3 {
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(end));
} else {
ctx.emit(Instruction::LocalGet(ptr_a));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(end));
}
let raw_len = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(end));
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(raw_len));
let new_len = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(raw_len));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32LtS);
ctx.emit(Instruction::If(BlockType::Empty));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(new_len));
ctx.emit(Instruction::Else);
ctx.emit(Instruction::LocalGet(raw_len));
ctx.emit(Instruction::LocalSet(new_len));
ctx.emit(Instruction::End);
let (ptr_b, dst_b) = emit_str_alloc(ctx, new_len);
let src = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr_a));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(src));
ctx.emit(Instruction::LocalGet(new_len));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32GtS);
ctx.emit(Instruction::If(BlockType::Empty));
ctx.emit(Instruction::LocalGet(dst_b));
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalGet(new_len));
ctx.emit(Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 });
ctx.emit(Instruction::End);
ctx.ptr_to_f64(ptr_b);
Ok(())
}
pub(super) fn emit_str_compare(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&str:compare")?;
let ptr_a = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_b = emit_ptr_to_i32(ctx, &args[1])?;
ctx.emit(Instruction::LocalGet(ptr_a));
ctx.emit(Instruction::LocalGet(ptr_b));
ctx.call_rt("__rt_str_compare");
Ok(())
}
pub(super) fn emit_str_contains(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&str:contains?")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::I32GtU);
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_str_find_index(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&str:find-index")?;
let ptr_h = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_n = emit_ptr_to_i32(ctx, &args[1])?;
ctx.emit(Instruction::LocalGet(ptr_h));
ctx.emit(Instruction::LocalGet(ptr_n));
ctx.call_rt("__rt_str_find_index");
Ok(())
}
pub(super) fn emit_str_includes(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&str:includes?")?;
let ptr_h = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_n = emit_ptr_to_i32(ctx, &args[1])?;
ctx.emit(Instruction::LocalGet(ptr_h));
ctx.emit(Instruction::LocalGet(ptr_n));
ctx.call_rt("__rt_str_find_index");
ctx.emit(Instruction::F64Const(Ieee64::from(0.0f64)));
ctx.emit(Instruction::F64Ge);
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_str_starts_with(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "starts-with?")?;
let ptr_s = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_p = emit_ptr_to_i32(ctx, &args[1])?;
ctx.emit(Instruction::LocalGet(ptr_s));
ctx.emit(Instruction::LocalGet(ptr_p));
ctx.call_rt("__rt_str_starts_with");
Ok(())
}
pub(super) fn emit_str_ends_with(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "ends-with?")?;
let ptr_s = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_suf = emit_ptr_to_i32(ctx, &args[1])?;
ctx.emit(Instruction::LocalGet(ptr_s));
ctx.emit(Instruction::LocalGet(ptr_suf));
ctx.call_rt("__rt_str_ends_with");
Ok(())
}
pub(super) fn emit_turn_string(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "turn-string")?;
if let Some(tuple_str) = super::try_format_tuple_literal(&args[0]) {
if let Some(&ptr) = ctx.string_pool.get(&tuple_str) {
ctx.emit(super::f64_const(ptr as f64));
return Ok(());
}
}
let f64_to_str_idx = *ctx
.runtime_fn_index
.get("__rt_f64_to_str")
.unwrap_or_else(|| panic!("runtime helper __rt_f64_to_str not found"));
let string_type_tag = *ctx.tag_index.get("string").ok_or("string tag missing")? as i32;
let v = ctx.alloc_local(); let result = ctx.alloc_local(); let v_i32 = ctx.alloc_local_typed(ValType::I32); let raw_base = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(v));
let heap_min = (HEAP_BASE as f64) + 8.0;
ctx.emit(Instruction::LocalGet(v));
ctx.emit(f64_const(heap_min));
ctx.emit(Instruction::F64Ge);
ctx.emit(Instruction::LocalGet(v));
ctx.emit(Instruction::F64Floor);
ctx.emit(Instruction::LocalGet(v));
ctx.emit(Instruction::F64Eq);
ctx.emit(Instruction::I32And);
ctx.begin_block_if();
{
ctx.emit(Instruction::LocalGet(v));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(v_i32));
ctx.emit(Instruction::LocalGet(v_i32));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(raw_base));
ctx.emit(Instruction::LocalGet(raw_base));
ctx.emit(Instruction::I32Load(mem_arg_i32(0)));
ctx.emit(Instruction::I32Const(HEAP_MAGIC));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::LocalGet(raw_base));
ctx.emit(Instruction::I32Load(mem_arg_i32(4)));
ctx.emit(Instruction::I32Const(string_type_tag));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::I32And);
ctx.begin_block_if();
ctx.emit(Instruction::LocalGet(v));
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::End); }
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
{
ctx.emit(Instruction::LocalGet(v));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
{
let raw = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::GlobalGet(HEAP_PTR_GLOBAL));
ctx.emit(Instruction::LocalTee(raw));
ctx.emit(Instruction::I32Const(HEAP_MAGIC));
ctx.emit(Instruction::I32Store(mem_arg_i32(0)));
ctx.emit(Instruction::LocalGet(raw));
ctx.emit(Instruction::I32Const(string_type_tag));
ctx.emit(Instruction::I32Store(mem_arg_i32(4)));
ctx.emit(Instruction::LocalGet(raw));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Const(wasm_encoder::Ieee64::from(0.0f64)));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(raw));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::GlobalSet(HEAP_PTR_GLOBAL));
ctx.emit(Instruction::LocalGet(raw));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(result));
}
ctx.emit(Instruction::Else);
ctx.emit(Instruction::LocalGet(v));
ctx.emit(Instruction::Call(f64_to_str_idx));
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::End);
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
Ok(())
}
pub(super) fn emit_format_to_lisp(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() == 1 {
if let Calcit::List(inner) = &args[0] {
if inner.len() >= 2 {
if let Calcit::Syntax(CalcitSyntax::Quote, _) = &inner[0] {
let s = crate::calcit::format_to_lisp(&inner[1]);
if let Some(&ptr) = ctx.string_pool.get(&s) {
ctx.emit(super::f64_const(ptr as f64));
return Ok(());
}
}
}
}
}
ctx.stub_proc(args)
}
pub(super) fn emit_str_pad_left(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&str:pad-left")?;
let ptr_s = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_p = emit_ptr_to_i32(ctx, &args[2])?;
ctx.emit(Instruction::LocalGet(ptr_s));
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalGet(ptr_p));
ctx.call_rt("__rt_str_pad_left");
Ok(())
}
pub(super) fn emit_str_pad_right(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&str:pad-right")?;
let ptr_s = emit_ptr_to_i32(ctx, &args[0])?;
let ptr_p = emit_ptr_to_i32(ctx, &args[2])?;
ctx.emit(Instruction::LocalGet(ptr_s));
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalGet(ptr_p));
ctx.call_rt("__rt_str_pad_right");
Ok(())
}
fn concat_two_i32_ptrs(ctx: &mut WasmGenCtx, ptr_a: u32, ptr_b: u32) {
let len_a = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr_a));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(len_a));
let len_b = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr_b));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(len_b));
let len_c = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(len_a));
ctx.emit(Instruction::LocalGet(len_b));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(len_c));
let (ptr_c, dst_c) = emit_str_alloc(ctx, len_c);
let src_a = ctx.i32_offset(ptr_a, 8);
ctx.emit(Instruction::LocalGet(dst_c));
ctx.emit(Instruction::LocalGet(src_a));
ctx.emit(Instruction::LocalGet(len_a));
ctx.emit(Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 });
let dst_b_off = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_c));
ctx.emit(Instruction::LocalGet(len_a));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_b_off));
let src_b = ctx.i32_offset(ptr_b, 8);
ctx.emit(Instruction::LocalGet(dst_b_off));
ctx.emit(Instruction::LocalGet(src_b));
ctx.emit(Instruction::LocalGet(len_b));
ctx.emit(Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 });
ctx.ptr_to_f64(ptr_c);
}
pub(super) fn emit_turn_string_from_local(ctx: &mut WasmGenCtx, v_local: u32) -> u32 {
use crate::calcit::{CalcitLocal, CalcitSymbolInfo, DYNAMIC_TYPE};
use std::sync::Arc;
let sym: Arc<str> = Arc::from("__ts_arg__");
let dummy_info = Arc::new(CalcitSymbolInfo {
at_ns: Arc::from("wasm"),
at_def: Arc::from("__ts"),
});
let prev = ctx.locals.insert(sym.as_ref().to_owned(), v_local);
let expr = crate::calcit::Calcit::Local(CalcitLocal {
idx: 0,
sym: sym.clone(),
info: dummy_info,
location: None,
type_info: DYNAMIC_TYPE.clone(),
});
let _ = emit_turn_string(ctx, std::slice::from_ref(&expr));
match prev {
Some(v) => {
ctx.locals.insert(sym.as_ref().to_owned(), v);
}
None => {
ctx.locals.remove(sym.as_ref());
}
}
let str_i32 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(str_i32));
str_i32
}
pub(super) fn emit_str_variadic(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
let non_nil: Vec<&Calcit> = args.iter().filter(|a| !matches!(a, Calcit::Nil)).collect();
if non_nil.is_empty() {
let zero_local = ctx.alloc_i32(0);
let (ptr, _) = emit_str_alloc(ctx, zero_local);
ctx.ptr_to_f64(ptr);
return Ok(());
}
emit_turn_string(ctx, std::slice::from_ref(non_nil[0]))?;
let acc_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(acc_ptr));
for arg in &non_nil[1..] {
emit_turn_string(ctx, std::slice::from_ref(arg))?;
let next_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(next_ptr));
concat_two_i32_ptrs(ctx, acc_ptr, next_ptr);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(acc_ptr));
}
ctx.ptr_to_f64(acc_ptr);
Ok(())
}
pub(super) fn emit_str_spaced(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
let non_nil: Vec<&Calcit> = args.iter().filter(|a| !matches!(a, Calcit::Nil)).collect();
if non_nil.is_empty() {
let zero_local = ctx.alloc_i32(0);
let (ptr, _) = emit_str_alloc(ctx, zero_local);
ctx.ptr_to_f64(ptr);
return Ok(());
}
let one_local = ctx.alloc_i32(1);
let (space_ptr, space_content) = emit_str_alloc(ctx, one_local);
ctx.emit(Instruction::LocalGet(space_content));
ctx.emit(Instruction::I32Const(0x20));
ctx.emit(Instruction::I32Store8(super::mem_arg_byte(0)));
emit_turn_string(ctx, std::slice::from_ref(non_nil[0]))?;
let acc_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(acc_ptr));
for arg in &non_nil[1..] {
concat_two_i32_ptrs(ctx, acc_ptr, space_ptr);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(acc_ptr));
emit_turn_string(ctx, std::slice::from_ref(arg))?;
let next_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(next_ptr));
concat_two_i32_ptrs(ctx, acc_ptr, next_ptr);
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(acc_ptr));
}
ctx.ptr_to_f64(acc_ptr);
Ok(())
}
pub(super) fn build_str_new_fn(str_tag_id: i32) -> CompiledFn {
let instructions = vec![
Instruction::LocalGet(1),
Instruction::I32Const(7),
Instruction::I32Add,
Instruction::I32Const(-8i32),
Instruction::I32And,
Instruction::LocalSet(2), Instruction::I32Const(8),
Instruction::LocalGet(2),
Instruction::I32Add,
Instruction::LocalSet(3), Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(HEAP_MAGIC),
Instruction::I32Store(mem_arg_i32(0)),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(str_tag_id),
Instruction::I32Store(mem_arg_i32(4)),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalTee(4), Instruction::LocalGet(3), Instruction::I32Add,
Instruction::GlobalSet(HEAP_PTR_GLOBAL),
Instruction::LocalGet(4),
Instruction::LocalGet(1), Instruction::F64ConvertI32U,
Instruction::F64Store(mem_arg_f64(0)),
Instruction::LocalGet(4),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalGet(0), Instruction::LocalGet(1), Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 },
Instruction::LocalGet(4),
Instruction::F64ConvertI32U,
];
CompiledFn {
export_name: Some("__str_new".to_string()),
params: vec![ValType::I32, ValType::I32],
results: vec![ValType::F64],
locals: vec![ValType::I32, ValType::I32, ValType::I32], instructions,
}
}
pub(super) fn build_str_pad_left_fn(str_tag_id: i32) -> CompiledFn {
let instructions = vec![
Instruction::LocalGet(0),
Instruction::F64Load(mem_arg_f64(0)),
Instruction::I32TruncF64U,
Instruction::LocalSet(3),
Instruction::Block(wasm_encoder::BlockType::Result(ValType::F64)),
Instruction::LocalGet(3),
Instruction::LocalGet(1),
Instruction::I32GeU,
Instruction::If(wasm_encoder::BlockType::Empty),
Instruction::LocalGet(0),
Instruction::F64ConvertI32U,
Instruction::Br(1), Instruction::End,
Instruction::LocalGet(1),
Instruction::LocalGet(3),
Instruction::I32Sub,
Instruction::LocalSet(4),
Instruction::LocalGet(2),
Instruction::F64Load(mem_arg_f64(0)),
Instruction::I32TruncF64U,
Instruction::LocalSet(5),
Instruction::LocalGet(5),
Instruction::I32Eqz,
Instruction::If(wasm_encoder::BlockType::Empty),
Instruction::LocalGet(0),
Instruction::F64ConvertI32U,
Instruction::Br(1),
Instruction::End,
Instruction::LocalGet(1),
Instruction::I32Const(7),
Instruction::I32Add,
Instruction::I32Const(-8i32),
Instruction::I32And,
Instruction::LocalSet(6),
Instruction::I32Const(8),
Instruction::LocalGet(6),
Instruction::I32Add,
Instruction::LocalSet(7),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(HEAP_MAGIC),
Instruction::I32Store(mem_arg_i32(0)),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(str_tag_id),
Instruction::I32Store(mem_arg_i32(4)),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalSet(8), Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(8),
Instruction::LocalGet(7),
Instruction::I32Add,
Instruction::I32Add,
Instruction::GlobalSet(HEAP_PTR_GLOBAL),
Instruction::LocalGet(8),
Instruction::LocalGet(1),
Instruction::F64ConvertI32U,
Instruction::F64Store(mem_arg_f64(0)),
Instruction::LocalGet(8),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalSet(9),
Instruction::I32Const(0),
Instruction::LocalSet(10),
Instruction::I32Const(0),
Instruction::LocalSet(11),
Instruction::Block(wasm_encoder::BlockType::Empty), Instruction::Loop(wasm_encoder::BlockType::Empty), Instruction::LocalGet(10),
Instruction::LocalGet(4),
Instruction::I32GeU,
Instruction::BrIf(1),
Instruction::LocalGet(11),
Instruction::LocalGet(5),
Instruction::I32GeU,
Instruction::If(wasm_encoder::BlockType::Empty),
Instruction::I32Const(0),
Instruction::LocalSet(11),
Instruction::End,
Instruction::LocalGet(2),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalGet(11),
Instruction::I32Add,
Instruction::I32Load8U(mem_arg_byte(0)),
Instruction::LocalSet(12),
Instruction::LocalGet(9),
Instruction::LocalGet(10),
Instruction::I32Add,
Instruction::LocalGet(12),
Instruction::I32Store8(mem_arg_byte(0)),
Instruction::LocalGet(10),
Instruction::I32Const(1),
Instruction::I32Add,
Instruction::LocalSet(10),
Instruction::LocalGet(11),
Instruction::I32Const(1),
Instruction::I32Add,
Instruction::LocalSet(11),
Instruction::Br(0), Instruction::End, Instruction::End, Instruction::LocalGet(9),
Instruction::LocalGet(4),
Instruction::I32Add, Instruction::LocalGet(0),
Instruction::I32Const(8),
Instruction::I32Add, Instruction::LocalGet(3), Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 },
Instruction::LocalGet(8),
Instruction::F64ConvertI32U,
Instruction::End, ];
CompiledFn {
export_name: None,
params: vec![ValType::I32, ValType::I32, ValType::I32],
results: vec![ValType::F64],
locals: vec![
ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ],
instructions,
}
}
pub(super) fn build_str_pad_right_fn(str_tag_id: i32) -> CompiledFn {
let instructions = vec![
Instruction::LocalGet(0),
Instruction::F64Load(mem_arg_f64(0)),
Instruction::I32TruncF64U,
Instruction::LocalSet(3),
Instruction::Block(wasm_encoder::BlockType::Result(ValType::F64)),
Instruction::LocalGet(3),
Instruction::LocalGet(1),
Instruction::I32GeU,
Instruction::If(wasm_encoder::BlockType::Empty),
Instruction::LocalGet(0),
Instruction::F64ConvertI32U,
Instruction::Br(1),
Instruction::End,
Instruction::LocalGet(1),
Instruction::LocalGet(3),
Instruction::I32Sub,
Instruction::LocalSet(4),
Instruction::LocalGet(2),
Instruction::F64Load(mem_arg_f64(0)),
Instruction::I32TruncF64U,
Instruction::LocalSet(5),
Instruction::LocalGet(5),
Instruction::I32Eqz,
Instruction::If(wasm_encoder::BlockType::Empty),
Instruction::LocalGet(0),
Instruction::F64ConvertI32U,
Instruction::Br(1),
Instruction::End,
Instruction::LocalGet(1),
Instruction::I32Const(7),
Instruction::I32Add,
Instruction::I32Const(-8i32),
Instruction::I32And,
Instruction::LocalSet(6),
Instruction::I32Const(8),
Instruction::LocalGet(6),
Instruction::I32Add,
Instruction::LocalSet(7),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(HEAP_MAGIC),
Instruction::I32Store(mem_arg_i32(0)),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(str_tag_id),
Instruction::I32Store(mem_arg_i32(4)),
Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalSet(8), Instruction::GlobalGet(HEAP_PTR_GLOBAL),
Instruction::I32Const(8),
Instruction::LocalGet(7),
Instruction::I32Add,
Instruction::I32Add,
Instruction::GlobalSet(HEAP_PTR_GLOBAL),
Instruction::LocalGet(8),
Instruction::LocalGet(1),
Instruction::F64ConvertI32U,
Instruction::F64Store(mem_arg_f64(0)),
Instruction::LocalGet(8),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalSet(9),
Instruction::LocalGet(9), Instruction::LocalGet(0),
Instruction::I32Const(8),
Instruction::I32Add, Instruction::LocalGet(3), Instruction::MemoryCopy { dst_mem: 0, src_mem: 0 },
Instruction::I32Const(0),
Instruction::LocalSet(10),
Instruction::I32Const(0),
Instruction::LocalSet(11),
Instruction::Block(wasm_encoder::BlockType::Empty),
Instruction::Loop(wasm_encoder::BlockType::Empty),
Instruction::LocalGet(10),
Instruction::LocalGet(4),
Instruction::I32GeU,
Instruction::BrIf(1),
Instruction::LocalGet(11),
Instruction::LocalGet(5),
Instruction::I32GeU,
Instruction::If(wasm_encoder::BlockType::Empty),
Instruction::I32Const(0),
Instruction::LocalSet(11),
Instruction::End,
Instruction::LocalGet(2),
Instruction::I32Const(8),
Instruction::I32Add,
Instruction::LocalGet(11),
Instruction::I32Add,
Instruction::I32Load8U(mem_arg_byte(0)),
Instruction::LocalSet(12),
Instruction::LocalGet(9),
Instruction::LocalGet(3), Instruction::I32Add,
Instruction::LocalGet(10), Instruction::I32Add,
Instruction::LocalGet(12),
Instruction::I32Store8(mem_arg_byte(0)),
Instruction::LocalGet(10),
Instruction::I32Const(1),
Instruction::I32Add,
Instruction::LocalSet(10),
Instruction::LocalGet(11),
Instruction::I32Const(1),
Instruction::I32Add,
Instruction::LocalSet(11),
Instruction::Br(0),
Instruction::End,
Instruction::End,
Instruction::LocalGet(8),
Instruction::F64ConvertI32U,
Instruction::End, ];
CompiledFn {
export_name: None,
params: vec![ValType::I32, ValType::I32, ValType::I32],
results: vec![ValType::F64],
locals: vec![
ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ],
instructions,
}
}
pub(super) fn emit_join_str_from_locals(ctx: &mut WasmGenCtx, xs_f64: u32, sep_f64: u32) -> Result<(), String> {
let xs_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(xs_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(xs_ptr));
let count = emit_load_count_i32(ctx, xs_ptr);
let result = ctx.alloc_local();
let zero = ctx.alloc_i32(0);
let (empty_ptr, _) = emit_str_alloc(ctx, zero);
ctx.ptr_to_f64(empty_ptr);
ctx.emit(Instruction::LocalSet(result));
let sep_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(sep_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(sep_ptr));
let i = ctx.alloc_i32(0);
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32GtU);
ctx.begin_block_if();
let result_ptr_for_sep = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(result));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(result_ptr_for_sep));
concat_two_i32_ptrs(ctx, result_ptr_for_sep, sep_ptr);
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::End);
let elem = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(xs_ptr));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(elem));
let elem_arr = [crate::calcit::Calcit::Nil]; let _ = elem_arr;
let elem_str_ptr = emit_turn_string_from_local(ctx, elem);
let result_ptr2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(result));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(result_ptr2));
concat_two_i32_ptrs(ctx, result_ptr2, elem_str_ptr);
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(i));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
Ok(())
}
pub(super) fn emit_join_str(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "join-str")?;
let xs = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(xs));
let sep = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(sep));
emit_join_str_from_locals(ctx, xs, sep)
}
pub(super) fn emit_trim(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() == 1 {
emit_expr(ctx, &args[0])?;
ctx.call_rt("__rt_trim_ws");
Ok(())
} else if args.len() == 2 {
emit_expr(ctx, &args[0])?;
emit_expr(ctx, &args[1])?;
ctx.call_rt("__rt_trim_char");
Ok(())
} else {
Err(format!("trim expects 1 or 2 args, got {}", args.len()))
}
}
pub(super) fn emit_blank(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "blank?")?;
emit_expr(ctx, &args[0])?;
ctx.call_rt("__rt_blank");
Ok(())
}
pub(super) fn emit_get_char_code(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "get-char-code")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let content = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(content));
let b0 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(0)));
ctx.emit(Instruction::LocalSet(b0));
let result = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::I32Const(0x80));
ctx.emit(Instruction::I32LtU);
ctx.emit(Instruction::If(BlockType::Empty));
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::Else);
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::I32Const(0xE0));
ctx.emit(Instruction::I32LtU);
ctx.emit(Instruction::If(BlockType::Empty));
{
let b1 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(1)));
ctx.emit(Instruction::LocalSet(b1));
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::I32Const(0x1F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Const(6));
ctx.emit(Instruction::I32Shl);
ctx.emit(Instruction::LocalGet(b1));
ctx.emit(Instruction::I32Const(0x3F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Or);
ctx.emit(Instruction::LocalSet(result));
}
ctx.emit(Instruction::Else);
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::I32Const(0xF0));
ctx.emit(Instruction::I32LtU);
ctx.emit(Instruction::If(BlockType::Empty));
{
let b1 = ctx.alloc_local_typed(ValType::I32);
let b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(1)));
ctx.emit(Instruction::LocalSet(b1));
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(2)));
ctx.emit(Instruction::LocalSet(b2));
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::I32Const(0x0F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Const(12));
ctx.emit(Instruction::I32Shl);
ctx.emit(Instruction::LocalGet(b1));
ctx.emit(Instruction::I32Const(0x3F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Const(6));
ctx.emit(Instruction::I32Shl);
ctx.emit(Instruction::I32Or);
ctx.emit(Instruction::LocalGet(b2));
ctx.emit(Instruction::I32Const(0x3F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Or);
ctx.emit(Instruction::LocalSet(result));
}
ctx.emit(Instruction::Else);
{
let b1 = ctx.alloc_local_typed(ValType::I32);
let b2 = ctx.alloc_local_typed(ValType::I32);
let b3 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(1)));
ctx.emit(Instruction::LocalSet(b1));
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(2)));
ctx.emit(Instruction::LocalSet(b2));
ctx.emit(Instruction::LocalGet(content));
ctx.emit(Instruction::I32Load8U(mem_arg_byte(3)));
ctx.emit(Instruction::LocalSet(b3));
ctx.emit(Instruction::LocalGet(b0));
ctx.emit(Instruction::I32Const(0x07));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Const(18));
ctx.emit(Instruction::I32Shl);
ctx.emit(Instruction::LocalGet(b1));
ctx.emit(Instruction::I32Const(0x3F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Const(12));
ctx.emit(Instruction::I32Shl);
ctx.emit(Instruction::I32Or);
ctx.emit(Instruction::LocalGet(b2));
ctx.emit(Instruction::I32Const(0x3F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Const(6));
ctx.emit(Instruction::I32Shl);
ctx.emit(Instruction::I32Or);
ctx.emit(Instruction::LocalGet(b3));
ctx.emit(Instruction::I32Const(0x3F));
ctx.emit(Instruction::I32And);
ctx.emit(Instruction::I32Or);
ctx.emit(Instruction::LocalSet(result));
}
ctx.emit(Instruction::End); ctx.emit(Instruction::End); ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_parse_float(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "parse-float")?;
emit_expr(ctx, &args[0])?;
ctx.call_rt("__rt_parse_float");
Ok(())
}
pub(super) fn emit_char_from_code(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "char-from-code")?;
emit_expr(ctx, &args[0])?;
ctx.call_rt("__rt_char_from_code");
Ok(())
}
pub(super) fn emit_str_replace(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&str:replace")?;
emit_expr(ctx, &args[0])?;
emit_expr(ctx, &args[1])?;
emit_expr(ctx, &args[2])?;
ctx.call_rt("__rt_str_replace");
Ok(())
}
pub(super) fn emit_str_escape(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&str:escape")?;
emit_expr(ctx, &args[0])?;
ctx.call_rt("__rt_str_escape");
Ok(())
}
pub(super) fn emit_split(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "split")?;
let s = emit_ptr_to_i32(ctx, &args[0])?;
let pat = emit_ptr_to_i32(ctx, &args[1])?;
ctx.emit(Instruction::LocalGet(s));
ctx.emit(Instruction::LocalGet(pat));
ctx.call_rt("__rt_str_split");
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_split_lines(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "split-lines")?;
let s = emit_ptr_to_i32(ctx, &args[0])?;
let nl_ptr = ctx.alloc_local_typed(wasm_encoder::ValType::I32);
let one = ctx.alloc_i32(1);
let (ptr_nl, cont_nl) = emit_str_alloc(ctx, one);
ctx.emit(Instruction::LocalGet(cont_nl));
ctx.emit(Instruction::I32Const(0x0A)); ctx.emit(Instruction::I32Store8(mem_arg_byte(0)));
ctx.emit(Instruction::LocalGet(ptr_nl));
ctx.emit(Instruction::LocalSet(nl_ptr));
ctx.emit(Instruction::LocalGet(s));
ctx.emit(Instruction::LocalGet(nl_ptr));
ctx.call_rt("__rt_str_split");
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}