98 lines
2.3 KiB
Rust
98 lines
2.3 KiB
Rust
use crate::*;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
enum Expr {
|
|
Atom(Atom),
|
|
List(Vec<Expr>),
|
|
Quote(Vec<Expr>),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
enum Atom {
|
|
Num(i64),
|
|
Str(String),
|
|
Bool(bool),
|
|
Symbol(String),
|
|
}
|
|
|
|
fn parse_bool(input: &str) -> ParseResult<&str, Atom, ()> {
|
|
choice((
|
|
literal("#t").to(Atom::Bool(true)),
|
|
literal("#f").to(Atom::Bool(false)),
|
|
))
|
|
.parse(input)
|
|
}
|
|
|
|
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::<String>().parse::<i64>().unwrap()))
|
|
.parse(input)
|
|
}
|
|
|
|
fn parse_atom(input: &str) -> ParseResult<&str, Atom, ()> {
|
|
choice((parse_symbol, parse_bool, parse_number)).parse(input)
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_atom() {
|
|
let output = parse_atom.parse("#t").unwrap();
|
|
assert_eq!(output.0, Atom::Bool(true));
|
|
|
|
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_expr)
|
|
.separated_by(repeated(Whitespace).at_least(1).to(()))
|
|
.allow_trailing(true),
|
|
)
|
|
.then_ignore(literal_char(')'))
|
|
.map(Expr::List)
|
|
.parse(input)
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_list() {
|
|
let output = parse_list.parse("(1 2 (1 2) 9999 3)").unwrap();
|
|
assert_eq!(output.1, "");
|
|
}
|
|
|
|
fn parse_sexp(input: &str) -> ParseResult<&str, Expr, ()> {
|
|
parse_list.surrounded_by(repeated(Whitespace)).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, "");
|
|
|
|
let complex_input = r#"
|
|
(add (mul 28 9)
|
|
(if (eq a b) (jump #t) (hula 44))
|
|
)"#
|
|
.trim();
|
|
|
|
let output = parse_sexp(complex_input).unwrap();
|
|
assert_eq!(output.1, "");
|
|
}
|