Skip to main content

meta_language/grammar/emit/
rust.rs

1use std::collections::BTreeSet;
2
3use crate::grammar::{Grammar, GrammarExpr, GrammarRule};
4use crate::rust_codec::{RustFieldShape, RustTypeKind, RustTypeShape};
5
6use super::pest::emit_pest;
7use super::{EmitReport, GrammarEmitError};
8
9/// Bundled Rust parser codegen output.
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct RustParserArtifacts {
12    /// `.pest` grammar source accepted by `pest_meta` and `pest_derive`.
13    pub pest_grammar: String,
14    /// `pest_derive` parser struct stub using `#[grammar_inline = "..."]`.
15    pub parser_struct: String,
16    /// Rendered Rust AST type declarations, one declaration per grammar rule.
17    pub ast_types: String,
18    /// Queryable Rust type shapes used to render `ast_types`.
19    pub ast_shapes: Vec<RustTypeShape>,
20}
21
22/// Emits a runnable Rust parser bundle from the grammar IR.
23///
24/// The generated parser uses `pest_derive` with an inline pest grammar. AST
25/// declarations are derived from [`RustTypeShape`] so the generated type model
26/// can also be registered in the links codec.
27///
28/// # Errors
29///
30/// Returns [`GrammarEmitError`] when the underlying pest grammar emitter cannot
31/// represent an invalid IR construct, such as empty choices or invalid repeat
32/// bounds.
33pub fn emit_rust_parser(
34    grammar: &Grammar,
35) -> Result<(RustParserArtifacts, EmitReport), GrammarEmitError> {
36    let (pest_grammar, report) = emit_pest(grammar)?;
37    let ast_shapes = ast_shapes_for_grammar(grammar);
38    let ast_types = render_rust_types(&ast_shapes);
39    let parser_struct = render_parser_struct(&parser_struct_name(grammar), &pest_grammar);
40
41    Ok((
42        RustParserArtifacts {
43            pest_grammar,
44            parser_struct,
45            ast_types,
46            ast_shapes,
47        },
48        report,
49    ))
50}
51
52/// Renders a [`RustTypeShape`] as a Rust declaration.
53#[must_use]
54pub fn render_rust_type(shape: &RustTypeShape) -> String {
55    match shape.kind() {
56        RustTypeKind::Struct => render_struct(shape),
57        RustTypeKind::Enum => render_enum(shape),
58        RustTypeKind::Primitive => {
59            format!(
60                "#[derive(Debug, Clone)]\npub struct {}(pub String);\n",
61                shape.name()
62            )
63        }
64        RustTypeKind::Trait => render_trait(shape),
65        RustTypeKind::Sequence => render_sequence(shape),
66        RustTypeKind::Option => render_option(shape),
67        RustTypeKind::Map => render_map(shape),
68    }
69}
70
71fn ast_shapes_for_grammar(grammar: &Grammar) -> Vec<RustTypeShape> {
72    grammar.rules().iter().map(shape_for_rule).collect()
73}
74
75fn shape_for_rule(rule: &GrammarRule) -> RustTypeShape {
76    let type_name = rust_type_name(rule.name());
77    if let GrammarExpr::Choice { alternatives, .. } = rule.expr() {
78        return RustTypeShape::enumeration(type_name, enum_variants(alternatives));
79    }
80
81    let fields = ast_fields(rule.expr());
82    if fields.is_empty() && is_terminal_only(rule.expr()) {
83        RustTypeShape::structure(type_name, [RustFieldShape::new("0", "String")])
84    } else {
85        RustTypeShape::structure(type_name, fields)
86    }
87}
88
89fn enum_variants(alternatives: &[GrammarExpr]) -> Vec<RustFieldShape> {
90    let mut used = BTreeSet::new();
91    alternatives
92        .iter()
93        .enumerate()
94        .map(|(index, alternative)| {
95            let name = unique_name(variant_name(alternative, index), &mut used);
96            RustFieldShape::new(name, variant_type(alternative))
97        })
98        .collect()
99}
100
101fn variant_name(expr: &GrammarExpr, index: usize) -> String {
102    match expr {
103        // Variants live in their enum's namespace, so only `Self` is reserved.
104        GrammarExpr::NonTerminal(name)
105        | GrammarExpr::Capture {
106            label: Some(name), ..
107        } => match rust_type_name_with_fallback(name, "Generated") {
108            variant if variant == "Self" => "SelfNode".to_string(),
109            variant => variant,
110        },
111        GrammarExpr::Capture { expr, .. } => variant_name(expr, index),
112        GrammarExpr::Terminal(value) | GrammarExpr::TerminalInsensitive(value) => {
113            rust_type_name_with_fallback(value, &format!("Literal{}", index + 1))
114        }
115        GrammarExpr::Empty => "Empty".to_string(),
116        GrammarExpr::CharRange(_, _) | GrammarExpr::CharClass { .. } => {
117            format!("Character{}", index + 1)
118        }
119        GrammarExpr::AnyChar => format!("Any{}", index + 1),
120        GrammarExpr::Sequence(_) => format!("Sequence{}", index + 1),
121        GrammarExpr::Choice { .. } => format!("Choice{}", index + 1),
122        GrammarExpr::Optional(_) => format!("Optional{}", index + 1),
123        GrammarExpr::ZeroOrMore(_) | GrammarExpr::OneOrMore(_) | GrammarExpr::Repeat { .. } => {
124            format!("Repeated{}", index + 1)
125        }
126        GrammarExpr::And(_) | GrammarExpr::Not(_) => format!("Predicate{}", index + 1),
127        GrammarExpr::Feature(_) => format!("Feature{}", index + 1),
128    }
129}
130
131fn variant_type(expr: &GrammarExpr) -> String {
132    match expr {
133        GrammarExpr::NonTerminal(name) => rust_type_name(name),
134        GrammarExpr::Capture { expr, .. } => variant_type(expr),
135        GrammarExpr::Empty => "()".to_string(),
136        _ => "String".to_string(),
137    }
138}
139
140fn ast_fields(expr: &GrammarExpr) -> Vec<RustFieldShape> {
141    let mut fields = Vec::new();
142    collect_fields(expr, Quantifier::Single, &mut fields);
143    fields
144        .into_iter()
145        .map(|field| {
146            let type_name = field.type_name();
147            RustFieldShape::new(field.name, type_name)
148        })
149        .collect()
150}
151
152fn collect_fields(expr: &GrammarExpr, quantifier: Quantifier, fields: &mut Vec<AstField>) {
153    match expr {
154        GrammarExpr::NonTerminal(name) => {
155            push_field(
156                fields,
157                AstField::new(rust_field_name(name), rust_type_name(name), quantifier),
158            );
159        }
160        GrammarExpr::Capture {
161            label: Some(label),
162            expr,
163        } => {
164            let (type_name, quantifier) = captured_type(expr, quantifier);
165            push_field(
166                fields,
167                AstField::new(rust_field_name(label), type_name, quantifier),
168            );
169        }
170        GrammarExpr::Capture { label: None, expr } => collect_fields(expr, quantifier, fields),
171        GrammarExpr::Sequence(items) => {
172            for item in items {
173                collect_fields(item, quantifier, fields);
174            }
175        }
176        GrammarExpr::Choice { alternatives, .. } => {
177            for alternative in alternatives {
178                collect_fields(
179                    alternative,
180                    combine_quantifier(quantifier, Quantifier::Optional),
181                    fields,
182                );
183            }
184        }
185        GrammarExpr::Optional(inner) => {
186            collect_fields(
187                inner,
188                combine_quantifier(quantifier, Quantifier::Optional),
189                fields,
190            );
191        }
192        GrammarExpr::ZeroOrMore(inner)
193        | GrammarExpr::OneOrMore(inner)
194        | GrammarExpr::Repeat { expr: inner, .. } => {
195            collect_fields(
196                inner,
197                combine_quantifier(quantifier, Quantifier::Repeated),
198                fields,
199            );
200        }
201        GrammarExpr::And(_)
202        | GrammarExpr::Not(_)
203        | GrammarExpr::Empty
204        | GrammarExpr::Terminal(_)
205        | GrammarExpr::TerminalInsensitive(_)
206        | GrammarExpr::CharRange(_, _)
207        | GrammarExpr::CharClass { .. }
208        | GrammarExpr::Feature(_)
209        | GrammarExpr::AnyChar => {}
210    }
211}
212
213fn captured_type(expr: &GrammarExpr, quantifier: Quantifier) -> (String, Quantifier) {
214    match expr {
215        GrammarExpr::NonTerminal(name) => (rust_type_name(name), quantifier),
216        GrammarExpr::Capture { expr, .. } => captured_type(expr, quantifier),
217        GrammarExpr::Optional(inner) => {
218            captured_type(inner, combine_quantifier(quantifier, Quantifier::Optional))
219        }
220        GrammarExpr::ZeroOrMore(inner)
221        | GrammarExpr::OneOrMore(inner)
222        | GrammarExpr::Repeat { expr: inner, .. } => {
223            captured_type(inner, combine_quantifier(quantifier, Quantifier::Repeated))
224        }
225        _ => ("String".to_string(), quantifier),
226    }
227}
228
229fn push_field(fields: &mut Vec<AstField>, field: AstField) {
230    if let Some(existing) = fields
231        .iter_mut()
232        .find(|existing| existing.name == field.name && existing.base_type == field.base_type)
233    {
234        existing.quantifier = Quantifier::Repeated;
235        return;
236    }
237
238    if fields.iter().any(|existing| existing.name == field.name) {
239        let mut used = fields
240            .iter()
241            .map(|existing| existing.name.clone())
242            .collect::<BTreeSet<_>>();
243        let mut renamed = field;
244        renamed.name = unique_name(renamed.name, &mut used);
245        fields.push(renamed);
246    } else {
247        fields.push(field);
248    }
249}
250
251#[derive(Clone, Debug, PartialEq, Eq)]
252struct AstField {
253    name: String,
254    base_type: String,
255    quantifier: Quantifier,
256}
257
258impl AstField {
259    const fn new(name: String, base_type: String, quantifier: Quantifier) -> Self {
260        Self {
261            name,
262            base_type,
263            quantifier,
264        }
265    }
266
267    fn type_name(&self) -> String {
268        match self.quantifier {
269            Quantifier::Single => self.base_type.clone(),
270            Quantifier::Optional => format!("Option<{}>", self.base_type),
271            Quantifier::Repeated => format!("Vec<{}>", self.base_type),
272        }
273    }
274}
275
276#[derive(Clone, Copy, Debug, PartialEq, Eq)]
277enum Quantifier {
278    Single,
279    Optional,
280    Repeated,
281}
282
283const fn combine_quantifier(outer: Quantifier, inner: Quantifier) -> Quantifier {
284    match (outer, inner) {
285        (Quantifier::Repeated, _) | (_, Quantifier::Repeated) => Quantifier::Repeated,
286        (Quantifier::Optional, _) | (_, Quantifier::Optional) => Quantifier::Optional,
287        (Quantifier::Single, Quantifier::Single) => Quantifier::Single,
288    }
289}
290
291fn is_terminal_only(expr: &GrammarExpr) -> bool {
292    match expr {
293        GrammarExpr::Empty
294        | GrammarExpr::Terminal(_)
295        | GrammarExpr::TerminalInsensitive(_)
296        | GrammarExpr::CharRange(_, _)
297        | GrammarExpr::CharClass { .. }
298        | GrammarExpr::AnyChar => true,
299        GrammarExpr::NonTerminal(_) | GrammarExpr::Feature(_) => false,
300        GrammarExpr::Choice { alternatives, .. } | GrammarExpr::Sequence(alternatives) => {
301            alternatives.iter().all(is_terminal_only)
302        }
303        GrammarExpr::Optional(expr)
304        | GrammarExpr::ZeroOrMore(expr)
305        | GrammarExpr::OneOrMore(expr)
306        | GrammarExpr::And(expr)
307        | GrammarExpr::Not(expr)
308        | GrammarExpr::Capture { expr, .. }
309        | GrammarExpr::Repeat { expr, .. } => is_terminal_only(expr),
310    }
311}
312
313fn render_rust_types(shapes: &[RustTypeShape]) -> String {
314    let mut output = String::new();
315    for (index, shape) in shapes.iter().enumerate() {
316        if index > 0 {
317            output.push('\n');
318        }
319        output.push_str(&render_rust_type(shape));
320    }
321    output
322}
323
324fn render_parser_struct(name: &str, pest_grammar: &str) -> String {
325    format!(
326        "#[derive(pest_derive::Parser)]\n#[grammar_inline = {pest_grammar:?}]\npub struct {name};\n"
327    )
328}
329
330fn render_struct(shape: &RustTypeShape) -> String {
331    if shape.fields().is_empty() {
332        return format!("#[derive(Debug, Clone)]\npub struct {};\n", shape.name());
333    }
334
335    if tuple_fields(shape.fields()) {
336        let fields = shape
337            .fields()
338            .iter()
339            .map(|field| format!("pub {}", field.type_name()))
340            .collect::<Vec<_>>()
341            .join(", ");
342        return format!(
343            "#[derive(Debug, Clone)]\npub struct {}({fields});\n",
344            shape.name()
345        );
346    }
347
348    let mut output = format!("#[derive(Debug, Clone)]\npub struct {} {{\n", shape.name());
349    for field in shape.fields() {
350        output.push_str("    pub ");
351        output.push_str(field.name());
352        output.push_str(": ");
353        output.push_str(field.type_name());
354        output.push_str(",\n");
355    }
356    output.push_str("}\n");
357    output
358}
359
360fn render_enum(shape: &RustTypeShape) -> String {
361    let mut output = format!("#[derive(Debug, Clone)]\npub enum {} {{\n", shape.name());
362    for field in shape.fields() {
363        output.push_str("    ");
364        output.push_str(field.name());
365        if field.type_name() == "()" {
366            output.push_str(",\n");
367        } else {
368            output.push('(');
369            output.push_str(field.type_name());
370            output.push_str("),\n");
371        }
372    }
373    output.push_str("}\n");
374    output
375}
376
377fn render_trait(shape: &RustTypeShape) -> String {
378    let mut output = format!("pub trait {} {{\n", shape.name());
379    for field in shape.fields() {
380        output.push_str("    fn ");
381        output.push_str(field.name());
382        output.push_str("(&self) -> ");
383        output.push_str(field.type_name());
384        output.push_str(";\n");
385    }
386    output.push_str("}\n");
387    output
388}
389
390fn render_sequence(shape: &RustTypeShape) -> String {
391    let element_type = shape
392        .fields()
393        .first()
394        .map_or("()", RustFieldShape::type_name);
395    format!(
396        "#[derive(Debug, Clone)]\npub struct {}(pub Vec<{element_type}>);\n",
397        shape.name()
398    )
399}
400
401fn render_option(shape: &RustTypeShape) -> String {
402    let some_type = shape
403        .fields()
404        .first()
405        .map_or("()", RustFieldShape::type_name);
406    format!(
407        "#[derive(Debug, Clone)]\npub enum {} {{\n    Some({some_type}),\n    None,\n}}\n",
408        shape.name()
409    )
410}
411
412fn render_map(shape: &RustTypeShape) -> String {
413    let key_type = shape
414        .fields()
415        .iter()
416        .find(|field| field.name() == "key")
417        .map_or("()", RustFieldShape::type_name);
418    let value_type = shape
419        .fields()
420        .iter()
421        .find(|field| field.name() == "value")
422        .map_or("()", RustFieldShape::type_name);
423    format!(
424        "#[derive(Debug, Clone)]\npub struct {}(pub std::collections::BTreeMap<{key_type}, {value_type}>);\n",
425        shape.name()
426    )
427}
428
429fn tuple_fields(fields: &[RustFieldShape]) -> bool {
430    fields.iter().enumerate().all(|(index, field)| {
431        field
432            .name()
433            .parse::<usize>()
434            .is_ok_and(|field_index| field_index == index)
435    })
436}
437
438fn parser_struct_name(grammar: &Grammar) -> String {
439    let base = grammar.start_rule().map_or("Generated", GrammarRule::name);
440    format!("{}Parser", rust_type_name(base))
441}
442
443/// Type names the generated module already uses: prelude types named in
444/// field types, the `Rule` enum derived by `pest_derive`, and `Self`.
445const RESERVED_TYPE_NAMES: &[&str] = &["Box", "Option", "Result", "Rule", "Self", "String", "Vec"];
446
447fn rust_type_name(value: &str) -> String {
448    let name = rust_type_name_with_fallback(value, "Generated");
449    if RESERVED_TYPE_NAMES.contains(&name.as_str()) {
450        format!("{name}Node")
451    } else {
452        name
453    }
454}
455
456fn rust_type_name_with_fallback(value: &str, fallback: &str) -> String {
457    let mut output = String::new();
458    let mut next_upper = true;
459    for character in value.chars() {
460        if character.is_ascii_alphanumeric() {
461            if output.is_empty() && character.is_ascii_digit() {
462                output.push_str(fallback);
463            }
464            if next_upper {
465                output.push(character.to_ascii_uppercase());
466            } else {
467                output.push(character.to_ascii_lowercase());
468            }
469            next_upper = false;
470        } else {
471            next_upper = true;
472        }
473    }
474
475    if output.is_empty() {
476        fallback.to_string()
477    } else {
478        output
479    }
480}
481
482fn rust_field_name(value: &str) -> String {
483    let mut output = String::new();
484    let mut previous_was_separator = false;
485
486    for (index, character) in value.chars().enumerate() {
487        if character.is_ascii_alphanumeric() {
488            if output.is_empty() && character.is_ascii_digit() {
489                output.push_str("field_");
490            }
491            if character.is_ascii_uppercase()
492                && !output.is_empty()
493                && index > 0
494                && !previous_was_separator
495            {
496                output.push('_');
497            }
498            output.push(character.to_ascii_lowercase());
499            previous_was_separator = false;
500        } else if !output.is_empty() && !previous_was_separator {
501            output.push('_');
502            previous_was_separator = true;
503        }
504    }
505
506    while output.ends_with('_') {
507        output.pop();
508    }
509
510    if output.is_empty() {
511        output.push_str("field");
512    }
513
514    if is_rust_keyword(&output) {
515        format!("r#{output}")
516    } else {
517        output
518    }
519}
520
521fn unique_name(name: String, used: &mut BTreeSet<String>) -> String {
522    if used.insert(name.clone()) {
523        return name;
524    }
525
526    for index in 2.. {
527        let candidate = format!("{name}{index}");
528        if used.insert(candidate.clone()) {
529            return candidate;
530        }
531    }
532
533    unreachable!("unbounded unique-name loop always returns")
534}
535
536fn is_rust_keyword(value: &str) -> bool {
537    matches!(
538        value,
539        "as" | "break"
540            | "const"
541            | "continue"
542            | "crate"
543            | "else"
544            | "enum"
545            | "extern"
546            | "false"
547            | "fn"
548            | "for"
549            | "if"
550            | "impl"
551            | "in"
552            | "let"
553            | "loop"
554            | "match"
555            | "mod"
556            | "move"
557            | "mut"
558            | "pub"
559            | "ref"
560            | "return"
561            | "self"
562            | "Self"
563            | "static"
564            | "struct"
565            | "super"
566            | "trait"
567            | "true"
568            | "type"
569            | "unsafe"
570            | "use"
571            | "where"
572            | "while"
573            | "async"
574            | "await"
575            | "dyn"
576    )
577}