Skip to main content

meta_language/grammar/emit/
antlr.rs

1//! ANTLR v4 grammar emitter.
2
3use std::collections::BTreeSet;
4use std::fmt::Write as _;
5
6use super::structural::{
7    NamePlan, Precedence, Rendered, ascii_identifier, case_variants, collect_references,
8    join_choice, join_sequence, lower_repeat, negated_lookahead_set, repeat_bounds, wrap,
9};
10use super::{EmitReport, GrammarEmitError, finish_lines, unsupported_error};
11use crate::grammar::{
12    CharClassItem, Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleKind, import_antlr,
13};
14
15const FORMAT: GrammarFormat = GrammarFormat::Antlr;
16const LABEL: &str = "ANTLR";
17
18/// Lower-case words ANTLR reserves, which cannot name parser rules or labels.
19const RESERVED_WORDS: &[&str] = &[
20    "catch", "channels", "finally", "fragment", "grammar", "import", "lexer", "locals", "mode",
21    "options", "parser", "returns", "throws", "tokens",
22];
23
24/// Token names ANTLR predefines, which cannot name lexer rules.
25const RESERVED_TOKENS: &[&str] = &["EOF"];
26
27/// Emits a combined ANTLR v4 grammar.
28///
29/// Normal rules become parser rules (lower-case first letter), token and
30/// atomic rules become lexer rules (upper-case first letter) and silent rules
31/// become `fragment` lexer rules. Rule names are adjusted deterministically to
32/// ANTLR's case conventions and reserved words; every rename is recorded in
33/// [`EmitReport::lossy`] as `ANTLR renamed rule "a" to "b"`.
34///
35/// Ordered choice is written as ANTLR alternation, negative lookahead as the
36/// `~` complement (a lookahead over a set followed by any character becomes a
37/// complemented set), case-insensitive literals as per-letter sets, counted
38/// repetition as copies with nested optionals, and captures as `label=`
39/// element labels. Positive lookahead and anonymous captures have no ANTLR
40/// form and are dropped. Every lowering and drop is recorded in the report,
41/// so `import_antlr` of the output reproduces the grammar modulo the recorded
42/// notes, and emitting the re-imported grammar reproduces the same text.
43///
44/// Rule documentation is written as comments above each rule; a trailing
45/// `-> command` documentation part of a lexer rule, as produced by
46/// `import_antlr`, is written back as the lexer command.
47///
48/// # Errors
49///
50/// Returns [`GrammarEmitError::Unsupported`] for empty non-negated character
51/// classes, descending character ranges, repetitions whose maximum is below
52/// their minimum, and counted repetitions that would expand to more than 256
53/// copies of their operand.
54pub fn emit_antlr(grammar: &Grammar) -> Result<(String, EmitReport), GrammarEmitError> {
55    AntlrEmitter::new(grammar).emit()
56}
57
58#[derive(Debug)]
59struct AntlrEmitter<'grammar> {
60    grammar: &'grammar Grammar,
61    names: NamePlan,
62    report: EmitReport,
63    foreign: bool,
64    rule: String,
65}
66
67impl<'grammar> AntlrEmitter<'grammar> {
68    fn new(grammar: &'grammar Grammar) -> Self {
69        let mut report = EmitReport::default();
70        let candidates = grammar
71            .rules()
72            .iter()
73            .map(|rule| rule_candidate(&rule.name, is_lexer_kind(rule.kind)))
74            .collect::<Vec<_>>();
75        let names = NamePlan::new(
76            grammar,
77            LABEL,
78            &candidates,
79            &BTreeSet::new(),
80            reference_candidate,
81            &mut report,
82        );
83        Self {
84            grammar,
85            names,
86            report,
87            foreign: grammar.source_format() != Some(FORMAT),
88            rule: String::new(),
89        }
90    }
91
92    fn emit(mut self) -> Result<(String, EmitReport), GrammarEmitError> {
93        let rules = self.grammar.rules();
94        let order = self.emission_order();
95        self.note_start(&order);
96
97        let has_parser_rules = rules.iter().any(|rule| rule.kind == RuleKind::Normal);
98        let grammar_name = order.first().map_or_else(String::new, |&index| {
99            pascal_case(self.names.rule_name(index))
100        });
101        let grammar_name = if grammar_name.starts_with(|c: char| c.is_ascii_alphabetic()) {
102            grammar_name
103        } else {
104            "Grammar".to_string()
105        };
106        let mut lines = vec![if has_parser_rules {
107            format!("grammar {grammar_name};")
108        } else {
109            format!("lexer grammar {grammar_name};")
110        }];
111        if !order.is_empty() {
112            lines.push(String::new());
113        }
114        for index in order {
115            self.emit_rule(index, &mut lines)?;
116        }
117        Ok((finish_lines(&lines), self.report))
118    }
119
120    /// Orders rules so that `import_antlr` picks the same start rule: the
121    /// importer starts at the first parser rule, or the first rule when there
122    /// are no parser rules.
123    fn emission_order(&self) -> Vec<usize> {
124        let rules = self.grammar.rules();
125        let start_index = self
126            .grammar
127            .start()
128            .and_then(|start| rules.iter().position(|rule| rule.name == start));
129        let effective = match start_index {
130            Some(index) if rules[index].kind == RuleKind::Normal => Some(index),
131            _ => rules
132                .iter()
133                .position(|rule| rule.kind == RuleKind::Normal)
134                .or(start_index),
135        };
136        let mut order = effective.into_iter().collect::<Vec<_>>();
137        order.extend((0..rules.len()).filter(|&index| Some(index) != effective));
138        order
139    }
140
141    fn note_start(&mut self, order: &[usize]) {
142        let Some(start) = self.grammar.start() else {
143            return;
144        };
145        let Some(&first) = order.first() else {
146            return;
147        };
148        let expected = self.names.resolve(start).to_string();
149        let actual = self.names.rule_name(first).to_string();
150        if expected != actual {
151            self.report.add_lossy(format!(
152                "ANTLR re-imports start rule as {actual:?} instead of {expected:?}, \
153                 because ANTLR starts at the first parser rule"
154            ));
155        }
156    }
157
158    fn emit_rule(&mut self, index: usize, lines: &mut Vec<String>) -> Result<(), GrammarEmitError> {
159        let rule = &self.grammar.rules()[index];
160        let name = self.names.rule_name(index).to_string();
161        self.rule.clone_from(&name);
162        let lexer = is_lexer_kind(rule.kind);
163        self.note_kind(rule, &name);
164
165        let doc = plan_doc(rule.doc(), lexer);
166        if doc.reimported.as_deref() != rule.doc() {
167            self.report.add_lossy(format!(
168                "ANTLR re-imports the documentation of rule {name:?} as {:?}",
169                doc.reimported
170            ));
171        }
172        lines.extend(doc.comments);
173
174        let body = self.render(rule.expr())?.0;
175        let mut line = if rule.kind == RuleKind::Silent {
176            format!("fragment {name} :")
177        } else {
178            format!("{name} :")
179        };
180        if !body.is_empty() {
181            line.push(' ');
182            line.push_str(&body);
183        }
184        if let Some(command) = doc.command {
185            line.push(' ');
186            line.push_str(&command);
187        }
188        line.push_str(" ;");
189        lines.push(line);
190
191        if self.foreign {
192            self.note_rule_placement(rule, &name, lexer);
193        }
194        Ok(())
195    }
196
197    fn note_kind(&mut self, rule: &GrammarRule, name: &str) {
198        match rule.kind {
199            RuleKind::Atomic => self.report.add_lossy(format!(
200                "ANTLR emitted atomic rule {:?} as lexer rule {name:?}, which re-imports as a token rule",
201                rule.name
202            )),
203            RuleKind::Silent if self.foreign => self.report.add_lossy(format!(
204                "ANTLR emitted silent rule {:?} as lexer fragment {name:?}",
205                rule.name
206            )),
207            RuleKind::Normal | RuleKind::Silent | RuleKind::Token => {}
208        }
209    }
210
211    /// Records rules that real ANTLR would reject: lexer rules referring to
212    /// parser rules, parser rules referring to fragments, and parser rules
213    /// using character-level constructs.
214    fn note_rule_placement(&mut self, rule: &GrammarRule, name: &str, lexer: bool) {
215        let mut references = Vec::new();
216        collect_references(rule.expr(), &mut references);
217        for reference in references {
218            let Some(target) = self.grammar.rule(reference) else {
219                continue;
220            };
221            let emitted = self.names.resolve(reference).to_string();
222            if lexer && target.kind == RuleKind::Normal {
223                self.report.add_lossy(format!(
224                    "ANTLR lexer rule {name:?} references parser rule {emitted:?}"
225                ));
226            } else if !lexer && target.kind == RuleKind::Silent {
227                self.report.add_lossy(format!(
228                    "ANTLR parser rule {name:?} references lexer fragment {emitted:?}"
229                ));
230            }
231        }
232        if !lexer && uses_character_constructs(rule.expr()) {
233            self.report.add_lossy(format!(
234                "ANTLR parser rule {name:?} uses character-level constructs that ANTLR matches only in lexer rules"
235            ));
236        }
237    }
238
239    fn note(&mut self, message: &str) {
240        let note = format!("ANTLR {message} in rule {:?}", self.rule);
241        self.report.add_lossy(note);
242    }
243
244    fn render(&mut self, expr: &GrammarExpr) -> Result<Rendered, GrammarEmitError> {
245        Ok(match expr {
246            GrammarExpr::Empty => (String::new(), Precedence::Sequence),
247            GrammarExpr::Terminal(value) => {
248                if value.is_empty() {
249                    self.note("keeps an empty literal, which the ANTLR tool rejects,");
250                }
251                (quote(value), Precedence::Atom)
252            }
253            GrammarExpr::TerminalInsensitive(value) => self.render_insensitive(value)?,
254            GrammarExpr::CharRange(start, end) => {
255                if start > end {
256                    return Err(unsupported_error(
257                        FORMAT,
258                        format!("descending character range {start:?}..{end:?}"),
259                    ));
260                }
261                (
262                    format!("{}..{}", quote(&start.to_string()), quote(&end.to_string())),
263                    Precedence::Atom,
264                )
265            }
266            GrammarExpr::CharClass { negated, items } => self.render_class(*negated, items)?,
267            GrammarExpr::AnyChar => (".".to_string(), Precedence::Atom),
268            GrammarExpr::NonTerminal(name) => {
269                (self.names.resolve(name).to_string(), Precedence::Atom)
270            }
271            GrammarExpr::Choice {
272                ordered,
273                alternatives,
274            } => {
275                if *ordered && alternatives.len() > 1 {
276                    self.note("treats ordered choice as unordered choice");
277                }
278                let rendered = alternatives
279                    .iter()
280                    .map(|alternative| self.render(alternative))
281                    .collect::<Result<Vec<_>, _>>()?;
282                join_choice(rendered)
283            }
284            GrammarExpr::Sequence(items) => self.render_sequence(items)?,
285            GrammarExpr::Optional(inner) => self.render_postfix(inner, "?")?,
286            GrammarExpr::ZeroOrMore(inner) => self.render_postfix(inner, "*")?,
287            GrammarExpr::OneOrMore(inner) => self.render_postfix(inner, "+")?,
288            GrammarExpr::Repeat { expr, min, max } => {
289                let lowered = lower_repeat(FORMAT, expr, *min, *max)?;
290                self.note(&format!(
291                    "expanded counted repetition {}",
292                    repeat_bounds(*min, *max)
293                ));
294                self.render(&lowered)?
295            }
296            GrammarExpr::And(_) => {
297                self.note("dropped positive lookahead");
298                (String::new(), Precedence::Sequence)
299            }
300            GrammarExpr::Not(inner) => self.render_not(inner)?,
301            GrammarExpr::Capture { label, expr } => self.render_capture(label.as_deref(), expr)?,
302            GrammarExpr::Feature(feature) => {
303                return Err(unsupported_error(GrammarFormat::Antlr, feature.head()));
304            }
305        })
306    }
307
308    fn render_sequence(&mut self, items: &[GrammarExpr]) -> Result<Rendered, GrammarEmitError> {
309        let mut rendered = Vec::with_capacity(items.len());
310        let mut index = 0;
311        while index < items.len() {
312            if self.foreign
313                && let Some((negated, class)) = negated_lookahead_set(items, index)
314            {
315                self.note(
316                    "lowered negative lookahead followed by any character to a complemented set",
317                );
318                rendered.push(render_set(negated, &class));
319                index += 2;
320                continue;
321            }
322            rendered.push(self.render(&items[index])?);
323            index += 1;
324        }
325        Ok(join_sequence(rendered))
326    }
327
328    fn render_postfix(
329        &mut self,
330        inner: &GrammarExpr,
331        operator: &str,
332    ) -> Result<Rendered, GrammarEmitError> {
333        let operand = self.render(inner)?;
334        Ok((
335            format!("{}{operator}", wrap(operand, Precedence::Prefix)),
336            Precedence::Postfix,
337        ))
338    }
339
340    fn render_not(&mut self, inner: &GrammarExpr) -> Result<Rendered, GrammarEmitError> {
341        if matches!(inner, GrammarExpr::CharClass { negated: false, .. }) {
342            self.note("emitted negative lookahead over a character set as a complemented set");
343        } else if self.foreign {
344            self.note(
345                "emitted negative lookahead as the `~` complement, which consumes one symbol",
346            );
347        }
348        let operand = self.render(inner)?;
349        Ok((
350            format!("~{}", wrap(operand, Precedence::Atom)),
351            Precedence::Prefix,
352        ))
353    }
354
355    fn render_capture(
356        &mut self,
357        label: Option<&str>,
358        inner: &GrammarExpr,
359    ) -> Result<Rendered, GrammarEmitError> {
360        let Some(label) = label else {
361            self.note("dropped anonymous capture");
362            return self.render(inner);
363        };
364        if label == "non_greedy"
365            && matches!(
366                inner,
367                GrammarExpr::Optional(_) | GrammarExpr::ZeroOrMore(_) | GrammarExpr::OneOrMore(_)
368            )
369        {
370            let (text, _precedence) = self.render(inner)?;
371            return Ok((format!("{text}?"), Precedence::Postfix));
372        }
373        let emitted = label_candidate(label);
374        if emitted != label {
375            self.note(&format!("renamed capture label {label:?} to {emitted:?}"));
376        }
377        if label == "regex"
378            && self.grammar.source_format() == Some(GrammarFormat::Lark)
379            && let GrammarExpr::Terminal(pattern) = inner
380        {
381            self.note(&format!(
382                "emitted regex /{pattern}/ as a literal labelled `regex`; regex semantics are not translated"
383            ));
384        }
385        let operand = self.render(inner)?;
386        Ok((
387            format!("{emitted}={}", wrap(operand, Precedence::Postfix)),
388            Precedence::Labeled,
389        ))
390    }
391
392    fn render_insensitive(&mut self, value: &str) -> Result<Rendered, GrammarEmitError> {
393        if value.is_empty() {
394            return self.render(&GrammarExpr::Terminal(String::new()));
395        }
396        let mut items = Vec::new();
397        let mut literal = String::new();
398        for character in value.chars() {
399            if let Some(variants) = case_variants(character) {
400                if !literal.is_empty() {
401                    items.push(GrammarExpr::Terminal(std::mem::take(&mut literal)));
402                }
403                items.push(GrammarExpr::CharClass {
404                    negated: false,
405                    items: variants.into_iter().map(CharClassItem::Char).collect(),
406                });
407            } else {
408                literal.push(character);
409            }
410        }
411        if !literal.is_empty() {
412            items.push(GrammarExpr::Terminal(literal));
413        }
414        self.note(&format!(
415            "expanded case-insensitive literal {value:?} to per-letter character sets"
416        ));
417        let lowered = if items.len() == 1 {
418            items.remove(0)
419        } else {
420            GrammarExpr::Sequence(items)
421        };
422        self.render(&lowered)
423    }
424
425    fn render_class(
426        &mut self,
427        negated: bool,
428        items: &[CharClassItem],
429    ) -> Result<Rendered, GrammarEmitError> {
430        if items.is_empty() {
431            if negated {
432                self.note("emitted an empty negated character class as `.`");
433                return Ok((".".to_string(), Precedence::Atom));
434            }
435            return Err(unsupported_error(FORMAT, "empty character class"));
436        }
437        if let Some(CharClassItem::Range(start, end)) = items
438            .iter()
439            .find(|item| matches!(item, CharClassItem::Range(start, end) if start > end))
440        {
441            return Err(unsupported_error(
442                FORMAT,
443                format!("descending character class range {start:?}-{end:?}"),
444            ));
445        }
446        Ok(render_set(negated, items))
447    }
448}
449
450const fn is_lexer_kind(kind: RuleKind) -> bool {
451    !matches!(kind, RuleKind::Normal)
452}
453
454/// Returns ANTLR's conventional spelling of a rule name.
455fn rule_candidate(name: &str, lexer: bool) -> String {
456    let mut output = ascii_identifier(name);
457    match output.chars().next() {
458        Some(first) if first.is_ascii_alphabetic() => {
459            let fixed = if lexer {
460                first.to_ascii_uppercase()
461            } else {
462                first.to_ascii_lowercase()
463            };
464            output.replace_range(..1, fixed.encode_utf8(&mut [0; 4]));
465        }
466        _ => output.insert(0, if lexer { 'T' } else { 'r' }),
467    }
468    let reserved = if lexer {
469        RESERVED_TOKENS
470    } else {
471        RESERVED_WORDS
472    };
473    if reserved.contains(&output.as_str()) {
474        output.push('_');
475    }
476    output
477}
478
479/// Returns a valid spelling for a reference without a rule, keeping its case.
480fn reference_candidate(name: &str) -> String {
481    let lexer = name.starts_with(|c: char| c.is_ascii_uppercase());
482    let mut output = ascii_identifier(name);
483    if !output.starts_with(|c: char| c.is_ascii_alphabetic()) {
484        output.insert(0, if lexer { 'T' } else { 'r' });
485    }
486    if RESERVED_WORDS.contains(&output.as_str()) {
487        output.push('_');
488    }
489    output
490}
491
492fn label_candidate(label: &str) -> String {
493    let mut output = ascii_identifier(label);
494    if !output.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_') {
495        output.insert(0, 'l');
496    }
497    if RESERVED_WORDS.contains(&output.as_str()) {
498        output.push('_');
499    }
500    output
501}
502
503fn pascal_case(name: &str) -> String {
504    name.split('_')
505        .filter(|part| !part.is_empty())
506        .map(|part| {
507            let mut chars = part.chars();
508            chars.next().map_or_else(String::new, |first| {
509                first.to_ascii_uppercase().to_string() + chars.as_str()
510            })
511        })
512        .collect()
513}
514
515fn uses_character_constructs(expr: &GrammarExpr) -> bool {
516    match expr {
517        GrammarExpr::TerminalInsensitive(_)
518        | GrammarExpr::CharRange(_, _)
519        | GrammarExpr::CharClass { .. }
520        | GrammarExpr::AnyChar => true,
521        GrammarExpr::Choice { alternatives, .. } => {
522            alternatives.iter().any(uses_character_constructs)
523        }
524        GrammarExpr::Sequence(items) => items.iter().any(uses_character_constructs),
525        GrammarExpr::Optional(inner)
526        | GrammarExpr::ZeroOrMore(inner)
527        | GrammarExpr::OneOrMore(inner)
528        | GrammarExpr::And(inner)
529        | GrammarExpr::Not(inner)
530        | GrammarExpr::Repeat { expr: inner, .. }
531        | GrammarExpr::Capture { expr: inner, .. } => uses_character_constructs(inner),
532        GrammarExpr::Empty
533        | GrammarExpr::Terminal(_)
534        | GrammarExpr::NonTerminal(_)
535        | GrammarExpr::Feature(_) => false,
536    }
537}
538
539/// Quotes an ANTLR string literal.
540fn quote(value: &str) -> String {
541    let mut output = String::from("'");
542    for character in value.chars() {
543        match character {
544            '\'' => output.push_str("\\'"),
545            '\\' => output.push_str("\\\\"),
546            '\n' => output.push_str("\\n"),
547            '\r' => output.push_str("\\r"),
548            '\t' => output.push_str("\\t"),
549            '\u{08}' => output.push_str("\\b"),
550            '\u{0c}' => output.push_str("\\f"),
551            character if character.is_control() => {
552                let _ = write!(output, "\\u{:04X}", u32::from(character));
553            }
554            character => output.push(character),
555        }
556    }
557    output.push('\'');
558    output
559}
560
561/// Renders a lexer set as `[...]`, or `~[...]` when negated.
562fn render_set(negated: bool, items: &[CharClassItem]) -> Rendered {
563    let mut body = String::new();
564    for item in items {
565        match item {
566            CharClassItem::Char(character) => push_set_char(&mut body, *character),
567            CharClassItem::Range(start, end) => {
568                push_set_char(&mut body, *start);
569                body.push('-');
570                push_set_char(&mut body, *end);
571            }
572        }
573    }
574    if negated {
575        (format!("~[{body}]"), Precedence::Prefix)
576    } else {
577        (format!("[{body}]"), Precedence::Atom)
578    }
579}
580
581fn push_set_char(body: &mut String, character: char) {
582    match character {
583        '\\' | ']' | '-' => {
584            body.push('\\');
585            body.push(character);
586        }
587        '^' if body.is_empty() => body.push_str("\\^"),
588        '\n' => body.push_str("\\n"),
589        '\r' => body.push_str("\\r"),
590        '\t' => body.push_str("\\t"),
591        '\u{08}' => body.push_str("\\b"),
592        '\u{0c}' => body.push_str("\\f"),
593        character => body.push(character),
594    }
595}
596
597/// Comments, lexer command and re-imported documentation for one rule.
598#[derive(Debug)]
599struct DocPlan {
600    comments: Vec<String>,
601    command: Option<String>,
602    reimported: Option<String>,
603}
604
605/// Plans how rule documentation is written so that `import_antlr` reads the
606/// same documentation back whenever it consists of comments and a lexer
607/// command, as `import_antlr` produces it.
608fn plan_doc(doc: Option<&str>, lexer: bool) -> DocPlan {
609    let Some(doc) = doc else {
610        return DocPlan {
611            comments: Vec::new(),
612            command: None,
613            reimported: None,
614        };
615    };
616    let (text, command) = if lexer {
617        split_command(doc)
618    } else {
619        (doc, None)
620    };
621    let comments = comment_lines(text);
622    let mut parts = comments
623        .iter()
624        .map(|comment| comment.trim().to_string())
625        .collect::<Vec<_>>();
626    parts.extend(command.clone());
627    DocPlan {
628        comments,
629        command,
630        reimported: (!parts.is_empty()).then(|| parts.join("; ")),
631    }
632}
633
634/// Splits a trailing `-> command` part off lexer rule documentation when the
635/// command re-imports verbatim.
636fn split_command(doc: &str) -> (&str, Option<String>) {
637    let (text, command) = if doc.starts_with("->") {
638        ("", doc)
639    } else if let Some(position) = doc.rfind("; ->") {
640        (&doc[..position], &doc[position + 2..])
641    } else {
642        return (doc, None);
643    };
644    let probe = format!("A : 'a' {command} ;");
645    let round_trips = !command.contains(['\n', '\r'])
646        && import_antlr(&probe)
647            .ok()
648            .and_then(|grammar| {
649                grammar
650                    .rule("A")
651                    .and_then(GrammarRule::doc)
652                    .map(str::to_string)
653            })
654            .is_some_and(|reimported| reimported == command);
655    if round_trips {
656        (text, Some(command.to_string()))
657    } else {
658        (doc, None)
659    }
660}
661
662/// Writes documentation as ANTLR comments. Parts that already are comments
663/// are written verbatim; other text becomes `//` line comments.
664fn comment_lines(text: &str) -> Vec<String> {
665    let mut lines = Vec::new();
666    for part in split_comment_parts(text) {
667        let block = part.len() >= 4
668            && part.starts_with("/*")
669            && part[2..].find("*/") == Some(part.len() - 4);
670        let line = part.starts_with("//") && !part.contains(['\n', '\r']);
671        if block || line {
672            lines.push(part.to_string());
673        } else {
674            lines.extend(super::structural::doc_lines(part).map(|line| {
675                if line.starts_with("//") {
676                    line.to_string()
677                } else {
678                    format!("// {line}")
679                }
680            }));
681        }
682    }
683    lines
684}
685
686/// Splits documentation at `; ` separators that precede a comment.
687fn split_comment_parts(text: &str) -> Vec<&str> {
688    let mut parts = Vec::new();
689    let mut rest = text;
690    loop {
691        let split = ["; //", "; /*"]
692            .iter()
693            .filter_map(|separator| rest.find(separator))
694            .min();
695        let Some(position) = split else {
696            parts.push(rest);
697            break;
698        };
699        parts.push(&rest[..position]);
700        rest = &rest[position + 2..];
701    }
702    parts.retain(|part| !part.trim().is_empty());
703    parts
704}