#![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_sign_loss)] #![allow(clippy::cast_possible_wrap)] use crate::transpiler::canonical_ast::{CoreExpr, CoreLiteral, DeBruijnIndex, PrimOp};
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Integer(i64),
Float(f64),
String(String),
Bool(bool),
Char(char),
Unit,
Nil,
Closure {
body: Rc<CoreExpr>,
env: Environment,
},
Array(Vec<Value>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Environment {
bindings: Vec<Value>,
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}
impl Environment {
#[must_use]
pub fn new() -> Self {
Self {
bindings: Vec::new(),
}
}
pub fn push(&mut self, value: Value) {
self.bindings.push(value);
}
pub fn pop(&mut self) {
self.bindings.pop();
}
#[must_use]
pub fn lookup(&self, index: &DeBruijnIndex) -> Option<&Value> {
let pos = self.bindings.len().checked_sub(index.0 + 1)?;
self.bindings.get(pos)
}
}
pub struct ReferenceInterpreter {
env: Environment,
trace: Vec<String>, }
impl Default for ReferenceInterpreter {
fn default() -> Self {
Self::new()
}
}
impl ReferenceInterpreter {
#[must_use]
pub fn new() -> Self {
Self {
env: Environment::new(),
trace: Vec::new(),
}
}
pub fn eval(&mut self, expr: &CoreExpr) -> Result<Value, String> {
self.trace.push(format!("Evaluating: {expr:?}"));
match expr {
CoreExpr::Var(idx) => self
.env
.lookup(idx)
.cloned()
.ok_or_else(|| format!("Unbound variable: {idx:?}")),
CoreExpr::Lambda { body, .. } => {
Ok(Value::Closure {
body: Rc::new(body.as_ref().clone()),
env: self.env.clone(),
})
}
CoreExpr::App(func, arg) => {
let func_val = self.eval(func)?;
let arg_val = self.eval(arg)?;
match func_val {
Value::Closure { body, mut env } => {
let saved_env = self.env.clone();
env.push(arg_val);
self.env = env;
let result = self.eval(&body)?;
self.env = saved_env;
Ok(result)
}
_ => Err(format!("Cannot apply non-function: {func_val:?}")),
}
}
CoreExpr::Let { value, body, name } => {
self.trace.push(format!("Let binding: {name:?}"));
let val = self.eval(value)?;
self.env.push(val);
let result = self.eval(body)?;
self.env.pop();
Ok(result)
}
CoreExpr::Literal(lit) => Ok(match lit {
CoreLiteral::Integer(i) => Value::Integer(*i),
CoreLiteral::Float(f) => Value::Float(*f),
CoreLiteral::String(s) => Value::String(s.clone()),
CoreLiteral::Bool(b) => Value::Bool(*b),
CoreLiteral::Char(c) => Value::Char(*c),
CoreLiteral::Unit => Value::Unit,
}),
CoreExpr::Prim(op, args) => self.eval_prim(op, args),
}
}
#[allow(clippy::too_many_lines)] fn eval_prim(&mut self, op: &PrimOp, args: &[CoreExpr]) -> Result<Value, String> {
let mut values = Vec::new();
for arg in args {
values.push(self.eval(arg)?);
}
match op {
PrimOp::Add => {
if values.len() != 2 {
return Err(format!("Add expects 2 arguments, got {}", values.len()));
}
match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a + b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
(Value::String(a), Value::String(b)) => Ok(Value::String(format!("{a}{b}"))),
_ => Err(format!(
"Type error in addition: {:?} + {:?}",
values[0], values[1]
)),
}
}
PrimOp::Sub => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a - b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
_ => Err("Type error in subtraction".to_string()),
},
PrimOp::Mul => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a * b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
_ => Err("Type error in multiplication".to_string()),
},
PrimOp::Div => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => {
if *b == 0 {
Err("Division by zero".to_string())
} else {
Ok(Value::Integer(a / b))
}
}
(Value::Float(a), Value::Float(b)) => {
if *b == 0.0 {
Err("Division by zero".to_string())
} else {
Ok(Value::Float(a / b))
}
}
_ => Err("Type error in division".to_string()),
},
PrimOp::Mod => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => {
if *b == 0 {
Err("Modulo by zero".to_string())
} else {
Ok(Value::Integer(a % b))
}
}
_ => Err("Type error in modulo".to_string()),
},
PrimOp::Pow => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => {
if *b < 0 {
Err("Negative exponent for integer".to_string())
} else {
Ok(Value::Integer(a.pow(*b as u32)))
}
}
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.powf(*b))),
_ => Err("Type error in power".to_string()),
},
PrimOp::Eq => Ok(Value::Bool(values[0] == values[1])),
PrimOp::Ne => Ok(Value::Bool(values[0] != values[1])),
PrimOp::Lt => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Bool(a < b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a < b)),
_ => Err("Type error in less-than".to_string()),
},
PrimOp::Le => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Bool(a <= b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a <= b)),
_ => Err("Type error in less-equal".to_string()),
},
PrimOp::Gt => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Bool(a > b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a > b)),
_ => Err("Type error in greater-than".to_string()),
},
PrimOp::Ge => match (&values[0], &values[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Bool(a >= b)),
(Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a >= b)),
_ => Err("Type error in greater-equal".to_string()),
},
PrimOp::And => match (&values[0], &values[1]) {
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(*a && *b)),
_ => Err("Type error in AND".to_string()),
},
PrimOp::Or => match (&values[0], &values[1]) {
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(*a || *b)),
_ => Err("Type error in OR".to_string()),
},
PrimOp::NullCoalesce => {
if values.len() != 2 {
return Err(format!(
"NullCoalesce expects 2 arguments, got {}",
values.len()
));
}
match &values[0] {
Value::Nil => Ok(values[1].clone()),
_ => Ok(values[0].clone()),
}
}
PrimOp::Not => {
if values.len() != 1 {
return Err(format!("NOT expects 1 argument, got {}", values.len()));
}
match &values[0] {
Value::Bool(b) => Ok(Value::Bool(!b)),
_ => Err("Type error in NOT".to_string()),
}
}
PrimOp::If => {
if values.len() != 3 {
return Err(format!("IF expects 3 arguments, got {}", values.len()));
}
match &values[0] {
Value::Bool(true) => Ok(values[1].clone()),
Value::Bool(false) => Ok(values[2].clone()),
_ => Err("Type error: IF condition must be boolean".to_string()),
}
}
PrimOp::ArrayNew => {
Ok(Value::Array(values))
}
PrimOp::ArrayIndex => {
if values.len() != 2 {
return Err("Array index expects 2 arguments".to_string());
}
match (&values[0], &values[1]) {
(Value::Array(arr), Value::Integer(idx)) => {
if *idx < 0 || *idx as usize >= arr.len() {
Err(format!("Array index out of bounds: {idx}"))
} else {
Ok(arr[*idx as usize].clone())
}
}
_ => Err("Type error in array indexing".to_string()),
}
}
PrimOp::ArrayLen => {
if values.len() != 1 {
return Err("Array length expects 1 argument".to_string());
}
match &values[0] {
Value::Array(arr) => Ok(Value::Integer(arr.len() as i64)),
_ => Err("Type error: expected array".to_string()),
}
}
PrimOp::Concat => Err(format!("Unsupported primitive: {op:?}")),
}
}
#[must_use]
pub fn get_trace(&self) -> &[String] {
&self.trace
}
pub fn clear_trace(&mut self) {
self.trace.clear();
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::transpiler::canonical_ast::AstNormalizer;
use crate::Parser;
#[test]
fn test_environment_new() {
let env = Environment::new();
assert!(env.bindings.is_empty());
}
#[test]
fn test_environment_default() {
let env = Environment::default();
assert!(env.bindings.is_empty());
}
#[test]
fn test_environment_push() {
let mut env = Environment::new();
env.push(Value::Integer(42));
assert_eq!(env.bindings.len(), 1);
}
#[test]
fn test_environment_pop() {
let mut env = Environment::new();
env.push(Value::Integer(42));
env.pop();
assert!(env.bindings.is_empty());
}
#[test]
fn test_environment_lookup_empty() {
let env = Environment::new();
assert!(env.lookup(&DeBruijnIndex(0)).is_none());
}
#[test]
fn test_environment_lookup_single() {
let mut env = Environment::new();
env.push(Value::Integer(42));
let result = env.lookup(&DeBruijnIndex(0));
assert_eq!(result, Some(&Value::Integer(42)));
}
#[test]
fn test_environment_lookup_multiple() {
let mut env = Environment::new();
env.push(Value::Integer(1));
env.push(Value::Integer(2));
env.push(Value::Integer(3));
assert_eq!(env.lookup(&DeBruijnIndex(0)), Some(&Value::Integer(3)));
assert_eq!(env.lookup(&DeBruijnIndex(1)), Some(&Value::Integer(2)));
assert_eq!(env.lookup(&DeBruijnIndex(2)), Some(&Value::Integer(1)));
}
#[test]
fn test_environment_lookup_out_of_bounds() {
let mut env = Environment::new();
env.push(Value::Integer(42));
assert!(env.lookup(&DeBruijnIndex(1)).is_none());
assert!(env.lookup(&DeBruijnIndex(10)).is_none());
}
#[test]
fn test_value_integer() {
let v = Value::Integer(42);
assert_eq!(v, Value::Integer(42));
}
#[test]
fn test_value_float() {
let v = Value::Float(3.14);
assert_eq!(v, Value::Float(3.14));
}
#[test]
fn test_value_string() {
let v = Value::String("hello".to_string());
assert_eq!(v, Value::String("hello".to_string()));
}
#[test]
fn test_value_bool() {
assert_eq!(Value::Bool(true), Value::Bool(true));
assert_eq!(Value::Bool(false), Value::Bool(false));
assert_ne!(Value::Bool(true), Value::Bool(false));
}
#[test]
fn test_value_char() {
let v = Value::Char('x');
assert_eq!(v, Value::Char('x'));
}
#[test]
fn test_value_unit() {
let v = Value::Unit;
assert_eq!(v, Value::Unit);
}
#[test]
fn test_value_nil() {
let v = Value::Nil;
assert_eq!(v, Value::Nil);
}
#[test]
fn test_value_array() {
let v = Value::Array(vec![Value::Integer(1), Value::Integer(2)]);
assert!(matches!(v, Value::Array(_)));
}
#[test]
fn test_value_closure() {
let v = Value::Closure {
body: Rc::new(CoreExpr::Literal(CoreLiteral::Unit)),
env: Environment::new(),
};
assert!(matches!(v, Value::Closure { .. }));
}
#[test]
fn test_value_clone() {
let v = Value::Integer(42);
let cloned = v.clone();
assert_eq!(v, cloned);
}
#[test]
fn test_value_debug() {
let v = Value::Integer(42);
let debug_str = format!("{:?}", v);
assert!(debug_str.contains("Integer"));
}
#[test]
fn test_interpreter_new() {
let interp = ReferenceInterpreter::new();
assert!(interp.trace.is_empty());
}
#[test]
fn test_interpreter_default() {
let interp = ReferenceInterpreter::default();
assert!(interp.trace.is_empty());
}
#[test]
fn test_interpreter_get_trace() {
let interp = ReferenceInterpreter::new();
assert!(interp.get_trace().is_empty());
}
#[test]
fn test_interpreter_clear_trace() {
let mut interp = ReferenceInterpreter::new();
let _ = interp.eval(&CoreExpr::Literal(CoreLiteral::Integer(42)));
assert!(!interp.get_trace().is_empty());
interp.clear_trace();
assert!(interp.get_trace().is_empty());
}
#[test]
fn test_eval_literal_integer() {
let mut interp = ReferenceInterpreter::new();
let result = interp
.eval(&CoreExpr::Literal(CoreLiteral::Integer(42)))
.unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_literal_float() {
let mut interp = ReferenceInterpreter::new();
let result = interp
.eval(&CoreExpr::Literal(CoreLiteral::Float(3.14)))
.unwrap();
assert_eq!(result, Value::Float(3.14));
}
#[test]
fn test_eval_literal_string() {
let mut interp = ReferenceInterpreter::new();
let result = interp
.eval(&CoreExpr::Literal(CoreLiteral::String("hello".to_string())))
.unwrap();
assert_eq!(result, Value::String("hello".to_string()));
}
#[test]
fn test_eval_literal_bool_true() {
let mut interp = ReferenceInterpreter::new();
let result = interp
.eval(&CoreExpr::Literal(CoreLiteral::Bool(true)))
.unwrap();
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_literal_bool_false() {
let mut interp = ReferenceInterpreter::new();
let result = interp
.eval(&CoreExpr::Literal(CoreLiteral::Bool(false)))
.unwrap();
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_literal_char() {
let mut interp = ReferenceInterpreter::new();
let result = interp
.eval(&CoreExpr::Literal(CoreLiteral::Char('x')))
.unwrap();
assert_eq!(result, Value::Char('x'));
}
#[test]
fn test_eval_literal_unit() {
let mut interp = ReferenceInterpreter::new();
let result = interp.eval(&CoreExpr::Literal(CoreLiteral::Unit)).unwrap();
assert_eq!(result, Value::Unit);
}
#[test]
fn test_eval_add_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Add,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Integer(20)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(30));
}
#[test]
fn test_eval_add_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Add,
vec![
CoreExpr::Literal(CoreLiteral::Float(1.5)),
CoreExpr::Literal(CoreLiteral::Float(2.5)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Float(4.0));
}
#[test]
fn test_eval_add_strings() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Add,
vec![
CoreExpr::Literal(CoreLiteral::String("hello".to_string())),
CoreExpr::Literal(CoreLiteral::String(" world".to_string())),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::String("hello world".to_string()));
}
#[test]
fn test_eval_add_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Add,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::String("hello".to_string())),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_add_wrong_arg_count() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Add,
vec![CoreExpr::Literal(CoreLiteral::Integer(10))],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_sub_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Sub,
vec![
CoreExpr::Literal(CoreLiteral::Integer(30)),
CoreExpr::Literal(CoreLiteral::Integer(10)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(20));
}
#[test]
fn test_eval_sub_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Sub,
vec![
CoreExpr::Literal(CoreLiteral::Float(5.0)),
CoreExpr::Literal(CoreLiteral::Float(2.5)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Float(2.5));
}
#[test]
fn test_eval_sub_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Sub,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Bool(true)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_mul_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Mul,
vec![
CoreExpr::Literal(CoreLiteral::Integer(6)),
CoreExpr::Literal(CoreLiteral::Integer(7)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_mul_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Mul,
vec![
CoreExpr::Literal(CoreLiteral::Float(2.0)),
CoreExpr::Literal(CoreLiteral::Float(3.0)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Float(6.0));
}
#[test]
fn test_eval_mul_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Mul,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::String("x".to_string())),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_div_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Div,
vec![
CoreExpr::Literal(CoreLiteral::Integer(20)),
CoreExpr::Literal(CoreLiteral::Integer(4)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(5));
}
#[test]
fn test_eval_div_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Div,
vec![
CoreExpr::Literal(CoreLiteral::Float(10.0)),
CoreExpr::Literal(CoreLiteral::Float(4.0)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Float(2.5));
}
#[test]
fn test_eval_div_by_zero_integer() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Div,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Integer(0)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("Division by zero"));
}
#[test]
fn test_eval_div_by_zero_float() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Div,
vec![
CoreExpr::Literal(CoreLiteral::Float(10.0)),
CoreExpr::Literal(CoreLiteral::Float(0.0)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("Division by zero"));
}
#[test]
fn test_eval_div_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Div,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Bool(true)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_mod_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Mod,
vec![
CoreExpr::Literal(CoreLiteral::Integer(17)),
CoreExpr::Literal(CoreLiteral::Integer(5)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(2));
}
#[test]
fn test_eval_mod_by_zero() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Mod,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Integer(0)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("Modulo by zero"));
}
#[test]
fn test_eval_mod_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Mod,
vec![
CoreExpr::Literal(CoreLiteral::Float(10.0)),
CoreExpr::Literal(CoreLiteral::Float(3.0)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_pow_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Pow,
vec![
CoreExpr::Literal(CoreLiteral::Integer(2)),
CoreExpr::Literal(CoreLiteral::Integer(10)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(1024));
}
#[test]
fn test_eval_pow_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Pow,
vec![
CoreExpr::Literal(CoreLiteral::Float(2.0)),
CoreExpr::Literal(CoreLiteral::Float(3.0)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Float(8.0));
}
#[test]
fn test_eval_pow_negative_exponent() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Pow,
vec![
CoreExpr::Literal(CoreLiteral::Integer(2)),
CoreExpr::Literal(CoreLiteral::Integer(-1)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("Negative exponent"));
}
#[test]
fn test_eval_pow_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Pow,
vec![
CoreExpr::Literal(CoreLiteral::Integer(2)),
CoreExpr::Literal(CoreLiteral::Bool(true)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_eq_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Eq,
vec![
CoreExpr::Literal(CoreLiteral::Integer(42)),
CoreExpr::Literal(CoreLiteral::Integer(42)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_eq_integers_false() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Eq,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(false));
}
#[test]
fn test_eval_ne_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Ne,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_lt_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Lt,
vec![
CoreExpr::Literal(CoreLiteral::Integer(5)),
CoreExpr::Literal(CoreLiteral::Integer(10)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_lt_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Lt,
vec![
CoreExpr::Literal(CoreLiteral::Float(1.0)),
CoreExpr::Literal(CoreLiteral::Float(2.0)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_lt_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Lt,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Bool(true)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_le_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Le,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Integer(10)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_le_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Le,
vec![
CoreExpr::Literal(CoreLiteral::Float(5.0)),
CoreExpr::Literal(CoreLiteral::Float(5.0)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_le_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Le,
vec![
CoreExpr::Literal(CoreLiteral::Bool(true)),
CoreExpr::Literal(CoreLiteral::Bool(false)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_gt_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Gt,
vec![
CoreExpr::Literal(CoreLiteral::Integer(20)),
CoreExpr::Literal(CoreLiteral::Integer(10)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_gt_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Gt,
vec![
CoreExpr::Literal(CoreLiteral::Float(3.0)),
CoreExpr::Literal(CoreLiteral::Float(2.0)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_gt_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Gt,
vec![
CoreExpr::Literal(CoreLiteral::String("a".to_string())),
CoreExpr::Literal(CoreLiteral::String("b".to_string())),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_ge_integers() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Ge,
vec![
CoreExpr::Literal(CoreLiteral::Integer(10)),
CoreExpr::Literal(CoreLiteral::Integer(5)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_ge_floats() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Ge,
vec![
CoreExpr::Literal(CoreLiteral::Float(5.0)),
CoreExpr::Literal(CoreLiteral::Float(5.0)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_ge_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Ge,
vec![
CoreExpr::Literal(CoreLiteral::Char('a')),
CoreExpr::Literal(CoreLiteral::Char('b')),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_and_true_true() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::And,
vec![
CoreExpr::Literal(CoreLiteral::Bool(true)),
CoreExpr::Literal(CoreLiteral::Bool(true)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_and_true_false() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::And,
vec![
CoreExpr::Literal(CoreLiteral::Bool(true)),
CoreExpr::Literal(CoreLiteral::Bool(false)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(false));
}
#[test]
fn test_eval_and_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::And,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_or_false_true() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Or,
vec![
CoreExpr::Literal(CoreLiteral::Bool(false)),
CoreExpr::Literal(CoreLiteral::Bool(true)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_or_false_false() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Or,
vec![
CoreExpr::Literal(CoreLiteral::Bool(false)),
CoreExpr::Literal(CoreLiteral::Bool(false)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(false));
}
#[test]
fn test_eval_or_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Or,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(0)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_not_true() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Not,
vec![CoreExpr::Literal(CoreLiteral::Bool(true))],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(false));
}
#[test]
fn test_eval_not_false() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Not,
vec![CoreExpr::Literal(CoreLiteral::Bool(false))],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Bool(true));
}
#[test]
fn test_eval_not_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Not,
vec![CoreExpr::Literal(CoreLiteral::Integer(1))],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_not_wrong_arg_count() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Not,
vec![
CoreExpr::Literal(CoreLiteral::Bool(true)),
CoreExpr::Literal(CoreLiteral::Bool(false)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_null_coalesce_nil() {
let mut interp = ReferenceInterpreter::new();
let env = Environment::new();
interp.env = env;
let expr = CoreExpr::Prim(
PrimOp::NullCoalesce,
vec![
CoreExpr::Literal(CoreLiteral::Integer(42)),
CoreExpr::Literal(CoreLiteral::Integer(0)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_null_coalesce_wrong_arg_count() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::NullCoalesce,
vec![CoreExpr::Literal(CoreLiteral::Integer(42))],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_if_true() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::If,
vec![
CoreExpr::Literal(CoreLiteral::Bool(true)),
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Integer(1));
}
#[test]
fn test_eval_if_false() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::If,
vec![
CoreExpr::Literal(CoreLiteral::Bool(false)),
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
],
);
assert_eq!(interp.eval(&expr).unwrap(), Value::Integer(2));
}
#[test]
fn test_eval_if_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::If,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
CoreExpr::Literal(CoreLiteral::Integer(3)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("condition must be boolean"));
}
#[test]
fn test_eval_if_wrong_arg_count() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::If,
vec![
CoreExpr::Literal(CoreLiteral::Bool(true)),
CoreExpr::Literal(CoreLiteral::Integer(1)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_array_new() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::ArrayNew,
vec![
CoreExpr::Literal(CoreLiteral::Integer(1)),
CoreExpr::Literal(CoreLiteral::Integer(2)),
CoreExpr::Literal(CoreLiteral::Integer(3)),
],
);
let result = interp.eval(&expr).unwrap();
assert!(matches!(result, Value::Array(arr) if arr.len() == 3));
}
#[test]
fn test_eval_array_new_empty() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(PrimOp::ArrayNew, vec![]);
let result = interp.eval(&expr).unwrap();
assert!(matches!(result, Value::Array(arr) if arr.is_empty()));
}
#[test]
fn test_eval_array_index() {
let mut interp = ReferenceInterpreter::new();
let arr = Value::Array(vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
]);
interp.env.push(arr);
let expr = CoreExpr::Prim(
PrimOp::ArrayIndex,
vec![
CoreExpr::Var(DeBruijnIndex(0)),
CoreExpr::Literal(CoreLiteral::Integer(1)),
],
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(20));
}
#[test]
fn test_eval_array_index_out_of_bounds() {
let mut interp = ReferenceInterpreter::new();
let arr = Value::Array(vec![Value::Integer(1)]);
interp.env.push(arr);
let expr = CoreExpr::Prim(
PrimOp::ArrayIndex,
vec![
CoreExpr::Var(DeBruijnIndex(0)),
CoreExpr::Literal(CoreLiteral::Integer(5)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("out of bounds"));
}
#[test]
fn test_eval_array_index_negative() {
let mut interp = ReferenceInterpreter::new();
let arr = Value::Array(vec![Value::Integer(1)]);
interp.env.push(arr);
let expr = CoreExpr::Prim(
PrimOp::ArrayIndex,
vec![
CoreExpr::Var(DeBruijnIndex(0)),
CoreExpr::Literal(CoreLiteral::Integer(-1)),
],
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("out of bounds"));
}
#[test]
fn test_eval_array_index_wrong_arg_count() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::ArrayIndex,
vec![CoreExpr::Literal(CoreLiteral::Integer(0))],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_array_index_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::ArrayIndex,
vec![
CoreExpr::Literal(CoreLiteral::Integer(0)),
CoreExpr::Literal(CoreLiteral::Integer(0)),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_array_len() {
let mut interp = ReferenceInterpreter::new();
let arr = Value::Array(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]);
interp.env.push(arr);
let expr = CoreExpr::Prim(PrimOp::ArrayLen, vec![CoreExpr::Var(DeBruijnIndex(0))]);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_array_len_empty() {
let mut interp = ReferenceInterpreter::new();
let arr = Value::Array(vec![]);
interp.env.push(arr);
let expr = CoreExpr::Prim(PrimOp::ArrayLen, vec![CoreExpr::Var(DeBruijnIndex(0))]);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_array_len_wrong_arg_count() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(PrimOp::ArrayLen, vec![]);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_array_len_type_error() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::ArrayLen,
vec![CoreExpr::Literal(CoreLiteral::Integer(0))],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_concat_unsupported() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Prim(
PrimOp::Concat,
vec![
CoreExpr::Literal(CoreLiteral::String("a".to_string())),
CoreExpr::Literal(CoreLiteral::String("b".to_string())),
],
);
assert!(interp.eval(&expr).is_err());
}
#[test]
fn test_eval_let_simple() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Let {
name: Some("x".to_string()),
value: Box::new(CoreExpr::Literal(CoreLiteral::Integer(42))),
body: Box::new(CoreExpr::Var(DeBruijnIndex(0))),
};
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_let_nested() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Let {
name: Some("x".to_string()),
value: Box::new(CoreExpr::Literal(CoreLiteral::Integer(10))),
body: Box::new(CoreExpr::Let {
name: Some("y".to_string()),
value: Box::new(CoreExpr::Literal(CoreLiteral::Integer(20))),
body: Box::new(CoreExpr::Prim(
PrimOp::Add,
vec![
CoreExpr::Var(DeBruijnIndex(1)), CoreExpr::Var(DeBruijnIndex(0)), ],
)),
}),
};
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(30));
}
#[test]
fn test_eval_lambda() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Lambda {
param_name: Some("x".to_string()),
body: Box::new(CoreExpr::Var(DeBruijnIndex(0))),
};
let result = interp.eval(&expr).unwrap();
assert!(matches!(result, Value::Closure { .. }));
}
#[test]
fn test_eval_lambda_application() {
let mut interp = ReferenceInterpreter::new();
let lambda = CoreExpr::Lambda {
param_name: Some("x".to_string()),
body: Box::new(CoreExpr::Var(DeBruijnIndex(0))),
};
let expr = CoreExpr::App(
Box::new(lambda),
Box::new(CoreExpr::Literal(CoreLiteral::Integer(42))),
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_lambda_application_add() {
let mut interp = ReferenceInterpreter::new();
let lambda = CoreExpr::Lambda {
param_name: Some("x".to_string()),
body: Box::new(CoreExpr::Prim(
PrimOp::Add,
vec![
CoreExpr::Var(DeBruijnIndex(0)),
CoreExpr::Literal(CoreLiteral::Integer(1)),
],
)),
};
let expr = CoreExpr::App(
Box::new(lambda),
Box::new(CoreExpr::Literal(CoreLiteral::Integer(41))),
);
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_app_non_function() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::App(
Box::new(CoreExpr::Literal(CoreLiteral::Integer(42))),
Box::new(CoreExpr::Literal(CoreLiteral::Integer(1))),
);
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("Cannot apply non-function"));
}
#[test]
fn test_eval_var_unbound() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Var(DeBruijnIndex(0));
let err = interp.eval(&expr).unwrap_err();
assert!(err.contains("Unbound variable"));
}
#[test]
fn test_eval_var_bound() {
let mut interp = ReferenceInterpreter::new();
interp.env.push(Value::Integer(42));
let expr = CoreExpr::Var(DeBruijnIndex(0));
let result = interp.eval(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_arithmetic() {
let input = "1 + 2 * 3";
let mut parser = Parser::new(input);
let ast = parser.parse().expect("Failed to parse");
let mut normalizer = AstNormalizer::new();
let core = normalizer.normalize(&ast);
let mut interp = ReferenceInterpreter::new();
let result = interp.eval(&core).unwrap();
assert_eq!(result, Value::Integer(7)); }
#[test]
fn test_eval_let_binding() {
let input = "let x = 10 in ()";
let mut parser = Parser::new(input);
let ast = parser.parse().expect("Failed to parse");
let mut normalizer = AstNormalizer::new();
let core = normalizer.normalize(&ast);
let mut interp = ReferenceInterpreter::new();
let result = interp.eval(&core).unwrap();
assert_eq!(result, Value::Unit);
}
#[test]
fn test_eval_function() {
}
#[test]
fn test_trace_recorded() {
let mut interp = ReferenceInterpreter::new();
let _ = interp.eval(&CoreExpr::Literal(CoreLiteral::Integer(42)));
assert!(!interp.get_trace().is_empty());
assert!(interp.get_trace()[0].contains("Evaluating"));
}
#[test]
fn test_trace_let_binding() {
let mut interp = ReferenceInterpreter::new();
let expr = CoreExpr::Let {
name: Some("x".to_string()),
value: Box::new(CoreExpr::Literal(CoreLiteral::Integer(42))),
body: Box::new(CoreExpr::Literal(CoreLiteral::Unit)),
};
let _ = interp.eval(&expr);
let trace = interp.get_trace();
assert!(trace.iter().any(|t| t.contains("Let binding")));
}
}
#[cfg(test)]
mod property_tests_reference_interpreter {
use proptest::proptest;
proptest! {
#[test]
fn test_new_never_panics(input: String) {
let _input = if input.len() > 100 { &input[..100] } else { &input[..] };
let _ = std::panic::catch_unwind(|| {
});
}
}
}