#![allow(unsafe_code)]
use anyhow::{anyhow, Result};
use cranelift::prelude::*;
use cranelift_codegen::settings;
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{FuncId, Linkage, Module};
use std::collections::HashMap;
use crate::frontend::ast::{BinaryOp, Expr, ExprKind, Literal};
pub struct JitCompiler {
module: JITModule,
builder_context: FunctionBuilderContext,
ctx: codegen::Context,
variables: HashMap<String, Variable>,
next_var: u32,
}
struct CompileContext {
variables: HashMap<String, Variable>,
next_var: u32,
functions: HashMap<String, cranelift_codegen::ir::FuncRef>,
loop_merge_block: Option<cranelift_codegen::ir::Block>,
loop_continue_block: Option<cranelift_codegen::ir::Block>,
block_terminated: bool,
tuple_sizes: HashMap<String, usize>,
}
impl JitCompiler {
pub fn new() -> Result<Self> {
let mut flag_builder = settings::builder();
flag_builder
.set("opt_level", "speed")
.map_err(|e| anyhow!("Failed to set opt_level: {e}"))?;
let flags = settings::Flags::new(flag_builder);
let isa = cranelift_native::builder()
.unwrap_or_else(|_| panic!("Cranelift native ISA not available on this platform"))
.finish(flags)
.map_err(|e| anyhow!("Failed to create ISA: {e}"))?;
let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let module = JITModule::new(builder);
Ok(Self {
module,
builder_context: FunctionBuilderContext::new(),
ctx: codegen::Context::new(),
variables: HashMap::new(),
next_var: 0,
})
}
pub fn compile_and_execute(&mut self, ast: &Expr) -> Result<i64> {
let func_id = self.compile_expr_as_function(ast)?;
self.module.finalize_definitions()?;
let code = self.module.get_finalized_function(func_id);
let func: fn() -> i64 = unsafe { std::mem::transmute(code) };
Ok(func())
}
fn compile_expr_as_function(&mut self, ast: &Expr) -> Result<FuncId> {
let mut function_table = HashMap::new();
self.collect_and_declare_functions(ast, &mut function_table)?;
let mut sig = self.module.make_signature();
sig.returns.push(AbiParam::new(types::I64));
let main_func_id = self
.module
.declare_function("main", Linkage::Export, &sig)?;
self.compile_declared_functions(ast, &function_table)?;
self.ctx.func.signature = sig;
{
let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);
let entry_block = builder.create_block();
builder.switch_to_block(entry_block);
builder.seal_block(entry_block);
let mut func_refs = HashMap::new();
for (name, func_id) in &function_table {
let func_ref = self.module.declare_func_in_func(*func_id, builder.func);
func_refs.insert(name.clone(), func_ref);
}
let mut ctx = CompileContext {
variables: HashMap::new(),
next_var: 0,
functions: func_refs,
loop_merge_block: None,
loop_continue_block: None,
block_terminated: false,
tuple_sizes: HashMap::new(),
};
let result = Self::compile_expr(&mut builder, &mut ctx, ast)?;
builder.ins().return_(&[result]);
builder.finalize();
}
self.module.define_function(main_func_id, &mut self.ctx)?;
self.module.clear_context(&mut self.ctx);
Ok(main_func_id)
}
fn collect_and_declare_functions(
&mut self,
expr: &Expr,
table: &mut HashMap<String, FuncId>,
) -> Result<()> {
if let ExprKind::Block(exprs) = &expr.kind {
for e in exprs {
if let ExprKind::Function { name, params, .. } = &e.kind {
let mut sig = self.module.make_signature();
for _ in params {
sig.params.push(AbiParam::new(types::I64));
}
sig.returns.push(AbiParam::new(types::I64));
let func_id = self.module.declare_function(name, Linkage::Local, &sig)?;
table.insert(name.clone(), func_id);
}
}
}
Ok(())
}
fn compile_declared_functions(
&mut self,
expr: &Expr,
table: &HashMap<String, FuncId>,
) -> Result<()> {
if let ExprKind::Block(exprs) = &expr.kind {
for e in exprs {
if let ExprKind::Function {
name, params, body, ..
} = &e.kind
{
let func_id = *table
.get(name)
.expect("function name should exist in symbol table");
self.compile_function_body(func_id, params, body, table)?;
}
}
}
Ok(())
}
fn compile_function_body(
&mut self,
func_id: FuncId,
params: &[crate::frontend::ast::Param],
body: &Expr,
function_table: &HashMap<String, FuncId>,
) -> Result<()> {
self.ctx.func.signature = self
.module
.declarations()
.get_function_decl(func_id)
.signature
.clone();
{
let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);
let entry_block = builder.create_block();
builder.append_block_params_for_function_params(entry_block);
builder.switch_to_block(entry_block);
builder.seal_block(entry_block);
let mut func_refs = HashMap::new();
for (name, func_id) in function_table {
let func_ref = self.module.declare_func_in_func(*func_id, builder.func);
func_refs.insert(name.clone(), func_ref);
}
let mut ctx = CompileContext {
variables: HashMap::new(),
next_var: 0,
functions: func_refs,
loop_merge_block: None,
loop_continue_block: None,
block_terminated: false,
tuple_sizes: HashMap::new(),
};
let block_params = builder.block_params(entry_block).to_vec();
for (i, param) in params.iter().enumerate() {
if let crate::frontend::ast::Pattern::Identifier(param_name) = ¶m.pattern {
let var = builder.declare_var(types::I64);
builder.def_var(var, block_params[i]);
ctx.variables.insert(param_name.clone(), var);
}
}
let result = Self::compile_expr(&mut builder, &mut ctx, body)?;
if !ctx.block_terminated {
builder.ins().return_(&[result]);
}
builder.finalize();
}
self.module.define_function(func_id, &mut self.ctx)?;
self.module.clear_context(&mut self.ctx);
Ok(())
}
fn compile_expr(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
expr: &Expr,
) -> Result<Value> {
match &expr.kind {
ExprKind::Literal(Literal::Integer(n, _)) => Ok(builder.ins().iconst(types::I64, *n)),
ExprKind::Literal(Literal::Bool(b)) => {
Ok(builder.ins().iconst(types::I64, i64::from(*b)))
}
ExprKind::Literal(Literal::Unit) => Ok(builder.ins().iconst(types::I64, 0)),
ExprKind::Binary { left, op, right } => {
Self::compile_binary_op(builder, ctx, left, op, right)
}
ExprKind::Block(exprs) => Self::compile_block(builder, ctx, exprs),
ExprKind::If {
condition,
then_branch,
else_branch,
} => Self::compile_if(builder, ctx, condition, then_branch, else_branch.as_deref()),
ExprKind::Let {
name, value, body, ..
} => Self::compile_let(builder, ctx, name, value, body),
ExprKind::Identifier(name) => Self::compile_identifier(builder, ctx, name),
ExprKind::Function { .. } => {
Ok(builder.ins().iconst(types::I64, 0))
}
ExprKind::Call { func, args } => Self::compile_call(builder, ctx, func, args),
ExprKind::Unary { op, operand } => Self::compile_unary_op(builder, ctx, op, operand),
ExprKind::While {
condition, body, ..
} => Self::compile_while(builder, ctx, condition, body),
ExprKind::For {
var, iter, body, ..
} => Self::compile_for(builder, ctx, var, iter, body),
ExprKind::Break { .. } => Self::compile_break(builder, ctx),
ExprKind::Continue { .. } => Self::compile_continue(builder, ctx),
ExprKind::Return { value } => Self::compile_return(builder, ctx, value.as_deref()),
ExprKind::Assign { target, value } => Self::compile_assign(builder, ctx, target, value),
ExprKind::Tuple(elements) => Self::compile_tuple(builder, ctx, elements),
ExprKind::FieldAccess { object, field } => {
Self::compile_field_access(builder, ctx, object, field)
}
_ => Err(anyhow!(
"JIT-002: Expression kind not yet supported: {:?}",
expr.kind
)),
}
}
fn compile_binary_op(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
left: &Expr,
op: &BinaryOp,
right: &Expr,
) -> Result<Value> {
match op {
BinaryOp::And => return Self::compile_logical_and(builder, ctx, left, right),
BinaryOp::Or => return Self::compile_logical_or(builder, ctx, left, right),
_ => {} }
let lhs = Self::compile_expr(builder, ctx, left)?;
let rhs = Self::compile_expr(builder, ctx, right)?;
let result = match op {
BinaryOp::Add => builder.ins().iadd(lhs, rhs),
BinaryOp::Subtract => builder.ins().isub(lhs, rhs),
BinaryOp::Multiply => builder.ins().imul(lhs, rhs),
BinaryOp::Divide => builder.ins().sdiv(lhs, rhs),
BinaryOp::Modulo => builder.ins().srem(lhs, rhs),
BinaryOp::Equal => {
let cmp = builder.ins().icmp(IntCC::Equal, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::NotEqual => {
let cmp = builder.ins().icmp(IntCC::NotEqual, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::LessEqual => {
let cmp = builder.ins().icmp(IntCC::SignedLessThanOrEqual, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Less => {
let cmp = builder.ins().icmp(IntCC::SignedLessThan, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::Greater => {
let cmp = builder.ins().icmp(IntCC::SignedGreaterThan, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
BinaryOp::GreaterEqual => {
let cmp = builder
.ins()
.icmp(IntCC::SignedGreaterThanOrEqual, lhs, rhs);
builder.ins().uextend(types::I64, cmp)
}
_ => return Err(anyhow!("Unsupported binary operation in JIT: {op:?}")),
};
Ok(result)
}
fn compile_unary_op(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
op: &crate::frontend::ast::UnaryOp,
operand: &Expr,
) -> Result<Value> {
let operand_value = Self::compile_expr(builder, ctx, operand)?;
let result = match op {
crate::frontend::ast::UnaryOp::Negate => {
let zero = builder.ins().iconst(types::I64, 0);
builder.ins().isub(zero, operand_value)
}
crate::frontend::ast::UnaryOp::Not => {
let one = builder.ins().iconst(types::I64, 1);
builder.ins().isub(one, operand_value)
}
_ => return Err(anyhow!("JIT-003: Unsupported unary operation: {op:?}")),
};
Ok(result)
}
fn compile_logical_and(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
left: &Expr,
right: &Expr,
) -> Result<Value> {
let left_value = Self::compile_expr(builder, ctx, left)?;
let eval_right_block = builder.create_block();
let short_circuit_block = builder.create_block();
let merge_block = builder.create_block();
let result_var = builder.declare_var(types::I64);
builder
.ins()
.brif(left_value, eval_right_block, &[], short_circuit_block, &[]);
builder.switch_to_block(eval_right_block);
builder.seal_block(eval_right_block);
let right_value = Self::compile_expr(builder, ctx, right)?;
builder.def_var(result_var, right_value);
builder.ins().jump(merge_block, &[]);
builder.switch_to_block(short_circuit_block);
builder.seal_block(short_circuit_block);
let false_val = builder.ins().iconst(types::I64, 0);
builder.def_var(result_var, false_val);
builder.ins().jump(merge_block, &[]);
builder.switch_to_block(merge_block);
builder.seal_block(merge_block);
Ok(builder.use_var(result_var))
}
fn compile_logical_or(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
left: &Expr,
right: &Expr,
) -> Result<Value> {
let left_value = Self::compile_expr(builder, ctx, left)?;
let short_circuit_block = builder.create_block();
let eval_right_block = builder.create_block();
let merge_block = builder.create_block();
let result_var = builder.declare_var(types::I64);
builder
.ins()
.brif(left_value, short_circuit_block, &[], eval_right_block, &[]);
builder.switch_to_block(short_circuit_block);
builder.seal_block(short_circuit_block);
let true_val = builder.ins().iconst(types::I64, 1);
builder.def_var(result_var, true_val);
builder.ins().jump(merge_block, &[]);
builder.switch_to_block(eval_right_block);
builder.seal_block(eval_right_block);
let right_value = Self::compile_expr(builder, ctx, right)?;
builder.def_var(result_var, right_value);
builder.ins().jump(merge_block, &[]);
builder.switch_to_block(merge_block);
builder.seal_block(merge_block);
Ok(builder.use_var(result_var))
}
fn compile_while(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
condition: &Expr,
body: &Expr,
) -> Result<Value> {
let loop_block = builder.create_block();
let body_block = builder.create_block();
let merge_block = builder.create_block();
let prev_loop = ctx.loop_merge_block;
let prev_continue = ctx.loop_continue_block;
ctx.loop_merge_block = Some(merge_block);
ctx.loop_continue_block = Some(loop_block);
builder.ins().jump(loop_block, &[]);
builder.switch_to_block(loop_block);
let cond_value = Self::compile_expr(builder, ctx, condition)?;
builder
.ins()
.brif(cond_value, body_block, &[], merge_block, &[]);
builder.switch_to_block(body_block);
builder.seal_block(body_block);
Self::compile_expr(builder, ctx, body)?;
if !ctx.block_terminated {
builder.ins().jump(loop_block, &[]);
}
ctx.block_terminated = false;
builder.seal_block(loop_block);
builder.switch_to_block(merge_block);
builder.seal_block(merge_block);
ctx.loop_merge_block = prev_loop;
ctx.loop_continue_block = prev_continue;
Ok(builder.ins().iconst(types::I64, 0))
}
fn compile_for(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
var_name: &str,
iter: &Expr,
body: &Expr,
) -> Result<Value> {
let (start, end, inclusive) = match &iter.kind {
crate::frontend::ast::ExprKind::Range {
start,
end,
inclusive,
} => (start, end, *inclusive),
_ => return Err(anyhow!("JIT-005: For loop requires range iterator")),
};
let start_val = Self::compile_expr(builder, ctx, start)?;
let end_val = Self::compile_expr(builder, ctx, end)?;
let loop_var = builder.declare_var(types::I64);
builder.def_var(loop_var, start_val);
ctx.variables.insert(var_name.to_string(), loop_var);
let loop_block = builder.create_block();
let body_block = builder.create_block();
let incr_block = builder.create_block(); let merge_block = builder.create_block();
let prev_loop = ctx.loop_merge_block;
let prev_continue = ctx.loop_continue_block;
ctx.loop_merge_block = Some(merge_block);
ctx.loop_continue_block = Some(incr_block);
builder.ins().jump(loop_block, &[]);
builder.switch_to_block(loop_block);
let current_val = builder.use_var(loop_var);
let cond = if inclusive {
builder.ins().icmp(
cranelift_codegen::ir::condcodes::IntCC::SignedLessThanOrEqual,
current_val,
end_val,
)
} else {
builder.ins().icmp(
cranelift_codegen::ir::condcodes::IntCC::SignedLessThan,
current_val,
end_val,
)
};
builder.ins().brif(cond, body_block, &[], merge_block, &[]);
builder.switch_to_block(body_block);
builder.seal_block(body_block);
Self::compile_expr(builder, ctx, body)?;
if !ctx.block_terminated {
builder.ins().jump(incr_block, &[]);
}
ctx.block_terminated = false;
builder.switch_to_block(incr_block);
builder.seal_block(incr_block);
let current_val = builder.use_var(loop_var);
let one = builder.ins().iconst(types::I64, 1);
let next_val = builder.ins().iadd(current_val, one);
builder.def_var(loop_var, next_val);
builder.ins().jump(loop_block, &[]);
builder.seal_block(loop_block);
builder.switch_to_block(merge_block);
builder.seal_block(merge_block);
ctx.loop_merge_block = prev_loop;
ctx.loop_continue_block = prev_continue;
Ok(builder.ins().iconst(types::I64, 0))
}
fn compile_break(builder: &mut FunctionBuilder, ctx: &mut CompileContext) -> Result<Value> {
match ctx.loop_merge_block {
Some(merge_block) => {
let dummy = builder.ins().iconst(types::I64, 0);
builder.ins().jump(merge_block, &[]);
ctx.block_terminated = true;
Ok(dummy)
}
None => Err(anyhow!("JIT-005: Break outside of loop")),
}
}
fn compile_continue(builder: &mut FunctionBuilder, ctx: &mut CompileContext) -> Result<Value> {
match ctx.loop_continue_block {
Some(continue_block) => {
let dummy = builder.ins().iconst(types::I64, 0);
builder.ins().jump(continue_block, &[]);
ctx.block_terminated = true;
Ok(dummy)
}
None => Err(anyhow!("JIT-005B: Continue outside of loop")),
}
}
fn compile_return(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
value: Option<&Expr>,
) -> Result<Value> {
let return_value = match value {
Some(expr) => Self::compile_expr(builder, ctx, expr)?,
None => builder.ins().iconst(types::I64, 0),
};
builder.ins().return_(&[return_value]);
ctx.block_terminated = true;
Ok(return_value)
}
fn compile_assign(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
target: &Expr,
value: &Expr,
) -> Result<Value> {
let var_name = match &target.kind {
crate::frontend::ast::ExprKind::Identifier(name) => name,
_ => return Err(anyhow!("JIT-005: Assignment target must be identifier")),
};
let new_val = Self::compile_expr(builder, ctx, value)?;
let var = ctx
.variables
.get(var_name)
.ok_or_else(|| anyhow!("JIT-005: Undefined variable: {var_name}"))?;
builder.def_var(*var, new_val);
Ok(builder.ins().iconst(types::I64, 0))
}
fn compile_block(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
exprs: &[Expr],
) -> Result<Value> {
if exprs.is_empty() {
return Ok(builder.ins().iconst(types::I64, 0));
}
let mut last_value = None;
for expr in exprs {
last_value = Some(Self::compile_expr(builder, ctx, expr)?);
}
Ok(last_value.expect("Non-empty block should have at least one value"))
}
fn compile_if(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
condition: &Expr,
then_branch: &Expr,
else_branch: Option<&Expr>,
) -> Result<Value> {
let cond_value = Self::compile_expr(builder, ctx, condition)?;
let then_block = builder.create_block();
let else_block = builder.create_block();
let merge_block = builder.create_block();
let result_var = builder.declare_var(types::I64);
builder
.ins()
.brif(cond_value, then_block, &[], else_block, &[]);
builder.switch_to_block(then_block);
builder.seal_block(then_block);
let then_value = Self::compile_expr(builder, ctx, then_branch)?;
if !ctx.block_terminated {
builder.def_var(result_var, then_value);
builder.ins().jump(merge_block, &[]);
}
let then_terminated = ctx.block_terminated;
ctx.block_terminated = false;
builder.switch_to_block(else_block);
builder.seal_block(else_block);
let else_value = if let Some(else_expr) = else_branch {
Self::compile_expr(builder, ctx, else_expr)?
} else {
builder.ins().iconst(types::I64, 0)
};
if !ctx.block_terminated {
builder.def_var(result_var, else_value);
builder.ins().jump(merge_block, &[]);
}
let else_terminated = ctx.block_terminated;
ctx.block_terminated = false;
builder.switch_to_block(merge_block);
builder.seal_block(merge_block);
if then_terminated && else_terminated {
ctx.block_terminated = true;
let dummy = builder.ins().iconst(types::I64, 0);
builder.ins().return_(&[dummy]);
Ok(dummy)
} else {
let result = builder.use_var(result_var);
Ok(result)
}
}
fn compile_let(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
name: &str,
value: &Expr,
body: &Expr,
) -> Result<Value> {
if let ExprKind::Tuple(elements) = &value.kind {
for (i, elem) in elements.iter().enumerate() {
let elem_value = Self::compile_expr(builder, ctx, elem)?;
let elem_var = builder.declare_var(types::I64);
builder.def_var(elem_var, elem_value);
let elem_name = format!("{name}${i}");
ctx.variables.insert(elem_name, elem_var);
}
ctx.tuple_sizes.insert(name.to_string(), elements.len());
let var = builder.declare_var(types::I64);
let dummy = builder.ins().iconst(types::I64, 0);
builder.def_var(var, dummy);
ctx.variables.insert(name.to_string(), var);
} else {
let init_value = Self::compile_expr(builder, ctx, value)?;
let var = builder.declare_var(types::I64);
builder.def_var(var, init_value);
ctx.variables.insert(name.to_string(), var);
}
let result = Self::compile_expr(builder, ctx, body)?;
Ok(result)
}
fn compile_identifier(
builder: &mut FunctionBuilder,
ctx: &CompileContext,
name: &str,
) -> Result<Value> {
let var = ctx
.variables
.get(name)
.ok_or_else(|| anyhow!("Undefined variable: {name}"))?;
Ok(builder.use_var(*var))
}
fn compile_call(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
func: &Expr,
args: &[Expr],
) -> Result<Value> {
let func_name = match &func.kind {
ExprKind::Identifier(name) => name,
_ => return Err(anyhow!("JIT-002: Only direct function calls supported")),
};
let func_ref = *ctx
.functions
.get(func_name)
.ok_or_else(|| anyhow!("Undefined function: {func_name}"))?;
let mut arg_values = Vec::new();
for arg in args {
arg_values.push(Self::compile_expr(builder, ctx, arg)?);
}
let call_inst = builder.ins().call(func_ref, &arg_values);
let results = builder.inst_results(call_inst);
Ok(results[0])
}
fn compile_tuple(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
elements: &[Expr],
) -> Result<Value> {
if elements.is_empty() {
return Err(anyhow!("JIT-007: Empty tuples not supported"));
}
let tuple_id = ctx.next_var;
ctx.next_var += 1;
let tuple_name = format!("$tuple{tuple_id}");
for (i, elem) in elements.iter().enumerate() {
let elem_value = Self::compile_expr(builder, ctx, elem)?;
let elem_var = builder.declare_var(types::I64);
builder.def_var(elem_var, elem_value);
let elem_name = format!("{tuple_name}${i}");
ctx.variables.insert(elem_name, elem_var);
}
ctx.tuple_sizes.insert(tuple_name.clone(), elements.len());
ctx.variables
.insert(tuple_name, builder.declare_var(types::I64));
Ok(builder.ins().iconst(types::I64, i64::from(tuple_id)))
}
fn compile_field_access(
builder: &mut FunctionBuilder,
ctx: &mut CompileContext,
object: &Expr,
field: &str,
) -> Result<Value> {
let field_idx: usize = field
.parse()
.map_err(|_| anyhow!("JIT-007: Field must be numeric index: {field}"))?;
if let ExprKind::Identifier(var_name) = &object.kind {
if let Some(&tuple_size) = ctx.tuple_sizes.get(var_name) {
if field_idx >= tuple_size {
return Err(anyhow!(
"JIT-007: Tuple index {field_idx} out of bounds (size {tuple_size})"
));
}
let elem_name = format!("{var_name}${field_idx}");
if let Some(&var) = ctx.variables.get(&elem_name) {
return Ok(builder.use_var(var));
}
}
let tuple_handle = Self::compile_expr(builder, ctx, object)?;
let tuple_id = tuple_handle; let tuple_name = format!("$tuple{tuple_id}");
let elem_name = format!("{tuple_name}${field_idx}");
if let Some(&var) = ctx.variables.get(&elem_name) {
return Ok(builder.use_var(var));
}
Err(anyhow!("JIT-007: Variable '{var_name}' is not a tuple"))
} else {
Err(anyhow!(
"JIT-007: Field access only supported on identifiers"
))
}
}
fn next_variable(&mut self) -> Variable {
let var = Variable::new(self.next_var as usize);
self.next_var += 1;
var
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::parser::Parser;
#[test]
fn test_jit_compiler_creation() {
let compiler = JitCompiler::new();
assert!(
compiler.is_ok(),
"JIT compiler should initialize successfully"
);
}
#[test]
fn test_jit_simple_literal() {
let code = "42";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile literal: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 42);
}
#[test]
fn test_jit_simple_addition() {
let code = "2 + 3";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile addition: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 5);
}
#[test]
fn test_jit_complex_arithmetic() {
let code = "(10 + 5) * 2 - 8 / 4";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(
result.is_ok(),
"JIT should compile complex arithmetic: {result:?}"
);
assert_eq!(result.expect("operation should succeed in test"), 28); }
#[test]
fn test_jit_subtraction() {
let code = "10 - 3";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile subtraction");
assert_eq!(result.expect("operation should succeed in test"), 7);
}
#[test]
fn test_jit_multiplication() {
let code = "6 * 7";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile multiplication");
assert_eq!(result.expect("operation should succeed in test"), 42);
}
#[test]
fn test_jit_division() {
let code = "100 / 4";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile division");
assert_eq!(result.expect("operation should succeed in test"), 25);
}
#[test]
fn test_jit_modulo() {
let code = "17 % 5";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile modulo: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 2);
}
#[test]
fn test_jit_negative_literal() {
let code = "-42";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile negative: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), -42);
}
#[test]
fn test_jit_comparison_equal_true() {
let code = "5 == 5";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile equal: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 1); }
#[test]
fn test_jit_comparison_equal_false() {
let code = "5 == 6";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile equal: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 0); }
#[test]
fn test_jit_comparison_less_than() {
let code = "3 < 5";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile less than: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 1); }
#[test]
fn test_jit_comparison_greater_than() {
let code = "10 > 3";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile greater: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 1); }
#[test]
fn test_jit_bool_true() {
let code = "true";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile true: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 1);
}
#[test]
fn test_jit_bool_false() {
let code = "false";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile false: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 0);
}
#[test]
fn test_jit_if_else_true() {
let code = "if true { 42 } else { 0 }";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile if-else: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 42);
}
#[test]
fn test_jit_if_else_false() {
let code = "if false { 42 } else { 99 }";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile if-else: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 99);
}
#[test]
fn test_jit_let_binding() {
let code = "let x = 10 in x * 2";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile let: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 20);
}
#[test]
fn test_jit_nested_let() {
let code = "let x = 5 in let y = 3 in x + y";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile nested let: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 8);
}
#[test]
fn test_jit_block() {
let code = "{ 1; 2; 3 }";
let ast = Parser::new(code)
.parse()
.expect("operation should succeed in test");
let mut compiler = JitCompiler::new().expect("operation should succeed in test");
let result = compiler.compile_and_execute(&ast);
assert!(result.is_ok(), "JIT should compile block: {result:?}");
assert_eq!(result.expect("operation should succeed in test"), 3);
}
}