Introduce NodeId for AST and synthetic slices

This commit is contained in:
Giulio Agostini
2026-06-30 14:05:46 +02:00
parent ef8abda048
commit d40fe2a550
11 changed files with 504 additions and 170 deletions
+42 -30
View File
@@ -3,6 +3,25 @@ use crate::{
frontend::{source_registry::SourceSlice, tokens::TokenType},
};
use std::fmt::{Debug, Display};
use std::sync::atomic::{AtomicUsize, Ordering};
/// A unique identity for an AST node, assigned once at construction.
///
/// Unlike a [`SourceSlice`] (which describes *where* a node is, for
/// diagnostics), a `NodeId` describes *which* node it is. It is stable across
/// clones, so analyses such as the resolver can key per-reference data on it
/// without relying on source positions being unique.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(pub usize);
static NEXT_NODE_ID: AtomicUsize = AtomicUsize::new(0);
impl NodeId {
/// Allocate the next globally-unique node id.
pub fn next() -> Self {
NodeId(NEXT_NODE_ID.fetch_add(1, Ordering::Relaxed))
}
}
/*
* grammar:
* program -> statement* EOF
@@ -10,7 +29,6 @@ use std::fmt::{Debug, Display};
* | print_statement
* | var_statement
* | block_statement
* | assignment_statement
* | if_statement
*
* expression_statement -> expression ";"
@@ -18,7 +36,6 @@ use std::fmt::{Debug, Display};
* 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 ";"
* block_statement -> "do" statement* "end"
* if_statement -> "if" expression "then" statement ("elif" expression "then" statement)* ("else" statement)? "end"
* while_statement -> "while" expression "do" statement "end"
@@ -61,6 +78,10 @@ pub enum Expr {
callee: Box<AstNode<Expr>>,
arguments: Vec<AstNode<Expr>>,
},
Assign {
name: String,
value: Box<AstNode<Expr>>,
},
}
// Implementazione Display per Expr (per debugging)
@@ -86,6 +107,7 @@ impl std::fmt::Display for Expr {
.collect::<Vec<String>>()
.join(", ")
),
Expr::Assign { name, value } => write!(f, "Assign ({} = {})", name, value.node),
}
}
}
@@ -112,6 +134,7 @@ impl Debug for Expr {
.collect::<Vec<String>>()
.join(", ")
),
Expr::Assign { name, value } => write!(f, "(assign {:?} {:?})", name, value.node),
}
}
}
@@ -131,11 +154,6 @@ pub enum Stmt {
initializer: Option<Box<AstNode<Expr>>>,
return_value: Box<BaseValue>,
},
VarAssigment {
name: String,
value: Box<AstNode<Expr>>,
return_value: Box<BaseValue>,
},
Return {
expression: Box<AstNode<Expr>>,
label: String,
@@ -208,15 +226,6 @@ impl Display for Stmt {
Some(init) => write!(f, "Var({} = {}) -> {:?};", name, init.node, return_value),
None => write!(f, "Var({}) -> {:?};", name, return_value),
},
Stmt::VarAssigment {
name,
value,
return_value,
} => write!(
f,
"Assign({} = {}) -> {:?};",
name, value.node, return_value
),
Stmt::Return {
expression,
return_value,
@@ -319,17 +328,6 @@ impl Debug for Stmt {
),
None => write!(f, "Var({:?}) -> {:?};", name, return_value),
},
Stmt::VarAssigment {
name,
value,
return_value,
} => {
write!(
f,
"Assign({:?} = {:?}) -> {:?};",
name, value.node, return_value
)
}
Stmt::Return {
expression,
return_value,
@@ -397,6 +395,7 @@ impl AstNodeKind for Expr {
Expr::Grouping { .. } => "grouping expression",
Expr::Identifier { .. } => "variable expression",
Expr::Call { .. } => "call expression",
Expr::Assign { .. } => "assignment expression",
}
}
}
@@ -406,7 +405,6 @@ impl AstNodeKind for Stmt {
match self {
Stmt::Expression { .. } => "expression",
Stmt::VarDeclaration { .. } => "variable declaration",
Stmt::VarAssigment { .. } => "assignment",
Stmt::Return { .. } => "return statement",
Stmt::Block { .. } => "block statement",
Stmt::If { .. } => "if statement",
@@ -417,12 +415,22 @@ impl AstNodeKind for Stmt {
}
}
#[derive(Clone, PartialEq, Default)]
#[derive(Clone)]
pub struct AstNode<T: AstNodeKind + Debug + Display> {
/// Stable identity, assigned at construction and preserved across clones.
pub id: NodeId,
pub node: T,
pub source_slice: SourceSlice,
}
// Identity (`id`) deliberately does not participate in equality: two nodes are
// equal when their content and location match, regardless of node id.
impl<T: AstNodeKind + Debug + Display + PartialEq> PartialEq for AstNode<T> {
fn eq(&self, other: &Self) -> bool {
self.node == other.node && self.source_slice == other.source_slice
}
}
impl<T: AstNodeKind + Debug + Display> Display for AstNode<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
@@ -445,6 +453,10 @@ impl<T: AstNodeKind + Debug + Display> Debug for AstNode<T> {
impl<T: AstNodeKind + Debug + Display> AstNode<T> {
pub fn new(node: T, source_slice: SourceSlice) -> Self {
AstNode { node, source_slice }
AstNode {
id: NodeId::next(),
node,
source_slice,
}
}
}
+7 -7
View File
@@ -398,7 +398,7 @@ impl Add for BaseValue {
Ok(BaseValue::String(format!("{}{}", a, b)))
}
_ => runtime_error(
SourceSlice::default(),
SourceSlice::synthetic(),
"Cannot add non-numeric values".to_string(),
),
}
@@ -428,7 +428,7 @@ impl Sub for BaseValue {
match (self, other) {
(BaseValue::Number(a), BaseValue::Number(b)) => Ok(BaseValue::Number(a.sub(b))),
_ => runtime_error(
SourceSlice::default(),
SourceSlice::synthetic(),
"Cannot subtract non-numeric values".to_string(),
),
}
@@ -442,10 +442,10 @@ impl Div for BaseValue {
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()),
None => runtime_error(SourceSlice::synthetic(), "Division by zero".to_string()),
},
_ => runtime_error(
SourceSlice::default(),
SourceSlice::synthetic(),
"Cannot divide non-numeric values".to_string(),
),
}
@@ -459,7 +459,7 @@ impl Mul for BaseValue {
match (self, other) {
(BaseValue::Number(a), BaseValue::Number(b)) => Ok(BaseValue::Number(a.mul(b))),
_ => runtime_error(
SourceSlice::default(),
SourceSlice::synthetic(),
"Cannot multiply non-numeric values".to_string(),
),
}
@@ -473,10 +473,10 @@ impl Rem for BaseValue {
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()),
None => runtime_error(SourceSlice::synthetic(), "Division by zero".to_string()),
},
_ => runtime_error(
SourceSlice::default(),
SourceSlice::synthetic(),
"Cannot divide non-numeric values".to_string(),
),
}