diff --git a/src/primitives.rs b/src/primitives.rs index 7fb1243..8af564d 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -23,7 +23,7 @@ pub fn literal_char<'a>(expected: char) -> impl Parser<&'a str, char, ()> { } pub fn one_of<'a>(items: &'static str) -> impl Parser<&'a str, char, ()> { - move |input: &'a str| { + (move |input: &'a str| { if let Some(ch) = input.chars().next() { if items.contains(ch) { let (_first, rest) = input.split_at(1); @@ -31,7 +31,16 @@ pub fn one_of<'a>(items: &'static str) -> impl Parser<&'a str, char, ()> { } } Err(((), input)) - } + }) + .to_anno() + .with_repr( + Representation::new().with_production(EBNF::Alternation( + items + .chars() + .map(|ch| EBNF::CharTerminal(ch)) + .collect::>(), + )), + ) } /// Parses a standard identifier in a programming language @@ -56,10 +65,22 @@ pub fn identifier(input: &str) -> ParseResult<&str, String, ()> { Ok((buf, &input[next_index..])) } -pub fn whitespace(input: &str) -> ParseResult<&str, char, ()> { - match input.chars().next() { - Some(ch) if ch.is_whitespace() => Ok((ch, &input[1..])), - _ => Err(((), input)), +pub struct Whitespace; + +impl Parser<&str, char, ()> for Whitespace { + fn name(&self) -> Option { + Some("whitespace".into()) + } + + fn representation(&self) -> Representation { + Representation::new().with_production(EBNF::LabeledTerminal("whitespace".into())) + } + + fn parse<'a>(&self, input: &'a str) -> ParseResult<&'a str, char, ()> { + match input.chars().next() { + Some(ch) if ch.is_whitespace() => Ok((ch, &input[1..])), + _ => Err(((), input)), + } } } @@ -68,11 +89,22 @@ mod tests { use super::*; #[test] - fn literals() { + fn primitive_parsers() { let parser = literal_char('f'); assert_eq!(Ok(('f', "unky")), parser.parse("funky")); let repr = parser.representation(); assert!(matches!(repr.production(), EBNF::CharTerminal('f'))); + + let parser = one_of("asdf"); + let production = parser.representation().production(); + assert!( + matches!(production, EBNF::Alternation(v) if matches!(v.as_slice(), [ + EBNF::CharTerminal('a'), + EBNF::CharTerminal('s'), + EBNF::CharTerminal('d'), + EBNF::CharTerminal('f'), + ])) + ); } } diff --git a/src/representation.rs b/src/representation.rs index f507817..89c77dd 100644 --- a/src/representation.rs +++ b/src/representation.rs @@ -36,6 +36,7 @@ pub enum EBNF { Nonterminal(String), CharTerminal(char), StringTerminal(String), + LabeledTerminal(String), Alternation(Vec), } @@ -55,6 +56,7 @@ impl fmt::Display for EBNF { } EBNF::Nonterminal(name) => write!(f, "{name}"), EBNF::StringTerminal(term) => write!(f, r#""{term}""#), + EBNF::LabeledTerminal(s) => write!(f, "<{s}>"), } } } diff --git a/src/test/sexp.rs b/src/test/sexp.rs index 52fae60..f4814ad 100644 --- a/src/test/sexp.rs +++ b/src/test/sexp.rs @@ -55,7 +55,7 @@ fn parse_list(input: &str) -> ParseResult<&str, Expr, ()> { literal_char('(') .ignore_then( repeated(parse_expr) - .separated_by(repeated(whitespace).at_least(1).to(())) + .separated_by(repeated(Whitespace).at_least(1).to(())) .allow_trailing(true), ) .then_ignore(literal_char(')')) @@ -70,7 +70,7 @@ fn test_parse_list() { } fn parse_sexp(input: &str) -> ParseResult<&str, Expr, ()> { - parse_list.surrounded_by(repeated(whitespace)).parse(input) + parse_list.surrounded_by(repeated(Whitespace)).parse(input) } #[test]