rink_core/ast/
expr.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use super::*;
6use serde_derive::Serialize;
7
8#[derive(Debug, Clone, Serialize, PartialEq)]
9#[serde(rename_all = "lowercase")]
10#[serde(tag = "type")]
11pub enum Expr {
12    Unit { name: String },
13    Quote { string: String },
14    Const { value: Numeric },
15    Date { tokens: Vec<DateToken> },
16    BinOp(BinOpExpr),
17    UnaryOp(UnaryOpExpr),
18    Mul { exprs: Vec<Expr> },
19    Of { property: String, expr: Box<Expr> },
20    Call { func: Function, args: Vec<Expr> },
21    Error { message: String },
22}
23
24impl Expr {
25    pub fn new_const(value: Numeric) -> Expr {
26        Expr::Const { value }
27    }
28
29    pub fn new_error(message: String) -> Expr {
30        Expr::Error { message }
31    }
32
33    pub fn new_unit(name: String) -> Expr {
34        Expr::Unit { name }
35    }
36
37    pub fn new_call(func: Function, args: Vec<Expr>) -> Expr {
38        Expr::Call { func, args }
39    }
40
41    pub fn new_mul(mut exprs: Vec<Expr>) -> Expr {
42        if exprs.len() == 1 {
43            exprs.pop().unwrap()
44        } else {
45            Expr::Mul { exprs }
46        }
47    }
48
49    pub fn new_bin(op: BinOpType, numer: Expr, denom: Expr) -> Expr {
50        let left = Box::new(numer);
51        let right = Box::new(denom);
52        Expr::BinOp(BinOpExpr { op, left, right })
53    }
54
55    pub fn new_add(numer: Expr, denom: Expr) -> Expr {
56        Expr::new_bin(BinOpType::Add, numer, denom)
57    }
58
59    pub fn new_sub(numer: Expr, denom: Expr) -> Expr {
60        Expr::new_bin(BinOpType::Sub, numer, denom)
61    }
62
63    pub fn new_frac(numer: Expr, denom: Expr) -> Expr {
64        Expr::new_bin(BinOpType::Frac, numer, denom)
65    }
66
67    pub fn new_pow(numer: Expr, denom: Expr) -> Expr {
68        Expr::new_bin(BinOpType::Pow, numer, denom)
69    }
70
71    pub fn new_equals(numer: Expr, denom: Expr) -> Expr {
72        Expr::new_bin(BinOpType::Equals, numer, denom)
73    }
74
75    pub fn new_of(property: &str, expr: Expr) -> Expr {
76        let property = property.to_owned();
77        let expr = Box::new(expr);
78        Expr::Of { property, expr }
79    }
80
81    pub fn new_unary(op: UnaryOpType, expr: Expr) -> Expr {
82        let expr = Box::new(expr);
83        Expr::UnaryOp(UnaryOpExpr { op, expr })
84    }
85
86    pub fn new_suffix(suffix: Degree, expr: Expr) -> Expr {
87        let op = UnaryOpType::Degree(suffix);
88        Expr::new_unary(op, expr)
89    }
90
91    pub fn new_plus(expr: Expr) -> Expr {
92        Expr::new_unary(UnaryOpType::Positive, expr)
93    }
94
95    pub fn new_negate(expr: Expr) -> Expr {
96        Expr::new_unary(UnaryOpType::Negative, expr)
97    }
98}
99
100#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
101pub enum Precedence {
102    Term,
103    Plus,
104    Pow,
105    Mul,
106    Div,
107    Add,
108    Equals,
109}
110
111impl Precedence {
112    pub fn from(binop_type: BinOpType) -> Precedence {
113        match binop_type {
114            BinOpType::Add => Precedence::Add,
115            BinOpType::Sub => Precedence::Add,
116            BinOpType::Pow => Precedence::Pow,
117            BinOpType::Frac => Precedence::Div,
118            BinOpType::ShiftL => Precedence::Div,
119            BinOpType::ShiftR => Precedence::Div,
120            BinOpType::Mod => Precedence::Div,
121            BinOpType::And => Precedence::Div,
122            BinOpType::Or => Precedence::Div,
123            BinOpType::Xor => Precedence::Div,
124            BinOpType::Equals => Precedence::Equals,
125        }
126    }
127
128    pub fn next(binop_type: BinOpType) -> Precedence {
129        match binop_type {
130            BinOpType::Add => Precedence::Div,
131            BinOpType::Sub => Precedence::Div,
132            BinOpType::Pow => Precedence::Term,
133            BinOpType::Frac => Precedence::Mul,
134            BinOpType::ShiftL => Precedence::Mul,
135            BinOpType::ShiftR => Precedence::Mul,
136            BinOpType::Mod => Precedence::Mul,
137            BinOpType::And => Precedence::Mul,
138            BinOpType::Or => Precedence::Mul,
139            BinOpType::Xor => Precedence::Mul,
140            BinOpType::Equals => Precedence::Add,
141        }
142    }
143}
144
145impl fmt::Display for Expr {
146    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
147        fn recurse(expr: &Expr, fmt: &mut fmt::Formatter<'_>, prec: Precedence) -> fmt::Result {
148            match *expr {
149                Expr::Unit { ref name } => write!(fmt, "{}", name),
150                Expr::Quote { ref string } => write!(fmt, "'{}'", string),
151                Expr::Const { ref value } => {
152                    let (_exact, val) = value.to_string(10, Digits::Default);
153                    write!(fmt, "{}", val)
154                }
155                Expr::Date { .. } => write!(fmt, "NYI: date expr Display"),
156                Expr::BinOp(ref binop) => {
157                    let op_prec = Precedence::from(binop.op);
158                    let succ = Precedence::next(binop.op);
159                    if prec < op_prec {
160                        write!(fmt, "(")?;
161                    }
162                    recurse(&binop.left, fmt, succ)?;
163                    write!(fmt, "{}", binop.op.symbol())?;
164                    recurse(&binop.right, fmt, op_prec)?;
165                    if prec < op_prec {
166                        write!(fmt, ")")?;
167                    }
168                    Ok(())
169                }
170                Expr::UnaryOp(ref unaryop) => match unaryop.op {
171                    UnaryOpType::Positive => {
172                        write!(fmt, "+")?;
173                        recurse(&unaryop.expr, fmt, Precedence::Plus)
174                    }
175                    UnaryOpType::Negative => {
176                        write!(fmt, "-")?;
177                        recurse(&unaryop.expr, fmt, Precedence::Plus)
178                    }
179                    UnaryOpType::Degree(ref suffix) => {
180                        if prec < Precedence::Mul {
181                            write!(fmt, "(")?;
182                        }
183                        recurse(&unaryop.expr, fmt, Precedence::Mul)?;
184                        write!(fmt, " {}", suffix)?;
185                        if prec < Precedence::Mul {
186                            write!(fmt, ")")?;
187                        }
188                        Ok(())
189                    }
190                },
191                Expr::Mul { ref exprs } => {
192                    if prec < Precedence::Mul {
193                        write!(fmt, "(")?;
194                    }
195                    if let Some(first) = exprs.first() {
196                        recurse(first, fmt, Precedence::Pow)?;
197                    }
198                    for expr in exprs.iter().skip(1) {
199                        write!(fmt, " ")?;
200                        recurse(expr, fmt, Precedence::Pow)?;
201                    }
202                    if prec < Precedence::Mul {
203                        write!(fmt, ")")?;
204                    }
205                    Ok(())
206                }
207                Expr::Call { ref func, ref args } => {
208                    write!(fmt, "{}(", func.name())?;
209                    if let Some(first) = args.first() {
210                        recurse(first, fmt, Precedence::Equals)?;
211                    }
212                    for arg in args.iter().skip(1) {
213                        write!(fmt, ", ")?;
214                        recurse(arg, fmt, Precedence::Equals)?;
215                    }
216                    write!(fmt, ")")
217                }
218                Expr::Of {
219                    ref property,
220                    ref expr,
221                } => {
222                    if prec < Precedence::Add {
223                        write!(fmt, "(")?;
224                    }
225                    write!(fmt, "{} of ", property)?;
226                    recurse(expr, fmt, Precedence::Div)?;
227                    if prec < Precedence::Add {
228                        write!(fmt, ")")?;
229                    }
230                    Ok(())
231                }
232                Expr::Error { ref message } => write!(fmt, "<error: {}>", message),
233            }
234        }
235
236        recurse(self, fmt, Precedence::Equals)
237    }
238}
239
240impl From<i64> for Expr {
241    fn from(x: i64) -> Self {
242        Expr::new_const(x.into())
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use crate::parsing::text_query::{parse_expr, TokenIterator};
249
250    fn parse_then_pretty(input: &str) -> String {
251        let mut iter = TokenIterator::new(&input).peekable();
252        let expr = parse_expr(&mut iter);
253        expr.to_string()
254    }
255
256    #[test]
257    fn expr_display() {
258        assert_eq!(parse_then_pretty("meter"), "meter");
259        assert_eq!(parse_then_pretty("'hello world'"), "'hello world'");
260        assert_eq!(parse_then_pretty("234234"), "234234");
261        assert_eq!(parse_then_pretty("1 + 2"), "1 + 2");
262        assert_eq!(parse_then_pretty("speed of light"), "speed of light");
263        assert_eq!(parse_then_pretty("1 + 2 * 3"), "1 + 2 3");
264        assert_eq!(parse_then_pretty("(1 + 2) * 3"), "(1 + 2) 3");
265        assert_eq!(parse_then_pretty("a = 2"), "a = 2");
266    }
267}