mod value;
pub use value::*;
mod runtime;
pub use runtime::*;
mod scoping;
pub use scoping::*;
mod functions;
pub use functions::*;
mod builtins;
pub use builtins::*;
use crate::ast::{Block, Expr, ForDirection, Literal, Program, Stmt, ClassDecl, Type};
use anyhow::Result;
use std::collections::HashMap;
pub type DebugBreakpointCheck = Box<dyn FnMut(&str) -> bool>;
pub type DebugBreakpointHandler = Box<dyn FnMut(&mut Interpreter)>;
pub struct Interpreter {
runtime: RuntimeEnvironment,
scope_manager: ScopeManager,
functions: FunctionRegistry,
classes: HashMap<String, ClassDecl>,
builtins: BuiltinRegistry,
pub debug_breakpoint_check: Option<DebugBreakpointCheck>,
pub debug_breakpoint_handler: Option<DebugBreakpointHandler>,
}
impl Interpreter {
pub fn new(verbose: bool) -> Self {
Self {
runtime: RuntimeEnvironment::new(verbose),
scope_manager: ScopeManager::new(verbose),
functions: FunctionRegistry::new(),
classes: HashMap::new(),
builtins: create_default_registry(),
debug_breakpoint_check: None,
debug_breakpoint_handler: None,
}
}
pub fn current_scope(&self) -> &Scope {
self.runtime.current_scope()
}
pub fn get_variable_value(&self, name: &str) -> Option<Value> {
self.runtime.get_variable_value(name)
}
pub fn load_uses_clause(&mut self, uses: &[String]) -> Result<()> {
for unit_name in uses {
if self.runtime.is_verbose() {
eprintln!("[interpreter] Loading unit: {}", unit_name);
}
match unit_name.as_str() {
"SysUtils" | "Classes" => {
}
_ => {
return Err(anyhow::anyhow!("Unit '{}' not found", unit_name));
}
}
}
Ok(())
}
pub fn declare_block_vars(&mut self, block: &Block) -> Result<()> {
for var in &block.vars {
let default_value = Self::default_value_for_type(&var.variable_type)?;
self.runtime.declare_variable(&var.name, default_value)?;
}
Ok(())
}
fn default_value_for_type(typ: &crate::ast::Type) -> Result<Value> {
use crate::ast::{Type, SimpleType};
Ok(match typ {
Type::Simple(SimpleType::Integer) => Value::Integer(0),
Type::Simple(SimpleType::Real) => Value::Real(0.0),
Type::Simple(SimpleType::Boolean) => Value::Boolean(false),
Type::Simple(SimpleType::String) => Value::String("".to_string()),
Type::Simple(SimpleType::Char) => Value::Char('\0'),
Type::Integer => Value::Integer(0),
Type::Real => Value::Real(0.0),
Type::Boolean => Value::Boolean(false),
Type::Char => Value::Char('\0'),
Type::String => Value::String("".to_string()),
Type::WideString => Value::String("".to_string()),
Type::Array { element_type, range, .. } => {
let elem_default = Self::default_value_for_type(element_type)?;
if let Some((start, end)) = range {
let size = (end - start + 1).max(0) as usize;
Value::Array {
elements: vec![elem_default; size],
lower_bound: *start,
}
} else {
Value::Array {
elements: vec![],
lower_bound: 0,
}
}
}
Type::Record { fields, .. } => {
let mut field_values = HashMap::new();
for (name, field_type) in fields {
field_values.insert(name.clone(), Self::default_value_for_type(field_type)?);
}
Value::Record { fields: field_values }
}
Type::Enum { values } => {
if let Some(first) = values.first() {
Value::Enum {
type_name: first.clone(),
ordinal: 0,
}
} else {
Value::Nil
}
}
Type::Pointer(_) => Value::Nil,
Type::Set { .. } => Value::Nil,
Type::File { .. } => Value::Nil,
_ => Value::Nil,
})
}
pub fn register_block_types(&mut self, block: &Block) -> Result<()> {
for type_decl in &block.types {
if let Type::Enum { values } = &type_decl.type_definition {
let type_name = type_decl.name.clone();
for (i, value_name) in values.iter().enumerate() {
self.runtime.set_variable(
value_name.clone(),
Value::Enum {
type_name: type_name.clone(),
ordinal: i as i64,
},
);
}
}
}
Ok(())
}
pub fn register_block_classes(&mut self, block: &Block) -> Result<()> {
for class_decl in &block.classes {
self.register_class(class_decl)?;
}
Ok(())
}
pub fn register_class(&mut self, class_decl: &ClassDecl) -> Result<()> {
if self.classes.contains_key(&class_decl.name) {
return Err(anyhow::anyhow!("Class '{}' already defined", class_decl.name));
}
if self.runtime.is_verbose() {
eprintln!("[interpreter] Registering class: {}", class_decl.name);
}
self.classes.insert(class_decl.name.clone(), class_decl.clone());
Ok(())
}
pub fn register_block_functions(&mut self, block: &Block) -> Result<()> {
FunctionConverter::register_from_block(
&mut self.functions,
&block.functions,
&block.procedures,
)
}
pub fn run_program(&mut self, program: &Program) -> Result<()> {
if self.runtime.is_verbose() {
eprintln!("[interpreter] Running program '{}'", program.name);
}
self.load_uses_clause(&program.uses)?;
self.register_block_types(&program.block)?;
self.declare_block_vars(&program.block)?;
self.register_block_classes(&program.block)?;
self.register_block_functions(&program.block)?;
for stmt in &program.block.statements {
self.execute_stmt(stmt)?;
}
Ok(())
}
pub fn execute_stmt(&mut self, stmt: &Stmt) -> Result<()> {
match stmt {
Stmt::Assignment { target, value } => {
let val = self.eval_expr(value)?;
match target {
Expr::Variable(name) => {
self.runtime.set_variable(name.clone(), val);
},
Expr::FunctionCall { name, arguments } if name == "__index__" => {
if let Some((root_name, index_exprs)) = Self::collect_index_chain(target) {
let indices: Vec<i64> = index_exprs.iter()
.map(|e| self.eval_expr(e)?.as_integer())
.collect::<Result<_>>()?;
self.runtime.set_nested_element(root_name, &indices, val)?;
} else {
return Err(anyhow::anyhow!("Unsupported assignment target"));
}
},
_ => {
return Err(anyhow::anyhow!("Unsupported assignment target: {:?}", target));
}
}
},
Stmt::ProcedureCall { name, arguments } => {
let name_lower = name.to_lowercase();
if name_lower == "exit" {
let val = if let Some(arg) = arguments.first() {
Some(self.eval_expr(arg)?)
} else {
None
};
return Err(anyhow::Error::new(EarlyReturn { value: val }));
} else if name_lower == "inc" {
if let Some(first) = arguments.first() {
let var_name = match first {
Expr::Variable(n) => n.clone(),
_ => return Err(anyhow::anyhow!("inc requires a variable")),
};
let delta = if arguments.len() > 1 {
self.eval_expr(&arguments[1])?.as_integer()?
} else {
1
};
let current = self.runtime.get_variable_value(&var_name)
.ok_or_else(|| anyhow::anyhow!("inc: variable {} not found", var_name))?;
let new_val = match current {
Value::Integer(i) => Value::Integer(i + delta),
_ => return Err(anyhow::anyhow!("inc requires integer variable")),
};
self.runtime.set_variable(var_name, new_val);
}
} else if name_lower == "dec" {
if let Some(first) = arguments.first() {
let var_name = match first {
Expr::Variable(n) => n.clone(),
_ => return Err(anyhow::anyhow!("dec requires a variable")),
};
let delta = if arguments.len() > 1 {
self.eval_expr(&arguments[1])?.as_integer()?
} else {
1
};
let current = self.runtime.get_variable_value(&var_name)
.ok_or_else(|| anyhow::anyhow!("dec: variable {} not found", var_name))?;
let new_val = match current {
Value::Integer(i) => Value::Integer(i - delta),
_ => return Err(anyhow::anyhow!("dec requires integer variable")),
};
self.runtime.set_variable(var_name, new_val);
}
} else {
let args: Vec<Value> = arguments.iter()
.map(|arg| self.eval_expr(arg))
.collect::<Result<_>>()?;
self.call_procedure(name, &args)?;
}
},
Stmt::Block(block) => {
self.scope_manager.enter_scope();
for stmt in &block.statements {
self.execute_stmt(&stmt)?;
}
self.scope_manager.exit_scope();
},
Stmt::If { condition, then_branch, else_branch } => {
let cond_val = self.eval_expr(condition)?;
if self.is_truthy(&cond_val) {
for stmt in then_branch {
self.execute_stmt(&stmt)?;
}
} else if let Some(else_branch) = else_branch {
for stmt in else_branch {
self.execute_stmt(&stmt)?;
}
}
},
Stmt::While { condition, body } => {
loop {
let cond = self.eval_expr(condition)?;
if !self.is_truthy(&cond) {
break;
}
for stmt in body.iter() {
self.execute_stmt(stmt)?;
}
}
},
Stmt::For { var_name, start, end, direction, body } => {
let start_val = self.eval_expr(start)?;
let end_val = self.eval_expr(end)?;
let (start_num, end_num) = match (start_val, end_val) {
(Value::Integer(s), Value::Integer(e)) => (s, e),
_ => return Err(anyhow::anyhow!("For loop bounds must be integers")),
};
if matches!(direction, ForDirection::To) {
for i in start_num..=end_num {
self.runtime.set_variable(var_name.clone(), Value::Integer(i));
for stmt in body.clone() {
self.execute_stmt(&stmt)?;
}
}
} else {
for i in (end_num..=start_num).rev() {
self.runtime.set_variable(var_name.clone(), Value::Integer(i));
for stmt in body {
self.execute_stmt(stmt)?;
}
}
}
},
Stmt::Repeat { body, until_condition } => {
loop {
for stmt in body {
self.execute_stmt(stmt)?;
}
let until_result = self.eval_expr(until_condition)?;
if self.is_truthy(&until_result) {
break;
}
}
},
Stmt::Case { expression, branches, else_branch } => {
let expr_val = self.eval_expr(expression)?;
let mut matched = false;
for branch in branches {
for val_expr in &branch.values {
let branch_val = self.eval_expr(val_expr)?;
if self.values_equal(&expr_val, &branch_val) {
for stmt in &branch.body {
self.execute_stmt(stmt)?;
}
matched = true;
break;
}
}
if matched {
break;
}
}
if !matched {
if let Some(else_stmts) = else_branch {
for stmt in else_stmts {
self.execute_stmt(stmt)?;
}
}
}
},
Stmt::Try { try_block, except_clauses, finally_block } => {
let result = (|| {
for stmt in try_block {
self.execute_stmt(stmt)?;
}
Ok(()) as Result<()>
})();
if let Err(e) = result {
let mut handled = false;
for clause in except_clauses {
if clause.exception_type.is_none() ||
e.to_string().to_lowercase().contains(&clause.exception_type.as_ref().unwrap().to_lowercase()) {
for stmt in &clause.body {
self.execute_stmt(stmt)?;
}
handled = true;
break;
}
}
if !handled {
return Err(e);
}
}
if let Some(finally_stmts) = finally_block {
for stmt in finally_stmts {
self.execute_stmt(stmt)?;
}
}
},
Stmt::Raise { exception, message } => {
let exc_str = if let Some(exc) = exception {
format!("{:?}", self.eval_expr(exc)?)
} else {
"Re-raise".to_string()
};
return Err(anyhow::anyhow!("Exception: {}", exc_str));
},
Stmt::With { variable, statements } => {
let var_val = self.eval_expr(variable)?;
if let Value::Record { fields } | Value::Object { fields, .. } = var_val {
self.scope_manager.enter_scope();
for (k, v) in fields {
self.runtime.set_variable(k.clone(), v.clone());
}
for stmt in statements {
self.execute_stmt(stmt)?;
}
self.scope_manager.exit_scope();
}
},
Stmt::Empty => {},
Stmt::Goto { .. } | Stmt::Label { .. } => {
return Err(anyhow::anyhow!("Goto/Label not yet supported"));
},
}
Ok(())
}
pub fn eval_expr(&mut self, expr: &Expr) -> Result<Value> {
match expr {
Expr::Literal(literal) => self.eval_literal(literal),
Expr::Variable(name) => {
if let Some(val) = self.runtime.get_variable_value(name) {
Ok(val)
} else {
Err(anyhow::anyhow!("Undefined variable: {}", name))
}
},
Expr::BinaryOp { left, operator, right } => {
match operator.as_str() {
"and" => {
let left_val = self.eval_expr(left)?;
if !self.is_truthy(&left_val) {
Ok(Value::Boolean(false))
} else {
let right_val = self.eval_expr(right)?;
Ok(Value::Boolean(self.is_truthy(&right_val)))
}
},
"or" => {
let left_val = self.eval_expr(left)?;
if self.is_truthy(&left_val) {
Ok(Value::Boolean(true))
} else {
let right_val = self.eval_expr(right)?;
Ok(Value::Boolean(self.is_truthy(&right_val)))
}
},
"xor" => {
let left_val = self.eval_expr(left)?;
let right_val = self.eval_expr(right)?;
Ok(Value::Boolean(self.is_truthy(&left_val) ^ self.is_truthy(&right_val)))
},
_ => {
let left_val = self.eval_expr(left)?;
let right_val = self.eval_expr(right)?;
match operator.as_str() {
"+" => self.add_values(&left_val, &right_val),
"-" => self.sub_values(&left_val, &right_val),
"*" => self.mul_values(&left_val, &right_val),
"/" => self.div_values(&left_val, &right_val),
"div" => self.int_div_values(&left_val, &right_val),
"mod" => self.mod_values(&left_val, &right_val),
"shl" => self.shl_values(&left_val, &right_val),
"shr" => self.shr_values(&left_val, &right_val),
"=" => Ok(Value::Boolean(self.values_equal(&left_val, &right_val))),
"<>" => Ok(Value::Boolean(!self.values_equal(&left_val, &right_val))),
"<" | "<=" | ">" | ">=" => self.compare_values(&left_val, &right_val, operator),
"in" => {
match (&left_val, &right_val) {
(Value::Integer(n), Value::Set { elements }) => {
Ok(Value::Boolean(elements.contains(n)))
}
(Value::Enum { ordinal, .. }, Value::Set { elements }) => {
Ok(Value::Boolean(elements.contains(ordinal)))
}
_ => Err(anyhow::anyhow!("in requires ordinal value and set")),
}
}
_ => Err(anyhow::anyhow!("Unsupported operator: {}", operator)),
}
}
}
},
Expr::UnaryOp { operator, operand } => {
let right_val = self.eval_expr(operand)?;
match operator.as_str() {
"-" => {
match right_val {
Value::Integer(i) => Ok(Value::Integer(-i)),
Value::Real(f) => Ok(Value::Real(-f)),
_ => Err(anyhow::anyhow!("Unary minus requires numeric operand")),
}
},
"+" => Ok(right_val), "not" => Ok(Value::Boolean(!self.is_truthy(&right_val))),
_ => Err(anyhow::anyhow!("Unsupported unary operator: {}", operator)),
}
},
Expr::Set { elements } => {
let mut set = std::collections::HashSet::new();
for e in elements {
let val = self.eval_expr(e)?;
set.insert(val.as_integer()?);
}
Ok(Value::Set { elements: set })
},
Expr::FunctionCall { name, arguments } => {
let args: Vec<Value> = arguments.iter()
.map(|arg| self.eval_expr(arg))
.collect::<Result<_>>()?;
self.call_function(name, &args)
},
_ => Err(anyhow::anyhow!("Unsupported expression type")),
}
}
pub fn eval_literal(&self, literal: &Literal) -> Result<Value> {
match literal {
Literal::Integer(i) => Ok(Value::Integer(*i)),
Literal::Real(f) => Ok(Value::Real(*f)),
Literal::String(s) => Ok(Value::String(s.clone())),
Literal::Boolean(b) => Ok(Value::Boolean(*b)),
Literal::Char(c) => Ok(Value::Char(*c)),
Literal::Nil => Ok(Value::Nil),
Literal::WideString(_) => todo!("WideString literals not implemented"),
Literal::Set(_) => todo!("Set literals not implemented"),
}
}
pub fn call_function(&mut self, name: &str, args: &[Value]) -> Result<Value> {
if let Some((_, _, func)) = self.builtins.get_function(name) {
return func(args);
}
if let Some(user_func) = self.functions.get_function(name) {
if user_func.params().len() != args.len() {
return Err(anyhow::anyhow!(
"Function '{}' expects {} arguments, got {}",
name,
user_func.params().len(),
args.len()
));
}
self.scope_manager.enter_scope();
self.runtime.enter_scope();
for (param, arg) in user_func.params().iter().zip(args.iter()) {
self.runtime.set_variable(param.0.clone(), arg.clone());
}
let is_function = user_func.is_function();
let func_name_lower = name.to_lowercase();
if is_function {
let default = match user_func.return_type_name() {
"integer" => Value::Integer(0),
"boolean" => Value::Boolean(false),
"real" | "float" => Value::Real(0.0),
"char" => Value::Char('\0'),
"string" => Value::String("".to_string()),
_ => Value::Nil,
};
self.runtime.set_variable(func_name_lower.clone(), default);
}
let body_statements = user_func.body().statements.clone();
let body_result = self.execute_block_stmts(&body_statements);
let return_value = if is_function {
self.runtime.get_variable_value(&func_name_lower).unwrap_or(Value::Nil)
} else {
Value::Nil
};
self.runtime.exit_scope();
self.scope_manager.exit_scope();
match body_result {
Ok(()) => {
if is_function {
Ok(return_value)
} else {
Ok(Value::Nil)
}
},
Err(e) => {
if let Some(early) = e.downcast_ref::<EarlyReturn>() {
if let Some(val) = &early.value {
Ok(val.clone())
} else {
Ok(return_value)
}
} else {
Err(e)
}
}
}
} else {
Err(anyhow::anyhow!("Undefined function: {}", name))
}
}
pub fn call_procedure(&mut self, name: &str, args: &[Value]) -> Result<()> {
let result = self.call_function(name, args)?;
if result != Value::Nil {
return Err(anyhow::anyhow!("Procedure {} returned a value", name));
}
Ok(())
}
pub fn execute_block_stmts(&mut self, statements: &[Stmt]) -> Result<()> {
for stmt in statements {
self.execute_stmt(stmt)?;
}
Ok(())
}
fn add_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => Ok(Value::Integer(l + r)),
(Value::Real(l), Value::Real(r)) => Ok(Value::Real(l + r)),
(Value::Integer(l), Value::Real(r)) => Ok(Value::Real(*l as f64 + *r)),
(Value::Real(l), Value::Integer(r)) => Ok(Value::Real(*l + *r as f64)),
(Value::String(l), Value::String(r)) => Ok(Value::String(format!("{}{}", l, r))),
(Value::Set { elements: l }, Value::Set { elements: r }) => {
let mut result = l.clone();
result.extend(r);
Ok(Value::Set { elements: result })
}
_ => Err(anyhow::anyhow!("Incompatible types for addition")),
}
}
fn sub_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => Ok(Value::Integer(l - r)),
(Value::Real(l), Value::Real(r)) => Ok(Value::Real(l - r)),
(Value::Integer(l), Value::Real(r)) => Ok(Value::Real(*l as f64 - *r)),
(Value::Real(l), Value::Integer(r)) => Ok(Value::Real(*l - *r as f64)),
(Value::Set { elements: l }, Value::Set { elements: r }) => {
let mut result = l.clone();
result.retain(|e| !r.contains(e));
Ok(Value::Set { elements: result })
}
_ => Err(anyhow::anyhow!("Incompatible types for subtraction")),
}
}
fn mul_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => Ok(Value::Integer(l * r)),
(Value::Real(l), Value::Real(r)) => Ok(Value::Real(l * r)),
(Value::Integer(l), Value::Real(r)) => Ok(Value::Real(*l as f64 * *r)),
(Value::Real(l), Value::Integer(r)) => Ok(Value::Real(*l * *r as f64)),
(Value::Set { elements: l }, Value::Set { elements: r }) => {
let result: std::collections::HashSet<i64> = l.iter().filter(|e| r.contains(e)).copied().collect();
Ok(Value::Set { elements: result })
}
_ => Err(anyhow::anyhow!("Incompatible types for multiplication")),
}
}
fn div_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => {
if *r == 0 {
return Err(anyhow::anyhow!("Division by zero"));
}
Ok(Value::Integer(l / r))
},
(Value::Real(l), Value::Real(r)) => {
if *r == 0.0 {
return Err(anyhow::anyhow!("Division by zero"));
}
Ok(Value::Real(l / r))
},
(Value::Integer(l), Value::Real(r)) => {
if *r == 0.0 {
return Err(anyhow::anyhow!("Division by zero"));
}
Ok(Value::Real(*l as f64 / *r))
},
(Value::Real(l), Value::Integer(r)) => {
if *r == 0 {
return Err(anyhow::anyhow!("Division by zero"));
}
Ok(Value::Real(*l / *r as f64))
},
_ => Err(anyhow::anyhow!("Incompatible types for division")),
}
}
fn int_div_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => {
if *r == 0 {
return Err(anyhow::anyhow!("Division by zero"));
}
Ok(Value::Integer(l / r))
},
_ => Err(anyhow::anyhow!("div requires integer operands")),
}
}
fn mod_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => {
if *r == 0 {
return Err(anyhow::anyhow!("Division by zero"));
}
Ok(Value::Integer(l % r))
},
_ => Err(anyhow::anyhow!("mod requires integer operands")),
}
}
fn shl_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => Ok(Value::Integer(l << r)),
_ => Err(anyhow::anyhow!("shl requires integer operands")),
}
}
fn shr_values(&self, left: &Value, right: &Value) -> Result<Value> {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => Ok(Value::Integer(l >> r)),
_ => Err(anyhow::anyhow!("shr requires integer operands")),
}
}
fn compare_values(&self, left: &Value, right: &Value, operator: &str) -> Result<Value> {
let result = match (left, right) {
(Value::Integer(l), Value::Integer(r)) => {
match operator {
"<" => l < r,
"<=" => l <= r,
">" => l > r,
">=" => l >= r,
_ => false,
}
},
(Value::Real(l), Value::Real(r)) => {
match operator {
"<" => l < r,
"<=" => l <= r,
">" => l > r,
">=" => l >= r,
_ => false,
}
},
(Value::String(l), Value::String(r)) => {
match operator {
"<" => l < r,
"<=" => l <= r,
">" => l > r,
">=" => l >= r,
_ => false,
}
},
(Value::Char(l), Value::Char(r)) => {
match operator {
"<" => l < r,
"<=" => l <= r,
">" => l > r,
">=" => l >= r,
_ => false,
}
},
(Value::Boolean(l), Value::Boolean(r)) => {
match operator {
"<" => !*l && *r,
"<=" => !*l || *r,
">" => *l && !*r,
">=" => *l || !*r,
_ => false,
}
},
(Value::Enum { ordinal: l, .. }, Value::Enum { ordinal: r, .. }) => {
match operator {
"<" => l < r,
"<=" => l <= r,
">" => l > r,
">=" => l >= r,
_ => false,
}
},
_ => return Err(anyhow::anyhow!("Incompatible types for comparison")),
};
Ok(Value::Boolean(result))
}
fn values_equal(&self, left: &Value, right: &Value) -> bool {
match (left, right) {
(Value::Integer(l), Value::Integer(r)) => l == r,
(Value::Real(l), Value::Real(r)) => (l - r).abs() < f64::EPSILON,
(Value::String(l), Value::String(r)) => l == r,
(Value::Boolean(l), Value::Boolean(r)) => l == r,
(Value::Char(l), Value::Char(r)) => l == r,
(Value::Nil, Value::Nil) => true,
(Value::Enum { ordinal: l, .. }, Value::Enum { ordinal: r, .. }) => l == r,
(Value::Set { elements: l }, Value::Set { elements: r }) => l == r,
_ => false,
}
}
fn is_truthy(&self, value: &Value) -> bool {
match value {
Value::Boolean(b) => *b,
Value::Integer(i) => *i != 0,
Value::Real(f) => *f != 0.0,
Value::String(s) => !s.is_empty(),
Value::Array { elements: arr, .. } => !arr.is_empty(),
Value::Nil => false,
Value::Char(_) => true,
Value::Object { .. } => true,
Value::Record { .. } => true,
Value::Enum { .. } => true,
Value::Set { elements } => !elements.is_empty(),
}
}
pub fn set_debug_mode(&mut self, debug: bool) {
self.runtime.set_verbose(debug);
self.scope_manager.set_debug_mode(debug);
}
pub fn set_debug_breakpoint_check(&mut self, callback: DebugBreakpointCheck) {
self.debug_breakpoint_check = Some(callback);
}
pub fn set_debug_breakpoint_handler(&mut self, handler: DebugBreakpointHandler) {
self.debug_breakpoint_handler = Some(handler);
}
fn collect_index_chain(expr: &Expr) -> Option<(&str, Vec<&Expr>)> {
match expr {
Expr::Variable(name) => Some((name.as_str(), vec![])),
Expr::FunctionCall { name, arguments } if name == "__index__" && arguments.len() == 2 => {
let (root, mut indices) = Self::collect_index_chain(&arguments[0])?;
indices.push(&arguments[1]);
Some((root, indices))
},
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Block, Statement, Expr, Literal, FieldVisibility};
#[test]
fn test_interpreter_creation() {
let interp = Interpreter::new(false);
assert!(!interp.runtime.is_verbose());
}
#[test]
fn test_variable_declaration() {
let mut interp = Interpreter::new(false);
let block = Block {
consts: vec![],
types: vec![],
vars: vec![crate::ast::VariableDecl {
name: "x".to_string(),
variable_type: crate::ast::Type::Simple(crate::ast::SimpleType::Integer),
initial_value: None,
visibility: FieldVisibility::Public,
is_absolute: false,
absolute_address: None,
}],
procedures: vec![],
functions: vec![],
classes: vec![],
statements: vec![],
};
assert!(interp.declare_block_vars(&block).is_ok());
assert!(interp.get_variable_value("x").is_some());
}
#[test]
fn test_arithmetic_operations() {
let mut interp = Interpreter::new(false);
let expr = Expr::BinaryOp {
left: Box::new(Expr::Literal(Literal::Integer(5))),
operator: "+".to_string(),
right: Box::new(Expr::Literal(Literal::Integer(3))),
};
let result = interp.eval_expr(&expr).unwrap();
assert_eq!(result, Value::Integer(8));
}
#[test]
fn test_variable_assignment() {
let mut interp = Interpreter::new(false);
let assignment = Statement::Assignment {
target: Expr::Variable("x".to_string()),
value: Expr::Literal(Literal::Integer(42)),
};
interp.execute_stmt(&assignment).unwrap();
assert_eq!(interp.get_variable_value("x"), Some(Value::Integer(42)));
}
}