diff --git a/example.lox b/example.lox index 244e8f2..e72e665 100644 --- a/example.lox +++ b/example.lox @@ -13,10 +13,11 @@ do cavallo end do + var cavallo = 0; print "cavallo"; while cavallo < 10 do - print "loop: " + cavallo; cavallo = cavallo + 1; + print cavallo; end 89 end diff --git a/src/backend/environment.rs b/src/backend/environment.rs index 2646de5..0da0e53 100644 --- a/src/backend/environment.rs +++ b/src/backend/environment.rs @@ -2,43 +2,44 @@ use std::collections::HashMap; use crate::{ frontend::{source_registry::SourceSlice, tokens::LiteralValue}, - result::{LoxError, LoxResult}, + result::{runtime_error, LoxResult}, }; -pub struct Environment<'a> { - values: HashMap, - parent: Option<&'a Environment<'a>>, +#[derive(Debug, Clone)] +pub struct EnvironmentStack { + stack: Vec>, } -impl<'a> Environment<'a> { +impl EnvironmentStack { pub fn new() -> Self { - Environment { - values: HashMap::new(), - parent: None, + EnvironmentStack { + stack: vec![HashMap::new()], } } - pub fn new_with_parent(parent: &'a Environment<'a>) -> Self { - Environment { - values: HashMap::new(), - parent: Some(parent), - } + pub fn push_new_scope(&mut self) { + self.stack.push(HashMap::new()); + } + + pub fn pop_scope(&mut self) { + self.stack.pop(); } pub fn get(&self, name: &str) -> LoxResult { - match self.values.get(name) { - Some(value) => Ok(value.clone()), - None => match self.parent { - Some(parent) => parent.get(name), - None => Err(LoxError::RuntimeError { - source_slice: SourceSlice::default(), // todo change this to the actual source slice - message: format!("Undefined variable '{}'", name), - }), - }, + let size = self.stack.len(); + + for i in (0..size).rev() { + if let Some(value) = self.stack[i].get(name) { + return Ok(value.clone()); + } } + runtime_error( + SourceSlice::default(), // todo change this to the actual source slice + format!("Undefined variable '{}'", name), + ) } pub fn set(&mut self, name: String, value: LiteralValue) { - self.values.insert(name, value); + self.stack.last_mut().unwrap().insert(name, value); } } diff --git a/src/backend/interpreter.rs b/src/backend/interpreter.rs index d5ddccb..c06497c 100644 --- a/src/backend/interpreter.rs +++ b/src/backend/interpreter.rs @@ -1,5 +1,5 @@ use crate::{ - backend::environment::Environment, + backend::environment::EnvironmentStack, frontend::{ ast::{AstNode, AstNodeKind, Expr, Stmt}, source_registry::SourceSlice, @@ -8,7 +8,7 @@ use crate::{ result::{LoxError, LoxResult}, }; use std::{ - fmt::{format, Debug, Display}, + fmt::{Debug, Display}, ops::{Add, Div, Mul, Neg, Not, Rem, Sub}, }; @@ -169,59 +169,17 @@ impl From for bool { } } -pub struct Interpreter<'a> { - enviorment: &'a mut Environment<'a>, -} - -impl<'a> Interpreter<'a> { - pub fn new(env: &'a mut Environment<'a>) -> Self { - Self { enviorment: env } - } - - fn interpret_binary( - &mut self, - left: AstNode, - operator: TokenType, - right: AstNode, - source_slice: SourceSlice, - ) -> LoxResult { - let left_value = self.interpret(left)?; - let right_value = self.interpret(right)?; - match operator { - TokenType::Minus => left_value - right_value, - TokenType::Plus => left_value.add_with_source(right_value, source_slice.clone()), - TokenType::Slash => left_value / right_value, - TokenType::Star => left_value * right_value, - TokenType::EqualEqual => Ok(LiteralValue::Boolean(left_value == right_value)), - TokenType::BangEqual => Ok(LiteralValue::Boolean(left_value != right_value)), - TokenType::Greater => Ok(LiteralValue::Boolean(left_value > right_value)), - TokenType::GreaterEqual => Ok(LiteralValue::Boolean(left_value >= right_value)), - TokenType::Less => Ok(LiteralValue::Boolean(left_value < right_value)), - TokenType::LessEqual => Ok(LiteralValue::Boolean(left_value <= right_value)), - TokenType::Percent => left_value % right_value, - - TokenType::And => Ok(LiteralValue::Boolean( - left_value.is_truthy() && right_value.is_truthy(), - )), - TokenType::Or => Ok(LiteralValue::Boolean( - left_value.is_truthy() || right_value.is_truthy(), - )), - _ => Err(LoxError::RuntimeError { - source_slice: SourceSlice::default(), // todo change this to the actual source slice - message: format!("Unsupported binary operator {}", operator), - }), - } - } +pub struct Interpreter { + enviorment: EnvironmentStack, } pub trait EvaluateInterpreter { fn interpret(&mut self, stmt: T) -> LoxResult; } -impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter> - for Interpreter<'a> +impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter> for Interpreter where - Interpreter<'a>: EvaluateInterpreter, + Interpreter: EvaluateInterpreter, { fn interpret(&mut self, stmt: AstNode) -> LoxResult { match self.interpret(stmt.node.clone()) { @@ -235,7 +193,7 @@ where } // Direct Expr evaluation to avoid infinite recursion -impl<'a> EvaluateInterpreter for Interpreter<'a> { +impl EvaluateInterpreter for Interpreter { fn interpret(&mut self, expr: Expr) -> LoxResult { match expr { Expr::Literal { value } => Ok(value), @@ -258,7 +216,7 @@ impl<'a> EvaluateInterpreter for Interpreter<'a> { } } -impl<'a> EvaluateInterpreter> for Interpreter<'a> { +impl EvaluateInterpreter> for Interpreter { fn interpret(&mut self, node: AstNode) -> LoxResult { let stmt = node.node; let _source_slice = node.source_slice; @@ -266,7 +224,13 @@ impl<'a> EvaluateInterpreter> for Interpreter<'a> { } } -impl<'a> Interpreter<'a> { +impl Interpreter { + pub fn new() -> Self { + Self { + enviorment: EnvironmentStack::new(), + } + } + fn evaluate_binary( &mut self, left: LiteralValue, @@ -311,30 +275,7 @@ impl<'a> Interpreter<'a> { println!("print interpreter: \t{}", value); Ok(LiteralValue::Nil) } - Stmt::Block { statements } => { - let (elements, final_expr) = match statements.split_last() { - Some((stmt, body)) => match &stmt.node { - Stmt::Expression { expression } => (body, Some(expression)), - Stmt::Return { expression } => (body, Some(expression)), - _ => (statements.as_slice(), None), - }, - - None => { - (&[][..], None) // Blocco vuoto - } - }; - - // Ora elements è sempre disponibile - for statement in elements.iter() { - self.interpret((*statement).clone())?; - } - - // Gestisci l'espressione finale se presente - match final_expr { - Some(expr) => self.interpret(*expr.clone()), - None => Ok(LiteralValue::Nil), - } - } + Stmt::Block { statements } => self.evaluate_block(*statements), Stmt::Stmt { expression } => self.interpret(*expression.clone()), Stmt::Return { expression } => self.interpret(*expression), Stmt::Var { name, initializer } => { @@ -396,11 +337,40 @@ impl<'a> Interpreter<'a> { } Ok(LiteralValue::Nil) } - Stmt::For { - variable, - iterable, - body, - } => todo!(), + Stmt::For { .. } => todo!(), + } + } + + 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 { + Stmt::Expression { expression } => (body, Some(expression)), + Stmt::Return { expression } => (body, Some(expression)), + _ => (statements.as_slice(), None), + }, + + None => { + (&[][..], None) // Blocco vuoto + } + }; + + // Ora elements è sempre disponibile + for statement in elements.iter() { + self.interpret((*statement).clone())?; + } + + // Gestisci l'espressione finale se presente + match final_expr { + Some(expr) => { + let res = self.interpret(*expr.clone()); + self.enviorment.pop_scope(); + res + } + None => { + self.enviorment.pop_scope(); + Ok(LiteralValue::Nil) + } } } } diff --git a/src/frontend/ast.rs b/src/frontend/ast.rs index 1378009..fe8e7ad 100644 --- a/src/frontend/ast.rs +++ b/src/frontend/ast.rs @@ -1,5 +1,5 @@ use crate::frontend::{ - source_registry::{SourceId, SourcePosition, SourceSlice}, + source_registry::SourceSlice, tokens::{LiteralValue, TokenType}, }; use std::fmt::{Debug, Display}; @@ -54,34 +54,6 @@ pub enum Expr { }, } -pub trait ExprPrint { - fn print(&self) -> String; -} - -impl ExprPrint for Expr { - fn print(&self) -> String { - match self { - Expr::Literal { value } => format!("Literal {}", value), - Expr::Binary { - left, - operator, - right, - } => { - format!("Binary ({} {} {})", left.node, operator, right.node) - } - Expr::Unary { operator, operand } => { - format!("Unary ({} {})", operator, operand.node) - } - Expr::Grouping { expression } => { - format!("Grouping (group {})", expression.node) - } - Expr::Variable { name } => { - format!("Variable (variable {})", name) - } - } - } -} - // Implementazione Display per Expr (per debugging) impl std::fmt::Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -294,28 +266,16 @@ impl AstNodeKind for Expr { impl AstNodeKind for Stmt { fn kind(&self) -> &'static str { match self { - Stmt::Expression { expression: _ } => "expression", - Stmt::Var { - name: _, - initializer: _, - } => "variable declaration", - Stmt::Assign { name: _, value: _ } => "assignment", - Stmt::Return { expression: _ } => "return statement", - Stmt::Block { statements: _ } => "block statement", - Stmt::If { - condition: _, - then_branch: _, - elif_branch: _, - else_branch: _, - } => "if statement", - Stmt::Print { expression: _ } => "print statement", - Stmt::Stmt { expression: _ } => "statement", - Stmt::While { condition, body } => "while statement", - Stmt::For { - variable, - iterable, - body, - } => "for statement", + Stmt::Expression { .. } => "expression", + Stmt::Var { .. } => "variable declaration", + Stmt::Assign { .. } => "assignment", + Stmt::Return { .. } => "return statement", + Stmt::Block { .. } => "block statement", + Stmt::If { .. } => "if statement", + Stmt::Print { .. } => "print statement", + Stmt::Stmt { .. } => "statement", + Stmt::While { .. } => "while statement", + Stmt::For { .. } => "for statement", } } } diff --git a/src/frontend/lexer.rs b/src/frontend/lexer.rs index d18b110..0933b5c 100644 --- a/src/frontend/lexer.rs +++ b/src/frontend/lexer.rs @@ -26,6 +26,7 @@ fn get_keyword_token(word: &str) -> Option { "elif" => Some(TokenType::Elif), "else" => Some(TokenType::Else), "or" => Some(TokenType::Or), + "in" => Some(TokenType::In), "print" => Some(TokenType::Print), "return" => Some(TokenType::Return), "super" => Some(TokenType::Super), @@ -146,6 +147,7 @@ impl Lexer { ('+', _) => Ok(Some(self.make_token(TokenType::Plus))), (';', _) => Ok(Some(self.make_token(TokenType::Semicolon))), ('*', _) => Ok(Some(self.make_token(TokenType::Star))), + ('%', _) => Ok(Some(self.make_token(TokenType::Percent))), ('!', '=') => { self.advance(); // consuma il '=' Ok(Some(self.make_token(TokenType::BangEqual))) diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index 381205d..4a3012b 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -5,7 +5,7 @@ use crate::{ tokens::{LiteralValue, Token, TokenType}, }, logging::display_ast::{pretty_print_with_config, PrettyConfig}, - result::{LoxError, LoxResult}, + result::{parse_error, LoxError, LoxResult}, }; pub struct Parser { @@ -65,10 +65,53 @@ impl Parser { (TokenType::Identifier, TokenType::Equal) => self.assignment_statement(), (TokenType::If, _) => self.if_statement(), (TokenType::While, _) => self.while_statement(), + (TokenType::For, _) => self.for_statement(), _ => self.expression_statement(), } } + fn for_statement(&mut self) -> LoxResult> { + let start_slice = self.peek().source_slice.clone(); + self.advance(); + let variable = if let Token { + token_type: TokenType::Identifier, + literal: Some(variable_name), + source_slice, + .. + } = self.peek() + { + AstNode { + node: Expr::Literal { + value: variable_name.clone(), + }, + source_slice: source_slice.clone(), + } + } else { + return parse_error( + self.peek().source_slice.clone(), + "Expect variable name after 'for' keyword.", + ); + }; + self.advance(); + self.consume(TokenType::In, "Expect 'in' after for variable.")?; + let iterable = self.expression()?; + let body = self.statement()?; + let end_slice = body.source_slice.clone(); + let combined_slice = SourceSlice::from_positions( + start_slice.source_id, + start_slice.start_position, + end_slice.end_position, + ); + Ok(AstNode::new( + Stmt::For { + variable: Box::new(variable), + iterable: Box::new(iterable), + body: Box::new(body), + }, + combined_slice, + )) + } + fn while_statement(&mut self) -> LoxResult> { let start_slice = self.peek().source_slice.clone(); self.advance(); diff --git a/src/frontend/tokens.rs b/src/frontend/tokens.rs index b863337..9535000 100644 --- a/src/frontend/tokens.rs +++ b/src/frontend/tokens.rs @@ -72,6 +72,7 @@ pub enum TokenType { Else, Nil, Or, + In, Print, Return, Super, @@ -132,6 +133,7 @@ impl fmt::Display for TokenType { TokenType::Elif => write!(f, "elif"), TokenType::Val => write!(f, "val"), TokenType::Percent => write!(f, "%"), + TokenType::In => write!(f, "in"), } } } diff --git a/src/logging/display_ast.rs b/src/logging/display_ast.rs index 1e648b1..4b9ae82 100644 --- a/src/logging/display_ast.rs +++ b/src/logging/display_ast.rs @@ -3,10 +3,7 @@ //! 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}, - source_registry::SourceSlice, -}; +use crate::frontend::ast::{AstNode, AstNodeKind, Expr, Stmt}; use std::fmt::{self, Write}; /// Configuration for pretty printing output @@ -39,6 +36,7 @@ impl Default for PrettyConfig { } } +#[allow(dead_code)] impl PrettyConfig { /// Create a compact configuration pub fn compact() -> Self { @@ -471,6 +469,7 @@ impl PrettyPrint for A } // Extension trait for convenience methods +#[allow(dead_code)] pub trait PrettyPrintExt { fn pretty(&self) -> String; fn pretty_compact(&self) -> String; @@ -531,24 +530,3 @@ impl PrettyPrintExt fo pretty_print_with_config(self, config) } } - -/// Convenience functions for quick pretty printing -pub fn pretty_expr(expr: &Expr) -> String { - pretty_print(expr) -} - -pub fn pretty_stmt(stmt: &Stmt) -> String { - pretty_print(stmt) -} - -pub fn pretty_expr_compact(expr: &Expr) -> String { - pretty_print_with_config(expr, &PrettyConfig::compact()) -} - -pub fn pretty_stmt_compact(stmt: &Stmt) -> String { - pretty_print_with_config(stmt, &PrettyConfig::compact()) -} - -pub fn pretty_tree(item: &T) -> String { - pretty_print_with_config(item, &PrettyConfig::tree()) -} diff --git a/src/logging/display_token.rs b/src/logging/display_token.rs index bba5446..a4db9aa 100644 --- a/src/logging/display_token.rs +++ b/src/logging/display_token.rs @@ -3,11 +3,7 @@ //! This module provides elegant and functional printing capabilities for Token objects, //! inspired by Rust's debug formatting but optimized for readability and analysis. -use crate::frontend::{ - source_registry::SourceSlice, - tokens::{LiteralValue, Token, TokenType}, -}; -use std::fmt; +use crate::frontend::tokens::{Token, TokenType}; /// Configuration for pretty printing tokens #[derive(Clone, Debug)] @@ -199,12 +195,12 @@ impl Token { TokenType::Var => "VAR", TokenType::Val => "VAL", TokenType::Eof => "EOF", + TokenType::In => "IN", } } fn colored_type(&self) -> String { let (color_code, symbol) = match self.token_type { - // Operators - Red TokenType::Plus | TokenType::Minus | TokenType::Star @@ -218,8 +214,6 @@ impl Token { | TokenType::GreaterEqual | TokenType::Less | TokenType::LessEqual => ("\x1b[31m", self.token_type_symbol()), - - // Keywords - Blue TokenType::And | TokenType::Class | TokenType::False @@ -239,13 +233,9 @@ impl Token { | TokenType::This | TokenType::Var | TokenType::Val => ("\x1b[34m", self.token_type_symbol()), - - // Literals - Green TokenType::Identifier | TokenType::String | TokenType::Number => { ("\x1b[32m", self.token_type_symbol()) } - - // Punctuation - Gray TokenType::LeftParen | TokenType::RightParen | TokenType::LeftBrace @@ -255,12 +245,9 @@ impl Token { | TokenType::Comma | TokenType::Dot | TokenType::Semicolon => ("\x1b[37m", self.token_type_symbol()), - - // Block delimiters - Magenta TokenType::StartBlock | TokenType::EndBlock => ("\x1b[35m", self.token_type_symbol()), - - // EOF - Yellow TokenType::Eof => ("\x1b[33m", self.token_type_symbol()), + TokenType::In => ("\x1b[36m", self.token_type_symbol()), }; format!("{}{}\x1b[0m", color_code, symbol) @@ -298,6 +285,7 @@ pub fn pretty_print_tokens(tokens: &[Token], config: &TokenPrettyConfig) -> Stri } /// Extension trait for convenience methods +#[allow(dead_code)] pub trait TokenPrettyPrintExt { fn pretty(&self) -> String; fn pretty_compact(&self) -> String; @@ -358,28 +346,3 @@ impl TokenPrettyPrintExt for Vec { pretty_print_tokens(self, config) } } - -/// Convenience functions for quick pretty printing -pub fn pretty_token(token: &Token) -> String { - token.pretty() -} - -pub fn pretty_token_compact(token: &Token) -> String { - token.pretty_compact() -} - -pub fn pretty_token_minimal(token: &Token) -> String { - token.pretty_minimal() -} - -pub fn pretty_tokens(tokens: &[Token]) -> String { - pretty_print_tokens(tokens, &TokenPrettyConfig::default()) -} - -pub fn pretty_tokens_compact(tokens: &[Token]) -> String { - pretty_print_tokens(tokens, &TokenPrettyConfig::compact()) -} - -pub fn pretty_tokens_colored(tokens: &[Token]) -> String { - pretty_print_tokens(tokens, &TokenPrettyConfig::colored()) -} diff --git a/src/main.rs b/src/main.rs index da8eb2e..ce2694f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,19 +6,19 @@ mod result; use crate::{ backend::{ - environment::Environment, + environment::EnvironmentStack, interpreter::{EvaluateInterpreter, Interpreter}, }, frontend::{ lexer::Lexer, parser::Parser, - source_registry::{SourceFile, SourceId, SourcePosition, SourceRegistry, SourceSlice}, + source_registry::{SourceId, SourceRegistry, SourceSlice}, }, prompt::prompt_lines, result::{LoxError, LoxResult}, }; +use std::env; use std::fs; -use std::{env, error::Error}; fn main() -> LoxResult<()> { let args: Vec = env::args().collect(); @@ -83,8 +83,7 @@ impl LoxInterpreter { Ok(asts) }) .and_then(|stmts| { - let mut env = Environment::new(); - let mut interpreter = Interpreter::new(&mut env); + let mut interpreter = Interpreter::new(); let mut result = None; for (index, stmt) in stmts.iter().enumerate() { println!("executing stmt {}: {:?}", index, stmt); diff --git a/src/result.rs b/src/result.rs index 8365ae4..aa708ae 100644 --- a/src/result.rs +++ b/src/result.rs @@ -402,45 +402,48 @@ impl std::error::Error for LoxError {} pub type LoxResult = Result; /// Helper function to create a lexical error -pub fn lexical_error(source_slice: SourceSlice, message: impl Into) -> LoxError { - LoxError::LexicalError { +pub fn lexical_error(source_slice: SourceSlice, message: impl Into) -> LoxResult { + Err(LoxError::LexicalError { source_slice, message: message.into(), - } + }) } /// Helper function to create a parse error -pub fn parse_error(source_slice: SourceSlice, message: impl Into) -> LoxError { - LoxError::ParseError { +pub fn parse_error(source_slice: SourceSlice, message: impl Into) -> LoxResult { + Err(LoxError::ParseError { source_slice, message: message.into(), - } + }) } /// Helper function to create a runtime error -pub fn runtime_error(source_slice: SourceSlice, message: impl Into) -> LoxError { - LoxError::RuntimeError { +#[allow(dead_code)] +pub fn runtime_error(source_slice: SourceSlice, message: impl Into) -> LoxResult { + Err(LoxError::RuntimeError { source_slice, message: message.into(), - } + }) } /// Helper function to create a type mismatch error -pub fn type_mismatch_error( +#[allow(dead_code)] +pub fn type_mismatch_error( source_slice: SourceSlice, expected: impl Into, found: impl Into, -) -> LoxError { - LoxError::TypeMismatch { +) -> LoxResult { + Err(LoxError::TypeMismatch { source_slice, expected: expected.into(), found: found.into(), - } + }) } /// Helper function to create an IO error -pub fn io_error(message: impl Into) -> LoxError { - LoxError::IoError { +#[allow(dead_code)] +pub fn io_error(message: impl Into) -> LoxResult { + Err(LoxError::IoError { message: message.into(), - } + }) }