1use crate::{
18 ast::*,
19 expr_builder::{self, ExprBuilder as _},
20 extensions::Extensions,
21 parser::{err::ParseErrors, Loc},
22};
23use educe::Educe;
24use miette::Diagnostic;
25use serde::{Deserialize, Serialize};
26use smol_str::SmolStr;
27use std::{
28 borrow::Cow,
29 collections::{btree_map, BTreeMap, HashMap},
30 hash::{Hash, Hasher},
31 mem,
32 sync::Arc,
33};
34use thiserror::Error;
35
36#[cfg(feature = "wasm")]
37extern crate tsify;
38
39#[derive(Educe, Serialize, Deserialize, Debug, Clone)]
46#[educe(PartialEq, Eq, Hash)]
47pub struct Expr<T = ()> {
48 expr_kind: ExprKind<T>,
49 #[educe(PartialEq(ignore))]
50 #[educe(Hash(ignore))]
51 source_loc: Option<Loc>,
52 data: T,
53}
54
55#[derive(Serialize, Deserialize, Hash, Debug, Clone, PartialEq, Eq)]
58pub enum ExprKind<T = ()> {
59 Lit(Literal),
61 Var(Var),
63 Slot(SlotId),
65 Unknown(Unknown),
67 If {
69 test_expr: Arc<Expr<T>>,
71 then_expr: Arc<Expr<T>>,
73 else_expr: Arc<Expr<T>>,
75 },
76 And {
78 left: Arc<Expr<T>>,
80 right: Arc<Expr<T>>,
82 },
83 Or {
85 left: Arc<Expr<T>>,
87 right: Arc<Expr<T>>,
89 },
90 UnaryApp {
92 op: UnaryOp,
94 arg: Arc<Expr<T>>,
96 },
97 BinaryApp {
99 op: BinaryOp,
101 arg1: Arc<Expr<T>>,
103 arg2: Arc<Expr<T>>,
105 },
106 ExtensionFunctionApp {
112 fn_name: Name,
114 args: Arc<Vec<Expr<T>>>,
116 },
117 GetAttr {
119 expr: Arc<Expr<T>>,
122 attr: SmolStr,
124 },
125 HasAttr {
127 expr: Arc<Expr<T>>,
129 attr: SmolStr,
131 },
132 Like {
134 expr: Arc<Expr<T>>,
136 pattern: Pattern,
140 },
141 Is {
144 expr: Arc<Expr<T>>,
146 entity_type: EntityType,
148 },
149 Set(Arc<Vec<Expr<T>>>),
156 Record(Arc<BTreeMap<SmolStr, Expr<T>>>),
158}
159
160impl From<Value> for Expr {
161 fn from(v: Value) -> Self {
162 Expr::from(v.value).with_maybe_source_loc(v.loc)
163 }
164}
165
166impl From<ValueKind> for Expr {
167 fn from(v: ValueKind) -> Self {
168 match v {
169 ValueKind::Lit(lit) => Expr::val(lit),
170 ValueKind::Set(set) => Expr::set(set.iter().map(|v| Expr::from(v.clone()))),
171 #[allow(clippy::expect_used)]
173 ValueKind::Record(record) => Expr::record(
174 Arc::unwrap_or_clone(record)
175 .into_iter()
176 .map(|(k, v)| (k, Expr::from(v))),
177 )
178 .expect("cannot have duplicate key because the input was already a BTreeMap"),
179 ValueKind::ExtensionValue(ev) => RestrictedExpr::from(ev.as_ref().clone()).into(),
180 }
181 }
182}
183
184impl From<PartialValue> for Expr {
185 fn from(pv: PartialValue) -> Self {
186 match pv {
187 PartialValue::Value(v) => Expr::from(v),
188 PartialValue::Residual(expr) => expr,
189 }
190 }
191}
192
193impl<T> Expr<T> {
194 fn new(expr_kind: ExprKind<T>, source_loc: Option<Loc>, data: T) -> Self {
195 Self {
196 expr_kind,
197 source_loc,
198 data,
199 }
200 }
201
202 pub fn expr_kind(&self) -> &ExprKind<T> {
206 &self.expr_kind
207 }
208
209 pub fn into_expr_kind(self) -> ExprKind<T> {
211 self.expr_kind
212 }
213
214 pub fn data(&self) -> &T {
216 &self.data
217 }
218
219 pub fn into_data(self) -> T {
222 self.data
223 }
224
225 pub fn into_parts(self) -> (ExprKind<T>, Option<Loc>, T) {
228 (self.expr_kind, self.source_loc, self.data)
229 }
230
231 pub fn source_loc(&self) -> Option<&Loc> {
233 self.source_loc.as_ref()
234 }
235
236 pub fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
238 Self { source_loc, ..self }
239 }
240
241 pub fn set_data(&mut self, data: T) {
244 self.data = data;
245 }
246
247 pub fn is_ref(&self) -> bool {
252 match &self.expr_kind {
253 ExprKind::Lit(lit) => lit.is_ref(),
254 _ => false,
255 }
256 }
257
258 pub fn is_slot(&self) -> bool {
260 matches!(&self.expr_kind, ExprKind::Slot(_))
261 }
262
263 pub fn is_ref_set(&self) -> bool {
268 match &self.expr_kind {
269 ExprKind::Set(exprs) => exprs.iter().all(|e| e.is_ref()),
270 _ => false,
271 }
272 }
273
274 pub fn subexpressions(&self) -> impl Iterator<Item = &Self> {
276 expr_iterator::ExprIterator::new(self)
277 }
278
279 pub fn slots(&self) -> impl Iterator<Item = Slot> + '_ {
281 self.subexpressions()
282 .filter_map(|exp| match &exp.expr_kind {
283 ExprKind::Slot(slotid) => Some(Slot {
284 id: *slotid,
285 loc: exp.source_loc().cloned(),
286 }),
287 _ => None,
288 })
289 }
290
291 pub fn is_projectable(&self) -> bool {
295 self.subexpressions().all(|e| {
296 matches!(
297 e.expr_kind(),
298 ExprKind::Lit(_)
299 | ExprKind::Unknown(_)
300 | ExprKind::Set(_)
301 | ExprKind::Var(_)
302 | ExprKind::Record(_)
303 )
304 })
305 }
306
307 pub fn try_type_of(&self, extensions: &Extensions<'_>) -> Option<Type> {
319 match &self.expr_kind {
320 ExprKind::Lit(l) => Some(l.type_of()),
321 ExprKind::Var(_) => None,
322 ExprKind::Slot(_) => None,
323 ExprKind::Unknown(u) => u.type_annotation.clone(),
324 ExprKind::If {
325 then_expr,
326 else_expr,
327 ..
328 } => {
329 let type_of_then = then_expr.try_type_of(extensions);
330 let type_of_else = else_expr.try_type_of(extensions);
331 if type_of_then == type_of_else {
332 type_of_then
333 } else {
334 None
335 }
336 }
337 ExprKind::And { .. } => Some(Type::Bool),
338 ExprKind::Or { .. } => Some(Type::Bool),
339 ExprKind::UnaryApp {
340 op: UnaryOp::Neg, ..
341 } => Some(Type::Long),
342 ExprKind::UnaryApp {
343 op: UnaryOp::Not, ..
344 } => Some(Type::Bool),
345 ExprKind::UnaryApp {
346 op: UnaryOp::IsEmpty,
347 ..
348 } => Some(Type::Bool),
349 ExprKind::BinaryApp {
350 op: BinaryOp::Add | BinaryOp::Mul | BinaryOp::Sub,
351 ..
352 } => Some(Type::Long),
353 ExprKind::BinaryApp {
354 op:
355 BinaryOp::Contains
356 | BinaryOp::ContainsAll
357 | BinaryOp::ContainsAny
358 | BinaryOp::Eq
359 | BinaryOp::In
360 | BinaryOp::Less
361 | BinaryOp::LessEq,
362 ..
363 } => Some(Type::Bool),
364 ExprKind::BinaryApp {
365 op: BinaryOp::HasTag,
366 ..
367 } => Some(Type::Bool),
368 ExprKind::ExtensionFunctionApp { fn_name, .. } => extensions
369 .func(fn_name)
370 .ok()?
371 .return_type()
372 .map(|rty| rty.clone().into()),
373 ExprKind::GetAttr { .. } => None,
378 ExprKind::BinaryApp {
380 op: BinaryOp::GetTag,
381 ..
382 } => None,
383 ExprKind::HasAttr { .. } => Some(Type::Bool),
384 ExprKind::Like { .. } => Some(Type::Bool),
385 ExprKind::Is { .. } => Some(Type::Bool),
386 ExprKind::Set(_) => Some(Type::Set),
387 ExprKind::Record(_) => Some(Type::Record),
388 }
389 }
390}
391
392#[allow(dead_code)] #[allow(clippy::should_implement_trait)] impl Expr {
395 pub fn val(v: impl Into<Literal>) -> Self {
399 ExprBuilder::new().val(v)
400 }
401
402 pub fn unknown(u: Unknown) -> Self {
404 ExprBuilder::new().unknown(u)
405 }
406
407 pub fn var(v: Var) -> Self {
409 ExprBuilder::new().var(v)
410 }
411
412 pub fn slot(s: SlotId) -> Self {
414 ExprBuilder::new().slot(s)
415 }
416
417 pub fn ite(test_expr: Expr, then_expr: Expr, else_expr: Expr) -> Self {
421 ExprBuilder::new().ite(test_expr, then_expr, else_expr)
422 }
423
424 pub fn ite_arc(test_expr: Arc<Expr>, then_expr: Arc<Expr>, else_expr: Arc<Expr>) -> Self {
428 ExprBuilder::new().ite_arc(test_expr, then_expr, else_expr)
429 }
430
431 pub fn not(e: Expr) -> Self {
433 ExprBuilder::new().not(e)
434 }
435
436 pub fn is_eq(e1: Expr, e2: Expr) -> Self {
438 ExprBuilder::new().is_eq(e1, e2)
439 }
440
441 pub fn noteq(e1: Expr, e2: Expr) -> Self {
443 ExprBuilder::new().noteq(e1, e2)
444 }
445
446 pub fn and(e1: Expr, e2: Expr) -> Self {
448 ExprBuilder::new().and(e1, e2)
449 }
450
451 pub fn or(e1: Expr, e2: Expr) -> Self {
453 ExprBuilder::new().or(e1, e2)
454 }
455
456 pub fn less(e1: Expr, e2: Expr) -> Self {
458 ExprBuilder::new().less(e1, e2)
459 }
460
461 pub fn lesseq(e1: Expr, e2: Expr) -> Self {
463 ExprBuilder::new().lesseq(e1, e2)
464 }
465
466 pub fn greater(e1: Expr, e2: Expr) -> Self {
468 ExprBuilder::new().greater(e1, e2)
469 }
470
471 pub fn greatereq(e1: Expr, e2: Expr) -> Self {
473 ExprBuilder::new().greatereq(e1, e2)
474 }
475
476 pub fn add(e1: Expr, e2: Expr) -> Self {
478 ExprBuilder::new().add(e1, e2)
479 }
480
481 pub fn sub(e1: Expr, e2: Expr) -> Self {
483 ExprBuilder::new().sub(e1, e2)
484 }
485
486 pub fn mul(e1: Expr, e2: Expr) -> Self {
488 ExprBuilder::new().mul(e1, e2)
489 }
490
491 pub fn neg(e: Expr) -> Self {
493 ExprBuilder::new().neg(e)
494 }
495
496 pub fn is_in(e1: Expr, e2: Expr) -> Self {
500 ExprBuilder::new().is_in(e1, e2)
501 }
502
503 pub fn contains(e1: Expr, e2: Expr) -> Self {
506 ExprBuilder::new().contains(e1, e2)
507 }
508
509 pub fn contains_all(e1: Expr, e2: Expr) -> Self {
511 ExprBuilder::new().contains_all(e1, e2)
512 }
513
514 pub fn contains_any(e1: Expr, e2: Expr) -> Self {
516 ExprBuilder::new().contains_any(e1, e2)
517 }
518
519 pub fn is_empty(e: Expr) -> Self {
521 ExprBuilder::new().is_empty(e)
522 }
523
524 pub fn get_tag(expr: Expr, tag: Expr) -> Self {
527 ExprBuilder::new().get_tag(expr, tag)
528 }
529
530 pub fn has_tag(expr: Expr, tag: Expr) -> Self {
533 ExprBuilder::new().has_tag(expr, tag)
534 }
535
536 pub fn set(exprs: impl IntoIterator<Item = Expr>) -> Self {
538 ExprBuilder::new().set(exprs)
539 }
540
541 pub fn record(
543 pairs: impl IntoIterator<Item = (SmolStr, Expr)>,
544 ) -> Result<Self, ExpressionConstructionError> {
545 ExprBuilder::new().record(pairs)
546 }
547
548 pub fn record_arc(map: Arc<BTreeMap<SmolStr, Expr>>) -> Self {
556 ExprBuilder::new().record_arc(map)
557 }
558
559 pub fn call_extension_fn(fn_name: Name, args: Vec<Expr>) -> Self {
562 ExprBuilder::new().call_extension_fn(fn_name, args)
563 }
564
565 pub fn unary_app(op: impl Into<UnaryOp>, arg: Expr) -> Self {
568 ExprBuilder::new().unary_app(op, arg)
569 }
570
571 pub fn binary_app(op: impl Into<BinaryOp>, arg1: Expr, arg2: Expr) -> Self {
574 ExprBuilder::new().binary_app(op, arg1, arg2)
575 }
576
577 pub fn get_attr(expr: Expr, attr: SmolStr) -> Self {
581 ExprBuilder::new().get_attr(expr, attr)
582 }
583
584 pub fn has_attr(expr: Expr, attr: SmolStr) -> Self {
589 ExprBuilder::new().has_attr(expr, attr)
590 }
591
592 pub fn like(expr: Expr, pattern: Pattern) -> Self {
596 ExprBuilder::new().like(expr, pattern)
597 }
598
599 pub fn is_entity_type(expr: Expr, entity_type: EntityType) -> Self {
601 ExprBuilder::new().is_entity_type(expr, entity_type)
602 }
603
604 pub fn contains_unknown(&self) -> bool {
606 self.subexpressions()
607 .any(|e| matches!(e.expr_kind(), ExprKind::Unknown(_)))
608 }
609
610 pub fn unknowns(&self) -> impl Iterator<Item = &Unknown> {
612 self.subexpressions()
613 .filter_map(|subexpr| match subexpr.expr_kind() {
614 ExprKind::Unknown(u) => Some(u),
615 _ => None,
616 })
617 }
618
619 pub fn substitute(&self, definitions: &HashMap<SmolStr, Value>) -> Expr {
624 match self.substitute_general::<UntypedSubstitution>(definitions) {
625 Ok(e) => e,
626 Err(empty) => match empty {},
627 }
628 }
629
630 pub fn substitute_typed(
635 &self,
636 definitions: &HashMap<SmolStr, Value>,
637 ) -> Result<Expr, SubstitutionError> {
638 self.substitute_general::<TypedSubstitution>(definitions)
639 }
640
641 fn substitute_general<T: SubstitutionFunction>(
645 &self,
646 definitions: &HashMap<SmolStr, Value>,
647 ) -> Result<Expr, T::Err> {
648 match self.expr_kind() {
649 ExprKind::Lit(_) => Ok(self.clone()),
650 ExprKind::Unknown(u @ Unknown { name, .. }) => T::substitute(u, definitions.get(name)),
651 ExprKind::Var(_) => Ok(self.clone()),
652 ExprKind::Slot(_) => Ok(self.clone()),
653 ExprKind::If {
654 test_expr,
655 then_expr,
656 else_expr,
657 } => Ok(Expr::ite(
658 test_expr.substitute_general::<T>(definitions)?,
659 then_expr.substitute_general::<T>(definitions)?,
660 else_expr.substitute_general::<T>(definitions)?,
661 )),
662 ExprKind::And { left, right } => Ok(Expr::and(
663 left.substitute_general::<T>(definitions)?,
664 right.substitute_general::<T>(definitions)?,
665 )),
666 ExprKind::Or { left, right } => Ok(Expr::or(
667 left.substitute_general::<T>(definitions)?,
668 right.substitute_general::<T>(definitions)?,
669 )),
670 ExprKind::UnaryApp { op, arg } => Ok(Expr::unary_app(
671 *op,
672 arg.substitute_general::<T>(definitions)?,
673 )),
674 ExprKind::BinaryApp { op, arg1, arg2 } => Ok(Expr::binary_app(
675 *op,
676 arg1.substitute_general::<T>(definitions)?,
677 arg2.substitute_general::<T>(definitions)?,
678 )),
679 ExprKind::ExtensionFunctionApp { fn_name, args } => {
680 let args = args
681 .iter()
682 .map(|e| e.substitute_general::<T>(definitions))
683 .collect::<Result<Vec<Expr>, _>>()?;
684
685 Ok(Expr::call_extension_fn(fn_name.clone(), args))
686 }
687 ExprKind::GetAttr { expr, attr } => Ok(Expr::get_attr(
688 expr.substitute_general::<T>(definitions)?,
689 attr.clone(),
690 )),
691 ExprKind::HasAttr { expr, attr } => Ok(Expr::has_attr(
692 expr.substitute_general::<T>(definitions)?,
693 attr.clone(),
694 )),
695 ExprKind::Like { expr, pattern } => Ok(Expr::like(
696 expr.substitute_general::<T>(definitions)?,
697 pattern.clone(),
698 )),
699 ExprKind::Set(members) => {
700 let members = members
701 .iter()
702 .map(|e| e.substitute_general::<T>(definitions))
703 .collect::<Result<Vec<_>, _>>()?;
704 Ok(Expr::set(members))
705 }
706 ExprKind::Record(map) => {
707 let map = map
708 .iter()
709 .map(|(name, e)| Ok((name.clone(), e.substitute_general::<T>(definitions)?)))
710 .collect::<Result<BTreeMap<_, _>, _>>()?;
711 #[allow(clippy::expect_used)]
713 Ok(Expr::record(map)
714 .expect("cannot have a duplicate key because the input was already a BTreeMap"))
715 }
716 ExprKind::Is { expr, entity_type } => Ok(Expr::is_entity_type(
717 expr.substitute_general::<T>(definitions)?,
718 entity_type.clone(),
719 )),
720 }
721 }
722}
723
724trait SubstitutionFunction {
726 type Err;
728 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err>;
734}
735
736struct TypedSubstitution {}
737
738impl SubstitutionFunction for TypedSubstitution {
739 type Err = SubstitutionError;
740
741 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
742 match (substitute, &value.type_annotation) {
743 (None, _) => Ok(Expr::unknown(value.clone())),
744 (Some(v), None) => Ok(v.clone().into()),
745 (Some(v), Some(t)) => {
746 if v.type_of() == *t {
747 Ok(v.clone().into())
748 } else {
749 Err(SubstitutionError::TypeError {
750 expected: t.clone(),
751 actual: v.type_of(),
752 })
753 }
754 }
755 }
756 }
757}
758
759struct UntypedSubstitution {}
760
761impl SubstitutionFunction for UntypedSubstitution {
762 type Err = std::convert::Infallible;
763
764 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
765 Ok(substitute
766 .map(|v| v.clone().into())
767 .unwrap_or_else(|| Expr::unknown(value.clone())))
768 }
769}
770
771impl<T: Clone> std::fmt::Display for Expr<T> {
772 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
773 write!(f, "{}", crate::est::Expr::from(self.clone()))
777 }
778}
779
780impl<T: Clone> BoundedDisplay for Expr<T> {
781 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
782 BoundedDisplay::fmt(&crate::est::Expr::from(self.clone()), f, n)
785 }
786}
787
788impl std::str::FromStr for Expr {
789 type Err = ParseErrors;
790
791 fn from_str(s: &str) -> Result<Expr, Self::Err> {
792 crate::parser::parse_expr(s)
793 }
794}
795
796#[derive(Debug, Clone, Diagnostic, Error)]
798pub enum SubstitutionError {
799 #[error("expected a value of type {expected}, got a value of type {actual}")]
801 TypeError {
802 expected: Type,
804 actual: Type,
806 },
807}
808
809#[derive(Serialize, Deserialize, Hash, Debug, Clone, PartialEq, Eq)]
811pub struct Unknown {
812 pub name: SmolStr,
814 pub type_annotation: Option<Type>,
818}
819
820impl Unknown {
821 pub fn new_untyped(name: impl Into<SmolStr>) -> Self {
823 Self {
824 name: name.into(),
825 type_annotation: None,
826 }
827 }
828
829 pub fn new_with_type(name: impl Into<SmolStr>, ty: Type) -> Self {
832 Self {
833 name: name.into(),
834 type_annotation: Some(ty),
835 }
836 }
837}
838
839impl std::fmt::Display for Unknown {
840 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
841 write!(f, "{}", crate::est::Expr::from(Expr::unknown(self.clone())))
844 }
845}
846
847#[derive(Clone, Debug)]
850pub struct ExprBuilder<T> {
851 source_loc: Option<Loc>,
852 data: T,
853}
854
855impl<T: Default + Clone> expr_builder::ExprBuilder for ExprBuilder<T> {
856 type Expr = Expr<T>;
857
858 type Data = T;
859
860 fn loc(&self) -> Option<&Loc> {
861 self.source_loc.as_ref()
862 }
863
864 fn data(&self) -> &Self::Data {
865 &self.data
866 }
867
868 fn with_data(data: T) -> Self {
869 Self {
870 source_loc: None,
871 data,
872 }
873 }
874
875 fn with_maybe_source_loc(mut self, maybe_source_loc: Option<&Loc>) -> Self {
876 self.source_loc = maybe_source_loc.cloned();
877 self
878 }
879
880 fn val(self, v: impl Into<Literal>) -> Expr<T> {
884 self.with_expr_kind(ExprKind::Lit(v.into()))
885 }
886
887 fn unknown(self, u: Unknown) -> Expr<T> {
889 self.with_expr_kind(ExprKind::Unknown(u))
890 }
891
892 fn var(self, v: Var) -> Expr<T> {
894 self.with_expr_kind(ExprKind::Var(v))
895 }
896
897 fn slot(self, s: SlotId) -> Expr<T> {
899 self.with_expr_kind(ExprKind::Slot(s))
900 }
901
902 fn ite(self, test_expr: Expr<T>, then_expr: Expr<T>, else_expr: Expr<T>) -> Expr<T> {
906 self.with_expr_kind(ExprKind::If {
907 test_expr: Arc::new(test_expr),
908 then_expr: Arc::new(then_expr),
909 else_expr: Arc::new(else_expr),
910 })
911 }
912
913 fn not(self, e: Expr<T>) -> Expr<T> {
915 self.with_expr_kind(ExprKind::UnaryApp {
916 op: UnaryOp::Not,
917 arg: Arc::new(e),
918 })
919 }
920
921 fn is_eq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
923 self.with_expr_kind(ExprKind::BinaryApp {
924 op: BinaryOp::Eq,
925 arg1: Arc::new(e1),
926 arg2: Arc::new(e2),
927 })
928 }
929
930 fn and(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
932 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
933 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
934 ExprKind::Lit(Literal::Bool(*b1 && *b2))
935 }
936 _ => ExprKind::And {
937 left: Arc::new(e1),
938 right: Arc::new(e2),
939 },
940 })
941 }
942
943 fn or(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
945 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
946 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
947 ExprKind::Lit(Literal::Bool(*b1 || *b2))
948 }
949
950 _ => ExprKind::Or {
951 left: Arc::new(e1),
952 right: Arc::new(e2),
953 },
954 })
955 }
956
957 fn less(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
959 self.with_expr_kind(ExprKind::BinaryApp {
960 op: BinaryOp::Less,
961 arg1: Arc::new(e1),
962 arg2: Arc::new(e2),
963 })
964 }
965
966 fn lesseq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
968 self.with_expr_kind(ExprKind::BinaryApp {
969 op: BinaryOp::LessEq,
970 arg1: Arc::new(e1),
971 arg2: Arc::new(e2),
972 })
973 }
974
975 fn add(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
977 self.with_expr_kind(ExprKind::BinaryApp {
978 op: BinaryOp::Add,
979 arg1: Arc::new(e1),
980 arg2: Arc::new(e2),
981 })
982 }
983
984 fn sub(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
986 self.with_expr_kind(ExprKind::BinaryApp {
987 op: BinaryOp::Sub,
988 arg1: Arc::new(e1),
989 arg2: Arc::new(e2),
990 })
991 }
992
993 fn mul(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
995 self.with_expr_kind(ExprKind::BinaryApp {
996 op: BinaryOp::Mul,
997 arg1: Arc::new(e1),
998 arg2: Arc::new(e2),
999 })
1000 }
1001
1002 fn neg(self, e: Expr<T>) -> Expr<T> {
1004 self.with_expr_kind(ExprKind::UnaryApp {
1005 op: UnaryOp::Neg,
1006 arg: Arc::new(e),
1007 })
1008 }
1009
1010 fn is_in(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1014 self.with_expr_kind(ExprKind::BinaryApp {
1015 op: BinaryOp::In,
1016 arg1: Arc::new(e1),
1017 arg2: Arc::new(e2),
1018 })
1019 }
1020
1021 fn contains(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1024 self.with_expr_kind(ExprKind::BinaryApp {
1025 op: BinaryOp::Contains,
1026 arg1: Arc::new(e1),
1027 arg2: Arc::new(e2),
1028 })
1029 }
1030
1031 fn contains_all(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1033 self.with_expr_kind(ExprKind::BinaryApp {
1034 op: BinaryOp::ContainsAll,
1035 arg1: Arc::new(e1),
1036 arg2: Arc::new(e2),
1037 })
1038 }
1039
1040 fn contains_any(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1042 self.with_expr_kind(ExprKind::BinaryApp {
1043 op: BinaryOp::ContainsAny,
1044 arg1: Arc::new(e1),
1045 arg2: Arc::new(e2),
1046 })
1047 }
1048
1049 fn is_empty(self, expr: Expr<T>) -> Expr<T> {
1051 self.with_expr_kind(ExprKind::UnaryApp {
1052 op: UnaryOp::IsEmpty,
1053 arg: Arc::new(expr),
1054 })
1055 }
1056
1057 fn get_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1060 self.with_expr_kind(ExprKind::BinaryApp {
1061 op: BinaryOp::GetTag,
1062 arg1: Arc::new(expr),
1063 arg2: Arc::new(tag),
1064 })
1065 }
1066
1067 fn has_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1070 self.with_expr_kind(ExprKind::BinaryApp {
1071 op: BinaryOp::HasTag,
1072 arg1: Arc::new(expr),
1073 arg2: Arc::new(tag),
1074 })
1075 }
1076
1077 fn set(self, exprs: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1079 self.with_expr_kind(ExprKind::Set(Arc::new(exprs.into_iter().collect())))
1080 }
1081
1082 fn record(
1084 self,
1085 pairs: impl IntoIterator<Item = (SmolStr, Expr<T>)>,
1086 ) -> Result<Expr<T>, ExpressionConstructionError> {
1087 let mut map = BTreeMap::new();
1088 for (k, v) in pairs {
1089 match map.entry(k) {
1090 btree_map::Entry::Occupied(oentry) => {
1091 return Err(expression_construction_errors::DuplicateKeyError {
1092 key: oentry.key().clone(),
1093 context: "in record literal",
1094 }
1095 .into());
1096 }
1097 btree_map::Entry::Vacant(ventry) => {
1098 ventry.insert(v);
1099 }
1100 }
1101 }
1102 Ok(self.with_expr_kind(ExprKind::Record(Arc::new(map))))
1103 }
1104
1105 fn call_extension_fn(self, fn_name: Name, args: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1108 self.with_expr_kind(ExprKind::ExtensionFunctionApp {
1109 fn_name,
1110 args: Arc::new(args.into_iter().collect()),
1111 })
1112 }
1113
1114 fn unary_app(self, op: impl Into<UnaryOp>, arg: Expr<T>) -> Expr<T> {
1117 self.with_expr_kind(ExprKind::UnaryApp {
1118 op: op.into(),
1119 arg: Arc::new(arg),
1120 })
1121 }
1122
1123 fn binary_app(self, op: impl Into<BinaryOp>, arg1: Expr<T>, arg2: Expr<T>) -> Expr<T> {
1126 self.with_expr_kind(ExprKind::BinaryApp {
1127 op: op.into(),
1128 arg1: Arc::new(arg1),
1129 arg2: Arc::new(arg2),
1130 })
1131 }
1132
1133 fn get_attr(self, expr: Expr<T>, attr: SmolStr) -> Expr<T> {
1137 self.with_expr_kind(ExprKind::GetAttr {
1138 expr: Arc::new(expr),
1139 attr,
1140 })
1141 }
1142
1143 fn has_attr(self, expr: Expr<T>, attr: SmolStr) -> Expr<T> {
1148 self.with_expr_kind(ExprKind::HasAttr {
1149 expr: Arc::new(expr),
1150 attr,
1151 })
1152 }
1153
1154 fn like(self, expr: Expr<T>, pattern: Pattern) -> Expr<T> {
1158 self.with_expr_kind(ExprKind::Like {
1159 expr: Arc::new(expr),
1160 pattern,
1161 })
1162 }
1163
1164 fn is_entity_type(self, expr: Expr<T>, entity_type: EntityType) -> Expr<T> {
1166 self.with_expr_kind(ExprKind::Is {
1167 expr: Arc::new(expr),
1168 entity_type,
1169 })
1170 }
1171}
1172
1173impl<T> ExprBuilder<T> {
1174 pub fn with_expr_kind(self, expr_kind: ExprKind<T>) -> Expr<T> {
1177 Expr::new(expr_kind, self.source_loc, self.data)
1178 }
1179
1180 pub fn ite_arc(
1184 self,
1185 test_expr: Arc<Expr<T>>,
1186 then_expr: Arc<Expr<T>>,
1187 else_expr: Arc<Expr<T>>,
1188 ) -> Expr<T> {
1189 self.with_expr_kind(ExprKind::If {
1190 test_expr,
1191 then_expr,
1192 else_expr,
1193 })
1194 }
1195
1196 pub fn record_arc(self, map: Arc<BTreeMap<SmolStr, Expr<T>>>) -> Expr<T> {
1203 self.with_expr_kind(ExprKind::Record(map))
1204 }
1205}
1206
1207impl<T: Clone + Default> ExprBuilder<T> {
1208 pub fn with_same_source_loc<U>(self, expr: &Expr<U>) -> Self {
1212 self.with_maybe_source_loc(expr.source_loc.as_ref())
1213 }
1214}
1215
1216#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1222pub enum ExpressionConstructionError {
1223 #[error(transparent)]
1225 #[diagnostic(transparent)]
1226 DuplicateKey(#[from] expression_construction_errors::DuplicateKeyError),
1227}
1228
1229pub mod expression_construction_errors {
1231 use miette::Diagnostic;
1232 use smol_str::SmolStr;
1233 use thiserror::Error;
1234
1235 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1241 #[error("duplicate key `{key}` {context}")]
1242 pub struct DuplicateKeyError {
1243 pub(crate) key: SmolStr,
1245 pub(crate) context: &'static str,
1247 }
1248
1249 impl DuplicateKeyError {
1250 pub fn key(&self) -> &str {
1252 &self.key
1253 }
1254
1255 pub(crate) fn with_context(self, context: &'static str) -> Self {
1257 Self { context, ..self }
1258 }
1259 }
1260}
1261
1262#[derive(Eq, Debug, Clone)]
1266pub struct ExprShapeOnly<'a, T: Clone = ()>(Cow<'a, Expr<T>>);
1267
1268impl<'a, T: Clone> ExprShapeOnly<'a, T> {
1269 pub fn new_from_borrowed(e: &'a Expr<T>) -> ExprShapeOnly<'a, T> {
1273 ExprShapeOnly(Cow::Borrowed(e))
1274 }
1275
1276 pub fn new_from_owned(e: Expr<T>) -> ExprShapeOnly<'a, T> {
1280 ExprShapeOnly(Cow::Owned(e))
1281 }
1282}
1283
1284impl<T: Clone> PartialEq for ExprShapeOnly<'_, T> {
1285 fn eq(&self, other: &Self) -> bool {
1286 self.0.eq_shape(&other.0)
1287 }
1288}
1289
1290impl<T: Clone> Hash for ExprShapeOnly<'_, T> {
1291 fn hash<H: Hasher>(&self, state: &mut H) {
1292 self.0.hash_shape(state);
1293 }
1294}
1295
1296impl<T> Expr<T> {
1297 pub fn eq_shape<U>(&self, other: &Expr<U>) -> bool {
1304 use ExprKind::*;
1305 match (self.expr_kind(), other.expr_kind()) {
1306 (Lit(lit), Lit(lit1)) => lit == lit1,
1307 (Var(v), Var(v1)) => v == v1,
1308 (Slot(s), Slot(s1)) => s == s1,
1309 (
1310 Unknown(self::Unknown {
1311 name: name1,
1312 type_annotation: ta_1,
1313 }),
1314 Unknown(self::Unknown {
1315 name: name2,
1316 type_annotation: ta_2,
1317 }),
1318 ) => (name1 == name2) && (ta_1 == ta_2),
1319 (
1320 If {
1321 test_expr,
1322 then_expr,
1323 else_expr,
1324 },
1325 If {
1326 test_expr: test_expr1,
1327 then_expr: then_expr1,
1328 else_expr: else_expr1,
1329 },
1330 ) => {
1331 test_expr.eq_shape(test_expr1)
1332 && then_expr.eq_shape(then_expr1)
1333 && else_expr.eq_shape(else_expr1)
1334 }
1335 (
1336 And { left, right },
1337 And {
1338 left: left1,
1339 right: right1,
1340 },
1341 )
1342 | (
1343 Or { left, right },
1344 Or {
1345 left: left1,
1346 right: right1,
1347 },
1348 ) => left.eq_shape(left1) && right.eq_shape(right1),
1349 (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1350 op == op1 && arg.eq_shape(arg1)
1351 }
1352 (
1353 BinaryApp { op, arg1, arg2 },
1354 BinaryApp {
1355 op: op1,
1356 arg1: arg11,
1357 arg2: arg21,
1358 },
1359 ) => op == op1 && arg1.eq_shape(arg11) && arg2.eq_shape(arg21),
1360 (
1361 ExtensionFunctionApp { fn_name, args },
1362 ExtensionFunctionApp {
1363 fn_name: fn_name1,
1364 args: args1,
1365 },
1366 ) => fn_name == fn_name1 && args.iter().zip(args1.iter()).all(|(a, a1)| a.eq_shape(a1)),
1367 (
1368 GetAttr { expr, attr },
1369 GetAttr {
1370 expr: expr1,
1371 attr: attr1,
1372 },
1373 )
1374 | (
1375 HasAttr { expr, attr },
1376 HasAttr {
1377 expr: expr1,
1378 attr: attr1,
1379 },
1380 ) => attr == attr1 && expr.eq_shape(expr1),
1381 (
1382 Like { expr, pattern },
1383 Like {
1384 expr: expr1,
1385 pattern: pattern1,
1386 },
1387 ) => pattern == pattern1 && expr.eq_shape(expr1),
1388 (Set(elems), Set(elems1)) => elems
1389 .iter()
1390 .zip(elems1.iter())
1391 .all(|(e, e1)| e.eq_shape(e1)),
1392 (Record(map), Record(map1)) => {
1393 map.len() == map1.len()
1394 && map
1395 .iter()
1396 .zip(map1.iter()) .all(|((a, e), (a1, e1))| a == a1 && e.eq_shape(e1))
1398 }
1399 (
1400 Is { expr, entity_type },
1401 Is {
1402 expr: expr1,
1403 entity_type: entity_type1,
1404 },
1405 ) => entity_type == entity_type1 && expr.eq_shape(expr1),
1406 _ => false,
1407 }
1408 }
1409
1410 pub fn hash_shape<H>(&self, state: &mut H)
1414 where
1415 H: Hasher,
1416 {
1417 mem::discriminant(self).hash(state);
1418 match self.expr_kind() {
1419 ExprKind::Lit(lit) => lit.hash(state),
1420 ExprKind::Var(v) => v.hash(state),
1421 ExprKind::Slot(s) => s.hash(state),
1422 ExprKind::Unknown(u) => u.hash(state),
1423 ExprKind::If {
1424 test_expr,
1425 then_expr,
1426 else_expr,
1427 } => {
1428 test_expr.hash_shape(state);
1429 then_expr.hash_shape(state);
1430 else_expr.hash_shape(state);
1431 }
1432 ExprKind::And { left, right } => {
1433 left.hash_shape(state);
1434 right.hash_shape(state);
1435 }
1436 ExprKind::Or { left, right } => {
1437 left.hash_shape(state);
1438 right.hash_shape(state);
1439 }
1440 ExprKind::UnaryApp { op, arg } => {
1441 op.hash(state);
1442 arg.hash_shape(state);
1443 }
1444 ExprKind::BinaryApp { op, arg1, arg2 } => {
1445 op.hash(state);
1446 arg1.hash_shape(state);
1447 arg2.hash_shape(state);
1448 }
1449 ExprKind::ExtensionFunctionApp { fn_name, args } => {
1450 fn_name.hash(state);
1451 state.write_usize(args.len());
1452 args.iter().for_each(|a| {
1453 a.hash_shape(state);
1454 });
1455 }
1456 ExprKind::GetAttr { expr, attr } => {
1457 expr.hash_shape(state);
1458 attr.hash(state);
1459 }
1460 ExprKind::HasAttr { expr, attr } => {
1461 expr.hash_shape(state);
1462 attr.hash(state);
1463 }
1464 ExprKind::Like { expr, pattern } => {
1465 expr.hash_shape(state);
1466 pattern.hash(state);
1467 }
1468 ExprKind::Set(elems) => {
1469 state.write_usize(elems.len());
1470 elems.iter().for_each(|e| {
1471 e.hash_shape(state);
1472 })
1473 }
1474 ExprKind::Record(map) => {
1475 state.write_usize(map.len());
1476 map.iter().for_each(|(s, a)| {
1477 s.hash(state);
1478 a.hash_shape(state);
1479 });
1480 }
1481 ExprKind::Is { expr, entity_type } => {
1482 expr.hash_shape(state);
1483 entity_type.hash(state);
1484 }
1485 }
1486 }
1487}
1488
1489#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
1491#[serde(rename_all = "camelCase")]
1492#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1493#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
1494#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
1495pub enum Var {
1496 Principal,
1498 Action,
1500 Resource,
1502 Context,
1504}
1505
1506#[cfg(test)]
1507mod var_generator {
1508 use super::Var;
1509 #[cfg(test)]
1510 pub fn all_vars() -> impl Iterator<Item = Var> {
1511 [Var::Principal, Var::Action, Var::Resource, Var::Context].into_iter()
1512 }
1513}
1514impl From<PrincipalOrResource> for Var {
1521 fn from(v: PrincipalOrResource) -> Self {
1522 match v {
1523 PrincipalOrResource::Principal => Var::Principal,
1524 PrincipalOrResource::Resource => Var::Resource,
1525 }
1526 }
1527}
1528
1529#[allow(clippy::fallible_impl_from)]
1531impl From<Var> for Id {
1532 fn from(var: Var) -> Self {
1533 #[allow(clippy::unwrap_used)]
1535 format!("{var}").parse().unwrap()
1536 }
1537}
1538
1539#[allow(clippy::fallible_impl_from)]
1541impl From<Var> for UnreservedId {
1542 fn from(var: Var) -> Self {
1543 #[allow(clippy::unwrap_used)]
1545 Id::from(var).try_into().unwrap()
1546 }
1547}
1548
1549impl std::fmt::Display for Var {
1550 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1551 match self {
1552 Self::Principal => write!(f, "principal"),
1553 Self::Action => write!(f, "action"),
1554 Self::Resource => write!(f, "resource"),
1555 Self::Context => write!(f, "context"),
1556 }
1557 }
1558}
1559
1560#[cfg(test)]
1561mod test {
1562 use cool_asserts::assert_matches;
1563 use itertools::Itertools;
1564 use std::collections::{hash_map::DefaultHasher, HashSet};
1565
1566 use crate::expr_builder::ExprBuilder as _;
1567
1568 use super::{var_generator::all_vars, *};
1569
1570 #[test]
1572 fn all_vars_are_ids() {
1573 for var in all_vars() {
1574 let _id: Id = var.into();
1575 let _id: UnreservedId = var.into();
1576 }
1577 }
1578
1579 #[test]
1580 fn exprs() {
1581 assert_eq!(
1582 Expr::val(33),
1583 Expr::new(ExprKind::Lit(Literal::Long(33)), None, ())
1584 );
1585 assert_eq!(
1586 Expr::val("hello"),
1587 Expr::new(ExprKind::Lit(Literal::from("hello")), None, ())
1588 );
1589 assert_eq!(
1590 Expr::val(EntityUID::with_eid("foo")),
1591 Expr::new(
1592 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1593 None,
1594 ()
1595 )
1596 );
1597 assert_eq!(
1598 Expr::var(Var::Principal),
1599 Expr::new(ExprKind::Var(Var::Principal), None, ())
1600 );
1601 assert_eq!(
1602 Expr::ite(Expr::val(true), Expr::val(88), Expr::val(-100)),
1603 Expr::new(
1604 ExprKind::If {
1605 test_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(true)), None, ())),
1606 then_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(88)), None, ())),
1607 else_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(-100)), None, ())),
1608 },
1609 None,
1610 ()
1611 )
1612 );
1613 assert_eq!(
1614 Expr::not(Expr::val(false)),
1615 Expr::new(
1616 ExprKind::UnaryApp {
1617 op: UnaryOp::Not,
1618 arg: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(false)), None, ())),
1619 },
1620 None,
1621 ()
1622 )
1623 );
1624 assert_eq!(
1625 Expr::get_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
1626 Expr::new(
1627 ExprKind::GetAttr {
1628 expr: Arc::new(Expr::new(
1629 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1630 None,
1631 ()
1632 )),
1633 attr: "some_attr".into()
1634 },
1635 None,
1636 ()
1637 )
1638 );
1639 assert_eq!(
1640 Expr::has_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
1641 Expr::new(
1642 ExprKind::HasAttr {
1643 expr: Arc::new(Expr::new(
1644 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1645 None,
1646 ()
1647 )),
1648 attr: "some_attr".into()
1649 },
1650 None,
1651 ()
1652 )
1653 );
1654 assert_eq!(
1655 Expr::is_entity_type(
1656 Expr::val(EntityUID::with_eid("foo")),
1657 "Type".parse().unwrap()
1658 ),
1659 Expr::new(
1660 ExprKind::Is {
1661 expr: Arc::new(Expr::new(
1662 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1663 None,
1664 ()
1665 )),
1666 entity_type: "Type".parse().unwrap()
1667 },
1668 None,
1669 ()
1670 ),
1671 );
1672 }
1673
1674 #[test]
1675 fn like_display() {
1676 let e = Expr::like(Expr::val("a"), Pattern::from(vec![PatternElem::Char('\0')]));
1678 assert_eq!(format!("{e}"), r#""a" like "\0""#);
1679 let e = Expr::like(
1681 Expr::val("a"),
1682 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('0')]),
1683 );
1684 assert_eq!(format!("{e}"), r#""a" like "\\0""#);
1685 let e = Expr::like(
1687 Expr::val("a"),
1688 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Wildcard]),
1689 );
1690 assert_eq!(format!("{e}"), r#""a" like "\\*""#);
1691 let e = Expr::like(
1693 Expr::val("a"),
1694 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('*')]),
1695 );
1696 assert_eq!(format!("{e}"), r#""a" like "\\\*""#);
1697 }
1698
1699 #[test]
1700 fn has_display() {
1701 let e = Expr::has_attr(Expr::val("a"), "\0".into());
1703 assert_eq!(format!("{e}"), r#""a" has "\0""#);
1704 let e = Expr::has_attr(Expr::val("a"), r"\".into());
1706 assert_eq!(format!("{e}"), r#""a" has "\\""#);
1707 }
1708
1709 #[test]
1710 fn slot_display() {
1711 let e = Expr::slot(SlotId::principal());
1712 assert_eq!(format!("{e}"), "?principal");
1713 let e = Expr::slot(SlotId::resource());
1714 assert_eq!(format!("{e}"), "?resource");
1715 let e = Expr::val(EntityUID::with_eid("eid"));
1716 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
1717 }
1718
1719 #[test]
1720 fn simple_slots() {
1721 let e = Expr::slot(SlotId::principal());
1722 let p = SlotId::principal();
1723 let r = SlotId::resource();
1724 let set: HashSet<SlotId> = HashSet::from_iter([p]);
1725 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
1726 let e = Expr::or(
1727 Expr::slot(SlotId::principal()),
1728 Expr::ite(
1729 Expr::val(true),
1730 Expr::slot(SlotId::resource()),
1731 Expr::val(false),
1732 ),
1733 );
1734 let set: HashSet<SlotId> = HashSet::from_iter([p, r]);
1735 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
1736 }
1737
1738 #[test]
1739 fn unknowns() {
1740 let e = Expr::ite(
1741 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
1742 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
1743 Expr::unknown(Unknown::new_untyped("c")),
1744 );
1745 let unknowns = e.unknowns().collect_vec();
1746 assert_eq!(unknowns.len(), 3);
1747 assert!(unknowns.contains(&&Unknown::new_untyped("a")));
1748 assert!(unknowns.contains(&&Unknown::new_untyped("b")));
1749 assert!(unknowns.contains(&&Unknown::new_untyped("c")));
1750 }
1751
1752 #[test]
1753 fn is_unknown() {
1754 let e = Expr::ite(
1755 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
1756 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
1757 Expr::unknown(Unknown::new_untyped("c")),
1758 );
1759 assert!(e.contains_unknown());
1760 let e = Expr::ite(
1761 Expr::not(Expr::val(true)),
1762 Expr::and(Expr::val(1), Expr::val(3)),
1763 Expr::val(1),
1764 );
1765 assert!(!e.contains_unknown());
1766 }
1767
1768 #[test]
1769 fn expr_with_data() {
1770 let e = ExprBuilder::with_data("data").val(1);
1771 assert_eq!(e.into_data(), "data");
1772 }
1773
1774 #[test]
1775 fn expr_shape_only_eq() {
1776 let temp = ExprBuilder::with_data(1).val(1);
1777 let exprs = &[
1778 (ExprBuilder::with_data(1).val(33), Expr::val(33)),
1779 (ExprBuilder::with_data(1).val(true), Expr::val(true)),
1780 (
1781 ExprBuilder::with_data(1).var(Var::Principal),
1782 Expr::var(Var::Principal),
1783 ),
1784 (
1785 ExprBuilder::with_data(1).slot(SlotId::principal()),
1786 Expr::slot(SlotId::principal()),
1787 ),
1788 (
1789 ExprBuilder::with_data(1).ite(temp.clone(), temp.clone(), temp.clone()),
1790 Expr::ite(Expr::val(1), Expr::val(1), Expr::val(1)),
1791 ),
1792 (
1793 ExprBuilder::with_data(1).not(temp.clone()),
1794 Expr::not(Expr::val(1)),
1795 ),
1796 (
1797 ExprBuilder::with_data(1).is_eq(temp.clone(), temp.clone()),
1798 Expr::is_eq(Expr::val(1), Expr::val(1)),
1799 ),
1800 (
1801 ExprBuilder::with_data(1).and(temp.clone(), temp.clone()),
1802 Expr::and(Expr::val(1), Expr::val(1)),
1803 ),
1804 (
1805 ExprBuilder::with_data(1).or(temp.clone(), temp.clone()),
1806 Expr::or(Expr::val(1), Expr::val(1)),
1807 ),
1808 (
1809 ExprBuilder::with_data(1).less(temp.clone(), temp.clone()),
1810 Expr::less(Expr::val(1), Expr::val(1)),
1811 ),
1812 (
1813 ExprBuilder::with_data(1).lesseq(temp.clone(), temp.clone()),
1814 Expr::lesseq(Expr::val(1), Expr::val(1)),
1815 ),
1816 (
1817 ExprBuilder::with_data(1).greater(temp.clone(), temp.clone()),
1818 Expr::greater(Expr::val(1), Expr::val(1)),
1819 ),
1820 (
1821 ExprBuilder::with_data(1).greatereq(temp.clone(), temp.clone()),
1822 Expr::greatereq(Expr::val(1), Expr::val(1)),
1823 ),
1824 (
1825 ExprBuilder::with_data(1).add(temp.clone(), temp.clone()),
1826 Expr::add(Expr::val(1), Expr::val(1)),
1827 ),
1828 (
1829 ExprBuilder::with_data(1).sub(temp.clone(), temp.clone()),
1830 Expr::sub(Expr::val(1), Expr::val(1)),
1831 ),
1832 (
1833 ExprBuilder::with_data(1).mul(temp.clone(), temp.clone()),
1834 Expr::mul(Expr::val(1), Expr::val(1)),
1835 ),
1836 (
1837 ExprBuilder::with_data(1).neg(temp.clone()),
1838 Expr::neg(Expr::val(1)),
1839 ),
1840 (
1841 ExprBuilder::with_data(1).is_in(temp.clone(), temp.clone()),
1842 Expr::is_in(Expr::val(1), Expr::val(1)),
1843 ),
1844 (
1845 ExprBuilder::with_data(1).contains(temp.clone(), temp.clone()),
1846 Expr::contains(Expr::val(1), Expr::val(1)),
1847 ),
1848 (
1849 ExprBuilder::with_data(1).contains_all(temp.clone(), temp.clone()),
1850 Expr::contains_all(Expr::val(1), Expr::val(1)),
1851 ),
1852 (
1853 ExprBuilder::with_data(1).contains_any(temp.clone(), temp.clone()),
1854 Expr::contains_any(Expr::val(1), Expr::val(1)),
1855 ),
1856 (
1857 ExprBuilder::with_data(1).is_empty(temp.clone()),
1858 Expr::is_empty(Expr::val(1)),
1859 ),
1860 (
1861 ExprBuilder::with_data(1).set([temp.clone()]),
1862 Expr::set([Expr::val(1)]),
1863 ),
1864 (
1865 ExprBuilder::with_data(1)
1866 .record([("foo".into(), temp.clone())])
1867 .unwrap(),
1868 Expr::record([("foo".into(), Expr::val(1))]).unwrap(),
1869 ),
1870 (
1871 ExprBuilder::with_data(1)
1872 .call_extension_fn("foo".parse().unwrap(), vec![temp.clone()]),
1873 Expr::call_extension_fn("foo".parse().unwrap(), vec![Expr::val(1)]),
1874 ),
1875 (
1876 ExprBuilder::with_data(1).get_attr(temp.clone(), "foo".into()),
1877 Expr::get_attr(Expr::val(1), "foo".into()),
1878 ),
1879 (
1880 ExprBuilder::with_data(1).has_attr(temp.clone(), "foo".into()),
1881 Expr::has_attr(Expr::val(1), "foo".into()),
1882 ),
1883 (
1884 ExprBuilder::with_data(1)
1885 .like(temp.clone(), Pattern::from(vec![PatternElem::Wildcard])),
1886 Expr::like(Expr::val(1), Pattern::from(vec![PatternElem::Wildcard])),
1887 ),
1888 (
1889 ExprBuilder::with_data(1).is_entity_type(temp, "T".parse().unwrap()),
1890 Expr::is_entity_type(Expr::val(1), "T".parse().unwrap()),
1891 ),
1892 ];
1893
1894 for (e0, e1) in exprs {
1895 assert!(e0.eq_shape(e0));
1896 assert!(e1.eq_shape(e1));
1897 assert!(e0.eq_shape(e1));
1898 assert!(e1.eq_shape(e0));
1899
1900 let mut hasher0 = DefaultHasher::new();
1901 e0.hash_shape(&mut hasher0);
1902 let hash0 = hasher0.finish();
1903
1904 let mut hasher1 = DefaultHasher::new();
1905 e1.hash_shape(&mut hasher1);
1906 let hash1 = hasher1.finish();
1907
1908 assert_eq!(hash0, hash1);
1909 }
1910 }
1911
1912 #[test]
1913 fn expr_shape_only_not_eq() {
1914 let expr1 = ExprBuilder::with_data(1).val(1);
1915 let expr2 = ExprBuilder::with_data(1).val(2);
1916 assert_ne!(
1917 ExprShapeOnly::new_from_borrowed(&expr1),
1918 ExprShapeOnly::new_from_borrowed(&expr2)
1919 );
1920 }
1921
1922 #[test]
1923 fn untyped_subst_present() {
1924 let u = Unknown {
1925 name: "foo".into(),
1926 type_annotation: None,
1927 };
1928 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
1929 match r {
1930 Ok(e) => assert_eq!(e, Expr::val(1)),
1931 Err(empty) => match empty {},
1932 }
1933 }
1934
1935 #[test]
1936 fn untyped_subst_present_correct_type() {
1937 let u = Unknown {
1938 name: "foo".into(),
1939 type_annotation: Some(Type::Long),
1940 };
1941 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
1942 match r {
1943 Ok(e) => assert_eq!(e, Expr::val(1)),
1944 Err(empty) => match empty {},
1945 }
1946 }
1947
1948 #[test]
1949 fn untyped_subst_present_wrong_type() {
1950 let u = Unknown {
1951 name: "foo".into(),
1952 type_annotation: Some(Type::Bool),
1953 };
1954 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
1955 match r {
1956 Ok(e) => assert_eq!(e, Expr::val(1)),
1957 Err(empty) => match empty {},
1958 }
1959 }
1960
1961 #[test]
1962 fn untyped_subst_not_present() {
1963 let u = Unknown {
1964 name: "foo".into(),
1965 type_annotation: Some(Type::Bool),
1966 };
1967 let r = UntypedSubstitution::substitute(&u, None);
1968 match r {
1969 Ok(n) => assert_eq!(n, Expr::unknown(u)),
1970 Err(empty) => match empty {},
1971 }
1972 }
1973
1974 #[test]
1975 fn typed_subst_present() {
1976 let u = Unknown {
1977 name: "foo".into(),
1978 type_annotation: None,
1979 };
1980 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
1981 assert_eq!(e, Expr::val(1));
1982 }
1983
1984 #[test]
1985 fn typed_subst_present_correct_type() {
1986 let u = Unknown {
1987 name: "foo".into(),
1988 type_annotation: Some(Type::Long),
1989 };
1990 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
1991 assert_eq!(e, Expr::val(1));
1992 }
1993
1994 #[test]
1995 fn typed_subst_present_wrong_type() {
1996 let u = Unknown {
1997 name: "foo".into(),
1998 type_annotation: Some(Type::Bool),
1999 };
2000 let r = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap_err();
2001 assert_matches!(
2002 r,
2003 SubstitutionError::TypeError {
2004 expected: Type::Bool,
2005 actual: Type::Long,
2006 }
2007 );
2008 }
2009
2010 #[test]
2011 fn typed_subst_not_present() {
2012 let u = Unknown {
2013 name: "foo".into(),
2014 type_annotation: None,
2015 };
2016 let r = TypedSubstitution::substitute(&u, None).unwrap();
2017 assert_eq!(r, Expr::unknown(u));
2018 }
2019}