Skip to content

Commit fd5c283

Browse files
committed
Add arithmetic example
1 parent f612320 commit fd5c283

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

examples/arithmetic.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#[macro_use]
2+
extern crate combine;
3+
4+
use combine::{
5+
between, chainl1, choice,
6+
error::ParseError,
7+
many1,
8+
parser::char::{char, digit, spaces},
9+
Parser, Stream,
10+
};
11+
12+
#[derive(Debug)]
13+
pub enum Expr {
14+
Scalar(f64),
15+
Prod(Box<Expr>, Box<Expr>),
16+
Div(Box<Expr>, Box<Expr>),
17+
Sum(Box<Expr>, Box<Expr>),
18+
Diff(Box<Expr>, Box<Expr>),
19+
}
20+
21+
fn parse_expr<Input>() -> impl Parser<Input, Output = Expr>
22+
where
23+
Input: Stream<Token = char>,
24+
Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
25+
{
26+
let tok = choice([char('-'), char('+')]).map(|op| {
27+
move |a, b| {
28+
if op == '+' {
29+
Expr::Sum(Box::new(a), Box::new(b))
30+
} else if op == '-' {
31+
Expr::Diff(Box::new(a), Box::new(b))
32+
} else {
33+
unimplemented!()
34+
}
35+
}
36+
});
37+
let sum_or_sub = chainl1(parse_term(), tok);
38+
sum_or_sub
39+
}
40+
41+
fn parse_term<Input>() -> impl Parser<Input, Output = Expr>
42+
where
43+
Input: Stream<Token = char>,
44+
Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
45+
{
46+
let tok = choice([char('*'), char('/')]).map(|op| {
47+
move |a, b| {
48+
if op == '*' {
49+
Expr::Prod(Box::new(a), Box::new(b))
50+
} else if op == '/' {
51+
Expr::Div(Box::new(a), Box::new(b))
52+
} else {
53+
unimplemented!()
54+
}
55+
}
56+
});
57+
let prod_or_div = chainl1(parse_factor_(), tok);
58+
prod_or_div
59+
}
60+
61+
parser! {
62+
pub fn parse_factor_[Input]()(Input) -> Expr
63+
where [Input: Stream<Token=char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>]
64+
{
65+
let scalar = many1(digit()).map(|t: String| Expr::Scalar(t.parse().unwrap()));
66+
let parens = between(char('('), char(')'), parse_expr());
67+
let p = spaces().with(scalar.or(parens));
68+
p
69+
}
70+
}
71+
72+
fn print_calc_result(ast: Box<Expr>) -> f64 {
73+
match *ast {
74+
Expr::Scalar(val) => val,
75+
Expr::Sum(left, right) => print_calc_result(left) + print_calc_result(right),
76+
Expr::Diff(left, right) => print_calc_result(left) - print_calc_result(right),
77+
Expr::Prod(left, right) => print_calc_result(left) * print_calc_result(right),
78+
Expr::Div(left, right) => print_calc_result(left) / print_calc_result(right),
79+
}
80+
}
81+
82+
#[test]
83+
fn test() {
84+
// without parens
85+
let parsed = parse_expr().parse("3*1+2");
86+
let ast = parsed.expect("fail to parse").0;
87+
let calculated = print_calc_result(ast.into());
88+
assert_eq!(calculated, 5.0);
89+
90+
// with paren
91+
let parsed = parse_expr().parse("3*(1+2)");
92+
let ast = parsed.expect("fail to parse").0;
93+
let calculated = print_calc_result(ast.into());
94+
assert_eq!(calculated, 9.0);
95+
96+
// div and diff
97+
let parsed = parse_expr().parse("6/(2-4)");
98+
let ast = parsed.expect("fail to parse").0;
99+
let calculated = print_calc_result(ast.into());
100+
assert_eq!(calculated, -3.0);
101+
}
102+
103+
fn main() {
104+
let res = parse_expr().parse("3*(1+2)");
105+
let ast = res.expect("fail to parse").0;
106+
println!("{:?}", print_calc_result(ast.into()));
107+
}

0 commit comments

Comments
 (0)