diff --git a/src/combinators.rs b/src/combinators.rs index 9f8e0fe..5032b90 100644 --- a/src/combinators.rs +++ b/src/combinators.rs @@ -104,8 +104,12 @@ where fn representation(&self) -> Representation { let at_least = self.at_least.unwrap_or(0); - let at_most = self.at_most.unwrap_or(u32::MAX); - let production = EBNF::Repeated(Box::new(self.inner_parser.representation().production())); + //TODO flesh this out better + let _at_most = self.at_most.unwrap_or(u32::MAX); + let production = EBNF::Repeated { + inner: Box::new(self.inner_parser.representation().production()), + more_than_once: at_least >= 1, + }; Representation::new().with_production(production) } } @@ -204,6 +208,19 @@ where Ok((results, input)) } + + fn representation(&self) -> Representation { + let inner = &self.inner_repeated.inner_parser; + let at_least = self.inner_repeated.at_least.unwrap_or(0); + let inner_production = inner.representation().production(); + let delimiter_production = self.delimiter.representation().production(); + + let production = EBNF::Repeated { + inner: Box::new(EBNF::Sequence(vec![inner_production, delimiter_production])), + more_than_once: at_least >= 1, + }; + Representation::new().with_production(production) + } } pub fn optional(parser: impl Parser) -> impl Parser, E> diff --git a/src/map.rs b/src/map.rs index 7477308..c4de7ba 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,13 +1,16 @@ -use crate::Parser; +use crate::{representation::Representation, Parser, ParserExtension}; pub fn map(parser: P, map_fn: F) -> impl Parser where P: Parser, F: Fn(O1) -> O2, { - move |input| { + let production = parser.representation().production(); + (move |input| { parser .parse(input) .map(|(result, rest)| (map_fn(result), rest)) - } + }) + .to_anno() + .with_repr(Representation::new().with_production(production)) } diff --git a/src/representation.rs b/src/representation.rs index b8f4fba..ce2b90b 100644 --- a/src/representation.rs +++ b/src/representation.rs @@ -38,13 +38,33 @@ pub enum EBNF { StringTerminal(String), LabeledTerminal(String), Alternation(Vec), - Repeated(Box), + Sequence(Vec), + Repeated { + inner: Box, + 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, + } + } } impl fmt::Display for EBNF { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - EBNF::None => write!(f, "none"), + //TODO should try to show the name if possible + EBNF::None => write!(f, ""), EBNF::CharTerminal(ch) => write!(f, "'{ch}'"), EBNF::Alternation(items) => { for item in intersperse_option(items.iter()) { @@ -58,7 +78,27 @@ impl fmt::Display for EBNF { EBNF::Nonterminal(name) => write!(f, "{name}"), EBNF::StringTerminal(term) => write!(f, r#""{term}""#), EBNF::LabeledTerminal(s) => write!(f, "<{s}>"), - EBNF::Repeated(inner) => write!(f, "[ {inner} ]"), + 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, "") + } } } } diff --git a/src/test/mod.rs b/src/test/mod.rs index 045f18f..6f4363e 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -81,11 +81,14 @@ fn test_repeated() { let output = parser.parse("bongo bongo bongo bongo"); let output = output.unwrap(); assert_eq!(output.0, vec!["bongo", "bongo", "bongo", "bongo"]); + assert_eq!(parser.representation().show(), r#"["bongo" ' '+]*"#); let bongos = repeated(literal("bongo")); let output = bongos.parse("tra la la").unwrap(); assert_eq!(output.0.len(), 0); assert_eq!(output.1, "tra la la"); + + assert_eq!(bongos.representation().show(), r#""bongo"*"#); } #[test]