Skip to main content

openpql_pql_parser/ast/
expr.rs

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