diff --git a/examples/example.lox b/examples/example.lox index 828bb0c..b9a3c8b 100644 --- a/examples/example.lox +++ b/examples/example.lox @@ -12,7 +12,12 @@ func_name :: fn(param: Int, param2: String) do cavallo = cavallo + 1; print cavallo; end - + if True then do + print "this shoul be stop"; + return False; + end + print "this shoud be un other stop"; + return 42; for i := 11; i <= 21; i = i + 1; do print i; end diff --git a/src/backend/environment.rs b/src/backend/environment.rs index 91681f2..97166ec 100644 --- a/src/backend/environment.rs +++ b/src/backend/environment.rs @@ -1,13 +1,16 @@ use std::collections::HashMap; use crate::{ - frontend::{source_registry::SourceSlice, tokens::LiteralValue}, - result::{runtime_error, LoxResult}, + common::{ + base_value::BaseValue, + lox_result::{runtime_error, LoxResult}, + }, + frontend::source_registry::SourceSlice, }; #[derive(Debug, Clone, PartialEq)] pub struct EnvironmentStack { - stack: Vec>, + stack: Vec>, } impl EnvironmentStack { @@ -25,7 +28,7 @@ impl EnvironmentStack { self.stack.pop(); } - pub fn get(&self, name: &str) -> LoxResult { + pub fn get(&self, name: &str) -> LoxResult { let size = self.stack.len(); for i in (0..size).rev() { @@ -39,12 +42,12 @@ impl EnvironmentStack { ) } - pub fn declare(&mut self, name: String, value: LiteralValue) -> LoxResult { + pub fn declare(&mut self, name: String, value: BaseValue) -> LoxResult { self.stack.last_mut().unwrap().insert(name, value.clone()); Ok(value) } - pub fn set(&mut self, name: String, value: LiteralValue) -> LoxResult { + pub fn set(&mut self, name: String, value: BaseValue) -> LoxResult { let size = self.stack.len(); for i in (0..size).rev() { diff --git a/src/backend/interpreter.rs b/src/backend/interpreter.rs index a361a6a..235467f 100644 --- a/src/backend/interpreter.rs +++ b/src/backend/interpreter.rs @@ -1,201 +1,37 @@ use crate::{ backend::environment::EnvironmentStack, - frontend::{ + common::{ ast::{AstNode, AstNodeKind, Expr, Stmt}, - source_registry::SourceSlice, - tokens::{LiteralValue, NativeFunction, TokenType}, + base_value::{BaseValue, NativeFunction, Number, Truthy}, + lox_result::{runtime_error, LoxError, LoxResult}, }, - logging::display_ast::PrettyPrintExt, - result::{runtime_error, LoxError, LoxResult}, + frontend::{source_registry::SourceSlice, tokens::TokenType}, }; -use std::{ - fmt::{Debug, Display}, - ops::{Add, Div, Mul, Neg, Not, Rem, Sub}, -}; - -fn error(message: String) -> LoxError { - LoxError::RuntimeError { - source_slice: SourceSlice::default(), // todo change this with the actual source slice - message: message, - } -} - -fn error_at(source_slice: SourceSlice, message: String) -> LoxError { - LoxError::RuntimeError { - source_slice, - message, - } -} - -impl Not for LiteralValue { - type Output = LiteralValue; - - fn not(self) -> Self::Output { - match self { - LiteralValue::Boolean(b) => LiteralValue::Boolean(!b), - LiteralValue::Number(n) => LiteralValue::Boolean(n == 0.0), - _ => LiteralValue::Boolean(false), - } - } -} - -pub trait Truthy { - fn is_truthy(&self) -> bool; -} - -impl Truthy for LiteralValue { - fn is_truthy(&self) -> bool { - match self { - LiteralValue::Boolean(b) => *b, - LiteralValue::Number(n) => *n != 0.0, - LiteralValue::String(s) => !s.is_empty(), - _ => false, - } - } -} - -impl Neg for LiteralValue { - type Output = LoxResult; - - fn neg(self) -> Self::Output { - Ok(LiteralValue::Boolean(!self.is_truthy())) - } -} - -impl Add for LiteralValue { - type Output = LoxResult; - - fn add(self, other: LiteralValue) -> Self::Output { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a + b)), - (LiteralValue::String(a), LiteralValue::String(b)) => { - Ok(LiteralValue::String(format!("{}{}", a, b))) - } - _ => Err(error("Cannot add non-numeric values".to_string())), - } - } -} - -impl LiteralValue { - pub fn add_with_source( - self, - other: LiteralValue, - source_slice: SourceSlice, - ) -> LoxResult { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a + b)), - (LiteralValue::String(a), LiteralValue::String(b)) => { - Ok(LiteralValue::String(format!("{}{}", a, b))) - } - _ => Err(error_at( - source_slice, - "Cannot add non-numeric values".to_string(), - )), - } - } -} - -impl Sub for LiteralValue { - type Output = LoxResult; - - fn sub(self, other: LiteralValue) -> Self::Output { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a - b)), - _ => Err(error("Cannot subtract non-numeric values".to_string())), - } - } -} - -impl Div for LiteralValue { - type Output = LoxResult; - - fn div(self, other: LiteralValue) -> Self::Output { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => { - if b == 0.0 { - Err(error("Division by zero".to_string())) - } else { - Ok(LiteralValue::Number(a / b)) - } - } - _ => Err(error("Cannot divide non-numeric values".to_string())), - } - } -} - -impl Mul for LiteralValue { - type Output = LoxResult; - - fn mul(self, other: LiteralValue) -> Self::Output { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a * b)), - _ => Err(error("Cannot multiply non-numeric values".to_string())), - } - } -} - -impl Rem for LiteralValue { - type Output = LoxResult; - - fn rem(self, other: LiteralValue) -> Self::Output { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => { - if b == 0.0 { - Err(error("Division by zero".to_string())) - } else { - Ok(LiteralValue::Number(a % b)) - } - } - _ => Err(error("Cannot divide non-numeric values".to_string())), - } - } -} - -impl PartialOrd for LiteralValue { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (LiteralValue::Number(a), LiteralValue::Number(b)) => a.partial_cmp(b), - _ => None, - } - } -} - -impl From for bool { - fn from(value: LiteralValue) -> Self { - match value { - LiteralValue::Boolean(b) => b, - LiteralValue::Number(n) => n != 0.0, - _ => false, - } - } -} +use std::fmt::{Debug, Display}; pub struct Interpreter { enviorment: EnvironmentStack, } pub trait EvaluateInterpreter { - fn evaluate(&mut self, stmt: T) -> LoxResult; + fn evaluate(&mut self, stmt: T) -> LoxResult; } impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter> for Interpreter where Interpreter: EvaluateInterpreter, { - fn evaluate(&mut self, stmt: AstNode) -> LoxResult { + fn evaluate(&mut self, stmt: AstNode) -> LoxResult { match self.evaluate(stmt.node.clone()) { Ok(value) => Ok(value), - Err(err) => Err(LoxError::RuntimeError { - source_slice: stmt.source_slice, - message: err.get_message(), - }), + Err(err) => runtime_error(stmt.source_slice, err.get_message()), } } } // Direct Expr evaluation to avoid infinite recursion impl EvaluateInterpreter for Interpreter { - fn evaluate(&mut self, expr: Expr) -> LoxResult { + fn evaluate(&mut self, expr: Expr) -> LoxResult { match expr { Expr::Literal { value } => Ok(value), Expr::Identifier { name } => self.enviorment.get(&name), @@ -219,7 +55,7 @@ impl EvaluateInterpreter for Interpreter { } impl EvaluateInterpreter> for Interpreter { - fn evaluate(&mut self, node: AstNode) -> LoxResult { + fn evaluate(&mut self, node: AstNode) -> LoxResult { let stmt = node.node; let result = self.interpret_stmt_inner(stmt); match result { @@ -240,13 +76,13 @@ impl Interpreter { let mut env = EnvironmentStack::new(); let _ = env.declare( "clock".to_string(), - LiteralValue::NativeFunction(NativeFunction::new(0, |_args| { - LiteralValue::Number( + BaseValue::NativeFunction(NativeFunction::new(Vec::default(), |_args| { + BaseValue::Number(Number::U128( std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() - .as_millis() as f64, - ) + .as_millis(), + )) })), ); Self { enviorment: env } @@ -255,7 +91,7 @@ impl Interpreter { &mut self, callee: Box>, arguments: Vec>, - ) -> LoxResult { + ) -> LoxResult { let source_slice = callee.source_slice.clone(); // Estrai il nome della variabile se il callee è un identificatore @@ -279,11 +115,11 @@ impl Interpreter { let evaluated_arguments = arguments .iter() .map(|arg| self.evaluate(arg.clone())) - .collect::, LoxError>>()?; + .collect::, LoxError>>()?; match function { - LiteralValue::NativeFunction(func) => Ok((func.function)(&evaluated_arguments)), - LiteralValue::Function(func) => { + BaseValue::NativeFunction(func) => Ok((func.function)(&evaluated_arguments)), + BaseValue::Function(func) => { func.parameters .iter() .enumerate() @@ -291,7 +127,7 @@ impl Interpreter { let value = evaluated_arguments.get(index).unwrap(); self.enviorment.declare(par.0.clone(), value.clone()) }) - .collect::>>()?; + .collect::>>()?; self.evaluate(func.body) } _ => runtime_error( @@ -303,49 +139,54 @@ impl Interpreter { fn evaluate_binary( &mut self, - left: LiteralValue, + left: BaseValue, operator: TokenType, - right: LiteralValue, - ) -> LoxResult { + right: BaseValue, + ) -> LoxResult { match operator { TokenType::Plus => left + right, TokenType::Minus => left - right, TokenType::Star => left * right, TokenType::Slash => left / right, - TokenType::Greater => Ok(LiteralValue::Boolean(left > right)), - TokenType::GreaterEqual => Ok(LiteralValue::Boolean(left >= right)), - TokenType::Less => Ok(LiteralValue::Boolean(left < right)), - TokenType::LessEqual => Ok(LiteralValue::Boolean(left <= right)), - TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)), - TokenType::BangEqual => Ok(LiteralValue::Boolean(left != right)), - TokenType::And => Ok(LiteralValue::Boolean(left.is_truthy() && right.is_truthy())), - TokenType::Or => Ok(LiteralValue::Boolean(left.is_truthy() || right.is_truthy())), - _ => Err(error(format!( - "Unsupported binary operator: {:?}", - operator - ))), + TokenType::Greater => Ok(BaseValue::Boolean(left > right)), + TokenType::GreaterEqual => Ok(BaseValue::Boolean(left >= right)), + TokenType::Less => Ok(BaseValue::Boolean(left < right)), + TokenType::LessEqual => Ok(BaseValue::Boolean(left <= right)), + TokenType::EqualEqual => Ok(BaseValue::Boolean(left == right)), + TokenType::BangEqual => Ok(BaseValue::Boolean(left != right)), + TokenType::And => Ok(BaseValue::Boolean(left.is_truthy() && right.is_truthy())), + TokenType::Or => Ok(BaseValue::Boolean(left.is_truthy() || right.is_truthy())), + _ => Err(LoxError::RuntimeError { + source_slice: SourceSlice::default(), + message: format!("Unsupported binary operator: {:?}", operator), + }), } } - fn evaluate_unary( - &mut self, - operator: TokenType, - operand: LiteralValue, - ) -> LoxResult { + fn evaluate_unary(&mut self, operator: TokenType, operand: BaseValue) -> LoxResult { match operator { - TokenType::Minus => -operand, + TokenType::Minus => match operand { + BaseValue::Number(n) => Ok(BaseValue::Number(n.neg())), + _ => Err(LoxError::RuntimeError { + source_slice: SourceSlice::default(), + message: "Cannot negate non-numeric value".to_string(), + }), + }, TokenType::Bang => Ok(!operand), - _ => Err(error(format!("Unsupported unary operator: {:?}", operator))), + _ => Err(LoxError::RuntimeError { + source_slice: SourceSlice::default(), + message: format!("Unsupported unary operator: {:?}", operator), + }), } } - fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult { + fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult { match stmt { Stmt::Expression { expression } => self.evaluate(*expression), Stmt::Print { expression } => { let value = self.evaluate(*expression)?; - println!("print interpreter: \t{}", value); - Ok(LiteralValue::Nil) + println!("{}", value); + Ok(BaseValue::Nil) } Stmt::Block { statements } => self.evaluate_block(*statements), Stmt::Stmt { expression } => self.evaluate(*expression.clone()), @@ -362,7 +203,7 @@ impl Interpreter { } _ => self.evaluate(*expr_node)?, }, - None => LiteralValue::Nil, + None => BaseValue::Nil, }; self.enviorment.declare(name.clone(), value) } @@ -381,7 +222,7 @@ impl Interpreter { while self.evaluate(*condition.clone())?.is_truthy() { self.evaluate(*body.clone())?; } - Ok(LiteralValue::Nil) + Ok(BaseValue::Nil) } Stmt::For { variable, @@ -392,10 +233,10 @@ impl Interpreter { self.enviorment.push_new_scope(); let source_slice = variable.source_slice.clone(); let val = self.evaluate(*variable)?; - if !matches!(val, LiteralValue::Number(..)) { + if !matches!(val, BaseValue::Number(..)) { return runtime_error(source_slice, "Expected number literal"); } - let mut ret = LiteralValue::Nil; + let mut ret = BaseValue::Nil; while self.evaluate(*condition.clone())?.is_truthy() { ret = self.evaluate(*body.clone())?; self.evaluate(*increment.clone())?; @@ -411,18 +252,18 @@ impl Interpreter { then_branch: Box>, elif_branch: Vec<(Box>, Box>)>, else_branch: Option>>, - ) -> LoxResult { + ) -> LoxResult { let condition = self.evaluate(*condition)?; match condition { - LiteralValue::Boolean(true) => self.evaluate(*then_branch), - LiteralValue::Boolean(false) => { + BaseValue::Boolean(true) => self.evaluate(*then_branch), + BaseValue::Boolean(false) => { for (elif_condition, elif_then_branch) in elif_branch { let condition = self.evaluate(*elif_condition)?; match condition { - LiteralValue::Boolean(true) => { + BaseValue::Boolean(true) => { return self.evaluate(*elif_then_branch); } - LiteralValue::Boolean(false) => continue, + BaseValue::Boolean(false) => continue, _ => { return Err(LoxError::TypeMismatch { source_slice: SourceSlice::default(), // todo change this to the actual source slice @@ -435,7 +276,7 @@ impl Interpreter { if let Some(else_block) = else_branch { self.evaluate(*else_block) } else { - Ok(LiteralValue::Nil) + Ok(BaseValue::Nil) } } _ => Err(LoxError::TypeMismatch { @@ -446,7 +287,7 @@ impl Interpreter { } } - fn evaluate_block(&mut self, statements: Vec>) -> LoxResult { + fn evaluate_block(&mut self, statements: Vec>) -> LoxResult { self.enviorment.push_new_scope(); let (elements, final_expr) = match statements.split_last() { Some((stmt, body)) => match &stmt.node { @@ -474,7 +315,7 @@ impl Interpreter { } None => { self.enviorment.pop_scope(); - Ok(LiteralValue::Nil) + Ok(BaseValue::Nil) } } } diff --git a/src/frontend/ast.rs b/src/common/ast.rs similarity index 97% rename from src/frontend/ast.rs rename to src/common/ast.rs index 9131315..0127b79 100644 --- a/src/frontend/ast.rs +++ b/src/common/ast.rs @@ -1,6 +1,6 @@ -use crate::frontend::{ - source_registry::SourceSlice, - tokens::{LiteralValue, TokenType}, +use crate::{ + common::base_value::BaseValue, + frontend::{source_registry::SourceSlice, tokens::TokenType}, }; use std::fmt::{Debug, Display}; /* @@ -15,7 +15,7 @@ use std::fmt::{Debug, Display}; * * expression_statement -> expression ";" * print_statement -> "print" expression ";" - * var_statement -> ("var")? IDENTIFIER (":" IDENTIFIER)? (("=" expression)? ";")| function_declaration + * var_statement -> ("var"|"dyn"|"mut")? IDENTIFIER (":" IDENTIFIER)? (("=" expression)? ";")| function_declaration * function_declaration -> "::" "(" parameters? ")" ("->" IDENTIFIER)? block_statement * parameters -> ( IDENTIFIER (":" IDENTIFIER)? ("," IDENTIFIER (":" IDENTIFIER)? )* ) * assignment_statement -> IDENTIFIER "=" expression ";" @@ -26,7 +26,8 @@ use std::fmt::{Debug, Display}; * expression -> assignment * assignment -> IDENTIFIER "=" assignment | logical_or * logical_or -> logical_and (("or" logical_and)* - * logical_and -> equality (("and" equality)* + * logical_and -> logical_is (("and" logical_is)* + * logical_is -> equality (("is" equality)* * equality -> comparison (("==" | "!=") comparison)* * comparison -> term ((">" | ">=" | "<" | "<=") term)* * term -> factor (("+" | "-") factor)* @@ -39,7 +40,7 @@ use std::fmt::{Debug, Display}; #[derive(Clone, PartialEq)] pub enum Expr { Literal { - value: LiteralValue, + value: BaseValue, }, Binary { left: Box>, diff --git a/src/common/base_value.rs b/src/common/base_value.rs new file mode 100644 index 0000000..9ae8024 --- /dev/null +++ b/src/common/base_value.rs @@ -0,0 +1,488 @@ +use core::fmt; +use std::fmt::Display; +use std::ops::{Add, Div, Mul, Not, Rem, Sub}; + +use crate::common::lox_result::{runtime_error, LoxResult}; +use crate::{ + backend::environment::EnvironmentStack, + common::ast::{AstNode, Expr, Stmt}, + frontend::source_registry::SourceSlice, +}; +#[derive(Debug, Clone, PartialEq)] +pub enum Number { + I16(i16), + I32(i32), + I64(i64), + F32(f32), + F64(f64), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), +} + +impl Display for Number { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Number::I16(n) => write!(f, "{}", n), + Number::I32(n) => write!(f, "{}", n), + Number::I64(n) => write!(f, "{}", n), + Number::F32(n) => write!(f, "{}", n), + Number::F64(n) => write!(f, "{}", n), + Number::U8(n) => write!(f, "{}", n), + Number::U16(n) => write!(f, "{}", n), + Number::U32(n) => write!(f, "{}", n), + Number::U64(n) => write!(f, "{}", n), + Number::U128(n) => write!(f, "{}", n), + } + } +} + +impl Number { + /// Promotes two numbers to their common type following the hierarchy: + /// Unsigned -> Integer -> Float + fn promote_to_common_type(a: Number, b: Number) -> (Number, Number) { + use Number::*; + match (&a, &b) { + // If either is a float, both become F64 + (F32(_), _) | (F64(_), _) => { + let a_as_f64 = match a { + I16(n) => n as f64, + I32(n) => n as f64, + I64(n) => n as f64, + F32(n) => n as f64, + F64(n) => n, + U8(n) => n as f64, + U16(n) => n as f64, + U32(n) => n as f64, + U64(n) => n as f64, + U128(n) => n as f64, + }; + let b_as_f64 = match b { + I16(n) => n as f64, + I32(n) => n as f64, + I64(n) => n as f64, + F32(n) => n as f64, + F64(n) => n, + U8(n) => n as f64, + U16(n) => n as f64, + U32(n) => n as f64, + U64(n) => n as f64, + U128(n) => n as f64, + }; + (F64(a_as_f64), F64(b_as_f64)) + } + (_, F32(_)) | (_, F64(_)) => { + // Swap and recurse to handle the float case + let (b_promoted, a_promoted) = Self::promote_to_common_type(b.clone(), a.clone()); + (a_promoted, b_promoted) + } + // If either is signed integer, both become I64 + (I16(_), _) | (I32(_), _) | (I64(_), _) => { + let a_as_i64 = match a { + I16(n) => n as i64, + I32(n) => n as i64, + I64(n) => n, + U8(n) => n as i64, + U16(n) => n as i64, + U32(n) => n as i64, + U64(n) => n as i64, + U128(n) => n as i64, // May overflow but keeping it simple + _ => unreachable!(), + }; + let b_as_i64 = match b { + I16(n) => n as i64, + I32(n) => n as i64, + I64(n) => n, + U8(n) => n as i64, + U16(n) => n as i64, + U32(n) => n as i64, + U64(n) => n as i64, + U128(n) => n as i64, // May overflow but keeping it simple + _ => unreachable!(), + }; + (I64(a_as_i64), I64(b_as_i64)) + } + (_, I16(_)) | (_, I32(_)) | (_, I64(_)) => { + // Swap and recurse to handle the signed case + let (b_promoted, a_promoted) = Self::promote_to_common_type(b.clone(), a.clone()); + (a_promoted, b_promoted) + } + // Both are unsigned, promote to U128 + _ => { + let a_as_u128 = match a { + U8(n) => n as u128, + U16(n) => n as u128, + U32(n) => n as u128, + U64(n) => n as u128, + U128(n) => n, + _ => unreachable!(), + }; + let b_as_u128 = match b { + U8(n) => n as u128, + U16(n) => n as u128, + U32(n) => n as u128, + U64(n) => n as u128, + U128(n) => n, + _ => unreachable!(), + }; + (U128(a_as_u128), U128(b_as_u128)) + } + } + } + + pub fn add(self, other: Number) -> Number { + let (a, b) = Self::promote_to_common_type(self, other); + match (a, b) { + (Number::F64(x), Number::F64(y)) => Number::F64(x + y), + (Number::I64(x), Number::I64(y)) => Number::I64(x + y), + (Number::U128(x), Number::U128(y)) => Number::U128(x + y), + _ => unreachable!("promote_to_common_type should handle all cases"), + } + } + + pub fn sub(self, other: Number) -> Number { + let (a, b) = Self::promote_to_common_type(self, other); + match (a, b) { + (Number::F64(x), Number::F64(y)) => Number::F64(x - y), + (Number::I64(x), Number::I64(y)) => Number::I64(x - y), + (Number::U128(x), Number::U128(y)) => Number::U128(x - y), + _ => unreachable!("promote_to_common_type should handle all cases"), + } + } + + pub fn mul(self, other: Number) -> Number { + let (a, b) = Self::promote_to_common_type(self, other); + match (a, b) { + (Number::F64(x), Number::F64(y)) => Number::F64(x * y), + (Number::I64(x), Number::I64(y)) => Number::I64(x * y), + (Number::U128(x), Number::U128(y)) => Number::U128(x * y), + _ => unreachable!("promote_to_common_type should handle all cases"), + } + } + + pub fn div(self, other: Number) -> Option { + let (a, b) = Self::promote_to_common_type(self, other); + match (a, b) { + (Number::F64(x), Number::F64(y)) => { + if y == 0.0 { + None + } else { + Some(Number::F64(x / y)) + } + } + (Number::I64(x), Number::I64(y)) => { + if y == 0 { + None + } else { + Some(Number::I64(x / y)) + } + } + (Number::U128(x), Number::U128(y)) => { + if y == 0 { + None + } else { + Some(Number::U128(x / y)) + } + } + _ => unreachable!("promote_to_common_type should handle all cases"), + } + } + + pub fn rem(self, other: Number) -> Option { + let (a, b) = Self::promote_to_common_type(self, other); + match (a, b) { + (Number::F64(x), Number::F64(y)) => { + if y == 0.0 { + None + } else { + Some(Number::F64(x % y)) + } + } + (Number::I64(x), Number::I64(y)) => { + if y == 0 { + None + } else { + Some(Number::I64(x % y)) + } + } + (Number::U128(x), Number::U128(y)) => { + if y == 0 { + None + } else { + Some(Number::U128(x % y)) + } + } + _ => unreachable!("promote_to_common_type should handle all cases"), + } + } + + pub fn neg(self) -> Number { + match self { + Number::I16(n) => Number::I16(-n), + Number::I32(n) => Number::I32(-n), + Number::I64(n) => Number::I64(-n), + Number::F32(n) => Number::F32(-n), + Number::F64(n) => Number::F64(-n), + // Unsigned numbers become signed when negated + Number::U8(n) => Number::I32(-(n as i32)), + Number::U16(n) => Number::I32(-(n as i32)), + Number::U32(n) => Number::I64(-(n as i64)), + Number::U64(n) => Number::I64(-(n as i64)), + Number::U128(n) => Number::I64(-(n as i64)), // May overflow + } + } + + pub fn is_zero(&self) -> bool { + match self { + Number::I16(n) => *n == 0, + Number::I32(n) => *n == 0, + Number::I64(n) => *n == 0, + Number::F32(n) => *n == 0.0, + Number::F64(n) => *n == 0.0, + Number::U8(n) => *n == 0, + Number::U16(n) => *n == 0, + Number::U32(n) => *n == 0, + Number::U64(n) => *n == 0, + Number::U128(n) => *n == 0, + } + } +} + +impl PartialOrd for Number { + fn partial_cmp(&self, other: &Self) -> Option { + let (a, b) = Self::promote_to_common_type(self.clone(), other.clone()); + match (a, b) { + (Number::F64(x), Number::F64(y)) => x.partial_cmp(&y), + (Number::I64(x), Number::I64(y)) => x.partial_cmp(&y), + (Number::U128(x), Number::U128(y)) => x.partial_cmp(&y), + _ => unreachable!(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum BaseValue { + Identifier(String), + String(String), + Number(Number), + Boolean(bool), + Nil, + Function(LoxFunction), + NativeFunction(NativeFunction), +} + +impl BaseValue { + pub fn is_callable(&self) -> bool { + match self { + BaseValue::Function(..) | BaseValue::NativeFunction(..) => true, + _ => false, + } + } +} + +impl Display for BaseValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BaseValue::Identifier(id) => write!(f, "{}", id), + BaseValue::String(s) => write!(f, "{}", s), + BaseValue::Number(n) => write!(f, "{}", n), + BaseValue::Boolean(b) => write!(f, "{}", b), + BaseValue::Nil => write!(f, "nil"), + BaseValue::Function(..) => write!(f, ""), + BaseValue::NativeFunction(..) => write!(f, ""), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct LoxFunction { + pub parameters: Vec<(String, String)>, + pub body: AstNode, + pub closure: Option, + pub guard: Option>, +} + +impl LoxFunction { + pub fn new( + parameters: Vec<(String, String)>, + body: AstNode, + closure: Option, + guard: Option>, + ) -> Self { + LoxFunction { + parameters, + body, + closure, + guard, + } + } + + pub fn anonymous_function(parameters: Vec<(String, String)>, body: AstNode) -> Self { + LoxFunction { + parameters, + body, + closure: None, + guard: None, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct NativeFunction { + pub parameters: Vec<(String, String)>, + pub function: fn(&[BaseValue]) -> BaseValue, +} + +impl NativeFunction { + pub fn new(parameters: Vec<(String, String)>, function: fn(&[BaseValue]) -> BaseValue) -> Self { + NativeFunction { + parameters, + function, + } + } +} + +// Trait implementations for BaseValue + +pub trait Truthy { + fn is_truthy(&self) -> bool; +} + +impl Truthy for BaseValue { + fn is_truthy(&self) -> bool { + match self { + BaseValue::Boolean(b) => *b, + BaseValue::Number(n) => !n.is_zero(), + BaseValue::String(s) => !s.is_empty(), + _ => false, + } + } +} + +impl Not for BaseValue { + type Output = BaseValue; + + fn not(self) -> Self::Output { + match self { + BaseValue::Boolean(b) => BaseValue::Boolean(!b), + BaseValue::Number(n) => BaseValue::Boolean(n.is_zero()), + _ => BaseValue::Boolean(false), + } + } +} + +impl Add for BaseValue { + type Output = LoxResult; + + fn add(self, other: BaseValue) -> Self::Output { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => Ok(BaseValue::Number(a.add(b))), + (BaseValue::String(a), BaseValue::String(b)) => { + Ok(BaseValue::String(format!("{}{}", a, b))) + } + _ => runtime_error( + SourceSlice::default(), + "Cannot add non-numeric values".to_string(), + ), + } + } +} + +impl BaseValue { + pub fn add_with_source( + self, + other: BaseValue, + source_slice: SourceSlice, + ) -> LoxResult { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => Ok(BaseValue::Number(a.add(b))), + (BaseValue::String(a), BaseValue::String(b)) => { + Ok(BaseValue::String(format!("{}{}", a, b))) + } + _ => runtime_error(source_slice, "Cannot add non-numeric values".to_string()), + } + } +} + +impl Sub for BaseValue { + type Output = LoxResult; + + fn sub(self, other: BaseValue) -> Self::Output { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => Ok(BaseValue::Number(a.sub(b))), + _ => runtime_error( + SourceSlice::default(), + "Cannot subtract non-numeric values".to_string(), + ), + } + } +} + +impl Div for BaseValue { + type Output = LoxResult; + + fn div(self, other: BaseValue) -> Self::Output { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => match a.div(b) { + Some(result) => Ok(BaseValue::Number(result)), + None => runtime_error(SourceSlice::default(), "Division by zero".to_string()), + }, + _ => runtime_error( + SourceSlice::default(), + "Cannot divide non-numeric values".to_string(), + ), + } + } +} + +impl Mul for BaseValue { + type Output = LoxResult; + + fn mul(self, other: BaseValue) -> Self::Output { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => Ok(BaseValue::Number(a.mul(b))), + _ => runtime_error( + SourceSlice::default(), + "Cannot multiply non-numeric values".to_string(), + ), + } + } +} + +impl Rem for BaseValue { + type Output = LoxResult; + + fn rem(self, other: BaseValue) -> Self::Output { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => match a.rem(b) { + Some(result) => Ok(BaseValue::Number(result)), + None => runtime_error(SourceSlice::default(), "Division by zero".to_string()), + }, + _ => runtime_error( + SourceSlice::default(), + "Cannot divide non-numeric values".to_string(), + ), + } + } +} + +impl PartialOrd for BaseValue { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (BaseValue::Number(a), BaseValue::Number(b)) => a.partial_cmp(b), + _ => None, + } + } +} + +impl From for bool { + fn from(value: BaseValue) -> Self { + match value { + BaseValue::Boolean(b) => b, + BaseValue::Number(n) => !n.is_zero(), + _ => false, + } + } +} diff --git a/src/result.rs b/src/common/lox_result.rs similarity index 100% rename from src/result.rs rename to src/common/lox_result.rs diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 0000000..736f74d --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1,3 @@ +pub mod ast; +pub mod base_value; +pub mod lox_result; diff --git a/src/frontend/lexer.rs b/src/frontend/lexer.rs index 588c15a..b34d5ec 100644 --- a/src/frontend/lexer.rs +++ b/src/frontend/lexer.rs @@ -1,7 +1,7 @@ +use crate::common::base_value::{BaseValue, Number}; +use crate::common::lox_result::{lexical_error, LoxError, LoxResult}; use crate::frontend::source_registry::{SourceId, SourcePosition, SourceSlice}; -use crate::frontend::tokens::{LiteralValue, Token, TokenType}; -use crate::logging::display_token::TokenPrettyPrintExt; -use crate::result::{LoxError, LoxResult}; +use crate::frontend::tokens::{Token, TokenType}; pub struct Lexer { input: String, @@ -77,8 +77,6 @@ impl Lexer { } } tokens.push(self.make_token(TokenType::Eof)); - println!("Tokens:"); - tokens.iter().for_each(|val| println!("{}", val.pretty())); Ok(tokens) } @@ -119,7 +117,7 @@ impl Lexer { ), ) } - fn make_token_with_literal(&self, token_type: TokenType, literal: LiteralValue) -> Token { + fn make_token_with_literal(&self, token_type: TokenType, literal: BaseValue) -> Token { let text = self.input[self.start_char..self.current_char].to_string(); Token::new_complete( token_type, @@ -186,14 +184,14 @@ impl Lexer { self.advance(); } if self.is_at_end() { - return Err(LoxError::LexicalError { - source_slice: SourceSlice::from_positions( + return lexical_error( + SourceSlice::from_positions( self.source_id.clone(), self.start_pos.clone(), self.end_pos.clone(), ), - message: "Unterminated comment".to_string(), - }); + "Unterminated comment".to_string(), + ); } else { self.advance(); // consuma '*' self.advance(); // consuma '/' @@ -237,30 +235,67 @@ impl Lexer { self.advance(); Ok(Some(self.make_token_with_literal( TokenType::String, - LiteralValue::String(self.input[self.start_char..self.current_char].to_string()), + BaseValue::String(self.input[self.start_char..self.current_char].to_string()), ))) } fn number(&mut self) -> LoxResult> { + // Leggi la parte intera while self.peek().is_digit(10) { self.advance(); } - if self.peek() == '.' && self.peek_next().is_digit(10) { - self.advance(); + + // Controlla se c'è una parte decimale + let has_decimal = if self.peek() == '.' && self.peek_next().is_digit(10) { + self.advance(); // consuma il '.' while self.peek().is_digit(10) { self.advance(); } - } - Ok(Some( - self.make_token_with_literal( - TokenType::Number, - LiteralValue::Number( - self.input[self.start_char..self.current_char] - .parse() - .unwrap(), - ), - ), - )) + true + } else { + false + }; + + // Controlla se c'è un suffisso (f o u) + let suffix = if self.peek() == 'f' || self.peek() == 'u' { + let s = self.peek(); + self.advance(); + Some(s) + } else { + None + }; + + let num_str = &self.input[self.start_char..self.current_char]; + let num_str_without_suffix = if suffix.is_some() { + &num_str[..num_str.len() - 1] + } else { + num_str + }; + + // Determina il tipo di numero basandosi sul contenuto e suffisso + let number_value = match suffix { + Some('f') => { + // Float esplicito con suffisso 'f' + Number::F64(num_str_without_suffix.parse().unwrap()) + } + Some('u') => { + // Unsigned esplicito con suffisso 'u' + Number::U128(num_str_without_suffix.parse().unwrap()) + } + _ if has_decimal => { + // Float implicito (contiene un punto decimale) + Number::F64(num_str.parse().unwrap()) + } + _ => { + // Intero con segno di default + Number::I32(num_str.parse().unwrap()) + } + }; + + Ok(Some(self.make_token_with_literal( + TokenType::Number, + BaseValue::Number(number_value), + ))) } fn identifier(&mut self) -> LoxResult> { @@ -270,18 +305,18 @@ impl Lexer { let text = self.input[self.start_char..self.current_char].to_string(); match get_keyword_token(&text) { Some(TokenType::True) => Ok(Some( - self.make_token_with_literal(TokenType::True, LiteralValue::Boolean(true)), + self.make_token_with_literal(TokenType::True, BaseValue::Boolean(true)), )), Some(TokenType::False) => Ok(Some( - self.make_token_with_literal(TokenType::False, LiteralValue::Boolean(false)), + self.make_token_with_literal(TokenType::False, BaseValue::Boolean(false)), )), Some(TokenType::Nil) => Ok(Some( - self.make_token_with_literal(TokenType::Nil, LiteralValue::Nil), + self.make_token_with_literal(TokenType::Nil, BaseValue::Nil), )), Some(token_type) => Ok(Some(self.make_token(token_type))), None => Ok(Some(self.make_token_with_literal( TokenType::Identifier, - LiteralValue::String(text.clone()), + BaseValue::String(text.clone()), ))), } } diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index e2f449a..732ebff 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -1,4 +1,3 @@ -pub mod ast; pub mod lexer; pub mod parser; pub mod source_registry; diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index b1283b3..bb705f8 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -1,11 +1,13 @@ use crate::{ - frontend::{ + common::{ ast::{AstNode, Expr, Stmt}, - source_registry::{SourcePosition, SourceSlice}, - tokens::{LiteralValue, LoxFunction, Token, TokenType}, + base_value::{BaseValue, LoxFunction}, + lox_result::{parse_error, runtime_error, LoxResult}, + }, + frontend::{ + source_registry::SourceSlice, + tokens::{Token, TokenType}, }, - logging::display_ast::{pretty_print_with_config, PrettyConfig}, - result::{parse_error, runtime_error, LoxResult}, }; pub struct Parser { @@ -21,30 +23,14 @@ impl Parser { pub fn parse(&mut self) -> LoxResult>> { let mut statements = Vec::new(); let mut errors = Vec::new(); - println!("\n \n ++++++++++++++++++++++++++++++++++++\nStarting parsing"); - // Con configurazione personalizzata - let config = PrettyConfig { - indent: " ".to_string(), - max_depth: None, - show_positions: false, - ..Default::default() - }; while !self.is_at_end() { match self.statement() { Ok(stmt) => { - println!( - "Parsed Successfully: {}", - pretty_print_with_config(&stmt.node, &config) - ); - statements.push(stmt.clone()); } Err(err) => { - println!("Error: {:?}", err); errors.push(err); - let old_current = self.current; self.synchronize(); - println!("Synchronized from {} to {}", old_current, self.current); } } } @@ -217,7 +203,7 @@ impl Parser { let name = self.consume(TokenType::Identifier, "Expect variable name.")?; let name_lexeme = name.lexeme.clone(); - let mut _type_annotation = LiteralValue::Nil; + let mut _type_annotation = BaseValue::Nil; self.consume(TokenType::Colon, "Expect column")?; match self.peek().token_type { @@ -254,16 +240,18 @@ impl Parser { self.advance(); } } - self.advance(); + self.consume(TokenType::RightParen, "Expected ')' after parameters")?; let end_position = self.peek().source_slice.clone(); let combine_position = SourceSlice { source_id: start_slice.source_id, start_position: start_slice.start_position.clone(), end_position: end_position.end_position, }; + let mut guard: Option> = None; - if self.peek().token_type == TokenType::RightBrace { - self.advance(); + if self.peek().token_type == TokenType::LeftBrace { + self.consume(TokenType::LeftBrace, "Expected '{' after guard expression")?; + println!("ho trovato una guradia"); guard = Some(Box::new(self.expression()?.node.clone())); self.consume(TokenType::RightBrace, "Expected '}' after guard expression")?; } @@ -271,7 +259,7 @@ impl Parser { let body = self.statement()?; let node = AstNode { node: Expr::Literal { - value: LiteralValue::Function(LoxFunction { + value: BaseValue::Function(LoxFunction { parameters, body, closure: None, @@ -303,7 +291,7 @@ impl Parser { } let mut value = AstNode { node: Expr::Literal { - value: LiteralValue::Nil, + value: BaseValue::Nil, }, source_slice: start_slice.clone(), }; @@ -453,9 +441,36 @@ impl Parser { } fn or_and(&mut self) -> LoxResult> { - let mut expr = self.equality()?; + let mut expr = self.logical_is()?; while [TokenType::Or, TokenType::And].contains(&self.peek().token_type) { + let start_slice = expr.source_slice.clone(); + let operator = self.peek().token_type.clone(); + self.advance(); + let right = self.logical_is()?; + let end_slice = right.source_slice.clone(); + let combined_slice = SourceSlice::from_positions( + start_slice.source_id, + start_slice.start_position, + end_slice.end_position, + ); + expr = AstNode::new( + Expr::Binary { + left: Box::new(expr), + operator, + right: Box::new(right), + }, + combined_slice, + ); + } + + Ok(expr) + } + + fn logical_is(&mut self) -> LoxResult> { + let mut expr = self.equality()?; + + while self.peek().token_type == TokenType::Is { let start_slice = expr.source_slice.clone(); let operator = self.peek().token_type.clone(); self.advance(); @@ -478,6 +493,7 @@ impl Parser { Ok(expr) } + fn equality(&mut self) -> LoxResult> { let mut expr = self.comparison()?; @@ -667,7 +683,7 @@ impl Parser { self.advance(); Ok(AstNode::new( Expr::Literal { - value: LiteralValue::Boolean(false), + value: BaseValue::Boolean(false), }, source_slice, )) @@ -677,7 +693,7 @@ impl Parser { self.advance(); Ok(AstNode::new( Expr::Literal { - value: LiteralValue::Boolean(true), + value: BaseValue::Boolean(true), }, source_slice, )) @@ -687,7 +703,7 @@ impl Parser { self.advance(); Ok(AstNode::new( Expr::Literal { - value: LiteralValue::Nil, + value: BaseValue::Nil, }, source_slice, )) diff --git a/src/frontend/source_registry.rs b/src/frontend/source_registry.rs index a0543fa..3bdaa25 100644 --- a/src/frontend/source_registry.rs +++ b/src/frontend/source_registry.rs @@ -4,7 +4,7 @@ use std::{ io::ErrorKind, }; -use crate::result::{LoxError, LoxResult}; +use crate::common::lox_result::{io_error, LoxError, LoxResult}; pub struct SourceRegistry { pub sources: Vec, @@ -147,7 +147,7 @@ impl SourceRegistry { format!("Failed to read '{}': {}", path, error) } }; - Err(LoxError::IoError { message: error_msg }) + io_error(error_msg) } } } diff --git a/src/frontend/tokens.rs b/src/frontend/tokens.rs index a3345c3..cdad154 100644 --- a/src/frontend/tokens.rs +++ b/src/frontend/tokens.rs @@ -1,67 +1,6 @@ use std::fmt; -use crate::{ - backend::environment::EnvironmentStack, - frontend::{ - ast::{AstNode, Expr, Stmt}, - source_registry::SourceSlice, - }, - result::{runtime_error, LoxResult}, -}; - -#[derive(Debug, Clone, PartialEq)] -pub enum LiteralValue { - Identifier(String), - String(String), - Number(f64), - Boolean(bool), - Nil, - Function(LoxFunction), - NativeFunction(NativeFunction), -} - -#[derive(Debug, Clone, PartialEq)] -pub struct LoxFunction { - pub parameters: Vec<(String, String)>, - pub body: AstNode, - pub closure: Option, - pub guard: Option>, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct NativeFunction { - pub arity: usize, - pub function: fn(&[LiteralValue]) -> LiteralValue, -} - -impl NativeFunction { - pub fn new(arity: usize, function: fn(&[LiteralValue]) -> LiteralValue) -> Self { - NativeFunction { arity, function } - } -} - -impl LiteralValue { - pub fn is_callable(&self) -> bool { - match self { - LiteralValue::Function(..) | LiteralValue::NativeFunction(..) => true, - _ => false, - } - } -} - -impl fmt::Display for LiteralValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - LiteralValue::Identifier(id) => write!(f, "{}", id), - LiteralValue::String(s) => write!(f, "{}", s), - LiteralValue::Number(n) => write!(f, "{}", n), - LiteralValue::Boolean(b) => write!(f, "{}", b), - LiteralValue::Nil => write!(f, "nil"), - LiteralValue::Function(..) => write!(f, ""), - LiteralValue::NativeFunction(..) => write!(f, ""), - } - } -} +use crate::{common::base_value::BaseValue, frontend::source_registry::SourceSlice}; #[derive(Debug, Clone, PartialEq)] pub enum TokenType { @@ -115,6 +54,7 @@ pub enum TokenType { Nil, Or, In, + Is, Print, Return, Super, @@ -178,6 +118,7 @@ impl fmt::Display for TokenType { TokenType::In => write!(f, "in"), TokenType::Colon => write!(f, ":"), TokenType::Fn => write!(f, "fn"), + TokenType::Is => write!(f, "is"), } } } @@ -186,7 +127,7 @@ impl fmt::Display for TokenType { pub struct Token { pub token_type: TokenType, pub lexeme: String, - pub literal: Option, + pub literal: Option, pub source_slice: SourceSlice, } @@ -202,7 +143,7 @@ impl Token { pub fn new_complete( token_type: TokenType, lexeme: String, - literal: Option, + literal: Option, source_slice: SourceSlice, ) -> Self { Token { diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2d494f2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod backend; +pub mod common; +pub mod frontend; diff --git a/src/logging/display_ast.rs b/src/logging/display_ast.rs index c21f473..da837e1 100644 --- a/src/logging/display_ast.rs +++ b/src/logging/display_ast.rs @@ -3,9 +3,10 @@ //! This module provides elegant and functional printing capabilities for AST objects, //! inspired by Rust's debug formatting but optimized for readability and analysis. -use crate::frontend::ast::{AstNode, AstNodeKind, Expr, Stmt}; use std::fmt::{self, Write}; +use crate::common::ast::{AstNode, AstNodeKind, Expr, Stmt}; + /// Configuration for pretty printing output #[derive(Clone, Debug)] pub struct PrettyConfig { diff --git a/src/logging/display_token.rs b/src/logging/display_token.rs index df15511..f3b645f 100644 --- a/src/logging/display_token.rs +++ b/src/logging/display_token.rs @@ -198,6 +198,7 @@ impl Token { TokenType::In => "IN", TokenType::Colon => ":", TokenType::Fn => "FN", + TokenType::Is => "IS", } } @@ -235,6 +236,7 @@ impl Token { | TokenType::This | TokenType::Var | TokenType::Fn + | TokenType::Is | TokenType::Val => ("\x1b[34m", self.token_type_symbol()), TokenType::Identifier | TokenType::String | TokenType::Number => { ("\x1b[32m", self.token_type_symbol()) diff --git a/src/main.rs b/src/main.rs index 957bc57..98e2653 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,19 @@ mod backend; +mod common; mod frontend; mod logging; -mod result; use crate::{ backend::interpreter::{EvaluateInterpreter, Interpreter}, + common::{ + ast::{AstNode, Stmt}, + lox_result::{LoxError, LoxResult}, + }, frontend::{ lexer::Lexer, parser::Parser, source_registry::{SourceId, SourceRegistry}, }, - result::{LoxError, LoxResult}, }; use std::env; use std::fs; @@ -24,9 +27,9 @@ enum ExecutionStage { fn main() -> LoxResult<()> { let args: Vec = env::args().collect(); - let mut lox = LoxInterpreter::new(); - let (stage, file_path) = parse_args(&args); + let (stage, file_path, debug) = parse_args(&args); + let mut lox = LoxInterpreter::new(debug); let _ = match file_path { Some(path) => lox.run_file(&path, stage), @@ -36,71 +39,158 @@ fn main() -> LoxResult<()> { Ok(()) } -fn parse_args(args: &[String]) -> (ExecutionStage, Option) { +fn parse_args(args: &[String]) -> (ExecutionStage, Option, bool) { if args.len() == 1 { // Solo il nome del programma: modalità interattiva completa - (ExecutionStage::Full, None) + (ExecutionStage::Full, None, false) } else if args.len() == 2 { // Un argomento: potrebbe essere file o flag let arg = &args[1]; - if arg == "--tokens" || arg == "--ast" || arg == "--full" { + if arg == "--tokens" || arg == "--ast" || arg == "--full" || arg == "--debug" { // Flag senza file: modalità interattiva let stage = match arg.as_str() { "--tokens" => ExecutionStage::Tokens, "--ast" => ExecutionStage::Ast, "--full" => ExecutionStage::Full, + "--debug" => ExecutionStage::Full, _ => ExecutionStage::Full, }; - (stage, None) + let debug = arg == "--debug"; + (stage, None, debug) } else { // File senza flag: esecuzione completa del file - (ExecutionStage::Full, Some(arg.clone())) + (ExecutionStage::Full, Some(arg.clone()), false) } } else if args.len() == 3 { - // Due argomenti: flag + file - let flag = &args[1]; - let file = &args[2]; - let stage = match flag.as_str() { - "--tokens" => ExecutionStage::Tokens, - "--ast" => ExecutionStage::Ast, - "--full" => ExecutionStage::Full, - _ => { - eprintln!("Unknown flag: {}. Use --tokens, --ast, or --full", flag); - eprintln!("Usage: {} [--tokens|--ast|--full] [script]", args[0]); + // Due argomenti: potrebbero essere flag + file o due flag + let mut debug = false; + let mut stage = ExecutionStage::Full; + let mut file_path = None; + + for arg in &args[1..3] { + if arg == "--debug" { + debug = true; + } else if arg == "--tokens" || arg == "--ast" || arg == "--full" { + stage = match arg.as_str() { + "--tokens" => ExecutionStage::Tokens, + "--ast" => ExecutionStage::Ast, + "--full" => ExecutionStage::Full, + _ => ExecutionStage::Full, + }; + } else if !arg.starts_with("--") { + file_path = Some(arg.clone()); + } else { + eprintln!( + "Unknown flag: {}. Use --tokens, --ast, --full, or --debug", + arg + ); + eprintln!( + "Usage: {} [--tokens|--ast|--full] [--debug] [script]", + args[0] + ); std::process::exit(64); } - }; - (stage, Some(file.clone())) + } + + (stage, file_path, debug) + } else if args.len() == 4 { + // Tre argomenti: flag + flag + file + let mut debug = false; + let mut stage = ExecutionStage::Full; + let mut file_path = None; + + for arg in &args[1..4] { + if arg == "--debug" { + debug = true; + } else if arg == "--tokens" || arg == "--ast" || arg == "--full" { + stage = match arg.as_str() { + "--tokens" => ExecutionStage::Tokens, + "--ast" => ExecutionStage::Ast, + "--full" => ExecutionStage::Full, + _ => ExecutionStage::Full, + }; + } else if !arg.starts_with("--") { + file_path = Some(arg.clone()); + } else { + eprintln!( + "Unknown flag: {}. Use --tokens, --ast, --full, or --debug", + arg + ); + eprintln!( + "Usage: {} [--tokens|--ast|--full] [--debug] [script]", + args[0] + ); + std::process::exit(64); + } + } + + (stage, file_path, debug) } else { - eprintln!("Usage: {} [--tokens|--ast|--full] [script]", args[0]); + eprintln!( + "Usage: {} [--tokens|--ast|--full] [--debug] [script]", + args[0] + ); std::process::exit(64); } } struct LoxInterpreter { source_registry: SourceRegistry, + debug: bool, } impl LoxInterpreter { - pub fn new() -> Self { + pub fn new(debug: bool) -> Self { LoxInterpreter { source_registry: SourceRegistry::new(), + debug, } } // ✅ Pipeline funzionale per file fn run_file(&mut self, path: &str, stage: ExecutionStage) -> LoxResult<()> { - fs::read_to_string(path) - .map_err(|e| LoxError::IoError { - message: e.to_string(), - }) - .and_then(|source| self.source_registry.add_source_string(source)) - .and_then(|source_id| self.process_source(source_id, stage)) - .map(|result| match stage { - ExecutionStage::Tokens => println!("=== TOKENS ===\n{}", result), - ExecutionStage::Ast => println!("=== AST ===\n{}", result), - ExecutionStage::Full => println!("=== EXECUTION RESULT ===\n{}", result), - }) + // Fase 1: Lettura del file e creazione del source + let source = fs::read_to_string(path).map_err(|e| LoxError::IoError { + message: e.to_string(), + })?; + + let source_id = self.source_registry.add_source_string(source)?; + + // Se debug è attivo, mostra sempre tokens e AST + if self.debug { + let tokens = self.tokenize(source_id)?; + println!("=== TOKENS ===\n{}", format_tokens(&tokens)); + println!(); + } + + // Fase 2: Parsing completo - costruzione dell'AST + let ast = self.parse_to_ast(source_id)?; + + if self.debug { + println!("=== AST ===\n{}", format_ast(&ast)); + println!(); + } + + // Fase 3: Esecuzione (se richiesta) + match stage { + ExecutionStage::Tokens => { + if !self.debug { + let tokens = self.tokenize(source_id)?; + println!("=== TOKENS ===\n{}", format_tokens(&tokens)); + } + } + ExecutionStage::Ast => { + if !self.debug { + println!("=== AST ===\n{}", format_ast(&ast)); + } + } + ExecutionStage::Full => { + let result = self.execute_ast(ast, self.debug)?; + println!("=== EXECUTION RESULT ===\n{}", result); + } + } + + Ok(()) } // ✅ Pipeline funzionale per prompt @@ -123,7 +213,7 @@ impl LoxInterpreter { match self.source_registry.add_source_string(line.to_string()) { Ok(source_id) => { - match self.process_source(source_id, stage) { + match self.process_source(source_id, stage, self.debug) { Ok(result) => match stage { ExecutionStage::Tokens => println!("Tokens: {}", result), ExecutionStage::Ast => println!("AST: {}", result), @@ -145,7 +235,54 @@ impl LoxInterpreter { Ok(()) } - fn process_source(&self, source_id: SourceId, stage: ExecutionStage) -> LoxResult { + // Funzione per tokenizzare il codice + fn tokenize(&self, source_id: SourceId) -> LoxResult> { + let source_content = self.source_registry.get_by_id(source_id).content.clone(); + let mut lexer = Lexer::new(source_content, source_id); + + lexer.scans_tokens().or_else(|err| { + err.print_with_context(&self.source_registry); + Err(err) + }) + } + + // Funzione per parsare fino all'AST + fn parse_to_ast(&self, source_id: SourceId) -> LoxResult>> { + let tokens = self.tokenize(source_id)?; + + Parser::new(tokens).parse().or_else(|err| { + err.print_with_context(&self.source_registry); + Err(err) + }) + } + + // Funzione per eseguire l'AST + fn execute_ast(&self, ast: Vec>, debug: bool) -> LoxResult { + let mut interpreter = Interpreter::new(); + let mut result = None; + + for (index, stmt) in ast.iter().enumerate() { + if debug { + println!("Executing statement {}: {:?}", index, stmt); + } + result = Some(interpreter.evaluate(stmt.clone()).or_else(|err| { + err.print_with_context(&self.source_registry); + Err(err) + })?); + } + + match result { + Some(res) => Ok(format!("{}", res)), + None => Ok("No statements executed".to_string()), + } + } + + fn process_source( + &self, + source_id: SourceId, + stage: ExecutionStage, + debug: bool, + ) -> LoxResult { let source_content = self.source_registry.get_by_id(source_id).content.clone(); let mut lexer = Lexer::new(source_content, source_id); @@ -155,6 +292,12 @@ impl LoxInterpreter { Err(err) })?; + // Se debug è attivo, mostra sempre i tokens + if debug && stage != ExecutionStage::Tokens { + println!("=== TOKENS ===\n{}", format_tokens(&tokens)); + println!(); + } + if stage == ExecutionStage::Tokens { return Ok(format_tokens(&tokens)); } @@ -165,6 +308,12 @@ impl LoxInterpreter { Err(err) })?; + // Se debug è attivo, mostra sempre l'AST + if debug && stage != ExecutionStage::Ast { + println!("=== AST ===\n{}", format_ast(&ast)); + println!(); + } + if stage == ExecutionStage::Ast { return Ok(format_ast(&ast)); } @@ -174,7 +323,9 @@ impl LoxInterpreter { let mut result = None; for (index, stmt) in ast.iter().enumerate() { - println!("Executing statement {}: {:?}", index, stmt); + if debug { + println!("Executing statement {}: {:?}", index, stmt); + } result = Some(interpreter.evaluate(stmt.clone()).or_else(|err| { err.print_with_context(&self.source_registry); Err(err) @@ -197,7 +348,7 @@ fn format_tokens(tokens: &[crate::frontend::tokens::Token]) -> String { .join("\n") } -fn format_ast(ast: &[crate::frontend::ast::AstNode]) -> String { +fn format_ast(ast: &[AstNode]) -> String { use crate::logging::display_ast::{pretty_print_with_config, PrettyConfig}; let config = PrettyConfig { diff --git a/tests/number_operations.rs b/tests/number_operations.rs new file mode 100644 index 0000000..fb85f14 --- /dev/null +++ b/tests/number_operations.rs @@ -0,0 +1,187 @@ +use rlox::common::base_value::{BaseValue, Number}; + +#[test] +fn test_number_addition_with_type_promotion() { + // Test float + int -> both become F64 + let float_num = Number::F64(3.5); + let int_num = Number::I32(2); + let result = float_num.add(int_num); + match result { + Number::F64(val) => assert_eq!(val, 5.5), + _ => panic!("Expected F64 result from float + int"), + } + + // Test int + unsigned -> both become I64 + let int_num = Number::I32(-5); + let unsigned_num = Number::U32(10); + let result = int_num.add(unsigned_num); + match result { + Number::I64(val) => assert_eq!(val, 5), + _ => panic!("Expected I64 result from int + unsigned"), + } + + // Test float + unsigned -> both become F64 + let float_num = Number::F32(2.5); + let unsigned_num = Number::U64(3); + let result = float_num.add(unsigned_num); + match result { + Number::F64(val) => assert_eq!(val, 5.5), + _ => panic!("Expected F64 result from float + unsigned"), + } + + // Test unsigned + unsigned -> both stay U128 + let unsigned1 = Number::U32(100); + let unsigned2 = Number::U64(200); + let result = unsigned1.add(unsigned2); + match result { + Number::U128(val) => assert_eq!(val, 300), + _ => panic!("Expected U128 result from unsigned + unsigned"), + } +} + +#[test] +fn test_number_subtraction() { + let a = Number::F64(10.5); + let b = Number::I32(5); + let result = a.sub(b); + match result { + Number::F64(val) => assert_eq!(val, 5.5), + _ => panic!("Expected F64 result"), + } + + let a = Number::I32(10); + let b = Number::U32(3); + let result = a.sub(b); + match result { + Number::I64(val) => assert_eq!(val, 7), + _ => panic!("Expected I64 result"), + } +} + +#[test] +fn test_number_multiplication() { + let a = Number::F64(2.5); + let b = Number::I32(4); + let result = a.mul(b); + match result { + Number::F64(val) => assert_eq!(val, 10.0), + _ => panic!("Expected F64 result"), + } + + let a = Number::U32(5); + let b = Number::U32(6); + let result = a.mul(b); + match result { + Number::U128(val) => assert_eq!(val, 30), + _ => panic!("Expected U128 result"), + } +} + +#[test] +fn test_number_division() { + let a = Number::F64(10.0); + let b = Number::I32(4); + let result = a.div(b).unwrap(); + match result { + Number::F64(val) => assert_eq!(val, 2.5), + _ => panic!("Expected F64 result"), + } + + // Test division by zero + let a = Number::I32(10); + let b = Number::I32(0); + assert!(a.div(b).is_none()); + + let a = Number::F64(10.0); + let b = Number::F64(0.0); + assert!(a.div(b).is_none()); +} + +#[test] +fn test_number_remainder() { + let a = Number::I32(10); + let b = Number::I32(3); + let result = a.rem(b).unwrap(); + match result { + Number::I64(val) => assert_eq!(val, 1), + _ => panic!("Expected I64 result"), + } + + let a = Number::F64(10.5); + let b = Number::F64(3.0); + let result = a.rem(b).unwrap(); + match result { + Number::F64(val) => assert_eq!(val, 1.5), + _ => panic!("Expected F64 result"), + } +} + +#[test] +fn test_number_negation() { + let num = Number::I32(5); + let result = num.neg(); + match result { + Number::I32(val) => assert_eq!(val, -5), + _ => panic!("Expected I32 result"), + } + + let num = Number::U32(5); + let result = num.neg(); + match result { + Number::I64(val) => assert_eq!(val, -5), + _ => panic!("Expected I64 result for negated unsigned"), + } + + let num = Number::F64(3.5); + let result = num.neg(); + match result { + Number::F64(val) => assert_eq!(val, -3.5), + _ => panic!("Expected F64 result"), + } +} + +#[test] +fn test_number_comparison() { + let a = Number::I32(5); + let b = Number::F64(5.0); + assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Equal)); + + let a = Number::I32(5); + let b = Number::F64(6.0); + assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Less)); + + let a = Number::U32(10); + let b = Number::I32(5); + assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Greater)); +} + +#[test] +fn test_baevalue_operations() { + // Test BaseValue addition + let a = BaseValue::Number(Number::I32(5)); + let b = BaseValue::Number(Number::F64(3.5)); + let result = (a + b).unwrap(); + match result { + BaseValue::Number(Number::F64(val)) => assert_eq!(val, 8.5), + _ => panic!("Expected Number(F64) result"), + } + + // Test string concatenation + let a = BaseValue::String("Hello ".to_string()); + let b = BaseValue::String("World".to_string()); + let result = (a + b).unwrap(); + match result { + BaseValue::String(s) => assert_eq!(s, "Hello World"), + _ => panic!("Expected String result"), + } +} + +#[test] +fn test_is_zero() { + assert!(Number::I32(0).is_zero()); + assert!(Number::F64(0.0).is_zero()); + assert!(Number::U32(0).is_zero()); + assert!(!Number::I32(1).is_zero()); + assert!(!Number::F64(0.1).is_zero()); + assert!(!Number::U32(1).is_zero()); +}