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, Span};
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    injections: Vec<Injection<'a>>,
57}
58
59impl<'a> Parse<'a> {
60    pub(crate) fn new(
61        language: &'a Language,
62        source: &'a str,
63        tree: Node<Kind>,
64        diagnostics: Vec<Diagnostic>,
65        injections: Vec<Injection<'a>>,
66    ) -> Self {
67        Self {
68            language,
69            source,
70            tree,
71            diagnostics,
72            injections,
73        }
74    }
75
76    /// The injections found in the source (format 2), in source order: the
77    /// ranges `[injections]` and `embedded` tokens with `parse` name. A
78    /// self-injection carries its own [`tree`](Injection::tree), parsed with
79    /// this language; an injection of another language, or one the sketch
80    /// leaves to the editor, carries only its range.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use lang_forge::Language;
86    ///
87    /// let lang = Language::from_lsf(r#"
88    ///     [sketch]
89    ///     format = 2
90    ///     [language]
91    ///     name = "tpl"
92    ///     version = "1.0.0"
93    ///     [lexer.strings.QUOTED]
94    ///     open = '"'
95    ///     embedded = [{ token = "REF", regex = '\$[a-z]+', parse = "ref" }]
96    ///     [rules]
97    ///     file = "QUOTED*"
98    ///     ref = "'$' IDENT"
99    ///     "#)?;
100    /// let parse = lang.parse(r#""hello $name""#);
101    /// let injection = &parse.injections()[0];
102    /// assert_eq!(injection.language(), "self");
103    /// assert_eq!(&parse.source()[injection.span().start().to_usize()..injection.span().end().to_usize()], "$name");
104    /// let tree = injection.tree().expect("a self-injection is parsed");
105    /// assert_eq!(*tree.kind(), lang.kind("ref").expect("a rule"));
106    /// # Ok::<(), lang_forge::Error>(())
107    /// ```
108    #[must_use]
109    pub fn injections(&self) -> &[Injection<'a>] {
110        &self.injections
111    }
112
113    /// The root of the syntax tree: a node of the start rule's kind that
114    /// covers the whole source.
115    ///
116    /// Walk it with the [`syntax_lang::Node`] API — [`children`](Node::children),
117    /// [`descendants`](Node::descendants), [`tokens`](Node::tokens) — and slice
118    /// the source with [`Node::text`]. Every walk is iterative, so even very
119    /// deep trees are safe to traverse.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use lang_forge::Language;
125    ///
126    /// let lang = Language::from_lsf(
127    ///     "[language]\nname = \"list\"\n[rules]\nlist = \"'[' (NUMBER (',' NUMBER)*)? ']'\"\n",
128    /// )?;
129    /// let parse = lang.parse("[1, 2, 3]");
130    /// let number = lang.kind("NUMBER").expect("built in");
131    /// let numbers: Vec<&str> = parse
132    ///     .tree()
133    ///     .tokens()
134    ///     .filter(|t| *t.kind() == number)
135    ///     .map(|t| &parse.source()[t.span().start().to_usize()..t.span().end().to_usize()])
136    ///     .collect();
137    /// assert_eq!(numbers, ["1", "2", "3"]);
138    /// # Ok::<(), lang_forge::Error>(())
139    /// ```
140    #[inline]
141    #[must_use]
142    pub fn tree(&self) -> &Node<Kind> {
143        &self.tree
144    }
145
146    /// The source text that was parsed. Spans in the tree and the
147    /// diagnostics are byte offsets into it.
148    #[inline]
149    #[must_use]
150    pub fn source(&self) -> &'a str {
151        self.source
152    }
153
154    /// The language that parsed the source, for looking kinds up and naming
155    /// them.
156    #[inline]
157    #[must_use]
158    pub fn language(&self) -> &'a Language {
159        self.language
160    }
161
162    /// The problems found, in source order: lexical errors (unexpected
163    /// characters, unterminated strings and comments, malformed numbers),
164    /// syntax errors, and anything capability passes have [`report`]ed.
165    ///
166    /// Spans are byte offsets into [`source`](Self::source). Add the source to
167    /// a fresh [`SourceMap`](diag_lang::SourceMap) and
168    /// [`Renderer`](diag_lang::Renderer) draws each one under the line at
169    /// fault.
170    ///
171    /// [`report`]: Self::report
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use lang_forge::Language;
177    /// use lang_forge::diag_lang::{Renderer, SourceMap};
178    ///
179    /// let lang = Language::from_lsf(
180    ///     "[language]\nname = \"calls\"\n[rules]\nfile = \"call*\"\ncall = \"IDENT '(' ')' ';'\"\n",
181    /// )?;
182    /// let source = "start();\nstop(;\n";
183    /// let parse = lang.parse(source);
184    ///
185    /// let mut map = SourceMap::new();
186    /// map.add("main.calls", source).expect("fits");
187    /// let report = Renderer::new().render(&parse.diagnostics()[0], &map);
188    /// assert!(report.contains("expected `)`, found `;`"));
189    /// assert!(report.contains("main.calls:2:6"));
190    /// # Ok::<(), lang_forge::Error>(())
191    /// ```
192    #[inline]
193    #[must_use]
194    pub fn diagnostics(&self) -> &[Diagnostic] {
195        &self.diagnostics
196    }
197
198    /// Whether any diagnostic is an error (rather than a warning or a note a
199    /// capability pass added).
200    #[must_use]
201    pub fn has_errors(&self) -> bool {
202        self.diagnostics
203            .iter()
204            .any(|d| d.severity() == Severity::Error)
205    }
206
207    /// Adds a diagnostic — the way a capability pass reports what it finds.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use lang_forge::Language;
213    /// use lang_forge::diag_lang::{Diagnostic, Label, Severity};
214    ///
215    /// let lang = Language::from_lsf("[language]\nname = \"n\"\n[rules]\nn = \"NUMBER\"\n")?;
216    /// let mut parse = lang.parse("42");
217    /// let span = parse.tree().span();
218    /// parse.report(Diagnostic::new(Severity::Warning, "the answer", Label::unlabelled(span)));
219    ///
220    /// assert_eq!(parse.diagnostics().len(), 1);
221    /// assert!(!parse.has_errors());
222    /// # Ok::<(), lang_forge::Error>(())
223    /// ```
224    pub fn report(&mut self, diagnostic: Diagnostic) {
225        self.diagnostics.push(diagnostic);
226    }
227
228    /// Takes the tree, dropping the diagnostics and the borrows.
229    #[inline]
230    #[must_use]
231    pub fn into_tree(self) -> Node<Kind> {
232        self.tree
233    }
234
235    /// The tree as indented text, one node or token per line: each line is a
236    /// kind name and a byte range, and each token also shows its text.
237    ///
238    /// Meant for tests, snapshots, and debugging a grammar. The format is
239    /// stable within a major version.
240    ///
241    /// # Examples
242    ///
243    /// ```
244    /// use lang_forge::Language;
245    ///
246    /// let lang = Language::from_lsf(
247    ///     "[language]\nname = \"let\"\n[rules]\nstmt = \"'let' IDENT '=' NUMBER\"\n",
248    /// )?;
249    /// assert_eq!(
250    ///     lang.parse("let x = 1").dump(),
251    ///     "stmt@0..9\n  \
252    ///        let@0..3 \"let\"\n  \
253    ///        WHITESPACE@3..4 \" \"\n  \
254    ///        IDENT@4..5 \"x\"\n  \
255    ///        WHITESPACE@5..6 \" \"\n  \
256    ///        =@6..7 \"=\"\n  \
257    ///        WHITESPACE@7..8 \" \"\n  \
258    ///        NUMBER@8..9 \"1\"\n",
259    /// );
260    /// # Ok::<(), lang_forge::Error>(())
261    /// ```
262    #[must_use]
263    pub fn dump(&self) -> String {
264        let mut out = String::new();
265        let mut stack: Vec<(&Element<Kind>, usize)> = Vec::new();
266        let line = |out: &mut String, depth: usize, kind: Kind, start: u32, end: u32| {
267            for _ in 0..depth {
268                out.push_str("  ");
269            }
270            // A field label (format 2) prefixes the element it labels.
271            if let Some(label) = kind.label().and_then(|l| self.language.label_name(l)) {
272                out.push_str(label);
273                out.push(':');
274            }
275            // Writing to a `String` cannot fail.
276            let _ = write!(out, "{}@{start}..{end}", self.language.kind_name(kind));
277        };
278        let span = self.tree.span();
279        line(
280            &mut out,
281            0,
282            *self.tree.kind(),
283            span.start().to_u32(),
284            span.end().to_u32(),
285        );
286        out.push('\n');
287        stack.extend(self.tree.children().map(|c| (c, 1)));
288        stack.reverse();
289        while let Some((element, depth)) = stack.pop() {
290            let span = element.span();
291            line(
292                &mut out,
293                depth,
294                *element.kind(),
295                span.start().to_u32(),
296                span.end().to_u32(),
297            );
298            match element {
299                Element::Node(node) => {
300                    let from = stack.len();
301                    stack.extend(node.children().map(|c| (c, depth + 1)));
302                    stack[from..].reverse();
303                }
304                Element::Token(_) => {
305                    let text = &self.source[span.start().to_usize()..span.end().to_usize()];
306                    // Writing to a `String` cannot fail.
307                    let _ = write!(out, " {text:?}");
308                }
309            }
310            out.push('\n');
311        }
312        out
313    }
314}
315
316/// A range of a parsed source covered by an injection (LSF2 §12): another
317/// language's content, or a token of this language whose text has structure
318/// of its own. See [`Parse::injections`].
319///
320/// # Examples
321///
322/// ```
323/// use lang_forge::Language;
324///
325/// // `$name.field` inside a string is parsed by the `path` rule.
326/// let lang = Language::from_lsf(
327///     "[sketch]\nformat = 2\n[language]\nname = \"s\"\nversion = \"1.0.0\"\n\
328///      [lexer.strings.DQ]\nopen = '\"'\nembedded = [{ token = \"DQ_VAR\", regex = '\\$[a-z]+(\\.[a-z]+)*', parse = \"path\" }]\n\
329///      interpolate = [{ open = \"{\", close = \"}\", rule = \"path\" }]\n\
330///      [lexer.tokens]\nVAR = { regex = '\\$[a-z]+' }\n\
331///      [rules]\nfile = \"DQ*\"\npath = \"VAR ('.' IDENT)*\"\n",
332/// )?;
333/// let src = "\"hello $user.name\"";
334/// let parse = lang.parse(src);
335/// let injection = &parse.injections()[0];
336/// assert_eq!((injection.id(), injection.language(), injection.is_editor()), ("DQ_VAR", "self", false));
337/// assert_eq!(injection.span().start().to_usize(), 7);
338/// let tree = injection.tree().expect("a self-injection is parsed");
339/// assert_eq!(lang.kind_name(*tree.kind()), "path");
340/// assert_eq!(tree.text(src), Some("$user.name"));
341/// # Ok::<(), lang_forge::Error>(())
342/// ```
343#[derive(Clone, Debug)]
344pub struct Injection<'a> {
345    id: &'a str,
346    language: &'a str,
347    span: Span,
348    tree: Option<Node<Kind>>,
349    editor: bool,
350}
351
352impl<'a> Injection<'a> {
353    pub(crate) fn new(
354        id: &'a str,
355        language: &'a str,
356        span: Span,
357        tree: Option<Node<Kind>>,
358        editor: bool,
359    ) -> Self {
360        Self {
361            id,
362            language,
363            span,
364            tree,
365            editor,
366        }
367    }
368
369    /// The injection's id from `[injections]`, or the token class's name for
370    /// an `embedded` token with `parse`.
371    #[must_use]
372    pub fn id(&self) -> &'a str {
373        self.id
374    }
375
376    /// The injected language: `"self"`, or the name the sketch gives.
377    #[must_use]
378    pub fn language(&self) -> &'a str {
379        self.language
380    }
381
382    /// The range of the source the injection covers.
383    #[must_use]
384    pub fn span(&self) -> Span {
385        self.span
386    }
387
388    /// The injected tree, for a self-injection; `None` for another language
389    /// or an editor-resolved injection (and for one nested more than 16
390    /// levels deep, which a warning reports).
391    #[must_use]
392    pub fn tree(&self) -> Option<&Node<Kind>> {
393        self.tree.as_ref()
394    }
395
396    /// Whether the sketch leaves this injection to editors
397    /// (`resolve = "editor"`).
398    #[must_use]
399    pub fn is_editor(&self) -> bool {
400        self.editor
401    }
402}