1#[cfg(feature = "tolerant-ast")]
18use {
19 super::expr_allows_errors::AstExprErrorKind,
20 crate::parser::err::{ToASTError, ToASTErrorKind},
21};
22
23use crate::{
24 ast::*,
25 expr_builder::{self, ExprBuilder as _},
26 extensions::Extensions,
27 parser::{err::ParseErrors, Loc},
28};
29use educe::Educe;
30use miette::Diagnostic;
31use nonempty::NonEmpty;
32use serde::{Deserialize, Serialize};
33use smol_str::SmolStr;
34use std::{
35 borrow::Cow,
36 collections::{btree_map, BTreeMap, HashMap},
37 hash::{Hash, Hasher},
38 mem,
39 sync::Arc,
40};
41use thiserror::Error;
42
43#[cfg(feature = "wasm")]
44extern crate tsify;
45
46#[derive(Educe, Debug, Clone)]
53#[educe(PartialEq, Eq, Hash)]
54pub struct Expr<T = ()> {
55 expr_kind: ExprKind<T>,
56 #[educe(PartialEq(ignore))]
57 #[educe(Hash(ignore))]
58 source_loc: Option<Loc>,
59 data: T,
60}
61
62#[derive(Hash, Debug, Clone, PartialEq, Eq)]
65pub enum ExprKind<T = ()> {
66 Lit(Literal),
68 Var(Var),
70 Slot(SlotId),
72 Unknown(Unknown),
74 If {
76 test_expr: Arc<Expr<T>>,
78 then_expr: Arc<Expr<T>>,
80 else_expr: Arc<Expr<T>>,
82 },
83 And {
85 left: Arc<Expr<T>>,
87 right: Arc<Expr<T>>,
89 },
90 Or {
92 left: Arc<Expr<T>>,
94 right: Arc<Expr<T>>,
96 },
97 UnaryApp {
99 op: UnaryOp,
101 arg: Arc<Expr<T>>,
103 },
104 BinaryApp {
106 op: BinaryOp,
108 arg1: Arc<Expr<T>>,
110 arg2: Arc<Expr<T>>,
112 },
113 ExtensionFunctionApp {
119 fn_name: Name,
121 args: Arc<Vec<Expr<T>>>,
123 },
124 GetAttr {
126 expr: Arc<Expr<T>>,
129 attr: SmolStr,
131 },
132 HasAttr {
134 expr: Arc<Expr<T>>,
136 attr: SmolStr,
138 },
139 ExtHasAttr {
142 expr: Arc<Expr<T>>,
144 attrs: NonEmpty<SmolStr>,
146 },
147 Like {
149 expr: Arc<Expr<T>>,
151 pattern: Pattern,
155 },
156 Is {
159 expr: Arc<Expr<T>>,
161 entity_type: EntityType,
163 },
164 Set(Arc<Vec<Expr<T>>>),
171 Record(Arc<BTreeMap<SmolStr, Expr<T>>>),
173 #[cfg(feature = "tolerant-ast")]
174 Error {
176 error_kind: AstExprErrorKind,
178 },
179}
180
181impl<T> ExprKind<T> {
182 fn variant_order(&self) -> u8 {
184 match self {
185 ExprKind::Lit(_) => 0,
186 ExprKind::Var(_) => 1,
187 ExprKind::Slot(_) => 2,
188 ExprKind::Unknown(_) => 3,
189 ExprKind::If { .. } => 4,
190 ExprKind::And { .. } => 5,
191 ExprKind::Or { .. } => 6,
192 ExprKind::UnaryApp { .. } => 7,
193 ExprKind::BinaryApp { .. } => 8,
194 ExprKind::ExtensionFunctionApp { .. } => 9,
195 ExprKind::GetAttr { .. } => 10,
196 ExprKind::HasAttr { .. } => 11,
197 ExprKind::ExtHasAttr { .. } => 12,
198 ExprKind::Like { .. } => 13,
199 ExprKind::Set(_) => 14,
200 ExprKind::Record(_) => 15,
201 ExprKind::Is { .. } => 16,
202 #[cfg(feature = "tolerant-ast")]
203 ExprKind::Error { .. } => 17,
204 }
205 }
206}
207
208impl From<Value> for Expr {
209 fn from(v: Value) -> Self {
210 Expr::from(v.value).with_maybe_source_loc(v.loc)
211 }
212}
213
214impl From<ValueKind> for Expr {
215 fn from(v: ValueKind) -> Self {
216 match v {
217 ValueKind::Lit(lit) => Expr::val(lit),
218 ValueKind::Set(set) => Expr::set(set.iter().map(|v| Expr::from(v.clone()))),
219 #[expect(
220 clippy::expect_used,
221 reason = "cannot have duplicate key because the input was already a BTreeMap"
222 )]
223 ValueKind::Record(record) => Expr::record(
224 Arc::unwrap_or_clone(record)
225 .into_iter()
226 .map(|(k, v)| (k, Expr::from(v))),
227 )
228 .expect("cannot have duplicate key because the input was already a BTreeMap"),
229 ValueKind::ExtensionValue(ev) => RestrictedExpr::from(ev.as_ref().clone()).into(),
230 }
231 }
232}
233
234impl From<PartialValue> for Expr {
235 fn from(pv: PartialValue) -> Self {
236 match pv {
237 PartialValue::Value(v) => Expr::from(v),
238 PartialValue::Residual(expr) => expr,
239 }
240 }
241}
242
243impl<T> Expr<T> {
244 pub(crate) fn new(expr_kind: ExprKind<T>, source_loc: Option<Loc>, data: T) -> Self {
245 Self {
246 expr_kind,
247 source_loc,
248 data,
249 }
250 }
251
252 pub fn expr_kind(&self) -> &ExprKind<T> {
256 &self.expr_kind
257 }
258
259 pub fn into_expr_kind(self) -> ExprKind<T> {
261 self.expr_kind
262 }
263
264 pub fn data(&self) -> &T {
266 &self.data
267 }
268
269 pub fn into_data(self) -> T {
272 self.data
273 }
274
275 pub fn into_parts(self) -> (ExprKind<T>, Option<Loc>, T) {
278 (self.expr_kind, self.source_loc, self.data)
279 }
280
281 pub fn source_loc(&self) -> Option<&Loc> {
283 self.source_loc.as_ref()
284 }
285
286 pub fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
288 Self { source_loc, ..self }
289 }
290
291 pub fn set_data(&mut self, data: T) {
294 self.data = data;
295 }
296
297 pub fn is_ref(&self) -> bool {
302 match &self.expr_kind {
303 ExprKind::Lit(lit) => lit.is_ref(),
304 _ => false,
305 }
306 }
307
308 pub fn is_slot(&self) -> bool {
310 matches!(&self.expr_kind, ExprKind::Slot(_))
311 }
312
313 pub fn is_ref_set(&self) -> bool {
318 match &self.expr_kind {
319 ExprKind::Set(exprs) => exprs.iter().all(|e| e.is_ref()),
320 _ => false,
321 }
322 }
323
324 pub fn subexpressions(&self) -> impl Iterator<Item = &Self> {
326 expr_iterator::ExprIterator::new(self)
327 }
328
329 pub fn slots(&self) -> impl Iterator<Item = Slot> + '_ {
331 self.subexpressions()
332 .filter_map(|exp| match &exp.expr_kind {
333 ExprKind::Slot(slotid) => Some(Slot {
334 id: *slotid,
335 loc: exp.source_loc().cloned(),
336 }),
337 _ => None,
338 })
339 }
340
341 pub fn is_projectable(&self) -> bool {
345 self.subexpressions().all(|e| {
346 matches!(
347 e.expr_kind(),
348 ExprKind::Lit(_)
349 | ExprKind::Unknown(_)
350 | ExprKind::Set(_)
351 | ExprKind::Var(_)
352 | ExprKind::Record(_)
353 )
354 })
355 }
356
357 pub fn try_type_of(&self, extensions: &Extensions<'_>) -> Option<Type> {
369 match &self.expr_kind {
370 ExprKind::Lit(l) => Some(l.type_of()),
371 ExprKind::Var(_) => None,
372 ExprKind::Slot(_) => None,
373 ExprKind::Unknown(u) => u.type_annotation.clone(),
374 ExprKind::If {
375 then_expr,
376 else_expr,
377 ..
378 } => {
379 let type_of_then = then_expr.try_type_of(extensions);
380 let type_of_else = else_expr.try_type_of(extensions);
381 if type_of_then == type_of_else {
382 type_of_then
383 } else {
384 None
385 }
386 }
387 ExprKind::And { .. } => Some(Type::Bool),
388 ExprKind::Or { .. } => Some(Type::Bool),
389 ExprKind::UnaryApp {
390 op: UnaryOp::Neg, ..
391 } => Some(Type::Long),
392 ExprKind::UnaryApp {
393 op: UnaryOp::Not, ..
394 } => Some(Type::Bool),
395 ExprKind::UnaryApp {
396 op: UnaryOp::IsEmpty,
397 ..
398 } => Some(Type::Bool),
399 ExprKind::BinaryApp {
400 op: BinaryOp::Add | BinaryOp::Mul | BinaryOp::Sub,
401 ..
402 } => Some(Type::Long),
403 ExprKind::BinaryApp {
404 op:
405 BinaryOp::Contains
406 | BinaryOp::ContainsAll
407 | BinaryOp::ContainsAny
408 | BinaryOp::Eq
409 | BinaryOp::In
410 | BinaryOp::Less
411 | BinaryOp::LessEq,
412 ..
413 } => Some(Type::Bool),
414 ExprKind::BinaryApp {
415 op: BinaryOp::HasTag,
416 ..
417 } => Some(Type::Bool),
418 ExprKind::ExtensionFunctionApp { fn_name, .. } => extensions
419 .func(fn_name)
420 .ok()?
421 .return_type()
422 .map(|rty| rty.clone().into()),
423 ExprKind::GetAttr { .. } => None,
428 ExprKind::BinaryApp {
430 op: BinaryOp::GetTag,
431 ..
432 } => None,
433 ExprKind::HasAttr { .. } => Some(Type::Bool),
434 ExprKind::ExtHasAttr { .. } => Some(Type::Bool),
435 ExprKind::Like { .. } => Some(Type::Bool),
436 ExprKind::Is { .. } => Some(Type::Bool),
437 ExprKind::Set(_) => Some(Type::Set),
438 ExprKind::Record(_) => Some(Type::Record),
439 #[cfg(feature = "tolerant-ast")]
440 ExprKind::Error { .. } => None,
441 }
442 }
443
444 pub fn try_into_expr<B: expr_builder::ExprBuilder>(self) -> Result<B::Expr, B::BuildError>
451 where
452 T: Clone,
453 {
454 let builder = B::new().with_maybe_source_loc(self.source_loc());
455 match self.into_expr_kind() {
456 ExprKind::Lit(lit) => Ok(builder.val(lit)),
457 ExprKind::Var(var) => Ok(builder.var(var)),
458 ExprKind::Slot(slot) => Ok(builder.slot(slot)),
459 ExprKind::Unknown(u) => Ok(builder.unknown(u)),
460 ExprKind::If {
461 test_expr,
462 then_expr,
463 else_expr,
464 } => Ok(builder.ite(
465 Arc::unwrap_or_clone(test_expr).try_into_expr::<B>()?,
466 Arc::unwrap_or_clone(then_expr).try_into_expr::<B>()?,
467 Arc::unwrap_or_clone(else_expr).try_into_expr::<B>()?,
468 )),
469 ExprKind::And { left, right } => Ok(builder.and(
470 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
471 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
472 )),
473 ExprKind::Or { left, right } => Ok(builder.or(
474 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
475 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
476 )),
477 ExprKind::UnaryApp { op, arg } => {
478 Ok(builder.unary_app(op, Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
479 }
480 ExprKind::BinaryApp { op, arg1, arg2 } => Ok(builder.binary_app(
481 op,
482 Arc::unwrap_or_clone(arg1).try_into_expr::<B>()?,
483 Arc::unwrap_or_clone(arg2).try_into_expr::<B>()?,
484 )),
485 ExprKind::ExtensionFunctionApp { fn_name, args } => {
486 let args: Vec<_> = Arc::unwrap_or_clone(args)
487 .into_iter()
488 .map(|e| e.try_into_expr::<B>())
489 .collect::<Result<_, _>>()?;
490 builder.call_extension_fn(fn_name, args)
491 }
492 ExprKind::GetAttr { expr, attr } => {
493 Ok(builder.get_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attr))
494 }
495 ExprKind::HasAttr { expr, attr } => {
496 Ok(builder.has_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attr))
497 }
498 ExprKind::ExtHasAttr { expr, attrs } => {
499 Ok(builder
500 .extended_has_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attrs))
501 }
502 ExprKind::Like { expr, pattern } => {
503 Ok(builder.like(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, pattern))
504 }
505 ExprKind::Is { expr, entity_type } => Ok(builder.is_entity_type(
506 Arc::unwrap_or_clone(expr).try_into_expr::<B>()?,
507 entity_type,
508 )),
509 ExprKind::Set(set) => Ok(builder.set(
510 Arc::unwrap_or_clone(set)
511 .into_iter()
512 .map(|e| e.try_into_expr::<B>())
513 .collect::<Result<Vec<_>, _>>()?,
514 )),
515 #[expect(
516 clippy::unwrap_used,
517 reason = "`map` is a map, so it will not have duplicate keys, so the `.record()` constructor cannot error"
518 )]
519 ExprKind::Record(map) => Ok(builder
520 .record(
521 Arc::unwrap_or_clone(map)
522 .into_iter()
523 .map(|(k, v)| Ok((k, v.try_into_expr::<B>()?)))
524 .collect::<Result<Vec<_>, _>>()?,
525 )
526 .unwrap()),
527 #[cfg(feature = "tolerant-ast")]
528 #[expect(
529 clippy::unwrap_used,
530 reason = "error type is Infallible so can never happen"
531 )]
532 ExprKind::Error { .. } => Ok(builder
533 .error(ParseErrors::singleton(ToASTError::new(
534 ToASTErrorKind::ASTErrorNode,
535 Some(Loc::new(0..1, "AST_ERROR_NODE".into())),
536 )))
537 .unwrap()), }
539 }
540
541 pub fn into_expr<B: expr_builder::ExprBuilder>(self) -> B::Expr
543 where
544 T: Clone,
545 B::BuildError: IsInfallible,
546 {
547 self.try_into_expr::<B>().unwrap_infallible()
548 }
549}
550
551#[expect(
552 clippy::should_implement_trait,
553 reason = "the names of arithmetic constructors alias with those of certain trait methods such as `add` of `std::ops::Add`"
554)]
555impl Expr {
556 pub fn val(v: impl Into<Literal>) -> Self {
560 ExprBuilder::new().val(v)
561 }
562
563 pub fn unknown(u: Unknown) -> Self {
565 ExprBuilder::new().unknown(u)
566 }
567
568 pub fn var(v: Var) -> Self {
570 ExprBuilder::new().var(v)
571 }
572
573 pub fn slot(s: SlotId) -> Self {
575 ExprBuilder::new().slot(s)
576 }
577
578 pub fn ite(test_expr: Expr, then_expr: Expr, else_expr: Expr) -> Self {
582 ExprBuilder::new().ite(test_expr, then_expr, else_expr)
583 }
584
585 pub fn ite_arc(test_expr: Arc<Expr>, then_expr: Arc<Expr>, else_expr: Arc<Expr>) -> Self {
589 ExprBuilder::new().ite_arc(test_expr, then_expr, else_expr)
590 }
591
592 pub fn not(e: Expr) -> Self {
594 ExprBuilder::new().not(e)
595 }
596
597 pub fn is_eq(e1: Expr, e2: Expr) -> Self {
599 ExprBuilder::new().is_eq(e1, e2)
600 }
601
602 pub fn noteq(e1: Expr, e2: Expr) -> Self {
604 ExprBuilder::new().noteq(e1, e2)
605 }
606
607 pub fn and(e1: Expr, e2: Expr) -> Self {
609 ExprBuilder::new().and(e1, e2)
610 }
611
612 pub fn or(e1: Expr, e2: Expr) -> Self {
614 ExprBuilder::new().or(e1, e2)
615 }
616
617 pub fn less(e1: Expr, e2: Expr) -> Self {
619 ExprBuilder::new().less(e1, e2)
620 }
621
622 pub fn lesseq(e1: Expr, e2: Expr) -> Self {
624 ExprBuilder::new().lesseq(e1, e2)
625 }
626
627 pub fn greater(e1: Expr, e2: Expr) -> Self {
629 ExprBuilder::new().greater(e1, e2)
630 }
631
632 pub fn greatereq(e1: Expr, e2: Expr) -> Self {
634 ExprBuilder::new().greatereq(e1, e2)
635 }
636
637 pub fn add(e1: Expr, e2: Expr) -> Self {
639 ExprBuilder::new().add(e1, e2)
640 }
641
642 pub fn sub(e1: Expr, e2: Expr) -> Self {
644 ExprBuilder::new().sub(e1, e2)
645 }
646
647 pub fn mul(e1: Expr, e2: Expr) -> Self {
649 ExprBuilder::new().mul(e1, e2)
650 }
651
652 pub fn neg(e: Expr) -> Self {
654 ExprBuilder::new().neg(e)
655 }
656
657 pub fn is_in(e1: Expr, e2: Expr) -> Self {
661 ExprBuilder::new().is_in(e1, e2)
662 }
663
664 pub fn contains(e1: Expr, e2: Expr) -> Self {
667 ExprBuilder::new().contains(e1, e2)
668 }
669
670 pub fn contains_all(e1: Expr, e2: Expr) -> Self {
672 ExprBuilder::new().contains_all(e1, e2)
673 }
674
675 pub fn contains_any(e1: Expr, e2: Expr) -> Self {
677 ExprBuilder::new().contains_any(e1, e2)
678 }
679
680 pub fn is_empty(e: Expr) -> Self {
682 ExprBuilder::new().is_empty(e)
683 }
684
685 pub fn get_tag(expr: Expr, tag: Expr) -> Self {
688 ExprBuilder::new().get_tag(expr, tag)
689 }
690
691 pub fn has_tag(expr: Expr, tag: Expr) -> Self {
694 ExprBuilder::new().has_tag(expr, tag)
695 }
696
697 pub fn set(exprs: impl IntoIterator<Item = Expr>) -> Self {
699 ExprBuilder::new().set(exprs)
700 }
701
702 pub fn record(
704 pairs: impl IntoIterator<Item = (SmolStr, Expr)>,
705 ) -> Result<Self, ExpressionConstructionError> {
706 ExprBuilder::new().record(pairs)
707 }
708
709 pub fn record_arc(map: Arc<BTreeMap<SmolStr, Expr>>) -> Self {
717 ExprBuilder::new().record_arc(map)
718 }
719
720 pub fn call_extension_fn(fn_name: Name, args: Vec<Expr>) -> Self {
723 ExprBuilder::new()
724 .call_extension_fn(fn_name, args)
725 .unwrap_infallible()
726 }
727
728 pub fn unary_app(op: impl Into<UnaryOp>, arg: Expr) -> Self {
731 ExprBuilder::new().unary_app(op, arg)
732 }
733
734 pub fn binary_app(op: impl Into<BinaryOp>, arg1: Expr, arg2: Expr) -> Self {
737 ExprBuilder::new().binary_app(op, arg1, arg2)
738 }
739
740 pub fn get_attr(expr: Expr, attr: SmolStr) -> Self {
744 ExprBuilder::new().get_attr(expr, attr)
745 }
746
747 pub fn has_attr(expr: Expr, attr: SmolStr) -> Self {
752 ExprBuilder::new().has_attr(expr, attr)
753 }
754
755 pub fn extended_has_attr(expr: Expr, attrs: NonEmpty<SmolStr>) -> Self {
760 ExprBuilder::new().extended_has_attr(expr, attrs)
761 }
762
763 pub fn like(expr: Expr, pattern: Pattern) -> Self {
767 ExprBuilder::new().like(expr, pattern)
768 }
769
770 pub fn is_entity_type(expr: Expr, entity_type: EntityType) -> Self {
772 ExprBuilder::new().is_entity_type(expr, entity_type)
773 }
774
775 pub fn contains_unknown(&self) -> bool {
777 self.subexpressions()
778 .any(|e| matches!(e.expr_kind(), ExprKind::Unknown(_)))
779 }
780
781 pub fn unknowns(&self) -> impl Iterator<Item = &Unknown> {
783 self.subexpressions()
784 .filter_map(|subexpr| match subexpr.expr_kind() {
785 ExprKind::Unknown(u) => Some(u),
786 _ => None,
787 })
788 }
789
790 pub fn substitute(&self, definitions: &HashMap<SmolStr, Value>) -> Expr {
799 match self.substitute_general::<UntypedSubstitution>(definitions) {
800 Ok(e) => e,
801 Err(empty) => match empty {},
802 }
803 }
804
805 pub fn substitute_typed(
814 &self,
815 definitions: &HashMap<SmolStr, Value>,
816 ) -> Result<Expr, SubstitutionError> {
817 self.substitute_general::<TypedSubstitution>(definitions)
818 }
819
820 fn substitute_general<T: SubstitutionFunction>(
824 &self,
825 definitions: &HashMap<SmolStr, Value>,
826 ) -> Result<Expr, T::Err> {
827 match self.expr_kind() {
828 ExprKind::Lit(_) => Ok(self.clone()),
829 ExprKind::Unknown(u @ Unknown { name, .. }) => T::substitute(u, definitions.get(name)),
830 ExprKind::Var(_) => Ok(self.clone()),
831 ExprKind::Slot(_) => Ok(self.clone()),
832 ExprKind::If {
833 test_expr,
834 then_expr,
835 else_expr,
836 } => Ok(Expr::ite(
837 test_expr.substitute_general::<T>(definitions)?,
838 then_expr.substitute_general::<T>(definitions)?,
839 else_expr.substitute_general::<T>(definitions)?,
840 )),
841 ExprKind::And { left, right } => Ok(Expr::and(
842 left.substitute_general::<T>(definitions)?,
843 right.substitute_general::<T>(definitions)?,
844 )),
845 ExprKind::Or { left, right } => Ok(Expr::or(
846 left.substitute_general::<T>(definitions)?,
847 right.substitute_general::<T>(definitions)?,
848 )),
849 ExprKind::UnaryApp { op, arg } => Ok(Expr::unary_app(
850 *op,
851 arg.substitute_general::<T>(definitions)?,
852 )),
853 ExprKind::BinaryApp { op, arg1, arg2 } => Ok(Expr::binary_app(
854 *op,
855 arg1.substitute_general::<T>(definitions)?,
856 arg2.substitute_general::<T>(definitions)?,
857 )),
858 ExprKind::ExtensionFunctionApp { fn_name, args } => {
859 let args = args
860 .iter()
861 .map(|e| e.substitute_general::<T>(definitions))
862 .collect::<Result<Vec<Expr>, _>>()?;
863
864 Ok(Expr::call_extension_fn(fn_name.clone(), args))
865 }
866 ExprKind::GetAttr { expr, attr } => Ok(Expr::get_attr(
867 expr.substitute_general::<T>(definitions)?,
868 attr.clone(),
869 )),
870 ExprKind::HasAttr { expr, attr } => Ok(Expr::has_attr(
871 expr.substitute_general::<T>(definitions)?,
872 attr.clone(),
873 )),
874 ExprKind::ExtHasAttr { expr, attrs } => Ok(Expr::extended_has_attr(
875 expr.substitute_general::<T>(definitions)?,
876 attrs.clone(),
877 )),
878 ExprKind::Like { expr, pattern } => Ok(Expr::like(
879 expr.substitute_general::<T>(definitions)?,
880 pattern.clone(),
881 )),
882 ExprKind::Set(members) => {
883 let members = members
884 .iter()
885 .map(|e| e.substitute_general::<T>(definitions))
886 .collect::<Result<Vec<_>, _>>()?;
887 Ok(Expr::set(members))
888 }
889 ExprKind::Record(map) => {
890 let map = map
891 .iter()
892 .map(|(name, e)| Ok((name.clone(), e.substitute_general::<T>(definitions)?)))
893 .collect::<Result<BTreeMap<_, _>, _>>()?;
894 #[expect(
895 clippy::expect_used,
896 reason = "cannot have a duplicate key because the input was already a BTreeMap"
897 )]
898 Ok(Expr::record(map)
899 .expect("cannot have a duplicate key because the input was already a BTreeMap"))
900 }
901 ExprKind::Is { expr, entity_type } => Ok(Expr::is_entity_type(
902 expr.substitute_general::<T>(definitions)?,
903 entity_type.clone(),
904 )),
905 #[cfg(feature = "tolerant-ast")]
906 ExprKind::Error { .. } => Ok(self.clone()),
907 }
908 }
909
910 pub fn try_validate(self) -> Result<Self, ExprValidationError> {
928 for sub in self.subexpressions() {
929 match sub.expr_kind() {
930 ExprKind::ExtensionFunctionApp { fn_name, args } => {
931 let ext_fn = Extensions::all_available().func(fn_name).map_err(|_| {
933 ExprValidationError(format!("unknown extension function `{fn_name}`"))
934 })?;
935 if ext_fn.style() == CallStyle::MethodStyle && args.is_empty() {
937 return Err(ExprValidationError(format!(
938 "method-style extension function `{fn_name}` requires a receiver argument"
939 )));
940 }
941 }
943 ExprKind::ExtHasAttr { attrs, .. } => {
944 for attr in attrs {
945 if !is_normalized_ident(attr) {
946 return Err(ExprValidationError(format!(
947 "extended has attribute `{attr}` is not a valid identifier"
948 )));
949 }
950 }
951 }
952 _ => {}
953 }
954 }
955 Ok(self)
956 }
957}
958
959trait SubstitutionFunction {
961 type Err;
963 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err>;
969}
970
971struct TypedSubstitution {}
972
973impl SubstitutionFunction for TypedSubstitution {
974 type Err = SubstitutionError;
975
976 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
977 match (substitute, &value.type_annotation) {
978 (None, _) => Ok(Expr::unknown(value.clone())),
979 (Some(v), None) => Ok(v.clone().into()),
980 (Some(v), Some(t)) => {
981 if v.type_of() == *t {
982 Ok(v.clone().into())
983 } else {
984 Err(SubstitutionError::TypeError {
985 expected: t.clone(),
986 actual: v.type_of(),
987 })
988 }
989 }
990 }
991 }
992}
993
994struct UntypedSubstitution {}
995
996impl SubstitutionFunction for UntypedSubstitution {
997 type Err = std::convert::Infallible;
998
999 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
1000 Ok(substitute
1001 .map(|v| v.clone().into())
1002 .unwrap_or_else(|| Expr::unknown(value.clone())))
1003 }
1004}
1005
1006impl<T: Clone> std::fmt::Display for Expr<T> {
1007 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1008 write!(f, "{}", self.clone().into_expr::<crate::est::Builder>())
1012 }
1013}
1014
1015impl<T: Clone> BoundedDisplay for Expr<T> {
1016 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1017 BoundedDisplay::fmt(&self.clone().into_expr::<crate::est::Builder>(), f, n)
1020 }
1021}
1022
1023impl std::str::FromStr for Expr {
1024 type Err = ParseErrors;
1025
1026 fn from_str(s: &str) -> Result<Expr, Self::Err> {
1027 crate::parser::parse_expr(s)
1028 }
1029}
1030
1031#[derive(Debug, Clone, Diagnostic, Error)]
1033pub enum SubstitutionError {
1034 #[error("expected a value of type {expected}, got a value of type {actual}")]
1036 TypeError {
1037 expected: Type,
1039 actual: Type,
1041 },
1042}
1043
1044#[derive(Hash, Debug, Clone, PartialEq, Eq)]
1046pub struct Unknown {
1047 pub name: SmolStr,
1049 pub type_annotation: Option<Type>,
1053}
1054
1055impl Unknown {
1056 pub fn new_untyped(name: impl Into<SmolStr>) -> Self {
1058 Self {
1059 name: name.into(),
1060 type_annotation: None,
1061 }
1062 }
1063
1064 pub fn new_with_type(name: impl Into<SmolStr>, ty: Type) -> Self {
1067 Self {
1068 name: name.into(),
1069 type_annotation: Some(ty),
1070 }
1071 }
1072}
1073
1074impl std::fmt::Display for Unknown {
1075 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1076 write!(
1079 f,
1080 "{}",
1081 Expr::unknown(self.clone()).into_expr::<crate::est::Builder>()
1082 )
1083 }
1084}
1085
1086#[derive(Clone, Debug)]
1089pub struct ExprBuilder<T> {
1090 source_loc: Option<Loc>,
1091 data: T,
1092}
1093
1094impl<T: Default + Clone> expr_builder::ExprBuilderInfallibleBuild for ExprBuilder<T> {}
1095
1096impl<T: Default + Clone> expr_builder::ExprBuilder for ExprBuilder<T> {
1097 type Expr = Expr<T>;
1098
1099 type Data = T;
1100
1101 type BuildError = Infallible;
1102
1103 #[cfg(feature = "tolerant-ast")]
1104 type ErrorType = ParseErrors;
1105
1106 fn loc(&self) -> Option<&Loc> {
1107 self.source_loc.as_ref()
1108 }
1109
1110 fn data(&self) -> &Self::Data {
1111 &self.data
1112 }
1113
1114 fn with_data(data: T) -> Self {
1115 Self {
1116 source_loc: None,
1117 data,
1118 }
1119 }
1120
1121 fn with_maybe_source_loc(mut self, maybe_source_loc: Option<&Loc>) -> Self {
1122 self.source_loc = maybe_source_loc.cloned();
1123 self
1124 }
1125
1126 fn val(self, v: impl Into<Literal>) -> Expr<T> {
1130 self.with_expr_kind(ExprKind::Lit(v.into()))
1131 }
1132
1133 fn unknown(self, u: Unknown) -> Expr<T> {
1135 self.with_expr_kind(ExprKind::Unknown(u))
1136 }
1137
1138 fn var(self, v: Var) -> Expr<T> {
1140 self.with_expr_kind(ExprKind::Var(v))
1141 }
1142
1143 fn slot(self, s: SlotId) -> Expr<T> {
1145 self.with_expr_kind(ExprKind::Slot(s))
1146 }
1147
1148 fn ite_arc(
1152 self,
1153 test_expr: Arc<Expr<T>>,
1154 then_expr: Arc<Expr<T>>,
1155 else_expr: Arc<Expr<T>>,
1156 ) -> Expr<T> {
1157 self.with_expr_kind(ExprKind::If {
1158 test_expr,
1159 then_expr,
1160 else_expr,
1161 })
1162 }
1163
1164 fn not(self, e: Expr<T>) -> Expr<T> {
1166 self.with_expr_kind(ExprKind::UnaryApp {
1167 op: UnaryOp::Not,
1168 arg: Arc::new(e),
1169 })
1170 }
1171
1172 fn is_eq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1174 self.with_expr_kind(ExprKind::BinaryApp {
1175 op: BinaryOp::Eq,
1176 arg1: Arc::new(e1),
1177 arg2: Arc::new(e2),
1178 })
1179 }
1180
1181 fn and(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1183 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1184 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1185 ExprKind::Lit(Literal::Bool(*b1 && *b2))
1186 }
1187 _ => ExprKind::And {
1188 left: Arc::new(e1),
1189 right: Arc::new(e2),
1190 },
1191 })
1192 }
1193
1194 fn or(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1196 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1197 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1198 ExprKind::Lit(Literal::Bool(*b1 || *b2))
1199 }
1200
1201 _ => ExprKind::Or {
1202 left: Arc::new(e1),
1203 right: Arc::new(e2),
1204 },
1205 })
1206 }
1207
1208 fn less(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1210 self.with_expr_kind(ExprKind::BinaryApp {
1211 op: BinaryOp::Less,
1212 arg1: Arc::new(e1),
1213 arg2: Arc::new(e2),
1214 })
1215 }
1216
1217 fn lesseq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1219 self.with_expr_kind(ExprKind::BinaryApp {
1220 op: BinaryOp::LessEq,
1221 arg1: Arc::new(e1),
1222 arg2: Arc::new(e2),
1223 })
1224 }
1225
1226 fn add(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1228 self.with_expr_kind(ExprKind::BinaryApp {
1229 op: BinaryOp::Add,
1230 arg1: Arc::new(e1),
1231 arg2: Arc::new(e2),
1232 })
1233 }
1234
1235 fn sub(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1237 self.with_expr_kind(ExprKind::BinaryApp {
1238 op: BinaryOp::Sub,
1239 arg1: Arc::new(e1),
1240 arg2: Arc::new(e2),
1241 })
1242 }
1243
1244 fn mul(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1246 self.with_expr_kind(ExprKind::BinaryApp {
1247 op: BinaryOp::Mul,
1248 arg1: Arc::new(e1),
1249 arg2: Arc::new(e2),
1250 })
1251 }
1252
1253 fn neg(self, e: Expr<T>) -> Expr<T> {
1255 self.with_expr_kind(ExprKind::UnaryApp {
1256 op: UnaryOp::Neg,
1257 arg: Arc::new(e),
1258 })
1259 }
1260
1261 fn is_in_arc(self, arg1: Arc<Expr<T>>, arg2: Arc<Expr<T>>) -> Expr<T> {
1265 self.with_expr_kind(ExprKind::BinaryApp {
1266 op: BinaryOp::In,
1267 arg1,
1268 arg2,
1269 })
1270 }
1271
1272 fn contains(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1275 self.with_expr_kind(ExprKind::BinaryApp {
1276 op: BinaryOp::Contains,
1277 arg1: Arc::new(e1),
1278 arg2: Arc::new(e2),
1279 })
1280 }
1281
1282 fn contains_all(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1284 self.with_expr_kind(ExprKind::BinaryApp {
1285 op: BinaryOp::ContainsAll,
1286 arg1: Arc::new(e1),
1287 arg2: Arc::new(e2),
1288 })
1289 }
1290
1291 fn contains_any(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1293 self.with_expr_kind(ExprKind::BinaryApp {
1294 op: BinaryOp::ContainsAny,
1295 arg1: Arc::new(e1),
1296 arg2: Arc::new(e2),
1297 })
1298 }
1299
1300 fn is_empty(self, expr: Expr<T>) -> Expr<T> {
1302 self.with_expr_kind(ExprKind::UnaryApp {
1303 op: UnaryOp::IsEmpty,
1304 arg: Arc::new(expr),
1305 })
1306 }
1307
1308 fn get_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1311 self.with_expr_kind(ExprKind::BinaryApp {
1312 op: BinaryOp::GetTag,
1313 arg1: Arc::new(expr),
1314 arg2: Arc::new(tag),
1315 })
1316 }
1317
1318 fn has_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1321 self.with_expr_kind(ExprKind::BinaryApp {
1322 op: BinaryOp::HasTag,
1323 arg1: Arc::new(expr),
1324 arg2: Arc::new(tag),
1325 })
1326 }
1327
1328 fn set(self, exprs: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1330 self.with_expr_kind(ExprKind::Set(Arc::new(exprs.into_iter().collect())))
1331 }
1332
1333 fn record(
1335 self,
1336 pairs: impl IntoIterator<Item = (SmolStr, Expr<T>)>,
1337 ) -> Result<Expr<T>, ExpressionConstructionError> {
1338 let mut map = BTreeMap::new();
1339 for (k, v) in pairs {
1340 match map.entry(k) {
1341 btree_map::Entry::Occupied(oentry) => {
1342 return Err(expression_construction_errors::DuplicateKeyError {
1343 key: oentry.key().clone(),
1344 context: "in record literal",
1345 }
1346 .into());
1347 }
1348 btree_map::Entry::Vacant(ventry) => {
1349 ventry.insert(v);
1350 }
1351 }
1352 }
1353 Ok(self.with_expr_kind(ExprKind::Record(Arc::new(map))))
1354 }
1355
1356 fn call_extension_fn(
1359 self,
1360 fn_name: Name,
1361 args: impl IntoIterator<Item = Expr<T>>,
1362 ) -> Result<Expr<T>, Infallible> {
1363 Ok(self.with_expr_kind(ExprKind::ExtensionFunctionApp {
1364 fn_name,
1365 args: Arc::new(args.into_iter().collect()),
1366 }))
1367 }
1368
1369 fn unary_app(self, op: impl Into<UnaryOp>, arg: Expr<T>) -> Expr<T> {
1372 self.with_expr_kind(ExprKind::UnaryApp {
1373 op: op.into(),
1374 arg: Arc::new(arg),
1375 })
1376 }
1377
1378 fn binary_app(self, op: impl Into<BinaryOp>, arg1: Expr<T>, arg2: Expr<T>) -> Expr<T> {
1381 self.with_expr_kind(ExprKind::BinaryApp {
1382 op: op.into(),
1383 arg1: Arc::new(arg1),
1384 arg2: Arc::new(arg2),
1385 })
1386 }
1387
1388 fn get_attr_arc(self, expr: Arc<Expr<T>>, attr: SmolStr) -> Expr<T> {
1392 self.with_expr_kind(ExprKind::GetAttr { expr, attr })
1393 }
1394
1395 fn has_attr_arc(self, expr: Arc<Expr<T>>, attr: SmolStr) -> Expr<T> {
1400 self.with_expr_kind(ExprKind::HasAttr { expr, attr })
1401 }
1402
1403 fn like(self, expr: Expr<T>, pattern: Pattern) -> Expr<T> {
1407 self.with_expr_kind(ExprKind::Like {
1408 expr: Arc::new(expr),
1409 pattern,
1410 })
1411 }
1412
1413 fn is_entity_type_arc(self, expr: Arc<Expr<T>>, entity_type: EntityType) -> Expr<T> {
1415 self.with_expr_kind(ExprKind::Is { expr, entity_type })
1416 }
1417
1418 fn extended_has_attr_arc(self, expr: Arc<Expr<T>>, attrs: NonEmpty<SmolStr>) -> Expr<T> {
1420 if attrs.tail.is_empty() {
1422 self.with_expr_kind(ExprKind::HasAttr {
1423 expr,
1424 attr: attrs.head,
1425 })
1426 } else {
1427 self.with_expr_kind(ExprKind::ExtHasAttr { expr, attrs })
1428 }
1429 }
1430
1431 #[cfg(feature = "tolerant-ast")]
1433 fn error(self, parse_errors: ParseErrors) -> Result<Self::Expr, Self::ErrorType> {
1434 Err(parse_errors)
1435 }
1436}
1437
1438impl<T> ExprBuilder<T> {
1439 pub fn with_expr_kind(self, expr_kind: ExprKind<T>) -> Expr<T> {
1442 Expr::new(expr_kind, self.source_loc, self.data)
1443 }
1444
1445 pub fn record_arc(self, map: Arc<BTreeMap<SmolStr, Expr<T>>>) -> Expr<T> {
1452 self.with_expr_kind(ExprKind::Record(map))
1453 }
1454}
1455
1456impl<T: Clone + Default> ExprBuilder<T> {
1457 pub fn with_same_source_loc<U>(self, expr: &Expr<U>) -> Self {
1461 self.with_maybe_source_loc(expr.source_loc.as_ref())
1462 }
1463}
1464
1465#[derive(Error, Debug, Clone, Diagnostic, PartialEq, Eq)]
1467#[error("invalid expression: {0}")]
1468pub struct ExprValidationError(String);
1469
1470#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1476pub enum ExpressionConstructionError {
1477 #[error(transparent)]
1479 #[diagnostic(transparent)]
1480 DuplicateKey(#[from] expression_construction_errors::DuplicateKeyError),
1481}
1482
1483pub mod expression_construction_errors {
1485 use miette::Diagnostic;
1486 use smol_str::SmolStr;
1487 use thiserror::Error;
1488
1489 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1495 #[error("duplicate key `{key}` {context}")]
1496 pub struct DuplicateKeyError {
1497 pub(crate) key: SmolStr,
1499 pub(crate) context: &'static str,
1501 }
1502
1503 impl DuplicateKeyError {
1504 pub fn key(&self) -> &str {
1506 &self.key
1507 }
1508
1509 pub(crate) fn with_context(self, context: &'static str) -> Self {
1511 Self { context, ..self }
1512 }
1513 }
1514}
1515
1516#[derive(Debug, Clone)]
1520pub struct ExprShapeOnly<'a, T: Clone = ()>(Cow<'a, Expr<T>>);
1521
1522impl<'a, T: Clone> ExprShapeOnly<'a, T> {
1523 pub fn new_from_borrowed(e: &'a Expr<T>) -> ExprShapeOnly<'a, T> {
1527 ExprShapeOnly(Cow::Borrowed(e))
1528 }
1529
1530 pub fn new_from_owned(e: Expr<T>) -> ExprShapeOnly<'a, T> {
1534 ExprShapeOnly(Cow::Owned(e))
1535 }
1536}
1537
1538impl<T: Clone> PartialEq for ExprShapeOnly<'_, T> {
1539 fn eq(&self, other: &Self) -> bool {
1540 self.0.eq_shape(&other.0)
1541 }
1542}
1543
1544impl<T: Clone> Eq for ExprShapeOnly<'_, T> {}
1545
1546impl<T: Clone> Hash for ExprShapeOnly<'_, T> {
1547 fn hash<H: Hasher>(&self, state: &mut H) {
1548 self.0.hash_shape(state);
1549 }
1550}
1551
1552impl<T: Clone> PartialOrd for ExprShapeOnly<'_, T> {
1553 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1554 Some(self.cmp(other))
1555 }
1556}
1557
1558impl<T: Clone> Ord for ExprShapeOnly<'_, T> {
1559 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1560 self.0.cmp_shape(&other.0)
1561 }
1562}
1563
1564impl<T> Expr<T> {
1565 pub fn eq_shape<U>(&self, other: &Expr<U>) -> bool {
1572 use ExprKind::*;
1573 match (self.expr_kind(), other.expr_kind()) {
1574 (Lit(lit), Lit(lit1)) => lit == lit1,
1575 (Var(v), Var(v1)) => v == v1,
1576 (Slot(s), Slot(s1)) => s == s1,
1577 (
1578 Unknown(self::Unknown {
1579 name: name1,
1580 type_annotation: ta_1,
1581 }),
1582 Unknown(self::Unknown {
1583 name: name2,
1584 type_annotation: ta_2,
1585 }),
1586 ) => (name1 == name2) && (ta_1 == ta_2),
1587 (
1588 If {
1589 test_expr,
1590 then_expr,
1591 else_expr,
1592 },
1593 If {
1594 test_expr: test_expr1,
1595 then_expr: then_expr1,
1596 else_expr: else_expr1,
1597 },
1598 ) => {
1599 test_expr.eq_shape(test_expr1)
1600 && then_expr.eq_shape(then_expr1)
1601 && else_expr.eq_shape(else_expr1)
1602 }
1603 (
1604 And { left, right },
1605 And {
1606 left: left1,
1607 right: right1,
1608 },
1609 )
1610 | (
1611 Or { left, right },
1612 Or {
1613 left: left1,
1614 right: right1,
1615 },
1616 ) => left.eq_shape(left1) && right.eq_shape(right1),
1617 (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1618 op == op1 && arg.eq_shape(arg1)
1619 }
1620 (
1621 BinaryApp { op, arg1, arg2 },
1622 BinaryApp {
1623 op: op1,
1624 arg1: arg11,
1625 arg2: arg21,
1626 },
1627 ) => op == op1 && arg1.eq_shape(arg11) && arg2.eq_shape(arg21),
1628 (
1629 ExtensionFunctionApp { fn_name, args },
1630 ExtensionFunctionApp {
1631 fn_name: fn_name1,
1632 args: args1,
1633 },
1634 ) => {
1635 fn_name == fn_name1
1636 && args.len() == args1.len()
1637 && args.iter().zip(args1.iter()).all(|(a, a1)| a.eq_shape(a1))
1638 }
1639 (
1640 GetAttr { expr, attr },
1641 GetAttr {
1642 expr: expr1,
1643 attr: attr1,
1644 },
1645 )
1646 | (
1647 HasAttr { expr, attr },
1648 HasAttr {
1649 expr: expr1,
1650 attr: attr1,
1651 },
1652 ) => attr == attr1 && expr.eq_shape(expr1),
1653 (
1654 ExtHasAttr { expr, attrs },
1655 ExtHasAttr {
1656 expr: expr1,
1657 attrs: attrs1,
1658 },
1659 ) => attrs == attrs1 && expr.eq_shape(expr1),
1660 (
1661 Like { expr, pattern },
1662 Like {
1663 expr: expr1,
1664 pattern: pattern1,
1665 },
1666 ) => pattern == pattern1 && expr.eq_shape(expr1),
1667 (Set(elems), Set(elems1)) => {
1668 elems.len() == elems1.len()
1669 && elems
1670 .iter()
1671 .zip(elems1.iter())
1672 .all(|(e, e1)| e.eq_shape(e1))
1673 }
1674 (Record(map), Record(map1)) => {
1675 map.len() == map1.len()
1676 && map
1677 .iter()
1678 .zip(map1.iter()) .all(|((a, e), (a1, e1))| a == a1 && e.eq_shape(e1))
1680 }
1681 (
1682 Is { expr, entity_type },
1683 Is {
1684 expr: expr1,
1685 entity_type: entity_type1,
1686 },
1687 ) => entity_type == entity_type1 && expr.eq_shape(expr1),
1688 _ => false,
1689 }
1690 }
1691
1692 pub fn hash_shape<H>(&self, state: &mut H)
1696 where
1697 H: Hasher,
1698 {
1699 mem::discriminant(self).hash(state);
1700 match self.expr_kind() {
1701 ExprKind::Lit(lit) => lit.hash(state),
1702 ExprKind::Var(v) => v.hash(state),
1703 ExprKind::Slot(s) => s.hash(state),
1704 ExprKind::Unknown(u) => u.hash(state),
1705 ExprKind::If {
1706 test_expr,
1707 then_expr,
1708 else_expr,
1709 } => {
1710 test_expr.hash_shape(state);
1711 then_expr.hash_shape(state);
1712 else_expr.hash_shape(state);
1713 }
1714 ExprKind::And { left, right } => {
1715 left.hash_shape(state);
1716 right.hash_shape(state);
1717 }
1718 ExprKind::Or { left, right } => {
1719 left.hash_shape(state);
1720 right.hash_shape(state);
1721 }
1722 ExprKind::UnaryApp { op, arg } => {
1723 op.hash(state);
1724 arg.hash_shape(state);
1725 }
1726 ExprKind::BinaryApp { op, arg1, arg2 } => {
1727 op.hash(state);
1728 arg1.hash_shape(state);
1729 arg2.hash_shape(state);
1730 }
1731 ExprKind::ExtensionFunctionApp { fn_name, args } => {
1732 fn_name.hash(state);
1733 state.write_usize(args.len());
1734 args.iter().for_each(|a| {
1735 a.hash_shape(state);
1736 });
1737 }
1738 ExprKind::GetAttr { expr, attr } => {
1739 expr.hash_shape(state);
1740 attr.hash(state);
1741 }
1742 ExprKind::HasAttr { expr, attr } => {
1743 expr.hash_shape(state);
1744 attr.hash(state);
1745 }
1746 ExprKind::ExtHasAttr { expr, attrs } => {
1747 expr.hash_shape(state);
1748 attrs.hash(state);
1749 }
1750 ExprKind::Like { expr, pattern } => {
1751 expr.hash_shape(state);
1752 pattern.hash(state);
1753 }
1754 ExprKind::Set(elems) => {
1755 state.write_usize(elems.len());
1756 elems.iter().for_each(|e| {
1757 e.hash_shape(state);
1758 })
1759 }
1760 ExprKind::Record(map) => {
1761 state.write_usize(map.len());
1762 map.iter().for_each(|(s, a)| {
1763 s.hash(state);
1764 a.hash_shape(state);
1765 });
1766 }
1767 ExprKind::Is { expr, entity_type } => {
1768 expr.hash_shape(state);
1769 entity_type.hash(state);
1770 }
1771 #[cfg(feature = "tolerant-ast")]
1772 ExprKind::Error { error_kind, .. } => error_kind.hash(state),
1773 }
1774 }
1775
1776 pub fn cmp_shape(&self, other: &Expr<T>) -> std::cmp::Ordering {
1780 let self_kind = self.expr_kind();
1782 let other_kind = other.expr_kind();
1783 if std::mem::discriminant(self_kind) != std::mem::discriminant(other_kind) {
1784 return self_kind.variant_order().cmp(&other_kind.variant_order());
1785 }
1786
1787 use ExprKind::*;
1789 match (self_kind, other_kind) {
1790 (Lit(lit), Lit(lit1)) => lit.cmp(lit1),
1791 (Var(v), Var(v1)) => v.cmp(v1),
1792 (Slot(s), Slot(s1)) => s.cmp(s1),
1793 (
1794 Unknown(self::Unknown {
1795 name: name1,
1796 type_annotation: ta_1,
1797 }),
1798 Unknown(self::Unknown {
1799 name: name2,
1800 type_annotation: ta_2,
1801 }),
1802 ) => name1.cmp(name2).then_with(|| ta_1.cmp(ta_2)),
1803 (
1804 If {
1805 test_expr,
1806 then_expr,
1807 else_expr,
1808 },
1809 If {
1810 test_expr: test_expr1,
1811 then_expr: then_expr1,
1812 else_expr: else_expr1,
1813 },
1814 ) => test_expr
1815 .cmp_shape(test_expr1)
1816 .then_with(|| then_expr.cmp_shape(then_expr1))
1817 .then_with(|| else_expr.cmp_shape(else_expr1)),
1818 (
1819 And { left, right },
1820 And {
1821 left: left1,
1822 right: right1,
1823 },
1824 ) => left.cmp_shape(left1).then_with(|| right.cmp_shape(right1)),
1825 (
1826 Or { left, right },
1827 Or {
1828 left: left1,
1829 right: right1,
1830 },
1831 ) => left.cmp_shape(left1).then_with(|| right.cmp_shape(right1)),
1832 (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1833 op.cmp(op1).then_with(|| arg.cmp_shape(arg1))
1834 }
1835 (
1836 BinaryApp { op, arg1, arg2 },
1837 BinaryApp {
1838 op: op1,
1839 arg1: arg11,
1840 arg2: arg21,
1841 },
1842 ) => op
1843 .cmp(op1)
1844 .then_with(|| arg1.cmp_shape(arg11))
1845 .then_with(|| arg2.cmp_shape(arg21)),
1846 (
1847 ExtensionFunctionApp { fn_name, args },
1848 ExtensionFunctionApp {
1849 fn_name: fn_name1,
1850 args: args1,
1851 },
1852 ) => fn_name.cmp(fn_name1).then_with(|| {
1853 args.len().cmp(&args1.len()).then_with(|| {
1854 for (a, a1) in args.iter().zip(args1.iter()) {
1855 match a.cmp_shape(a1) {
1856 std::cmp::Ordering::Equal => continue,
1857 other => return other,
1858 }
1859 }
1860 std::cmp::Ordering::Equal
1861 })
1862 }),
1863 (
1864 GetAttr { expr, attr },
1865 GetAttr {
1866 expr: expr1,
1867 attr: attr1,
1868 },
1869 ) => attr.cmp(attr1).then_with(|| expr.cmp_shape(expr1)),
1870 (
1871 HasAttr { expr, attr },
1872 HasAttr {
1873 expr: expr1,
1874 attr: attr1,
1875 },
1876 ) => attr.cmp(attr1).then_with(|| expr.cmp_shape(expr1)),
1877 (
1878 ExtHasAttr { expr, attrs },
1879 ExtHasAttr {
1880 expr: expr1,
1881 attrs: attrs1,
1882 },
1883 ) => attrs.cmp(attrs1).then_with(|| expr.cmp_shape(expr1)),
1884 (
1885 Like { expr, pattern },
1886 Like {
1887 expr: expr1,
1888 pattern: pattern1,
1889 },
1890 ) => pattern.cmp(pattern1).then_with(|| expr.cmp_shape(expr1)),
1891 (Set(elems), Set(elems1)) => elems.len().cmp(&elems1.len()).then_with(|| {
1892 for (e, e1) in elems.iter().zip(elems1.iter()) {
1893 match e.cmp_shape(e1) {
1894 std::cmp::Ordering::Equal => continue,
1895 other => return other,
1896 }
1897 }
1898 std::cmp::Ordering::Equal
1899 }),
1900 (Record(map), Record(map1)) => map.len().cmp(&map1.len()).then_with(|| {
1901 for ((a, e), (a1, e1)) in map.iter().zip(map1.iter()) {
1902 match a.cmp(a1).then_with(|| e.cmp_shape(e1)) {
1903 std::cmp::Ordering::Equal => continue,
1904 other => return other,
1905 }
1906 }
1907 std::cmp::Ordering::Equal
1908 }),
1909 (
1910 Is { expr, entity_type },
1911 Is {
1912 expr: expr1,
1913 entity_type: entity_type1,
1914 },
1915 ) => entity_type
1916 .cmp(entity_type1)
1917 .then_with(|| expr.cmp_shape(expr1)),
1918 #[cfg(feature = "tolerant-ast")]
1919 (
1920 Error { error_kind },
1921 Error {
1922 error_kind: error_kind1,
1923 },
1924 ) => error_kind.cmp(error_kind1),
1925 #[expect(
1926 clippy::unreachable,
1927 reason = "This should never be reached since we compare variants first"
1928 )]
1929 _ => unreachable!(
1930 "Different variants should have been handled by variant_order comparison"
1931 ),
1932 }
1933 }
1934}
1935
1936#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
1938#[serde(rename_all = "camelCase")]
1939#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1940#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
1941#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
1942pub enum Var {
1943 Principal,
1945 Action,
1947 Resource,
1949 Context,
1951}
1952
1953impl From<PrincipalOrResource> for Var {
1954 fn from(v: PrincipalOrResource) -> Self {
1955 match v {
1956 PrincipalOrResource::Principal => Var::Principal,
1957 PrincipalOrResource::Resource => Var::Resource,
1958 }
1959 }
1960}
1961
1962#[expect(
1963 clippy::fallible_impl_from,
1964 reason = "Tested by `test::all_vars_are_ids`. Never panics"
1965)]
1966impl From<Var> for Id {
1967 fn from(var: Var) -> Self {
1968 #[expect(
1969 clippy::unwrap_used,
1970 reason = "`Var` is a simple enum and all vars are formatted as valid `Id`. Tested by `test::all_vars_are_ids`"
1971 )]
1972 format!("{var}").parse().unwrap()
1973 }
1974}
1975
1976#[expect(
1977 clippy::fallible_impl_from,
1978 reason = "Tested by `test::all_vars_are_ids`. Never panics"
1979)]
1980impl From<Var> for UnreservedId {
1981 fn from(var: Var) -> Self {
1982 #[expect(
1983 clippy::unwrap_used,
1984 reason = "`Var` is a simple enum and all vars are formatted as valid `UnreservedId`. Tested by `test::all_vars_are_ids`"
1985 )]
1986 Id::from(var).try_into().unwrap()
1987 }
1988}
1989
1990impl std::fmt::Display for Var {
1991 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1992 match self {
1993 Self::Principal => write!(f, "principal"),
1994 Self::Action => write!(f, "action"),
1995 Self::Resource => write!(f, "resource"),
1996 Self::Context => write!(f, "context"),
1997 }
1998 }
1999}
2000
2001#[cfg(test)]
2002mod test {
2003 use cool_asserts::assert_matches;
2004 use itertools::Itertools;
2005 use smol_str::ToSmolStr;
2006 use std::collections::{hash_map::DefaultHasher, HashSet};
2007
2008 use super::*;
2009
2010 pub fn all_vars() -> impl Iterator<Item = Var> {
2011 [Var::Principal, Var::Action, Var::Resource, Var::Context].into_iter()
2012 }
2013
2014 #[test]
2016 fn all_vars_are_ids() {
2017 for var in all_vars() {
2018 let _id: Id = var.into();
2019 let _id: UnreservedId = var.into();
2020 }
2021 }
2022
2023 #[test]
2024 fn exprs() {
2025 assert_eq!(
2026 Expr::val(33),
2027 Expr::new(ExprKind::Lit(Literal::Long(33)), None, ())
2028 );
2029 assert_eq!(
2030 Expr::val("hello"),
2031 Expr::new(ExprKind::Lit(Literal::from("hello")), None, ())
2032 );
2033 assert_eq!(
2034 Expr::val(EntityUID::with_eid("foo")),
2035 Expr::new(
2036 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2037 None,
2038 ()
2039 )
2040 );
2041 assert_eq!(
2042 Expr::var(Var::Principal),
2043 Expr::new(ExprKind::Var(Var::Principal), None, ())
2044 );
2045 assert_eq!(
2046 Expr::ite(Expr::val(true), Expr::val(88), Expr::val(-100)),
2047 Expr::new(
2048 ExprKind::If {
2049 test_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(true)), None, ())),
2050 then_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(88)), None, ())),
2051 else_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(-100)), None, ())),
2052 },
2053 None,
2054 ()
2055 )
2056 );
2057 assert_eq!(
2058 Expr::not(Expr::val(false)),
2059 Expr::new(
2060 ExprKind::UnaryApp {
2061 op: UnaryOp::Not,
2062 arg: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(false)), None, ())),
2063 },
2064 None,
2065 ()
2066 )
2067 );
2068 assert_eq!(
2069 Expr::get_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
2070 Expr::new(
2071 ExprKind::GetAttr {
2072 expr: Arc::new(Expr::new(
2073 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2074 None,
2075 ()
2076 )),
2077 attr: "some_attr".into()
2078 },
2079 None,
2080 ()
2081 )
2082 );
2083 assert_eq!(
2084 Expr::has_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
2085 Expr::new(
2086 ExprKind::HasAttr {
2087 expr: Arc::new(Expr::new(
2088 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2089 None,
2090 ()
2091 )),
2092 attr: "some_attr".into()
2093 },
2094 None,
2095 ()
2096 )
2097 );
2098 assert_eq!(
2099 Expr::is_entity_type(
2100 Expr::val(EntityUID::with_eid("foo")),
2101 "Type".parse().unwrap()
2102 ),
2103 Expr::new(
2104 ExprKind::Is {
2105 expr: Arc::new(Expr::new(
2106 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2107 None,
2108 ()
2109 )),
2110 entity_type: "Type".parse().unwrap()
2111 },
2112 None,
2113 ()
2114 ),
2115 );
2116 }
2117
2118 #[test]
2119 fn like_display() {
2120 let e = Expr::like(Expr::val("a"), Pattern::from(vec![PatternElem::Char('\0')]));
2122 assert_eq!(format!("{e}"), r#""a" like "\0""#);
2123 let e = Expr::like(
2125 Expr::val("a"),
2126 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('0')]),
2127 );
2128 assert_eq!(format!("{e}"), r#""a" like "\\0""#);
2129 let e = Expr::like(
2131 Expr::val("a"),
2132 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Wildcard]),
2133 );
2134 assert_eq!(format!("{e}"), r#""a" like "\\*""#);
2135 let e = Expr::like(
2137 Expr::val("a"),
2138 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('*')]),
2139 );
2140 assert_eq!(format!("{e}"), r#""a" like "\\\*""#);
2141 }
2142
2143 #[test]
2144 fn has_display() {
2145 let e = Expr::has_attr(Expr::val("a"), "\0".into());
2147 assert_eq!(format!("{e}"), r#""a" has "\0""#);
2148 let e = Expr::has_attr(Expr::val("a"), r"\".into());
2150 assert_eq!(format!("{e}"), r#""a" has "\\""#);
2151 }
2152
2153 #[test]
2154 fn extended_has_display() {
2155 use nonempty::nonempty;
2156 let e =
2158 Expr::extended_has_attr(Expr::var(Var::Principal), nonempty!["a".into(), "b".into()]);
2159 assert_eq!(format!("{e}"), "principal has a.b");
2160 let e = Expr::extended_has_attr(
2162 Expr::var(Var::Context),
2163 nonempty!["user".into(), "profile".into(), "email".into()],
2164 );
2165 assert_eq!(format!("{e}"), "context has user.profile.email");
2166 let e = Expr::extended_has_attr(
2168 Expr::var(Var::Resource),
2169 nonempty!["owner".into(), "ipinfo".into(), "additionalData".into()],
2170 );
2171 let displayed = format!("{e}");
2172 assert_eq!(displayed, "resource has owner.ipinfo.additionalData");
2173 let reparsed = displayed.parse::<Expr>().unwrap();
2174 assert!(e.eq_shape(&reparsed));
2175 }
2176
2177 #[test]
2178 fn slot_display() {
2179 let e = Expr::slot(SlotId::principal());
2180 assert_eq!(format!("{e}"), "?principal");
2181 let e = Expr::slot(SlotId::resource());
2182 assert_eq!(format!("{e}"), "?resource");
2183 let e = Expr::val(EntityUID::with_eid("eid"));
2184 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
2185 }
2186
2187 #[test]
2188 fn simple_slots() {
2189 let e = Expr::slot(SlotId::principal());
2190 let p = SlotId::principal();
2191 let r = SlotId::resource();
2192 let set: HashSet<SlotId> = HashSet::from_iter([p]);
2193 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
2194 let e = Expr::or(
2195 Expr::slot(SlotId::principal()),
2196 Expr::ite(
2197 Expr::val(true),
2198 Expr::slot(SlotId::resource()),
2199 Expr::val(false),
2200 ),
2201 );
2202 let set: HashSet<SlotId> = HashSet::from_iter([p, r]);
2203 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
2204 }
2205
2206 #[test]
2207 fn unknowns() {
2208 let e = Expr::ite(
2209 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
2210 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
2211 Expr::unknown(Unknown::new_untyped("c")),
2212 );
2213 let unknowns = e.unknowns().collect_vec();
2214 assert_eq!(unknowns.len(), 3);
2215 assert!(unknowns.contains(&&Unknown::new_untyped("a")));
2216 assert!(unknowns.contains(&&Unknown::new_untyped("b")));
2217 assert!(unknowns.contains(&&Unknown::new_untyped("c")));
2218 }
2219
2220 #[test]
2221 fn is_unknown() {
2222 let e = Expr::ite(
2223 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
2224 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
2225 Expr::unknown(Unknown::new_untyped("c")),
2226 );
2227 assert!(e.contains_unknown());
2228 let e = Expr::ite(
2229 Expr::not(Expr::val(true)),
2230 Expr::and(Expr::val(1), Expr::val(3)),
2231 Expr::val(1),
2232 );
2233 assert!(!e.contains_unknown());
2234 }
2235
2236 #[test]
2237 fn expr_with_data() {
2238 let e = ExprBuilder::with_data("data").val(1);
2239 assert_eq!(e.into_data(), "data");
2240 }
2241
2242 #[test]
2243 fn expr_shape_only_eq() {
2244 let temp = ExprBuilder::with_data(1).val(1);
2245 let exprs = &[
2246 (ExprBuilder::with_data(1).val(33), Expr::val(33)),
2247 (ExprBuilder::with_data(1).val(true), Expr::val(true)),
2248 (
2249 ExprBuilder::with_data(1).var(Var::Principal),
2250 Expr::var(Var::Principal),
2251 ),
2252 (
2253 ExprBuilder::with_data(1).slot(SlotId::principal()),
2254 Expr::slot(SlotId::principal()),
2255 ),
2256 (
2257 ExprBuilder::with_data(1).ite(temp.clone(), temp.clone(), temp.clone()),
2258 Expr::ite(Expr::val(1), Expr::val(1), Expr::val(1)),
2259 ),
2260 (
2261 ExprBuilder::with_data(1).not(temp.clone()),
2262 Expr::not(Expr::val(1)),
2263 ),
2264 (
2265 ExprBuilder::with_data(1).is_eq(temp.clone(), temp.clone()),
2266 Expr::is_eq(Expr::val(1), Expr::val(1)),
2267 ),
2268 (
2269 ExprBuilder::with_data(1).and(temp.clone(), temp.clone()),
2270 Expr::and(Expr::val(1), Expr::val(1)),
2271 ),
2272 (
2273 ExprBuilder::with_data(1).or(temp.clone(), temp.clone()),
2274 Expr::or(Expr::val(1), Expr::val(1)),
2275 ),
2276 (
2277 ExprBuilder::with_data(1).less(temp.clone(), temp.clone()),
2278 Expr::less(Expr::val(1), Expr::val(1)),
2279 ),
2280 (
2281 ExprBuilder::with_data(1).lesseq(temp.clone(), temp.clone()),
2282 Expr::lesseq(Expr::val(1), Expr::val(1)),
2283 ),
2284 (
2285 ExprBuilder::with_data(1).greater(temp.clone(), temp.clone()),
2286 Expr::greater(Expr::val(1), Expr::val(1)),
2287 ),
2288 (
2289 ExprBuilder::with_data(1).greatereq(temp.clone(), temp.clone()),
2290 Expr::greatereq(Expr::val(1), Expr::val(1)),
2291 ),
2292 (
2293 ExprBuilder::with_data(1).add(temp.clone(), temp.clone()),
2294 Expr::add(Expr::val(1), Expr::val(1)),
2295 ),
2296 (
2297 ExprBuilder::with_data(1).sub(temp.clone(), temp.clone()),
2298 Expr::sub(Expr::val(1), Expr::val(1)),
2299 ),
2300 (
2301 ExprBuilder::with_data(1).mul(temp.clone(), temp.clone()),
2302 Expr::mul(Expr::val(1), Expr::val(1)),
2303 ),
2304 (
2305 ExprBuilder::with_data(1).neg(temp.clone()),
2306 Expr::neg(Expr::val(1)),
2307 ),
2308 (
2309 ExprBuilder::with_data(1).is_in(temp.clone(), temp.clone()),
2310 Expr::is_in(Expr::val(1), Expr::val(1)),
2311 ),
2312 (
2313 ExprBuilder::with_data(1).contains(temp.clone(), temp.clone()),
2314 Expr::contains(Expr::val(1), Expr::val(1)),
2315 ),
2316 (
2317 ExprBuilder::with_data(1).contains_all(temp.clone(), temp.clone()),
2318 Expr::contains_all(Expr::val(1), Expr::val(1)),
2319 ),
2320 (
2321 ExprBuilder::with_data(1).contains_any(temp.clone(), temp.clone()),
2322 Expr::contains_any(Expr::val(1), Expr::val(1)),
2323 ),
2324 (
2325 ExprBuilder::with_data(1).is_empty(temp.clone()),
2326 Expr::is_empty(Expr::val(1)),
2327 ),
2328 (
2329 ExprBuilder::with_data(1).set([temp.clone()]),
2330 Expr::set([Expr::val(1)]),
2331 ),
2332 (
2333 ExprBuilder::with_data(1)
2334 .record([("foo".into(), temp.clone())])
2335 .unwrap(),
2336 Expr::record([("foo".into(), Expr::val(1))]).unwrap(),
2337 ),
2338 (
2339 ExprBuilder::with_data(1)
2340 .call_extension_fn("foo".parse().unwrap(), vec![temp.clone()])
2341 .unwrap_infallible(),
2342 Expr::call_extension_fn("foo".parse().unwrap(), vec![Expr::val(1)]),
2343 ),
2344 (
2345 ExprBuilder::with_data(1).get_attr(temp.clone(), "foo".into()),
2346 Expr::get_attr(Expr::val(1), "foo".into()),
2347 ),
2348 (
2349 ExprBuilder::with_data(1).has_attr(temp.clone(), "foo".into()),
2350 Expr::has_attr(Expr::val(1), "foo".into()),
2351 ),
2352 (
2353 ExprBuilder::with_data(1)
2354 .like(temp.clone(), Pattern::from(vec![PatternElem::Wildcard])),
2355 Expr::like(Expr::val(1), Pattern::from(vec![PatternElem::Wildcard])),
2356 ),
2357 (
2358 ExprBuilder::with_data(1).is_entity_type(temp, "T".parse().unwrap()),
2359 Expr::is_entity_type(Expr::val(1), "T".parse().unwrap()),
2360 ),
2361 ];
2362
2363 for (e0, e1) in exprs {
2364 assert!(e0.eq_shape(e0));
2365 assert!(e1.eq_shape(e1));
2366 assert!(e0.eq_shape(e1));
2367 assert!(e1.eq_shape(e0));
2368
2369 let mut hasher0 = DefaultHasher::new();
2370 e0.hash_shape(&mut hasher0);
2371 let hash0 = hasher0.finish();
2372
2373 let mut hasher1 = DefaultHasher::new();
2374 e1.hash_shape(&mut hasher1);
2375 let hash1 = hasher1.finish();
2376
2377 assert_eq!(hash0, hash1);
2378 }
2379 }
2380
2381 #[test]
2382 fn expr_shape_only_not_eq() {
2383 let expr1 = ExprBuilder::with_data(1).val(1);
2384 let expr2 = ExprBuilder::with_data(1).val(2);
2385 assert_ne!(
2386 ExprShapeOnly::new_from_borrowed(&expr1),
2387 ExprShapeOnly::new_from_borrowed(&expr2)
2388 );
2389 }
2390
2391 #[test]
2392 fn expr_shape_only_set_prefix_ne() {
2393 let e1 = ExprShapeOnly::new_from_owned(Expr::set([]));
2394 let e2 = ExprShapeOnly::new_from_owned(Expr::set([Expr::val(1)]));
2395 let e3 = ExprShapeOnly::new_from_owned(Expr::set([Expr::val(1), Expr::val(2)]));
2396
2397 assert_ne!(e1, e2);
2398 assert_ne!(e1, e3);
2399 assert_ne!(e2, e1);
2400 assert_ne!(e2, e3);
2401 assert_ne!(e3, e1);
2402 assert_ne!(e2, e1);
2403 }
2404
2405 #[test]
2406 fn expr_shape_only_ext_fn_arg_prefix_ne() {
2407 let e1 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2408 "decimal".parse().unwrap(),
2409 vec![],
2410 ));
2411 let e2 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2412 "decimal".parse().unwrap(),
2413 vec![Expr::val("0.0")],
2414 ));
2415 let e3 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2416 "decimal".parse().unwrap(),
2417 vec![Expr::val("0.0"), Expr::val("0.0")],
2418 ));
2419
2420 assert_ne!(e1, e2);
2421 assert_ne!(e1, e3);
2422 assert_ne!(e2, e1);
2423 assert_ne!(e2, e3);
2424 assert_ne!(e3, e1);
2425 assert_ne!(e2, e1);
2426 }
2427
2428 #[test]
2429 fn expr_shape_only_record_attr_prefix_ne() {
2430 let e1 = ExprShapeOnly::new_from_owned(Expr::record([]).unwrap());
2431 let e2 = ExprShapeOnly::new_from_owned(
2432 Expr::record([("a".to_smolstr(), Expr::val(1))]).unwrap(),
2433 );
2434 let e3 = ExprShapeOnly::new_from_owned(
2435 Expr::record([
2436 ("a".to_smolstr(), Expr::val(1)),
2437 ("b".to_smolstr(), Expr::val(2)),
2438 ])
2439 .unwrap(),
2440 );
2441
2442 assert_ne!(e1, e2);
2443 assert_ne!(e1, e3);
2444 assert_ne!(e2, e1);
2445 assert_ne!(e2, e3);
2446 assert_ne!(e3, e1);
2447 assert_ne!(e2, e1);
2448 }
2449
2450 #[test]
2451 fn untyped_subst_present() {
2452 let u = Unknown {
2453 name: "foo".into(),
2454 type_annotation: None,
2455 };
2456 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2457 match r {
2458 Ok(e) => assert_eq!(e, Expr::val(1)),
2459 Err(empty) => match empty {},
2460 }
2461 }
2462
2463 #[test]
2464 fn untyped_subst_present_correct_type() {
2465 let u = Unknown {
2466 name: "foo".into(),
2467 type_annotation: Some(Type::Long),
2468 };
2469 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2470 match r {
2471 Ok(e) => assert_eq!(e, Expr::val(1)),
2472 Err(empty) => match empty {},
2473 }
2474 }
2475
2476 #[test]
2477 fn untyped_subst_present_wrong_type() {
2478 let u = Unknown {
2479 name: "foo".into(),
2480 type_annotation: Some(Type::Bool),
2481 };
2482 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2483 match r {
2484 Ok(e) => assert_eq!(e, Expr::val(1)),
2485 Err(empty) => match empty {},
2486 }
2487 }
2488
2489 #[test]
2490 fn untyped_subst_not_present() {
2491 let u = Unknown {
2492 name: "foo".into(),
2493 type_annotation: Some(Type::Bool),
2494 };
2495 let r = UntypedSubstitution::substitute(&u, None);
2496 match r {
2497 Ok(n) => assert_eq!(n, Expr::unknown(u)),
2498 Err(empty) => match empty {},
2499 }
2500 }
2501
2502 #[test]
2503 fn typed_subst_present() {
2504 let u = Unknown {
2505 name: "foo".into(),
2506 type_annotation: None,
2507 };
2508 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2509 assert_eq!(e, Expr::val(1));
2510 }
2511
2512 #[test]
2513 fn typed_subst_present_correct_type() {
2514 let u = Unknown {
2515 name: "foo".into(),
2516 type_annotation: Some(Type::Long),
2517 };
2518 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2519 assert_eq!(e, Expr::val(1));
2520 }
2521
2522 #[test]
2523 fn typed_subst_present_wrong_type() {
2524 let u = Unknown {
2525 name: "foo".into(),
2526 type_annotation: Some(Type::Bool),
2527 };
2528 let r = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap_err();
2529 assert_matches!(
2530 r,
2531 SubstitutionError::TypeError {
2532 expected: Type::Bool,
2533 actual: Type::Long,
2534 }
2535 );
2536 }
2537
2538 #[test]
2539 fn typed_subst_not_present() {
2540 let u = Unknown {
2541 name: "foo".into(),
2542 type_annotation: None,
2543 };
2544 let r = TypedSubstitution::substitute(&u, None).unwrap();
2545 assert_eq!(r, Expr::unknown(u));
2546 }
2547}
2548
2549#[cfg(test)]
2550mod validate_test {
2551 use cool_asserts::assert_matches;
2552
2553 use super::*;
2554
2555 fn ext_call(name: &str, args: Vec<Expr>) -> Expr {
2556 Expr::call_extension_fn(Name::parse_unqualified_name(name).unwrap(), args)
2557 }
2558
2559 #[test]
2560 fn valid_function_style_accepted() {
2561 assert!(ext_call("ip", vec![Expr::val("127.0.0.1")])
2562 .try_validate()
2563 .is_ok());
2564 }
2565
2566 #[test]
2567 fn valid_method_style_accepted() {
2568 let receiver = ext_call("ip", vec![Expr::val("127.0.0.1")]);
2569 assert!(ext_call("isIpv4", vec![receiver]).try_validate().is_ok());
2570 }
2571
2572 #[test]
2573 fn unknown_extension_fn_rejected() {
2574 let err = ext_call("notReal", vec![Expr::val("x")])
2575 .try_validate()
2576 .unwrap_err();
2577 assert!(
2578 err.to_string().contains("unknown extension function"),
2579 "got: {err}"
2580 );
2581 }
2582
2583 #[test]
2584 fn method_style_empty_args_rejected() {
2585 let err = ext_call("isIpv4", vec![]).try_validate().unwrap_err();
2586 assert!(
2587 err.to_string().contains("requires a receiver argument"),
2588 "got: {err}"
2589 );
2590 }
2591
2592 #[test]
2593 fn extended_has_with_invalid_ids_rejected() {
2594 let exprs = vec![
2595 Expr::extended_has_attr(
2596 Expr::var(Var::Principal),
2597 nonempty::nonempty!["".into(), "a".into()], ),
2599 Expr::extended_has_attr(
2600 Expr::var(Var::Principal),
2601 nonempty::nonempty!["a".into(), "".into()], ),
2603 Expr::extended_has_attr(
2604 Expr::var(Var::Principal),
2605 nonempty::nonempty!["true".into(), "a".into()], ),
2607 ];
2608 for e in exprs {
2609 let e = e.try_validate();
2610 assert_matches!(e, Err(ExprValidationError(..)));
2611 assert!(e
2612 .unwrap_err()
2613 .to_string()
2614 .starts_with("invalid expression: extended has attribute"))
2615 }
2616 }
2617}