1use std::fmt::Write;
4
5use indexmap::IndexSet;
6use rowan::NodeOrToken;
7
8use crate::parser::{self as ast, Expr, SyntaxNode};
9
10use super::Query;
11use super::shapes::ShapeCardinality;
12
13pub struct QueryPrinter<'q, 'src> {
14 query: &'q Query<'src>,
15 raw: bool,
16 trivia: bool,
17 cardinalities: bool,
18 spans: bool,
19 symbols: bool,
20}
21
22impl<'q, 'src> QueryPrinter<'q, 'src> {
23 pub fn new(query: &'q Query<'src>) -> Self {
24 Self {
25 query,
26 raw: false,
27 trivia: false,
28 cardinalities: false,
29 spans: false,
30 symbols: false,
31 }
32 }
33
34 pub fn raw(mut self, value: bool) -> Self {
35 self.raw = value;
36 self
37 }
38
39 pub fn with_trivia(mut self, value: bool) -> Self {
40 self.trivia = value;
41 self
42 }
43
44 pub fn with_cardinalities(mut self, value: bool) -> Self {
45 self.cardinalities = value;
46 self
47 }
48
49 pub fn with_spans(mut self, value: bool) -> Self {
50 self.spans = value;
51 self
52 }
53
54 pub fn only_symbols(mut self, value: bool) -> Self {
55 self.symbols = value;
56 self
57 }
58
59 pub fn dump(&self) -> String {
60 let mut out = String::new();
61 self.format(&mut out).expect("String write never fails");
62 out
63 }
64
65 pub fn format(&self, w: &mut impl Write) -> std::fmt::Result {
66 if self.symbols {
67 return self.format_symbols(w);
68 }
69 if self.raw {
70 return self.format_cst(self.query.as_cst(), 0, w);
71 }
72 self.format_root(self.query.root(), w)
73 }
74
75 fn format_symbols(&self, w: &mut impl Write) -> std::fmt::Result {
76 use std::collections::HashMap;
77
78 let symbols = &self.query.symbol_table;
79 if symbols.is_empty() {
80 return Ok(());
81 }
82
83 let defined: IndexSet<&str> = symbols.keys().copied().collect();
84
85 let mut body_nodes: HashMap<String, SyntaxNode> = HashMap::new();
86 for def in self.query.root().defs() {
87 if let (Some(name_tok), Some(body)) = (def.name(), def.body()) {
88 body_nodes.insert(name_tok.text().to_string(), body.as_cst().clone());
89 }
90 }
91
92 for name in symbols.keys() {
93 let mut visited = IndexSet::new();
94 self.format_symbol_tree(name, 0, &defined, &body_nodes, &mut visited, w)?;
95 }
96 Ok(())
97 }
98
99 fn format_symbol_tree(
100 &self,
101 name: &str,
102 indent: usize,
103 defined: &indexmap::IndexSet<&str>,
104 body_nodes: &std::collections::HashMap<String, SyntaxNode>,
105 visited: &mut indexmap::IndexSet<String>,
106 w: &mut impl Write,
107 ) -> std::fmt::Result {
108 let prefix = " ".repeat(indent);
109
110 if visited.contains(name) {
111 writeln!(w, "{}{} (cycle)", prefix, name)?;
112 return Ok(());
113 }
114
115 let is_broken = !defined.contains(name);
116 if is_broken {
117 writeln!(w, "{}{}?", prefix, name)?;
118 return Ok(());
119 }
120
121 let card = body_nodes
122 .get(name)
123 .map(|n| self.cardinality_mark(n))
124 .unwrap_or("");
125 writeln!(w, "{}{}{}", prefix, name, card)?;
126 visited.insert(name.to_string());
127
128 if let Some(body) = self.query.symbol_table.get(name) {
129 let refs_set = collect_refs(body);
130 let mut refs: Vec<_> = refs_set.iter().map(|s| s.as_str()).collect();
131 refs.sort();
132 for r in refs {
133 self.format_symbol_tree(r, indent + 1, defined, body_nodes, visited, w)?;
134 }
135 }
136
137 visited.shift_remove(name);
138 Ok(())
139 }
140
141 fn format_cst(&self, node: &SyntaxNode, indent: usize, w: &mut impl Write) -> std::fmt::Result {
142 let prefix = " ".repeat(indent);
143 let card = self.cardinality_mark(node);
144 let span = self.span_str(node.text_range());
145
146 writeln!(w, "{}{:?}{}{}", prefix, node.kind(), card, span)?;
147
148 for child in node.children_with_tokens() {
149 match child {
150 NodeOrToken::Node(n) => self.format_cst(&n, indent + 1, w)?,
151 NodeOrToken::Token(t) => {
152 if !self.trivia && t.kind().is_trivia() {
153 continue;
154 }
155 let child_prefix = " ".repeat(indent + 1);
156 let child_span = self.span_str(t.text_range());
157 writeln!(
158 w,
159 "{}{:?}{} {:?}",
160 child_prefix,
161 t.kind(),
162 child_span,
163 t.text()
164 )?;
165 }
166 }
167 }
168 Ok(())
169 }
170
171 fn format_root(&self, root: &ast::Root, w: &mut impl Write) -> std::fmt::Result {
172 let card = self.cardinality_mark(root.as_cst());
173 let span = self.span_str(root.text_range());
174 writeln!(w, "Root{}{}", card, span)?;
175
176 for def in root.defs() {
177 self.format_def(&def, 1, w)?;
178 }
179 assert!(
181 root.exprs().next().is_none(),
182 "printer: unexpected bare Expr in Root (parser should wrap in Def)"
183 );
184 Ok(())
185 }
186
187 fn format_def(&self, def: &ast::Def, indent: usize, w: &mut impl Write) -> std::fmt::Result {
188 let prefix = " ".repeat(indent);
189 let card = self.cardinality_mark(def.as_cst());
190 let span = self.span_str(def.text_range());
191 let name = def.name().map(|t| t.text().to_string());
192
193 match name {
194 Some(n) => writeln!(w, "{}Def{}{} {}", prefix, card, span, n)?,
195 None => writeln!(w, "{}Def{}{}", prefix, card, span)?,
196 }
197
198 let Some(body) = def.body() else {
199 return Ok(());
200 };
201 self.format_expr(&body, indent + 1, w)
202 }
203
204 fn format_expr(&self, expr: &ast::Expr, indent: usize, w: &mut impl Write) -> std::fmt::Result {
205 let prefix = " ".repeat(indent);
206 let card = self.cardinality_mark(expr.as_cst());
207 let span = self.span_str(expr.text_range());
208
209 match expr {
210 ast::Expr::NamedNode(n) => {
211 if n.is_any() {
212 writeln!(w, "{}NamedNode{}{} (any)", prefix, card, span)?;
213 } else {
214 let node_type = n.node_type().map(|tok| tok.text().to_string());
215 match node_type {
216 Some(ty) => writeln!(w, "{}NamedNode{}{} {}", prefix, card, span, ty)?,
217 None => writeln!(w, "{}NamedNode{}{}", prefix, card, span)?,
218 }
219 }
220 self.format_tree_children(n.as_cst(), indent + 1, w)?;
221 }
222 ast::Expr::Ref(r) => {
223 let name = r.name().map(|t| t.text().to_string()).unwrap_or_default();
224 writeln!(w, "{}Ref{}{} {}", prefix, card, span, name)?;
225 }
226 ast::Expr::AnonymousNode(a) => {
227 if a.is_any() {
228 writeln!(w, "{}AnonymousNode{}{} (any)", prefix, card, span)?;
229 } else {
230 let value = a.value().map(|t| t.text().to_string()).unwrap_or_default();
231 writeln!(w, "{}AnonymousNode{}{} \"{}\"", prefix, card, span, value)?;
232 }
233 }
234 ast::Expr::AltExpr(a) => {
235 writeln!(w, "{}Alt{}{}", prefix, card, span)?;
236 for branch in a.branches() {
237 self.format_branch(&branch, indent + 1, w)?;
238 }
239 for expr in a.exprs() {
240 self.format_expr(&expr, indent + 1, w)?;
241 }
242 }
243 ast::Expr::SeqExpr(s) => {
244 writeln!(w, "{}Seq{}{}", prefix, card, span)?;
245 self.format_tree_children(s.as_cst(), indent + 1, w)?;
246 }
247 ast::Expr::CapturedExpr(c) => {
248 let name = c.name().map(|t| t.text().to_string()).unwrap_or_default();
249 let type_ann = c
250 .type_annotation()
251 .and_then(|t| t.name())
252 .map(|t| t.text().to_string());
253 match type_ann {
254 Some(ty) => writeln!(
255 w,
256 "{}CapturedExpr{}{} @{} :: {}",
257 prefix, card, span, name, ty
258 )?,
259 None => writeln!(w, "{}CapturedExpr{}{} @{}", prefix, card, span, name)?,
260 }
261 let Some(inner) = c.inner() else {
262 return Ok(());
263 };
264 self.format_expr(&inner, indent + 1, w)?;
265 }
266 ast::Expr::QuantifiedExpr(q) => {
267 let op = q
268 .operator()
269 .map(|t| t.text().to_string())
270 .unwrap_or_default();
271 writeln!(w, "{}QuantifiedExpr{}{} {}", prefix, card, span, op)?;
272 let Some(inner) = q.inner() else {
273 return Ok(());
274 };
275 self.format_expr(&inner, indent + 1, w)?;
276 }
277 ast::Expr::FieldExpr(f) => {
278 let name = f.name().map(|t| t.text().to_string()).unwrap_or_default();
279 writeln!(w, "{}FieldExpr{}{} {}:", prefix, card, span, name)?;
280 let Some(value) = f.value() else {
281 return Ok(());
282 };
283 self.format_expr(&value, indent + 1, w)?;
284 }
285 }
286 Ok(())
287 }
288
289 fn format_tree_children(
290 &self,
291 node: &SyntaxNode,
292 indent: usize,
293 w: &mut impl Write,
294 ) -> std::fmt::Result {
295 use crate::parser::cst::SyntaxKind;
296 for child in node.children() {
297 if child.kind() == SyntaxKind::Anchor {
298 self.mark_anchor(indent, w)?;
299 } else if child.kind() == SyntaxKind::NegatedField {
300 self.format_negated_field(&ast::NegatedField::cast(child).unwrap(), indent, w)?;
301 } else if let Some(expr) = ast::Expr::cast(child) {
302 self.format_expr(&expr, indent, w)?;
303 }
304 }
305 Ok(())
306 }
307
308 fn mark_anchor(&self, indent: usize, w: &mut impl Write) -> std::fmt::Result {
309 let prefix = " ".repeat(indent);
310 writeln!(w, "{}.", prefix)
311 }
312
313 fn format_negated_field(
314 &self,
315 nf: &ast::NegatedField,
316 indent: usize,
317 w: &mut impl Write,
318 ) -> std::fmt::Result {
319 let prefix = " ".repeat(indent);
320 let span = self.span_str(nf.text_range());
321 let name = nf.name().map(|t| t.text().to_string()).unwrap_or_default();
322 writeln!(w, "{}NegatedField{} !{}", prefix, span, name)
323 }
324
325 fn format_branch(
326 &self,
327 branch: &ast::Branch,
328 indent: usize,
329 w: &mut impl Write,
330 ) -> std::fmt::Result {
331 let prefix = " ".repeat(indent);
332 let card = self.cardinality_mark(branch.as_cst());
333 let span = self.span_str(branch.text_range());
334 let label = branch.label().map(|t| t.text().to_string());
335
336 match label {
337 Some(l) => writeln!(w, "{}Branch{}{} {}:", prefix, card, span, l)?,
338 None => writeln!(w, "{}Branch{}{}", prefix, card, span)?,
339 }
340
341 let Some(body) = branch.body() else {
342 return Ok(());
343 };
344 self.format_expr(&body, indent + 1, w)
345 }
346
347 fn cardinality_mark(&self, node: &SyntaxNode) -> &'static str {
348 if !self.cardinalities {
349 return "";
350 }
351 match self.query.shape_cardinality(node) {
352 ShapeCardinality::One => "¹",
353 ShapeCardinality::Many => "⁺",
354 ShapeCardinality::Invalid => "⁻",
355 }
356 }
357
358 fn span_str(&self, range: rowan::TextRange) -> String {
359 if !self.spans {
360 return String::new();
361 }
362 format!(
363 " [{}..{}]",
364 u32::from(range.start()),
365 u32::from(range.end())
366 )
367 }
368}
369
370impl Query<'_> {
371 pub fn printer(&self) -> QueryPrinter<'_, '_> {
372 QueryPrinter::new(self)
373 }
374}
375
376fn collect_refs(expr: &Expr) -> IndexSet<String> {
377 expr.as_cst()
378 .descendants()
379 .filter_map(ast::Ref::cast)
380 .filter_map(|r| r.name())
381 .map(|tok| tok.text().to_string())
382 .collect()
383}