diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 7579f38..386aee4 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -130,9 +130,7 @@ impl Binder { }; binder.functions.push(FunctionCompiler::new( ScopeKind::Root, - Rc::new(crate::ast::types::NodeIdentity { - location: crate::ast::types::SourceLocation { line: 0, col: 0 }, - }), + crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }), )); binder } diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 096c2a2..8931f07 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -101,9 +101,10 @@ impl Dumper { if !captured_by.is_empty() { for capturer in captured_by { self.write_indent(); + let loc = capturer.location.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 }); self.output.push_str(&format!( "- Capturer: Lambda at line {}, col {}\n", - capturer.location.line, capturer.location.col + loc.line, loc.col )); } } diff --git a/src/ast/compiler/macros.rs b/src/ast/compiler/macros.rs index 684c8c3..9a19259 100644 --- a/src/ast/compiler/macros.rs +++ b/src/ast/compiler/macros.rs @@ -717,9 +717,7 @@ mod tests { Symbol::from("*"), ( 0, - Rc::new(crate::ast::types::NodeIdentity { - location: crate::ast::types::SourceLocation { line: 0, col: 0 }, - }), + crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }), ), ); let globals = Rc::new(RefCell::new(global_names)); diff --git a/src/ast/environment.rs b/src/ast/environment.rs index c5259d4..12d1f14 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -134,9 +134,7 @@ impl Environment { let mut purity = self.global_purity.borrow_mut(); let idx = values.len() as u32; - let identity = Rc::new(NodeIdentity { - location: SourceLocation { line: 0, col: 0 }, - }); + let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 }); names.insert(Symbol::from(name), (idx, identity)); types.insert(idx, ty); purity.insert(idx, func.purity); @@ -167,9 +165,7 @@ impl Environment { let mut purity = self.global_purity.borrow_mut(); let idx = values.len() as u32; - let identity = Rc::new(NodeIdentity { - location: SourceLocation { line: 0, col: 0 }, - }); + let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 }); names.insert(Symbol::from(name), (idx, identity)); types.insert(idx, ty); purity.insert(idx, Purity::Pure); diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 2c3e8fd..ec36ec8 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -29,9 +29,7 @@ impl<'a> Parser<'a> { pub fn parse_expression(&mut self) -> Result, String> { let token_loc = self.current_token.location; - let identity = Rc::new(NodeIdentity { - location: token_loc, - }); + let identity = NodeIdentity::new(token_loc); match self.peek() { TokenKind::LeftParen => self.parse_list(), @@ -93,9 +91,7 @@ impl<'a> Parser<'a> { fn parse_atom(&mut self) -> Result, String> { let token = self.advance()?; - let identity = Rc::new(NodeIdentity { - location: token.location, - }); + let identity = NodeIdentity::new(token.location); let kind = match token.kind { TokenKind::Integer(n) => UntypedKind::Constant(Value::Int(n)), @@ -123,9 +119,7 @@ impl<'a> Parser<'a> { fn parse_list(&mut self) -> Result, String> { let start_loc = self.advance()?.location; // consume '(' - let identity = Rc::new(NodeIdentity { - location: start_loc, - }); + let identity = NodeIdentity::new(start_loc); if *self.peek() == TokenKind::RightParen { return Err(format!( @@ -272,18 +266,14 @@ impl<'a> Parser<'a> { _ => unreachable!(), }; Ok(Node { - identity: Rc::new(NodeIdentity { - location: token.location, - }), + identity: NodeIdentity::new(token.location), kind: UntypedKind::Parameter(sym), ty: (), }) } TokenKind::LeftBracket => { let token = self.advance()?; - let identity = Rc::new(NodeIdentity { - location: token.location, - }); + let identity = NodeIdentity::new(token.location); let mut elements = Vec::new(); while *self.peek() != TokenKind::RightBracket { @@ -344,9 +334,7 @@ impl<'a> Parser<'a> { self.expect(TokenKind::RightBracket)?; Ok(Node { - identity: Rc::new(NodeIdentity { - location: token.location, - }), + identity: NodeIdentity::new(token.location), kind: UntypedKind::Tuple { elements }, ty: (), }) @@ -380,9 +368,7 @@ impl<'a> Parser<'a> { self.expect(TokenKind::RightBrace)?; Ok(Node { - identity: Rc::new(NodeIdentity { - location: token.location, - }), + identity: NodeIdentity::new(token.location), kind: UntypedKind::Record { fields }, ty: (), }) diff --git a/src/ast/types.rs b/src/ast/types.rs index 9db81fd..0695d22 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -14,14 +14,54 @@ pub struct SourceLocation { pub col: u32, } -/// Shared identity for nodes (Location, etc.) -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Shared identity for nodes. The identity is defined by the object instance (unique ID). +/// SourceLocation is kept as optional metadata. +#[derive(Debug, Clone)] pub struct NodeIdentity { - pub location: SourceLocation, + pub id: u64, + pub location: Option, +} + +impl PartialEq for NodeIdentity { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for NodeIdentity {} + +impl std::hash::Hash for NodeIdentity { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } } pub type Identity = Rc; +static NODE_ID_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1); + +impl NodeIdentity { + pub fn next_id() -> u64 { + NODE_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + } + + /// Creates a new unique identity at the given location. + pub fn new(location: SourceLocation) -> Identity { + Rc::new(NodeIdentity { + id: Self::next_id(), + location: Some(location), + }) + } + + /// Creates a new unique identity without location metadata (for synthetic nodes). + pub fn anonymous() -> Identity { + Rc::new(NodeIdentity { + id: Self::next_id(), + location: None, + }) + } +} + /// Interned string identifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Keyword(pub u32);