cedar_policy_core/
expr_builder.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Contains the trait [`ExprBuilder`], defining a generic interface for
18//! building different expression data structures (e.g., AST and EST).
19
20use smol_str::SmolStr;
21
22use crate::{
23    ast::{
24        BinaryOp, EntityType, ExpressionConstructionError, Literal, Name, Pattern, SlotId, UnaryOp,
25        Unknown, Var,
26    },
27    parser::{cst, Loc},
28};
29
30/// Defines a generic interface for building different expression data
31/// structures.
32#[allow(clippy::wrong_self_convention)]
33pub trait ExprBuilder: Clone {
34    /// The type of expression constructed by this instance of `ExprBuilder`.
35    type Expr: Clone + std::fmt::Display;
36
37    /// Type for extra information stored on nodes of the expression AST. This
38    /// can be `()` if no data is stored.
39    type Data: Default;
40
41    /// Construct a new expression builder for an expression that will not carry any data.
42    fn new() -> Self
43    where
44        Self: Sized,
45    {
46        Self::with_data(Self::Data::default())
47    }
48
49    /// Build an expression storing this information
50    fn with_data(data: Self::Data) -> Self;
51
52    /// Build an expression located at `l`, if `l` is Some. An implementation
53    /// may ignore this if it cannot store source information.
54    fn with_maybe_source_loc(self, l: Option<&Loc>) -> Self;
55
56    /// Build an expression located at `l`. An implementation may ignore this if
57    /// it cannot store source information.
58    fn with_source_loc(self, l: &Loc) -> Self
59    where
60        Self: Sized,
61    {
62        self.with_maybe_source_loc(Some(l))
63    }
64
65    /// Extract the location for this builder, if set. Used internally to
66    /// provide utilities that construct multiple nodes which should all be
67    /// reported as having the same source location.
68    fn loc(&self) -> Option<&Loc>;
69
70    /// Extract the data that will be stored on the constructed expression.
71    /// Used internally to provide utilities that construct multiple nodes which
72    /// will all have the same data.
73    fn data(&self) -> &Self::Data;
74
75    /// Create an expression that's just a single `Literal`.
76    ///
77    /// Note that you can pass this a `Literal`, an `Integer`, a `String`, etc.
78    fn val(self, v: impl Into<Literal>) -> Self::Expr;
79
80    /// Create an `Expr` that's just this literal `Var`
81    fn var(self, v: Var) -> Self::Expr;
82
83    /// Create an `Unknown` `Expr`
84    fn unknown(self, u: Unknown) -> Self::Expr;
85
86    /// Create an `Expr` that's just this `SlotId`
87    fn slot(self, s: SlotId) -> Self::Expr;
88
89    /// Create a ternary (if-then-else) `Expr`.
90    fn ite(self, test_expr: Self::Expr, then_expr: Self::Expr, else_expr: Self::Expr)
91        -> Self::Expr;
92
93    /// Create a 'not' expression.
94    fn not(self, e: Self::Expr) -> Self::Expr;
95
96    /// Create a '==' expression
97    fn is_eq(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
98
99    /// Create an 'and' expression.
100    fn and(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
101
102    /// Create an 'or' expression.
103    fn or(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
104
105    /// Create a '<' expression.
106    fn less(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
107
108    /// Create a '<=' expression.
109    fn lesseq(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
110
111    /// Create an 'add' expression.
112    fn add(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
113
114    /// Create a 'sub' expression.
115    fn sub(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
116
117    /// Create a 'mul' expression.
118    fn mul(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
119
120    /// Create a 'neg' expression.
121    fn neg(self, e: Self::Expr) -> Self::Expr;
122
123    /// Create an 'in' expression. First argument must evaluate to Entity type.
124    fn is_in(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
125
126    /// Create a 'contains' expression.
127    fn contains(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
128
129    /// Create a 'contains_all' expression. Arguments must evaluate to Set type
130    fn contains_all(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
131
132    /// Create an 'contains_any' expression. Arguments must evaluate to Set type
133    fn contains_any(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr;
134
135    /// Create an 'is_empty' expression. Argument must evaluate to Set type
136    fn is_empty(self, expr: Self::Expr) -> Self::Expr;
137
138    /// Create a 'getTag' expression.
139    fn get_tag(self, expr: Self::Expr, tag: Self::Expr) -> Self::Expr;
140
141    /// Create a 'hasTag' expression.
142    fn has_tag(self, expr: Self::Expr, tag: Self::Expr) -> Self::Expr;
143
144    /// Create an `Expr` which evaluates to a Set of the given `Expr`s
145    fn set(self, exprs: impl IntoIterator<Item = Self::Expr>) -> Self::Expr;
146
147    /// Create an `Expr` which evaluates to a Record with the given (key, value) pairs.
148    fn record(
149        self,
150        pairs: impl IntoIterator<Item = (SmolStr, Self::Expr)>,
151    ) -> Result<Self::Expr, ExpressionConstructionError>;
152
153    /// Create an `Expr` which calls the extension function with the given
154    /// `Name` on `args`
155    fn call_extension_fn(
156        self,
157        fn_name: Name,
158        args: impl IntoIterator<Item = Self::Expr>,
159    ) -> Self::Expr;
160
161    /// Create an `Expr` which gets a given attribute of a given `Entity` or record.
162    fn get_attr(self, expr: Self::Expr, attr: SmolStr) -> Self::Expr;
163
164    /// Create an `Expr` which tests for the existence of a given
165    /// attribute on a given `Entity` or record.
166    fn has_attr(self, expr: Self::Expr, attr: SmolStr) -> Self::Expr;
167
168    /// Create a 'like' expression.
169    fn like(self, expr: Self::Expr, pattern: Pattern) -> Self::Expr;
170
171    /// Create an 'is' expression.
172    fn is_entity_type(self, expr: Self::Expr, entity_type: EntityType) -> Self::Expr;
173
174    /// Create an `_ is _ in _`  expression
175    fn is_in_entity_type(
176        self,
177        e1: Self::Expr,
178        entity_type: EntityType,
179        e2: Self::Expr,
180    ) -> Self::Expr
181    where
182        Self: Sized,
183    {
184        self.clone().and(
185            self.clone().is_entity_type(e1.clone(), entity_type),
186            self.is_in(e1, e2),
187        )
188    }
189
190    /// Create an application `Expr` which applies the given built-in unary
191    /// operator to the given `arg`
192    fn unary_app(self, op: impl Into<UnaryOp>, arg: Self::Expr) -> Self::Expr
193    where
194        Self: Sized,
195    {
196        match op.into() {
197            UnaryOp::Not => self.not(arg),
198            UnaryOp::Neg => self.neg(arg),
199            UnaryOp::IsEmpty => self.is_empty(arg),
200        }
201    }
202
203    /// Create an application `Expr` which applies the given built-in binary
204    /// operator to `arg1` and `arg2`
205    fn binary_app(self, op: impl Into<BinaryOp>, arg1: Self::Expr, arg2: Self::Expr) -> Self::Expr
206    where
207        Self: Sized,
208    {
209        match op.into() {
210            BinaryOp::Eq => self.is_eq(arg1, arg2),
211            BinaryOp::Less => self.less(arg1, arg2),
212            BinaryOp::LessEq => self.lesseq(arg1, arg2),
213            BinaryOp::Add => self.add(arg1, arg2),
214            BinaryOp::Sub => self.sub(arg1, arg2),
215            BinaryOp::Mul => self.mul(arg1, arg2),
216            BinaryOp::In => self.is_in(arg1, arg2),
217            BinaryOp::Contains => self.contains(arg1, arg2),
218            BinaryOp::ContainsAll => self.contains_all(arg1, arg2),
219            BinaryOp::ContainsAny => self.contains_any(arg1, arg2),
220            BinaryOp::GetTag => self.get_tag(arg1, arg2),
221            BinaryOp::HasTag => self.has_tag(arg1, arg2),
222        }
223    }
224
225    /// Create a '!=' expression.
226    fn noteq(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr
227    where
228        Self: Sized,
229    {
230        self.clone().not(self.is_eq(e1, e2))
231    }
232
233    /// Create a '>' expression.
234    fn greater(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr
235    where
236        Self: Sized,
237    {
238        // e1 > e2 is defined as !(e1 <= e2)
239        self.clone().not(self.lesseq(e1, e2))
240    }
241
242    /// Create a '>=' expression.
243    fn greatereq(self, e1: Self::Expr, e2: Self::Expr) -> Self::Expr
244    where
245        Self: Sized,
246    {
247        // e1 >= e2 is defined as !(e1 < e2)
248        self.clone().not(self.less(e1, e2))
249    }
250
251    /// Create an `and` expression that may have more than two subexpressions (A && B && C)
252    /// or may have only one subexpression, in which case no `&&` is performed at all.
253    /// Arguments must evaluate to Bool type.
254    ///
255    /// This may create multiple AST `&&` nodes. If it does, all the nodes will have the same
256    /// source location and the same `T` data (taken from this builder) unless overridden, e.g.,
257    /// with another call to `with_source_loc()`.
258    fn and_nary(self, first: Self::Expr, others: impl IntoIterator<Item = Self::Expr>) -> Self::Expr
259    where
260        Self: Sized,
261    {
262        others
263            .into_iter()
264            .fold(first, |acc, next| self.clone().and(acc, next))
265    }
266
267    /// Create an `or` expression that may have more than two subexpressions (A || B || C)
268    /// or may have only one subexpression, in which case no `||` is performed at all.
269    /// Arguments must evaluate to Bool type.
270    ///
271    /// This may create multiple AST `||` nodes. If it does, all the nodes will have the same
272    /// source location and the same `T` data (taken from this builder) unless overridden, e.g.,
273    /// with another call to `with_source_loc()`.
274    fn or_nary(self, first: Self::Expr, others: impl IntoIterator<Item = Self::Expr>) -> Self::Expr
275    where
276        Self: Sized,
277    {
278        others
279            .into_iter()
280            .fold(first, |acc, next| self.clone().or(acc, next))
281    }
282
283    /// Create expression containing addition and subtraction that may have more
284    /// than two subexpressions (A + B - C) or may have only one subexpression,
285    /// in which case no operations are performed at all.
286    fn add_nary(
287        self,
288        first: Self::Expr,
289        other: impl IntoIterator<Item = (cst::AddOp, Self::Expr)>,
290    ) -> Self::Expr
291    where
292        Self: Sized,
293    {
294        other.into_iter().fold(first, |acc, (op, next)| match op {
295            cst::AddOp::Plus => self.clone().add(acc, next),
296            cst::AddOp::Minus => self.clone().sub(acc, next),
297        })
298    }
299
300    /// Create expression containing multiplication that may have more than two
301    /// subexpressions (A * B * C) or may have only one subexpression,
302    /// in which case no operations are performed at all.
303    fn mul_nary(self, first: Self::Expr, other: impl IntoIterator<Item = Self::Expr>) -> Self::Expr
304    where
305        Self: Sized,
306    {
307        other
308            .into_iter()
309            .fold(first, |acc, next| self.clone().mul(acc, next))
310    }
311}