1use flexi_parse::group::Group;
4use flexi_parse::group::Parentheses;
5use flexi_parse::parse;
6use flexi_parse::parse_string;
7use flexi_parse::pretty_unwrap;
8use flexi_parse::token;
9use flexi_parse::Parse;
10use flexi_parse::ParseStream;
11use flexi_parse::Punct;
12use flexi_parse::Result;
13
14use std::env;
15
16enum Expr {
17 Num(f64),
18 Neg(Punct!["-"], Box<Expr>),
19 Mul(Box<Expr>, Punct!["*"], Box<Expr>),
20 Div(Box<Expr>, Punct!["/"], Box<Expr>),
21 Mod(Box<Expr>, Punct!["%"], Box<Expr>),
22 Add(Box<Expr>, Punct!["+"], Box<Expr>),
23 Sub(Box<Expr>, Punct!["-"], Box<Expr>),
24}
25
26impl Expr {
27 fn eval(&self) -> f64 {
28 match self {
29 Expr::Num(num) => *num,
30 Expr::Neg(_, expr) => -expr.eval(),
31 Expr::Mul(left, _, right) => left.eval() * right.eval(),
32 Expr::Div(left, _, right) => left.eval() / right.eval(),
33 Expr::Mod(left, _, right) => left.eval() % right.eval(),
34 Expr::Add(left, _, right) => left.eval() + right.eval(),
35 Expr::Sub(left, _, right) => left.eval() - right.eval(),
36 }
37 }
38}
39
40impl Parse for Expr {
41 fn parse(input: ParseStream) -> Result<Self> {
42 let mut expr = factor(input)?;
43 loop {
44 if input.peek(Punct!["+"]) {
45 expr = Expr::Add(Box::new(expr), input.parse()?, Box::new(factor(input)?));
46 } else if input.peek(Punct!["-"]) {
47 expr = Expr::Sub(Box::new(expr), input.parse()?, Box::new(factor(input)?));
48 } else {
49 break;
50 }
51 }
52 Ok(expr)
53 }
54}
55
56fn factor(input: ParseStream) -> Result<Expr> {
57 let mut expr: Expr = unary(input)?;
58 loop {
59 if input.peek(Punct!["*"]) {
60 expr = Expr::Mul(Box::new(expr), input.parse()?, Box::new(unary(input)?));
61 } else if input.peek(Punct!["/"]) {
62 expr = Expr::Div(Box::new(expr), input.parse()?, Box::new(unary(input)?));
63 } else if input.peek(Punct!["%"]) {
64 expr = Expr::Mod(Box::new(expr), input.parse()?, Box::new(unary(input)?));
65 } else {
66 break;
67 }
68 }
69 Ok(expr)
70}
71
72fn unary(input: ParseStream) -> Result<Expr> {
73 if input.peek(Punct!["-"]) {
74 Ok(Expr::Neg(input.parse()?, Box::new(unary(input)?)))
75 } else {
76 primary(input)
77 }
78}
79
80#[allow(clippy::cast_precision_loss)]
81fn primary(input: ParseStream) -> Result<Expr> {
82 let lookahead = input.lookahead();
83 if lookahead.peek(token::LitFloat) {
84 Ok(Expr::Num(input.parse::<token::LitFloat>()?.value()))
85 } else if lookahead.peek(token::LitInt) {
86 Ok(Expr::Num(input.parse::<token::LitInt>()?.value() as f64))
87 } else if lookahead.peek(token::LeftParen) {
88 let group: Group<Parentheses> = input.parse()?;
89 parse(group.into_token_stream())
90 } else {
91 Err(lookahead.error())
92 }
93}
94
95fn main() {
96 let expr: Expr = pretty_unwrap(parse_string(env::args().nth(1).expect("expect expression")));
97 println!("{}", expr.eval());
98}
99
100#[cfg(test)]
101mod tests {
102 use super::Expr;
103
104 use flexi_parse::parse_string;
105 use flexi_parse::pretty_unwrap;
106
107 #[test]
108 #[allow(clippy::float_cmp)]
109 fn test_features() {
110 let expr: Expr = pretty_unwrap(parse_string(
111 "
112 (
113 (
114 (4 - 1) + 5
115 ) / (2.5 + -0.5) * 2
116 ) % 3
117 "
118 .to_string(),
119 ));
120 assert_eq!(expr.eval(), 2.0);
121 }
122}