#![allow(dead_code)]
use super::instruction::{Instruction, OpCode, Operand, ConstantPool, ConstantValue, Bytecode};
use crate::ast::{Expr, Program, Literal, Formals, Binding};
use crate::diagnostics::{Result, Error, Spanned};
use crate::utils::SymbolId;
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct CompilerOptions {
pub debug_info: bool,
pub profiling: bool,
pub target_stack_size: usize,
pub aggressive_optimization: bool,
pub max_local_variables: usize,
}
impl Default for CompilerOptions {
fn default() -> Self {
Self {
debug_info: false,
profiling: false,
target_stack_size: 256,
aggressive_optimization: false,
max_local_variables: 256,
}
}
}
#[derive(Debug, Clone)]
pub struct CompilationResult {
pub bytecode: Bytecode,
pub constant_pool: ConstantPool,
pub stats: CompilationStats,
}
#[derive(Debug, Clone)]
pub struct CompilationStats {
pub expressions_compiled: usize,
pub instructions_generated: usize,
pub constants_created: usize,
pub compilation_time_us: u64,
pub memory_usage_bytes: usize,
pub local_variables: usize,
pub max_stack_depth: usize,
}
#[derive(Debug, Clone)]
struct LocalVariable {
name: String,
index: u16,
scope_depth: usize,
mutable: bool,
}
#[derive(Debug)]
#[allow(dead_code)]
struct CompilerContext {
locals: Vec<LocalVariable>,
scope_depth: usize,
stack_depth: usize,
max_stack_depth: usize,
loop_stack: Vec<LoopContext>,
function_stack: Vec<FunctionContext>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct LoopContext {
start_label: usize,
end_label: usize,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct FunctionContext {
name: Option<String>,
param_count: usize,
return_label: usize,
}
pub struct BytecodeCompiler {
options: CompilerOptions,
stats: CompilationStats,
}
impl BytecodeCompiler {
pub fn new(options: CompilerOptions) -> Self {
Self {
options,
stats: CompilationStats {
expressions_compiled: 0,
instructions_generated: 0,
constants_created: 0,
compilation_time_us: 0,
memory_usage_bytes: 0,
local_variables: 0,
max_stack_depth: 0,
},
}
}
pub fn compile_program(&mut self, program: &Program) -> Result<CompilationResult> {
let start_time = Instant::now();
let mut bytecode = Bytecode::new();
let mut constant_pool = ConstantPool::new();
let mut context = CompilerContext {
locals: Vec::new(),
scope_depth: 0,
stack_depth: 0,
max_stack_depth: 0,
loop_stack: Vec::new(),
function_stack: Vec::new(),
};
for expr in &program.expressions {
self.compile_expression_internal(&expr.inner, &mut bytecode, &mut constant_pool, &mut context)?;
if expr != program.expressions.last().unwrap() {
bytecode.add_instruction(Instruction::new(OpCode::Pop));
context.stack_depth = context.stack_depth.saturating_sub(1);
}
}
bytecode.add_instruction(Instruction::new(OpCode::Halt));
bytecode.constants = constant_pool.clone();
bytecode.local_count = context.locals.len();
bytecode.max_stack_depth = context.max_stack_depth;
self.stats.expressions_compiled += program.expressions.len();
self.stats.instructions_generated += bytecode.instructions.len();
self.stats.constants_created += constant_pool.len();
self.stats.compilation_time_us += start_time.elapsed().as_micros() as u64;
self.stats.memory_usage_bytes += bytecode.estimated_size();
self.stats.local_variables = context.locals.len();
self.stats.max_stack_depth = context.max_stack_depth;
Ok(CompilationResult {
bytecode,
constant_pool,
stats: self.stats.clone(),
})
}
pub fn compile_expression(&mut self, expr: &Expr) -> Result<CompilationResult> {
let start_time = Instant::now();
let mut bytecode = Bytecode::new();
let mut constant_pool = ConstantPool::new();
let mut context = CompilerContext {
locals: Vec::new(),
scope_depth: 0,
stack_depth: 0,
max_stack_depth: 0,
loop_stack: Vec::new(),
function_stack: Vec::new(),
};
self.compile_expression_internal(expr, &mut bytecode, &mut constant_pool, &mut context)?;
bytecode.add_instruction(Instruction::new(OpCode::Halt));
bytecode.constants = constant_pool.clone();
bytecode.local_count = context.locals.len();
bytecode.max_stack_depth = context.max_stack_depth;
self.stats.expressions_compiled += 1;
self.stats.instructions_generated += bytecode.instructions.len();
self.stats.constants_created += constant_pool.len();
self.stats.compilation_time_us += start_time.elapsed().as_micros() as u64;
self.stats.memory_usage_bytes += bytecode.estimated_size();
self.stats.local_variables = context.locals.len();
self.stats.max_stack_depth = context.max_stack_depth;
Ok(CompilationResult {
bytecode,
constant_pool,
stats: self.stats.clone(),
})
}
fn compile_expression_internal(
&mut self,
expr: &Expr,
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
match expr {
Expr::Literal(literal) => {
self.compile_literal(literal, bytecode, constant_pool, context)
}
Expr::Symbol(symbol) => {
let symbol_id = crate::utils::intern_symbol(symbol);
self.compile_symbol_reference(symbol_id, bytecode, constant_pool, context)
}
Expr::Application { operator, operands } => {
self.compile_application(operator, operands, bytecode, constant_pool, context)
}
Expr::Lambda { formals, body, metadata: _ } => {
self.compile_lambda(formals, body, bytecode, constant_pool, context)
}
Expr::Define { name, value, metadata: _ } => {
self.compile_define(name, value, bytecode, constant_pool, context)
}
Expr::Set { name, value } => {
self.compile_set(name, value, bytecode, constant_pool, context)
}
Expr::If { test, consequent, alternative } => {
self.compile_if(test, consequent, alternative.as_deref(), bytecode, constant_pool, context)
}
Expr::Begin(expressions) => {
self.compile_begin(expressions, bytecode, constant_pool, context)
}
Expr::Let { bindings, body } => {
self.compile_let(bindings, body, bytecode, constant_pool, context)
}
Expr::LetRec { bindings, body } => {
self.compile_letrec(bindings, body, bytecode, constant_pool, context)
}
Expr::Quote(quoted) => {
self.compile_quote(quoted, bytecode, constant_pool, context)
}
_ => Err(Box::new(Error::compilation_error(format!("Unsupported expression type: {expr:?}")))),
}
}
fn compile_literal(
&mut self,
literal: &Literal,
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
let constant_value = match literal {
Literal::ExactInteger(i) => ConstantValue::Number(*i as f64),
Literal::InexactReal(f) => ConstantValue::Number(*f),
Literal::Number(f) => ConstantValue::Number(*f),
Literal::Rational { numerator, denominator } => {
ConstantValue::Number(*numerator as f64 / *denominator as f64)
}
Literal::Complex { real, imaginary } => {
if *imaginary != 0.0 {
return Err(Box::new(Error::compilation_error("Complex literals not yet fully supported in bytecode".to_string())));
}
ConstantValue::Number(*real)
}
Literal::String(s) => ConstantValue::String(s.clone()),
Literal::Boolean(b) => ConstantValue::Boolean(*b),
Literal::Character(c) => ConstantValue::String(c.to_string()), Literal::Bytevector(_bytes) => {
return Err(Box::new(Error::compilation_error("Bytevector literals not yet supported in bytecode".to_string())));
}
Literal::Nil => {
ConstantValue::String("()".to_string()) }
Literal::Unspecified => {
ConstantValue::String("#<unspecified>".to_string())
}
};
let const_index = constant_pool.add_constant(constant_value);
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(const_index)));
self.push_stack(context);
Ok(())
}
fn compile_symbol_reference(
&mut self,
symbol: SymbolId,
bytecode: &mut Bytecode,
_constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
if let Some(local) = self.find_local(symbol, context) {
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadLocal, Operand::LocalIndex(local.index)));
} else {
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadGlobal, Operand::Symbol(symbol)));
}
self.push_stack(context);
Ok(())
}
fn compile_application(
&mut self,
operator: &Spanned<Expr>,
operands: &[Spanned<Expr>],
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
if let Expr::Symbol(symbol) = &operator.inner {
let symbol_id = crate::utils::intern_symbol(symbol);
if let Some(builtin_op) = self.get_builtin_operation(symbol_id) {
return self.compile_builtin_operation(builtin_op, operands, bytecode, constant_pool, context);
}
}
for operand in operands {
self.compile_expression_internal(&operand.inner, bytecode, constant_pool, context)?;
}
self.compile_expression_internal(&operator.inner, bytecode, constant_pool, context)?;
let arg_count = operands.len() as u32;
bytecode.add_instruction(Instruction::with_operand(OpCode::Call, Operand::U32(arg_count)));
context.stack_depth = context.stack_depth.saturating_sub(operands.len() + 1);
self.push_stack(context);
Ok(())
}
fn compile_lambda(
&mut self,
formals: &Formals,
body: &[Spanned<Expr>],
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
let mut lambda_bytecode = Bytecode::new();
let mut lambda_context = CompilerContext {
locals: Vec::new(),
scope_depth: 0,
stack_depth: 0,
max_stack_depth: 0,
loop_stack: Vec::new(),
function_stack: Vec::new(),
};
let _param_count = match formals {
Formals::Fixed(params) => {
for (i, param) in params.iter().enumerate() {
let local = LocalVariable {
name: param.clone(),
index: i as u16,
scope_depth: 0,
mutable: false,
};
lambda_context.locals.push(local);
}
params.len()
}
Formals::Variable(param) => {
let local = LocalVariable {
name: param.clone(),
index: 0,
scope_depth: 0,
mutable: false,
};
lambda_context.locals.push(local);
1
}
Formals::Mixed { fixed, rest } => {
for (i, param) in fixed.iter().enumerate() {
let local = LocalVariable {
name: param.clone(),
index: i as u16,
scope_depth: 0,
mutable: false,
};
lambda_context.locals.push(local);
}
let rest_local = LocalVariable {
name: rest.clone(),
index: fixed.len() as u16,
scope_depth: 0,
mutable: false,
};
lambda_context.locals.push(rest_local);
fixed.len() + 1
}
Formals::Keyword { fixed, rest, keywords: _ } => {
for (i, param) in fixed.iter().enumerate() {
let local = LocalVariable {
name: param.clone(),
index: i as u16,
scope_depth: 0,
mutable: false,
};
lambda_context.locals.push(local);
}
let mut count = fixed.len();
if let Some(rest) = rest {
let rest_local = LocalVariable {
name: rest.clone(),
index: count as u16,
scope_depth: 0,
mutable: false,
};
lambda_context.locals.push(rest_local);
count += 1;
}
count
}
};
for (i, expr) in body.iter().enumerate() {
self.compile_expression_internal(&expr.inner, &mut lambda_bytecode, constant_pool, &mut lambda_context)?;
if i < body.len() - 1 {
lambda_bytecode.add_instruction(Instruction::new(OpCode::Pop));
lambda_context.stack_depth = lambda_context.stack_depth.saturating_sub(1);
}
}
lambda_bytecode.add_instruction(Instruction::new(OpCode::Return));
let lambda_const = ConstantValue::Bytecode(lambda_bytecode.instructions);
let const_index = constant_pool.add_constant(lambda_const);
bytecode.add_instruction(Instruction::with_operand(OpCode::MakeClosure, Operand::ConstIndex(const_index)));
self.push_stack(context);
Ok(())
}
fn compile_define(
&mut self,
name: &str,
value: &Spanned<Expr>,
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
self.compile_expression_internal(&value.inner, bytecode, constant_pool, context)?;
let symbol_id = crate::utils::intern_symbol(name).id();
bytecode.add_instruction(Instruction::with_operand(OpCode::StoreGlobal, Operand::Symbol(SymbolId::new(symbol_id))));
self.pop_stack(context);
let unspec_const = constant_pool.add_constant(ConstantValue::Boolean(false)); bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(unspec_const)));
self.push_stack(context);
Ok(())
}
fn compile_set(
&mut self,
name: &str,
value: &Spanned<Expr>,
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
self.compile_expression_internal(&value.inner, bytecode, constant_pool, context)?;
let symbol_id = crate::utils::intern_symbol(name).id();
if let Some(local) = self.find_local(SymbolId::new(symbol_id), context) {
if !local.mutable {
return Err(Box::new(Error::compilation_error(format!("Cannot assign to immutable variable: {name}"))));
}
bytecode.add_instruction(Instruction::with_operand(OpCode::StoreLocal, Operand::LocalIndex(local.index)));
} else {
bytecode.add_instruction(Instruction::with_operand(OpCode::StoreGlobal, Operand::Symbol(SymbolId::new(symbol_id))));
}
self.pop_stack(context);
let unspec_const = constant_pool.add_constant(ConstantValue::Boolean(false));
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(unspec_const)));
self.push_stack(context);
Ok(())
}
fn compile_if(
&mut self,
condition: &Spanned<Expr>,
consequent: &Spanned<Expr>,
alternative: Option<&Spanned<Expr>>,
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
self.compile_expression_internal(&condition.inner, bytecode, constant_pool, context)?;
let false_jump = bytecode.instructions.len();
bytecode.add_instruction(Instruction::with_operand(OpCode::JumpIfFalse, Operand::JumpOffset(0))); self.pop_stack(context);
self.compile_expression_internal(&consequent.inner, bytecode, constant_pool, context)?;
let end_jump = bytecode.instructions.len();
bytecode.add_instruction(Instruction::with_operand(OpCode::Jump, Operand::JumpOffset(0)));
let alternative_start = bytecode.instructions.len();
if let Operand::JumpOffset(offset) = &mut bytecode.instructions[false_jump].operand {
*offset = (alternative_start as i32) - (false_jump as i32);
}
self.pop_stack(context);
if let Some(alt) = alternative {
self.compile_expression_internal(&alt.inner, bytecode, constant_pool, context)?;
} else {
let unspec_const = constant_pool.add_constant(ConstantValue::Boolean(false));
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(unspec_const)));
self.push_stack(context);
}
let end_pos = bytecode.instructions.len();
if let Operand::JumpOffset(offset) = &mut bytecode.instructions[end_jump].operand {
*offset = (end_pos as i32) - (end_jump as i32);
}
Ok(())
}
fn compile_begin(
&mut self,
expressions: &[Spanned<Expr>],
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
if expressions.is_empty() {
let unspec_const = constant_pool.add_constant(ConstantValue::Boolean(false));
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(unspec_const)));
self.push_stack(context);
return Ok(());
}
for (i, expr) in expressions.iter().enumerate() {
self.compile_expression_internal(&expr.inner, bytecode, constant_pool, context)?;
if i < expressions.len() - 1 {
bytecode.add_instruction(Instruction::new(OpCode::Pop));
self.pop_stack(context);
}
}
Ok(())
}
fn compile_let(
&mut self,
bindings: &[Binding],
body: &[Spanned<Expr>],
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
self.enter_scope(context);
for binding in bindings {
self.compile_expression_internal(&binding.value.inner, bytecode, constant_pool, context)?;
let local_index = context.locals.len() as u16;
let local = LocalVariable {
name: binding.name.clone(),
index: local_index,
scope_depth: context.scope_depth,
mutable: false,
};
context.locals.push(local);
bytecode.add_instruction(Instruction::with_operand(OpCode::StoreLocal, Operand::LocalIndex(local_index)));
self.pop_stack(context); }
self.compile_begin(body, bytecode, constant_pool, context)?;
self.exit_scope(context);
Ok(())
}
fn compile_letrec(
&mut self,
bindings: &[Binding],
body: &[Spanned<Expr>],
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
self.enter_scope(context);
let unspec_const = constant_pool.add_constant(ConstantValue::Boolean(false));
for binding in bindings {
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(unspec_const)));
self.push_stack(context);
let local_index = context.locals.len() as u16;
let local = LocalVariable {
name: binding.name.clone(),
index: local_index,
scope_depth: context.scope_depth,
mutable: true, };
context.locals.push(local);
bytecode.add_instruction(Instruction::with_operand(OpCode::StoreLocal, Operand::LocalIndex(local_index)));
self.pop_stack(context);
}
for binding in bindings {
self.compile_expression_internal(&binding.value.inner, bytecode, constant_pool, context)?;
if let Some(local) = self.find_local_by_name(&binding.name, context) {
bytecode.add_instruction(Instruction::with_operand(OpCode::StoreLocal, Operand::LocalIndex(local.index)));
self.pop_stack(context);
} else {
return Err(Box::new(Error::compilation_error(format!("Internal error: letrec binding not found: {}", binding.name))));
}
}
self.compile_begin(body, bytecode, constant_pool, context)?;
self.exit_scope(context);
Ok(())
}
fn compile_quote(
&mut self,
quoted: &Expr,
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
match quoted {
Expr::Literal(literal) => {
self.compile_literal(literal, bytecode, constant_pool, context)
}
Expr::Symbol(symbol) => {
let symbol_id = crate::utils::intern_symbol(symbol);
let const_value = ConstantValue::Symbol(symbol_id);
let const_index = constant_pool.add_constant(const_value);
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(const_index)));
self.push_stack(context);
Ok(())
}
_ => {
Err(Box::new(Error::compilation_error("Complex quoted expressions not yet supported in bytecode".to_string())))
}
}
}
fn compile_builtin_operation(
&mut self,
op: BuiltinOperation,
operands: &[Spanned<Expr>],
bytecode: &mut Bytecode,
constant_pool: &mut ConstantPool,
context: &mut CompilerContext,
) -> Result<()> {
match op {
BuiltinOperation::Add => {
for operand in operands {
self.compile_expression_internal(&operand.inner, bytecode, constant_pool, context)?;
}
for _ in 1..operands.len() {
bytecode.add_instruction(Instruction::new(OpCode::Add));
context.stack_depth = context.stack_depth.saturating_sub(1); }
if operands.is_empty() {
let zero_const = constant_pool.add_constant(ConstantValue::Number(0.0));
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(zero_const)));
self.push_stack(context);
}
Ok(())
}
BuiltinOperation::Subtract => {
if operands.is_empty() {
return Err(Box::new(Error::compilation_error("- requires at least one argument".to_string())));
}
self.compile_expression_internal(&operands[0].inner, bytecode, constant_pool, context)?;
if operands.len() == 1 {
bytecode.add_instruction(Instruction::new(OpCode::Neg));
} else {
for operand in &operands[1..] {
self.compile_expression_internal(&operand.inner, bytecode, constant_pool, context)?;
bytecode.add_instruction(Instruction::new(OpCode::Sub));
context.stack_depth = context.stack_depth.saturating_sub(1);
}
}
Ok(())
}
BuiltinOperation::Multiply => {
for operand in operands {
self.compile_expression_internal(&operand.inner, bytecode, constant_pool, context)?;
}
for _ in 1..operands.len() {
bytecode.add_instruction(Instruction::new(OpCode::Mul));
context.stack_depth = context.stack_depth.saturating_sub(1);
}
if operands.is_empty() {
let one_const = constant_pool.add_constant(ConstantValue::Number(1.0));
bytecode.add_instruction(Instruction::with_operand(OpCode::LoadConst, Operand::ConstIndex(one_const)));
self.push_stack(context);
}
Ok(())
}
BuiltinOperation::Equal => {
if operands.len() != 2 {
return Err(Box::new(Error::compilation_error("= requires exactly 2 arguments".to_string())));
}
self.compile_expression_internal(&operands[0].inner, bytecode, constant_pool, context)?;
self.compile_expression_internal(&operands[1].inner, bytecode, constant_pool, context)?;
bytecode.add_instruction(Instruction::new(OpCode::Eq));
context.stack_depth = context.stack_depth.saturating_sub(1);
Ok(())
}
BuiltinOperation::Cons => {
if operands.len() != 2 {
return Err(Box::new(Error::compilation_error("cons requires exactly 2 arguments".to_string())));
}
self.compile_expression_internal(&operands[0].inner, bytecode, constant_pool, context)?;
self.compile_expression_internal(&operands[1].inner, bytecode, constant_pool, context)?;
bytecode.add_instruction(Instruction::new(OpCode::Cons));
context.stack_depth = context.stack_depth.saturating_sub(1);
Ok(())
}
BuiltinOperation::Car => {
if operands.len() != 1 {
return Err(Box::new(Error::compilation_error("car requires exactly 1 argument".to_string())));
}
self.compile_expression_internal(&operands[0].inner, bytecode, constant_pool, context)?;
bytecode.add_instruction(Instruction::new(OpCode::Car));
Ok(())
}
BuiltinOperation::Cdr => {
if operands.len() != 1 {
return Err(Box::new(Error::compilation_error("cdr requires exactly 1 argument".to_string())));
}
self.compile_expression_internal(&operands[0].inner, bytecode, constant_pool, context)?;
bytecode.add_instruction(Instruction::new(OpCode::Cdr));
Ok(())
}
}
}
fn get_builtin_operation(&self, _symbol: SymbolId) -> Option<BuiltinOperation> {
None
}
fn find_local(&self, _symbol: SymbolId, _context: &CompilerContext) -> Option<&LocalVariable> {
None
}
fn find_local_by_name<'a>(&self, name: &str, context: &'a CompilerContext) -> Option<&'a LocalVariable> {
context.locals.iter().rev().find(|local| local.name == name)
}
fn enter_scope(&self, context: &mut CompilerContext) {
context.scope_depth += 1;
}
fn exit_scope(&self, context: &mut CompilerContext) {
context.scope_depth = context.scope_depth.saturating_sub(1);
context.locals.retain(|local| local.scope_depth <= context.scope_depth);
}
fn push_stack(&self, context: &mut CompilerContext) {
context.stack_depth += 1;
context.max_stack_depth = context.max_stack_depth.max(context.stack_depth);
}
fn pop_stack(&self, context: &mut CompilerContext) {
context.stack_depth = context.stack_depth.saturating_sub(1);
}
pub fn get_stats(&self) -> super::CompilerStats {
super::CompilerStats {
expressions_compiled: self.stats.expressions_compiled,
instructions_generated: self.stats.instructions_generated,
constants_count: self.stats.constants_created,
compilation_time_us: self.stats.compilation_time_us,
memory_usage_bytes: self.stats.memory_usage_bytes,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BuiltinOperation {
Add,
Subtract,
Multiply,
Equal,
Cons,
Car,
Cdr,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::Literal;
#[test]
fn test_compile_literal() {
let mut compiler = BytecodeCompiler::new(CompilerOptions::default());
let expr = Expr::Literal(Literal::Number(42.0));
let result = compiler.compile_expression(&expr).unwrap();
assert!(result.bytecode.instructions.len() > 0);
assert_eq!(result.constant_pool.len(), 1);
assert_eq!(result.bytecode.instructions[0].opcode, OpCode::LoadConst);
assert_eq!(result.bytecode.instructions[1].opcode, OpCode::Halt);
}
#[test]
fn test_compile_begin() {
let mut compiler = BytecodeCompiler::new(CompilerOptions::default());
let expressions = vec![
Spanned::new(Expr::Literal(Literal::Number(1.0)), (0..0).into()),
Spanned::new(Expr::Literal(Literal::Number(2.0)), (0..0).into()),
];
let expr = Expr::Begin(expressions);
let result = compiler.compile_expression(&expr).unwrap();
assert!(result.bytecode.instructions.len() >= 4); assert_eq!(result.constant_pool.len(), 2);
}
#[test]
fn test_compiler_stats() {
let mut compiler = BytecodeCompiler::new(CompilerOptions::default());
let expr = Expr::Literal(Literal::String("hello".to_string()));
let _result = compiler.compile_expression(&expr).unwrap();
let stats = compiler.get_stats();
assert_eq!(stats.expressions_compiled, 1);
assert!(stats.instructions_generated > 0);
assert!(stats.constants_count > 0);
assert!(stats.compilation_time_us > 0);
}
}