elm_ast/node.rs
1use std::hash::{Hash, Hasher};
2
3use crate::comment::Comment;
4use crate::span::Span;
5
6/// A syntax node with source span information attached.
7///
8/// This is the universal wrapper for AST nodes, analogous to:
9/// - `A.Located` in elm/compiler
10/// - `Node` in stil4m/elm-syntax
11/// - `Spanned` in many Rust parser crates
12///
13/// Each node carries optional leading comments — comments that appeared
14/// immediately before this node in source. This enables round-tripping
15/// of comments inside expressions (let-in, case-of, etc.).
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct Spanned<T> {
19 pub span: Span,
20 pub value: T,
21 /// Leading comments that appeared immediately before this node in source.
22 #[cfg_attr(
23 feature = "serde",
24 serde(default, skip_serializing_if = "Vec::is_empty")
25 )]
26 pub comments: Vec<Spanned<Comment>>,
27}
28
29// Manual Hash: exclude comments so hashing is based on span + value only.
30impl<T: Hash> Hash for Spanned<T> {
31 fn hash<H: Hasher>(&self, state: &mut H) {
32 self.span.hash(state);
33 self.value.hash(state);
34 }
35}
36
37impl<T> Spanned<T> {
38 /// Create a new spanned node with no leading comments.
39 pub fn new(span: Span, value: T) -> Self {
40 Self {
41 span,
42 value,
43 comments: Vec::new(),
44 }
45 }
46
47 /// Create a spanned node with a dummy span (for synthesized/constructed nodes).
48 pub fn dummy(value: T) -> Self {
49 Self {
50 span: Span::dummy(),
51 value,
52 comments: Vec::new(),
53 }
54 }
55
56 /// Map the inner value, preserving the span and comments.
57 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
58 Spanned {
59 span: self.span,
60 value: f(self.value),
61 comments: self.comments,
62 }
63 }
64
65 /// Attach leading comments to this node.
66 pub fn with_comments(mut self, comments: Vec<Spanned<Comment>>) -> Self {
67 self.comments = comments;
68 self
69 }
70
71 /// Get a reference to the inner value.
72 pub fn inner(&self) -> &T {
73 &self.value
74 }
75
76 /// Get a mutable reference to the inner value.
77 pub fn inner_mut(&mut self) -> &mut T {
78 &mut self.value
79 }
80
81 /// Unwrap the spanned node, discarding the span.
82 pub fn into_inner(self) -> T {
83 self.value
84 }
85}