Skip to content

Instantly share code, notes, and snippets.

@sinkuu
Created September 22, 2016 08:55
Show Gist options
  • Select an option

  • Save sinkuu/9f3580fc350eab017aca78d51c42bfd1 to your computer and use it in GitHub Desktop.

Select an option

Save sinkuu/9f3580fc350eab017aca78d51c42bfd1 to your computer and use it in GitHub Desktop.
pub fn parse(s: &str) -> Result<Expr, ParseError<combine::State<&str>>> {
use combine::{Parser, eof, parser, many1, many, between, try};
use combine::char::{char, letter, spaces};
fn parse_inner(s: combine::State<&str>) -> ParseResult<Expr, combine::State<&str>> {
macro_rules! between_parens {
($p:expr) => { between((char('('), spaces()), (spaces(), char(')')), $p) }
}
fn abs_or_var(s: combine::State<&str>) -> ParseResult<Expr, combine::State<&str>> {
let variable = many1::<String, _>(letter()).map(Expr::Var);
let abstruction = (char('\\').or(char('λ')).skip(spaces())
.with(many1::<Vec<_>, _>(many1::<String, _>(letter()).skip(spaces()))),
char('.').skip(spaces()).with(parser(parse_inner)))
.map(|(v, e)| {
debug_assert!(!v.is_empty());
let mut iter = v.into_iter().rev();
let last = iter.next().unwrap();
iter.fold(Expr::Abs(last, box e), |abs, v| Expr::Abs(v, box abs))
});
abstruction.or(variable).parse_stream(s)
}
let expr = (parser(abs_or_var).or(between_parens!(parser(parse_inner))),
many::<Vec<_>, _>(try(spaces()
.with(parser(abs_or_var).or(between_parens!(parser(parse_inner)))))))
.map(|(func, apps)| apps.into_iter().fold(func, |acc, x| Expr::App(box acc, box x)));
spaces()
.with(expr)
.skip(spaces())
.parse_stream(s)
}
parser(parse_inner).skip(eof()).parse(combine::State::new(s))
.map(|(expr, _)| {
expr
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment