Skip to content

Instantly share code, notes, and snippets.

@dermesser
Last active May 5, 2016 09:50
Show Gist options
  • Save dermesser/61ff0b43d9498ae91e4e7484a067d7be to your computer and use it in GitHub Desktop.
Save dermesser/61ff0b43d9498ae91e4e7484a067d7be to your computer and use it in GitHub Desktop.
A simple arithmetic parser in Rust, based on the excellent `combine` crate. Use by giving an expression as argument: $ target/debug/test-combine "a+4*b/c"
// use with
// [dependencies]
// combine = "1.3.0"
extern crate combine;
use combine::*;
#[derive(Debug)]
enum Expr {
Scalar(f64),
Var(char),
Prod(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Sum(Box<Expr>, Box<Expr>),
Diff(Box<Expr>, Box<Expr>),
}
fn parse_expr(inp: State<&str>) -> ParseResult<Expr, &str> {
let tok = choice([char('-'), char('+')]).map(|op| {
move |a, b| {
if op == '+' {
Expr::Sum(Box::new(a), Box::new(b))
} else if op == '-' {
Expr::Diff(Box::new(a), Box::new(b))
} else {
unimplemented!()
}
}
});
let mut sum = chainl1(parser(parse_term), tok);
sum.parse_state(inp)
}
fn parse_term(inp: State<&str>) -> ParseResult<Expr, &str> {
let tok = choice([char('*'), char('/')]).map(|op| {
move |a, b| {
if op == '*' {
Expr::Prod(Box::new(a), Box::new(b))
} else if op == '/' {
Expr::Div(Box::new(a), Box::new(b))
} else {
unimplemented!()
}
}
});
let mut prod = chainl1(parser(parse_factor), tok);
prod.parse_state(inp)
}
fn parse_factor(inp: State<&str>) -> ParseResult<Expr, &str> {
let scalar = many1(digit()).map(|t: String| Expr::Scalar(t.parse().unwrap()));
let var = letter().map(Expr::Var);
let parens = between(char('('), char(')'), parser(parse_expr));
spaces().with(scalar.or(var).or(parens)).skip(spaces()).parse_state(inp)
}
fn main() {
use std::env;
let mut a = env::args();
let s = a.nth(1).unwrap();
let result = parser(parse_expr).parse(s.as_str());
println!("{:?}", result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment