Skip to main content

chumsky/
debug.rs

1//! Unstable utilities for debugging in-development parsers.
2//!
3//! See [`Parser::debug`].
4
5use crate::label::DebugInfoOverride;
6
7use super::*;
8
9#[doc(hidden)]
10#[derive(Debug)]
11pub enum SeqInfo {
12    Char(char),
13    String(String),
14    Opaque(String),
15    Unknown(String),
16}
17
18impl SeqInfo {
19    fn show(&self) -> String {
20        match self {
21            Self::Char(c) => format!("'{c}'"),
22            Self::String(s) => format!("\"{s}\""),
23            Self::Opaque(s) => s.to_string(),
24            Self::Unknown(s) => s.to_string(),
25        }
26    }
27}
28
29#[doc(hidden)]
30#[derive(Debug)]
31pub enum NodeInfo {
32    Unknown(String),
33    // The root of a recursive definition
34    Recursive(usize, Box<Self>),
35    RecursiveRef(usize),
36    Repeated(core::ops::Range<u64>, Box<Self>),
37    SeparatedBy(Box<Self>, Box<Self>),
38    Choice(Vec<Self>),
39    Any,
40    Just(SeqInfo),
41    OneOf(SeqInfo),
42    NoneOf(SeqInfo),
43    Then(Box<Self>, Box<Self>),
44    Padded(Box<Self>),
45    Filter(Box<Self>),
46    OrNot(Box<Self>),
47    Labelled(String, Box<Self>, Option<DebugInfoOverride>),
48    NestedIn(Box<Self>, Box<Self>),
49}
50
51impl NodeInfo {
52    // ctx { 0 = any, 1 = or, 2 = then }
53    fn bnf_inner(&self, depth: usize, defs: &mut Vec<String>, ctx: usize) -> String {
54        match self {
55            Self::Unknown(s) => format!("<unknown: {s}>"),
56            Self::Recursive(r, inner) => {
57                let def = inner.bnf_inner(1, defs, 0);
58                defs.push(format!("def_{r} ::= {def};"));
59                format!("def_{r}")
60            }
61            Self::Repeated(_, inner) => format!("{{ {} }}", inner.bnf_inner(depth, defs, 0)),
62            Self::SeparatedBy(inner, sep) => format!(
63                "{{ {} [{}]}}",
64                inner.bnf_inner(depth, defs, 2),
65                sep.bnf_inner(depth, defs, 0)
66            ),
67            Self::OrNot(inner) => format!("[ {} ]", inner.bnf_inner(depth, defs, 0)),
68            Self::Choice(inners) => {
69                let s = inners
70                    .iter()
71                    .map(|i| i.bnf_inner(depth + 1, defs, 1))
72                    .collect::<Vec<_>>()
73                    .join(&format!("\n{}| ", "  ".repeat(depth)));
74                if ctx == 1 || ctx == 0 {
75                    s
76                } else {
77                    format!("({s})")
78                }
79            }
80            Self::Just(seq) => seq.show(),
81            Self::OneOf(seq) => format!("one_of({})", seq.show()),
82            Self::NoneOf(seq) => format!("none_of({})", seq.show()),
83            Self::Then(a, b) => {
84                let s = format!(
85                    "{} {}",
86                    a.bnf_inner(depth, defs, 2),
87                    b.bnf_inner(depth, defs, 2)
88                );
89                if ctx == 1 || ctx == 0 {
90                    s
91                } else {
92                    format!("({s})")
93                }
94            }
95            Self::Any => "any".to_string(),
96            Self::NestedIn(a, b) => format!(
97                "({}).nested_in({})",
98                a.bnf_inner(depth, defs, 0),
99                b.bnf_inner(depth, defs, 0)
100            ),
101            Self::Padded(inner) | Self::Filter(inner) | Self::Labelled(_, inner, None) => {
102                inner.bnf_inner(depth, defs, ctx)
103            }
104            Self::Labelled(label, _, Some(_)) => label.clone(),
105            Self::RecursiveRef(r) => format!("def_{r}"),
106        }
107    }
108
109    fn railroad_inner(&self, defs: &mut Vec<Box<dyn railroad::Node>>) -> Box<dyn railroad::Node> {
110        use railroad::*;
111        match self {
112            Self::Unknown(s) => Box::new(Comment::new(s.to_string())),
113            Self::Recursive(r, inner) => {
114                let inner = inner.railroad_inner(defs);
115                defs.push(Box::new(LabeledBox::new(
116                    Sequence::new(vec![
117                        Box::new(SimpleStart) as Box<dyn Node>,
118                        inner,
119                        Box::new(SimpleEnd),
120                    ]),
121                    Terminal::new(format!("def_{r}")),
122                )));
123                Box::new(Terminal::new(format!("def_{r}")))
124            }
125            Self::Repeated(_, inner) => Box::new(Repeat::new(inner.railroad_inner(defs), Empty)),
126            Self::SeparatedBy(inner, sep) => Box::new(Repeat::new(
127                inner.railroad_inner(defs),
128                sep.railroad_inner(defs),
129            )),
130            Self::Choice(inners) => Box::new(Choice::new(
131                inners.iter().map(|i| i.railroad_inner(defs)).collect(),
132            )),
133            Self::Just(seq) => Box::new(Terminal::new(seq.show())),
134            Self::OneOf(seq) => Box::new(Terminal::new(format!("one_of({})", seq.show()))),
135            Self::NoneOf(seq) => Box::new(Terminal::new(format!("none_of({})", seq.show()))),
136            Self::Then(a, b) => Box::new(Sequence::new(vec![
137                a.railroad_inner(defs),
138                b.railroad_inner(defs),
139            ])),
140            Self::RecursiveRef(r) => Box::new(Terminal::new(format!("def_{r}"))),
141            // Self::Padded(inner) => Box::new(Sequence::new(vec![
142            //     Box::new(Terminal::new(format!("whitespace"))) as Box<dyn Node>,
143            //     inner.railroad_inner(defs),
144            //     Box::new(Terminal::new(format!("whitespace"))),
145            // ])),
146            Self::Padded(inner) => inner.railroad_inner(defs),
147            Self::Filter(inner) => Box::new(LabeledBox::new(
148                inner.railroad_inner(defs),
149                Comment::new("filtered".to_string()),
150            )),
151            Self::Labelled(label, inner, None) => Box::new(LabeledBox::new(
152                inner.railroad_inner(defs),
153                NonTerminal::new(label.to_string()),
154            )),
155            Self::Labelled(label, _, Some(DebugInfoOverride::Terminal)) => {
156                Box::new(Terminal::new(label.to_string()))
157            }
158            Self::Labelled(label, _, Some(DebugInfoOverride::NonTerminal)) => {
159                Box::new(NonTerminal::new(label.to_string()))
160            }
161            Self::NestedIn(inner, outer) => Box::new(LabeledBox::new(
162                Box::new(LabeledBox::new(
163                    outer.railroad_inner(defs),
164                    NonTerminal::new("outer".to_string()),
165                )),
166                Box::new(LabeledBox::new(
167                    Sequence::new(vec![
168                        Box::new(SimpleStart) as Box<dyn Node>,
169                        inner.railroad_inner(defs),
170                        Box::new(SimpleEnd),
171                    ]),
172                    NonTerminal::new("inner".to_string()),
173                )),
174            )),
175            Self::Any => Box::new(Terminal::new("any".to_string())),
176            Self::OrNot(inner) => Box::new(Optional::new(inner.railroad_inner(defs))),
177        }
178    }
179}
180
181#[doc(hidden)]
182#[derive(Default)]
183pub struct NodeScope {
184    rec_count: usize,
185    // (ptr, index, name)
186    rec: Vec<(usize, usize)>,
187}
188
189impl NodeScope {
190    pub fn lookup_rec(&mut self, ptr: usize, f: impl FnOnce(&mut Self) -> NodeInfo) -> NodeInfo {
191        self.rec
192            .iter()
193            .rev()
194            .find(|(p, _)| *p == ptr)
195            .map(|(_, r)| NodeInfo::RecursiveRef(*r))
196            .unwrap_or_else(|| {
197                self.rec_count += 1;
198                self.rec.push((ptr, self.rec_count));
199                NodeInfo::Recursive(self.rec_count, Box::new(f(self)))
200            })
201    }
202}
203
204/// A catch-all box of tricks for debugging a parser.
205pub struct DebugInfo<'a> {
206    pub(crate) node_info: NodeInfo,
207    pub(crate) phantom: PhantomData<&'a ()>,
208}
209
210impl<'a> DebugInfo<'a> {
211    /// Generate a string containing an [eBNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form#EBNF) grammar for this parser.
212    ///
213    /// The exact format of the grammar definition is not specified, and is only intended to be read by a human being.
214    pub fn to_ebnf(&self) -> String {
215        let mut defs = Vec::new();
216        let def = self.node_info.bnf_inner(1, &mut defs, 0);
217        defs.push(def);
218        defs.join("\n\n")
219    }
220
221    /// Generate a human-readable [railroad diagram](https://en.wikipedia.org/wiki/Syntax_diagram) that describes the grammar.
222    ///
223    /// The resulting diagram is in [SVG](https://en.wikipedia.org/wiki/SVG) format and may be printed to the console, written to a file, etc.
224    ///
225    /// The exact format of the diagram is not specified and its quality may depend heavily on annotations, such as [`Parser::labelled`].
226    /// Aspects of the grammar may also not be captured. For example, context-sensitive parsers are largely ignored today.
227    ///
228    /// # Examples
229    ///
230    /// Here is a generated railroad diagram for the example JSON parser. See `examples/json.rs` in the repository.
231    ///
232    /// ![A railroad diagram of the grammar for JSON](https://codeberg.org/zesterer/chumsky/src/branch/main/misc/json-railroad.svg)
233    pub fn to_railroad_svg(&self) -> impl core::fmt::Display + Clone {
234        use railroad::*;
235
236        let mut seq = Sequence::default();
237        let mut defs = Vec::new();
238        let def = self.node_info.railroad_inner(&mut defs);
239        defs.push(def);
240        seq.push(Rc::new(VerticalGrid::new(defs)) as Rc<dyn Node>);
241
242        let mut dia = Diagram::new(seq);
243
244        dia.add_element(
245            svg::Element::new("style")
246                .set("type", "text/css")
247                .text(DEFAULT_CSS),
248        );
249        dia
250    }
251}