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