Skip to main content

lang_forge/
parse.rs

1//! [`Parse`]: the result of parsing source text with a forged language.
2
3use alloc::{string::String, vec::Vec};
4use core::fmt::Write as _;
5
6use diag_lang::{Diagnostic, Severity};
7use syntax_lang::{Element, Node};
8
9use crate::{Language, kind::Kind};
10
11/// The result of [`Language::parse`]: a lossless syntax tree and the problems
12/// found while building it.
13///
14/// Parsing never fails. Malformed input still yields a complete tree — missing
15/// pieces are left out, unexpected tokens are wrapped in `ERROR` nodes — and a
16/// [`Diagnostic`] for each problem, so an editor can highlight and navigate a
17/// half-typed file and a compiler can report every syntax error in one pass.
18/// The tree is lossless either way: it covers every byte of the source,
19/// whitespace and comments included.
20///
21/// A `Parse` borrows the language and the source it was made from, so names,
22/// text, and diagnostics can be resolved from it alone. It is also the unit
23/// that capability passes run over; see [`Language::pipeline`].
24///
25/// # Examples
26///
27/// ```
28/// use lang_forge::Language;
29///
30/// let lang = Language::from_lsf(
31///     r#"
32///     [language]
33///     name = "pairs"
34///
35///     [rules]
36///     file = "pair*"
37///     pair = "IDENT '=' NUMBER ';'"
38///     "#,
39/// )?;
40///
41/// let parse = lang.parse("a = 1;\nb = ;\n");
42/// assert!(parse.has_errors());
43/// assert_eq!(parse.diagnostics()[0].message(), "expected a number, found `;`");
44///
45/// // Still a full tree, covering the whole source.
46/// assert_eq!(parse.tree().text(parse.source()), Some("a = 1;\nb = ;\n"));
47/// assert_eq!(parse.tree().child_nodes().count(), 2);
48/// # Ok::<(), lang_forge::Error>(())
49/// ```
50#[derive(Clone, Debug)]
51pub struct Parse<'a> {
52    language: &'a Language,
53    source: &'a str,
54    tree: Node<Kind>,
55    diagnostics: Vec<Diagnostic>,
56}
57
58impl<'a> Parse<'a> {
59    pub(crate) fn new(
60        language: &'a Language,
61        source: &'a str,
62        tree: Node<Kind>,
63        diagnostics: Vec<Diagnostic>,
64    ) -> Self {
65        Self {
66            language,
67            source,
68            tree,
69            diagnostics,
70        }
71    }
72
73    /// The root of the syntax tree: a node of the start rule's kind that
74    /// covers the whole source.
75    ///
76    /// Walk it with the [`syntax_lang::Node`] API — [`children`](Node::children),
77    /// [`descendants`](Node::descendants), [`tokens`](Node::tokens) — and slice
78    /// the source with [`Node::text`]. Every walk is iterative, so even very
79    /// deep trees are safe to traverse.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use lang_forge::Language;
85    ///
86    /// let lang = Language::from_lsf(
87    ///     "[language]\nname = \"list\"\n[rules]\nlist = \"'[' (NUMBER (',' NUMBER)*)? ']'\"\n",
88    /// )?;
89    /// let parse = lang.parse("[1, 2, 3]");
90    /// let number = lang.kind("NUMBER").expect("built in");
91    /// let numbers: Vec<&str> = parse
92    ///     .tree()
93    ///     .tokens()
94    ///     .filter(|t| *t.kind() == number)
95    ///     .map(|t| &parse.source()[t.span().start().to_usize()..t.span().end().to_usize()])
96    ///     .collect();
97    /// assert_eq!(numbers, ["1", "2", "3"]);
98    /// # Ok::<(), lang_forge::Error>(())
99    /// ```
100    #[inline]
101    #[must_use]
102    pub fn tree(&self) -> &Node<Kind> {
103        &self.tree
104    }
105
106    /// The source text that was parsed. Spans in the tree and the
107    /// diagnostics are byte offsets into it.
108    #[inline]
109    #[must_use]
110    pub fn source(&self) -> &'a str {
111        self.source
112    }
113
114    /// The language that parsed the source, for looking kinds up and naming
115    /// them.
116    #[inline]
117    #[must_use]
118    pub fn language(&self) -> &'a Language {
119        self.language
120    }
121
122    /// The problems found, in source order: lexical errors (unexpected
123    /// characters, unterminated strings and comments, malformed numbers),
124    /// syntax errors, and anything capability passes have [`report`]ed.
125    ///
126    /// Spans are byte offsets into [`source`](Self::source). Add the source to
127    /// a fresh [`SourceMap`](diag_lang::SourceMap) and
128    /// [`Renderer`](diag_lang::Renderer) draws each one under the line at
129    /// fault.
130    ///
131    /// [`report`]: Self::report
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use lang_forge::Language;
137    /// use lang_forge::diag_lang::{Renderer, SourceMap};
138    ///
139    /// let lang = Language::from_lsf(
140    ///     "[language]\nname = \"calls\"\n[rules]\nfile = \"call*\"\ncall = \"IDENT '(' ')' ';'\"\n",
141    /// )?;
142    /// let source = "start();\nstop(;\n";
143    /// let parse = lang.parse(source);
144    ///
145    /// let mut map = SourceMap::new();
146    /// map.add("main.calls", source).expect("fits");
147    /// let report = Renderer::new().render(&parse.diagnostics()[0], &map);
148    /// assert!(report.contains("expected `)`, found `;`"));
149    /// assert!(report.contains("main.calls:2:6"));
150    /// # Ok::<(), lang_forge::Error>(())
151    /// ```
152    #[inline]
153    #[must_use]
154    pub fn diagnostics(&self) -> &[Diagnostic] {
155        &self.diagnostics
156    }
157
158    /// Whether any diagnostic is an error (rather than a warning or a note a
159    /// capability pass added).
160    #[must_use]
161    pub fn has_errors(&self) -> bool {
162        self.diagnostics
163            .iter()
164            .any(|d| d.severity() == Severity::Error)
165    }
166
167    /// Adds a diagnostic — the way a capability pass reports what it finds.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use lang_forge::Language;
173    /// use lang_forge::diag_lang::{Diagnostic, Label, Severity};
174    ///
175    /// let lang = Language::from_lsf("[language]\nname = \"n\"\n[rules]\nn = \"NUMBER\"\n")?;
176    /// let mut parse = lang.parse("42");
177    /// let span = parse.tree().span();
178    /// parse.report(Diagnostic::new(Severity::Warning, "the answer", Label::unlabelled(span)));
179    ///
180    /// assert_eq!(parse.diagnostics().len(), 1);
181    /// assert!(!parse.has_errors());
182    /// # Ok::<(), lang_forge::Error>(())
183    /// ```
184    pub fn report(&mut self, diagnostic: Diagnostic) {
185        self.diagnostics.push(diagnostic);
186    }
187
188    /// Takes the tree, dropping the diagnostics and the borrows.
189    #[inline]
190    #[must_use]
191    pub fn into_tree(self) -> Node<Kind> {
192        self.tree
193    }
194
195    /// The tree as indented text, one node or token per line: each line is a
196    /// kind name and a byte range, and each token also shows its text.
197    ///
198    /// Meant for tests, snapshots, and debugging a grammar. The format is
199    /// stable within a major version.
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// use lang_forge::Language;
205    ///
206    /// let lang = Language::from_lsf(
207    ///     "[language]\nname = \"let\"\n[rules]\nstmt = \"'let' IDENT '=' NUMBER\"\n",
208    /// )?;
209    /// assert_eq!(
210    ///     lang.parse("let x = 1").dump(),
211    ///     "stmt@0..9\n  \
212    ///        let@0..3 \"let\"\n  \
213    ///        WHITESPACE@3..4 \" \"\n  \
214    ///        IDENT@4..5 \"x\"\n  \
215    ///        WHITESPACE@5..6 \" \"\n  \
216    ///        =@6..7 \"=\"\n  \
217    ///        WHITESPACE@7..8 \" \"\n  \
218    ///        NUMBER@8..9 \"1\"\n",
219    /// );
220    /// # Ok::<(), lang_forge::Error>(())
221    /// ```
222    #[must_use]
223    pub fn dump(&self) -> String {
224        let mut out = String::new();
225        let mut stack: Vec<(&Element<Kind>, usize)> = Vec::new();
226        let line = |out: &mut String, depth: usize, kind: Kind, start: u32, end: u32| {
227            for _ in 0..depth {
228                out.push_str("  ");
229            }
230            // Writing to a `String` cannot fail.
231            let _ = write!(out, "{}@{start}..{end}", self.language.kind_name(kind));
232        };
233        let span = self.tree.span();
234        line(
235            &mut out,
236            0,
237            *self.tree.kind(),
238            span.start().to_u32(),
239            span.end().to_u32(),
240        );
241        out.push('\n');
242        stack.extend(self.tree.children().map(|c| (c, 1)));
243        stack.reverse();
244        while let Some((element, depth)) = stack.pop() {
245            let span = element.span();
246            line(
247                &mut out,
248                depth,
249                *element.kind(),
250                span.start().to_u32(),
251                span.end().to_u32(),
252            );
253            match element {
254                Element::Node(node) => {
255                    let from = stack.len();
256                    stack.extend(node.children().map(|c| (c, depth + 1)));
257                    stack[from..].reverse();
258                }
259                Element::Token(_) => {
260                    let text = &self.source[span.start().to_usize()..span.end().to_usize()];
261                    // Writing to a `String` cannot fail.
262                    let _ = write!(out, " {text:?}");
263                }
264            }
265            out.push('\n');
266        }
267        out
268    }
269}