use cel::common::ast::CallExpr;
use ferricel_types::functions::RuntimeFunction;
use walrus::{InstrSeqBuilder, LocalId, ValType};
use crate::compiler::{
context::{CompilerContext, CompilerEnv},
expr::compile_expr,
};
pub fn get_memory_id(module: &walrus::Module) -> Result<walrus::MemoryId, anyhow::Error> {
module
.memories
.iter()
.next()
.ok_or_else(|| anyhow::anyhow!("No memory found"))
.map(|m| m.id())
}
pub fn compile_string_to_local(
s: &str,
body: &mut InstrSeqBuilder,
env: &CompilerEnv,
module: &mut walrus::Module,
) -> Result<LocalId, anyhow::Error> {
let bytes = s.as_bytes();
let len = bytes.len() as i32;
let data_ptr_local = module.locals.add(ValType::I32);
body.i32_const(len)
.call(env.get(RuntimeFunction::Malloc))
.local_set(data_ptr_local);
let memory_id = get_memory_id(module)?;
for (offset, &byte) in bytes.iter().enumerate() {
body.local_get(data_ptr_local);
body.i32_const(byte as i32);
body.store(
memory_id,
walrus::ir::StoreKind::I32_8 { atomic: false },
walrus::ir::MemArg {
align: 1,
offset: offset as u64,
},
);
}
body.local_get(data_ptr_local);
body.i32_const(len);
body.call(env.get(RuntimeFunction::CreateString));
let result_local = module.locals.add(ValType::I32);
body.local_set(result_local);
Ok(result_local)
}
pub fn emit_string_const(
s: &str,
body: &mut InstrSeqBuilder,
env: &CompilerEnv,
memory_id: walrus::MemoryId,
module: &mut walrus::Module,
) {
let bytes = s.as_bytes();
let len = bytes.len() as i32;
if len == 0 {
body.i32_const(0);
body.i32_const(0);
return;
}
let ptr_local = module.locals.add(ValType::I32);
body.i32_const(len)
.call(env.get(RuntimeFunction::Malloc))
.local_set(ptr_local);
for (offset, &byte) in bytes.iter().enumerate() {
body.local_get(ptr_local);
body.i32_const(byte as i32);
body.store(
memory_id,
walrus::ir::StoreKind::I32_8 { atomic: false },
walrus::ir::MemArg {
align: 1,
offset: offset as u64,
},
);
}
body.local_get(ptr_local);
body.i32_const(len);
}
pub fn compile_call_unary(
call_expr: &CallExpr,
func_name: &str,
runtime_fn: RuntimeFunction,
body: &mut InstrSeqBuilder,
env: &CompilerEnv,
ctx: &CompilerContext,
module: &mut walrus::Module,
) -> Result<(), anyhow::Error> {
if let Some(target) = &call_expr.target {
if !call_expr.args.is_empty() {
anyhow::bail!("{}() method expects 0 arguments", func_name);
}
compile_expr(&target.expr, body, env, ctx, module)?;
} else {
if call_expr.args.len() != 1 {
anyhow::bail!("{}() function expects 1 argument", func_name);
}
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
}
body.call(env.get(runtime_fn));
Ok(())
}
pub fn compile_call_ternary(
call_expr: &CallExpr,
func_name: &str,
runtime_fn: RuntimeFunction,
body: &mut InstrSeqBuilder,
env: &CompilerEnv,
ctx: &CompilerContext,
module: &mut walrus::Module,
) -> Result<(), anyhow::Error> {
if let Some(target) = &call_expr.target {
if call_expr.args.len() != 2 {
anyhow::bail!("{}() method expects 2 arguments", func_name);
}
compile_expr(&target.expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[1].expr, body, env, ctx, module)?;
} else {
if call_expr.args.len() != 3 {
anyhow::bail!("{}() function expects 3 arguments", func_name);
}
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[1].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[2].expr, body, env, ctx, module)?;
}
body.call(env.get(runtime_fn));
Ok(())
}
pub fn compile_call_quaternary(
call_expr: &CallExpr,
func_name: &str,
runtime_fn: RuntimeFunction,
body: &mut InstrSeqBuilder,
env: &CompilerEnv,
ctx: &CompilerContext,
module: &mut walrus::Module,
) -> Result<(), anyhow::Error> {
if let Some(target) = &call_expr.target {
if call_expr.args.len() != 3 {
anyhow::bail!("{}() method expects 3 arguments", func_name);
}
compile_expr(&target.expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[1].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[2].expr, body, env, ctx, module)?;
} else {
if call_expr.args.len() != 4 {
anyhow::bail!("{}() function expects 4 arguments", func_name);
}
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[1].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[2].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[3].expr, body, env, ctx, module)?;
}
body.call(env.get(runtime_fn));
Ok(())
}
pub fn compile_call_binary(
call_expr: &CallExpr,
func_name: &str,
runtime_fn: RuntimeFunction,
body: &mut InstrSeqBuilder,
env: &CompilerEnv,
ctx: &CompilerContext,
module: &mut walrus::Module,
) -> Result<(), anyhow::Error> {
if let Some(target) = &call_expr.target {
if call_expr.args.len() != 1 {
anyhow::bail!("{}() method expects 1 argument", func_name);
}
compile_expr(&target.expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
} else {
if call_expr.args.len() != 2 {
anyhow::bail!("{}() function expects 2 arguments", func_name);
}
compile_expr(&call_expr.args[0].expr, body, env, ctx, module)?;
compile_expr(&call_expr.args[1].expr, body, env, ctx, module)?;
}
body.call(env.get(runtime_fn));
Ok(())
}