1use crate::{
18 ast::*,
19 extensions::Extensions,
20 parser::{err::ParseErrors, Loc},
21};
22use miette::Diagnostic;
23use serde::{Deserialize, Serialize};
24use smol_str::SmolStr;
25use std::{
26 borrow::Cow,
27 collections::{btree_map, BTreeMap, HashMap},
28 hash::{Hash, Hasher},
29 mem,
30 sync::Arc,
31};
32use thiserror::Error;
33
34#[cfg(feature = "wasm")]
35extern crate tsify;
36
37#[derive(Serialize, Deserialize, Hash, Debug, Clone, PartialEq, Eq)]
44pub struct Expr<T = ()> {
45 expr_kind: ExprKind<T>,
46 source_loc: Option<Loc>,
47 data: T,
48}
49
50#[derive(Serialize, Deserialize, Hash, Debug, Clone, PartialEq, Eq)]
53pub enum ExprKind<T = ()> {
54 Lit(Literal),
56 Var(Var),
58 Slot(SlotId),
60 Unknown(Unknown),
62 If {
64 test_expr: Arc<Expr<T>>,
66 then_expr: Arc<Expr<T>>,
68 else_expr: Arc<Expr<T>>,
70 },
71 And {
73 left: Arc<Expr<T>>,
75 right: Arc<Expr<T>>,
77 },
78 Or {
80 left: Arc<Expr<T>>,
82 right: Arc<Expr<T>>,
84 },
85 UnaryApp {
87 op: UnaryOp,
89 arg: Arc<Expr<T>>,
91 },
92 BinaryApp {
94 op: BinaryOp,
96 arg1: Arc<Expr<T>>,
98 arg2: Arc<Expr<T>>,
100 },
101 ExtensionFunctionApp {
107 fn_name: Name,
109 args: Arc<Vec<Expr<T>>>,
111 },
112 GetAttr {
114 expr: Arc<Expr<T>>,
117 attr: SmolStr,
119 },
120 HasAttr {
122 expr: Arc<Expr<T>>,
124 attr: SmolStr,
126 },
127 Like {
129 expr: Arc<Expr<T>>,
131 pattern: Pattern,
135 },
136 Is {
139 expr: Arc<Expr<T>>,
141 entity_type: EntityType,
143 },
144 Set(Arc<Vec<Expr<T>>>),
151 Record(Arc<BTreeMap<SmolStr, Expr<T>>>),
153}
154
155impl From<Value> for Expr {
156 fn from(v: Value) -> Self {
157 Expr::from(v.value).with_maybe_source_loc(v.loc)
158 }
159}
160
161impl From<ValueKind> for Expr {
162 fn from(v: ValueKind) -> Self {
163 match v {
164 ValueKind::Lit(lit) => Expr::val(lit),
165 ValueKind::Set(set) => Expr::set(set.iter().map(|v| Expr::from(v.clone()))),
166 #[allow(clippy::expect_used)]
168 ValueKind::Record(record) => Expr::record(
169 Arc::unwrap_or_clone(record)
170 .into_iter()
171 .map(|(k, v)| (k, Expr::from(v))),
172 )
173 .expect("cannot have duplicate key because the input was already a BTreeMap"),
174 ValueKind::ExtensionValue(ev) => Expr::from(ev.as_ref().clone()),
175 }
176 }
177}
178
179impl From<PartialValue> for Expr {
180 fn from(pv: PartialValue) -> Self {
181 match pv {
182 PartialValue::Value(v) => Expr::from(v),
183 PartialValue::Residual(expr) => expr,
184 }
185 }
186}
187
188impl<T> ExprKind<T> {
189 pub fn operator_description(self: &ExprKind<T>) -> String {
191 match self {
192 ExprKind::Lit(_) => "literal".to_string(),
193 ExprKind::Var(_) => "variable".to_string(),
194 ExprKind::Slot(_) => "slot".to_string(),
195 ExprKind::Unknown(_) => "unknown".to_string(),
196 ExprKind::If { .. } => "if".to_string(),
197 ExprKind::And { .. } => "&&".to_string(),
198 ExprKind::Or { .. } => "||".to_string(),
199 ExprKind::UnaryApp { op, .. } => op.to_string(),
200 ExprKind::BinaryApp { op, .. } => op.to_string(),
201 ExprKind::ExtensionFunctionApp { fn_name, .. } => fn_name.to_string(),
202 ExprKind::GetAttr { .. } => "get attribute".to_string(),
203 ExprKind::HasAttr { .. } => "has attribute".to_string(),
204 ExprKind::Like { .. } => "like".to_string(),
205 ExprKind::Is { .. } => "is".to_string(),
206 ExprKind::Set(_) => "set".to_string(),
207 ExprKind::Record(_) => "record".to_string(),
208 }
209 }
210}
211
212impl<T> Expr<T> {
213 fn new(expr_kind: ExprKind<T>, source_loc: Option<Loc>, data: T) -> Self {
214 Self {
215 expr_kind,
216 source_loc,
217 data,
218 }
219 }
220
221 pub fn expr_kind(&self) -> &ExprKind<T> {
225 &self.expr_kind
226 }
227
228 pub fn into_expr_kind(self) -> ExprKind<T> {
230 self.expr_kind
231 }
232
233 pub fn data(&self) -> &T {
235 &self.data
236 }
237
238 pub fn into_data(self) -> T {
241 self.data
242 }
243
244 pub fn source_loc(&self) -> Option<&Loc> {
246 self.source_loc.as_ref()
247 }
248
249 pub fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
251 Self { source_loc, ..self }
252 }
253
254 pub fn set_data(&mut self, data: T) {
257 self.data = data;
258 }
259
260 pub fn is_ref(&self) -> bool {
265 match &self.expr_kind {
266 ExprKind::Lit(lit) => lit.is_ref(),
267 _ => false,
268 }
269 }
270
271 pub fn is_slot(&self) -> bool {
273 matches!(&self.expr_kind, ExprKind::Slot(_))
274 }
275
276 pub fn is_ref_set(&self) -> bool {
281 match &self.expr_kind {
282 ExprKind::Set(exprs) => exprs.iter().all(|e| e.is_ref()),
283 _ => false,
284 }
285 }
286
287 pub fn subexpressions(&self) -> impl Iterator<Item = &Self> {
289 expr_iterator::ExprIterator::new(self)
290 }
291
292 pub fn slots(&self) -> impl Iterator<Item = Slot> + '_ {
294 self.subexpressions()
295 .filter_map(|exp| match &exp.expr_kind {
296 ExprKind::Slot(slotid) => Some(Slot {
297 id: *slotid,
298 loc: exp.source_loc().cloned(),
299 }),
300 _ => None,
301 })
302 }
303
304 pub fn is_projectable(&self) -> bool {
308 self.subexpressions().all(|e| {
309 matches!(
310 e.expr_kind(),
311 ExprKind::Lit(_)
312 | ExprKind::Unknown(_)
313 | ExprKind::Set(_)
314 | ExprKind::Var(_)
315 | ExprKind::Record(_)
316 )
317 })
318 }
319
320 pub fn try_type_of(&self, extensions: &Extensions<'_>) -> Option<Type> {
332 match &self.expr_kind {
333 ExprKind::Lit(l) => Some(l.type_of()),
334 ExprKind::Var(_) => None,
335 ExprKind::Slot(_) => None,
336 ExprKind::Unknown(u) => u.type_annotation.clone(),
337 ExprKind::If {
338 then_expr,
339 else_expr,
340 ..
341 } => {
342 let type_of_then = then_expr.try_type_of(extensions);
343 let type_of_else = else_expr.try_type_of(extensions);
344 if type_of_then == type_of_else {
345 type_of_then
346 } else {
347 None
348 }
349 }
350 ExprKind::And { .. } => Some(Type::Bool),
351 ExprKind::Or { .. } => Some(Type::Bool),
352 ExprKind::UnaryApp {
353 op: UnaryOp::Neg, ..
354 } => Some(Type::Long),
355 ExprKind::UnaryApp {
356 op: UnaryOp::Not, ..
357 } => Some(Type::Bool),
358 ExprKind::BinaryApp {
359 op: BinaryOp::Add | BinaryOp::Mul | BinaryOp::Sub,
360 ..
361 } => Some(Type::Long),
362 ExprKind::BinaryApp {
363 op:
364 BinaryOp::Contains
365 | BinaryOp::ContainsAll
366 | BinaryOp::ContainsAny
367 | BinaryOp::Eq
368 | BinaryOp::In
369 | BinaryOp::Less
370 | BinaryOp::LessEq,
371 ..
372 } => Some(Type::Bool),
373 ExprKind::BinaryApp {
374 op: BinaryOp::HasTag,
375 ..
376 } => Some(Type::Bool),
377 ExprKind::ExtensionFunctionApp { fn_name, .. } => extensions
378 .func(fn_name)
379 .ok()?
380 .return_type()
381 .map(|rty| rty.clone().into()),
382 ExprKind::GetAttr { .. } => None,
387 ExprKind::BinaryApp {
389 op: BinaryOp::GetTag,
390 ..
391 } => None,
392 ExprKind::HasAttr { .. } => Some(Type::Bool),
393 ExprKind::Like { .. } => Some(Type::Bool),
394 ExprKind::Is { .. } => Some(Type::Bool),
395 ExprKind::Set(_) => Some(Type::Set),
396 ExprKind::Record(_) => Some(Type::Record),
397 }
398 }
399}
400
401#[allow(dead_code)] #[allow(clippy::should_implement_trait)] impl Expr {
404 pub fn val(v: impl Into<Literal>) -> Self {
408 ExprBuilder::new().val(v)
409 }
410
411 pub fn unknown(u: Unknown) -> Self {
413 ExprBuilder::new().unknown(u)
414 }
415
416 pub fn var(v: Var) -> Self {
418 ExprBuilder::new().var(v)
419 }
420
421 pub fn slot(s: SlotId) -> Self {
423 ExprBuilder::new().slot(s)
424 }
425
426 pub fn ite(test_expr: Expr, then_expr: Expr, else_expr: Expr) -> Self {
430 ExprBuilder::new().ite(test_expr, then_expr, else_expr)
431 }
432
433 pub fn ite_arc(test_expr: Arc<Expr>, then_expr: Arc<Expr>, else_expr: Arc<Expr>) -> Self {
437 ExprBuilder::new().ite_arc(test_expr, then_expr, else_expr)
438 }
439
440 pub fn not(e: Expr) -> Self {
442 ExprBuilder::new().not(e)
443 }
444
445 pub fn is_eq(e1: Expr, e2: Expr) -> Self {
447 ExprBuilder::new().is_eq(e1, e2)
448 }
449
450 pub fn noteq(e1: Expr, e2: Expr) -> Self {
452 ExprBuilder::new().noteq(e1, e2)
453 }
454
455 pub fn and(e1: Expr, e2: Expr) -> Self {
457 ExprBuilder::new().and(e1, e2)
458 }
459
460 pub fn or(e1: Expr, e2: Expr) -> Self {
462 ExprBuilder::new().or(e1, e2)
463 }
464
465 pub fn less(e1: Expr, e2: Expr) -> Self {
467 ExprBuilder::new().less(e1, e2)
468 }
469
470 pub fn lesseq(e1: Expr, e2: Expr) -> Self {
472 ExprBuilder::new().lesseq(e1, e2)
473 }
474
475 pub fn greater(e1: Expr, e2: Expr) -> Self {
477 ExprBuilder::new().greater(e1, e2)
478 }
479
480 pub fn greatereq(e1: Expr, e2: Expr) -> Self {
482 ExprBuilder::new().greatereq(e1, e2)
483 }
484
485 pub fn add(e1: Expr, e2: Expr) -> Self {
487 ExprBuilder::new().add(e1, e2)
488 }
489
490 pub fn sub(e1: Expr, e2: Expr) -> Self {
492 ExprBuilder::new().sub(e1, e2)
493 }
494
495 pub fn mul(e1: Expr, e2: Expr) -> Self {
497 ExprBuilder::new().mul(e1, e2)
498 }
499
500 pub fn neg(e: Expr) -> Self {
502 ExprBuilder::new().neg(e)
503 }
504
505 pub fn is_in(e1: Expr, e2: Expr) -> Self {
509 ExprBuilder::new().is_in(e1, e2)
510 }
511
512 pub fn contains(e1: Expr, e2: Expr) -> Self {
515 ExprBuilder::new().contains(e1, e2)
516 }
517
518 pub fn contains_all(e1: Expr, e2: Expr) -> Self {
520 ExprBuilder::new().contains_all(e1, e2)
521 }
522
523 pub fn contains_any(e1: Expr, e2: Expr) -> Self {
525 ExprBuilder::new().contains_any(e1, e2)
526 }
527
528 pub fn get_tag(expr: Expr, tag: Expr) -> Self {
531 ExprBuilder::new().get_tag(expr, tag)
532 }
533
534 pub fn has_tag(expr: Expr, tag: Expr) -> Self {
537 ExprBuilder::new().has_tag(expr, tag)
538 }
539
540 pub fn set(exprs: impl IntoIterator<Item = Expr>) -> Self {
542 ExprBuilder::new().set(exprs)
543 }
544
545 pub fn record(
547 pairs: impl IntoIterator<Item = (SmolStr, Expr)>,
548 ) -> Result<Self, ExpressionConstructionError> {
549 ExprBuilder::new().record(pairs)
550 }
551
552 pub fn record_arc(map: Arc<BTreeMap<SmolStr, Expr>>) -> Self {
560 ExprBuilder::new().record_arc(map)
561 }
562
563 pub fn call_extension_fn(fn_name: Name, args: Vec<Expr>) -> Self {
566 ExprBuilder::new().call_extension_fn(fn_name, args)
567 }
568
569 pub fn unary_app(op: impl Into<UnaryOp>, arg: Expr) -> Self {
572 ExprBuilder::new().unary_app(op, arg)
573 }
574
575 pub fn binary_app(op: impl Into<BinaryOp>, arg1: Expr, arg2: Expr) -> Self {
578 ExprBuilder::new().binary_app(op, arg1, arg2)
579 }
580
581 pub fn get_attr(expr: Expr, attr: SmolStr) -> Self {
585 ExprBuilder::new().get_attr(expr, attr)
586 }
587
588 pub fn has_attr(expr: Expr, attr: SmolStr) -> Self {
593 ExprBuilder::new().has_attr(expr, attr)
594 }
595
596 pub fn like(expr: Expr, pattern: impl IntoIterator<Item = PatternElem>) -> Self {
600 ExprBuilder::new().like(expr, pattern)
601 }
602
603 pub fn is_entity_type(expr: Expr, entity_type: EntityType) -> Self {
605 ExprBuilder::new().is_entity_type(expr, entity_type)
606 }
607
608 pub fn contains_unknown(&self) -> bool {
610 self.subexpressions()
611 .any(|e| matches!(e.expr_kind(), ExprKind::Unknown(_)))
612 }
613
614 pub fn unknowns(&self) -> impl Iterator<Item = &Unknown> {
616 self.subexpressions()
617 .filter_map(|subexpr| match subexpr.expr_kind() {
618 ExprKind::Unknown(u) => Some(u),
619 _ => None,
620 })
621 }
622
623 pub fn substitute(&self, definitions: &HashMap<SmolStr, Value>) -> Expr {
628 match self.substitute_general::<UntypedSubstitution>(definitions) {
629 Ok(e) => e,
630 Err(empty) => match empty {},
631 }
632 }
633
634 pub fn substitute_typed(
639 &self,
640 definitions: &HashMap<SmolStr, Value>,
641 ) -> Result<Expr, SubstitutionError> {
642 self.substitute_general::<TypedSubstitution>(definitions)
643 }
644
645 fn substitute_general<T: SubstitutionFunction>(
649 &self,
650 definitions: &HashMap<SmolStr, Value>,
651 ) -> Result<Expr, T::Err> {
652 match self.expr_kind() {
653 ExprKind::Lit(_) => Ok(self.clone()),
654 ExprKind::Unknown(u @ Unknown { name, .. }) => T::substitute(u, definitions.get(name)),
655 ExprKind::Var(_) => Ok(self.clone()),
656 ExprKind::Slot(_) => Ok(self.clone()),
657 ExprKind::If {
658 test_expr,
659 then_expr,
660 else_expr,
661 } => Ok(Expr::ite(
662 test_expr.substitute_general::<T>(definitions)?,
663 then_expr.substitute_general::<T>(definitions)?,
664 else_expr.substitute_general::<T>(definitions)?,
665 )),
666 ExprKind::And { left, right } => Ok(Expr::and(
667 left.substitute_general::<T>(definitions)?,
668 right.substitute_general::<T>(definitions)?,
669 )),
670 ExprKind::Or { left, right } => Ok(Expr::or(
671 left.substitute_general::<T>(definitions)?,
672 right.substitute_general::<T>(definitions)?,
673 )),
674 ExprKind::UnaryApp { op, arg } => Ok(Expr::unary_app(
675 *op,
676 arg.substitute_general::<T>(definitions)?,
677 )),
678 ExprKind::BinaryApp { op, arg1, arg2 } => Ok(Expr::binary_app(
679 *op,
680 arg1.substitute_general::<T>(definitions)?,
681 arg2.substitute_general::<T>(definitions)?,
682 )),
683 ExprKind::ExtensionFunctionApp { fn_name, args } => {
684 let args = args
685 .iter()
686 .map(|e| e.substitute_general::<T>(definitions))
687 .collect::<Result<Vec<Expr>, _>>()?;
688
689 Ok(Expr::call_extension_fn(fn_name.clone(), args))
690 }
691 ExprKind::GetAttr { expr, attr } => Ok(Expr::get_attr(
692 expr.substitute_general::<T>(definitions)?,
693 attr.clone(),
694 )),
695 ExprKind::HasAttr { expr, attr } => Ok(Expr::has_attr(
696 expr.substitute_general::<T>(definitions)?,
697 attr.clone(),
698 )),
699 ExprKind::Like { expr, pattern } => Ok(Expr::like(
700 expr.substitute_general::<T>(definitions)?,
701 pattern.iter().cloned(),
702 )),
703 ExprKind::Set(members) => {
704 let members = members
705 .iter()
706 .map(|e| e.substitute_general::<T>(definitions))
707 .collect::<Result<Vec<_>, _>>()?;
708 Ok(Expr::set(members))
709 }
710 ExprKind::Record(map) => {
711 let map = map
712 .iter()
713 .map(|(name, e)| Ok((name.clone(), e.substitute_general::<T>(definitions)?)))
714 .collect::<Result<BTreeMap<_, _>, _>>()?;
715 #[allow(clippy::expect_used)]
717 Ok(Expr::record(map)
718 .expect("cannot have a duplicate key because the input was already a BTreeMap"))
719 }
720 ExprKind::Is { expr, entity_type } => Ok(Expr::is_entity_type(
721 expr.substitute_general::<T>(definitions)?,
722 entity_type.clone(),
723 )),
724 }
725 }
726}
727
728trait SubstitutionFunction {
730 type Err;
732 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err>;
738}
739
740struct TypedSubstitution {}
741
742impl SubstitutionFunction for TypedSubstitution {
743 type Err = SubstitutionError;
744
745 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
746 match (substitute, &value.type_annotation) {
747 (None, _) => Ok(Expr::unknown(value.clone())),
748 (Some(v), None) => Ok(v.clone().into()),
749 (Some(v), Some(t)) => {
750 if v.type_of() == *t {
751 Ok(v.clone().into())
752 } else {
753 Err(SubstitutionError::TypeError {
754 expected: t.clone(),
755 actual: v.type_of(),
756 })
757 }
758 }
759 }
760 }
761}
762
763struct UntypedSubstitution {}
764
765impl SubstitutionFunction for UntypedSubstitution {
766 type Err = std::convert::Infallible;
767
768 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
769 Ok(substitute
770 .map(|v| v.clone().into())
771 .unwrap_or_else(|| Expr::unknown(value.clone())))
772 }
773}
774
775impl<T: Clone> std::fmt::Display for Expr<T> {
776 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
777 write!(f, "{}", crate::est::Expr::from(self.clone()))
781 }
782}
783
784impl std::str::FromStr for Expr {
785 type Err = ParseErrors;
786
787 fn from_str(s: &str) -> Result<Expr, Self::Err> {
788 crate::parser::parse_expr(s)
789 }
790}
791
792#[derive(Debug, Clone, Diagnostic, Error)]
794pub enum SubstitutionError {
795 #[error("expected a value of type {expected}, got a value of type {actual}")]
797 TypeError {
798 expected: Type,
800 actual: Type,
802 },
803}
804
805#[derive(Serialize, Deserialize, Hash, Debug, Clone, PartialEq, Eq)]
807pub struct Unknown {
808 pub name: SmolStr,
810 pub type_annotation: Option<Type>,
814}
815
816impl Unknown {
817 pub fn new_untyped(name: impl Into<SmolStr>) -> Self {
819 Self {
820 name: name.into(),
821 type_annotation: None,
822 }
823 }
824
825 pub fn new_with_type(name: impl Into<SmolStr>, ty: Type) -> Self {
828 Self {
829 name: name.into(),
830 type_annotation: Some(ty),
831 }
832 }
833}
834
835impl std::fmt::Display for Unknown {
836 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
837 write!(f, "{}", crate::est::Expr::from(Expr::unknown(self.clone())))
840 }
841}
842
843#[derive(Debug)]
846pub struct ExprBuilder<T> {
847 source_loc: Option<Loc>,
848 data: T,
849}
850
851impl<T> ExprBuilder<T>
852where
853 T: Default,
854{
855 pub fn new() -> Self {
858 Self {
859 source_loc: None,
860 data: T::default(),
861 }
862 }
863
864 pub fn noteq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
868 match &self.source_loc {
869 Some(source_loc) => ExprBuilder::new().with_source_loc(source_loc.clone()),
870 None => ExprBuilder::new(),
871 }
872 .not(self.with_expr_kind(ExprKind::BinaryApp {
873 op: BinaryOp::Eq,
874 arg1: Arc::new(e1),
875 arg2: Arc::new(e2),
876 }))
877 }
878}
879
880impl<T: Default> Default for ExprBuilder<T> {
881 fn default() -> Self {
882 Self::new()
883 }
884}
885
886impl<T> ExprBuilder<T> {
887 pub fn with_data(data: T) -> Self {
892 Self {
893 source_loc: None,
894 data,
895 }
896 }
897
898 pub fn with_source_loc(self, source_loc: Loc) -> Self {
901 self.with_maybe_source_loc(Some(source_loc))
902 }
903
904 pub fn with_same_source_loc<U>(self, expr: &Expr<U>) -> Self {
908 self.with_maybe_source_loc(expr.source_loc.clone())
909 }
910
911 fn with_maybe_source_loc(mut self, maybe_source_loc: Option<Loc>) -> Self {
913 self.source_loc = maybe_source_loc;
914 self
915 }
916
917 fn with_expr_kind(self, expr_kind: ExprKind<T>) -> Expr<T> {
921 Expr::new(expr_kind, self.source_loc, self.data)
922 }
923
924 pub fn val(self, v: impl Into<Literal>) -> Expr<T> {
928 self.with_expr_kind(ExprKind::Lit(v.into()))
929 }
930
931 pub fn unknown(self, u: Unknown) -> Expr<T> {
933 self.with_expr_kind(ExprKind::Unknown(u))
934 }
935
936 pub fn var(self, v: Var) -> Expr<T> {
938 self.with_expr_kind(ExprKind::Var(v))
939 }
940
941 pub fn slot(self, s: SlotId) -> Expr<T> {
943 self.with_expr_kind(ExprKind::Slot(s))
944 }
945
946 pub fn ite(self, test_expr: Expr<T>, then_expr: Expr<T>, else_expr: Expr<T>) -> Expr<T> {
950 self.with_expr_kind(ExprKind::If {
951 test_expr: Arc::new(test_expr),
952 then_expr: Arc::new(then_expr),
953 else_expr: Arc::new(else_expr),
954 })
955 }
956
957 pub fn ite_arc(
961 self,
962 test_expr: Arc<Expr<T>>,
963 then_expr: Arc<Expr<T>>,
964 else_expr: Arc<Expr<T>>,
965 ) -> Expr<T> {
966 self.with_expr_kind(ExprKind::If {
967 test_expr,
968 then_expr,
969 else_expr,
970 })
971 }
972
973 pub fn not(self, e: Expr<T>) -> Expr<T> {
975 self.with_expr_kind(ExprKind::UnaryApp {
976 op: UnaryOp::Not,
977 arg: Arc::new(e),
978 })
979 }
980
981 pub fn is_eq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
983 self.with_expr_kind(ExprKind::BinaryApp {
984 op: BinaryOp::Eq,
985 arg1: Arc::new(e1),
986 arg2: Arc::new(e2),
987 })
988 }
989
990 pub fn and(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
992 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
993 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
994 ExprKind::Lit(Literal::Bool(*b1 && *b2))
995 }
996 _ => ExprKind::And {
997 left: Arc::new(e1),
998 right: Arc::new(e2),
999 },
1000 })
1001 }
1002
1003 pub fn or(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1005 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1006 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1007 ExprKind::Lit(Literal::Bool(*b1 || *b2))
1008 }
1009
1010 _ => ExprKind::Or {
1011 left: Arc::new(e1),
1012 right: Arc::new(e2),
1013 },
1014 })
1015 }
1016
1017 pub fn less(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1019 self.with_expr_kind(ExprKind::BinaryApp {
1020 op: BinaryOp::Less,
1021 arg1: Arc::new(e1),
1022 arg2: Arc::new(e2),
1023 })
1024 }
1025
1026 pub fn lesseq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1028 self.with_expr_kind(ExprKind::BinaryApp {
1029 op: BinaryOp::LessEq,
1030 arg1: Arc::new(e1),
1031 arg2: Arc::new(e2),
1032 })
1033 }
1034
1035 pub fn add(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1037 self.with_expr_kind(ExprKind::BinaryApp {
1038 op: BinaryOp::Add,
1039 arg1: Arc::new(e1),
1040 arg2: Arc::new(e2),
1041 })
1042 }
1043
1044 pub fn sub(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1046 self.with_expr_kind(ExprKind::BinaryApp {
1047 op: BinaryOp::Sub,
1048 arg1: Arc::new(e1),
1049 arg2: Arc::new(e2),
1050 })
1051 }
1052
1053 pub fn mul(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1055 self.with_expr_kind(ExprKind::BinaryApp {
1056 op: BinaryOp::Mul,
1057 arg1: Arc::new(e1),
1058 arg2: Arc::new(e2),
1059 })
1060 }
1061
1062 pub fn neg(self, e: Expr<T>) -> Expr<T> {
1064 self.with_expr_kind(ExprKind::UnaryApp {
1065 op: UnaryOp::Neg,
1066 arg: Arc::new(e),
1067 })
1068 }
1069
1070 pub fn is_in(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1074 self.with_expr_kind(ExprKind::BinaryApp {
1075 op: BinaryOp::In,
1076 arg1: Arc::new(e1),
1077 arg2: Arc::new(e2),
1078 })
1079 }
1080
1081 pub fn contains(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1084 self.with_expr_kind(ExprKind::BinaryApp {
1085 op: BinaryOp::Contains,
1086 arg1: Arc::new(e1),
1087 arg2: Arc::new(e2),
1088 })
1089 }
1090
1091 pub fn contains_all(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1093 self.with_expr_kind(ExprKind::BinaryApp {
1094 op: BinaryOp::ContainsAll,
1095 arg1: Arc::new(e1),
1096 arg2: Arc::new(e2),
1097 })
1098 }
1099
1100 pub fn contains_any(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1102 self.with_expr_kind(ExprKind::BinaryApp {
1103 op: BinaryOp::ContainsAny,
1104 arg1: Arc::new(e1),
1105 arg2: Arc::new(e2),
1106 })
1107 }
1108
1109 pub fn get_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1112 self.with_expr_kind(ExprKind::BinaryApp {
1113 op: BinaryOp::GetTag,
1114 arg1: Arc::new(expr),
1115 arg2: Arc::new(tag),
1116 })
1117 }
1118
1119 pub fn has_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1122 self.with_expr_kind(ExprKind::BinaryApp {
1123 op: BinaryOp::HasTag,
1124 arg1: Arc::new(expr),
1125 arg2: Arc::new(tag),
1126 })
1127 }
1128
1129 pub fn set(self, exprs: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1131 self.with_expr_kind(ExprKind::Set(Arc::new(exprs.into_iter().collect())))
1132 }
1133
1134 pub fn record(
1136 self,
1137 pairs: impl IntoIterator<Item = (SmolStr, Expr<T>)>,
1138 ) -> Result<Expr<T>, ExpressionConstructionError> {
1139 let mut map = BTreeMap::new();
1140 for (k, v) in pairs {
1141 match map.entry(k) {
1142 btree_map::Entry::Occupied(oentry) => {
1143 return Err(expression_construction_errors::DuplicateKeyError {
1144 key: oentry.key().clone(),
1145 context: "in record literal",
1146 }
1147 .into());
1148 }
1149 btree_map::Entry::Vacant(ventry) => {
1150 ventry.insert(v);
1151 }
1152 }
1153 }
1154 Ok(self.with_expr_kind(ExprKind::Record(Arc::new(map))))
1155 }
1156
1157 pub fn record_arc(self, map: Arc<BTreeMap<SmolStr, Expr<T>>>) -> Expr<T> {
1164 self.with_expr_kind(ExprKind::Record(map))
1165 }
1166
1167 pub fn call_extension_fn(
1170 self,
1171 fn_name: Name,
1172 args: impl IntoIterator<Item = Expr<T>>,
1173 ) -> Expr<T> {
1174 self.with_expr_kind(ExprKind::ExtensionFunctionApp {
1175 fn_name,
1176 args: Arc::new(args.into_iter().collect()),
1177 })
1178 }
1179
1180 pub fn unary_app(self, op: impl Into<UnaryOp>, arg: Expr<T>) -> Expr<T> {
1183 self.with_expr_kind(ExprKind::UnaryApp {
1184 op: op.into(),
1185 arg: Arc::new(arg),
1186 })
1187 }
1188
1189 pub fn binary_app(self, op: impl Into<BinaryOp>, arg1: Expr<T>, arg2: Expr<T>) -> Expr<T> {
1192 self.with_expr_kind(ExprKind::BinaryApp {
1193 op: op.into(),
1194 arg1: Arc::new(arg1),
1195 arg2: Arc::new(arg2),
1196 })
1197 }
1198
1199 pub fn get_attr(self, expr: Expr<T>, attr: SmolStr) -> Expr<T> {
1203 self.with_expr_kind(ExprKind::GetAttr {
1204 expr: Arc::new(expr),
1205 attr,
1206 })
1207 }
1208
1209 pub fn has_attr(self, expr: Expr<T>, attr: SmolStr) -> Expr<T> {
1214 self.with_expr_kind(ExprKind::HasAttr {
1215 expr: Arc::new(expr),
1216 attr,
1217 })
1218 }
1219
1220 pub fn like(self, expr: Expr<T>, pattern: impl IntoIterator<Item = PatternElem>) -> Expr<T> {
1224 self.with_expr_kind(ExprKind::Like {
1225 expr: Arc::new(expr),
1226 pattern: Pattern::new(pattern),
1227 })
1228 }
1229
1230 pub fn is_entity_type(self, expr: Expr<T>, entity_type: EntityType) -> Expr<T> {
1232 self.with_expr_kind(ExprKind::Is {
1233 expr: Arc::new(expr),
1234 entity_type,
1235 })
1236 }
1237}
1238
1239impl<T: Clone> ExprBuilder<T> {
1240 pub fn and_nary(self, first: Expr<T>, others: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1248 others.into_iter().fold(first, |acc, next| {
1249 Self::with_data(self.data.clone())
1250 .with_maybe_source_loc(self.source_loc.clone())
1251 .and(acc, next)
1252 })
1253 }
1254
1255 pub fn or_nary(self, first: Expr<T>, others: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1263 others.into_iter().fold(first, |acc, next| {
1264 Self::with_data(self.data.clone())
1265 .with_maybe_source_loc(self.source_loc.clone())
1266 .or(acc, next)
1267 })
1268 }
1269
1270 pub fn greater(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1272 let leq = Self::with_data(self.data.clone())
1274 .with_maybe_source_loc(self.source_loc.clone())
1275 .lesseq(e1, e2);
1276 self.not(leq)
1277 }
1278
1279 pub fn greatereq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1281 let leq = Self::with_data(self.data.clone())
1283 .with_maybe_source_loc(self.source_loc.clone())
1284 .less(e1, e2);
1285 self.not(leq)
1286 }
1287}
1288
1289#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1295pub enum ExpressionConstructionError {
1296 #[error(transparent)]
1298 #[diagnostic(transparent)]
1299 DuplicateKey(#[from] expression_construction_errors::DuplicateKeyError),
1300}
1301
1302pub mod expression_construction_errors {
1304 use miette::Diagnostic;
1305 use smol_str::SmolStr;
1306 use thiserror::Error;
1307
1308 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1314 #[error("duplicate key `{key}` {context}")]
1315 pub struct DuplicateKeyError {
1316 pub(crate) key: SmolStr,
1318 pub(crate) context: &'static str,
1320 }
1321
1322 impl DuplicateKeyError {
1323 pub fn key(&self) -> &str {
1325 &self.key
1326 }
1327
1328 pub(crate) fn with_context(self, context: &'static str) -> Self {
1330 Self { context, ..self }
1331 }
1332 }
1333}
1334
1335#[derive(Eq, Debug, Clone)]
1339pub struct ExprShapeOnly<'a, T: Clone = ()>(Cow<'a, Expr<T>>);
1340
1341impl<'a, T: Clone> ExprShapeOnly<'a, T> {
1342 pub fn new_from_borrowed(e: &'a Expr<T>) -> ExprShapeOnly<'a, T> {
1346 ExprShapeOnly(Cow::Borrowed(e))
1347 }
1348
1349 pub fn new_from_owned(e: Expr<T>) -> ExprShapeOnly<'a, T> {
1353 ExprShapeOnly(Cow::Owned(e))
1354 }
1355}
1356
1357impl<'a, T: Clone> PartialEq for ExprShapeOnly<'a, T> {
1358 fn eq(&self, other: &Self) -> bool {
1359 self.0.eq_shape(&other.0)
1360 }
1361}
1362
1363impl<'a, T: Clone> Hash for ExprShapeOnly<'a, T> {
1364 fn hash<H: Hasher>(&self, state: &mut H) {
1365 self.0.hash_shape(state);
1366 }
1367}
1368
1369impl<T> Expr<T> {
1370 pub fn eq_shape<U>(&self, other: &Expr<U>) -> bool {
1377 use ExprKind::*;
1378 match (self.expr_kind(), other.expr_kind()) {
1379 (Lit(lit), Lit(lit1)) => lit == lit1,
1380 (Var(v), Var(v1)) => v == v1,
1381 (Slot(s), Slot(s1)) => s == s1,
1382 (
1383 Unknown(self::Unknown {
1384 name: name1,
1385 type_annotation: ta_1,
1386 }),
1387 Unknown(self::Unknown {
1388 name: name2,
1389 type_annotation: ta_2,
1390 }),
1391 ) => (name1 == name2) && (ta_1 == ta_2),
1392 (
1393 If {
1394 test_expr,
1395 then_expr,
1396 else_expr,
1397 },
1398 If {
1399 test_expr: test_expr1,
1400 then_expr: then_expr1,
1401 else_expr: else_expr1,
1402 },
1403 ) => {
1404 test_expr.eq_shape(test_expr1)
1405 && then_expr.eq_shape(then_expr1)
1406 && else_expr.eq_shape(else_expr1)
1407 }
1408 (
1409 And { left, right },
1410 And {
1411 left: left1,
1412 right: right1,
1413 },
1414 )
1415 | (
1416 Or { left, right },
1417 Or {
1418 left: left1,
1419 right: right1,
1420 },
1421 ) => left.eq_shape(left1) && right.eq_shape(right1),
1422 (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1423 op == op1 && arg.eq_shape(arg1)
1424 }
1425 (
1426 BinaryApp { op, arg1, arg2 },
1427 BinaryApp {
1428 op: op1,
1429 arg1: arg11,
1430 arg2: arg21,
1431 },
1432 ) => op == op1 && arg1.eq_shape(arg11) && arg2.eq_shape(arg21),
1433 (
1434 ExtensionFunctionApp { fn_name, args },
1435 ExtensionFunctionApp {
1436 fn_name: fn_name1,
1437 args: args1,
1438 },
1439 ) => fn_name == fn_name1 && args.iter().zip(args1.iter()).all(|(a, a1)| a.eq_shape(a1)),
1440 (
1441 GetAttr { expr, attr },
1442 GetAttr {
1443 expr: expr1,
1444 attr: attr1,
1445 },
1446 )
1447 | (
1448 HasAttr { expr, attr },
1449 HasAttr {
1450 expr: expr1,
1451 attr: attr1,
1452 },
1453 ) => attr == attr1 && expr.eq_shape(expr1),
1454 (
1455 Like { expr, pattern },
1456 Like {
1457 expr: expr1,
1458 pattern: pattern1,
1459 },
1460 ) => pattern == pattern1 && expr.eq_shape(expr1),
1461 (Set(elems), Set(elems1)) => elems
1462 .iter()
1463 .zip(elems1.iter())
1464 .all(|(e, e1)| e.eq_shape(e1)),
1465 (Record(map), Record(map1)) => {
1466 map.len() == map1.len()
1467 && map
1468 .iter()
1469 .zip(map1.iter()) .all(|((a, e), (a1, e1))| a == a1 && e.eq_shape(e1))
1471 }
1472 (
1473 Is { expr, entity_type },
1474 Is {
1475 expr: expr1,
1476 entity_type: entity_type1,
1477 },
1478 ) => entity_type == entity_type1 && expr.eq_shape(expr1),
1479 _ => false,
1480 }
1481 }
1482
1483 pub fn hash_shape<H>(&self, state: &mut H)
1487 where
1488 H: Hasher,
1489 {
1490 mem::discriminant(self).hash(state);
1491 match self.expr_kind() {
1492 ExprKind::Lit(lit) => lit.hash(state),
1493 ExprKind::Var(v) => v.hash(state),
1494 ExprKind::Slot(s) => s.hash(state),
1495 ExprKind::Unknown(u) => u.hash(state),
1496 ExprKind::If {
1497 test_expr,
1498 then_expr,
1499 else_expr,
1500 } => {
1501 test_expr.hash_shape(state);
1502 then_expr.hash_shape(state);
1503 else_expr.hash_shape(state);
1504 }
1505 ExprKind::And { left, right } => {
1506 left.hash_shape(state);
1507 right.hash_shape(state);
1508 }
1509 ExprKind::Or { left, right } => {
1510 left.hash_shape(state);
1511 right.hash_shape(state);
1512 }
1513 ExprKind::UnaryApp { op, arg } => {
1514 op.hash(state);
1515 arg.hash_shape(state);
1516 }
1517 ExprKind::BinaryApp { op, arg1, arg2 } => {
1518 op.hash(state);
1519 arg1.hash_shape(state);
1520 arg2.hash_shape(state);
1521 }
1522 ExprKind::ExtensionFunctionApp { fn_name, args } => {
1523 fn_name.hash(state);
1524 state.write_usize(args.len());
1525 args.iter().for_each(|a| {
1526 a.hash_shape(state);
1527 });
1528 }
1529 ExprKind::GetAttr { expr, attr } => {
1530 expr.hash_shape(state);
1531 attr.hash(state);
1532 }
1533 ExprKind::HasAttr { expr, attr } => {
1534 expr.hash_shape(state);
1535 attr.hash(state);
1536 }
1537 ExprKind::Like { expr, pattern } => {
1538 expr.hash_shape(state);
1539 pattern.hash(state);
1540 }
1541 ExprKind::Set(elems) => {
1542 state.write_usize(elems.len());
1543 elems.iter().for_each(|e| {
1544 e.hash_shape(state);
1545 })
1546 }
1547 ExprKind::Record(map) => {
1548 state.write_usize(map.len());
1549 map.iter().for_each(|(s, a)| {
1550 s.hash(state);
1551 a.hash_shape(state);
1552 });
1553 }
1554 ExprKind::Is { expr, entity_type } => {
1555 expr.hash_shape(state);
1556 entity_type.hash(state);
1557 }
1558 }
1559 }
1560}
1561
1562#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
1564#[serde(rename_all = "camelCase")]
1565#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1566#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
1567#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
1568pub enum Var {
1569 Principal,
1571 Action,
1573 Resource,
1575 Context,
1577}
1578
1579#[cfg(test)]
1580pub(crate) mod var_generator {
1581 use super::Var;
1582 #[cfg(test)]
1583 pub fn all_vars() -> impl Iterator<Item = Var> {
1584 [Var::Principal, Var::Action, Var::Resource, Var::Context].into_iter()
1585 }
1586}
1587impl From<PrincipalOrResource> for Var {
1594 fn from(v: PrincipalOrResource) -> Self {
1595 match v {
1596 PrincipalOrResource::Principal => Var::Principal,
1597 PrincipalOrResource::Resource => Var::Resource,
1598 }
1599 }
1600}
1601
1602#[allow(clippy::fallible_impl_from)]
1604impl From<Var> for Id {
1605 fn from(var: Var) -> Self {
1606 #[allow(clippy::unwrap_used)]
1608 format!("{var}").parse().unwrap()
1609 }
1610}
1611
1612#[allow(clippy::fallible_impl_from)]
1614impl From<Var> for UnreservedId {
1615 fn from(var: Var) -> Self {
1616 #[allow(clippy::unwrap_used)]
1618 Id::from(var).try_into().unwrap()
1619 }
1620}
1621
1622impl std::fmt::Display for Var {
1623 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1624 match self {
1625 Self::Principal => write!(f, "principal"),
1626 Self::Action => write!(f, "action"),
1627 Self::Resource => write!(f, "resource"),
1628 Self::Context => write!(f, "context"),
1629 }
1630 }
1631}
1632
1633#[cfg(test)]
1634mod test {
1635 use cool_asserts::assert_matches;
1636 use itertools::Itertools;
1637 use std::collections::{hash_map::DefaultHasher, HashSet};
1638
1639 use super::{var_generator::all_vars, *};
1640
1641 #[test]
1643 fn all_vars_are_ids() {
1644 for var in all_vars() {
1645 let _id: Id = var.into();
1646 let _id: UnreservedId = var.into();
1647 }
1648 }
1649
1650 #[test]
1651 fn exprs() {
1652 assert_eq!(
1653 Expr::val(33),
1654 Expr::new(ExprKind::Lit(Literal::Long(33)), None, ())
1655 );
1656 assert_eq!(
1657 Expr::val("hello"),
1658 Expr::new(ExprKind::Lit(Literal::from("hello")), None, ())
1659 );
1660 assert_eq!(
1661 Expr::val(EntityUID::with_eid("foo")),
1662 Expr::new(
1663 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1664 None,
1665 ()
1666 )
1667 );
1668 assert_eq!(
1669 Expr::var(Var::Principal),
1670 Expr::new(ExprKind::Var(Var::Principal), None, ())
1671 );
1672 assert_eq!(
1673 Expr::ite(Expr::val(true), Expr::val(88), Expr::val(-100)),
1674 Expr::new(
1675 ExprKind::If {
1676 test_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(true)), None, ())),
1677 then_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(88)), None, ())),
1678 else_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(-100)), None, ())),
1679 },
1680 None,
1681 ()
1682 )
1683 );
1684 assert_eq!(
1685 Expr::not(Expr::val(false)),
1686 Expr::new(
1687 ExprKind::UnaryApp {
1688 op: UnaryOp::Not,
1689 arg: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(false)), None, ())),
1690 },
1691 None,
1692 ()
1693 )
1694 );
1695 assert_eq!(
1696 Expr::get_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
1697 Expr::new(
1698 ExprKind::GetAttr {
1699 expr: Arc::new(Expr::new(
1700 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1701 None,
1702 ()
1703 )),
1704 attr: "some_attr".into()
1705 },
1706 None,
1707 ()
1708 )
1709 );
1710 assert_eq!(
1711 Expr::has_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
1712 Expr::new(
1713 ExprKind::HasAttr {
1714 expr: Arc::new(Expr::new(
1715 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1716 None,
1717 ()
1718 )),
1719 attr: "some_attr".into()
1720 },
1721 None,
1722 ()
1723 )
1724 );
1725 assert_eq!(
1726 Expr::is_entity_type(
1727 Expr::val(EntityUID::with_eid("foo")),
1728 "Type".parse().unwrap()
1729 ),
1730 Expr::new(
1731 ExprKind::Is {
1732 expr: Arc::new(Expr::new(
1733 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1734 None,
1735 ()
1736 )),
1737 entity_type: "Type".parse().unwrap()
1738 },
1739 None,
1740 ()
1741 ),
1742 );
1743 }
1744
1745 #[test]
1746 fn like_display() {
1747 let e = Expr::like(Expr::val("a"), vec![PatternElem::Char('\0')]);
1749 assert_eq!(format!("{e}"), r#""a" like "\0""#);
1750 let e = Expr::like(
1752 Expr::val("a"),
1753 vec![PatternElem::Char('\\'), PatternElem::Char('0')],
1754 );
1755 assert_eq!(format!("{e}"), r#""a" like "\\0""#);
1756 let e = Expr::like(
1758 Expr::val("a"),
1759 vec![PatternElem::Char('\\'), PatternElem::Wildcard],
1760 );
1761 assert_eq!(format!("{e}"), r#""a" like "\\*""#);
1762 let e = Expr::like(
1764 Expr::val("a"),
1765 vec![PatternElem::Char('\\'), PatternElem::Char('*')],
1766 );
1767 assert_eq!(format!("{e}"), r#""a" like "\\\*""#);
1768 }
1769
1770 #[test]
1771 fn has_display() {
1772 let e = Expr::has_attr(Expr::val("a"), "\0".into());
1774 assert_eq!(format!("{e}"), r#""a" has "\0""#);
1775 let e = Expr::has_attr(Expr::val("a"), r"\".into());
1777 assert_eq!(format!("{e}"), r#""a" has "\\""#);
1778 }
1779
1780 #[test]
1781 fn slot_display() {
1782 let e = Expr::slot(SlotId::principal());
1783 assert_eq!(format!("{e}"), "?principal");
1784 let e = Expr::slot(SlotId::resource());
1785 assert_eq!(format!("{e}"), "?resource");
1786 let e = Expr::val(EntityUID::with_eid("eid"));
1787 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
1788 }
1789
1790 #[test]
1791 fn simple_slots() {
1792 let e = Expr::slot(SlotId::principal());
1793 let p = SlotId::principal();
1794 let r = SlotId::resource();
1795 let set: HashSet<SlotId> = HashSet::from_iter([p]);
1796 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
1797 let e = Expr::or(
1798 Expr::slot(SlotId::principal()),
1799 Expr::ite(
1800 Expr::val(true),
1801 Expr::slot(SlotId::resource()),
1802 Expr::val(false),
1803 ),
1804 );
1805 let set: HashSet<SlotId> = HashSet::from_iter([p, r]);
1806 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
1807 }
1808
1809 #[test]
1810 fn unknowns() {
1811 let e = Expr::ite(
1812 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
1813 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
1814 Expr::unknown(Unknown::new_untyped("c")),
1815 );
1816 let unknowns = e.unknowns().collect_vec();
1817 assert_eq!(unknowns.len(), 3);
1818 assert!(unknowns.contains(&&Unknown::new_untyped("a")));
1819 assert!(unknowns.contains(&&Unknown::new_untyped("b")));
1820 assert!(unknowns.contains(&&Unknown::new_untyped("c")));
1821 }
1822
1823 #[test]
1824 fn is_unknown() {
1825 let e = Expr::ite(
1826 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
1827 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
1828 Expr::unknown(Unknown::new_untyped("c")),
1829 );
1830 assert!(e.contains_unknown());
1831 let e = Expr::ite(
1832 Expr::not(Expr::val(true)),
1833 Expr::and(Expr::val(1), Expr::val(3)),
1834 Expr::val(1),
1835 );
1836 assert!(!e.contains_unknown());
1837 }
1838
1839 #[test]
1840 fn expr_with_data() {
1841 let e = ExprBuilder::with_data("data").val(1);
1842 assert_eq!(e.into_data(), "data");
1843 }
1844
1845 #[test]
1846 fn expr_shape_only_eq() {
1847 let temp = ExprBuilder::with_data(1).val(1);
1848 let exprs = &[
1849 (ExprBuilder::with_data(1).val(33), Expr::val(33)),
1850 (ExprBuilder::with_data(1).val(true), Expr::val(true)),
1851 (
1852 ExprBuilder::with_data(1).var(Var::Principal),
1853 Expr::var(Var::Principal),
1854 ),
1855 (
1856 ExprBuilder::with_data(1).slot(SlotId::principal()),
1857 Expr::slot(SlotId::principal()),
1858 ),
1859 (
1860 ExprBuilder::with_data(1).ite(temp.clone(), temp.clone(), temp.clone()),
1861 Expr::ite(Expr::val(1), Expr::val(1), Expr::val(1)),
1862 ),
1863 (
1864 ExprBuilder::with_data(1).not(temp.clone()),
1865 Expr::not(Expr::val(1)),
1866 ),
1867 (
1868 ExprBuilder::with_data(1).is_eq(temp.clone(), temp.clone()),
1869 Expr::is_eq(Expr::val(1), Expr::val(1)),
1870 ),
1871 (
1872 ExprBuilder::with_data(1).and(temp.clone(), temp.clone()),
1873 Expr::and(Expr::val(1), Expr::val(1)),
1874 ),
1875 (
1876 ExprBuilder::with_data(1).or(temp.clone(), temp.clone()),
1877 Expr::or(Expr::val(1), Expr::val(1)),
1878 ),
1879 (
1880 ExprBuilder::with_data(1).less(temp.clone(), temp.clone()),
1881 Expr::less(Expr::val(1), Expr::val(1)),
1882 ),
1883 (
1884 ExprBuilder::with_data(1).lesseq(temp.clone(), temp.clone()),
1885 Expr::lesseq(Expr::val(1), Expr::val(1)),
1886 ),
1887 (
1888 ExprBuilder::with_data(1).greater(temp.clone(), temp.clone()),
1889 Expr::greater(Expr::val(1), Expr::val(1)),
1890 ),
1891 (
1892 ExprBuilder::with_data(1).greatereq(temp.clone(), temp.clone()),
1893 Expr::greatereq(Expr::val(1), Expr::val(1)),
1894 ),
1895 (
1896 ExprBuilder::with_data(1).add(temp.clone(), temp.clone()),
1897 Expr::add(Expr::val(1), Expr::val(1)),
1898 ),
1899 (
1900 ExprBuilder::with_data(1).sub(temp.clone(), temp.clone()),
1901 Expr::sub(Expr::val(1), Expr::val(1)),
1902 ),
1903 (
1904 ExprBuilder::with_data(1).mul(temp.clone(), temp.clone()),
1905 Expr::mul(Expr::val(1), Expr::val(1)),
1906 ),
1907 (
1908 ExprBuilder::with_data(1).neg(temp.clone()),
1909 Expr::neg(Expr::val(1)),
1910 ),
1911 (
1912 ExprBuilder::with_data(1).is_in(temp.clone(), temp.clone()),
1913 Expr::is_in(Expr::val(1), Expr::val(1)),
1914 ),
1915 (
1916 ExprBuilder::with_data(1).contains(temp.clone(), temp.clone()),
1917 Expr::contains(Expr::val(1), Expr::val(1)),
1918 ),
1919 (
1920 ExprBuilder::with_data(1).contains_all(temp.clone(), temp.clone()),
1921 Expr::contains_all(Expr::val(1), Expr::val(1)),
1922 ),
1923 (
1924 ExprBuilder::with_data(1).contains_any(temp.clone(), temp.clone()),
1925 Expr::contains_any(Expr::val(1), Expr::val(1)),
1926 ),
1927 (
1928 ExprBuilder::with_data(1).set([temp.clone()]),
1929 Expr::set([Expr::val(1)]),
1930 ),
1931 (
1932 ExprBuilder::with_data(1)
1933 .record([("foo".into(), temp.clone())])
1934 .unwrap(),
1935 Expr::record([("foo".into(), Expr::val(1))]).unwrap(),
1936 ),
1937 (
1938 ExprBuilder::with_data(1)
1939 .call_extension_fn("foo".parse().unwrap(), vec![temp.clone()]),
1940 Expr::call_extension_fn("foo".parse().unwrap(), vec![Expr::val(1)]),
1941 ),
1942 (
1943 ExprBuilder::with_data(1).get_attr(temp.clone(), "foo".into()),
1944 Expr::get_attr(Expr::val(1), "foo".into()),
1945 ),
1946 (
1947 ExprBuilder::with_data(1).has_attr(temp.clone(), "foo".into()),
1948 Expr::has_attr(Expr::val(1), "foo".into()),
1949 ),
1950 (
1951 ExprBuilder::with_data(1).like(temp.clone(), vec![PatternElem::Wildcard]),
1952 Expr::like(Expr::val(1), vec![PatternElem::Wildcard]),
1953 ),
1954 (
1955 ExprBuilder::with_data(1).is_entity_type(temp, "T".parse().unwrap()),
1956 Expr::is_entity_type(Expr::val(1), "T".parse().unwrap()),
1957 ),
1958 ];
1959
1960 for (e0, e1) in exprs {
1961 assert!(e0.eq_shape(e0));
1962 assert!(e1.eq_shape(e1));
1963 assert!(e0.eq_shape(e1));
1964 assert!(e1.eq_shape(e0));
1965
1966 let mut hasher0 = DefaultHasher::new();
1967 e0.hash_shape(&mut hasher0);
1968 let hash0 = hasher0.finish();
1969
1970 let mut hasher1 = DefaultHasher::new();
1971 e1.hash_shape(&mut hasher1);
1972 let hash1 = hasher1.finish();
1973
1974 assert_eq!(hash0, hash1);
1975 }
1976 }
1977
1978 #[test]
1979 fn expr_shape_only_not_eq() {
1980 let expr1 = ExprBuilder::with_data(1).val(1);
1981 let expr2 = ExprBuilder::with_data(1).val(2);
1982 assert_ne!(
1983 ExprShapeOnly::new_from_borrowed(&expr1),
1984 ExprShapeOnly::new_from_borrowed(&expr2)
1985 );
1986 }
1987
1988 #[test]
1989 fn untyped_subst_present() {
1990 let u = Unknown {
1991 name: "foo".into(),
1992 type_annotation: None,
1993 };
1994 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
1995 match r {
1996 Ok(e) => assert_eq!(e, Expr::val(1)),
1997 Err(empty) => match empty {},
1998 }
1999 }
2000
2001 #[test]
2002 fn untyped_subst_present_correct_type() {
2003 let u = Unknown {
2004 name: "foo".into(),
2005 type_annotation: Some(Type::Long),
2006 };
2007 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2008 match r {
2009 Ok(e) => assert_eq!(e, Expr::val(1)),
2010 Err(empty) => match empty {},
2011 }
2012 }
2013
2014 #[test]
2015 fn untyped_subst_present_wrong_type() {
2016 let u = Unknown {
2017 name: "foo".into(),
2018 type_annotation: Some(Type::Bool),
2019 };
2020 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2021 match r {
2022 Ok(e) => assert_eq!(e, Expr::val(1)),
2023 Err(empty) => match empty {},
2024 }
2025 }
2026
2027 #[test]
2028 fn untyped_subst_not_present() {
2029 let u = Unknown {
2030 name: "foo".into(),
2031 type_annotation: Some(Type::Bool),
2032 };
2033 let r = UntypedSubstitution::substitute(&u, None);
2034 match r {
2035 Ok(n) => assert_eq!(n, Expr::unknown(u)),
2036 Err(empty) => match empty {},
2037 }
2038 }
2039
2040 #[test]
2041 fn typed_subst_present() {
2042 let u = Unknown {
2043 name: "foo".into(),
2044 type_annotation: None,
2045 };
2046 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2047 assert_eq!(e, Expr::val(1));
2048 }
2049
2050 #[test]
2051 fn typed_subst_present_correct_type() {
2052 let u = Unknown {
2053 name: "foo".into(),
2054 type_annotation: Some(Type::Long),
2055 };
2056 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2057 assert_eq!(e, Expr::val(1));
2058 }
2059
2060 #[test]
2061 fn typed_subst_present_wrong_type() {
2062 let u = Unknown {
2063 name: "foo".into(),
2064 type_annotation: Some(Type::Bool),
2065 };
2066 let r = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap_err();
2067 assert_matches!(
2068 r,
2069 SubstitutionError::TypeError {
2070 expected: Type::Bool,
2071 actual: Type::Long,
2072 }
2073 );
2074 }
2075
2076 #[test]
2077 fn typed_subst_not_present() {
2078 let u = Unknown {
2079 name: "foo".into(),
2080 type_annotation: None,
2081 };
2082 let r = TypedSubstitution::substitute(&u, None).unwrap();
2083 assert_eq!(r, Expr::unknown(u));
2084 }
2085}