Skip to main content

arity_parser/rd/
ast.rs

1//! Read-only navigation over the source CST, without semantic interpretation.
2
3use rowan::ast::AstNode;
4use smol_str::SmolStr;
5
6use super::syntax::{RdLanguage, SyntaxKind, SyntaxNode, SyntaxToken};
7
8macro_rules! wrapper {
9    ($name:ident, $kind:ident) => {
10        #[doc = concat!("A typed view of an Rd `", stringify!($kind), "` node.")]
11        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
12        pub struct $name(SyntaxNode);
13
14        impl AstNode for $name {
15            type Language = RdLanguage;
16            fn can_cast(kind: SyntaxKind) -> bool {
17                kind == SyntaxKind::$kind
18            }
19            fn cast(node: SyntaxNode) -> std::option::Option<Self> {
20                Self::can_cast(node.kind()).then_some(Self(node))
21            }
22            fn syntax(&self) -> &SyntaxNode {
23                &self.0
24            }
25        }
26    };
27}
28
29wrapper!(Document, ROOT);
30wrapper!(Macro, MACRO);
31wrapper!(Argument, ARGUMENT);
32wrapper!(Group, GROUP);
33wrapper!(Option, OPTION);
34wrapper!(Conditional, CONDITIONAL);
35
36fn token(node: &SyntaxNode, kind: SyntaxKind) -> std::option::Option<SyntaxToken> {
37    node.children_with_tokens()
38        .filter_map(|element| element.into_token())
39        .find(|token| token.kind() == kind)
40}
41
42impl Document {
43    /// Top-level macros in source order, without flattening conditional bodies.
44    pub fn macros(&self) -> impl Iterator<Item = Macro> + use<> {
45        self.0.children().filter_map(Macro::cast)
46    }
47}
48
49impl Macro {
50    /// The original macro name, including its backslash.
51    pub fn name_token(&self) -> std::option::Option<SyntaxToken> {
52        token(&self.0, SyntaxKind::MACRO_NAME)
53    }
54
55    /// The macro name without its leading backslash.
56    pub fn name(&self) -> SmolStr {
57        self.name_token()
58            .map(|token| SmolStr::new(token.text().trim_start_matches('\\')))
59            .unwrap_or_default()
60    }
61
62    /// Supplied arguments, including recovered incomplete arguments.
63    pub fn arguments(&self) -> impl Iterator<Item = Argument> + use<> {
64        self.0.children().filter_map(Argument::cast)
65    }
66
67    /// The supplied bracketed option, if any.
68    pub fn option(&self) -> std::option::Option<Option> {
69        self.0.children().find_map(Option::cast)
70    }
71}
72
73impl Option {
74    /// Original option contents, excluding its opening and closing brackets.
75    pub fn text(&self) -> String {
76        self.0
77            .children_with_tokens()
78            .filter(|element| {
79                !matches!(element.kind(), SyntaxKind::LBRACKET | SyntaxKind::RBRACKET)
80            })
81            .map(|element| element.to_string())
82            .collect()
83    }
84}
85
86impl Conditional {
87    /// The opening `#ifdef` or `#ifndef` token.
88    pub fn directive(&self) -> std::option::Option<SyntaxToken> {
89        token(&self.0, SyntaxKind::DIRECTIVE)
90    }
91
92    /// The source target, including its surrounding whitespace and line ending.
93    pub fn target(&self) -> std::option::Option<SyntaxToken> {
94        token(&self.0, SyntaxKind::TARGET)
95    }
96}