Sexp parser test

This commit is contained in:
Greg Shuflin 2024-01-29 03:55:24 -08:00
parent ea6a513572
commit 51d7380761
3 changed files with 63 additions and 10 deletions

View File

@ -22,6 +22,8 @@ pub trait ParserExtension<I, O, E>: Parser<I, O, E> {
fn to<O2: Clone>(self, item: O2) -> impl Parser<I, O2, E>; fn to<O2: Clone>(self, item: O2) -> impl Parser<I, O2, E>;
fn then<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, (O, O2), E>; fn then<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, (O, O2), E>;
fn then_ignore<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, O, E>;
fn ignore_then<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, O2, E>;
} }
impl<T, I, O, E> ParserExtension<I, O, E> for T impl<T, I, O, E> ParserExtension<I, O, E> for T
@ -42,4 +44,11 @@ where
fn then<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, (O, O2), E> { fn then<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, (O, O2), E> {
seq2(self, next) seq2(self, next)
} }
fn then_ignore<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, O, E> {
seq2(self, next).map(|(this, _)| this)
}
fn ignore_then<O2, P: Parser<I, O2, E>>(self, next: P) -> impl Parser<I, O2, E> {
seq2(self, next).map(|(_, next)| next)
}
} }

View File

@ -48,6 +48,13 @@ pub fn identifier(input: &str) -> ParseResult<&str, String, ()> {
Ok((buf, &input[next_index..])) 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -15,23 +15,60 @@ enum Atom {
Symbol(String), Symbol(String),
} }
#[test] fn parse_bool(input: &str) -> ParseResult<&str, Atom, ()> {
fn parse_sexp() { choice((
let parse_bool = choice((
literal("#t").to(Atom::Bool(true)), literal("#t").to(Atom::Bool(true)),
literal("#f").to(Atom::Bool(false)), literal("#f").to(Atom::Bool(false)),
)); ))
.parse(input)
}
let parse_symbol = identifier.map(Atom::Symbol); fn parse_symbol(input: &str) -> ParseResult<&str, Atom, ()> {
let parse_number = repeated(one_of("1234567890")) identifier.map(Atom::Symbol).parse(input)
}
fn parse_number(input: &str) -> ParseResult<&str, Atom, ()> {
repeated(one_of("1234567890"))
.at_least(1) .at_least(1)
.map(|n| Atom::Num(n.iter().collect::<String>().parse::<i64>().unwrap())); .map(|n| Atom::Num(n.iter().collect::<String>().parse::<i64>().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)); 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)); 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, "");
}