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}