rust-parser-combinator/src/representation.rs

130 lines
3.5 KiB
Rust
Raw Normal View History

2024-01-31 00:24:18 -08:00
use std::fmt;
use crate::util::intersperse_option;
2024-01-31 02:15:50 -08:00
#[derive(Debug, Clone)]
2024-01-31 00:24:18 -08:00
pub struct Representation {
production_output: EBNF,
}
2024-01-30 00:15:00 -08:00
impl Representation {
2024-01-31 00:24:18 -08:00
pub fn show(&self) -> String {
self.production_output.to_string()
2024-01-30 00:15:00 -08:00
}
2024-01-31 02:15:50 -08:00
pub fn production(&self) -> EBNF {
self.production_output.clone()
}
2024-01-30 00:15:00 -08:00
pub fn new() -> Self {
2024-01-31 00:24:18 -08:00
Self {
production_output: EBNF::None,
}
}
2024-01-31 02:15:50 -08:00
pub fn with_production(self, production_output: EBNF) -> Self {
Self {
production_output,
..self
}
2024-01-31 00:24:18 -08:00
}
}
2024-01-31 02:15:50 -08:00
#[derive(Debug, Clone)]
2024-01-31 00:24:18 -08:00
pub enum EBNF {
None,
2024-01-31 00:31:16 -08:00
Nonterminal(String),
2024-01-31 00:24:18 -08:00
CharTerminal(char),
2024-01-31 00:47:45 -08:00
StringTerminal(String),
2024-01-31 03:13:46 -08:00
LabeledTerminal(String),
2024-01-31 00:24:18 -08:00
Alternation(Vec<EBNF>),
2024-02-02 02:39:50 -08:00
Sequence(Vec<EBNF>),
Repeated {
inner: Box<EBNF>,
more_than_once: bool,
},
}
impl EBNF {
fn needs_wrapping(&self) -> bool {
match self {
EBNF::None => false,
EBNF::Nonterminal(_) => false,
EBNF::CharTerminal(_) => false,
EBNF::StringTerminal(_) => false,
EBNF::LabeledTerminal(_) => false,
EBNF::Sequence(items) => items.len() > 1,
EBNF::Alternation(_) => true,
EBNF::Repeated { .. } => false,
}
}
2024-01-31 00:24:18 -08:00
}
impl fmt::Display for EBNF {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
2024-02-02 02:39:50 -08:00
//TODO should try to show the name if possible
EBNF::None => write!(f, "<no-representation>"),
2024-01-31 00:24:18 -08:00
EBNF::CharTerminal(ch) => write!(f, "'{ch}'"),
EBNF::Alternation(items) => {
for item in intersperse_option(items.iter()) {
match item {
None => write!(f, " | ")?,
Some(item) => write!(f, "{item}")?,
}
}
write!(f, "")
}
2024-01-31 00:31:16 -08:00
EBNF::Nonterminal(name) => write!(f, "{name}"),
2024-01-31 00:47:45 -08:00
EBNF::StringTerminal(term) => write!(f, r#""{term}""#),
2024-01-31 03:13:46 -08:00
EBNF::LabeledTerminal(s) => write!(f, "<{s}>"),
2024-02-02 02:39:50 -08:00
EBNF::Repeated {
inner,
more_than_once,
} => {
let sigil = if *more_than_once { '+' } else { '*' };
if inner.needs_wrapping() {
write!(f, "[{inner}]{sigil}")
} else {
write!(f, "{inner}{sigil}")
}
}
EBNF::Sequence(items) => {
for item in intersperse_option(items.iter()) {
if let Some(item) = item {
write!(f, "{item}")?;
} else {
write!(f, " ")?;
}
}
write!(f, "")
}
2024-01-31 00:24:18 -08:00
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ebnf_print() {
let example = EBNF::Alternation(vec![
EBNF::CharTerminal('f'),
EBNF::CharTerminal('a'),
EBNF::CharTerminal('k'),
EBNF::CharTerminal('e'),
]);
assert_eq!(example.to_string(), "'f' | 'a' | 'k' | 'e'");
2024-01-31 00:31:16 -08:00
let example = EBNF::Alternation(vec![
EBNF::Nonterminal("other-rule".into()),
EBNF::CharTerminal('q'),
EBNF::CharTerminal('m'),
2024-01-31 00:47:45 -08:00
EBNF::StringTerminal("focus".into()),
2024-01-31 00:31:16 -08:00
]);
2024-01-31 00:47:45 -08:00
assert_eq!(example.to_string(), "other-rule | 'q' | 'm' | \"focus\"");
2024-01-30 00:15:00 -08:00
}
}