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        GrammarExpr::NonTerminal(name) => rust_type_name(name),
104        GrammarExpr::Capture {
105            label: Some(label), ..
106        } => rust_type_name(label),
107        GrammarExpr::Capture { expr, .. } => variant_name(expr, index),
108        GrammarExpr::Terminal(value) | GrammarExpr::TerminalInsensitive(value) => {
109            rust_type_name_with_fallback(value, &format!("Literal{}", index + 1))
110        }
111        GrammarExpr::Empty => "Empty".to_string(),
112        GrammarExpr::CharRange(_, _) | GrammarExpr::CharClass { .. } => {
113            format!("Character{}", index + 1)
114        }
115        GrammarExpr::AnyChar => format!("Any{}", index + 1),
116        GrammarExpr::Sequence(_) => format!("Sequence{}", index + 1),
117        GrammarExpr::Choice { .. } => format!("Choice{}", index + 1),
118        GrammarExpr::Optional(_) => format!("Optional{}", index + 1),
119        GrammarExpr::ZeroOrMore(_) | GrammarExpr::OneOrMore(_) | GrammarExpr::Repeat { .. } => {
120            format!("Repeated{}", index + 1)
121        }
122        GrammarExpr::And(_) | GrammarExpr::Not(_) => format!("Predicate{}", index + 1),
123    }
124}
125
126fn variant_type(expr: &GrammarExpr) -> String {
127    match expr {
128        GrammarExpr::NonTerminal(name) => rust_type_name(name),
129        GrammarExpr::Capture { expr, .. } => variant_type(expr),
130        GrammarExpr::Empty => "()".to_string(),
131        _ => "String".to_string(),
132    }
133}
134
135fn ast_fields(expr: &GrammarExpr) -> Vec<RustFieldShape> {
136    let mut fields = Vec::new();
137    collect_fields(expr, Quantifier::Single, &mut fields);
138    fields
139        .into_iter()
140        .map(|field| {
141            let type_name = field.type_name();
142            RustFieldShape::new(field.name, type_name)
143        })
144        .collect()
145}
146
147fn collect_fields(expr: &GrammarExpr, quantifier: Quantifier, fields: &mut Vec<AstField>) {
148    match expr {
149        GrammarExpr::NonTerminal(name) => {
150            push_field(
151                fields,
152                AstField::new(rust_field_name(name), rust_type_name(name), quantifier),
153            );
154        }
155        GrammarExpr::Capture {
156            label: Some(label),
157            expr,
158        } => {
159            let (type_name, quantifier) = captured_type(expr, quantifier);
160            push_field(
161                fields,
162                AstField::new(rust_field_name(label), type_name, quantifier),
163            );
164        }
165        GrammarExpr::Capture { label: None, expr } => collect_fields(expr, quantifier, fields),
166        GrammarExpr::Sequence(items) => {
167            for item in items {
168                collect_fields(item, quantifier, fields);
169            }
170        }
171        GrammarExpr::Choice { alternatives, .. } => {
172            for alternative in alternatives {
173                collect_fields(
174                    alternative,
175                    combine_quantifier(quantifier, Quantifier::Optional),
176                    fields,
177                );
178            }
179        }
180        GrammarExpr::Optional(inner) => {
181            collect_fields(
182                inner,
183                combine_quantifier(quantifier, Quantifier::Optional),
184                fields,
185            );
186        }
187        GrammarExpr::ZeroOrMore(inner)
188        | GrammarExpr::OneOrMore(inner)
189        | GrammarExpr::Repeat { expr: inner, .. } => {
190            collect_fields(
191                inner,
192                combine_quantifier(quantifier, Quantifier::Repeated),
193                fields,
194            );
195        }
196        GrammarExpr::And(_)
197        | GrammarExpr::Not(_)
198        | GrammarExpr::Empty
199        | GrammarExpr::Terminal(_)
200        | GrammarExpr::TerminalInsensitive(_)
201        | GrammarExpr::CharRange(_, _)
202        | GrammarExpr::CharClass { .. }
203        | GrammarExpr::AnyChar => {}
204    }
205}
206
207fn captured_type(expr: &GrammarExpr, quantifier: Quantifier) -> (String, Quantifier) {
208    match expr {
209        GrammarExpr::NonTerminal(name) => (rust_type_name(name), quantifier),
210        GrammarExpr::Capture { expr, .. } => captured_type(expr, quantifier),
211        GrammarExpr::Optional(inner) => {
212            captured_type(inner, combine_quantifier(quantifier, Quantifier::Optional))
213        }
214        GrammarExpr::ZeroOrMore(inner)
215        | GrammarExpr::OneOrMore(inner)
216        | GrammarExpr::Repeat { expr: inner, .. } => {
217            captured_type(inner, combine_quantifier(quantifier, Quantifier::Repeated))
218        }
219        _ => ("String".to_string(), quantifier),
220    }
221}
222
223fn push_field(fields: &mut Vec<AstField>, field: AstField) {
224    if let Some(existing) = fields
225        .iter_mut()
226        .find(|existing| existing.name == field.name && existing.base_type == field.base_type)
227    {
228        existing.quantifier = Quantifier::Repeated;
229        return;
230    }
231
232    if fields.iter().any(|existing| existing.name == field.name) {
233        let mut used = fields
234            .iter()
235            .map(|existing| existing.name.clone())
236            .collect::<BTreeSet<_>>();
237        let mut renamed = field;
238        renamed.name = unique_name(renamed.name, &mut used);
239        fields.push(renamed);
240    } else {
241        fields.push(field);
242    }
243}
244
245#[derive(Clone, Debug, PartialEq, Eq)]
246struct AstField {
247    name: String,
248    base_type: String,
249    quantifier: Quantifier,
250}
251
252impl AstField {
253    const fn new(name: String, base_type: String, quantifier: Quantifier) -> Self {
254        Self {
255            name,
256            base_type,
257            quantifier,
258        }
259    }
260
261    fn type_name(&self) -> String {
262        match self.quantifier {
263            Quantifier::Single => self.base_type.clone(),
264            Quantifier::Optional => format!("Option<{}>", self.base_type),
265            Quantifier::Repeated => format!("Vec<{}>", self.base_type),
266        }
267    }
268}
269
270#[derive(Clone, Copy, Debug, PartialEq, Eq)]
271enum Quantifier {
272    Single,
273    Optional,
274    Repeated,
275}
276
277const fn combine_quantifier(outer: Quantifier, inner: Quantifier) -> Quantifier {
278    match (outer, inner) {
279        (Quantifier::Repeated, _) | (_, Quantifier::Repeated) => Quantifier::Repeated,
280        (Quantifier::Optional, _) | (_, Quantifier::Optional) => Quantifier::Optional,
281        (Quantifier::Single, Quantifier::Single) => Quantifier::Single,
282    }
283}
284
285fn is_terminal_only(expr: &GrammarExpr) -> bool {
286    match expr {
287        GrammarExpr::Empty
288        | GrammarExpr::Terminal(_)
289        | GrammarExpr::TerminalInsensitive(_)
290        | GrammarExpr::CharRange(_, _)
291        | GrammarExpr::CharClass { .. }
292        | GrammarExpr::AnyChar => true,
293        GrammarExpr::NonTerminal(_) => false,
294        GrammarExpr::Choice { alternatives, .. } | GrammarExpr::Sequence(alternatives) => {
295            alternatives.iter().all(is_terminal_only)
296        }
297        GrammarExpr::Optional(expr)
298        | GrammarExpr::ZeroOrMore(expr)
299        | GrammarExpr::OneOrMore(expr)
300        | GrammarExpr::And(expr)
301        | GrammarExpr::Not(expr)
302        | GrammarExpr::Capture { expr, .. }
303        | GrammarExpr::Repeat { expr, .. } => is_terminal_only(expr),
304    }
305}
306
307fn render_rust_types(shapes: &[RustTypeShape]) -> String {
308    let mut output = String::new();
309    for (index, shape) in shapes.iter().enumerate() {
310        if index > 0 {
311            output.push('\n');
312        }
313        output.push_str(&render_rust_type(shape));
314    }
315    output
316}
317
318fn render_parser_struct(name: &str, pest_grammar: &str) -> String {
319    format!(
320        "#[derive(pest_derive::Parser)]\n#[grammar_inline = {pest_grammar:?}]\npub struct {name};\n"
321    )
322}
323
324fn render_struct(shape: &RustTypeShape) -> String {
325    if shape.fields().is_empty() {
326        return format!("#[derive(Debug, Clone)]\npub struct {};\n", shape.name());
327    }
328
329    if tuple_fields(shape.fields()) {
330        let fields = shape
331            .fields()
332            .iter()
333            .map(|field| format!("pub {}", field.type_name()))
334            .collect::<Vec<_>>()
335            .join(", ");
336        return format!(
337            "#[derive(Debug, Clone)]\npub struct {}({fields});\n",
338            shape.name()
339        );
340    }
341
342    let mut output = format!("#[derive(Debug, Clone)]\npub struct {} {{\n", shape.name());
343    for field in shape.fields() {
344        output.push_str("    pub ");
345        output.push_str(field.name());
346        output.push_str(": ");
347        output.push_str(field.type_name());
348        output.push_str(",\n");
349    }
350    output.push_str("}\n");
351    output
352}
353
354fn render_enum(shape: &RustTypeShape) -> String {
355    let mut output = format!("#[derive(Debug, Clone)]\npub enum {} {{\n", shape.name());
356    for field in shape.fields() {
357        output.push_str("    ");
358        output.push_str(field.name());
359        if field.type_name() == "()" {
360            output.push_str(",\n");
361        } else {
362            output.push('(');
363            output.push_str(field.type_name());
364            output.push_str("),\n");
365        }
366    }
367    output.push_str("}\n");
368    output
369}
370
371fn render_trait(shape: &RustTypeShape) -> String {
372    let mut output = format!("pub trait {} {{\n", shape.name());
373    for field in shape.fields() {
374        output.push_str("    fn ");
375        output.push_str(field.name());
376        output.push_str("(&self) -> ");
377        output.push_str(field.type_name());
378        output.push_str(";\n");
379    }
380    output.push_str("}\n");
381    output
382}
383
384fn render_sequence(shape: &RustTypeShape) -> String {
385    let element_type = shape
386        .fields()
387        .first()
388        .map_or("()", RustFieldShape::type_name);
389    format!(
390        "#[derive(Debug, Clone)]\npub struct {}(pub Vec<{element_type}>);\n",
391        shape.name()
392    )
393}
394
395fn render_option(shape: &RustTypeShape) -> String {
396    let some_type = shape
397        .fields()
398        .first()
399        .map_or("()", RustFieldShape::type_name);
400    format!(
401        "#[derive(Debug, Clone)]\npub enum {} {{\n    Some({some_type}),\n    None,\n}}\n",
402        shape.name()
403    )
404}
405
406fn render_map(shape: &RustTypeShape) -> String {
407    let key_type = shape
408        .fields()
409        .iter()
410        .find(|field| field.name() == "key")
411        .map_or("()", RustFieldShape::type_name);
412    let value_type = shape
413        .fields()
414        .iter()
415        .find(|field| field.name() == "value")
416        .map_or("()", RustFieldShape::type_name);
417    format!(
418        "#[derive(Debug, Clone)]\npub struct {}(pub std::collections::BTreeMap<{key_type}, {value_type}>);\n",
419        shape.name()
420    )
421}
422
423fn tuple_fields(fields: &[RustFieldShape]) -> bool {
424    fields.iter().enumerate().all(|(index, field)| {
425        field
426            .name()
427            .parse::<usize>()
428            .is_ok_and(|field_index| field_index == index)
429    })
430}
431
432fn parser_struct_name(grammar: &Grammar) -> String {
433    let base = grammar.start_rule().map_or("Generated", GrammarRule::name);
434    format!("{}Parser", rust_type_name(base))
435}
436
437fn rust_type_name(value: &str) -> String {
438    rust_type_name_with_fallback(value, "Generated")
439}
440
441fn rust_type_name_with_fallback(value: &str, fallback: &str) -> String {
442    let mut output = String::new();
443    let mut next_upper = true;
444    for character in value.chars() {
445        if character.is_ascii_alphanumeric() {
446            if output.is_empty() && character.is_ascii_digit() {
447                output.push_str(fallback);
448            }
449            if next_upper {
450                output.push(character.to_ascii_uppercase());
451            } else {
452                output.push(character.to_ascii_lowercase());
453            }
454            next_upper = false;
455        } else {
456            next_upper = true;
457        }
458    }
459
460    if output.is_empty() {
461        fallback.to_string()
462    } else {
463        output
464    }
465}
466
467fn rust_field_name(value: &str) -> String {
468    let mut output = String::new();
469    let mut previous_was_separator = false;
470
471    for (index, character) in value.chars().enumerate() {
472        if character.is_ascii_alphanumeric() {
473            if output.is_empty() && character.is_ascii_digit() {
474                output.push_str("field_");
475            }
476            if character.is_ascii_uppercase()
477                && !output.is_empty()
478                && index > 0
479                && !previous_was_separator
480            {
481                output.push('_');
482            }
483            output.push(character.to_ascii_lowercase());
484            previous_was_separator = false;
485        } else if !output.is_empty() && !previous_was_separator {
486            output.push('_');
487            previous_was_separator = true;
488        }
489    }
490
491    while output.ends_with('_') {
492        output.pop();
493    }
494
495    if output.is_empty() {
496        output.push_str("field");
497    }
498
499    if is_rust_keyword(&output) {
500        format!("r#{output}")
501    } else {
502        output
503    }
504}
505
506fn unique_name(name: String, used: &mut BTreeSet<String>) -> String {
507    if used.insert(name.clone()) {
508        return name;
509    }
510
511    for index in 2.. {
512        let candidate = format!("{name}{index}");
513        if used.insert(candidate.clone()) {
514            return candidate;
515        }
516    }
517
518    unreachable!("unbounded unique-name loop always returns")
519}
520
521fn is_rust_keyword(value: &str) -> bool {
522    matches!(
523        value,
524        "as" | "break"
525            | "const"
526            | "continue"
527            | "crate"
528            | "else"
529            | "enum"
530            | "extern"
531            | "false"
532            | "fn"
533            | "for"
534            | "if"
535            | "impl"
536            | "in"
537            | "let"
538            | "loop"
539            | "match"
540            | "mod"
541            | "move"
542            | "mut"
543            | "pub"
544            | "ref"
545            | "return"
546            | "self"
547            | "Self"
548            | "static"
549            | "struct"
550            | "super"
551            | "trait"
552            | "true"
553            | "type"
554            | "unsafe"
555            | "use"
556            | "where"
557            | "while"
558            | "async"
559            | "await"
560            | "dyn"
561    )
562}