thin_shunting/
rpnprint.rs

1use crate::parser::{precedence, Assoc, RPNExpr};
2use crate::tokenizer::MathToken;
3use std::fmt;
4
5#[derive(Debug, Clone)]
6enum AST<'a> {
7    Leaf(&'a MathToken),
8    Node(&'a MathToken, Vec<AST<'a>>),
9}
10
11impl RPNExpr {
12    fn build_ast(&self) -> AST {
13        let mut ops = Vec::new();
14        for token in self.0.iter() {
15            match *token {
16                MathToken::Number(_) | MathToken::Variable(_) => ops.push(AST::Leaf(token)),
17                MathToken::Function(_, arity) => {
18                    let n = ops.len() - arity;
19                    let operands = ops.split_off(n);
20                    ops.push(AST::Node(token, operands));
21                }
22                MathToken::BOp(_) => {
23                    let n = ops.len() - 2;
24                    let operands = ops.split_off(n);
25                    ops.push(AST::Node(token, operands));
26                }
27                MathToken::UOp(_) => {
28                    let n = ops.len() - 1;
29                    let operands = ops.split_off(n);
30                    ops.push(AST::Node(token, operands));
31                }
32                _ => unreachable!(),
33            }
34        }
35        ops.pop().unwrap()
36    }
37}
38
39impl fmt::Display for RPNExpr {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        fn printer(root: &AST) -> (String, (usize, Assoc)) {
42            match root {
43                AST::Leaf(ref token) => match *token {
44                    MathToken::Number(ref x) => (x.to_string(), precedence(token)),
45                    MathToken::Variable(ref x) => (x.to_string(), precedence(token)),
46                    _ => unreachable!(),
47                },
48                AST::Node(ref token, ref args) => {
49                    match *token {
50                        MathToken::UOp(ref op) => {
51                            let subtree = printer(&args[0]);
52                            let (prec, assoc) = precedence(token);
53                            // TODO: distinguish perfix/postfix operators
54                            if prec > (subtree.1).0 {
55                                (format!("{}({})", op, subtree.0), (prec, assoc))
56                            } else {
57                                (format!("{}{}", op, subtree.0), (prec, assoc))
58                            }
59                        }
60                        MathToken::BOp(ref op) => {
61                            let (lhs, rhs) = (printer(&args[0]), printer(&args[1]));
62                            let (prec, assoc) = precedence(token);
63
64                            let lh = if prec > (lhs.1).0
65                                || (prec == (lhs.1).0 && assoc != Assoc::Left)
66                            {
67                                format!("({})", lhs.0)
68                            } else {
69                                lhs.0
70                            };
71                            let rh = if prec > (rhs.1).0
72                                || (prec == (rhs.1).0 && assoc != Assoc::Right)
73                            {
74                                format!("({})", rhs.0)
75                            } else {
76                                rhs.0
77                            };
78                            // NOTE: '2+(3+4)' will show parens to indicate that user
79                            // explicitly put them there
80                            (format!("{} {} {}", lh, op, rh), (prec, assoc))
81                        }
82                        MathToken::Function(ref func, _) => {
83                            let expr = args
84                                .iter()
85                                .map(|leaf| printer(&leaf).0)
86                                .collect::<Vec<String>>()
87                                .join(", ");
88                            (format!("{}({})", func, expr), precedence(token))
89                        }
90                        _ => unreachable!(),
91                    }
92                }
93            }
94        }
95
96        write!(f, "{}", printer(&self.build_ast()).0)
97    }
98}