panmath/
operators.rs

1//! Defines operators for each symbol and their precedence.
2
3use crate::ast::Symbol;
4use crate::symbols;
5
6/// An operator with a given left and right precedence. Precedence is defined as an `Option<u8>`
7/// where 0 is the entire expression's precedence and lower values means higher-priority. `None`
8/// indicates that the operator doesn't support that mode of operation.
9#[derive(Debug, Clone, Hash, PartialEq, Eq)]
10pub struct Op {
11    /// The symbol used to define the operator.
12    pub sym: Symbol,
13
14    /// The left precedence.
15    pub l_prec: Option<u8>,
16
17    /// The right precedence.
18    pub r_prec: Option<u8>,
19}
20
21impl Op {
22    /// Makes a new `Op`, cloning the symbol used.
23    pub fn new(sym: &Symbol, l_prec: Option<u8>, r_prec: Option<u8>) -> Op {
24        Op {
25            sym: sym.clone(),
26            l_prec,
27            r_prec,
28        }
29    }
30
31    /// Given a string, returns a matched prefix of that string if the prefix matches one of the
32    /// operator's representations and None otherwise.
33    pub fn match_front(&self, input: &str) -> Option<&str> {
34        self.sym.match_front(input)
35    }
36}
37
38lazy_static! {
39    // Unary operators: these take precedence over binary operators and can't bind things to the
40    // left of them.
41    pub static ref UNARY_PLUS: Op = Op::new(&symbols::PLUS, None, Some(1));
42    pub static ref UNARY_MINUS: Op = Op::new(&symbols::MINUS, None, Some(1));
43    pub static ref UNARY_PM: Op = Op::new(&symbols::PM, None, Some(1));
44
45    // Binary operators. We give the right sides higher precedence when the operator is associative
46    // so they associate rightwards: `a + b + c` is parsed as `a + (b + c)`.
47
48    // unlike the others, this one needs right precedence: 2 ^ 3 ^ 4 = 2 ^ (3 ^ 4) and not the other
49    // way round!
50    pub static ref POWER: Op = Op::new(&symbols::POWER, Some(4), Some(3));
51    pub static ref MULT: Op = Op::new(&symbols::MULT, Some(6), Some(5));
52    pub static ref DIV: Op = Op::new(&symbols::DIV, Some(6), Some(5));
53    pub static ref ADD: Op = Op::new(&symbols::PLUS, Some(7), Some(8));
54    pub static ref SUB: Op = Op::new(&symbols::MINUS, Some(7), Some(8));
55    pub static ref PM: Op = Op::new(&symbols::PM, Some(7), Some(8));
56
57    // Comma is an operator as a hacky way of allowing expressions like max(1 + 2, 3 + 4). It should
58    // be the weakest operator, as the example shows: no matter what operator is used in place +,
59    // the postfix version should be 1 2 + 3 4 + , max
60    pub static ref COMMA: Op = Op::new(&symbols::COMMA, Some(10), Some(11));
61
62    /// The list of unary operators.
63    pub static ref UNARY_OPS: Vec<Op> = {
64        vec![
65            UNARY_PLUS.clone(),
66            UNARY_MINUS.clone(),
67            UNARY_PM.clone(),
68        ]
69    };
70
71    /// The list of binary operators.
72    pub static ref BINARY_OPS: Vec<Op> = {
73        vec![
74            POWER.clone(),
75            MULT.clone(),
76            DIV.clone(),
77            ADD.clone(),
78            SUB.clone(),
79            PM.clone(),
80            COMMA.clone()
81        ]
82    };
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_pm() {
91        assert_eq!(PM.match_front("pm 2"), Some("pm"));
92    }
93}