diff --git a/compiler/src/tests/compiler_test.rs b/compiler/src/tests/compiler_test.rs index 39516c5..481563c 100644 --- a/compiler/src/tests/compiler_test.rs +++ b/compiler/src/tests/compiler_test.rs @@ -1,26 +1,16 @@ -use core::panic; - -use crate::{bin_create_dir, builder::build, errors::CompilerError, parser}; +use crate::{errors::CompilerError, parser}; use transpiler::TranspileContext; #[test] fn compiler_variable_test() -> Result<(), CompilerError> { - const PATH: &str = "../examples/float.az"; - let sdk = file_system::read_file(PATH)?; - - let parsed_program = parser(sdk)?; + let parsed_program = parser("sabit kəsr a = 5.1\na".to_string())?; let validator = validator::Validator::default(); - let (context, program) = validator.validate(parsed_program)?; - - let output_zig = bin_create_dir()?; - - let mut ctx = transpiler::TranspileContext::default(); + let (_, program) = validator.validate(parsed_program)?; + let mut ctx = TranspileContext::default(); let code = ctx.transpile(program); - file_system::write_file(&output_zig.join("./src/main.zig"), code)?; - - build(output_zig)?; + assert!(code.contains("const a: f64 = 5.1;")); Ok(()) } diff --git a/interpreter/src/runner/binary_op.rs b/interpreter/src/runner/binary_op.rs index 00710b4..7df2441 100644 --- a/interpreter/src/runner/binary_op.rs +++ b/interpreter/src/runner/binary_op.rs @@ -1,7 +1,45 @@ +use std::cmp::Ordering; + use parser::{ast::Operation, shared_ast::Type}; use crate::runner::{Runner, runner::Value}; -//TODO: Burası güncellene bilinir + +fn value_ordering(left: &Value, right: &Value) -> Option { + match (left, right) { + (Value::Number(left), Value::Number(right)) => left.partial_cmp(right), + (Value::Number(left), Value::Float(right)) => (*left as f64).partial_cmp(right), + (Value::Float(left), Value::Number(right)) => left.partial_cmp(&(*right as f64)), + (Value::Float(left), Value::Float(right)) => left.partial_cmp(right), + (Value::String(left), Value::String(right)) => Some(left.cmp(right)), + (Value::Bool(left), Value::Bool(right)) => Some(left.cmp(right)), + (Value::Char(left), Value::Char(right)) => Some(left.cmp(right)), + (Value::List(left), Value::List(right)) => { + for (left, right) in left.iter().zip(right) { + let ordering = value_ordering(left, right)?; + if ordering != Ordering::Equal { + return Some(ordering); + } + } + Some(left.len().cmp(&right.len())) + } + (Value::Void, Value::Void) => Some(Ordering::Equal), + _ => None, + } +} + +fn comparison_result(left: Value, right: Value, op: Operation) -> Value { + let ordering = value_ordering(&left, &right) + .unwrap_or_else(|| panic!("Invalid operands for {op:?}: {left:?} and {right:?}")); + let result = match op { + Operation::Less => ordering == Ordering::Less, + Operation::LessEqual => ordering != Ordering::Greater, + Operation::Greater => ordering == Ordering::Greater, + Operation::GreaterEqual => ordering != Ordering::Less, + _ => unreachable!(), + }; + Value::Bool(result) +} + pub fn binary_op_runner( _ctx: &mut Runner, left: Value, @@ -10,7 +48,10 @@ pub fn binary_op_runner( cast_type: Option, ) -> Value { match op { - Operation::Not => Value::Bool(false), + Operation::Not => match right { + Value::Bool(value) => Value::Bool(!value), + other => panic!("Invalid operand for Not: {other:?}"), + }, Operation::Add => { if let Some(Type::Integer) = cast_type { let left = left.as_number(); @@ -69,39 +110,18 @@ pub fn binary_op_runner( Value::Float(left % right) } } - Operation::Equal => Value::Bool(left == right), - Operation::NotEqual => Value::Bool(left != right), - Operation::Less => match (left, right) { - (Value::Number(a), Value::Number(b)) => Value::Bool(a < b), - (Value::Float(a), Value::Float(b)) => Value::Bool(a < b), - (Value::String(a), Value::String(b)) => Value::Bool(a < b), - _ => Value::Bool(false), - }, - Operation::LessEqual => match (left, right) { - (Value::Number(a), Value::Number(b)) => Value::Bool(a <= b), - (Value::Float(a), Value::Float(b)) => Value::Bool(a <= b), - (Value::String(a), Value::String(b)) => Value::Bool(a <= b), - _ => Value::Bool(false), - }, - Operation::Greater => match (left, right) { - (Value::Number(a), Value::Number(b)) => Value::Bool(a > b), - (Value::Float(a), Value::Float(b)) => Value::Bool(a > b), - (Value::String(a), Value::String(b)) => Value::Bool(a > b), - _ => Value::Bool(false), - }, - Operation::GreaterEqual => match (left, right) { - (Value::Number(a), Value::Number(b)) => Value::Bool(a >= b), - (Value::Float(a), Value::Float(b)) => Value::Bool(a >= b), - (Value::String(a), Value::String(b)) => Value::Bool(a >= b), - _ => Value::Bool(false), - }, - Operation::And => match (left, right) { - (Value::Bool(a), Value::Bool(b)) => Value::Bool(a && b), - _ => Value::Bool(false), + Operation::Equal => Value::Bool(value_ordering(&left, &right) == Some(Ordering::Equal)), + Operation::NotEqual => Value::Bool(value_ordering(&left, &right) != Some(Ordering::Equal)), + Operation::Less | Operation::LessEqual | Operation::Greater | Operation::GreaterEqual => { + comparison_result(left, right, op) + } + Operation::And => match (&left, &right) { + (Value::Bool(left), Value::Bool(right)) => Value::Bool(*left && *right), + _ => panic!("Invalid operands for And: {left:?} and {right:?}"), }, - Operation::Or => match (left, right) { - (Value::Bool(a), Value::Bool(b)) => Value::Bool(a || b), - _ => Value::Bool(false), + Operation::Or => match (&left, &right) { + (Value::Bool(left), Value::Bool(right)) => Value::Bool(*left || *right), + _ => panic!("Invalid operands for Or: {left:?} and {right:?}"), }, } } diff --git a/interpreter/src/runner/runner.rs b/interpreter/src/runner/runner.rs index 7fe7b70..7394799 100644 --- a/interpreter/src/runner/runner.rs +++ b/interpreter/src/runner/runner.rs @@ -57,6 +57,7 @@ impl Value { pub fn as_float(&self) -> f64 { match self { Value::Float(f) => *f, + Value::Number(n) => *n as f64, _ => 0.0, } } diff --git a/interpreter/src/runner/tests/binary_op.rs b/interpreter/src/runner/tests/binary_op.rs index de54bd8..603f8c5 100644 --- a/interpreter/src/runner/tests/binary_op.rs +++ b/interpreter/src/runner/tests/binary_op.rs @@ -7,6 +7,10 @@ mod tests { use parser::ast::Operation; use parser::shared_ast::Type; + fn run_operation(left: Value, right: Value, op: Operation, cast_type: Option) -> Value { + binary_op_runner(&mut Runner::new(), left, right, op, cast_type) + } + #[test] fn binary_op_add_integer() { let mut runner = Runner::new(); @@ -151,16 +155,9 @@ mod tests { } #[test] - fn binary_op_default_case() { - let mut runner = Runner::new(); - let result = binary_op_runner( - &mut runner, - Value::Number(1), - Value::Number(2), - Operation::And, - None, - ); - assert_eq!(result, Value::Bool(false)); + #[should_panic(expected = "Invalid operands for And")] + fn binary_op_rejects_invalid_logical_operands() { + run_operation(Value::Number(1), Value::Number(2), Operation::And, None); } #[test] @@ -279,4 +276,117 @@ mod tests { ); assert_eq!(result, Value::String("test".to_string())); } + + #[test] + fn unary_not_negates_booleans() { + assert_eq!( + run_operation(Value::Void, Value::Bool(true), Operation::Not, None), + Value::Bool(false) + ); + assert_eq!( + run_operation(Value::Void, Value::Bool(false), Operation::Not, None), + Value::Bool(true) + ); + } + + #[test] + #[should_panic(expected = "Invalid operand for Not")] + fn unary_not_rejects_non_boolean_values() { + run_operation(Value::Void, Value::Number(1), Operation::Not, None); + } + + #[test] + fn equality_supports_runtime_values() { + let values = [ + Value::Float(1.5), + Value::String("azlang".to_string()), + Value::Bool(true), + Value::Char('a'), + Value::List(vec![Value::Number(1), Value::Number(2)]), + ]; + + for value in values { + assert_eq!( + run_operation(value.clone(), value, Operation::Equal, None), + Value::Bool(true) + ); + } + } + + #[test] + fn equality_compares_integer_and_float_values() { + assert_eq!( + run_operation(Value::Number(2), Value::Float(2.0), Operation::Equal, None,), + Value::Bool(true) + ); + assert_eq!( + run_operation( + Value::Float(2.0), + Value::Number(3), + Operation::NotEqual, + None, + ), + Value::Bool(true) + ); + } + + #[test] + fn comparisons_follow_value_ordering() { + let cases = [ + (Value::Number(2), Value::Float(2.5), Operation::Less), + (Value::Float(3.0), Value::Number(3), Operation::GreaterEqual), + ( + Value::String("b".to_string()), + Value::String("a".to_string()), + Operation::Greater, + ), + (Value::Bool(false), Value::Bool(true), Operation::Less), + (Value::Char('a'), Value::Char('b'), Operation::LessEqual), + ( + Value::List(vec![Value::Number(1), Value::Number(2)]), + Value::List(vec![Value::Number(1), Value::Number(3)]), + Operation::Less, + ), + ]; + + for (left, right, op) in cases { + assert_eq!(run_operation(left, right, op, None), Value::Bool(true)); + } + } + + #[test] + #[should_panic(expected = "Invalid operands for Less")] + fn comparisons_reject_incompatible_values() { + run_operation( + Value::Number(1), + Value::String("1".to_string()), + Operation::Less, + None, + ); + } + + #[test] + fn logical_operations_use_boolean_values() { + assert_eq!( + run_operation(Value::Bool(true), Value::Bool(false), Operation::And, None,), + Value::Bool(false) + ); + assert_eq!( + run_operation(Value::Bool(true), Value::Bool(false), Operation::Or, None,), + Value::Bool(true) + ); + } + + #[test] + fn mixed_numeric_arithmetic_uses_float_promotion() { + assert_eq!( + run_operation( + Value::Number(2), + Value::Float(0.5), + Operation::Add, + Some(Type::Float), + ), + Value::Float(2.5) + ); + } }