diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index c0ed167..6f29e3a 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -39,6 +39,7 @@ pub fn eval(exp: Expression, env: &Environment) -> Result eval_iserror_expression(*e, env), Expression::IsNothing(e) => eval_isnothing_expression(*e, env), Expression::FuncCall(name, args) => call(name, args, env), + Expression::MetaExp(f, args, _) => meta(f, args, env), _ if is_constant(exp.clone()) => Ok(EnvValue::Exp(exp)), _ => Err((String::from("Not implemented yet."), None)), } @@ -822,12 +823,29 @@ fn eval_err(exp: Expression, env: &Environment) -> Result) -> Result, + args: Vec, + env: &Environment, +) -> Result)> { + let mut args_values = Vec::new(); + for expr in args { + let value = eval(expr, env)?; + args_values.push(value); + } + let result_value = f(args_values).map_err(|s| (s, None))?; + + Ok(result_value) +} + #[cfg(test)] mod tests { use super::*; use crate::ir::ast::Expression::*; use crate::ir::ast::Function; use crate::ir::ast::Statement::*; + use crate::ir::ast::Type; + use crate::stdlib::math::sqrt_impl; use std::collections::HashMap; //use crate::ir::ast::Type; use crate::ir::ast::Type::*; @@ -2159,4 +2177,25 @@ mod tests { Err(s) => assert!(false, "{}", s), } } + + #[test] + fn interpreter_metaexp_sqrt_returns_correct_value() { + let mut env = Environment::::new(); + env.insert_variable("x".to_string(), EnvValue::Exp(Expression::CReal(25.0))); + + let meta_expr = Expression::MetaExp( + sqrt_impl, + vec![Expression::Var("x".to_string())], + Type::TReal, + ); + + let result_value = eval(meta_expr, &env).expect("Evaluation failed"); + + match result_value { + EnvValue::Exp(Expression::CReal(v)) => { + assert!((v - 5.0).abs() < 0.0001, "Expected 5.0, got {}", v) + } + _ => panic!("Expected a CReal result"), + } + } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index bd5a032..4797dd0 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -3,6 +3,8 @@ pub type Name = String; use nom::IResult; use std::collections::HashMap; +use crate::interpreter::interpreter::EnvValue; + #[derive(Debug, PartialEq, Clone)] pub struct Frame { pub parent_function: Option, @@ -164,6 +166,11 @@ pub enum Expression { /* function call */ FuncCall(Name, Vec), + MetaExp( + fn(Vec) -> Result, + Vec, + Type, + ), /* arithmetic expressions over numbers */ Add(Box, Box), diff --git a/src/main.rs b/src/main.rs index 9df7d2d..0600bf5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use std::io::Write;*/ pub mod interpreter; pub mod ir; pub mod parser; +pub mod stdlib; pub mod tc; fn main() { diff --git a/src/stdlib.rs b/src/stdlib.rs new file mode 100644 index 0000000..5e71e19 --- /dev/null +++ b/src/stdlib.rs @@ -0,0 +1,3 @@ +pub mod math; +pub mod stdlib; +pub mod string; diff --git a/src/stdlib/math.rs b/src/stdlib/math.rs new file mode 100644 index 0000000..d15454f --- /dev/null +++ b/src/stdlib/math.rs @@ -0,0 +1,1112 @@ +use crate::interpreter::interpreter::EnvValue; +use crate::ir::ast::{Expression, Function, Statement, Type}; +use std::collections::HashMap; + +pub fn load_math_stdlib() -> HashMap { + let mut math_stdlib = HashMap::new(); + + math_stdlib.insert( + "sqrt".to_string(), + Function { + name: "sqrt".to_string(), + kind: Some(Type::TReal), + params: Some(vec![("x".to_string(), Type::TReal)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + sqrt_impl, + vec![Expression::Var("x".to_string())], + Type::TReal, + ))))), + }, + ); + + math_stdlib.insert( + "factorial".to_string(), + Function { + name: "factorial".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![("n".to_string(), Type::TInteger)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + factorial_impl, + vec![Expression::Var("x".to_string())], + Type::TInteger, + ))))), + }, + ); + + math_stdlib.insert( + "gcd".to_string(), + Function { + name: "gcd".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![ + ("a".to_string(), Type::TInteger), + ("b".to_string(), Type::TInteger), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + gcd_impl, + vec![ + Expression::Var("a".to_string()), + Expression::Var("b".to_string()), + ], + Type::TInteger, + ))))), + }, + ); + + math_stdlib.insert( + "lcm".to_string(), + Function { + name: "lcm".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![ + ("a".to_string(), Type::TInteger), + ("b".to_string(), Type::TInteger), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + lcm_impl, + vec![ + Expression::Var("a".to_string()), + Expression::Var("b".to_string()), + ], + Type::TInteger, + ))))), + }, + ); + + math_stdlib.insert( + "is_prime".to_string(), + Function { + name: "is_prime".to_string(), + kind: Some(Type::TBool), + params: Some(vec![("x".to_string(), Type::TInteger)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + is_prime_impl, + vec![Expression::Var("x".to_string())], + Type::TBool, + ))))), + }, + ); + + math_stdlib.insert( + "comb".to_string(), + Function { + name: "comb".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![ + ("n".to_string(), Type::TInteger), + ("k".to_string(), Type::TInteger), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + comb_impl, + vec![ + Expression::Var("n".to_string()), + Expression::Var("r".to_string()), + ], + Type::TInteger, + ))))), + }, + ); + + math_stdlib.insert( + "perm".to_string(), + Function { + name: "perm".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![ + ("n".to_string(), Type::TInteger), + ("k".to_string(), Type::TInteger), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + perm_impl, + vec![ + Expression::Var("n".to_string()), + Expression::Var("r".to_string()), + ], + Type::TInteger, + ))))), + }, + ); + + math_stdlib.insert( + "log".to_string(), + Function { + name: "log".to_string(), + kind: Some(Type::TReal), + params: Some(vec![ + ("x".to_string(), Type::TReal), + ("base".to_string(), Type::TReal), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + log_impl, + vec![ + Expression::Var("x".to_string()), + Expression::Var("base".to_string()), + ], + Type::TReal, + ))))), + }, + ); + + math_stdlib.insert( + "degrees".to_string(), + Function { + name: "degrees".to_string(), + kind: Some(Type::TReal), + params: Some(vec![("rad".to_string(), Type::TReal)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + degrees_impl, + vec![Expression::Var("rad".to_string())], + Type::TReal, + ))))), + }, + ); + + math_stdlib.insert( + "radians".to_string(), + Function { + name: "radians".to_string(), + kind: Some(Type::TReal), + params: Some(vec![("deg".to_string(), Type::TReal)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + radians_impl, + vec![Expression::Var("deg".to_string())], + Type::TReal, + ))))), + }, + ); + + math_stdlib.insert( + "cos".to_string(), + Function { + name: "cos".to_string(), + kind: Some(Type::TReal), + params: Some(vec![("x".to_string(), Type::TReal)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + cos_impl, + vec![Expression::Var("x".to_string())], + Type::TReal, + ))))), + }, + ); + + math_stdlib.insert( + "sin".to_string(), + Function { + name: "sin".to_string(), + kind: Some(Type::TReal), + params: Some(vec![("x".to_string(), Type::TReal)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + sin_impl, + vec![Expression::Var("x".to_string())], + Type::TReal, + ))))), + }, + ); + + math_stdlib.insert( + "tan".to_string(), + Function { + name: "tan".to_string(), + kind: Some(Type::TReal), + params: Some(vec![("x".to_string(), Type::TReal)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + tan_impl, + vec![Expression::Var("x".to_string())], + Type::TReal, + ))))), + }, + ); + + math_stdlib +} + +pub fn sqrt_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("sqrt expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CReal(x)) = &args[0] { + Ok(EnvValue::Exp(Expression::CReal(x.sqrt()))) + } else { + Err("sqrt expects a real number argument".to_string()) + } +} + +pub fn factorial_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("factorial expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CInt(n)) = &args[0] { + if *n < 0 { + return Err("factorial expects a non-negative integer argument".to_string()); + } + let mut prod: i32 = 1; + for i in 1..=*n { + prod *= i; + } + Ok(EnvValue::Exp(Expression::CInt(prod))) + } else { + Err("factorial expects an integer argument".to_string()) + } +} + +pub fn gcd_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("gcd expects exactly two arguments".to_string()); + } + + if let (EnvValue::Exp(Expression::CInt(a)), EnvValue::Exp(Expression::CInt(b))) = + (&args[0], &args[1]) + { + let mut a = *a; + let mut b = *b; + while b != 0 { + let t = b; + b = a % b; + a = t; + } + Ok(EnvValue::Exp(Expression::CInt(a.abs()))) + } else { + Err("gcd expects two integer arguments".to_string()) + } +} + +pub fn lcm_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("lcm expects exactly two arguments".to_string()); + } + + if let (EnvValue::Exp(Expression::CInt(a)), EnvValue::Exp(Expression::CInt(b))) = + (&args[0], &args[1]) + { + let gcd_val = match gcd_impl(args.clone()) { + Ok(EnvValue::Exp(Expression::CInt(val))) => val, + Err(err) => return Err(format!("Error calculating gcd: {}", err)), + _ => return Err("Unexpected error in gcd calculation".to_string()), + }; + + let lcm_val = (a * b).abs() / gcd_val; + Ok(EnvValue::Exp(Expression::CInt(lcm_val))) + } else { + Err("lcm expects two integer arguments".to_string()) + } +} + +pub fn comb_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("comb expects exactly two arguments".to_string()); + } + + if let (EnvValue::Exp(Expression::CInt(n)), EnvValue::Exp(Expression::CInt(k))) = + (&args[0], &args[1]) + { + if *n < 0 || *k < 0 { + return Err("comb expects non-negative integers".to_string()); + } + let n = *n; + let mut k = *k; + if k > n { + return Ok(EnvValue::Exp(Expression::CInt(0))); + } + if k > n - k { + k = n - k; + } + let result = (0..k).fold(1, |acc, i| acc * (n - i) / (i + 1)); + Ok(EnvValue::Exp(Expression::CInt(result))) + } else { + Err("comb expects two integer arguments".to_string()) + } +} + +pub fn perm_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("perm expects exactly two arguments".to_string()); + } + + if let (EnvValue::Exp(Expression::CInt(n)), EnvValue::Exp(Expression::CInt(k))) = + (&args[0], &args[1]) + { + if *n < 0 || *k < 0 { + return Err("perm expects non-negative integers".to_string()); + } + let n = *n; + let k = *k; + if k > n { + return Ok(EnvValue::Exp(Expression::CInt(0))); + } + let mut result: i32 = 1; + for i in 0..k { + result *= n - i; + } + Ok(EnvValue::Exp(Expression::CInt(result))) + } else { + Err("perm expects two integer arguments".to_string()) + } +} + +pub fn is_prime_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("is_prime expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CInt(n)) = &args[0] { + if *n < 0 { + return Err("is_prime expects a non-negative integer".to_string()); + } + let n = *n; + let result = { + if n <= 1 { + false + } else if n <= 3 { + true + } else if n % 2 == 0 || n % 3 == 0 { + false + } else { + let mut i = 5; + let mut prime = true; + while i * i <= n { + if n % i == 0 || n % (i + 2) == 0 { + prime = false; + break; + } + i += 6; + } + prime + } + }; + if result { + Ok(EnvValue::Exp(Expression::CTrue)) + } else { + Ok(EnvValue::Exp(Expression::CFalse)) + } + } else { + Err("is_prime expects an integer argument".to_string()) + } +} + +pub fn log_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("log expects exactly two arguments".to_string()); + } + if let (EnvValue::Exp(Expression::CReal(base)), EnvValue::Exp(Expression::CReal(x))) = + (&args[0], &args[1]) + { + Ok(EnvValue::Exp(Expression::CReal(x.log(*base)))) + } else { + Err("log expects two real arguments".to_string()) + } +} + +// Constante de PI com precisão suficiente +const PI: f64 = 3.141592653589793; +const EPSILON: f64 = 1e-10; + +// Função para calcular o fatorial (usada em série de Taylor) +fn factorial(n: i32) -> f64 { + (1..=n).fold(1, |acc, x| acc * x) as f64 +} + +// Função para converter radianos em graus +pub fn degrees_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("degrees expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CReal(rad)) = &args[0] { + Ok(EnvValue::Exp(Expression::CReal(rad * (180.0 / PI)))) + } else { + Err("degrees expects a real number argument".to_string()) + } +} + +// Função para converter graus em radianos +pub fn radians_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("radians expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CReal(deg)) = &args[0] { + Ok(EnvValue::Exp(Expression::CReal(deg * (PI / 180.0)))) + } else { + Err("radians expects a real number argument".to_string()) + } +} + +// Série de Taylor para cosseno +fn taylor_cos(x: f64) -> f64 { + let mut sum = 0.0; + let mut term; + let mut n = 0; + loop { + term = ((-1.0f64).powi(n) * x.powi(2 * n)) / factorial(2 * n); + sum += term; + if term.abs() < EPSILON { + break; + } + n += 1; + } + sum +} + +// Série de Taylor para seno +fn taylor_sin(x: f64) -> f64 { + let mut sum = 0.0; + let mut term; + let mut n = 0; + loop { + term = ((-1.0f64).powi(n) * x.powi(2 * n + 1)) / factorial(2 * n + 1); + sum += term; + if term.abs() < EPSILON { + break; + } + n += 1; + } + sum +} + +// Tangente usando seno e cosseno +fn taylor_tan(x: f64) -> f64 { + taylor_sin(x) / taylor_cos(x) +} + +// Função cosseno +pub fn cos_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("cos expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CReal(x)) = &args[0] { + Ok(EnvValue::Exp(Expression::CReal(taylor_cos(*x)))) + } else { + Err("cos expects a real number argument".to_string()) + } +} + +// Função seno +pub fn sin_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("sin expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CReal(x)) = &args[0] { + Ok(EnvValue::Exp(Expression::CReal(taylor_sin(*x)))) + } else { + Err("sin expects a real number argument".to_string()) + } +} + +// Função tangente +pub fn tan_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("tan expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CReal(x)) = &args[0] { + Ok(EnvValue::Exp(Expression::CReal(taylor_tan(*x)))) + } else { + Err("tan expects a real number argument".to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + //TESTES FUNCAO SQRT + #[test] + fn test_sqrt_positive_real() { + let result = sqrt_impl(vec![EnvValue::Exp(Expression::CReal(9.0))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(res_value))) = result { + assert_eq!(res_value, 3.0); + } + + let result = sqrt_impl(vec![EnvValue::Exp(Expression::CReal(49.0))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(res_value))) = result { + assert_eq!(res_value, 7.0); + } + + let result = sqrt_impl(vec![EnvValue::Exp(Expression::CReal(121.0))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(res_value))) = result { + assert_eq!(res_value, 11.0); + } + } + + #[test] + fn test_sqrt_zero() { + let result = sqrt_impl(vec![EnvValue::Exp(Expression::CReal(0.0))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(res_value))) = result { + assert_eq!(res_value, 0.0); + } + } + + #[test] + fn test_sqrt_invalid_number_of_arguments() { + let result = sqrt_impl(vec![]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "sqrt expects exactly one argument"); + } + + #[test] + fn test_sqrt_invalid_number_of_arguments_multiple() { + let result = sqrt_impl(vec![ + EnvValue::Exp(Expression::CReal(25.0)), + EnvValue::Exp(Expression::CReal(9.0)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "sqrt expects exactly one argument"); + } + + #[test] + fn test_sqrt_invalid_argument_type() { + let result = sqrt_impl(vec![EnvValue::Exp(Expression::CInt(25))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "sqrt expects a real number argument"); + } + //TESTES FUNCAO FACTORIAL + #[test] + fn test_factorial_valid_inputs() { + let result = factorial_impl(vec![EnvValue::Exp(Expression::CInt(0))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 1); + } + + let result = factorial_impl(vec![EnvValue::Exp(Expression::CInt(1))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 1); + } + + let result = factorial_impl(vec![EnvValue::Exp(Expression::CInt(5))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 120); + } + + let result = factorial_impl(vec![EnvValue::Exp(Expression::CInt(10))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 3628800); + } + } + + #[test] + fn test_factorial_invalid_number_of_arguments() { + let result = factorial_impl(vec![]); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "factorial expects exactly one argument" + ); + } + + #[test] + fn test_factorial_invalid_number_of_arguments_multiple() { + let result = factorial_impl(vec![ + EnvValue::Exp(Expression::CInt(1)), + EnvValue::Exp(Expression::CInt(2)), + ]); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "factorial expects exactly one argument" + ); + } + + #[test] + fn test_factorial_invalid_argument_type() { + let result = factorial_impl(vec![EnvValue::Exp(Expression::CReal(3.5))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "factorial expects an integer argument"); + } + + #[test] + fn test_factorial_negative_argument() { + let result = factorial_impl(vec![EnvValue::Exp(Expression::CInt(-1))]); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "factorial expects a non-negative integer argument" + ); + } + //TESTES FUNCAO GCD + #[test] + fn test_gcd_valid_inputs() { + let result = gcd_impl(vec![ + EnvValue::Exp(Expression::CInt(48)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 6); + } + + let result = gcd_impl(vec![ + EnvValue::Exp(Expression::CInt(7)), + EnvValue::Exp(Expression::CInt(3)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 1); + } + + let result = gcd_impl(vec![ + EnvValue::Exp(Expression::CInt(-48)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 6); + } + + let result = gcd_impl(vec![ + EnvValue::Exp(Expression::CInt(0)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 18); + } + } + + #[test] + fn test_gcd_invalid_number_of_arguments() { + let result = gcd_impl(vec![EnvValue::Exp(Expression::CInt(48))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "gcd expects exactly two arguments"); + } + + #[test] + fn test_gcd_invalid_number_of_arguments_multiple() { + let result = gcd_impl(vec![ + EnvValue::Exp(Expression::CInt(48)), + EnvValue::Exp(Expression::CInt(18)), + EnvValue::Exp(Expression::CInt(6)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "gcd expects exactly two arguments"); + } + + #[test] + fn test_gcd_invalid_argument_type() { + let result = gcd_impl(vec![ + EnvValue::Exp(Expression::CReal(48.0)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "gcd expects two integer arguments"); + } + //TESTES PARA LCM + #[test] + fn test_lcm_valid_inputs() { + let result = lcm_impl(vec![ + EnvValue::Exp(Expression::CInt(48)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 144); + } + + let result = lcm_impl(vec![ + EnvValue::Exp(Expression::CInt(7)), + EnvValue::Exp(Expression::CInt(3)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 21); + } + + let result = lcm_impl(vec![ + EnvValue::Exp(Expression::CInt(-48)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 144); + } + + let result = lcm_impl(vec![ + EnvValue::Exp(Expression::CInt(0)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 0); + } + } + + #[test] + fn test_lcm_invalid_number_of_arguments() { + let result = lcm_impl(vec![EnvValue::Exp(Expression::CInt(48))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "lcm expects exactly two arguments"); + } + + #[test] + fn test_lcm_invalid_number_of_arguments_multiple() { + let result = lcm_impl(vec![ + EnvValue::Exp(Expression::CInt(48)), + EnvValue::Exp(Expression::CInt(18)), + EnvValue::Exp(Expression::CInt(6)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "lcm expects exactly two arguments"); + } + + #[test] + fn test_lcm_invalid_argument_type() { + let result = lcm_impl(vec![ + EnvValue::Exp(Expression::CReal(48.0)), + EnvValue::Exp(Expression::CInt(18)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "lcm expects two integer arguments"); + } + + //TESTES PARA COMB + #[test] + fn test_comb_valid_inputs() { + let result = comb_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(2)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 10); + } + + let result = comb_impl(vec![ + EnvValue::Exp(Expression::CInt(10)), + EnvValue::Exp(Expression::CInt(3)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 120); + } + + let result = comb_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(6)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 0); + } + } + + #[test] + fn test_comb_invalid_number_of_arguments() { + let result = comb_impl(vec![EnvValue::Exp(Expression::CInt(5))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "comb expects exactly two arguments"); + } + + #[test] + fn test_comb_invalid_number_of_arguments_multiple() { + let result = comb_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(2)), + EnvValue::Exp(Expression::CInt(1)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "comb expects exactly two arguments"); + } + + #[test] + fn test_comb_invalid_argument_type() { + let result = comb_impl(vec![ + EnvValue::Exp(Expression::CReal(5.0)), + EnvValue::Exp(Expression::CInt(2)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "comb expects two integer arguments"); + } + + #[test] + fn test_comb_negative_arguments() { + let result = comb_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(-2)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "comb expects non-negative integers"); + } + + //TESTES PARA PERM + #[test] + fn test_perm_valid_inputs() { + let result = perm_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(2)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 20); + } + + let result = perm_impl(vec![ + EnvValue::Exp(Expression::CInt(10)), + EnvValue::Exp(Expression::CInt(3)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 720); + } + + let result = perm_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(6)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(value))) = result { + assert_eq!(value, 0); + } + } + + #[test] + fn test_perm_invalid_number_of_arguments() { + let result = perm_impl(vec![EnvValue::Exp(Expression::CInt(5))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "perm expects exactly two arguments"); + } + + #[test] + fn test_perm_invalid_number_of_arguments_multiple() { + let result = perm_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(2)), + EnvValue::Exp(Expression::CInt(1)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "perm expects exactly two arguments"); + } + + #[test] + fn test_perm_invalid_argument_type() { + let result = perm_impl(vec![ + EnvValue::Exp(Expression::CReal(5.0)), + EnvValue::Exp(Expression::CInt(2)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "perm expects two integer arguments"); + } + + #[test] + fn test_perm_negative_arguments() { + let result = perm_impl(vec![ + EnvValue::Exp(Expression::CInt(5)), + EnvValue::Exp(Expression::CInt(-2)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "perm expects non-negative integers"); + } + + //================================================================================================= + #[test] + fn test_is_prime_valid_inputs() { + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CInt(2))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CTrue)) = result { + assert!(true); + } + + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CInt(3))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CTrue)) = result { + assert!(true); + } + + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CInt(7))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CTrue)) = result { + assert!(true); + } + + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CInt(13))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CTrue)) = result { + assert!(true); + } + + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CInt(17))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CTrue)) = result { + assert!(true); + } + } + + #[test] + fn test_is_prime_invalid_number_of_arguments() { + let result = is_prime_impl(vec![]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "is_prime expects exactly one argument"); + } + + #[test] + fn test_is_prime_invalid_number_of_arguments_multiple() { + let result = is_prime_impl(vec![ + EnvValue::Exp(Expression::CInt(2)), + EnvValue::Exp(Expression::CInt(3)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "is_prime expects exactly one argument"); + } + + #[test] + fn test_is_prime_invalid_argument_type() { + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CReal(2.0))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "is_prime expects an integer argument"); + } + + #[test] + fn test_is_prime_negative_argument() { + let result = is_prime_impl(vec![EnvValue::Exp(Expression::CInt(-1))]); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "is_prime expects a non-negative integer" + ); + } + + //================================================================================================= + #[test] + fn test_log_valid_inputs() { + let result = log_impl(vec![ + EnvValue::Exp(Expression::CReal(10.0)), + EnvValue::Exp(Expression::CReal(100.0)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(value))) = result { + assert_eq!(value, 2.0); + } + + let result = log_impl(vec![ + EnvValue::Exp(Expression::CReal(2.0)), + EnvValue::Exp(Expression::CReal(8.0)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(value))) = result { + assert_eq!(value, 3.0); + } + + let result = log_impl(vec![ + EnvValue::Exp(Expression::CReal(10.0)), + EnvValue::Exp(Expression::CReal(10000.0)), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CReal(value))) = result { + assert_eq!(value, 4.0); + } + } + + #[test] + fn test_log_invalid_number_of_arguments() { + let result = log_impl(vec![EnvValue::Exp(Expression::CReal(10.0))]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "log expects exactly two arguments"); + } + + #[test] + fn test_log_invalid_number_of_arguments_multiple() { + let result = log_impl(vec![ + EnvValue::Exp(Expression::CReal(10.0)), + EnvValue::Exp(Expression::CReal(100.0)), + EnvValue::Exp(Expression::CReal(10.0)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "log expects exactly two arguments"); + } + + #[test] + fn test_log_invalid_argument_type() { + let result = log_impl(vec![ + EnvValue::Exp(Expression::CInt(10)), + EnvValue::Exp(Expression::CReal(100.0)), + ]); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "log expects two real arguments"); + } + #[test] + fn test_degrees_impl() { + let arg = vec![EnvValue::Exp(Expression::CReal(PI))]; + let result = degrees_impl(arg).unwrap(); + if let EnvValue::Exp(Expression::CReal(deg)) = result { + assert!((deg - 180.0).abs() < EPSILON); + } else { + panic!("Unexpected result type"); + } + } + + #[test] + fn test_radians_impl() { + let arg = vec![EnvValue::Exp(Expression::CReal(180.0))]; + let result = radians_impl(arg).unwrap(); + if let EnvValue::Exp(Expression::CReal(rad)) = result { + assert!((rad - PI).abs() < EPSILON); + } else { + panic!("Unexpected result type"); + } + } + + #[test] + fn test_cos_impl() { + let arg = vec![EnvValue::Exp(Expression::CReal(0.0))]; + let result = cos_impl(arg).unwrap(); + if let EnvValue::Exp(Expression::CReal(value)) = result { + assert!((value - 1.0).abs() < EPSILON); + } else { + panic!("Unexpected result type"); + } + } + + #[test] + fn test_sin_impl() { + let arg = vec![EnvValue::Exp(Expression::CReal(0.0))]; + let result = sin_impl(arg).unwrap(); + if let EnvValue::Exp(Expression::CReal(value)) = result { + assert!(value.abs() < EPSILON); + } else { + panic!("Unexpected result type"); + } + } + + #[test] + fn test_tan_impl() { + let arg = vec![EnvValue::Exp(Expression::CReal(0.0))]; + let result = tan_impl(arg).unwrap(); + if let EnvValue::Exp(Expression::CReal(value)) = result { + assert!(value.abs() < EPSILON); + } else { + panic!("Unexpected result type"); + } + } + + #[test] + fn test_invalid_args() { + // Teste com número errado de argumentos + let too_many_args = vec![ + EnvValue::Exp(Expression::CReal(1.0)), + EnvValue::Exp(Expression::CReal(2.0)), + ]; + assert!(degrees_impl(too_many_args.clone()).is_err()); + assert!(radians_impl(too_many_args.clone()).is_err()); + assert!(cos_impl(too_many_args.clone()).is_err()); + assert!(sin_impl(too_many_args.clone()).is_err()); + assert!(tan_impl(too_many_args.clone()).is_err()); + + // Teste com tipo errado de argumento + let invalid_type_arg = vec![EnvValue::Exp(Expression::CString("invalid".to_string()))]; + assert!(degrees_impl(invalid_type_arg.clone()).is_err()); + assert!(radians_impl(invalid_type_arg.clone()).is_err()); + assert!(cos_impl(invalid_type_arg.clone()).is_err()); + assert!(sin_impl(invalid_type_arg.clone()).is_err()); + assert!(tan_impl(invalid_type_arg.clone()).is_err()); + } +} diff --git a/src/stdlib/stdlib.rs b/src/stdlib/stdlib.rs new file mode 100644 index 0000000..0343283 --- /dev/null +++ b/src/stdlib/stdlib.rs @@ -0,0 +1,727 @@ +use crate::ir::ast::Function; +use std::collections::HashMap; + +use crate::stdlib::math::load_math_stdlib; +use crate::stdlib::string::load_string_stdlib; + +pub fn load_stdlib() -> HashMap { + let mut stdlib = HashMap::new(); + + let math_stdlib = load_math_stdlib(); + for (name, func) in math_stdlib { + stdlib.insert(name, func); + } + + let string_stdlib = load_string_stdlib(); + for (name, func) in string_stdlib { + stdlib.insert(name, func); + } + + stdlib +} + +#[cfg(test)] +mod tests { + use crate::ir::ast::{Expression, Statement, Type}; + use crate::stdlib::math::load_math_stdlib; + use crate::stdlib::string::load_string_stdlib; + + #[test] + fn test_load_math_stdlib_contains_sqrt() { + let math_stdlib = load_math_stdlib(); + let sqrt_func = math_stdlib + .get("sqrt") + .expect("Function 'sqrt' not found in math stdlib"); + + assert_eq!(sqrt_func.kind, Some(Type::TReal)); + + let params = sqrt_func + .params + .as_ref() + .expect("Expected parameters for sqrt"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "x"); + assert_eq!(params[0].1, Type::TReal); + + match sqrt_func + .body + .as_ref() + .expect("Expected body for sqrt") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for sqrt"), + }, + _ => panic!("Expected Return statement for sqrt body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_factorial() { + let math_stdlib = load_math_stdlib(); + let factorial_func = math_stdlib + .get("factorial") + .expect("Function 'factorial' not found in math stdlib"); + + assert_eq!(factorial_func.kind, Some(Type::TInteger)); + + let params = factorial_func + .params + .as_ref() + .expect("Expected parameters for factorial"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "n"); + assert_eq!(params[0].1, Type::TInteger); + + match factorial_func + .body + .as_ref() + .expect("Expected body for factorial") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for factorial"), + }, + _ => panic!("Expected Return statement for factorial body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_gcd() { + let math_stdlib = load_math_stdlib(); + let gcd_func = math_stdlib + .get("gcd") + .expect("Function 'gcd' not found in math stdlib"); + + assert_eq!(gcd_func.kind, Some(Type::TInteger)); + + let params = gcd_func + .params + .as_ref() + .expect("Expected parameters for gcd"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "a"); + assert_eq!(params[0].1, Type::TInteger); + assert_eq!(params[1].0, "b"); + assert_eq!(params[1].1, Type::TInteger); + + match gcd_func + .body + .as_ref() + .expect("Expected body for gcd") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for gcd"), + }, + _ => panic!("Expected Return statement for gcd body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_lcm() { + let math_stdlib = load_math_stdlib(); + let lcm_func = math_stdlib + .get("lcm") + .expect("Function 'lcm' not found in math stdlib"); + + assert_eq!(lcm_func.kind, Some(Type::TInteger)); + + let params = lcm_func + .params + .as_ref() + .expect("Expected parameters for lcm"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "a"); + assert_eq!(params[0].1, Type::TInteger); + assert_eq!(params[1].0, "b"); + assert_eq!(params[1].1, Type::TInteger); + + match lcm_func + .body + .as_ref() + .expect("Expected body for lcm") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for lcm"), + }, + _ => panic!("Expected Return statement for lcm body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_comb() { + let math_stdlib = load_math_stdlib(); + let comb_func = math_stdlib + .get("comb") + .expect("Function 'comb' not found in math stdlib"); + + assert_eq!(comb_func.kind, Some(Type::TInteger)); + + let params = comb_func + .params + .as_ref() + .expect("Expected parameters for comb"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "n"); + assert_eq!(params[0].1, Type::TInteger); + assert_eq!(params[1].0, "k"); + assert_eq!(params[1].1, Type::TInteger); + + match comb_func + .body + .as_ref() + .expect("Expected body for comb") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for comb"), + }, + _ => panic!("Expected Return statement for comb body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_perm() { + let math_stdlib = load_math_stdlib(); + let perm_func = math_stdlib + .get("perm") + .expect("Function 'perm' not found in math stdlib"); + + assert_eq!(perm_func.kind, Some(Type::TInteger)); + + let params = perm_func + .params + .as_ref() + .expect("Expected parameters for perm"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "n"); + assert_eq!(params[0].1, Type::TInteger); + assert_eq!(params[1].0, "k"); + assert_eq!(params[1].1, Type::TInteger); + + match perm_func + .body + .as_ref() + .expect("Expected body for perm") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for perm"), + }, + _ => panic!("Expected Return statement for perm body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_is_prime() { + let math_stdlib = load_math_stdlib(); + let is_prime_func = math_stdlib + .get("is_prime") + .expect("Function 'is_prime' not found in math stdlib"); + + assert_eq!(is_prime_func.kind, Some(Type::TBool)); + + let params = is_prime_func + .params + .as_ref() + .expect("Expected parameters for is_prime"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "x"); + assert_eq!(params[0].1, Type::TInteger); + + match is_prime_func + .body + .as_ref() + .expect("Expected body for is_prime") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TBool); + } + _ => panic!("Expected MetaExp inside Return for is_prime"), + }, + _ => panic!("Expected Return statement for is_prime body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_log() { + let math_stdlib = load_math_stdlib(); + let log_func = math_stdlib + .get("log") + .expect("Function 'log' not found in math stdlib"); + + assert_eq!(log_func.kind, Some(Type::TReal)); + + let params = log_func + .params + .as_ref() + .expect("Expected parameters for log"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "x"); + assert_eq!(params[0].1, Type::TReal); + assert_eq!(params[1].0, "base"); + assert_eq!(params[1].1, Type::TReal); + + match log_func + .body + .as_ref() + .expect("Expected body for log") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for log"), + }, + _ => panic!("Expected Return statement for log body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_degrees() { + let math_stdlib = load_math_stdlib(); + let degrees_func = math_stdlib + .get("degrees") + .expect("Function 'degrees' not found in math stdlib"); + + assert_eq!(degrees_func.kind, Some(Type::TReal)); + + let params = degrees_func + .params + .as_ref() + .expect("Expected parameters for degrees"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "rad"); + assert_eq!(params[0].1, Type::TReal); + + match degrees_func + .body + .as_ref() + .expect("Expected body for degrees") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for degrees"), + }, + _ => panic!("Expected Return statement for degrees body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_radians() { + let math_stdlib = load_math_stdlib(); + let radians_func = math_stdlib + .get("radians") + .expect("Function 'radians' not found in math stdlib"); + + assert_eq!(radians_func.kind, Some(Type::TReal)); + + let params = radians_func + .params + .as_ref() + .expect("Expected parameters for radians"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "deg"); + assert_eq!(params[0].1, Type::TReal); + + match radians_func + .body + .as_ref() + .expect("Expected body for radians") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for radians"), + }, + _ => panic!("Expected Return statement for radians body"), + } + } + + #[test] + fn test_load_string_stdlib_contains_str_upper() { + let string_stdlib = load_string_stdlib(); + let str_upper_func = string_stdlib + .get("str_upper") + .expect("Function 'str_upper' not found in string stdlib"); + + assert_eq!(str_upper_func.kind, Some(Type::TString)); + + let params = str_upper_func + .params + .as_ref() + .expect("Expected parameters for str_upper"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + + match str_upper_func + .body + .as_ref() + .expect("Expected body for str_upper") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TString); + } + _ => panic!("Expected MetaExp inside Return for str_upper"), + }, + _ => panic!("Expected Return statement for str_upper body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_cos() { + let math_stdlib = load_math_stdlib(); + let cos_func = math_stdlib + .get("cos") + .expect("Function 'cos' not found in math stdlib"); + + assert_eq!(cos_func.kind, Some(Type::TReal)); + + let params = cos_func + .params + .as_ref() + .expect("Expected parameters for cos"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "x"); + assert_eq!(params[0].1, Type::TReal); + + match cos_func + .body + .as_ref() + .expect("Expected body for cos") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for cos"), + }, + _ => panic!("Expected Return statement for cos body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_sin() { + let math_stdlib = load_math_stdlib(); + let sin_func = math_stdlib + .get("sin") + .expect("Function 'sin' not found in math stdlib"); + + assert_eq!(sin_func.kind, Some(Type::TReal)); + + let params = sin_func + .params + .as_ref() + .expect("Expected parameters for sin"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "x"); + assert_eq!(params[0].1, Type::TReal); + + match sin_func + .body + .as_ref() + .expect("Expected body for sin") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for sin"), + }, + _ => panic!("Expected Return statement for sin body"), + } + } + + #[test] + fn test_load_math_stdlib_contains_tan() { + let math_stdlib = load_math_stdlib(); + let tan_func = math_stdlib + .get("tan") + .expect("Function 'tan' not found in math stdlib"); + + assert_eq!(tan_func.kind, Some(Type::TReal)); + + let params = tan_func + .params + .as_ref() + .expect("Expected parameters for tan"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "x"); + assert_eq!(params[0].1, Type::TReal); + + match tan_func + .body + .as_ref() + .expect("Expected body for tan") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TReal); + } + _ => panic!("Expected MetaExp inside Return for tan"), + }, + _ => panic!("Expected Return statement for tan body"), + } + } + + #[test] + fn test_load_string_stdlib_contains_str_lower() { + let string_stdlib = load_string_stdlib(); + let str_lower_func = string_stdlib + .get("str_lower") + .expect("Function 'str_lower' not found in string stdlib"); + + assert_eq!(str_lower_func.kind, Some(Type::TString)); + + let params = str_lower_func + .params + .as_ref() + .expect("Expected parameters for str_lower"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + + match str_lower_func + .body + .as_ref() + .expect("Expected body for str_lower") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TString); + } + _ => panic!("Expected MetaExp inside Return for str_lower"), + }, + _ => panic!("Expected Return statement for str_lower body"), + } + } + #[test] + fn test_load_string_stdlib_contains_str_length() { + let string_stdlib = load_string_stdlib(); + let str_length_func = string_stdlib + .get("str_length") + .expect("Function 'str_length' not found in string stdlib"); + + assert_eq!(str_length_func.kind, Some(Type::TInteger)); + + let params = str_length_func + .params + .as_ref() + .expect("Expected parameters for str_length"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + + match str_length_func + .body + .as_ref() + .expect("Expected body for str_length") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for str_length"), + }, + _ => panic!("Expected Return statement for str_length body"), + } + } + + #[test] + fn test_load_string_stdlib_contains_str_reverse() { + let string_stdlib = load_string_stdlib(); + let str_reverse_func = string_stdlib + .get("str_reverse") + .expect("Function 'str_reverse' not found in string stdlib"); + + assert_eq!(str_reverse_func.kind, Some(Type::TString)); + + let params = str_reverse_func + .params + .as_ref() + .expect("Expected parameters for str_reverse"); + assert_eq!(params.len(), 1); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + + match str_reverse_func + .body + .as_ref() + .expect("Expected body for str_reverse") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 1); + assert_eq!(*ret_type, Type::TString); + } + _ => panic!("Expected MetaExp inside Return for str_reverse"), + }, + _ => panic!("Expected Return statement for str_reverse body"), + } + } + + #[test] + fn test_load_string_stdlib_contains_cont_chars() { + let string_stdlib = load_string_stdlib(); + let cont_chars_func = string_stdlib + .get("cont_chars") + .expect("Function 'cont_chars' not found in string stdlib"); + + assert_eq!(cont_chars_func.kind, Some(Type::TInteger)); + + let params = cont_chars_func + .params + .as_ref() + .expect("Expected parameters for cont_chars"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + assert_eq!(params[1].0, "c"); + assert_eq!(params[1].1, Type::TString); + + match cont_chars_func + .body + .as_ref() + .expect("Expected body for cont_chars") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TInteger); + } + _ => panic!("Expected MetaExp inside Return for cont_chars"), + }, + _ => panic!("Expected Return statement for cont_chars body"), + } + } + + #[test] + fn test_load_string_stdlib_contains_filter_out_char() { + let string_stdlib = load_string_stdlib(); + let filter_out_char_func = string_stdlib + .get("filter_out_char") + .expect("Function 'filter_out_char' not found in string stdlib"); + + assert_eq!(filter_out_char_func.kind, Some(Type::TString)); + + let params = filter_out_char_func + .params + .as_ref() + .expect("Expected parameters for filter_out_char"); + assert_eq!(params.len(), 2); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + assert_eq!(params[1].0, "c"); + assert_eq!(params[1].1, Type::TString); + + match filter_out_char_func + .body + .as_ref() + .expect("Expected body for filter_out_char") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 2); + assert_eq!(*ret_type, Type::TString); + } + _ => panic!("Expected MetaExp inside Return for filter_out_char"), + }, + _ => panic!("Expected Return statement for filter_out_char body"), + } + } + + #[test] + fn test_load_string_stdlib_contains_replace() { + let string_stdlib = load_string_stdlib(); + let replace_func = string_stdlib + .get("replace") + .expect("Function 'replace' not found in string stdlib"); + + assert_eq!(replace_func.kind, Some(Type::TString)); + + let params = replace_func + .params + .as_ref() + .expect("Expected parameters for replace"); + assert_eq!(params.len(), 4); + assert_eq!(params[0].0, "s"); + assert_eq!(params[0].1, Type::TString); + assert_eq!(params[1].0, "old"); + assert_eq!(params[1].1, Type::TString); + assert_eq!(params[2].0, "new"); + assert_eq!(params[2].1, Type::TString); + assert_eq!(params[3].0, "count"); + assert_eq!(params[3].1, Type::TInteger); + + match replace_func + .body + .as_ref() + .expect("Expected body for replace") + .as_ref() + { + Statement::Return(ref inner) => match **inner { + Expression::MetaExp(_, ref args, ref ret_type) => { + assert_eq!(args.len(), 4); + assert_eq!(*ret_type, Type::TString); + } + _ => panic!("Expected MetaExp inside Return for replace"), + }, + _ => panic!("Expected Return statement for replace body"), + } + } +} diff --git a/src/stdlib/string.rs b/src/stdlib/string.rs new file mode 100644 index 0000000..248143f --- /dev/null +++ b/src/stdlib/string.rs @@ -0,0 +1,333 @@ +use crate::interpreter::interpreter::EnvValue; +use crate::ir::ast::{Expression, Function, Statement, Type}; +use std::collections::HashMap; + +pub fn load_string_stdlib() -> HashMap { + let mut string_stdlib = HashMap::new(); + + string_stdlib.insert( + "str_upper".to_string(), + Function { + name: "str_upper".to_string(), + kind: Some(Type::TString), + params: Some(vec![("s".to_string(), Type::TString)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + str_upper_impl, + vec![Expression::Var("s".to_string())], + Type::TString, + ))))), + }, + ); + + string_stdlib.insert( + "str_lower".to_string(), + Function { + name: "str_lower".to_string(), + kind: Some(Type::TString), + params: Some(vec![("s".to_string(), Type::TString)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + str_lower_impl, + vec![Expression::Var("s".to_string())], + Type::TString, + ))))), + }, + ); + + string_stdlib.insert( + "str_length".to_string(), + Function { + name: "str_length".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![("s".to_string(), Type::TString)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + str_length_impl, + vec![Expression::Var("s".to_string())], + Type::TInteger, + ))))), + }, + ); + + string_stdlib.insert( + "str_reverse".to_string(), + Function { + name: "str_reverse".to_string(), + kind: Some(Type::TString), + params: Some(vec![("s".to_string(), Type::TString)]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + str_reverse_impl, + vec![Expression::Var("s".to_string())], + Type::TString, + ))))), + }, + ); + + string_stdlib.insert( + "cont_chars".to_string(), + Function { + name: "cont_chars".to_string(), + kind: Some(Type::TInteger), + params: Some(vec![ + ("s".to_string(), Type::TString), + ("c".to_string(), Type::TString), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + cont_chars_impl, + vec![ + Expression::Var("s".to_string()), + Expression::Var("c".to_string()), + ], + Type::TInteger, + ))))), + }, + ); + + string_stdlib.insert( + "filter_out_char".to_string(), + Function { + name: "filter_out_char".to_string(), + kind: Some(Type::TString), + params: Some(vec![ + ("s".to_string(), Type::TString), + ("c".to_string(), Type::TString), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + filter_out_char_impl, + vec![ + Expression::Var("s".to_string()), + Expression::Var("c".to_string()), + ], + Type::TString, + ))))), + }, + ); + + string_stdlib.insert( + "replace".to_string(), + Function { + name: "replace".to_string(), + kind: Some(Type::TString), + params: Some(vec![ + ("s".to_string(), Type::TString), + ("old".to_string(), Type::TString), + ("new".to_string(), Type::TString), + ("count".to_string(), Type::TInteger), + ]), + body: Some(Box::new(Statement::Return(Box::new(Expression::MetaExp( + replace_impl, + vec![ + Expression::Var("s".to_string()), + Expression::Var("old".to_string()), + Expression::Var("new".to_string()), + Expression::Var("count".to_string()), + ], + Type::TString, + ))))), + }, + ); + + string_stdlib +} + +pub fn str_upper_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("str_upper expects exactly one argument".to_string()); + } + + if let EnvValue::Exp(Expression::CString(s)) = &args[0] { + Ok(EnvValue::Exp(Expression::CString(s.to_uppercase()))) + } else { + Err("str_upper expects a string argument".to_string()) + } +} + +pub fn str_lower_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("str_lower expects exactly one argument".to_string()); + } + if let EnvValue::Exp(Expression::CString(s)) = &args[0] { + Ok(EnvValue::Exp(Expression::CString(s.to_lowercase()))) + } else { + Err("str_lower expects a string argument".to_string()) + } +} + +pub fn str_length_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("str_length expects exactly one argument".to_string()); + } + if let EnvValue::Exp(Expression::CString(s)) = &args[0] { + Ok(EnvValue::Exp(Expression::CInt(s.chars().count() as i32))) + } else { + Err("str_length expects a string argument".to_string()) + } +} + +pub fn str_reverse_impl(args: Vec) -> Result { + if args.len() != 1 { + return Err("str_reverse expects exactly one argument".to_string()); + } + if let EnvValue::Exp(Expression::CString(s)) = &args[0] { + Ok(EnvValue::Exp(Expression::CString( + s.chars().rev().collect(), + ))) + } else { + Err("str_reverse expects a string argument".to_string()) + } +} + +pub fn cont_chars_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("cont_chars expects exactly two arguments".to_string()); + } + if let (EnvValue::Exp(Expression::CString(s)), EnvValue::Exp(Expression::CString(c))) = + (&args[0], &args[1]) + { + if c.len() != 1 { + return Err("cont_chars expects a single character as the second argument".to_string()); + } + let target = c.chars().next().unwrap(); + Ok(EnvValue::Exp(Expression::CInt( + s.chars().filter(|&ch| ch == target).count() as i32, + ))) + } else { + Err("cont_chars expects a string and a character as arguments".to_string()) + } +} + +pub fn filter_out_char_impl(args: Vec) -> Result { + if args.len() != 2 { + return Err("filter_out_char expects exactly two arguments".to_string()); + } + if let (EnvValue::Exp(Expression::CString(s)), EnvValue::Exp(Expression::CString(c))) = + (&args[0], &args[1]) + { + if c.len() != 1 { + return Err( + "filter_out_char expects a single character as the second argument".to_string(), + ); + } + let target = c.chars().next().unwrap(); + Ok(EnvValue::Exp(Expression::CString( + s.chars().filter(|&ch| ch != target).collect(), + ))) + } else { + Err("filter_out_char expects a string and a character as arguments".to_string()) + } +} + +pub fn replace_impl(args: Vec) -> Result { + if args.len() < 3 || args.len() > 4 { + return Err("replace expects between 3 and 4 arguments".to_string()); + } + if let ( + EnvValue::Exp(Expression::CString(s)), + EnvValue::Exp(Expression::CString(old)), + EnvValue::Exp(Expression::CString(new)), + ) = (&args[0], &args[1], &args[2]) + { + let count = if args.len() == 4 { + if let EnvValue::Exp(Expression::CInt(n)) = &args[3] { + *n + } else { + return Err("replace expects an integer as the fourth argument (count)".to_string()); + } + } else { + -1 + }; + + let result = if count < 0 { + s.replace(old, new) + } else { + let mut result = s.clone(); + let mut occurrences = 0; + while occurrences < count { + if let Some(pos) = result.find(old) { + result = format!("{}{}{}", &result[..pos], new, &result[pos + old.len()..]); + occurrences += 1; + } else { + break; + } + } + result + }; + + Ok(EnvValue::Exp(Expression::CString(result))) + } else { + Err("replace expects three string arguments and an optional integer".to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_str_lower_valid_strings() { + let result = str_lower_impl(vec![EnvValue::Exp(Expression::CString(String::from( + "HELLO", + )))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CString(res_value))) = result { + assert_eq!(res_value, "hello"); + } + } + + #[test] + fn test_str_length_valid_string() { + let result = str_length_impl(vec![EnvValue::Exp(Expression::CString(String::from( + "hello", + )))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(len))) = result { + assert_eq!(len, 5); + } + } + + #[test] + fn test_str_reverse_valid_string() { + let result = str_reverse_impl(vec![EnvValue::Exp(Expression::CString(String::from( + "hello", + )))]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CString(res_value))) = result { + assert_eq!(res_value, "olleh"); + } + } + + #[test] + fn test_cont_chars_valid_input() { + let result = cont_chars_impl(vec![ + EnvValue::Exp(Expression::CString(String::from("banana"))), + EnvValue::Exp(Expression::CString(String::from("a"))), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CInt(count))) = result { + assert_eq!(count, 3); + } + } + + #[test] + fn test_filter_out_char_valid_input() { + let result = filter_out_char_impl(vec![ + EnvValue::Exp(Expression::CString(String::from("banana"))), + EnvValue::Exp(Expression::CString(String::from("a"))), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CString(res_value))) = result { + assert_eq!(res_value, "bnn"); + } + } + + #[test] + fn test_replace_valid_input() { + let result = replace_impl(vec![ + EnvValue::Exp(Expression::CString(String::from("banana"))), + EnvValue::Exp(Expression::CString(String::from("a"))), + EnvValue::Exp(Expression::CString(String::from("o"))), + ]); + assert!(result.is_ok()); + if let Ok(EnvValue::Exp(Expression::CString(res_value))) = result { + assert_eq!(res_value, "bonono"); + } + } +} diff --git a/src/tc/type_checker.rs b/src/tc/type_checker.rs index c214d57..3d606dc 100644 --- a/src/tc/type_checker.rs +++ b/src/tc/type_checker.rs @@ -38,6 +38,7 @@ pub fn check_exp(exp: Expression, env: &Environment) -> Result check_unwrap_type(*e, env), Expression::Propagate(e) => check_propagate_type(*e, env), Expression::FuncCall(name, args) => check_func_call(name, args, env), + Expression::MetaExp(_, args, return_type) => check_metaexp(args, return_type, env), //_ => Err(String::from("not implemented yet")), } } @@ -364,6 +365,17 @@ fn check_isnothing_type(exp: Expression, env: &Environment) -> Result, + return_type: Type, + env: &Environment, +) -> Result { + for arg in args { + check_exp(arg.clone(), env)?; + } + Ok(return_type) +} + #[cfg(test)] mod tests { use super::*; @@ -373,6 +385,7 @@ mod tests { use crate::ir::ast::Function; use crate::ir::ast::Statement::*; use crate::ir::ast::Type::*; + use crate::stdlib::math::sqrt_impl; #[test] fn check_tlist_comparison() { @@ -1068,4 +1081,19 @@ mod tests { Err(msg) => assert_eq!(msg, "[Parameter Error] Duplicate parameter name 'x'"), } } + + #[test] + fn check_metaexp_sqrt() { + let mut env = Environment::::new(); + env.insert_variable("x".to_string(), Type::TReal); + + let meta_expr = Expression::MetaExp( + sqrt_impl, + vec![Expression::Var("x".to_string())], + Type::TReal, + ); + + let result_type = check_exp(meta_expr, &env).expect("Type checking failed"); + assert_eq!(result_type, Type::TReal); + } } diff --git a/test_results.txt b/test_results.txt index 31e41bb..caf74b6 100644 --- a/test_results.txt +++ b/test_results.txt @@ -262,7 +262,6 @@ Parsed AST: [ Final environment: {"y": (Some(Exp(CInt(1))), TInteger), "z": (Some(Exp(CInt(11))), TInteger), "x": (Some(Exp(CInt(10))), TInteger)} - === Running test: 4. Multiple assignments and references === Program: x = 42 @@ -322,10 +321,8 @@ Parsed AST: [ ] - Final environment: {"final": (Some(Exp(CInt(104))), TInteger), "w": (Some(Exp(CInt(52))), TInteger), "x": (Some(Exp(CInt(42))), TInteger), "z": (Some(Exp(CInt(52))), TInteger), "y": (Some(Exp(CInt(42))), TInteger)} - === Running test: 5. Complex arithmetic expressions === Program: a = 5 @@ -417,10 +414,8 @@ Parsed AST: [ ] - Final environment: {"a": (Some(Exp(CInt(5))), TInteger), "b": (Some(Exp(CInt(3))), TInteger), "d": (Some(Exp(CInt(14))), TInteger), "c": (Some(Exp(CInt(20))), TInteger), "e": (Some(Exp(CInt(38))), TInteger)} - === Running test: 6. Multiple nested comparisons === Program: x = 10 @@ -556,7 +551,6 @@ Parsed AST: [ Final environment: {"y": (Some(Exp(CInt(5))), TInteger), "x": (Some(Exp(CInt(10))), TInteger), "z": (Some(Exp(CInt(1))), TInteger)} - === Running test: 7. Mixed arithmetic and control flow === Program: a = 15 @@ -719,10 +713,8 @@ Parsed AST: [ ] - Final environment: {"b": (Some(Exp(CInt(3))), TInteger), "a": (Some(Exp(CInt(15))), TInteger), "c": (Some(Exp(CInt(18))), TInteger), "d": (Some(Exp(CInt(36))), TInteger), "e": (Some(Exp(CInt(26))), TInteger)} - === Running test: 8. Basic function definition and call === Program: def add(a: TInteger, b: TInteger) -> TInteger: @@ -801,10 +793,8 @@ Parsed AST: [ ] - Final environment: {"x": (Some(Exp(CInt(5))), TInteger), "add": (Some(Func(Function { kind: TInteger, params: Some([("a", TInteger), ("b", TInteger)]), body: Block([Return(Add(Var("a"), Var("b")))]) })), TInteger), "y": (Some(Exp(CInt(3))), TInteger), "result": (Some(Exp(CInt(8))), TInteger)} - === Running test: 9. Recursive function === Program: def fibonacci(n: TInteger) -> TInteger: @@ -1170,10 +1160,8 @@ Parsed AST: [ ] - Final environment: {"h": (Some(Exp(CInt(45))), TInteger), "b": (Some(Exp(CInt(-4))), TInteger), "c": (Some(Exp(CInt(24))), TInteger), "d": (Some(Exp(CInt(1))), TInteger), "e": (Some(Exp(CInt(7))), TInteger), "a": (Some(Exp(CInt(6))), TInteger), "f": (Some(Exp(CInt(9))), TInteger), "g": (Some(Exp(CInt(26))), TInteger)} - === Running test: 12. Complex expression chains === Program: x = 1 @@ -1458,10 +1446,8 @@ Parsed AST: [ ] - Final environment: {"result": (Some(Exp(CInt(-30))), TInteger), "b": (Some(Exp(CInt(-10))), TInteger), "a": (Some(Exp(CInt(-5))), TInteger), "c": (Some(Exp(CInt(-15))), TInteger)} - === Running test: 16. Mixed positive and negative operations === Program: x = 10 @@ -1535,10 +1521,8 @@ Parsed AST: [ ] - Final environment: {"w": (Some(Exp(CInt(-4))), TInteger), "z": (Some(Exp(CInt(16))), TInteger), "x": (Some(Exp(CInt(10))), TInteger), "y": (Some(Exp(CInt(-3))), TInteger)} - === Running test: 17. Complex expressions with negatives === Program: a = -2 @@ -1622,10 +1606,8 @@ Parsed AST: [ ] - Final environment: {"a": (Some(Exp(CInt(-2))), TInteger), "c": (Some(Exp(CInt(26))), TInteger), "d": (Some(Exp(CInt(-5))), TInteger), "b": (Some(Exp(CInt(3))), TInteger)} - === Running test: 18. Function calls with negative numbers === Program: def subtract(a: TInteger, b: TInteger) -> TInteger: @@ -1716,10 +1698,8 @@ Parsed AST: [ ] - Final environment: {"subtract": (Some(Func(Function { kind: TInteger, params: Some([("a", TInteger), ("b", TInteger)]), body: Block([Return(Sub(Var("a"), Var("b")))]) })), TInteger), "result1": (Some(Exp(CInt(-7))), TInteger), "result2": (Some(Exp(CInt(8))), TInteger), "result3": (Some(Exp(CInt(-8))), TInteger)} - === Running test: 19. Boolean Operations === Program: a = True @@ -1783,10 +1763,8 @@ Parsed AST: [ ] - Final environment: {"c": (Some(Exp(CFalse)), TInteger), "f": (Some(Exp(CTrue)), TInteger), "d": (Some(Exp(CTrue)), TInteger), "b": (Some(Exp(CFalse)), TInteger), "e": (Some(Exp(CTrue)), TInteger), "a": (Some(Exp(CTrue)), TInteger)} - === Running test: 20. Real Number Operations === Program: x = 3.14 @@ -1853,10 +1831,8 @@ Parsed AST: [ ] - Final environment: {"w": (Some(Exp(CReal(6.28))), TInteger), "z": (Some(Exp(CReal(0.6400000000000001))), TInteger), "v": (Some(Exp(CReal(5.0))), TInteger), "y": (Some(Exp(CReal(-2.5))), TInteger), "x": (Some(Exp(CReal(3.14))), TInteger)} - === Running test: 21. String Operations === Program: name = "Hello" @@ -1894,10 +1870,8 @@ Parsed AST: [ ] - Final environment: {"name": (Some(Exp(CString("Hello"))), TInteger), "greeting": (Some(Exp(CString("World"))), TInteger), "message": (Some(Exp(CString("Test "))), TInteger)} - === Running test: 22. Mixed Boolean Operations === Program: x = 10 @@ -1990,6 +1964,4 @@ Parsed AST: [ ] - Final environment: {"result2": (Some(Exp(CTrue)), TInteger), "result1": (Some(Exp(CTrue)), TInteger), "result3": (Some(Exp(CTrue)), TInteger), "y": (Some(Exp(CInt(5))), TInteger), "x": (Some(Exp(CInt(10))), TInteger), "result4": (Some(Exp(CTrue)), TInteger)} -