bytebraise_syntax/syntax/ast/
mod.rs

1// This module adapted from Rust Analyzer (https://github.com/rust-analyzer/rust-analyzer), which
2// is under MIT license.
3
4use crate::syntax::syntax_kind::SyntaxKind;
5use std::marker::PhantomData;
6
7use crate::syntax::syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken};
8
9pub mod nodes;
10pub mod quoted_value;
11pub mod tokens;
12
13/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
14/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
15/// the same representation: a pointer to the tree root and a pointer to the
16/// node itself.
17pub trait AstNode {
18    fn can_cast(kind: SyntaxKind) -> bool
19    where
20        Self: Sized;
21
22    fn cast(syntax: SyntaxNode) -> Option<Self>
23    where
24        Self: Sized;
25
26    fn syntax(&self) -> &SyntaxNode;
27
28    fn clone_for_update(&self) -> Self
29    where
30        Self: Sized,
31    {
32        Self::cast(self.syntax().clone_for_update()).unwrap()
33    }
34
35    fn clone_subtree(&self) -> Self
36    where
37        Self: Sized,
38    {
39        Self::cast(self.syntax().clone_subtree()).unwrap()
40    }
41}
42
43/// Like `AstNode`, but wraps tokens rather than interior nodes.
44pub trait AstToken {
45    fn can_cast(token: SyntaxKind) -> bool
46    where
47        Self: Sized;
48
49    fn cast(syntax: SyntaxToken) -> Option<Self>
50    where
51        Self: Sized;
52
53    fn syntax(&self) -> &SyntaxToken;
54
55    fn text(&self) -> &str {
56        self.syntax().text()
57    }
58}
59
60#[macro_export]
61macro_rules! ast_node {
62    ($ast:ident, $kind:ident) => {
63        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
64        #[repr(transparent)]
65        pub struct $ast {
66            pub(crate) syntax: SyntaxNode,
67        }
68        impl $crate::syntax::ast::AstNode for $ast {
69            #[allow(unused)]
70            fn cast(node: SyntaxNode) -> Option<Self> {
71                if node.kind() == SyntaxKind::$kind {
72                    Some(Self { syntax: node })
73                } else {
74                    None
75                }
76            }
77
78            #[allow(unused)]
79            fn can_cast(kind: SyntaxKind) -> bool {
80                kind == SyntaxKind::$kind
81            }
82
83            #[allow(unused)]
84            fn syntax(&self) -> &SyntaxNode {
85                &self.syntax
86            }
87        }
88    };
89}
90
91#[macro_export]
92macro_rules! ast_token {
93    ($ast:ident, $kind:ident) => {
94        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
95        #[repr(transparent)]
96        pub struct $ast {
97            pub(crate) syntax: SyntaxToken,
98        }
99        impl $crate::syntax::ast::AstToken for $ast {
100            #[allow(unused)]
101            fn cast(token: SyntaxToken) -> Option<Self> {
102                if token.kind() == SyntaxKind::$kind {
103                    Some(Self { syntax: token })
104                } else {
105                    None
106                }
107            }
108
109            #[allow(unused)]
110            fn can_cast(kind: SyntaxKind) -> bool {
111                kind == SyntaxKind::$kind
112            }
113
114            #[allow(unused)]
115            fn syntax(&self) -> &SyntaxToken {
116                &self.syntax
117            }
118        }
119    };
120}
121
122/// An iterator over `SyntaxNode` children of a particular AST type.
123#[derive(Debug, Clone)]
124pub struct AstChildren<N> {
125    inner: SyntaxNodeChildren,
126    ph: PhantomData<N>,
127}
128
129impl<N> AstChildren<N> {
130    fn new(parent: &SyntaxNode) -> Self {
131        AstChildren {
132            inner: parent.children(),
133            ph: PhantomData,
134        }
135    }
136}
137
138impl<N: AstNode> Iterator for AstChildren<N> {
139    type Item = N;
140    fn next(&mut self) -> Option<N> {
141        self.inner.find_map(N::cast)
142    }
143}
144
145pub mod support {
146    use super::{AstChildren, AstNode, SyntaxNode};
147    use crate::syntax::ast::AstToken;
148
149    pub(crate) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
150        parent.children().find_map(N::cast)
151    }
152
153    pub(crate) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
154        AstChildren::new(parent)
155    }
156
157    pub(crate) fn token<N: AstToken>(parent: &SyntaxNode) -> Option<N> {
158        parent
159            .children_with_tokens()
160            .filter_map(|it| it.into_token())
161            .find_map(N::cast)
162    }
163}
164
165// fn ast_from_text<N: AstNode>(text: &str) -> N {
166//     let parse = SourceFile::parse(text);
167//     let node = match parse.tree().syntax().descendants().find_map(N::cast) {
168//         Some(it) => it,
169//         None => {
170//             panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text)
171//         }
172//     };
173//     let node = node.clone_subtree();
174//     assert_eq!(node.syntax().text_range().start(), 0.into());
175//     node
176// }