From 51d7380761ffaae00f0df9faabe6af99eb011e4f Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Mon, 29 Jan 2024 03:55:24 -0800 Subject: [PATCH] Sexp parser test --- src/parser.rs | 9 ++++++++ src/primitives.rs | 7 ++++++ src/test/sexp.rs | 57 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 14ab5a5..1bf08ef 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -22,6 +22,8 @@ pub trait ParserExtension: Parser { fn to(self, item: O2) -> impl Parser; fn then>(self, next: P) -> impl Parser; + fn then_ignore>(self, next: P) -> impl Parser; + fn ignore_then>(self, next: P) -> impl Parser; } impl ParserExtension for T @@ -42,4 +44,11 @@ where fn then>(self, next: P) -> impl Parser { seq2(self, next) } + + fn then_ignore>(self, next: P) -> impl Parser { + seq2(self, next).map(|(this, _)| this) + } + fn ignore_then>(self, next: P) -> impl Parser { + seq2(self, next).map(|(_, next)| next) + } } diff --git a/src/primitives.rs b/src/primitives.rs index 45196c8..a6f74d8 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -48,6 +48,13 @@ 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)), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/test/sexp.rs b/src/test/sexp.rs index 5b2adad..0a4d7bd 100644 --- a/src/test/sexp.rs +++ b/src/test/sexp.rs @@ -15,23 +15,60 @@ enum Atom { Symbol(String), } -#[test] -fn parse_sexp() { - let parse_bool = choice(( +fn parse_bool(input: &str) -> ParseResult<&str, Atom, ()> { + choice(( literal("#t").to(Atom::Bool(true)), literal("#f").to(Atom::Bool(false)), - )); + )) + .parse(input) +} - let parse_symbol = identifier.map(Atom::Symbol); - let parse_number = repeated(one_of("1234567890")) +fn parse_symbol(input: &str) -> ParseResult<&str, Atom, ()> { + identifier.map(Atom::Symbol).parse(input) +} + +fn parse_number(input: &str) -> ParseResult<&str, Atom, ()> { + repeated(one_of("1234567890")) .at_least(1) - .map(|n| Atom::Num(n.iter().collect::().parse::().unwrap())); + .map(|n| Atom::Num(n.iter().collect::().parse::().unwrap())) + .parse(input) +} - let parser = choice((parse_symbol, parse_bool, parse_number)); +fn parse_atom(input: &str) -> ParseResult<&str, Atom, ()> { + choice((parse_symbol, parse_bool, parse_number)).parse(input) +} - let output = parser.parse("#t").unwrap(); +#[test] +fn test_parse_atom() { + let output = parse_atom.parse("#t").unwrap(); assert_eq!(output.0, Atom::Bool(true)); - let output = parser.parse("384").unwrap(); + let output = parse_atom.parse("384").unwrap(); assert_eq!(output.0, Atom::Num(384)); } + +fn parse_expr(input: &str) -> ParseResult<&str, Expr, ()> { + choice((parse_list, parse_atom.map(Expr::Atom))).parse(input) +} + +fn parse_list(input: &str) -> ParseResult<&str, Expr, ()> { + literal_char('(') + .ignore_then(repeated(parse_atom.map(Expr::Atom)).separated_by(whitespace.to(()))) + .then_ignore(literal_char(')')) + .map(Expr::List) + .parse(input) +} + +#[test] +fn test_parse_sexp() { + let output = parse_expr("(add 1 2)").unwrap(); + assert_eq!( + output.0, + Expr::List(vec![ + Expr::Atom(Atom::Symbol("add".to_string())), + Expr::Atom(Atom::Num(1)), + Expr::Atom(Atom::Num(2)) + ]) + ); + assert_eq!(output.1, ""); +}