summaryrefslogtreecommitdiff
path: root/examples/calc.rs
blob: 707162536fd60d7acf00da36bf5081d137fa8709 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
mod parser {
    use pest_derive::Parser;

    #[derive(Parser)]
    #[grammar = "../examples/base.pest"]
    #[grammar = "../examples/calc.pest"]
    pub struct Parser;
}

use parser::Rule;
use pest::{
    iterators::Pairs,
    pratt_parser::{Assoc::*, Op, PrattParser},
    Parser,
};
use std::io::{stdin, stdout, Write};

fn parse_to_str(pairs: Pairs<Rule>, pratt: &PrattParser<Rule>) -> String {
    pratt
        .map_primary(|primary| match primary.as_rule() {
            Rule::int => primary.as_str().to_owned(),
            Rule::expr => parse_to_str(primary.into_inner(), pratt),
            _ => unreachable!(),
        })
        .map_prefix(|op, rhs| match op.as_rule() {
            Rule::neg => format!("(-{})", rhs),
            _ => unreachable!(),
        })
        .map_postfix(|lhs, op| match op.as_rule() {
            Rule::fac => format!("({}!)", lhs),
            _ => unreachable!(),
        })
        .map_infix(|lhs, op, rhs| match op.as_rule() {
            Rule::add => format!("({}+{})", lhs, rhs),
            Rule::sub => format!("({}-{})", lhs, rhs),
            Rule::mul => format!("({}*{})", lhs, rhs),
            Rule::div => format!("({}/{})", lhs, rhs),
            Rule::pow => format!("({}^{})", lhs, rhs),
            _ => unreachable!(),
        })
        .parse(pairs)
}

fn parse_to_i32(pairs: Pairs<Rule>, pratt: &PrattParser<Rule>) -> i128 {
    pratt
        .map_primary(|primary| match primary.as_rule() {
            Rule::int => primary.as_str().parse().unwrap(),
            Rule::expr => parse_to_i32(primary.into_inner(), pratt),
            _ => unreachable!(),
        })
        .map_prefix(|op, rhs| match op.as_rule() {
            Rule::neg => -rhs,
            _ => unreachable!(),
        })
        .map_postfix(|lhs, op| match op.as_rule() {
            Rule::fac => (1..lhs + 1).product(),
            _ => unreachable!(),
        })
        .map_infix(|lhs, op, rhs| match op.as_rule() {
            Rule::add => lhs + rhs,
            Rule::sub => lhs - rhs,
            Rule::mul => lhs * rhs,
            Rule::div => lhs / rhs,
            Rule::pow => (1..rhs + 1).map(|_| lhs).product(),
            _ => unreachable!(),
        })
        .parse(pairs)
}

fn main() {
    let pratt = PrattParser::new()
        .op(Op::infix(Rule::add, Left) | Op::infix(Rule::sub, Left))
        .op(Op::infix(Rule::mul, Left) | Op::infix(Rule::div, Left))
        .op(Op::infix(Rule::pow, Right))
        .op(Op::postfix(Rule::fac))
        .op(Op::prefix(Rule::neg));

    let stdin = stdin();
    let mut stdout = stdout();

    loop {
        let source = {
            print!("> ");
            let _ = stdout.flush();
            let mut input = String::new();
            let _ = stdin.read_line(&mut input);
            input.trim().to_string()
        };

        let pairs = match parser::Parser::parse(Rule::program, &source) {
            Ok(mut parse_tree) => {
                parse_tree
                    .next()
                    .unwrap()
                    .into_inner() // inner of program
                    .next()
                    .unwrap()
                    .into_inner() // inner of expr
            }
            Err(err) => {
                println!("Failed parsing input: {:}", err);
                continue;
            }
        };

        print!("{} => ", source);
        print!("{} => ", parse_to_str(pairs.clone(), &pratt));
        println!("{}", parse_to_i32(pairs.clone(), &pratt));
    }
}