Introduce NodeId for AST and synthetic slices
This commit is contained in:
+42
-30
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user