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::{
103        parse_expression, ArithmeticOp::*, Expression, Expression::*, Literal::*, Member::*,
104    };
105
106    fn parse(input: &str) -> Expression {
107        parse_expression(input.to_string()).unwrap_or_else(|e| panic!("{}", e))
108    }
109
110    fn assert_parse_eq(input: &str, expected: Expression) {
111        assert_eq!(parse(input), expected);
112    }
113
114    #[test]
115    fn op_precedence() {
116        assert_parse_eq(
117            "1 + 2 * 3",
118            Arithmetic(
119                Add,
120                Literal(Int(1)).into(),
121                Arithmetic(Multiply, Literal(Int(2)).into(), Literal(Int(3)).into()).into(),
122            ),
123        );
124        assert_parse_eq(
125            "1 * 2 + 3",
126            Arithmetic(
127                Add,
128                Arithmetic(Multiply, Literal(Int(1)).into(), Literal(Int(2)).into()).into(),
129                Literal(Int(3)).into(),
130            ),
131        );
132        assert_parse_eq(
133            "1 * (2 + 3)",
134            Arithmetic(
135                Multiply,
136                Literal(Int(1)).into(),
137                Arithmetic(Add, Literal(Int(2)).into(), Literal(Int(3)).into()).into(),
138            ),
139        )
140    }
141
142    #[test]
143    fn simple_int() {
144        assert_parse_eq("1", Literal(Int(1)))
145    }
146
147    #[test]
148    fn simple_float() {
149        assert_parse_eq("1.0", Literal(Double("1.0".to_string().into())))
150    }
151
152    #[test]
153    fn lookup() {
154        assert_parse_eq(
155            "hello.world",
156            Member(
157                Ident("hello".to_string().into()).into(),
158                Attribute("world".to_string().into()).into(),
159            ),
160        )
161    }
162
163    #[test]
164    fn nested_attributes() {
165        assert_parse_eq(
166            "a.b[1]",
167            Member(
168                Member(
169                    Ident("a".to_string().into()).into(),
170                    Attribute("b".to_string().into()).into(),
171                )
172                .into(),
173                Index(Literal(Int(1)).into()).into(),
174            ),
175        )
176    }
177
178    #[test]
179    fn has_macro() {
180        assert_parse_eq(
181            "has(a.b)",
182            Has(Member(
183                Ident("a".to_string().into()).into(),
184                Attribute("b".to_string().into()).into(),
185            )
186            .into()),
187        );
188        assert_parse_eq(
189            "has(params.field)",
190            Has(Member(
191                Ident("params".to_string().into()).into(),
192                Attribute("field".to_string().into()).into(),
193            )
194            .into()),
195        );
196        // Test deeply nested has expression
197        assert_parse_eq(
198            "has(a.b.c.d)",
199            Has(Member(
200                Member(
201                    Member(
202                        Ident("a".to_string().into()).into(),
203                        Attribute("b".to_string().into()).into(),
204                    )
205                    .into(),
206                    Attribute("c".to_string().into()).into(),
207                )
208                .into(),
209                Attribute("d".to_string().into()).into(),
210            )
211            .into()),
212        );
213    }
214}