Skip to content

Instantly share code, notes, and snippets.

@emersion
Created August 4, 2017 09:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emersion/26c1a90866e95728ce1e358a921e8eca to your computer and use it in GitHub Desktop.
Save emersion/26c1a90866e95728ce1e358a921e8eca to your computer and use it in GitHub Desktop.
#[macro_use]
extern crate nom;
use nom::{IResult,digit};
// Parser definition
use std::str;
use std::str::FromStr;
#[derive(Debug)]
enum Expr {
Value(i64),
Op {
op: char,
left: Box<Expr>,
right: Box<Expr>,
},
}
// We parse any expr surrounded by parens, ignoring all whitespaces around those
named!(parens<Expr>, ws!(delimited!( tag!("("), expr, tag!(")") )) );
// We transform an integer string into a i64, ignoring surrounding whitespaces
// We look for a digit suite, and try to convert it.
// If either str::from_utf8 or FromStr::from_str fail,
// we fallback to the parens parser defined above
named!(factor<Expr>, alt!(
map!(
map_res!(
map_res!(
ws!(digit),
str::from_utf8
),
FromStr::from_str
),
|i| Expr::Value(i)
)
| parens
)
);
// We read an initial factor and for each time we find
// a * or / operator followed by another factor, we do
// the math by folding everything
named!(term <Expr>, do_parse!(
init: factor >>
res: fold_many0!(
pair!(alt!(tag!("*") | tag!("/")), factor),
init,
|acc, (op, val): (&[u8], Expr)| {
Expr::Op{
op: op[0] as char,
left: Box::new(acc),
right: Box::new(val),
}
}
) >>
(res)
)
);
named!(expr <Expr>, do_parse!(
init: term >>
res: fold_many0!(
pair!(alt!(tag!("+") | tag!("-")), term),
init,
|acc, (op, val): (&[u8], Expr)| {
Expr::Op{
op: op[0] as char,
left: Box::new(acc),
right: Box::new(val),
}
}
) >>
(res)
)
);
fn main() {
let e = expr(b"1+2*3+4").unwrap();
println!("{:?}", e);
// assert_eq!(expr(b"1+2"), IResult::Done(&b""[..], 3));
// assert_eq!(expr(b"12+6-4+3"), IResult::Done(&b""[..], 17));
// assert_eq!(expr(b"1+2*3+4"), IResult::Done(&b""[..], 11));
//
// assert_eq!(expr(b"(2)"), IResult::Done(&b""[..], 2));
// assert_eq!(expr(b"2*(3+4)"), IResult::Done(&b""[..], 14));
// assert_eq!(expr(b"2*2/(5-1)+3"), IResult::Done(&b""[..], 4));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment