meta_language/grammar/emit/
ebnf.rs1use crate::grammar::{CharClassItem, Grammar, GrammarExpr, GrammarFormat};
2
3use super::{
4 expanded_chars, finish_lines, ordered_rules, render_rule_line, unsupported_error, EmitReport,
5 GrammarEmitError, HelperRules, EBNF_RULE_TEMPLATE,
6};
7
8const MAX_EBNF_EXPANSION: u32 = 256;
9
10pub fn emit_ebnf(grammar: &Grammar) -> Result<(String, EmitReport), GrammarEmitError> {
22 let mut emitter = EbnfEmitter::new(grammar);
23 let mut lines = Vec::new();
24
25 for rule in ordered_rules(grammar) {
26 let body = emitter.emit_expr(rule.expr(), Precedence::Choice)?;
27 lines.push(render_rule_line(EBNF_RULE_TEMPLATE, rule.name(), &body));
28 }
29 for helper in emitter.helpers.entries() {
30 lines.push(render_rule_line(
31 EBNF_RULE_TEMPLATE,
32 &helper.name,
33 &helper.body,
34 ));
35 }
36
37 Ok((finish_lines(&lines), emitter.report))
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
41enum Precedence {
42 Choice = 0,
43 Sequence = 1,
44 Atom = 2,
45}
46
47#[derive(Clone, Debug)]
48struct EbnfEmitter {
49 report: EmitReport,
50 helpers: HelperRules,
51}
52
53impl EbnfEmitter {
54 fn new(grammar: &Grammar) -> Self {
55 Self {
56 report: EmitReport::default(),
57 helpers: HelperRules::new(grammar),
58 }
59 }
60
61 fn emit_expr(
62 &mut self,
63 expr: &GrammarExpr,
64 parent: Precedence,
65 ) -> Result<String, GrammarEmitError> {
66 let (text, precedence) = match expr {
67 GrammarExpr::Empty => (String::new(), Precedence::Atom),
68 GrammarExpr::Terminal(value) => (quote_terminal(value), Precedence::Atom),
69 GrammarExpr::TerminalInsensitive(value) => {
70 self.report.add_lossy(format!(
71 "EBNF cannot preserve case-insensitive terminal {value:?}"
72 ));
73 (quote_terminal(value), Precedence::Atom)
74 }
75 GrammarExpr::CharRange(start, end) => {
76 (self.emit_range_helper(*start, *end)?, Precedence::Atom)
77 }
78 GrammarExpr::CharClass { negated, items } => (
79 self.emit_char_class_helper(*negated, items)?,
80 Precedence::Atom,
81 ),
82 GrammarExpr::AnyChar => ("? any character ?".to_string(), Precedence::Atom),
83 GrammarExpr::NonTerminal(name) => (name.clone(), Precedence::Atom),
84 GrammarExpr::Choice {
85 ordered,
86 alternatives,
87 } => (
88 self.emit_choice(*ordered, alternatives)?,
89 Precedence::Choice,
90 ),
91 GrammarExpr::Sequence(items) => (self.emit_sequence(items)?, Precedence::Sequence),
92 GrammarExpr::Optional(inner) => {
93 let inner = self.emit_expr(inner, Precedence::Choice)?;
94 (format!("[ {inner} ]"), Precedence::Atom)
95 }
96 GrammarExpr::ZeroOrMore(inner) => {
97 let inner = self.emit_expr(inner, Precedence::Choice)?;
98 (format!("{{ {inner} }}"), Precedence::Atom)
99 }
100 GrammarExpr::OneOrMore(inner) => (
101 self.emit_repeat_sequence(inner, 1, None)?,
102 Precedence::Sequence,
103 ),
104 GrammarExpr::Repeat { expr, min, max } => (
105 self.emit_repeat_sequence(expr, *min, *max)?,
106 Precedence::Sequence,
107 ),
108 GrammarExpr::And(_) => return Err(unsupported_error(GrammarFormat::Ebnf, "And")),
109 GrammarExpr::Not(_) => return Err(unsupported_error(GrammarFormat::Ebnf, "Not")),
110 GrammarExpr::Capture { label, expr } => {
111 report_capture_loss(&mut self.report, GrammarFormat::Ebnf, label.as_ref());
112 return self.emit_expr(expr, parent);
113 }
114 };
115
116 if precedence < parent && !text.is_empty() {
117 Ok(format!("({text})"))
118 } else {
119 Ok(text)
120 }
121 }
122
123 fn emit_choice(
124 &mut self,
125 ordered: bool,
126 alternatives: &[GrammarExpr],
127 ) -> Result<String, GrammarEmitError> {
128 if ordered {
129 self.report
130 .add_lossy("EBNF treats ordered choice as unordered choice");
131 }
132 alternatives
133 .iter()
134 .map(|alternative| self.emit_expr(alternative, Precedence::Choice))
135 .collect::<Result<Vec<_>, _>>()
136 .map(|items| items.join(" | "))
137 }
138
139 fn emit_sequence(&mut self, items: &[GrammarExpr]) -> Result<String, GrammarEmitError> {
140 let mut emitted = Vec::new();
141 for item in items {
142 let text = self.emit_expr(item, Precedence::Sequence)?;
143 if !text.is_empty() {
144 emitted.push(text);
145 }
146 }
147 Ok(emitted.join(" , "))
148 }
149
150 fn emit_repeat_sequence(
151 &mut self,
152 expr: &GrammarExpr,
153 min: usize,
154 max: Option<usize>,
155 ) -> Result<String, GrammarEmitError> {
156 if max.is_some_and(|max| max < min) {
157 return Err(unsupported_error(
158 GrammarFormat::Ebnf,
159 format!("Repeat with min {min} greater than max {max:?}"),
160 ));
161 }
162
163 let mut parts = Vec::new();
164 for _ in 0..min {
165 let part = self.emit_expr(expr, Precedence::Sequence)?;
166 if !part.is_empty() {
167 parts.push(part);
168 }
169 }
170
171 if let Some(max) = max {
172 for _ in min..max {
173 let inner = self.emit_expr(expr, Precedence::Choice)?;
174 if !inner.is_empty() {
175 parts.push(format!("[ {inner} ]"));
176 }
177 }
178 } else {
179 let inner = self.emit_expr(expr, Precedence::Choice)?;
180 if !inner.is_empty() {
181 parts.push(format!("{{ {inner} }}"));
182 }
183 }
184
185 Ok(parts.join(" , "))
186 }
187
188 fn emit_range_helper(&mut self, start: char, end: char) -> Result<String, GrammarEmitError> {
189 let (name, is_new) = self
190 .helpers
191 .reserve("range", format!("{}:{}", start as u32, end as u32));
192 if is_new {
193 let body = expand_range(start, end)?
194 .into_iter()
195 .map(|character| quote_terminal(&character.to_string()))
196 .collect::<Vec<_>>()
197 .join(" | ");
198 self.helpers.push(name.clone(), body);
199 }
200 Ok(name)
201 }
202
203 fn emit_char_class_helper(
204 &mut self,
205 negated: bool,
206 items: &[CharClassItem],
207 ) -> Result<String, GrammarEmitError> {
208 if negated {
209 return Err(unsupported_error(GrammarFormat::Ebnf, "negated CharClass"));
210 }
211
212 let (name, is_new) = self.helpers.reserve("class", format!("{items:?}"));
213 if is_new {
214 let chars = expand_class_items(items)?;
215 if chars.is_empty() {
216 return Err(unsupported_error(GrammarFormat::Ebnf, "empty CharClass"));
217 }
218 let body = chars
219 .into_iter()
220 .map(|character| quote_terminal(&character.to_string()))
221 .collect::<Vec<_>>()
222 .join(" | ");
223 self.helpers.push(name.clone(), body);
224 }
225 Ok(name)
226 }
227}
228
229fn expand_range(start: char, end: char) -> Result<Vec<char>, GrammarEmitError> {
230 expanded_chars(
231 GrammarFormat::Ebnf,
232 "CharRange",
233 start,
234 end,
235 MAX_EBNF_EXPANSION,
236 )
237}
238
239fn expand_class_items(items: &[CharClassItem]) -> Result<Vec<char>, GrammarEmitError> {
240 let mut chars = Vec::new();
241 for item in items {
242 match item {
243 CharClassItem::Char(value) => chars.push(*value),
244 CharClassItem::Range(start, end) => chars.extend(expand_range(*start, *end)?),
245 }
246 if chars.len() > MAX_EBNF_EXPANSION as usize {
247 return Err(unsupported_error(
248 GrammarFormat::Ebnf,
249 format!("CharClass expands to more than {MAX_EBNF_EXPANSION} characters"),
250 ));
251 }
252 }
253 Ok(chars)
254}
255
256fn report_capture_loss(report: &mut EmitReport, format: GrammarFormat, label: Option<&String>) {
257 if let Some(label) = label {
258 report.add_lossy(format!("{format} dropped capture label {label:?}"));
259 } else {
260 report.add_lossy(format!("{format} dropped anonymous capture"));
261 }
262}
263
264fn quote_terminal(value: &str) -> String {
265 let quote = if value.contains('"') && !value.contains('\'') {
266 '\''
267 } else {
268 '"'
269 };
270 let escaped = value.replace(quote, "e.to_string().repeat(2));
271 format!("{quote}{escaped}{quote}")
272}