use crate::label::DebugInfoOverride;
use super::*;
#[doc(hidden)]
#[derive(Debug)]
pub enum SeqInfo {
Char(char),
String(String),
Opaque(String),
Unknown(String),
}
impl SeqInfo {
fn show(&self) -> String {
match self {
Self::Char(c) => format!("'{c}'"),
Self::String(s) => format!("\"{s}\""),
Self::Opaque(s) => s.to_string(),
Self::Unknown(s) => s.to_string(),
}
}
}
#[doc(hidden)]
#[derive(Debug)]
pub enum NodeInfo {
Unknown(String),
Recursive(usize, Box<Self>),
RecursiveRef(usize),
Repeated(core::ops::Range<u64>, Box<Self>),
SeparatedBy(Box<Self>, Box<Self>),
Choice(Vec<Self>),
Any,
Just(SeqInfo),
OneOf(SeqInfo),
NoneOf(SeqInfo),
Then(Box<Self>, Box<Self>),
Padded(Box<Self>),
Filter(Box<Self>),
OrNot(Box<Self>),
Labelled(String, Box<Self>, Option<DebugInfoOverride>),
NestedIn(Box<Self>, Box<Self>),
}
impl NodeInfo {
fn bnf_inner(&self, depth: usize, defs: &mut Vec<String>, ctx: usize) -> String {
match self {
Self::Unknown(s) => format!("<unknown: {s}>"),
Self::Recursive(r, inner) => {
let def = inner.bnf_inner(1, defs, 0);
defs.push(format!("def_{r} ::= {def};"));
format!("def_{r}")
}
Self::Repeated(_, inner) => format!("{{ {} }}", inner.bnf_inner(depth, defs, 0)),
Self::SeparatedBy(inner, sep) => format!(
"{{ {} [{}]}}",
inner.bnf_inner(depth, defs, 2),
sep.bnf_inner(depth, defs, 0)
),
Self::OrNot(inner) => format!("[ {} ]", inner.bnf_inner(depth, defs, 0)),
Self::Choice(inners) => {
let s = inners
.iter()
.map(|i| i.bnf_inner(depth + 1, defs, 1))
.collect::<Vec<_>>()
.join(&format!("\n{}| ", " ".repeat(depth)));
if ctx == 1 || ctx == 0 {
s
} else {
format!("({s})")
}
}
Self::Just(seq) => seq.show(),
Self::OneOf(seq) => format!("one_of({})", seq.show()),
Self::NoneOf(seq) => format!("none_of({})", seq.show()),
Self::Then(a, b) => {
let s = format!(
"{} {}",
a.bnf_inner(depth, defs, 2),
b.bnf_inner(depth, defs, 2)
);
if ctx == 1 || ctx == 0 {
s
} else {
format!("({s})")
}
}
Self::Any => "any".to_string(),
Self::NestedIn(a, b) => format!(
"({}).nested_in({})",
a.bnf_inner(depth, defs, 0),
b.bnf_inner(depth, defs, 0)
),
Self::Padded(inner) | Self::Filter(inner) | Self::Labelled(_, inner, None) => {
inner.bnf_inner(depth, defs, ctx)
}
Self::Labelled(label, _, Some(_)) => label.clone(),
Self::RecursiveRef(r) => format!("def_{r}"),
}
}
fn railroad_inner(&self, defs: &mut Vec<Box<dyn railroad::Node>>) -> Box<dyn railroad::Node> {
use railroad::*;
match self {
Self::Unknown(s) => Box::new(Comment::new(s.to_string())),
Self::Recursive(r, inner) => {
let inner = inner.railroad_inner(defs);
defs.push(Box::new(LabeledBox::new(
Sequence::new(vec![
Box::new(SimpleStart) as Box<dyn Node>,
inner,
Box::new(SimpleEnd),
]),
Terminal::new(format!("def_{r}")),
)));
Box::new(Terminal::new(format!("def_{r}")))
}
Self::Repeated(_, inner) => Box::new(Repeat::new(inner.railroad_inner(defs), Empty)),
Self::SeparatedBy(inner, sep) => Box::new(Repeat::new(
inner.railroad_inner(defs),
sep.railroad_inner(defs),
)),
Self::Choice(inners) => Box::new(Choice::new(
inners.iter().map(|i| i.railroad_inner(defs)).collect(),
)),
Self::Just(seq) => Box::new(Terminal::new(seq.show())),
Self::OneOf(seq) => Box::new(Terminal::new(format!("one_of({})", seq.show()))),
Self::NoneOf(seq) => Box::new(Terminal::new(format!("none_of({})", seq.show()))),
Self::Then(a, b) => Box::new(Sequence::new(vec![
a.railroad_inner(defs),
b.railroad_inner(defs),
])),
Self::RecursiveRef(r) => Box::new(Terminal::new(format!("def_{r}"))),
Self::Padded(inner) => inner.railroad_inner(defs),
Self::Filter(inner) => Box::new(LabeledBox::new(
inner.railroad_inner(defs),
Comment::new("filtered".to_string()),
)),
Self::Labelled(label, inner, None) => Box::new(LabeledBox::new(
inner.railroad_inner(defs),
NonTerminal::new(label.to_string()),
)),
Self::Labelled(label, _, Some(DebugInfoOverride::Terminal)) => {
Box::new(Terminal::new(label.to_string()))
}
Self::Labelled(label, _, Some(DebugInfoOverride::NonTerminal)) => {
Box::new(NonTerminal::new(label.to_string()))
}
Self::NestedIn(inner, outer) => Box::new(LabeledBox::new(
Box::new(LabeledBox::new(
outer.railroad_inner(defs),
NonTerminal::new("outer".to_string()),
)),
Box::new(LabeledBox::new(
Sequence::new(vec![
Box::new(SimpleStart) as Box<dyn Node>,
inner.railroad_inner(defs),
Box::new(SimpleEnd),
]),
NonTerminal::new("inner".to_string()),
)),
)),
Self::Any => Box::new(Terminal::new("any".to_string())),
Self::OrNot(inner) => Box::new(Optional::new(inner.railroad_inner(defs))),
}
}
}
#[doc(hidden)]
#[derive(Default)]
pub struct NodeScope {
rec_count: usize,
rec: Vec<(usize, usize)>,
}
impl NodeScope {
pub fn lookup_rec(&mut self, ptr: usize, f: impl FnOnce(&mut Self) -> NodeInfo) -> NodeInfo {
self.rec
.iter()
.rev()
.find(|(p, _)| *p == ptr)
.map(|(_, r)| NodeInfo::RecursiveRef(*r))
.unwrap_or_else(|| {
self.rec_count += 1;
self.rec.push((ptr, self.rec_count));
NodeInfo::Recursive(self.rec_count, Box::new(f(self)))
})
}
}
pub struct DebugInfo<'a> {
pub(crate) node_info: NodeInfo,
pub(crate) phantom: PhantomData<&'a ()>,
}
impl<'a> DebugInfo<'a> {
pub fn to_ebnf(&self) -> String {
let mut defs = Vec::new();
let def = self.node_info.bnf_inner(1, &mut defs, 0);
defs.push(def);
defs.join("\n\n")
}
pub fn to_railroad_svg(&self) -> impl core::fmt::Display + Clone {
use railroad::*;
let mut seq = Sequence::default();
let mut defs = Vec::new();
let def = self.node_info.railroad_inner(&mut defs);
defs.push(def);
seq.push(Rc::new(VerticalGrid::new(defs)) as Rc<dyn Node>);
let mut dia = Diagram::new(seq);
dia.add_element(
svg::Element::new("style")
.set("type", "text/css")
.text(DEFAULT_CSS),
);
dia
}
}