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}