diff --git a/src/backend/environment.rs b/src/backend/environment.rs index 640a45d..b161b57 100755 --- a/src/backend/environment.rs +++ b/src/backend/environment.rs @@ -79,6 +79,13 @@ impl EnvironmentStack { format!("Undefined variable '{}'", name), ) } + + pub fn depth(&self) -> usize { + self.stack.len() + } + pub fn scope_contains(&self, index: usize, name: &str) -> bool { + self.stack[index].contains_key(name) + } } #[cfg(test)] diff --git a/src/backend/mod.rs b/src/backend/mod.rs index a6f3b79..ad68c27 100755 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,3 +1,2 @@ pub mod environment; pub mod interpreter; -pub mod variable_resolution; diff --git a/src/backend/variable_resolution.rs b/src/backend/variable_resolution.rs deleted file mode 100755 index 1c9d515..0000000 --- a/src/backend/variable_resolution.rs +++ /dev/null @@ -1,204 +0,0 @@ -use crate::{ - backend::environment::EnvironmentStack, - common::{ - ast::{AstNode, AstNodeKind, Expr, Stmt}, - lox_result::{runtime_error, LoxError, LoxResult}, - }, - frontend::source_registry::SourceSlice, -}; - -use std::fmt::{Debug, Display}; -struct Resolver { - scopes: EnvironmentStack, -} - -impl Resolver { - pub fn new() -> Self { - Resolver { - scopes: EnvironmentStack::new(), - } - } - - fn declare(&mut self, name: &String) { - if self.scopes.is_empty() { - return; - } - let _ = self.scopes.set(name.clone(), false); - } - - fn define(&mut self, name: &String) { - if self.scopes.is_empty() { - return; - } - let _ = self.scopes.set(name.clone(), true); - } -} - -pub trait StaticAnalyzer { - fn resolve(&mut self, node: &T) -> LoxResult; -} - -impl StaticAnalyzer> for Resolver -where - Resolver: StaticAnalyzer, -{ - fn resolve(&mut self, node: &AstNode) -> LoxResult { - self.resolve(&node.node) - } -} - -impl StaticAnalyzer for Resolver { - fn resolve(&mut self, node: &Stmt) -> LoxResult { - match node { - Stmt::Expression { expression, .. } => { - // Visit the expression - self.resolve(expression.as_ref()) - } - Stmt::Print { expression, .. } => { - // Visit the expression - self.resolve(expression.as_ref()) - } - Stmt::VarDeclaration { - initializer, name, .. - } => { - self.declare(name); - // Visit the initializer if present - if let Some(init) = initializer { - self.resolve(init.as_ref())?; - } - self.define(name); - Ok(true) - } - Stmt::VarAssigment { value, name, .. } => { - match self.scopes.get(name) { - Ok(true) => (), - Ok(false) => { - return Err(LoxError::RuntimeError { - source_slice: SourceSlice::default(), - message: "Cant read loac variable in it own lintilizer".to_string(), - }) - } - Err(err) => return Err(err), - } - self.resolve(value.as_ref()) - } - Stmt::Return { expression, .. } => { - // Visit the return expression - self.resolve(expression.as_ref()) - } - Stmt::Block { statements, .. } => { - self.scopes.push_new_scope(); - // Visit all statements in the block - for stmt in statements.iter() { - self.resolve(stmt)?; - } - self.scopes.pop_scope(); - Ok(true) - } - Stmt::If { - condition, - then_branch, - elif_branch, - else_branch, - .. - } => { - // Visit the condition - self.resolve(condition.as_ref())?; - - // Visit the then branch - self.resolve(then_branch.as_ref())?; - - // Visit all elif branches - for (elif_condition, elif_stmt) in elif_branch.iter() { - self.resolve(elif_condition.as_ref())?; - self.resolve(elif_stmt.as_ref())?; - } - - // Visit the else branch if present - if let Some(else_stmt) = else_branch { - self.resolve(else_stmt.as_ref())?; - } - - Ok(true) - } - Stmt::While { - condition, body, .. - } => { - // Visit the condition - self.resolve(condition.as_ref())?; - - // Visit the body - self.resolve(body.as_ref())?; - Ok(true) - } - Stmt::For { - variable, - condition, - increment, - body, - .. - } => { - // Visit the variable initialization - self.resolve(variable.as_ref())?; - - // Visit the condition - self.resolve(condition.as_ref())?; - - // Visit the increment - self.resolve(increment.as_ref())?; - - // Visit the body - self.resolve(body.as_ref())?; - Ok(true) - } - } - } -} - -impl StaticAnalyzer for Resolver { - fn resolve(&mut self, node: &Expr) -> LoxResult { - match node { - Expr::Literal { .. } => { - // Leaf node - no children to visit - Ok(true) - } - Expr::Binary { left, right, .. } => { - // Visit left operand - self.resolve(left.as_ref())?; - - // Visit right operand - self.resolve(right.as_ref()) - } - Expr::Unary { operand, .. } => { - // Visit the operand - self.resolve(operand.as_ref()) - } - Expr::Grouping { expression } => { - // Visit the grouped expression - self.resolve(expression.as_ref()) - } - Expr::Identifier { name, .. } => { - if !self.scopes.is_empty() && self.scopes.get(name).is_ok() { - return Err(LoxError::ParseError { - source_slice: SourceSlice::default(), - message: "Cant read local varialbe in it own initializer".to_string(), - }); - } - Ok(true) - } - Expr::Call { - callee, arguments, .. - } => { - // Visit the callee - self.resolve(callee.as_ref())?; - - // Visit all arguments - for arg in arguments.iter() { - self.resolve(arg)?; - } - - Ok(true) - } - } - } -} diff --git a/src/common/base_value.rs b/src/common/base_value.rs index 1929a4d..36e996f 100755 --- a/src/common/base_value.rs +++ b/src/common/base_value.rs @@ -314,7 +314,7 @@ pub struct LoxFunction { pub return_type: Option, pub body: AstNode, pub closure: Option>, - pub guard: Option>, + pub guard: Option>>, } impl LoxFunction { @@ -322,7 +322,7 @@ impl LoxFunction { parameters: Vec<(String, String)>, body: AstNode, closure: Option>, - guard: Option>, + guard: Option>>, ) -> Self { LoxFunction { parameters, diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index 1cc73be..a7a047f 100755 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -252,11 +252,10 @@ impl Parser { end_position: end_position.end_position, }; - let mut guard: Option> = None; + let mut guard: Option>> = None; 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())); + guard = Some(Box::new(self.expression()?)); self.consume(TokenType::RightBrace, "Expected '}' after guard expression")?; } diff --git a/src/lib.rs b/src/lib.rs index 2d494f2..f030b0c 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ pub mod backend; pub mod common; pub mod frontend; +pub mod middleend; diff --git a/src/middleend/crawler.rs b/src/middleend/crawler.rs deleted file mode 100755 index 8feaa2f..0000000 --- a/src/middleend/crawler.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::common::{ - ast::{AstNode, Expr, Stmt}, - lox_result::LoxResult, -}; - -// Enum per tutti i possibili target di operazioni -enum OperationTarget<'a> { - Stmt(&'a mut Stmt), - Expr(&'a mut Expr), - AstStmt(&'a mut AstNode), - AstExpr(&'a mut AstNode), -} - -// Trait per le operazioni -trait Operation: Send + Sync { - fn execute(&self, target: OperationTarget) -> LoxResult<()>; -} - -// Operazione concreta per set_label -struct SetLabelOperation { - new_label: String, -} - -impl SetLabelOperation { - fn new(label: String) -> Self { - Self { new_label: label } - } -} - -impl Operation for SetLabelOperation { - fn execute(&self, target: OperationTarget) -> LoxResult<()> { - match target { - OperationTarget::Stmt(stmt) => { - if let Stmt::Block { label, .. } = stmt { - *label = self.new_label.clone(); - } - } - OperationTarget::AstStmt(node) => { - if let Stmt::Block { label, .. } = &mut node.node { - *label = self.new_label.clone(); - } - } - _ => {} // Expr non hanno label - } - Ok(()) - } -} - -// Trait per convertire tipi in OperationTarget -trait AsOperationTarget { - fn as_target(&mut self) -> OperationTarget; -} - -impl AsOperationTarget for Stmt { - fn as_target(&mut self) -> OperationTarget { - OperationTarget::Stmt(self) - } -} - -impl AsOperationTarget for Expr { - fn as_target(&mut self) -> OperationTarget { - OperationTarget::Expr(self) - } -} - -impl AsOperationTarget for AstNode { - fn as_target(&mut self) -> OperationTarget { - OperationTarget::AstStmt(self) - } -} - -impl AsOperationTarget for AstNode { - fn as_target(&mut self) -> OperationTarget { - OperationTarget::AstExpr(self) - } -} - -// Crawler generico con Command Pattern -struct Crawler { - node: T, - operations: Vec>, -} - -impl Crawler { - fn new(node: T) -> Self { - Self { - node, - operations: vec![], - } - } - - fn add_operation(&mut self, op: Box) { - self.operations.push(op); - } - - fn execute_operations(&mut self) -> LoxResult<()> { - for op in &self.operations { - op.execute(self.node.as_target())?; - } - Ok(()) - } - - fn with_operation(mut self, op: Box) -> Self { - self.operations.push(op); - self - } - - fn get_node(&self) -> &T { - &self.node - } - - fn get_node_mut(&mut self) -> &mut T { - &mut self.node - } -} diff --git a/src/middleend/mod.rs b/src/middleend/mod.rs index 145a637..05d6ea6 100755 --- a/src/middleend/mod.rs +++ b/src/middleend/mod.rs @@ -1 +1,2 @@ -pub mod crawler; +pub mod variable_resolution; +pub mod visit_ast; diff --git a/src/middleend/variable_resolution.rs b/src/middleend/variable_resolution.rs new file mode 100755 index 0000000..71cfb6c --- /dev/null +++ b/src/middleend/variable_resolution.rs @@ -0,0 +1,99 @@ +use std::collections::HashMap; + +use crate::{ + backend::environment::EnvironmentStack, + common::{ + ast::{AstNode, Expr, Stmt}, + lox_result::{LoxError, LoxResult}, + }, + frontend::source_registry::SourceSlice, + middleend::visit_ast::{walk_expr, walk_stmt, Visitor}, +}; + +struct Resolver { + scopes: EnvironmentStack, + locals: HashMap, +} + +impl Resolver { + pub fn new() -> Self { + Resolver { + scopes: EnvironmentStack::new(), + locals: HashMap::new(), + } + } + + fn declare(&mut self, name: &String) { + if self.scopes.is_empty() { + return; + } + let _ = self.scopes.set(name.clone(), false); + } + + fn define(&mut self, name: &String) { + if self.scopes.is_empty() { + return; + } + let _ = self.scopes.set(name.clone(), true); + } + + fn resolve_local(&mut self, name: &String) { + let depth = self.scopes.depth(); + for i in (0..depth).rev() { + if self.scopes.scope_contains(i, name) { + self.locals.insert(, i); + } + } + } +} + +impl Visitor for Resolver { + fn visit_stmt(&mut self, stmt: &AstNode) -> LoxResult<()> { + match &stmt.node { + Stmt::VarDeclaration { name, .. } => { + self.declare(name); + // `walk_stmt` resolves the initializer (if present). + walk_stmt(self, stmt)?; + self.define(name); + Ok(()) + } + Stmt::VarAssigment { name, .. } => { + match self.scopes.get(name) { + Ok(true) => (), + Ok(false) => { + return Err(LoxError::RuntimeError { + source_slice: SourceSlice::default(), + message: "Cant read local variable in it own lintilizer".to_string(), + }) + } + Err(err) => return Err(err), + } + // `walk_stmt` resolves the assigned value. + walk_stmt(self, stmt) + } + Stmt::Block { .. } => { + self.scopes.push_new_scope(); + walk_stmt(self, stmt)?; + self.scopes.pop_scope(); + Ok(()) + } + // Expression, Print, Return, If, While, For: default traversal. + _ => walk_stmt(self, stmt), + } + } + + fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> { + match &expr.node { + Expr::Identifier { name, .. } => { + if !self.scopes.is_empty() && self.scopes.get(name).is_ok() { + return Err(LoxError::ParseError { + source_slice: SourceSlice::default(), + message: "Cant read local varialbe in it own initializer".to_string(), + }); + } + Ok(()) + } + _ => walk_expr(self, expr), + } + } +} diff --git a/src/middleend/visit_ast.rs b/src/middleend/visit_ast.rs new file mode 100644 index 0000000..1b1594f --- /dev/null +++ b/src/middleend/visit_ast.rs @@ -0,0 +1,248 @@ +//! A reusable, read-only AST visitor with default traversal. +//! +//! The traversal is split into two layers so that many different static +//! analyses can share a single definition of "how to walk the tree": +//! +//! * The `walk_*` free functions contain the canonical recursion. For each +//! node they call back into the visitor on every child. This is the only +//! place that needs to know the shape of the AST. +//! * The [`Visitor`] trait's `visit_*` methods are the overridable hooks. Each +//! one defaults to calling the matching `walk_*` function, so a visitor that +//! overrides nothing still performs a complete traversal. +//! +//! To implement an analysis, implement [`Visitor`] and override only the hooks +//! you care about. Inside an override, call the corresponding `walk_*` function +//! whenever you want the default "descend into children" behaviour to happen. +//! Accumulate results in your own struct fields (errors, scope tables, type +//! information, ...) rather than through the return value, which is only used +//! to short-circuit on error. +//! +//! # Example +//! +//! ```ignore +//! struct IdentifierCounter { count: usize } +//! +//! impl Visitor for IdentifierCounter { +//! fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> { +//! if let Expr::Identifier { .. } = &expr.node { +//! self.count += 1; +//! } +//! walk_expr(self, expr) // keep descending into children +//! } +//! } +//! ``` + +use crate::common::{ + ast::{AstNode, Expr, Stmt}, + base_value::{BaseValue, LoxFunction}, + lox_result::LoxResult, +}; + +/// A read-only visitor over the AST. +/// +/// Every hook has a default implementation that performs the standard +/// recursive traversal, so implementors only override the cases they need. +pub trait Visitor: Sized { + /// Visit a statement node. Defaults to [`walk_stmt`]. + fn visit_stmt(&mut self, stmt: &AstNode) -> LoxResult<()> { + walk_stmt(self, stmt) + } + + /// Visit an expression node. Defaults to [`walk_expr`]. + fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> { + walk_expr(self, expr) + } + + /// Visit a function literal (parameters, optional guard and body). + /// + /// Defaults to [`walk_function`], which walks the guard (if any) and the + /// body. Override this to manage a parameter scope before descending. + fn visit_function(&mut self, function: &LoxFunction) -> LoxResult<()> { + walk_function(self, function) + } +} + +/// Recurse into the children of `stmt`, calling back into `visitor`. +pub fn walk_stmt(visitor: &mut V, stmt: &AstNode) -> LoxResult<()> { + match &stmt.node { + Stmt::Expression { expression, .. } => visitor.visit_expr(expression), + Stmt::Print { expression, .. } => visitor.visit_expr(expression), + Stmt::VarDeclaration { initializer, .. } => { + if let Some(initializer) = initializer { + visitor.visit_expr(initializer)?; + } + Ok(()) + } + Stmt::VarAssigment { value, .. } => visitor.visit_expr(value), + Stmt::Return { expression, .. } => visitor.visit_expr(expression), + Stmt::Block { statements, .. } => { + for statement in statements.iter() { + visitor.visit_stmt(statement)?; + } + Ok(()) + } + Stmt::If { + condition, + then_branch, + elif_branch, + else_branch, + .. + } => { + visitor.visit_expr(condition)?; + visitor.visit_stmt(then_branch)?; + for (elif_condition, elif_body) in elif_branch.iter() { + visitor.visit_expr(elif_condition)?; + visitor.visit_stmt(elif_body)?; + } + if let Some(else_body) = else_branch { + visitor.visit_stmt(else_body)?; + } + Ok(()) + } + Stmt::While { + condition, body, .. + } => { + visitor.visit_expr(condition)?; + visitor.visit_stmt(body) + } + Stmt::For { + variable, + condition, + increment, + body, + .. + } => { + visitor.visit_stmt(variable)?; + visitor.visit_expr(condition)?; + visitor.visit_stmt(increment)?; + visitor.visit_stmt(body) + } + } +} + +/// Recurse into the children of `expr`, calling back into `visitor`. +pub fn walk_expr(visitor: &mut V, expr: &AstNode) -> LoxResult<()> { + match &expr.node { + // A function literal carries an entire sub-tree (its body), so it is + // not a leaf: hand it to the dedicated function hook. + Expr::Literal { value } => { + if let BaseValue::Function(function) = value { + visitor.visit_function(function)?; + } + Ok(()) + } + Expr::Identifier { .. } => Ok(()), + Expr::Binary { left, right, .. } => { + visitor.visit_expr(left)?; + visitor.visit_expr(right) + } + Expr::Unary { operand, .. } => visitor.visit_expr(operand), + Expr::Grouping { expression } => visitor.visit_expr(expression), + Expr::Call { + callee, arguments, .. + } => { + visitor.visit_expr(callee)?; + for argument in arguments.iter() { + visitor.visit_expr(argument)?; + } + Ok(()) + } + } +} + +/// Walk the guard (if present) and body of a function literal. +pub fn walk_function(visitor: &mut V, function: &LoxFunction) -> LoxResult<()> { + if let Some(guard) = &function.guard { + visitor.visit_expr(guard)?; + } + visitor.visit_stmt(&function.body) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::lox_result::runtime_error; + use crate::frontend::lexer::Lexer; + use crate::frontend::parser::Parser; + + fn parse(src: &str) -> Vec> { + let tokens = Lexer::new(src.to_string(), 0) + .scans_tokens() + .expect("source should lex"); + Parser::new(tokens).parse().expect("source should parse") + } + + /// A visitor that relies entirely on the default traversal and just counts + /// how many statement and expression nodes it sees. + #[derive(Default)] + struct Counter { + stmts: usize, + exprs: usize, + } + + impl Visitor for Counter { + fn visit_stmt(&mut self, stmt: &AstNode) -> LoxResult<()> { + self.stmts += 1; + walk_stmt(self, stmt) + } + + fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> { + self.exprs += 1; + walk_expr(self, expr) + } + } + + fn count(src: &str) -> Counter { + let mut counter = Counter::default(); + for stmt in parse(src).iter() { + counter.visit_stmt(stmt).unwrap(); + } + counter + } + + #[test] + fn counts_every_node_via_default_traversal() { + // 1 + 2 * 3 => Binary(+){ Literal, Binary(*){ Literal, Literal } } + let counter = count("1 + 2 * 3;"); + assert_eq!(counter.stmts, 1); + assert_eq!(counter.exprs, 5); + } + + #[test] + fn descends_into_function_bodies() { + // The function body must be traversed through `visit_function`, so the + // `return a;` inside it should contribute to the counts. + let counter = count("f :: fn (a) do return a; end"); + // VarDeclaration + Block + Return + assert_eq!(counter.stmts, 3); + // Function literal + identifier `a` + assert_eq!(counter.exprs, 2); + } + + /// A visitor that aborts as soon as it sees an identifier, used to check + /// that errors short-circuit the traversal. + struct FailOnIdentifier; + + impl Visitor for FailOnIdentifier { + fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> { + if let Expr::Identifier { .. } = &expr.node { + return runtime_error(expr.source_slice.clone(), "found an identifier"); + } + walk_expr(self, expr) + } + } + + #[test] + fn errors_propagate_through_traversal() { + let stmts = parse("var x: Int = 1; x;"); + let mut visitor = FailOnIdentifier; + let mut result = Ok(()); + for stmt in stmts.iter() { + result = visitor.visit_stmt(stmt); + if result.is_err() { + break; + } + } + assert!(result.is_err()); + } +}