use crate::transpiler::core_ast::{CoreExpr, PrimOp};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Integer(i64),
Float(f64),
Bool(bool),
String(String),
List(Vec<Value>),
Function(Vec<String>, Box<CoreExpr>, HashMap<String, Value>),
Unit,
}
pub struct Interpreter {
env: HashMap<String, Value>,
}
impl Interpreter {
pub fn new() -> Self {
Interpreter {
env: HashMap::new(),
}
}
pub fn eval_prim(&mut self, op: &PrimOp, args: &[CoreExpr]) -> Result<Value, String> {
let values = self.evaluate_arguments(args)?;
match op {
PrimOp::Add | PrimOp::Sub | PrimOp::Mul | PrimOp::Div | PrimOp::Mod | PrimOp::Pow => {
self.eval_arithmetic(op, &values)
}
PrimOp::Eq | PrimOp::Ne | PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge => {
self.eval_comparison(op, &values)
}
PrimOp::And | PrimOp::Or | PrimOp::Not => self.eval_logical(op, &values),
PrimOp::Concat | PrimOp::Len | PrimOp::Substring => self.eval_string_ops(op, &values),
PrimOp::Head | PrimOp::Tail | PrimOp::Cons | PrimOp::IsEmpty => {
self.eval_list_ops(op, &values)
}
PrimOp::Print => self.eval_print(&values),
PrimOp::TypeOf => self.eval_typeof(&values),
}
}
fn evaluate_arguments(&mut self, args: &[CoreExpr]) -> Result<Vec<Value>, String> {
let mut values = Vec::new();
for arg in args {
values.push(self.eval(arg)?);
}
Ok(values)
}
fn eval_arithmetic(&self, op: &PrimOp, values: &[Value]) -> Result<Value, String> {
if values.len() != 2 {
return Err(format!(
"{:?} expects 2 arguments, got {}",
op,
values.len()
));
}
match op {
PrimOp::Add => self.eval_add(&values[0], &values[1]),
PrimOp::Sub => self.eval_subtract(&values[0], &values[1]),
PrimOp::Mul => self.eval_multiply(&values[0], &values[1]),
PrimOp::Div => self.eval_divide(&values[0], &values[1]),
PrimOp::Mod => self.eval_modulo(&values[0], &values[1]),
PrimOp::Pow => self.eval_power(&values[0], &values[1]),
_ => unreachable!("Non-arithmetic op in eval_arithmetic"),
}
}
fn eval_add(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Integer(x + y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
(Value::Integer(x), Value::Float(y)) => Ok(Value::Float(*x as f64 + y)),
(Value::Float(x), Value::Integer(y)) => Ok(Value::Float(x + *y as f64)),
(Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))),
_ => Err(format!("Type error in addition: {:?} + {a, b:?}")),
}
}
fn eval_subtract(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Integer(x - y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)),
(Value::Integer(x), Value::Float(y)) => Ok(Value::Float(*x as f64 - y)),
(Value::Float(x), Value::Integer(y)) => Ok(Value::Float(x - *y as f64)),
_ => Err(format!("Type error in subtraction: {:?} - {a, b:?}")),
}
}
fn eval_multiply(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Integer(x * y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
(Value::Integer(x), Value::Float(y)) => Ok(Value::Float(*x as f64 * y)),
(Value::Float(x), Value::Integer(y)) => Ok(Value::Float(x * *y as f64)),
_ => Err(format!("Type error in multiplication: {:?} * {a, b:?}")),
}
}
fn eval_divide(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => {
if *y == 0 {
Err("Division by zero".to_string())
} else {
Ok(Value::Integer(x / y))
}
}
(Value::Float(x), Value::Float(y)) => {
if *y == 0.0 {
Err("Division by zero".to_string())
} else {
Ok(Value::Float(x / y))
}
}
(Value::Integer(x), Value::Float(y)) => {
if *y == 0.0 {
Err("Division by zero".to_string())
} else {
Ok(Value::Float(*x as f64 / y))
}
}
(Value::Float(x), Value::Integer(y)) => {
if *y == 0 {
Err("Division by zero".to_string())
} else {
Ok(Value::Float(x / *y as f64))
}
}
_ => Err(format!("Type error in division: {:?} / {a, b:?}")),
}
}
fn eval_modulo(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => {
if *y == 0 {
Err("Modulo by zero".to_string())
} else {
Ok(Value::Integer(x % y))
}
}
_ => Err(format!("Type error in modulo: {:?} % {a, b:?}")),
}
}
fn eval_power(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => {
if *y < 0 {
Err("Negative exponent not supported for integers".to_string())
} else {
Ok(Value::Integer(x.pow(*y as u32)))
}
}
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x.powf(*y))),
_ => Err(format!("Type error in power: {:?} ^ {a, b:?}")),
}
}
fn eval_comparison(&self, op: &PrimOp, values: &[Value]) -> Result<Value, String> {
if values.len() != 2 {
return Err(format!(
"{:?} expects 2 arguments, got {}",
op,
values.len()
));
}
match op {
PrimOp::Eq => Ok(Value::Bool(self.values_equal(&values[0], &values[1]))),
PrimOp::Ne => Ok(Value::Bool(!self.values_equal(&values[0], &values[1]))),
PrimOp::Lt => self.eval_less_than(&values[0], &values[1]),
PrimOp::Le => self.eval_less_equal(&values[0], &values[1]),
PrimOp::Gt => self.eval_greater_than(&values[0], &values[1]),
PrimOp::Ge => self.eval_greater_equal(&values[0], &values[1]),
_ => unreachable!("Non-comparison op in eval_comparison"),
}
}
fn values_equal(&self, a: &Value, b: &Value) -> bool {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => x == y,
(Value::Float(x), Value::Float(y)) => (x - y).abs() < f64::EPSILON,
(Value::Bool(x), Value::Bool(y)) => x == y,
(Value::String(x), Value::String(y)) => x == y,
(Value::Unit, Value::Unit) => true,
_ => false,
}
}
fn eval_less_than(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Bool(x < y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x < y)),
(Value::String(x), Value::String(y)) => Ok(Value::Bool(x < y)),
_ => Err(format!("Type error in comparison: {:?} < {a, b:?}")),
}
}
fn eval_less_equal(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Bool(x <= y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x <= y)),
(Value::String(x), Value::String(y)) => Ok(Value::Bool(x <= y)),
_ => Err(format!("Type error in comparison: {:?} <= {a, b:?}")),
}
}
fn eval_greater_than(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Bool(x > y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x > y)),
(Value::String(x), Value::String(y)) => Ok(Value::Bool(x > y)),
_ => Err(format!("Type error in comparison: {:?} > {a, b:?}")),
}
}
fn eval_greater_equal(&self, a: &Value, b: &Value) -> Result<Value, String> {
match (a, b) {
(Value::Integer(x), Value::Integer(y)) => Ok(Value::Bool(x >= y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x >= y)),
(Value::String(x), Value::String(y)) => Ok(Value::Bool(x >= y)),
_ => Err(format!("Type error in comparison: {:?} >= {a, b:?}")),
}
}
fn eval_logical(&self, op: &PrimOp, values: &[Value]) -> Result<Value, String> {
match op {
PrimOp::And => self.eval_and(values),
PrimOp::Or => self.eval_or(values),
PrimOp::Not => self.eval_not(values),
_ => unreachable!("Non-logical op in eval_logical"),
}
}
fn eval_and(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 2 {
return Err(format!("AND expects 2 arguments, got {}", values.len()));
}
match (&values[0], &values[1]) {
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(*a && *b)),
_ => Err("Type error: AND requires boolean arguments".to_string()),
}
}
fn eval_or(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 2 {
return Err(format!("OR expects 2 arguments, got {}", values.len()));
}
match (&values[0], &values[1]) {
(Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(*a || *b)),
_ => Err("Type error: OR requires boolean arguments".to_string()),
}
}
fn eval_not(&self, values: &[Value]) -> Result<Value, String> {
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: NOT requires boolean argument".to_string()),
}
}
fn eval_string_ops(&self, op: &PrimOp, values: &[Value]) -> Result<Value, String> {
match op {
PrimOp::Concat => self.eval_concat(values),
PrimOp::Len => self.eval_length(values),
PrimOp::Substring => self.eval_substring(values),
_ => unreachable!("Non-string op in eval_string_ops"),
}
}
fn eval_concat(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 2 {
return Err(format!("Concat expects 2 arguments, got {}", values.len()));
}
match (&values[0], &values[1]) {
(Value::String(a), Value::String(b)) => Ok(Value::String(format!("{a}{b}"))),
_ => Err("Type error: Concat requires string arguments".to_string()),
}
}
fn eval_length(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 1 {
return Err(format!("Length expects 1 argument, got {}", values.len()));
}
match &values[0] {
Value::String(s) => Ok(Value::Integer(s.len() as i64)),
Value::List(l) => Ok(Value::Integer(l.len() as i64)),
_ => Err("Type error: Length requires string or list".to_string()),
}
}
fn eval_substring(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 3 {
return Err(format!(
"Substring expects 3 arguments, got {}",
values.len()
));
}
match (&values[0], &values[1], &values[2]) {
(Value::String(s), Value::Integer(start), Value::Integer(end)) => {
let start = *start as usize;
let end = (*end as usize).min(s.len());
if start <= end {
Ok(Value::String(s[start..end].to_string()))
} else {
Err("Invalid substring indices".to_string())
}
}
_ => Err("Type error: Substring requires (string, int, int)".to_string()),
}
}
fn eval_list_ops(&self, op: &PrimOp, values: &[Value]) -> Result<Value, String> {
match op {
PrimOp::Head => self.eval_head(values),
PrimOp::Tail => self.eval_tail(values),
PrimOp::Cons => self.eval_cons(values),
PrimOp::IsEmpty => self.eval_is_empty(values),
_ => unreachable!("Non-list op in eval_list_ops"),
}
}
fn eval_head(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 1 {
return Err(format!("Head expects 1 argument, got {}", values.len()));
}
match &values[0] {
Value::List(l) if !l.is_empty() => Ok(l[0].clone()),
Value::List(_) => Err("Head of empty list".to_string()),
_ => Err("Type error: Head requires list".to_string()),
}
}
fn eval_tail(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 1 {
return Err(format!("Tail expects 1 argument, got {}", values.len()));
}
match &values[0] {
Value::List(l) if !l.is_empty() => Ok(Value::List(l[1..].to_vec())),
Value::List(_) => Err("Tail of empty list".to_string()),
_ => Err("Type error: Tail requires list".to_string()),
}
}
fn eval_cons(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 2 {
return Err(format!("Cons expects 2 arguments, got {}", values.len()));
}
match &values[1] {
Value::List(l) => {
let mut new_list = vec![values[0].clone()];
new_list.extend(l.clone());
Ok(Value::List(new_list))
}
_ => Err("Type error: Cons requires (value, list)".to_string()),
}
}
fn eval_is_empty(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 1 {
return Err(format!("IsEmpty expects 1 argument, got {}", values.len()));
}
match &values[0] {
Value::List(l) => Ok(Value::Bool(l.is_empty())),
_ => Err("Type error: IsEmpty requires list".to_string()),
}
}
fn eval_print(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 1 {
return Err(format!("Print expects 1 argument, got {}", values.len()));
}
println!("{:?}", values[0]);
Ok(Value::Unit)
}
fn eval_typeof(&self, values: &[Value]) -> Result<Value, String> {
if values.len() != 1 {
return Err(format!("TypeOf expects 1 argument, got {}", values.len()));
}
let type_name = match &values[0] {
Value::Integer(_) => "Integer",
Value::Float(_) => "Float",
Value::Bool(_) => "Bool",
Value::String(_) => "String",
Value::List(_) => "List",
Value::Function(_, _, _) => "Function",
Value::Unit => "Unit",
};
Ok(Value::String(type_name.to_string()))
}
fn eval(&mut self, expr: &CoreExpr) -> Result<Value, String> {
todo!("Main eval function")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(test)]
use proptest::prelude::*;
#[test]
fn test_complexity_reduced() {
}
#[test]
fn test_value_integer() {
let v = Value::Integer(42);
assert!(matches!(v, Value::Integer(42)));
}
#[test]
fn test_value_float() {
let v = Value::Float(3.14);
if let Value::Float(f) = v {
assert!((f - 3.14).abs() < f64::EPSILON);
} else {
panic!("Expected Float");
}
}
#[test]
fn test_value_bool() {
let v = Value::Bool(true);
assert!(matches!(v, Value::Bool(true)));
}
#[test]
fn test_value_string() {
let v = Value::String("hello".to_string());
assert!(matches!(v, Value::String(s) if s == "hello"));
}
#[test]
fn test_value_list() {
let v = Value::List(vec![Value::Integer(1), Value::Integer(2)]);
if let Value::List(l) = v {
assert_eq!(l.len(), 2);
} else {
panic!("Expected List");
}
}
#[test]
fn test_value_unit() {
let v = Value::Unit;
assert!(matches!(v, Value::Unit));
}
#[test]
fn test_interpreter_new() {
let interp = Interpreter::new();
assert!(interp.env.is_empty());
}
#[test]
fn test_eval_add_integers() {
let interp = Interpreter::new();
let result = interp.eval_add(&Value::Integer(2), &Value::Integer(3));
assert_eq!(result, Ok(Value::Integer(5)));
}
#[test]
fn test_eval_add_floats() {
let interp = Interpreter::new();
let result = interp.eval_add(&Value::Float(2.5), &Value::Float(3.5));
if let Ok(Value::Float(f)) = result {
assert!((f - 6.0).abs() < f64::EPSILON);
} else {
panic!("Expected Float result");
}
}
#[test]
fn test_eval_add_strings() {
let interp = Interpreter::new();
let result = interp.eval_add(
&Value::String("hello".to_string()),
&Value::String(" world".to_string()),
);
assert_eq!(result, Ok(Value::String("hello world".to_string())));
}
#[test]
fn test_eval_subtract() {
let interp = Interpreter::new();
let result = interp.eval_subtract(&Value::Integer(10), &Value::Integer(3));
assert_eq!(result, Ok(Value::Integer(7)));
}
#[test]
fn test_eval_multiply() {
let interp = Interpreter::new();
let result = interp.eval_multiply(&Value::Integer(4), &Value::Integer(5));
assert_eq!(result, Ok(Value::Integer(20)));
}
#[test]
fn test_eval_divide() {
let interp = Interpreter::new();
let result = interp.eval_divide(&Value::Integer(20), &Value::Integer(4));
assert_eq!(result, Ok(Value::Integer(5)));
}
#[test]
fn test_eval_divide_by_zero() {
let interp = Interpreter::new();
let result = interp.eval_divide(&Value::Integer(10), &Value::Integer(0));
assert!(result.is_err());
assert!(result.unwrap_err().contains("Division by zero"));
}
#[test]
fn test_eval_modulo() {
let interp = Interpreter::new();
let result = interp.eval_modulo(&Value::Integer(10), &Value::Integer(3));
assert_eq!(result, Ok(Value::Integer(1)));
}
#[test]
fn test_eval_modulo_by_zero() {
let interp = Interpreter::new();
let result = interp.eval_modulo(&Value::Integer(10), &Value::Integer(0));
assert!(result.is_err());
}
#[test]
fn test_eval_power() {
let interp = Interpreter::new();
let result = interp.eval_power(&Value::Integer(2), &Value::Integer(3));
assert_eq!(result, Ok(Value::Integer(8)));
}
#[test]
fn test_values_equal_integers() {
let interp = Interpreter::new();
assert!(interp.values_equal(&Value::Integer(5), &Value::Integer(5)));
assert!(!interp.values_equal(&Value::Integer(5), &Value::Integer(6)));
}
#[test]
fn test_values_equal_bools() {
let interp = Interpreter::new();
assert!(interp.values_equal(&Value::Bool(true), &Value::Bool(true)));
assert!(!interp.values_equal(&Value::Bool(true), &Value::Bool(false)));
}
#[test]
fn test_eval_less_than() {
let interp = Interpreter::new();
let result = interp.eval_less_than(&Value::Integer(3), &Value::Integer(5));
assert_eq!(result, Ok(Value::Bool(true)));
}
#[test]
fn test_eval_greater_than() {
let interp = Interpreter::new();
let result = interp.eval_greater_than(&Value::Integer(5), &Value::Integer(3));
assert_eq!(result, Ok(Value::Bool(true)));
}
#[test]
fn test_eval_and() {
let interp = Interpreter::new();
let result = interp.eval_and(&[Value::Bool(true), Value::Bool(false)]);
assert_eq!(result, Ok(Value::Bool(false)));
}
#[test]
fn test_eval_or() {
let interp = Interpreter::new();
let result = interp.eval_or(&[Value::Bool(true), Value::Bool(false)]);
assert_eq!(result, Ok(Value::Bool(true)));
}
#[test]
fn test_eval_not() {
let interp = Interpreter::new();
let result = interp.eval_not(&[Value::Bool(true)]);
assert_eq!(result, Ok(Value::Bool(false)));
}
#[test]
fn test_eval_concat() {
let interp = Interpreter::new();
let result = interp.eval_concat(&[
Value::String("foo".to_string()),
Value::String("bar".to_string()),
]);
assert_eq!(result, Ok(Value::String("foobar".to_string())));
}
#[test]
fn test_eval_length_string() {
let interp = Interpreter::new();
let result = interp.eval_length(&[Value::String("hello".to_string())]);
assert_eq!(result, Ok(Value::Integer(5)));
}
#[test]
fn test_eval_length_list() {
let interp = Interpreter::new();
let result = interp.eval_length(&[Value::List(vec![Value::Integer(1), Value::Integer(2)])]);
assert_eq!(result, Ok(Value::Integer(2)));
}
#[test]
fn test_eval_substring() {
let interp = Interpreter::new();
let result = interp.eval_substring(&[
Value::String("hello world".to_string()),
Value::Integer(0),
Value::Integer(5),
]);
assert_eq!(result, Ok(Value::String("hello".to_string())));
}
#[test]
fn test_eval_head() {
let interp = Interpreter::new();
let result =
interp.eval_head(&[Value::List(vec![Value::Integer(1), Value::Integer(2)])]);
assert_eq!(result, Ok(Value::Integer(1)));
}
#[test]
fn test_eval_head_empty() {
let interp = Interpreter::new();
let result = interp.eval_head(&[Value::List(vec![])]);
assert!(result.is_err());
}
#[test]
fn test_eval_tail() {
let interp = Interpreter::new();
let result = interp.eval_tail(&[Value::List(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
])]);
assert_eq!(
result,
Ok(Value::List(vec![Value::Integer(2), Value::Integer(3)]))
);
}
#[test]
fn test_eval_cons() {
let interp = Interpreter::new();
let result = interp.eval_cons(&[
Value::Integer(0),
Value::List(vec![Value::Integer(1), Value::Integer(2)]),
]);
assert_eq!(
result,
Ok(Value::List(vec![
Value::Integer(0),
Value::Integer(1),
Value::Integer(2)
]))
);
}
#[test]
fn test_eval_is_empty_true() {
let interp = Interpreter::new();
let result = interp.eval_is_empty(&[Value::List(vec![])]);
assert_eq!(result, Ok(Value::Bool(true)));
}
#[test]
fn test_eval_is_empty_false() {
let interp = Interpreter::new();
let result = interp.eval_is_empty(&[Value::List(vec![Value::Integer(1)])]);
assert_eq!(result, Ok(Value::Bool(false)));
}
#[test]
fn test_eval_typeof_integer() {
let interp = Interpreter::new();
let result = interp.eval_typeof(&[Value::Integer(42)]);
assert_eq!(result, Ok(Value::String("Integer".to_string())));
}
#[test]
fn test_eval_typeof_string() {
let interp = Interpreter::new();
let result = interp.eval_typeof(&[Value::String("test".to_string())]);
assert_eq!(result, Ok(Value::String("String".to_string())));
}
#[test]
fn test_eval_typeof_list() {
let interp = Interpreter::new();
let result = interp.eval_typeof(&[Value::List(vec![])]);
assert_eq!(result, Ok(Value::String("List".to_string())));
}
#[test]
fn test_eval_typeof_unit() {
let interp = Interpreter::new();
let result = interp.eval_typeof(&[Value::Unit]);
assert_eq!(result, Ok(Value::String("Unit".to_string())));
}
}
#[cfg(test)]
mod property_tests_reference_interpreter_refactored {
use super::*;
use proptest::prelude::*;
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(|| {
});
}
}
}