Add type system and refator for having only epression
This commit is contained in:
Giulio Agostini
2026-07-06 10:43:17 +02:00
parent 9f15a00b98
commit 842216729b
23 changed files with 940 additions and 979 deletions
+70 -90
View File
@@ -23,7 +23,7 @@
//! struct IdentifierCounter { count: usize }
//!
//! impl Visitor for IdentifierCounter {
//! fn visit_expr(&mut self, expr: &AstNode<Expr>) -> LoxResult<()> {
//! fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> {
//! if let Expr::Identifier { .. } = &expr.node {
//! self.count += 1;
//! }
@@ -33,7 +33,7 @@
//! ```
use crate::common::{
ast::{AstNode, Expr, Stmt},
ast::{AstNode, Expr},
base_value::{BaseValue, LoxFunction},
lox_result::LoxResult,
};
@@ -43,13 +43,8 @@ use crate::common::{
/// 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<Stmt>) -> LoxResult<()> {
walk_stmt(self, stmt)
}
/// Visit an expression node. Defaults to [`walk_expr`].
fn visit_expr(&mut self, expr: &AstNode<Expr>) -> LoxResult<()> {
fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> {
walk_expr(self, expr)
}
@@ -62,70 +57,13 @@ pub trait Visitor: Sized {
}
}
/// Recurse into the children of `stmt`, calling back into `visitor`.
pub fn walk_stmt<V: Visitor>(visitor: &mut V, stmt: &AstNode<Stmt>) -> 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::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<V: Visitor>(visitor: &mut V, expr: &AstNode<Expr>) -> LoxResult<()> {
pub fn walk_expr<V: Visitor>(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 {
if let BaseValue::Function(function) = &**value {
visitor.visit_function(function)?;
}
Ok(())
@@ -147,6 +85,57 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &AstNode<Expr>) -> LoxResult
}
Ok(())
}
Expr::Print { expression, .. } => visitor.visit_expr(expression),
Expr::VarDeclaration { initializer, .. } => {
if let Some(initializer) = initializer {
visitor.visit_expr(initializer)?;
}
Ok(())
}
Expr::Return { expression, .. } => visitor.visit_expr(expression),
Expr::Block { statements, .. } => {
for statement in statements.iter() {
visitor.visit_expr(statement)?;
}
Ok(())
}
Expr::If {
condition,
then_branch,
elif_branches,
else_branch,
..
} => {
visitor.visit_expr(condition)?;
visitor.visit_expr(then_branch)?;
for (elif_condition, elif_body) in elif_branches.iter() {
visitor.visit_expr(elif_condition)?;
visitor.visit_expr(elif_body)?;
}
if let Some(else_body) = else_branch {
visitor.visit_expr(else_body)?;
}
Ok(())
}
Expr::While {
condition, body, ..
} => {
visitor.visit_expr(condition)?;
visitor.visit_expr(body)
}
Expr::For {
variable,
condition,
increment,
body,
..
} => {
visitor.visit_expr(variable)?;
visitor.visit_expr(condition)?;
visitor.visit_expr(increment)?;
visitor.visit_expr(body)
}
}
}
@@ -155,7 +144,7 @@ pub fn walk_function<V: Visitor>(visitor: &mut V, function: &LoxFunction) -> Lox
if let Some(guard) = &function.guard {
visitor.visit_expr(guard)?;
}
visitor.visit_stmt(&function.body)
visitor.visit_expr(&function.body)
}
#[cfg(test)]
@@ -165,7 +154,7 @@ mod tests {
use crate::frontend::lexer::Lexer;
use crate::frontend::parser::Parser;
fn parse(src: &str) -> Vec<AstNode<Stmt>> {
fn parse(src: &str) -> Vec<AstNode> {
let tokens = Lexer::new(src.to_string(), 0)
.scans_tokens()
.expect("source should lex");
@@ -173,29 +162,23 @@ mod tests {
}
/// A visitor that relies entirely on the default traversal and just counts
/// how many statement and expression nodes it sees.
/// how many nodes it sees.
#[derive(Default)]
struct Counter {
stmts: usize,
exprs: usize,
nodes: usize,
}
impl Visitor for Counter {
fn visit_stmt(&mut self, stmt: &AstNode<Stmt>) -> LoxResult<()> {
self.stmts += 1;
walk_stmt(self, stmt)
}
fn visit_expr(&mut self, expr: &AstNode<Expr>) -> LoxResult<()> {
self.exprs += 1;
walk_expr(self, expr)
fn visit_expr(&mut self, node: &AstNode) -> LoxResult<()> {
self.nodes += 1;
walk_expr(self, node)
}
}
fn count(src: &str) -> Counter {
let mut counter = Counter::default();
for stmt in parse(src).iter() {
counter.visit_stmt(stmt).unwrap();
counter.visit_expr(stmt).unwrap();
}
counter
}
@@ -204,19 +187,16 @@ mod tests {
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);
assert_eq!(counter.nodes, 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);
let counter = count("f :: fn (a): Number do return a; end;");
// VarDeclaration + Function literal + Block + Return + identifier `a`
assert_eq!(counter.nodes, 5);
}
/// A visitor that aborts as soon as it sees an identifier, used to check
@@ -224,7 +204,7 @@ mod tests {
struct FailOnIdentifier;
impl Visitor for FailOnIdentifier {
fn visit_expr(&mut self, expr: &AstNode<Expr>) -> LoxResult<()> {
fn visit_expr(&mut self, expr: &AstNode) -> LoxResult<()> {
if let Expr::Identifier { .. } = &expr.node {
return runtime_error(expr.source_slice.clone(), "found an identifier");
}
@@ -238,7 +218,7 @@ mod tests {
let mut visitor = FailOnIdentifier;
let mut result = Ok(());
for stmt in stmts.iter() {
result = visitor.visit_stmt(stmt);
result = visitor.visit_expr(stmt);
if result.is_err() {
break;
}