1use std::collections::BTreeMap;
9use std::fmt::Write as _;
10
11use super::operations::OperationValue;
12use super::program::Compiled;
13use super::results::{Tree, TreeType};
14use super::text::{hex_of, quote_text, text_of};
15
16pub type SyntaxAttributes = BTreeMap<String, OperationValue>;
18
19#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum LeafText {
23 Text(String),
25 Hex(String),
27}
28
29impl LeafText {
30 fn of(bytes: &[u8], start: usize, end: usize) -> Self {
31 text_of(bytes, start, end).map_or_else(|| Self::Hex(hex_of(bytes, start, end)), Self::Text)
32 }
33
34 fn render(&self) -> String {
35 match self {
36 Self::Text(text) => quote_text(text),
37 Self::Hex(hex) => format!("<{hex}>"),
38 }
39 }
40}
41
42#[derive(Clone, Debug, PartialEq, Eq)]
44pub enum SyntaxTree {
45 Node {
47 kind: String,
49 field: Option<String>,
51 trivia: bool,
53 start: usize,
55 end: usize,
57 children: Vec<Self>,
59 attributes: Option<SyntaxAttributes>,
61 },
62 Token {
64 kind: Option<String>,
66 field: Option<String>,
68 trivia: bool,
70 start: usize,
72 end: usize,
74 attributes: Option<SyntaxAttributes>,
76 text: LeafText,
78 },
79 Error {
81 start: usize,
83 end: usize,
85 text: LeafText,
87 reason: Option<String>,
89 },
90 Missing {
92 kind: Option<String>,
94 start: usize,
96 end: usize,
98 literal: bool,
100 },
101 Embed {
103 language: String,
105 start: usize,
107 end: usize,
109 root: Box<Self>,
111 },
112}
113
114impl SyntaxTree {
115 #[must_use]
117 pub const fn start(&self) -> usize {
118 match self {
119 Self::Node { start, .. }
120 | Self::Token { start, .. }
121 | Self::Error { start, .. }
122 | Self::Missing { start, .. }
123 | Self::Embed { start, .. } => *start,
124 }
125 }
126
127 #[must_use]
129 pub const fn end(&self) -> usize {
130 match self {
131 Self::Node { end, .. }
132 | Self::Token { end, .. }
133 | Self::Error { end, .. }
134 | Self::Missing { end, .. }
135 | Self::Embed { end, .. } => *end,
136 }
137 }
138
139 #[must_use]
141 pub fn first_recovery(&self) -> Option<&Self> {
142 match self {
143 Self::Error { .. } | Self::Missing { .. } => Some(self),
144 Self::Node { children, .. } => children.iter().find_map(Self::first_recovery),
145 Self::Embed { root, .. } => root.first_recovery(),
146 Self::Token { .. } => None,
147 }
148 }
149
150 #[must_use]
156 pub fn render(&self) -> String {
157 let mut rendered = String::new();
158 self.render_into(&mut rendered);
159 rendered
160 }
161
162 fn render_into(&self, out: &mut String) {
163 match self {
164 Self::Node {
165 kind,
166 field,
167 trivia,
168 children,
169 attributes,
170 ..
171 } => {
172 render_field(field.as_deref(), out);
173 if *trivia {
174 out.push('~');
175 }
176 out.push('(');
177 out.push_str(&render_name(kind));
178 if let Some(attributes) = attributes {
179 out.push(' ');
180 render_attributes(attributes, out);
181 }
182 for child in children {
183 out.push(' ');
184 child.render_into(out);
185 }
186 out.push(')');
187 }
188 Self::Token {
189 kind,
190 field,
191 trivia,
192 attributes,
193 text,
194 ..
195 } => {
196 render_field(field.as_deref(), out);
197 if *trivia {
198 out.push('~');
199 }
200 if kind.is_none() && attributes.is_none() {
201 out.push_str(&text.render());
202 return;
203 }
204 out.push('(');
205 out.push_str(&kind.as_deref().map_or_else(|| "_".to_owned(), render_name));
206 if let Some(attributes) = attributes {
207 out.push(' ');
208 render_attributes(attributes, out);
209 }
210 out.push(' ');
211 out.push_str(&text.render());
212 out.push(')');
213 }
214 Self::Error {
215 start, end, text, ..
216 } => {
217 let _ = write!(out, "(ERROR@{start}..{end} {})", text.render());
218 }
219 Self::Missing {
220 kind,
221 start,
222 literal,
223 ..
224 } => match kind {
225 None => {
226 let _ = write!(out, "(MISSING@{start})");
227 }
228 Some(kind) => {
229 let name = if *literal {
230 quote_text(kind)
231 } else {
232 render_name(kind)
233 };
234 let _ = write!(out, "(MISSING@{start} {name})");
235 }
236 },
237 Self::Embed {
238 language,
239 start,
240 end,
241 root,
242 } => {
243 let _ = write!(out, "(EMBED {}@{start}..{end} ", render_name(language));
244 root.render_into(out);
245 out.push(')');
246 }
247 }
248 }
249}
250
251fn render_field(field: Option<&str>, out: &mut String) {
252 if let Some(field) = field {
253 out.push_str(&render_name(field));
254 out.push(':');
255 }
256}
257
258fn render_name(name: &str) -> String {
259 let mut chars = name.chars();
260 let plain = chars
261 .next()
262 .is_some_and(|first| first.is_ascii_alphabetic() || first == '_')
263 && chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-');
264 if plain {
265 name.to_owned()
266 } else {
267 quote_text(name)
268 }
269}
270
271fn render_attributes(attributes: &SyntaxAttributes, out: &mut String) {
272 out.push('{');
273 for (index, (name, value)) in attributes.iter().enumerate() {
274 if index > 0 {
275 out.push(',');
276 }
277 out.push_str(&render_name(name));
278 out.push('=');
279 match value {
280 OperationValue::Text(text) => out.push_str("e_text(text)),
281 OperationValue::Integer(integer) => {
282 let _ = write!(out, "{integer}");
283 }
284 }
285 }
286 out.push('}');
287}
288
289pub(super) fn public_tree(node: &Tree, bytes: &[u8]) -> SyntaxTree {
291 let name = |value: &Option<super::program::Name>| value.as_deref().map(str::to_owned);
292 match node.ty {
293 TreeType::Node => SyntaxTree::Node {
294 kind: name(&node.kind).unwrap_or_default(),
295 field: name(&node.field),
296 trivia: node.trivia,
297 start: node.start,
298 end: node.end,
299 children: node
300 .children
301 .iter()
302 .map(|child| public_tree(child, bytes))
303 .collect(),
304 attributes: node.attributes.clone(),
305 },
306 TreeType::Token => SyntaxTree::Token {
307 kind: name(&node.kind),
308 field: name(&node.field),
309 trivia: node.trivia,
310 start: node.start,
311 end: node.end,
312 attributes: node.attributes.clone(),
313 text: LeafText::of(bytes, node.start, node.end),
314 },
315 TreeType::Error => error_tree(bytes, node.start, node.end, None),
316 TreeType::Missing => SyntaxTree::Missing {
317 kind: name(&node.kind),
318 start: node.start,
319 end: node.end,
320 literal: node.literal,
321 },
322 TreeType::Embed => SyntaxTree::Embed {
323 language: name(&node.language).unwrap_or_default(),
324 start: node.start,
325 end: node.end,
326 root: Box::new(node.root.as_ref().map_or_else(
327 || error_tree(bytes, node.start, node.end, None),
328 |root| public_tree(root, bytes),
329 )),
330 },
331 }
332}
333
334pub(super) fn error_tree(
335 bytes: &[u8],
336 start: usize,
337 end: usize,
338 reason: Option<&str>,
339) -> SyntaxTree {
340 SyntaxTree::Error {
341 start,
342 end,
343 text: LeafText::of(bytes, start, end),
344 reason: reason.map(str::to_owned),
345 }
346}
347
348#[derive(Clone, Debug, PartialEq, Eq)]
350pub struct Ambiguity {
351 pub rule: String,
353 pub start: usize,
355 pub end: usize,
357}
358
359pub(super) fn collect_ambiguities(
361 node: &Tree,
362 compiled: &Compiled,
363 program: usize,
364 found: &mut Vec<Ambiguity>,
365) {
366 match node.ty {
367 TreeType::Node => {
368 let conflicts = &compiled.programs[program].conflicts;
369 if node.ambiguous
370 && !node
371 .rule
372 .as_deref()
373 .is_some_and(|rule| conflicts.contains(rule))
374 {
375 found.push(Ambiguity {
376 rule: node
377 .rule
378 .as_deref()
379 .or(node.kind.as_deref())
380 .unwrap_or_default()
381 .to_owned(),
382 start: node.start,
383 end: node.end,
384 });
385 }
386 for child in node.children.iter() {
387 collect_ambiguities(child, compiled, program, found);
388 }
389 }
390 TreeType::Embed => {
391 if let Some(root) = &node.root {
392 collect_ambiguities(root, compiled, node.program, found);
393 }
394 }
395 _ => {}
396 }
397}