Skip to main content

openpql_pql_parser/ast/
expr.rs

1use super::{
2    BinOp, FnCall, Ident, Loc, LocInfo, Num, Spanned, Str, UnaryOp, str,
3};
4
5/// Parsed expression tree.
6#[derive(Clone, PartialEq, derive_more::From, derive_more::Debug)]
7pub enum Expr<'i> {
8    /// Identifier reference.
9    #[debug("{_0:?}")]
10    Ident(Ident<'i>),
11    /// String literal.
12    #[debug("{_0:?}")]
13    Str(Str<'i>),
14    /// Function call.
15    #[debug("{_0:?}")]
16    FnCall(FnCall<'i>),
17    /// Numeric literal.
18    #[debug("{_0:?}")]
19    Num(Num),
20    /// Binary operation with its two operands.
21    #[debug("{_1:?} {} {_2:?}", _to_op(*_0))]
22    BinOp(BinOp, Box<Self>, Box<Self>),
23    /// Unary operation with its start offset and operand.
24    #[debug("{} {_2:?}", _to_unary_op(*_0))]
25    UnaryOp(UnaryOp, Loc, Box<Self>),
26}
27
28#[inline]
29const fn _to_op(op: BinOp) -> &'static str {
30    match op {
31        BinOp::Add => "+",
32        BinOp::Sub => "-",
33        BinOp::Mul => "*",
34        BinOp::Div => "/",
35        BinOp::Eq => "=",
36        BinOp::Ge => "≥",
37        BinOp::Gt => ">",
38        BinOp::Le => "≤",
39        BinOp::Lt => "<",
40        BinOp::And => "and",
41        BinOp::Or => "or",
42    }
43}
44
45#[inline]
46const fn _to_unary_op(op: UnaryOp) -> &'static str {
47    match op {
48        UnaryOp::Not => "not",
49    }
50}
51
52impl Spanned for Expr<'_> {
53    fn loc(&self) -> LocInfo {
54        match self {
55            Expr::Ident(id) => id.loc,
56            Expr::Str(s) => s.loc,
57            Expr::FnCall(fncall) => fncall.loc,
58            Expr::Num(int) => int.loc,
59            Expr::BinOp(_, l, r) => (l.loc().0, r.loc().1),
60            Expr::UnaryOp(_, start, e) => (*start, e.loc().1),
61        }
62    }
63}
64
65impl Expr<'_> {
66    pub(crate) fn binop(op: BinOp, l: Self, r: Self) -> Self {
67        Self::BinOp(op, Box::new(l), Box::new(r))
68    }
69
70    pub(crate) fn unary_op(op: UnaryOp, start: Loc, e: Self) -> Self {
71        Self::UnaryOp(op, start, Box::new(e))
72    }
73}
74
75#[cfg(test)]
76#[cfg_attr(coverage_nightly, coverage(off))]
77mod tests {
78    use super::*;
79    use crate::*;
80
81    fn assert_expr(src: &str, expected: &str) {
82        assert_eq!(format!("{:?}", parse_expr(src).unwrap()), expected);
83    }
84
85    #[test]
86    fn test_binop() {
87        assert_expr("1 + 1", "1 + 1");
88        assert_expr("1 - 1", "1 - 1");
89        assert_expr("1 * 1", "1 * 1");
90        assert_expr("1 / 1", "1 / 1");
91
92        assert_expr("1 = 1", "1 = 1");
93        assert_expr("1 > 1", "1 > 1");
94        assert_expr("1 >= 1", "1 ≥ 1");
95        assert_expr("1 < 1", "1 < 1");
96        assert_expr("1 <= 1", "1 ≤ 1");
97
98        assert_expr("a and b", "a and b");
99        assert_expr("a or b", "a or b");
100        assert_expr("a AND b", "a and b");
101        assert_expr("a OR b", "a or b");
102        assert_expr("a or b and c", "a or b and c");
103        assert_expr("a = 1 and b = 2", "a = 1 and b = 2");
104    }
105
106    #[test]
107    fn test_unary_op() {
108        assert_expr("not a", "not a");
109        assert_expr("NOT a", "not a");
110        assert_expr("not not a", "not not a");
111        assert_expr("not a and b", "not a and b");
112        assert_expr("a and not b", "a and not b");
113        assert_expr("not a = 1", "not a = 1");
114    }
115
116    #[test]
117    fn test_expr() {
118        assert_expr("id", "id");
119        assert_expr("'str'", "\"str\"");
120        assert_expr("sin(x)", "sin(x)");
121    }
122
123    fn assert_loc(src: &str, start: Loc, end: Loc) {
124        assert_eq!(parse_expr(src).unwrap().loc(), (start, end));
125    }
126
127    #[test]
128    fn test_loc() {
129        assert_loc("a", 0, 1);
130        assert_loc("'a'", 0, 3);
131        assert_loc("sin(x)", 0, 6);
132        assert_loc("10", 0, 2);
133        assert_loc("1 >= 3", 0, 6);
134        assert_loc("not a", 0, 5);
135    }
136
137    #[test]
138    fn test_debug() {
139        fn assert_dbg(s: &str, expected: &str) {
140            assert_eq!(format!("{:?}", parse_expr(s).unwrap()), expected);
141        }
142
143        assert_dbg("ident", "ident");
144
145        assert_dbg("'string'", "\"string\"");
146
147        assert_dbg("fncall(v1, v2)", "fncall(v1,v2)");
148
149        assert_dbg("100", "100");
150        assert_dbg("3.14", "3.14");
151
152        assert_dbg("1 = 1", "1 = 1");
153        assert_dbg("1 >= 1", "1 ≥ 1");
154    }
155}