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#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct RustParserArtifacts {
12 pub pest_grammar: String,
14 pub parser_struct: String,
16 pub ast_types: String,
18 pub ast_shapes: Vec<RustTypeShape>,
20}
21
22pub 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#[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}