cala_cel_parser/
ast.rs

1use std::sync::Arc;
2
3#[derive(Debug, Eq, PartialEq, Clone)]
4pub enum LogicOp {
5    And,
6    Or,
7}
8
9#[derive(Debug, Eq, PartialEq, Clone, Copy)]
10pub enum RelationOp {
11    LessThan,
12    LessThanEq,
13    GreaterThan,
14    GreaterThanEq,
15    Equals,
16    NotEquals,
17    In,
18}
19
20#[derive(Debug, Eq, PartialEq, Clone, Copy)]
21pub enum ArithmeticOp {
22    Add,
23    Subtract,
24    Divide,
25    Multiply,
26    Modulus,
27}
28
29#[derive(Debug, Eq, PartialEq, Clone)]
30pub enum UnaryOp {
31    Not,
32    DoubleNot,
33    Minus,
34    DoubleMinus,
35}
36
37#[derive(Debug, Eq, PartialEq, Clone)]
38pub enum LeftRightOp {
39    Logic(LogicOp),
40    Relation(RelationOp),
41    Arithmetic(ArithmeticOp),
42}
43
44#[derive(Debug, PartialEq, Clone)]
45pub enum Expression {
46    Ternary(Box<Expression>, Box<Expression>, Box<Expression>),
47    Relation(RelationOp, Box<Expression>, Box<Expression>),
48    Arithmetic(ArithmeticOp, Box<Expression>, Box<Expression>),
49    Unary(UnaryOp, Box<Expression>),
50
51    Member(Box<Expression>, Box<Member>),
52    Has(Box<Expression>),
53
54    List(Vec<Expression>),
55    Map(Vec<(Expression, Expression)>),
56    Struct(Vec<Arc<String>>, Vec<(Arc<String>, Expression)>),
57
58    Literal(Literal),
59    Ident(Arc<String>),
60}
61
62impl Expression {
63    pub(crate) fn from_op(op: LeftRightOp, left: Box<Expression>, right: Box<Expression>) -> Self {
64        use LeftRightOp::*;
65        match op {
66            Logic(LogicOp::Or) => Expression::Ternary(
67                left,
68                Box::new(Expression::Literal(Literal::Bool(true))),
69                right,
70            ),
71            Logic(LogicOp::And) => Expression::Ternary(
72                left,
73                right,
74                Box::new(Expression::Literal(Literal::Bool(false))),
75            ),
76            Relation(op) => Expression::Relation(op, left, right),
77            Arithmetic(op) => Expression::Arithmetic(op, left, right),
78        }
79    }
80}
81
82#[derive(Debug, PartialEq, Clone)]
83pub enum Member {
84    Attribute(Arc<String>),
85    FunctionCall(Vec<Expression>),
86    Index(Box<Expression>),
87}
88
89#[derive(Debug, PartialEq, Eq, Clone)]
90pub enum Literal {
91    Int(i64),
92    UInt(u64),
93    Double(Arc<String>),
94    String(Arc<String>),
95    Bytes(Arc<Vec<u8>>),
96    Bool(bool),
97    Null,
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::parser::ExpressionParser;
103    use crate::{ArithmeticOp::*, Expression, Expression::*, Literal::*, Member::*};
104
105    fn parse(input: &str) -> Expression {
106        ExpressionParser::new()
107            .parse(input)
108            .unwrap_or_else(|e| panic!("{}", e))
109    }
110
111    fn assert_parse_eq(input: &str, expected: Expression) {
112        assert_eq!(parse(input), expected);
113    }
114
115    #[test]
116    fn op_precedence() {
117        assert_parse_eq(
118            "1 + 2 * 3",
119            Arithmetic(
120                Add,
121                Literal(Int(1)).into(),
122                Arithmetic(Multiply, Literal(Int(2)).into(), Literal(Int(3)).into()).into(),
123            ),
124        );
125        assert_parse_eq(
126            "1 * 2 + 3",
127            Arithmetic(
128                Add,
129                Arithmetic(Multiply, Literal(Int(1)).into(), Literal(Int(2)).into()).into(),
130                Literal(Int(3)).into(),
131            ),
132        );
133        assert_parse_eq(
134            "1 * (2 + 3)",
135            Arithmetic(
136                Multiply,
137                Literal(Int(1)).into(),
138                Arithmetic(Add, Literal(Int(2)).into(), Literal(Int(3)).into()).into(),
139            ),
140        )
141    }
142
143    #[test]
144    fn simple_int() {
145        assert_parse_eq("1", Literal(Int(1)))
146    }
147
148    #[test]
149    fn simple_float() {
150        assert_parse_eq("1.0", Literal(Double("1.0".to_string().into())))
151    }
152
153    #[test]
154    fn lookup() {
155        assert_parse_eq(
156            "hello.world",
157            Member(
158                Ident("hello".to_string().into()).into(),
159                Attribute("world".to_string().into()).into(),
160            ),
161        )
162    }
163
164    #[test]
165    fn nested_attributes() {
166        assert_parse_eq(
167            "a.b[1]",
168            Member(
169                Member(
170                    Ident("a".to_string().into()).into(),
171                    Attribute("b".to_string().into()).into(),
172                )
173                .into(),
174                Index(Literal(Int(1)).into()).into(),
175            ),
176        )
177    }
178
179    #[test]
180    fn has_macro() {
181        assert_parse_eq(
182            "has(a.b)",
183            Has(Member(
184                Ident("a".to_string().into()).into(),
185                Attribute("b".to_string().into()).into(),
186            )
187            .into()),
188        );
189        assert_parse_eq(
190            "has(params.field)",
191            Has(Member(
192                Ident("params".to_string().into()).into(),
193                Attribute("field".to_string().into()).into(),
194            )
195            .into()),
196        );
197        // Test deeply nested has expression
198        assert_parse_eq(
199            "has(a.b.c.d)",
200            Has(Member(
201                Member(
202                    Member(
203                        Ident("a".to_string().into()).into(),
204                        Attribute("b".to_string().into()).into(),
205                    )
206                    .into(),
207                    Attribute("c".to_string().into()).into(),
208                )
209                .into(),
210                Attribute("d".to_string().into()).into(),
211            )
212            .into()),
213        );
214    }
215}