toml_parse/tkn_tree/
syntax.rs

1use rowan::{GreenNode, GreenNodeBuilder};
2
3use super::err::TomlResult;
4use super::kinds::TomlKind::{self, *};
5use super::parse_tkns::Tokenizer;
6use super::walk::{walk, walk_tokens};
7
8pub type SyntaxNode = rowan::SyntaxNode<TomlLang>;
9pub type SyntaxToken = rowan::SyntaxToken<TomlLang>;
10pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
11
12pub trait SyntaxNodeExtTrait {
13    /// walks tokens collecting each tokens text into a final String.
14    fn token_text(&self) -> String;
15    /// `rowan::SyntaxNode` by default only compares pointer equality
16    /// this method addition allows comparison of every token, the same
17    /// file parsed multiple times will return true, with pointer eq
18    /// this would be false.
19    fn deep_eq(&self, other: &Self) -> bool;
20}
21
22impl From<TomlKind> for rowan::SyntaxKind {
23    fn from(kind: TomlKind) -> Self {
24        Self(kind as u16)
25    }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct TomlLang;
30impl rowan::Language for TomlLang {
31    type Kind = TomlKind;
32    fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
33        assert!(raw.0 <= Root as u16);
34        unsafe { std::mem::transmute::<u16, TomlKind>(raw.0) }
35    }
36    fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
37        kind.into()
38    }
39}
40
41impl SyntaxNodeExtTrait for SyntaxNode {
42    fn token_text(&self) -> String {
43        walk_tokens(self).fold(String::default(), |mut s, tkn| {
44            s.push_str(tkn.text());
45            s
46        })
47    }
48
49    fn deep_eq(&self, other: &Self) -> bool {
50        for (a, b) in walk(self).zip(walk(other)) {
51            match (&a, &b) {
52                (SyntaxElement::Node(n1), SyntaxElement::Node(n2)) => {
53                    if n1.token_text() != n2.token_text() {
54                        return false;
55                    }
56                }
57                (SyntaxElement::Token(t1), SyntaxElement::Token(t2)) => {
58                    if t1.text() != t2.text() {
59                        return false;
60                    }
61                }
62                (_, _) => return false,
63            }
64            if a.kind() != b.kind() {
65                return false;
66            }
67        }
68        true
69    }
70}
71
72pub struct ParsedToml {
73    green: rowan::GreenNode,
74}
75
76impl ParsedToml {
77    pub fn syntax(&self) -> SyntaxNode {
78        SyntaxNode::new_root(self.green.clone())
79    }
80}
81
82pub struct Parser {
83    /// the in-progress tree.
84    pub(crate) builder: GreenNodeBuilder<'static>,
85}
86
87impl Default for Parser {
88    fn default() -> Self {
89        Parser::new()
90    }
91}
92
93impl Parser {
94    pub fn new() -> Parser {
95        Self {
96            builder: GreenNodeBuilder::new(),
97        }
98    }
99    pub fn parse(self) -> TomlResult<ParsedToml> {
100        let green: GreenNode = self.builder.finish();
101        // Construct a `SyntaxNode` from `GreenNode`,
102        // Since we only want valid toml errors cause a bubble up
103        // failure, not passed along in the tree as they can be.
104        Ok(ParsedToml { green })
105    }
106}
107
108/// Parses the input into a [`Result<ParsedToml>`][ParsedToml].
109///
110/// This contains a [`GreenNode`][rowan::GreenNode] and
111/// by calling `.syntax()` on `ParsedToml` you get the `TomlKind::Root`
112/// [`SyntaxNode`][rowan::SyntaxNode].
113///
114/// # Examples
115/// ```
116/// use toml_parse::{parse_it, TomlKind};
117///
118/// let toml =
119/// "[valid]
120/// toml = \"stuff\"
121/// ";
122///
123/// let root_node = parse_it(toml).unwrap().syntax();
124/// assert_eq!(root_node.first_child().unwrap().kind(), TomlKind::Table)
125/// ```
126pub fn parse_it(input: &str) -> TomlResult<ParsedToml> {
127    let parse_builder = Parser::new();
128    let parsed = Tokenizer::parse(input, parse_builder)?;
129    parsed.parse()
130}