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 serde::{Deserialize, Serialize};
32use smol_str::SmolStr;
33use std::{
34 borrow::Cow,
35 collections::{btree_map, BTreeMap, HashMap},
36 hash::{Hash, Hasher},
37 mem,
38 sync::Arc,
39};
40use thiserror::Error;
41
42#[cfg(feature = "wasm")]
43extern crate tsify;
44
45#[derive(Educe, Debug, Clone)]
52#[educe(PartialEq, Eq, Hash)]
53pub struct Expr<T = ()> {
54 expr_kind: ExprKind<T>,
55 #[educe(PartialEq(ignore))]
56 #[educe(Hash(ignore))]
57 source_loc: Option<Loc>,
58 data: T,
59}
60
61#[derive(Hash, Debug, Clone, PartialEq, Eq)]
64pub enum ExprKind<T = ()> {
65 Lit(Literal),
67 Var(Var),
69 Slot(SlotId),
71 Unknown(Unknown),
73 If {
75 test_expr: Arc<Expr<T>>,
77 then_expr: Arc<Expr<T>>,
79 else_expr: Arc<Expr<T>>,
81 },
82 And {
84 left: Arc<Expr<T>>,
86 right: Arc<Expr<T>>,
88 },
89 Or {
91 left: Arc<Expr<T>>,
93 right: Arc<Expr<T>>,
95 },
96 UnaryApp {
98 op: UnaryOp,
100 arg: Arc<Expr<T>>,
102 },
103 BinaryApp {
105 op: BinaryOp,
107 arg1: Arc<Expr<T>>,
109 arg2: Arc<Expr<T>>,
111 },
112 ExtensionFunctionApp {
118 fn_name: Name,
120 args: Arc<Vec<Expr<T>>>,
122 },
123 GetAttr {
125 expr: Arc<Expr<T>>,
128 attr: SmolStr,
130 },
131 HasAttr {
133 expr: Arc<Expr<T>>,
135 attr: SmolStr,
137 },
138 Like {
140 expr: Arc<Expr<T>>,
142 pattern: Pattern,
146 },
147 Is {
150 expr: Arc<Expr<T>>,
152 entity_type: EntityType,
154 },
155 Set(Arc<Vec<Expr<T>>>),
162 Record(Arc<BTreeMap<SmolStr, Expr<T>>>),
164 #[cfg(feature = "tolerant-ast")]
165 Error {
167 error_kind: AstExprErrorKind,
169 },
170}
171
172impl<T> ExprKind<T> {
173 fn variant_order(&self) -> u8 {
175 match self {
176 ExprKind::Lit(_) => 0,
177 ExprKind::Var(_) => 1,
178 ExprKind::Slot(_) => 2,
179 ExprKind::Unknown(_) => 3,
180 ExprKind::If { .. } => 4,
181 ExprKind::And { .. } => 5,
182 ExprKind::Or { .. } => 6,
183 ExprKind::UnaryApp { .. } => 7,
184 ExprKind::BinaryApp { .. } => 8,
185 ExprKind::ExtensionFunctionApp { .. } => 9,
186 ExprKind::GetAttr { .. } => 10,
187 ExprKind::HasAttr { .. } => 11,
188 ExprKind::Like { .. } => 12,
189 ExprKind::Set(_) => 13,
190 ExprKind::Record(_) => 14,
191 ExprKind::Is { .. } => 15,
192 #[cfg(feature = "tolerant-ast")]
193 ExprKind::Error { .. } => 16,
194 }
195 }
196}
197
198impl From<Value> for Expr {
199 fn from(v: Value) -> Self {
200 Expr::from(v.value).with_maybe_source_loc(v.loc)
201 }
202}
203
204impl From<ValueKind> for Expr {
205 fn from(v: ValueKind) -> Self {
206 match v {
207 ValueKind::Lit(lit) => Expr::val(lit),
208 ValueKind::Set(set) => Expr::set(set.iter().map(|v| Expr::from(v.clone()))),
209 #[expect(
210 clippy::expect_used,
211 reason = "cannot have duplicate key because the input was already a BTreeMap"
212 )]
213 ValueKind::Record(record) => Expr::record(
214 Arc::unwrap_or_clone(record)
215 .into_iter()
216 .map(|(k, v)| (k, Expr::from(v))),
217 )
218 .expect("cannot have duplicate key because the input was already a BTreeMap"),
219 ValueKind::ExtensionValue(ev) => RestrictedExpr::from(ev.as_ref().clone()).into(),
220 }
221 }
222}
223
224impl From<PartialValue> for Expr {
225 fn from(pv: PartialValue) -> Self {
226 match pv {
227 PartialValue::Value(v) => Expr::from(v),
228 PartialValue::Residual(expr) => expr,
229 }
230 }
231}
232
233impl<T> Expr<T> {
234 pub(crate) fn new(expr_kind: ExprKind<T>, source_loc: Option<Loc>, data: T) -> Self {
235 Self {
236 expr_kind,
237 source_loc,
238 data,
239 }
240 }
241
242 pub fn expr_kind(&self) -> &ExprKind<T> {
246 &self.expr_kind
247 }
248
249 pub fn into_expr_kind(self) -> ExprKind<T> {
251 self.expr_kind
252 }
253
254 pub fn data(&self) -> &T {
256 &self.data
257 }
258
259 pub fn into_data(self) -> T {
262 self.data
263 }
264
265 pub fn into_parts(self) -> (ExprKind<T>, Option<Loc>, T) {
268 (self.expr_kind, self.source_loc, self.data)
269 }
270
271 pub fn source_loc(&self) -> Option<&Loc> {
273 self.source_loc.as_ref()
274 }
275
276 pub fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
278 Self { source_loc, ..self }
279 }
280
281 pub fn set_data(&mut self, data: T) {
284 self.data = data;
285 }
286
287 pub fn is_ref(&self) -> bool {
292 match &self.expr_kind {
293 ExprKind::Lit(lit) => lit.is_ref(),
294 _ => false,
295 }
296 }
297
298 pub fn is_slot(&self) -> bool {
300 matches!(&self.expr_kind, ExprKind::Slot(_))
301 }
302
303 pub fn is_ref_set(&self) -> bool {
308 match &self.expr_kind {
309 ExprKind::Set(exprs) => exprs.iter().all(|e| e.is_ref()),
310 _ => false,
311 }
312 }
313
314 pub fn subexpressions(&self) -> impl Iterator<Item = &Self> {
316 expr_iterator::ExprIterator::new(self)
317 }
318
319 pub fn slots(&self) -> impl Iterator<Item = Slot> + '_ {
321 self.subexpressions()
322 .filter_map(|exp| match &exp.expr_kind {
323 ExprKind::Slot(slotid) => Some(Slot {
324 id: *slotid,
325 loc: exp.source_loc().cloned(),
326 }),
327 _ => None,
328 })
329 }
330
331 pub fn is_projectable(&self) -> bool {
335 self.subexpressions().all(|e| {
336 matches!(
337 e.expr_kind(),
338 ExprKind::Lit(_)
339 | ExprKind::Unknown(_)
340 | ExprKind::Set(_)
341 | ExprKind::Var(_)
342 | ExprKind::Record(_)
343 )
344 })
345 }
346
347 pub fn try_type_of(&self, extensions: &Extensions<'_>) -> Option<Type> {
359 match &self.expr_kind {
360 ExprKind::Lit(l) => Some(l.type_of()),
361 ExprKind::Var(_) => None,
362 ExprKind::Slot(_) => None,
363 ExprKind::Unknown(u) => u.type_annotation.clone(),
364 ExprKind::If {
365 then_expr,
366 else_expr,
367 ..
368 } => {
369 let type_of_then = then_expr.try_type_of(extensions);
370 let type_of_else = else_expr.try_type_of(extensions);
371 if type_of_then == type_of_else {
372 type_of_then
373 } else {
374 None
375 }
376 }
377 ExprKind::And { .. } => Some(Type::Bool),
378 ExprKind::Or { .. } => Some(Type::Bool),
379 ExprKind::UnaryApp {
380 op: UnaryOp::Neg, ..
381 } => Some(Type::Long),
382 ExprKind::UnaryApp {
383 op: UnaryOp::Not, ..
384 } => Some(Type::Bool),
385 ExprKind::UnaryApp {
386 op: UnaryOp::IsEmpty,
387 ..
388 } => Some(Type::Bool),
389 ExprKind::BinaryApp {
390 op: BinaryOp::Add | BinaryOp::Mul | BinaryOp::Sub,
391 ..
392 } => Some(Type::Long),
393 ExprKind::BinaryApp {
394 op:
395 BinaryOp::Contains
396 | BinaryOp::ContainsAll
397 | BinaryOp::ContainsAny
398 | BinaryOp::Eq
399 | BinaryOp::In
400 | BinaryOp::Less
401 | BinaryOp::LessEq,
402 ..
403 } => Some(Type::Bool),
404 ExprKind::BinaryApp {
405 op: BinaryOp::HasTag,
406 ..
407 } => Some(Type::Bool),
408 ExprKind::ExtensionFunctionApp { fn_name, .. } => extensions
409 .func(fn_name)
410 .ok()?
411 .return_type()
412 .map(|rty| rty.clone().into()),
413 ExprKind::GetAttr { .. } => None,
418 ExprKind::BinaryApp {
420 op: BinaryOp::GetTag,
421 ..
422 } => None,
423 ExprKind::HasAttr { .. } => Some(Type::Bool),
424 ExprKind::Like { .. } => Some(Type::Bool),
425 ExprKind::Is { .. } => Some(Type::Bool),
426 ExprKind::Set(_) => Some(Type::Set),
427 ExprKind::Record(_) => Some(Type::Record),
428 #[cfg(feature = "tolerant-ast")]
429 ExprKind::Error { .. } => None,
430 }
431 }
432
433 pub fn try_into_expr<B: expr_builder::ExprBuilder>(self) -> Result<B::Expr, B::BuildError>
440 where
441 T: Clone,
442 {
443 let builder = B::new().with_maybe_source_loc(self.source_loc());
444 match self.into_expr_kind() {
445 ExprKind::Lit(lit) => Ok(builder.val(lit)),
446 ExprKind::Var(var) => Ok(builder.var(var)),
447 ExprKind::Slot(slot) => Ok(builder.slot(slot)),
448 ExprKind::Unknown(u) => Ok(builder.unknown(u)),
449 ExprKind::If {
450 test_expr,
451 then_expr,
452 else_expr,
453 } => Ok(builder.ite(
454 Arc::unwrap_or_clone(test_expr).try_into_expr::<B>()?,
455 Arc::unwrap_or_clone(then_expr).try_into_expr::<B>()?,
456 Arc::unwrap_or_clone(else_expr).try_into_expr::<B>()?,
457 )),
458 ExprKind::And { left, right } => Ok(builder.and(
459 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
460 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
461 )),
462 ExprKind::Or { left, right } => Ok(builder.or(
463 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
464 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
465 )),
466 ExprKind::UnaryApp { op, arg } => {
467 Ok(builder.unary_app(op, Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
468 }
469 ExprKind::BinaryApp { op, arg1, arg2 } => Ok(builder.binary_app(
470 op,
471 Arc::unwrap_or_clone(arg1).try_into_expr::<B>()?,
472 Arc::unwrap_or_clone(arg2).try_into_expr::<B>()?,
473 )),
474 ExprKind::ExtensionFunctionApp { fn_name, args } => {
475 let args: Vec<_> = Arc::unwrap_or_clone(args)
476 .into_iter()
477 .map(|e| e.try_into_expr::<B>())
478 .collect::<Result<_, _>>()?;
479 builder.call_extension_fn(fn_name, args)
480 }
481 ExprKind::GetAttr { expr, attr } => {
482 Ok(builder.get_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attr))
483 }
484 ExprKind::HasAttr { expr, attr } => {
485 Ok(builder.has_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attr))
486 }
487 ExprKind::Like { expr, pattern } => {
488 Ok(builder.like(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, pattern))
489 }
490 ExprKind::Is { expr, entity_type } => Ok(builder.is_entity_type(
491 Arc::unwrap_or_clone(expr).try_into_expr::<B>()?,
492 entity_type,
493 )),
494 ExprKind::Set(set) => Ok(builder.set(
495 Arc::unwrap_or_clone(set)
496 .into_iter()
497 .map(|e| e.try_into_expr::<B>())
498 .collect::<Result<Vec<_>, _>>()?,
499 )),
500 #[expect(
501 clippy::unwrap_used,
502 reason = "`map` is a map, so it will not have duplicate keys, so the `.record()` constructor cannot error"
503 )]
504 ExprKind::Record(map) => Ok(builder
505 .record(
506 Arc::unwrap_or_clone(map)
507 .into_iter()
508 .map(|(k, v)| Ok((k, v.try_into_expr::<B>()?)))
509 .collect::<Result<Vec<_>, _>>()?,
510 )
511 .unwrap()),
512 #[cfg(feature = "tolerant-ast")]
513 #[expect(
514 clippy::unwrap_used,
515 reason = "error type is Infallible so can never happen"
516 )]
517 ExprKind::Error { .. } => Ok(builder
518 .error(ParseErrors::singleton(ToASTError::new(
519 ToASTErrorKind::ASTErrorNode,
520 Some(Loc::new(0..1, "AST_ERROR_NODE".into())),
521 )))
522 .unwrap()), }
524 }
525
526 pub fn into_expr<B: expr_builder::ExprBuilder>(self) -> B::Expr
528 where
529 T: Clone,
530 B::BuildError: IsInfallible,
531 {
532 self.try_into_expr::<B>().unwrap_infallible()
533 }
534}
535
536#[expect(
537 clippy::should_implement_trait,
538 reason = "the names of arithmetic constructors alias with those of certain trait methods such as `add` of `std::ops::Add`"
539)]
540impl Expr {
541 pub fn val(v: impl Into<Literal>) -> Self {
545 ExprBuilder::new().val(v)
546 }
547
548 pub fn unknown(u: Unknown) -> Self {
550 ExprBuilder::new().unknown(u)
551 }
552
553 pub fn var(v: Var) -> Self {
555 ExprBuilder::new().var(v)
556 }
557
558 pub fn slot(s: SlotId) -> Self {
560 ExprBuilder::new().slot(s)
561 }
562
563 pub fn ite(test_expr: Expr, then_expr: Expr, else_expr: Expr) -> Self {
567 ExprBuilder::new().ite(test_expr, then_expr, else_expr)
568 }
569
570 pub fn ite_arc(test_expr: Arc<Expr>, then_expr: Arc<Expr>, else_expr: Arc<Expr>) -> Self {
574 ExprBuilder::new().ite_arc(test_expr, then_expr, else_expr)
575 }
576
577 pub fn not(e: Expr) -> Self {
579 ExprBuilder::new().not(e)
580 }
581
582 pub fn is_eq(e1: Expr, e2: Expr) -> Self {
584 ExprBuilder::new().is_eq(e1, e2)
585 }
586
587 pub fn noteq(e1: Expr, e2: Expr) -> Self {
589 ExprBuilder::new().noteq(e1, e2)
590 }
591
592 pub fn and(e1: Expr, e2: Expr) -> Self {
594 ExprBuilder::new().and(e1, e2)
595 }
596
597 pub fn or(e1: Expr, e2: Expr) -> Self {
599 ExprBuilder::new().or(e1, e2)
600 }
601
602 pub fn less(e1: Expr, e2: Expr) -> Self {
604 ExprBuilder::new().less(e1, e2)
605 }
606
607 pub fn lesseq(e1: Expr, e2: Expr) -> Self {
609 ExprBuilder::new().lesseq(e1, e2)
610 }
611
612 pub fn greater(e1: Expr, e2: Expr) -> Self {
614 ExprBuilder::new().greater(e1, e2)
615 }
616
617 pub fn greatereq(e1: Expr, e2: Expr) -> Self {
619 ExprBuilder::new().greatereq(e1, e2)
620 }
621
622 pub fn add(e1: Expr, e2: Expr) -> Self {
624 ExprBuilder::new().add(e1, e2)
625 }
626
627 pub fn sub(e1: Expr, e2: Expr) -> Self {
629 ExprBuilder::new().sub(e1, e2)
630 }
631
632 pub fn mul(e1: Expr, e2: Expr) -> Self {
634 ExprBuilder::new().mul(e1, e2)
635 }
636
637 pub fn neg(e: Expr) -> Self {
639 ExprBuilder::new().neg(e)
640 }
641
642 pub fn is_in(e1: Expr, e2: Expr) -> Self {
646 ExprBuilder::new().is_in(e1, e2)
647 }
648
649 pub fn contains(e1: Expr, e2: Expr) -> Self {
652 ExprBuilder::new().contains(e1, e2)
653 }
654
655 pub fn contains_all(e1: Expr, e2: Expr) -> Self {
657 ExprBuilder::new().contains_all(e1, e2)
658 }
659
660 pub fn contains_any(e1: Expr, e2: Expr) -> Self {
662 ExprBuilder::new().contains_any(e1, e2)
663 }
664
665 pub fn is_empty(e: Expr) -> Self {
667 ExprBuilder::new().is_empty(e)
668 }
669
670 pub fn get_tag(expr: Expr, tag: Expr) -> Self {
673 ExprBuilder::new().get_tag(expr, tag)
674 }
675
676 pub fn has_tag(expr: Expr, tag: Expr) -> Self {
679 ExprBuilder::new().has_tag(expr, tag)
680 }
681
682 pub fn set(exprs: impl IntoIterator<Item = Expr>) -> Self {
684 ExprBuilder::new().set(exprs)
685 }
686
687 pub fn record(
689 pairs: impl IntoIterator<Item = (SmolStr, Expr)>,
690 ) -> Result<Self, ExpressionConstructionError> {
691 ExprBuilder::new().record(pairs)
692 }
693
694 pub fn record_arc(map: Arc<BTreeMap<SmolStr, Expr>>) -> Self {
702 ExprBuilder::new().record_arc(map)
703 }
704
705 pub fn call_extension_fn(fn_name: Name, args: Vec<Expr>) -> Self {
708 ExprBuilder::new()
709 .call_extension_fn(fn_name, args)
710 .unwrap_infallible()
711 }
712
713 pub fn unary_app(op: impl Into<UnaryOp>, arg: Expr) -> Self {
716 ExprBuilder::new().unary_app(op, arg)
717 }
718
719 pub fn binary_app(op: impl Into<BinaryOp>, arg1: Expr, arg2: Expr) -> Self {
722 ExprBuilder::new().binary_app(op, arg1, arg2)
723 }
724
725 pub fn get_attr(expr: Expr, attr: SmolStr) -> Self {
729 ExprBuilder::new().get_attr(expr, attr)
730 }
731
732 pub fn has_attr(expr: Expr, attr: SmolStr) -> Self {
737 ExprBuilder::new().has_attr(expr, attr)
738 }
739
740 pub fn like(expr: Expr, pattern: Pattern) -> Self {
744 ExprBuilder::new().like(expr, pattern)
745 }
746
747 pub fn is_entity_type(expr: Expr, entity_type: EntityType) -> Self {
749 ExprBuilder::new().is_entity_type(expr, entity_type)
750 }
751
752 pub fn contains_unknown(&self) -> bool {
754 self.subexpressions()
755 .any(|e| matches!(e.expr_kind(), ExprKind::Unknown(_)))
756 }
757
758 pub fn unknowns(&self) -> impl Iterator<Item = &Unknown> {
760 self.subexpressions()
761 .filter_map(|subexpr| match subexpr.expr_kind() {
762 ExprKind::Unknown(u) => Some(u),
763 _ => None,
764 })
765 }
766
767 pub fn substitute(&self, definitions: &HashMap<SmolStr, Value>) -> Expr {
776 match self.substitute_general::<UntypedSubstitution>(definitions) {
777 Ok(e) => e,
778 Err(empty) => match empty {},
779 }
780 }
781
782 pub fn substitute_typed(
791 &self,
792 definitions: &HashMap<SmolStr, Value>,
793 ) -> Result<Expr, SubstitutionError> {
794 self.substitute_general::<TypedSubstitution>(definitions)
795 }
796
797 fn substitute_general<T: SubstitutionFunction>(
801 &self,
802 definitions: &HashMap<SmolStr, Value>,
803 ) -> Result<Expr, T::Err> {
804 match self.expr_kind() {
805 ExprKind::Lit(_) => Ok(self.clone()),
806 ExprKind::Unknown(u @ Unknown { name, .. }) => T::substitute(u, definitions.get(name)),
807 ExprKind::Var(_) => Ok(self.clone()),
808 ExprKind::Slot(_) => Ok(self.clone()),
809 ExprKind::If {
810 test_expr,
811 then_expr,
812 else_expr,
813 } => Ok(Expr::ite(
814 test_expr.substitute_general::<T>(definitions)?,
815 then_expr.substitute_general::<T>(definitions)?,
816 else_expr.substitute_general::<T>(definitions)?,
817 )),
818 ExprKind::And { left, right } => Ok(Expr::and(
819 left.substitute_general::<T>(definitions)?,
820 right.substitute_general::<T>(definitions)?,
821 )),
822 ExprKind::Or { left, right } => Ok(Expr::or(
823 left.substitute_general::<T>(definitions)?,
824 right.substitute_general::<T>(definitions)?,
825 )),
826 ExprKind::UnaryApp { op, arg } => Ok(Expr::unary_app(
827 *op,
828 arg.substitute_general::<T>(definitions)?,
829 )),
830 ExprKind::BinaryApp { op, arg1, arg2 } => Ok(Expr::binary_app(
831 *op,
832 arg1.substitute_general::<T>(definitions)?,
833 arg2.substitute_general::<T>(definitions)?,
834 )),
835 ExprKind::ExtensionFunctionApp { fn_name, args } => {
836 let args = args
837 .iter()
838 .map(|e| e.substitute_general::<T>(definitions))
839 .collect::<Result<Vec<Expr>, _>>()?;
840
841 Ok(Expr::call_extension_fn(fn_name.clone(), args))
842 }
843 ExprKind::GetAttr { expr, attr } => Ok(Expr::get_attr(
844 expr.substitute_general::<T>(definitions)?,
845 attr.clone(),
846 )),
847 ExprKind::HasAttr { expr, attr } => Ok(Expr::has_attr(
848 expr.substitute_general::<T>(definitions)?,
849 attr.clone(),
850 )),
851 ExprKind::Like { expr, pattern } => Ok(Expr::like(
852 expr.substitute_general::<T>(definitions)?,
853 pattern.clone(),
854 )),
855 ExprKind::Set(members) => {
856 let members = members
857 .iter()
858 .map(|e| e.substitute_general::<T>(definitions))
859 .collect::<Result<Vec<_>, _>>()?;
860 Ok(Expr::set(members))
861 }
862 ExprKind::Record(map) => {
863 let map = map
864 .iter()
865 .map(|(name, e)| Ok((name.clone(), e.substitute_general::<T>(definitions)?)))
866 .collect::<Result<BTreeMap<_, _>, _>>()?;
867 #[expect(
868 clippy::expect_used,
869 reason = "cannot have a duplicate key because the input was already a BTreeMap"
870 )]
871 Ok(Expr::record(map)
872 .expect("cannot have a duplicate key because the input was already a BTreeMap"))
873 }
874 ExprKind::Is { expr, entity_type } => Ok(Expr::is_entity_type(
875 expr.substitute_general::<T>(definitions)?,
876 entity_type.clone(),
877 )),
878 #[cfg(feature = "tolerant-ast")]
879 ExprKind::Error { .. } => Ok(self.clone()),
880 }
881 }
882
883 pub fn try_validate(self) -> Result<Self, ExprValidationError> {
900 for sub in self.subexpressions() {
901 if let ExprKind::ExtensionFunctionApp { fn_name, args } = sub.expr_kind() {
902 let ext_fn = Extensions::all_available().func(fn_name).map_err(|_| {
904 ExprValidationError(format!("unknown extension function `{fn_name}`"))
905 })?;
906 if ext_fn.style() == CallStyle::MethodStyle && args.is_empty() {
908 return Err(ExprValidationError(format!(
909 "method-style extension function `{fn_name}` requires a receiver argument"
910 )));
911 }
912 }
914 }
915 Ok(self)
916 }
917}
918
919trait SubstitutionFunction {
921 type Err;
923 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err>;
929}
930
931struct TypedSubstitution {}
932
933impl SubstitutionFunction for TypedSubstitution {
934 type Err = SubstitutionError;
935
936 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
937 match (substitute, &value.type_annotation) {
938 (None, _) => Ok(Expr::unknown(value.clone())),
939 (Some(v), None) => Ok(v.clone().into()),
940 (Some(v), Some(t)) => {
941 if v.type_of() == *t {
942 Ok(v.clone().into())
943 } else {
944 Err(SubstitutionError::TypeError {
945 expected: t.clone(),
946 actual: v.type_of(),
947 })
948 }
949 }
950 }
951 }
952}
953
954struct UntypedSubstitution {}
955
956impl SubstitutionFunction for UntypedSubstitution {
957 type Err = std::convert::Infallible;
958
959 fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
960 Ok(substitute
961 .map(|v| v.clone().into())
962 .unwrap_or_else(|| Expr::unknown(value.clone())))
963 }
964}
965
966impl<T: Clone> std::fmt::Display for Expr<T> {
967 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
968 write!(f, "{}", &self.clone().into_expr::<crate::est::Builder>())
972 }
973}
974
975impl<T: Clone> BoundedDisplay for Expr<T> {
976 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
977 BoundedDisplay::fmt(&self.clone().into_expr::<crate::est::Builder>(), f, n)
980 }
981}
982
983impl std::str::FromStr for Expr {
984 type Err = ParseErrors;
985
986 fn from_str(s: &str) -> Result<Expr, Self::Err> {
987 crate::parser::parse_expr(s)
988 }
989}
990
991#[derive(Debug, Clone, Diagnostic, Error)]
993pub enum SubstitutionError {
994 #[error("expected a value of type {expected}, got a value of type {actual}")]
996 TypeError {
997 expected: Type,
999 actual: Type,
1001 },
1002}
1003
1004#[derive(Hash, Debug, Clone, PartialEq, Eq)]
1006pub struct Unknown {
1007 pub name: SmolStr,
1009 pub type_annotation: Option<Type>,
1013}
1014
1015impl Unknown {
1016 pub fn new_untyped(name: impl Into<SmolStr>) -> Self {
1018 Self {
1019 name: name.into(),
1020 type_annotation: None,
1021 }
1022 }
1023
1024 pub fn new_with_type(name: impl Into<SmolStr>, ty: Type) -> Self {
1027 Self {
1028 name: name.into(),
1029 type_annotation: Some(ty),
1030 }
1031 }
1032}
1033
1034impl std::fmt::Display for Unknown {
1035 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1036 write!(
1039 f,
1040 "{}",
1041 Expr::unknown(self.clone()).into_expr::<crate::est::Builder>()
1042 )
1043 }
1044}
1045
1046#[derive(Clone, Debug)]
1049pub struct ExprBuilder<T> {
1050 source_loc: Option<Loc>,
1051 data: T,
1052}
1053
1054impl<T: Default + Clone> expr_builder::ExprBuilderInfallibleBuild for ExprBuilder<T> {}
1055
1056impl<T: Default + Clone> expr_builder::ExprBuilder for ExprBuilder<T> {
1057 type Expr = Expr<T>;
1058
1059 type Data = T;
1060
1061 type BuildError = Infallible;
1062
1063 #[cfg(feature = "tolerant-ast")]
1064 type ErrorType = ParseErrors;
1065
1066 fn loc(&self) -> Option<&Loc> {
1067 self.source_loc.as_ref()
1068 }
1069
1070 fn data(&self) -> &Self::Data {
1071 &self.data
1072 }
1073
1074 fn with_data(data: T) -> Self {
1075 Self {
1076 source_loc: None,
1077 data,
1078 }
1079 }
1080
1081 fn with_maybe_source_loc(mut self, maybe_source_loc: Option<&Loc>) -> Self {
1082 self.source_loc = maybe_source_loc.cloned();
1083 self
1084 }
1085
1086 fn val(self, v: impl Into<Literal>) -> Expr<T> {
1090 self.with_expr_kind(ExprKind::Lit(v.into()))
1091 }
1092
1093 fn unknown(self, u: Unknown) -> Expr<T> {
1095 self.with_expr_kind(ExprKind::Unknown(u))
1096 }
1097
1098 fn var(self, v: Var) -> Expr<T> {
1100 self.with_expr_kind(ExprKind::Var(v))
1101 }
1102
1103 fn slot(self, s: SlotId) -> Expr<T> {
1105 self.with_expr_kind(ExprKind::Slot(s))
1106 }
1107
1108 fn ite_arc(
1112 self,
1113 test_expr: Arc<Expr<T>>,
1114 then_expr: Arc<Expr<T>>,
1115 else_expr: Arc<Expr<T>>,
1116 ) -> Expr<T> {
1117 self.with_expr_kind(ExprKind::If {
1118 test_expr,
1119 then_expr,
1120 else_expr,
1121 })
1122 }
1123
1124 fn not(self, e: Expr<T>) -> Expr<T> {
1126 self.with_expr_kind(ExprKind::UnaryApp {
1127 op: UnaryOp::Not,
1128 arg: Arc::new(e),
1129 })
1130 }
1131
1132 fn is_eq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1134 self.with_expr_kind(ExprKind::BinaryApp {
1135 op: BinaryOp::Eq,
1136 arg1: Arc::new(e1),
1137 arg2: Arc::new(e2),
1138 })
1139 }
1140
1141 fn and(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1143 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1144 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1145 ExprKind::Lit(Literal::Bool(*b1 && *b2))
1146 }
1147 _ => ExprKind::And {
1148 left: Arc::new(e1),
1149 right: Arc::new(e2),
1150 },
1151 })
1152 }
1153
1154 fn or(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1156 self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1157 (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1158 ExprKind::Lit(Literal::Bool(*b1 || *b2))
1159 }
1160
1161 _ => ExprKind::Or {
1162 left: Arc::new(e1),
1163 right: Arc::new(e2),
1164 },
1165 })
1166 }
1167
1168 fn less(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1170 self.with_expr_kind(ExprKind::BinaryApp {
1171 op: BinaryOp::Less,
1172 arg1: Arc::new(e1),
1173 arg2: Arc::new(e2),
1174 })
1175 }
1176
1177 fn lesseq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1179 self.with_expr_kind(ExprKind::BinaryApp {
1180 op: BinaryOp::LessEq,
1181 arg1: Arc::new(e1),
1182 arg2: Arc::new(e2),
1183 })
1184 }
1185
1186 fn add(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1188 self.with_expr_kind(ExprKind::BinaryApp {
1189 op: BinaryOp::Add,
1190 arg1: Arc::new(e1),
1191 arg2: Arc::new(e2),
1192 })
1193 }
1194
1195 fn sub(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1197 self.with_expr_kind(ExprKind::BinaryApp {
1198 op: BinaryOp::Sub,
1199 arg1: Arc::new(e1),
1200 arg2: Arc::new(e2),
1201 })
1202 }
1203
1204 fn mul(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1206 self.with_expr_kind(ExprKind::BinaryApp {
1207 op: BinaryOp::Mul,
1208 arg1: Arc::new(e1),
1209 arg2: Arc::new(e2),
1210 })
1211 }
1212
1213 fn neg(self, e: Expr<T>) -> Expr<T> {
1215 self.with_expr_kind(ExprKind::UnaryApp {
1216 op: UnaryOp::Neg,
1217 arg: Arc::new(e),
1218 })
1219 }
1220
1221 fn is_in_arc(self, arg1: Arc<Expr<T>>, arg2: Arc<Expr<T>>) -> Expr<T> {
1225 self.with_expr_kind(ExprKind::BinaryApp {
1226 op: BinaryOp::In,
1227 arg1,
1228 arg2,
1229 })
1230 }
1231
1232 fn contains(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1235 self.with_expr_kind(ExprKind::BinaryApp {
1236 op: BinaryOp::Contains,
1237 arg1: Arc::new(e1),
1238 arg2: Arc::new(e2),
1239 })
1240 }
1241
1242 fn contains_all(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1244 self.with_expr_kind(ExprKind::BinaryApp {
1245 op: BinaryOp::ContainsAll,
1246 arg1: Arc::new(e1),
1247 arg2: Arc::new(e2),
1248 })
1249 }
1250
1251 fn contains_any(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1253 self.with_expr_kind(ExprKind::BinaryApp {
1254 op: BinaryOp::ContainsAny,
1255 arg1: Arc::new(e1),
1256 arg2: Arc::new(e2),
1257 })
1258 }
1259
1260 fn is_empty(self, expr: Expr<T>) -> Expr<T> {
1262 self.with_expr_kind(ExprKind::UnaryApp {
1263 op: UnaryOp::IsEmpty,
1264 arg: Arc::new(expr),
1265 })
1266 }
1267
1268 fn get_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1271 self.with_expr_kind(ExprKind::BinaryApp {
1272 op: BinaryOp::GetTag,
1273 arg1: Arc::new(expr),
1274 arg2: Arc::new(tag),
1275 })
1276 }
1277
1278 fn has_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1281 self.with_expr_kind(ExprKind::BinaryApp {
1282 op: BinaryOp::HasTag,
1283 arg1: Arc::new(expr),
1284 arg2: Arc::new(tag),
1285 })
1286 }
1287
1288 fn set(self, exprs: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1290 self.with_expr_kind(ExprKind::Set(Arc::new(exprs.into_iter().collect())))
1291 }
1292
1293 fn record(
1295 self,
1296 pairs: impl IntoIterator<Item = (SmolStr, Expr<T>)>,
1297 ) -> Result<Expr<T>, ExpressionConstructionError> {
1298 let mut map = BTreeMap::new();
1299 for (k, v) in pairs {
1300 match map.entry(k) {
1301 btree_map::Entry::Occupied(oentry) => {
1302 return Err(expression_construction_errors::DuplicateKeyError {
1303 key: oentry.key().clone(),
1304 context: "in record literal",
1305 }
1306 .into());
1307 }
1308 btree_map::Entry::Vacant(ventry) => {
1309 ventry.insert(v);
1310 }
1311 }
1312 }
1313 Ok(self.with_expr_kind(ExprKind::Record(Arc::new(map))))
1314 }
1315
1316 fn call_extension_fn(
1319 self,
1320 fn_name: Name,
1321 args: impl IntoIterator<Item = Expr<T>>,
1322 ) -> Result<Expr<T>, Infallible> {
1323 Ok(self.with_expr_kind(ExprKind::ExtensionFunctionApp {
1324 fn_name,
1325 args: Arc::new(args.into_iter().collect()),
1326 }))
1327 }
1328
1329 fn unary_app(self, op: impl Into<UnaryOp>, arg: Expr<T>) -> Expr<T> {
1332 self.with_expr_kind(ExprKind::UnaryApp {
1333 op: op.into(),
1334 arg: Arc::new(arg),
1335 })
1336 }
1337
1338 fn binary_app(self, op: impl Into<BinaryOp>, arg1: Expr<T>, arg2: Expr<T>) -> Expr<T> {
1341 self.with_expr_kind(ExprKind::BinaryApp {
1342 op: op.into(),
1343 arg1: Arc::new(arg1),
1344 arg2: Arc::new(arg2),
1345 })
1346 }
1347
1348 fn get_attr_arc(self, expr: Arc<Expr<T>>, attr: SmolStr) -> Expr<T> {
1352 self.with_expr_kind(ExprKind::GetAttr { expr, attr })
1353 }
1354
1355 fn has_attr_arc(self, expr: Arc<Expr<T>>, attr: SmolStr) -> Expr<T> {
1360 self.with_expr_kind(ExprKind::HasAttr { expr, attr })
1361 }
1362
1363 fn like(self, expr: Expr<T>, pattern: Pattern) -> Expr<T> {
1367 self.with_expr_kind(ExprKind::Like {
1368 expr: Arc::new(expr),
1369 pattern,
1370 })
1371 }
1372
1373 fn is_entity_type_arc(self, expr: Arc<Expr<T>>, entity_type: EntityType) -> Expr<T> {
1375 self.with_expr_kind(ExprKind::Is { expr, entity_type })
1376 }
1377
1378 #[cfg(feature = "tolerant-ast")]
1380 fn error(self, parse_errors: ParseErrors) -> Result<Self::Expr, Self::ErrorType> {
1381 Err(parse_errors)
1382 }
1383}
1384
1385impl<T> ExprBuilder<T> {
1386 pub fn with_expr_kind(self, expr_kind: ExprKind<T>) -> Expr<T> {
1389 Expr::new(expr_kind, self.source_loc, self.data)
1390 }
1391
1392 pub fn record_arc(self, map: Arc<BTreeMap<SmolStr, Expr<T>>>) -> Expr<T> {
1399 self.with_expr_kind(ExprKind::Record(map))
1400 }
1401}
1402
1403impl<T: Clone + Default> ExprBuilder<T> {
1404 pub fn with_same_source_loc<U>(self, expr: &Expr<U>) -> Self {
1408 self.with_maybe_source_loc(expr.source_loc.as_ref())
1409 }
1410}
1411
1412#[derive(Error, Debug, Clone, Diagnostic, PartialEq, Eq)]
1414#[error("invalid expression: {0}")]
1415pub struct ExprValidationError(String);
1416
1417#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1423pub enum ExpressionConstructionError {
1424 #[error(transparent)]
1426 #[diagnostic(transparent)]
1427 DuplicateKey(#[from] expression_construction_errors::DuplicateKeyError),
1428}
1429
1430pub mod expression_construction_errors {
1432 use miette::Diagnostic;
1433 use smol_str::SmolStr;
1434 use thiserror::Error;
1435
1436 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1442 #[error("duplicate key `{key}` {context}")]
1443 pub struct DuplicateKeyError {
1444 pub(crate) key: SmolStr,
1446 pub(crate) context: &'static str,
1448 }
1449
1450 impl DuplicateKeyError {
1451 pub fn key(&self) -> &str {
1453 &self.key
1454 }
1455
1456 pub(crate) fn with_context(self, context: &'static str) -> Self {
1458 Self { context, ..self }
1459 }
1460 }
1461}
1462
1463#[derive(Debug, Clone)]
1467pub struct ExprShapeOnly<'a, T: Clone = ()>(Cow<'a, Expr<T>>);
1468
1469impl<'a, T: Clone> ExprShapeOnly<'a, T> {
1470 pub fn new_from_borrowed(e: &'a Expr<T>) -> ExprShapeOnly<'a, T> {
1474 ExprShapeOnly(Cow::Borrowed(e))
1475 }
1476
1477 pub fn new_from_owned(e: Expr<T>) -> ExprShapeOnly<'a, T> {
1481 ExprShapeOnly(Cow::Owned(e))
1482 }
1483}
1484
1485impl<T: Clone> PartialEq for ExprShapeOnly<'_, T> {
1486 fn eq(&self, other: &Self) -> bool {
1487 self.0.eq_shape(&other.0)
1488 }
1489}
1490
1491impl<T: Clone> Eq for ExprShapeOnly<'_, T> {}
1492
1493impl<T: Clone> Hash for ExprShapeOnly<'_, T> {
1494 fn hash<H: Hasher>(&self, state: &mut H) {
1495 self.0.hash_shape(state);
1496 }
1497}
1498
1499impl<T: Clone> PartialOrd for ExprShapeOnly<'_, T> {
1500 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1501 Some(self.cmp(other))
1502 }
1503}
1504
1505impl<T: Clone> Ord for ExprShapeOnly<'_, T> {
1506 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1507 self.0.cmp_shape(&other.0)
1508 }
1509}
1510
1511impl<T> Expr<T> {
1512 pub fn eq_shape<U>(&self, other: &Expr<U>) -> bool {
1519 use ExprKind::*;
1520 match (self.expr_kind(), other.expr_kind()) {
1521 (Lit(lit), Lit(lit1)) => lit == lit1,
1522 (Var(v), Var(v1)) => v == v1,
1523 (Slot(s), Slot(s1)) => s == s1,
1524 (
1525 Unknown(self::Unknown {
1526 name: name1,
1527 type_annotation: ta_1,
1528 }),
1529 Unknown(self::Unknown {
1530 name: name2,
1531 type_annotation: ta_2,
1532 }),
1533 ) => (name1 == name2) && (ta_1 == ta_2),
1534 (
1535 If {
1536 test_expr,
1537 then_expr,
1538 else_expr,
1539 },
1540 If {
1541 test_expr: test_expr1,
1542 then_expr: then_expr1,
1543 else_expr: else_expr1,
1544 },
1545 ) => {
1546 test_expr.eq_shape(test_expr1)
1547 && then_expr.eq_shape(then_expr1)
1548 && else_expr.eq_shape(else_expr1)
1549 }
1550 (
1551 And { left, right },
1552 And {
1553 left: left1,
1554 right: right1,
1555 },
1556 )
1557 | (
1558 Or { left, right },
1559 Or {
1560 left: left1,
1561 right: right1,
1562 },
1563 ) => left.eq_shape(left1) && right.eq_shape(right1),
1564 (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1565 op == op1 && arg.eq_shape(arg1)
1566 }
1567 (
1568 BinaryApp { op, arg1, arg2 },
1569 BinaryApp {
1570 op: op1,
1571 arg1: arg11,
1572 arg2: arg21,
1573 },
1574 ) => op == op1 && arg1.eq_shape(arg11) && arg2.eq_shape(arg21),
1575 (
1576 ExtensionFunctionApp { fn_name, args },
1577 ExtensionFunctionApp {
1578 fn_name: fn_name1,
1579 args: args1,
1580 },
1581 ) => {
1582 fn_name == fn_name1
1583 && args.len() == args1.len()
1584 && args.iter().zip(args1.iter()).all(|(a, a1)| a.eq_shape(a1))
1585 }
1586 (
1587 GetAttr { expr, attr },
1588 GetAttr {
1589 expr: expr1,
1590 attr: attr1,
1591 },
1592 )
1593 | (
1594 HasAttr { expr, attr },
1595 HasAttr {
1596 expr: expr1,
1597 attr: attr1,
1598 },
1599 ) => attr == attr1 && expr.eq_shape(expr1),
1600 (
1601 Like { expr, pattern },
1602 Like {
1603 expr: expr1,
1604 pattern: pattern1,
1605 },
1606 ) => pattern == pattern1 && expr.eq_shape(expr1),
1607 (Set(elems), Set(elems1)) => {
1608 elems.len() == elems1.len()
1609 && elems
1610 .iter()
1611 .zip(elems1.iter())
1612 .all(|(e, e1)| e.eq_shape(e1))
1613 }
1614 (Record(map), Record(map1)) => {
1615 map.len() == map1.len()
1616 && map
1617 .iter()
1618 .zip(map1.iter()) .all(|((a, e), (a1, e1))| a == a1 && e.eq_shape(e1))
1620 }
1621 (
1622 Is { expr, entity_type },
1623 Is {
1624 expr: expr1,
1625 entity_type: entity_type1,
1626 },
1627 ) => entity_type == entity_type1 && expr.eq_shape(expr1),
1628 _ => false,
1629 }
1630 }
1631
1632 pub fn hash_shape<H>(&self, state: &mut H)
1636 where
1637 H: Hasher,
1638 {
1639 mem::discriminant(self).hash(state);
1640 match self.expr_kind() {
1641 ExprKind::Lit(lit) => lit.hash(state),
1642 ExprKind::Var(v) => v.hash(state),
1643 ExprKind::Slot(s) => s.hash(state),
1644 ExprKind::Unknown(u) => u.hash(state),
1645 ExprKind::If {
1646 test_expr,
1647 then_expr,
1648 else_expr,
1649 } => {
1650 test_expr.hash_shape(state);
1651 then_expr.hash_shape(state);
1652 else_expr.hash_shape(state);
1653 }
1654 ExprKind::And { left, right } => {
1655 left.hash_shape(state);
1656 right.hash_shape(state);
1657 }
1658 ExprKind::Or { left, right } => {
1659 left.hash_shape(state);
1660 right.hash_shape(state);
1661 }
1662 ExprKind::UnaryApp { op, arg } => {
1663 op.hash(state);
1664 arg.hash_shape(state);
1665 }
1666 ExprKind::BinaryApp { op, arg1, arg2 } => {
1667 op.hash(state);
1668 arg1.hash_shape(state);
1669 arg2.hash_shape(state);
1670 }
1671 ExprKind::ExtensionFunctionApp { fn_name, args } => {
1672 fn_name.hash(state);
1673 state.write_usize(args.len());
1674 args.iter().for_each(|a| {
1675 a.hash_shape(state);
1676 });
1677 }
1678 ExprKind::GetAttr { expr, attr } => {
1679 expr.hash_shape(state);
1680 attr.hash(state);
1681 }
1682 ExprKind::HasAttr { expr, attr } => {
1683 expr.hash_shape(state);
1684 attr.hash(state);
1685 }
1686 ExprKind::Like { expr, pattern } => {
1687 expr.hash_shape(state);
1688 pattern.hash(state);
1689 }
1690 ExprKind::Set(elems) => {
1691 state.write_usize(elems.len());
1692 elems.iter().for_each(|e| {
1693 e.hash_shape(state);
1694 })
1695 }
1696 ExprKind::Record(map) => {
1697 state.write_usize(map.len());
1698 map.iter().for_each(|(s, a)| {
1699 s.hash(state);
1700 a.hash_shape(state);
1701 });
1702 }
1703 ExprKind::Is { expr, entity_type } => {
1704 expr.hash_shape(state);
1705 entity_type.hash(state);
1706 }
1707 #[cfg(feature = "tolerant-ast")]
1708 ExprKind::Error { error_kind, .. } => error_kind.hash(state),
1709 }
1710 }
1711
1712 pub fn cmp_shape(&self, other: &Expr<T>) -> std::cmp::Ordering {
1716 let self_kind = self.expr_kind();
1718 let other_kind = other.expr_kind();
1719 if std::mem::discriminant(self_kind) != std::mem::discriminant(other_kind) {
1720 return self_kind.variant_order().cmp(&other_kind.variant_order());
1721 }
1722
1723 use ExprKind::*;
1725 match (self_kind, other_kind) {
1726 (Lit(lit), Lit(lit1)) => lit.cmp(lit1),
1727 (Var(v), Var(v1)) => v.cmp(v1),
1728 (Slot(s), Slot(s1)) => s.cmp(s1),
1729 (
1730 Unknown(self::Unknown {
1731 name: name1,
1732 type_annotation: ta_1,
1733 }),
1734 Unknown(self::Unknown {
1735 name: name2,
1736 type_annotation: ta_2,
1737 }),
1738 ) => name1.cmp(name2).then_with(|| ta_1.cmp(ta_2)),
1739 (
1740 If {
1741 test_expr,
1742 then_expr,
1743 else_expr,
1744 },
1745 If {
1746 test_expr: test_expr1,
1747 then_expr: then_expr1,
1748 else_expr: else_expr1,
1749 },
1750 ) => test_expr
1751 .cmp_shape(test_expr1)
1752 .then_with(|| then_expr.cmp_shape(then_expr1))
1753 .then_with(|| else_expr.cmp_shape(else_expr1)),
1754 (
1755 And { left, right },
1756 And {
1757 left: left1,
1758 right: right1,
1759 },
1760 ) => left.cmp_shape(left1).then_with(|| right.cmp_shape(right1)),
1761 (
1762 Or { left, right },
1763 Or {
1764 left: left1,
1765 right: right1,
1766 },
1767 ) => left.cmp_shape(left1).then_with(|| right.cmp_shape(right1)),
1768 (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1769 op.cmp(op1).then_with(|| arg.cmp_shape(arg1))
1770 }
1771 (
1772 BinaryApp { op, arg1, arg2 },
1773 BinaryApp {
1774 op: op1,
1775 arg1: arg11,
1776 arg2: arg21,
1777 },
1778 ) => op
1779 .cmp(op1)
1780 .then_with(|| arg1.cmp_shape(arg11))
1781 .then_with(|| arg2.cmp_shape(arg21)),
1782 (
1783 ExtensionFunctionApp { fn_name, args },
1784 ExtensionFunctionApp {
1785 fn_name: fn_name1,
1786 args: args1,
1787 },
1788 ) => fn_name.cmp(fn_name1).then_with(|| {
1789 args.len().cmp(&args1.len()).then_with(|| {
1790 for (a, a1) in args.iter().zip(args1.iter()) {
1791 match a.cmp_shape(a1) {
1792 std::cmp::Ordering::Equal => continue,
1793 other => return other,
1794 }
1795 }
1796 std::cmp::Ordering::Equal
1797 })
1798 }),
1799 (
1800 GetAttr { expr, attr },
1801 GetAttr {
1802 expr: expr1,
1803 attr: attr1,
1804 },
1805 ) => attr.cmp(attr1).then_with(|| expr.cmp_shape(expr1)),
1806 (
1807 HasAttr { expr, attr },
1808 HasAttr {
1809 expr: expr1,
1810 attr: attr1,
1811 },
1812 ) => attr.cmp(attr1).then_with(|| expr.cmp_shape(expr1)),
1813 (
1814 Like { expr, pattern },
1815 Like {
1816 expr: expr1,
1817 pattern: pattern1,
1818 },
1819 ) => pattern.cmp(pattern1).then_with(|| expr.cmp_shape(expr1)),
1820 (Set(elems), Set(elems1)) => elems.len().cmp(&elems1.len()).then_with(|| {
1821 for (e, e1) in elems.iter().zip(elems1.iter()) {
1822 match e.cmp_shape(e1) {
1823 std::cmp::Ordering::Equal => continue,
1824 other => return other,
1825 }
1826 }
1827 std::cmp::Ordering::Equal
1828 }),
1829 (Record(map), Record(map1)) => map.len().cmp(&map1.len()).then_with(|| {
1830 for ((a, e), (a1, e1)) in map.iter().zip(map1.iter()) {
1831 match a.cmp(a1).then_with(|| e.cmp_shape(e1)) {
1832 std::cmp::Ordering::Equal => continue,
1833 other => return other,
1834 }
1835 }
1836 std::cmp::Ordering::Equal
1837 }),
1838 (
1839 Is { expr, entity_type },
1840 Is {
1841 expr: expr1,
1842 entity_type: entity_type1,
1843 },
1844 ) => entity_type
1845 .cmp(entity_type1)
1846 .then_with(|| expr.cmp_shape(expr1)),
1847 #[cfg(feature = "tolerant-ast")]
1848 (
1849 Error { error_kind },
1850 Error {
1851 error_kind: error_kind1,
1852 },
1853 ) => error_kind.cmp(error_kind1),
1854 #[expect(
1855 clippy::unreachable,
1856 reason = "This should never be reached since we compare variants first"
1857 )]
1858 _ => unreachable!(
1859 "Different variants should have been handled by variant_order comparison"
1860 ),
1861 }
1862 }
1863}
1864
1865#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
1867#[serde(rename_all = "camelCase")]
1868#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1869#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
1870#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
1871pub enum Var {
1872 Principal,
1874 Action,
1876 Resource,
1878 Context,
1880}
1881
1882impl From<PrincipalOrResource> for Var {
1883 fn from(v: PrincipalOrResource) -> Self {
1884 match v {
1885 PrincipalOrResource::Principal => Var::Principal,
1886 PrincipalOrResource::Resource => Var::Resource,
1887 }
1888 }
1889}
1890
1891#[expect(
1892 clippy::fallible_impl_from,
1893 reason = "Tested by `test::all_vars_are_ids`. Never panics"
1894)]
1895impl From<Var> for Id {
1896 fn from(var: Var) -> Self {
1897 #[expect(
1898 clippy::unwrap_used,
1899 reason = "`Var` is a simple enum and all vars are formatted as valid `Id`. Tested by `test::all_vars_are_ids`"
1900 )]
1901 format!("{var}").parse().unwrap()
1902 }
1903}
1904
1905#[expect(
1906 clippy::fallible_impl_from,
1907 reason = "Tested by `test::all_vars_are_ids`. Never panics"
1908)]
1909impl From<Var> for UnreservedId {
1910 fn from(var: Var) -> Self {
1911 #[expect(
1912 clippy::unwrap_used,
1913 reason = "`Var` is a simple enum and all vars are formatted as valid `UnreservedId`. Tested by `test::all_vars_are_ids`"
1914 )]
1915 Id::from(var).try_into().unwrap()
1916 }
1917}
1918
1919impl std::fmt::Display for Var {
1920 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1921 match self {
1922 Self::Principal => write!(f, "principal"),
1923 Self::Action => write!(f, "action"),
1924 Self::Resource => write!(f, "resource"),
1925 Self::Context => write!(f, "context"),
1926 }
1927 }
1928}
1929
1930#[cfg(test)]
1931mod test {
1932 use cool_asserts::assert_matches;
1933 use itertools::Itertools;
1934 use smol_str::ToSmolStr;
1935 use std::collections::{hash_map::DefaultHasher, HashSet};
1936
1937 use crate::expr_builder::ExprBuilder as _;
1938
1939 use super::*;
1940
1941 pub fn all_vars() -> impl Iterator<Item = Var> {
1942 [Var::Principal, Var::Action, Var::Resource, Var::Context].into_iter()
1943 }
1944
1945 #[test]
1947 fn all_vars_are_ids() {
1948 for var in all_vars() {
1949 let _id: Id = var.into();
1950 let _id: UnreservedId = var.into();
1951 }
1952 }
1953
1954 #[test]
1955 fn exprs() {
1956 assert_eq!(
1957 Expr::val(33),
1958 Expr::new(ExprKind::Lit(Literal::Long(33)), None, ())
1959 );
1960 assert_eq!(
1961 Expr::val("hello"),
1962 Expr::new(ExprKind::Lit(Literal::from("hello")), None, ())
1963 );
1964 assert_eq!(
1965 Expr::val(EntityUID::with_eid("foo")),
1966 Expr::new(
1967 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
1968 None,
1969 ()
1970 )
1971 );
1972 assert_eq!(
1973 Expr::var(Var::Principal),
1974 Expr::new(ExprKind::Var(Var::Principal), None, ())
1975 );
1976 assert_eq!(
1977 Expr::ite(Expr::val(true), Expr::val(88), Expr::val(-100)),
1978 Expr::new(
1979 ExprKind::If {
1980 test_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(true)), None, ())),
1981 then_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(88)), None, ())),
1982 else_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(-100)), None, ())),
1983 },
1984 None,
1985 ()
1986 )
1987 );
1988 assert_eq!(
1989 Expr::not(Expr::val(false)),
1990 Expr::new(
1991 ExprKind::UnaryApp {
1992 op: UnaryOp::Not,
1993 arg: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(false)), None, ())),
1994 },
1995 None,
1996 ()
1997 )
1998 );
1999 assert_eq!(
2000 Expr::get_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
2001 Expr::new(
2002 ExprKind::GetAttr {
2003 expr: Arc::new(Expr::new(
2004 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2005 None,
2006 ()
2007 )),
2008 attr: "some_attr".into()
2009 },
2010 None,
2011 ()
2012 )
2013 );
2014 assert_eq!(
2015 Expr::has_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
2016 Expr::new(
2017 ExprKind::HasAttr {
2018 expr: Arc::new(Expr::new(
2019 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2020 None,
2021 ()
2022 )),
2023 attr: "some_attr".into()
2024 },
2025 None,
2026 ()
2027 )
2028 );
2029 assert_eq!(
2030 Expr::is_entity_type(
2031 Expr::val(EntityUID::with_eid("foo")),
2032 "Type".parse().unwrap()
2033 ),
2034 Expr::new(
2035 ExprKind::Is {
2036 expr: Arc::new(Expr::new(
2037 ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2038 None,
2039 ()
2040 )),
2041 entity_type: "Type".parse().unwrap()
2042 },
2043 None,
2044 ()
2045 ),
2046 );
2047 }
2048
2049 #[test]
2050 fn like_display() {
2051 let e = Expr::like(Expr::val("a"), Pattern::from(vec![PatternElem::Char('\0')]));
2053 assert_eq!(format!("{e}"), r#""a" like "\0""#);
2054 let e = Expr::like(
2056 Expr::val("a"),
2057 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('0')]),
2058 );
2059 assert_eq!(format!("{e}"), r#""a" like "\\0""#);
2060 let e = Expr::like(
2062 Expr::val("a"),
2063 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Wildcard]),
2064 );
2065 assert_eq!(format!("{e}"), r#""a" like "\\*""#);
2066 let e = Expr::like(
2068 Expr::val("a"),
2069 Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('*')]),
2070 );
2071 assert_eq!(format!("{e}"), r#""a" like "\\\*""#);
2072 }
2073
2074 #[test]
2075 fn has_display() {
2076 let e = Expr::has_attr(Expr::val("a"), "\0".into());
2078 assert_eq!(format!("{e}"), r#""a" has "\0""#);
2079 let e = Expr::has_attr(Expr::val("a"), r"\".into());
2081 assert_eq!(format!("{e}"), r#""a" has "\\""#);
2082 }
2083
2084 #[test]
2085 fn slot_display() {
2086 let e = Expr::slot(SlotId::principal());
2087 assert_eq!(format!("{e}"), "?principal");
2088 let e = Expr::slot(SlotId::resource());
2089 assert_eq!(format!("{e}"), "?resource");
2090 let e = Expr::val(EntityUID::with_eid("eid"));
2091 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
2092 }
2093
2094 #[test]
2095 fn simple_slots() {
2096 let e = Expr::slot(SlotId::principal());
2097 let p = SlotId::principal();
2098 let r = SlotId::resource();
2099 let set: HashSet<SlotId> = HashSet::from_iter([p]);
2100 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
2101 let e = Expr::or(
2102 Expr::slot(SlotId::principal()),
2103 Expr::ite(
2104 Expr::val(true),
2105 Expr::slot(SlotId::resource()),
2106 Expr::val(false),
2107 ),
2108 );
2109 let set: HashSet<SlotId> = HashSet::from_iter([p, r]);
2110 assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
2111 }
2112
2113 #[test]
2114 fn unknowns() {
2115 let e = Expr::ite(
2116 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
2117 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
2118 Expr::unknown(Unknown::new_untyped("c")),
2119 );
2120 let unknowns = e.unknowns().collect_vec();
2121 assert_eq!(unknowns.len(), 3);
2122 assert!(unknowns.contains(&&Unknown::new_untyped("a")));
2123 assert!(unknowns.contains(&&Unknown::new_untyped("b")));
2124 assert!(unknowns.contains(&&Unknown::new_untyped("c")));
2125 }
2126
2127 #[test]
2128 fn is_unknown() {
2129 let e = Expr::ite(
2130 Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
2131 Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
2132 Expr::unknown(Unknown::new_untyped("c")),
2133 );
2134 assert!(e.contains_unknown());
2135 let e = Expr::ite(
2136 Expr::not(Expr::val(true)),
2137 Expr::and(Expr::val(1), Expr::val(3)),
2138 Expr::val(1),
2139 );
2140 assert!(!e.contains_unknown());
2141 }
2142
2143 #[test]
2144 fn expr_with_data() {
2145 let e = ExprBuilder::with_data("data").val(1);
2146 assert_eq!(e.into_data(), "data");
2147 }
2148
2149 #[test]
2150 fn expr_shape_only_eq() {
2151 let temp = ExprBuilder::with_data(1).val(1);
2152 let exprs = &[
2153 (ExprBuilder::with_data(1).val(33), Expr::val(33)),
2154 (ExprBuilder::with_data(1).val(true), Expr::val(true)),
2155 (
2156 ExprBuilder::with_data(1).var(Var::Principal),
2157 Expr::var(Var::Principal),
2158 ),
2159 (
2160 ExprBuilder::with_data(1).slot(SlotId::principal()),
2161 Expr::slot(SlotId::principal()),
2162 ),
2163 (
2164 ExprBuilder::with_data(1).ite(temp.clone(), temp.clone(), temp.clone()),
2165 Expr::ite(Expr::val(1), Expr::val(1), Expr::val(1)),
2166 ),
2167 (
2168 ExprBuilder::with_data(1).not(temp.clone()),
2169 Expr::not(Expr::val(1)),
2170 ),
2171 (
2172 ExprBuilder::with_data(1).is_eq(temp.clone(), temp.clone()),
2173 Expr::is_eq(Expr::val(1), Expr::val(1)),
2174 ),
2175 (
2176 ExprBuilder::with_data(1).and(temp.clone(), temp.clone()),
2177 Expr::and(Expr::val(1), Expr::val(1)),
2178 ),
2179 (
2180 ExprBuilder::with_data(1).or(temp.clone(), temp.clone()),
2181 Expr::or(Expr::val(1), Expr::val(1)),
2182 ),
2183 (
2184 ExprBuilder::with_data(1).less(temp.clone(), temp.clone()),
2185 Expr::less(Expr::val(1), Expr::val(1)),
2186 ),
2187 (
2188 ExprBuilder::with_data(1).lesseq(temp.clone(), temp.clone()),
2189 Expr::lesseq(Expr::val(1), Expr::val(1)),
2190 ),
2191 (
2192 ExprBuilder::with_data(1).greater(temp.clone(), temp.clone()),
2193 Expr::greater(Expr::val(1), Expr::val(1)),
2194 ),
2195 (
2196 ExprBuilder::with_data(1).greatereq(temp.clone(), temp.clone()),
2197 Expr::greatereq(Expr::val(1), Expr::val(1)),
2198 ),
2199 (
2200 ExprBuilder::with_data(1).add(temp.clone(), temp.clone()),
2201 Expr::add(Expr::val(1), Expr::val(1)),
2202 ),
2203 (
2204 ExprBuilder::with_data(1).sub(temp.clone(), temp.clone()),
2205 Expr::sub(Expr::val(1), Expr::val(1)),
2206 ),
2207 (
2208 ExprBuilder::with_data(1).mul(temp.clone(), temp.clone()),
2209 Expr::mul(Expr::val(1), Expr::val(1)),
2210 ),
2211 (
2212 ExprBuilder::with_data(1).neg(temp.clone()),
2213 Expr::neg(Expr::val(1)),
2214 ),
2215 (
2216 ExprBuilder::with_data(1).is_in(temp.clone(), temp.clone()),
2217 Expr::is_in(Expr::val(1), Expr::val(1)),
2218 ),
2219 (
2220 ExprBuilder::with_data(1).contains(temp.clone(), temp.clone()),
2221 Expr::contains(Expr::val(1), Expr::val(1)),
2222 ),
2223 (
2224 ExprBuilder::with_data(1).contains_all(temp.clone(), temp.clone()),
2225 Expr::contains_all(Expr::val(1), Expr::val(1)),
2226 ),
2227 (
2228 ExprBuilder::with_data(1).contains_any(temp.clone(), temp.clone()),
2229 Expr::contains_any(Expr::val(1), Expr::val(1)),
2230 ),
2231 (
2232 ExprBuilder::with_data(1).is_empty(temp.clone()),
2233 Expr::is_empty(Expr::val(1)),
2234 ),
2235 (
2236 ExprBuilder::with_data(1).set([temp.clone()]),
2237 Expr::set([Expr::val(1)]),
2238 ),
2239 (
2240 ExprBuilder::with_data(1)
2241 .record([("foo".into(), temp.clone())])
2242 .unwrap(),
2243 Expr::record([("foo".into(), Expr::val(1))]).unwrap(),
2244 ),
2245 (
2246 ExprBuilder::with_data(1)
2247 .call_extension_fn("foo".parse().unwrap(), vec![temp.clone()])
2248 .unwrap_infallible(),
2249 Expr::call_extension_fn("foo".parse().unwrap(), vec![Expr::val(1)]),
2250 ),
2251 (
2252 ExprBuilder::with_data(1).get_attr(temp.clone(), "foo".into()),
2253 Expr::get_attr(Expr::val(1), "foo".into()),
2254 ),
2255 (
2256 ExprBuilder::with_data(1).has_attr(temp.clone(), "foo".into()),
2257 Expr::has_attr(Expr::val(1), "foo".into()),
2258 ),
2259 (
2260 ExprBuilder::with_data(1)
2261 .like(temp.clone(), Pattern::from(vec![PatternElem::Wildcard])),
2262 Expr::like(Expr::val(1), Pattern::from(vec![PatternElem::Wildcard])),
2263 ),
2264 (
2265 ExprBuilder::with_data(1).is_entity_type(temp, "T".parse().unwrap()),
2266 Expr::is_entity_type(Expr::val(1), "T".parse().unwrap()),
2267 ),
2268 ];
2269
2270 for (e0, e1) in exprs {
2271 assert!(e0.eq_shape(e0));
2272 assert!(e1.eq_shape(e1));
2273 assert!(e0.eq_shape(e1));
2274 assert!(e1.eq_shape(e0));
2275
2276 let mut hasher0 = DefaultHasher::new();
2277 e0.hash_shape(&mut hasher0);
2278 let hash0 = hasher0.finish();
2279
2280 let mut hasher1 = DefaultHasher::new();
2281 e1.hash_shape(&mut hasher1);
2282 let hash1 = hasher1.finish();
2283
2284 assert_eq!(hash0, hash1);
2285 }
2286 }
2287
2288 #[test]
2289 fn expr_shape_only_not_eq() {
2290 let expr1 = ExprBuilder::with_data(1).val(1);
2291 let expr2 = ExprBuilder::with_data(1).val(2);
2292 assert_ne!(
2293 ExprShapeOnly::new_from_borrowed(&expr1),
2294 ExprShapeOnly::new_from_borrowed(&expr2)
2295 );
2296 }
2297
2298 #[test]
2299 fn expr_shape_only_set_prefix_ne() {
2300 let e1 = ExprShapeOnly::new_from_owned(Expr::set([]));
2301 let e2 = ExprShapeOnly::new_from_owned(Expr::set([Expr::val(1)]));
2302 let e3 = ExprShapeOnly::new_from_owned(Expr::set([Expr::val(1), Expr::val(2)]));
2303
2304 assert_ne!(e1, e2);
2305 assert_ne!(e1, e3);
2306 assert_ne!(e2, e1);
2307 assert_ne!(e2, e3);
2308 assert_ne!(e3, e1);
2309 assert_ne!(e2, e1);
2310 }
2311
2312 #[test]
2313 fn expr_shape_only_ext_fn_arg_prefix_ne() {
2314 let e1 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2315 "decimal".parse().unwrap(),
2316 vec![],
2317 ));
2318 let e2 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2319 "decimal".parse().unwrap(),
2320 vec![Expr::val("0.0")],
2321 ));
2322 let e3 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2323 "decimal".parse().unwrap(),
2324 vec![Expr::val("0.0"), Expr::val("0.0")],
2325 ));
2326
2327 assert_ne!(e1, e2);
2328 assert_ne!(e1, e3);
2329 assert_ne!(e2, e1);
2330 assert_ne!(e2, e3);
2331 assert_ne!(e3, e1);
2332 assert_ne!(e2, e1);
2333 }
2334
2335 #[test]
2336 fn expr_shape_only_record_attr_prefix_ne() {
2337 let e1 = ExprShapeOnly::new_from_owned(Expr::record([]).unwrap());
2338 let e2 = ExprShapeOnly::new_from_owned(
2339 Expr::record([("a".to_smolstr(), Expr::val(1))]).unwrap(),
2340 );
2341 let e3 = ExprShapeOnly::new_from_owned(
2342 Expr::record([
2343 ("a".to_smolstr(), Expr::val(1)),
2344 ("b".to_smolstr(), Expr::val(2)),
2345 ])
2346 .unwrap(),
2347 );
2348
2349 assert_ne!(e1, e2);
2350 assert_ne!(e1, e3);
2351 assert_ne!(e2, e1);
2352 assert_ne!(e2, e3);
2353 assert_ne!(e3, e1);
2354 assert_ne!(e2, e1);
2355 }
2356
2357 #[test]
2358 fn untyped_subst_present() {
2359 let u = Unknown {
2360 name: "foo".into(),
2361 type_annotation: None,
2362 };
2363 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2364 match r {
2365 Ok(e) => assert_eq!(e, Expr::val(1)),
2366 Err(empty) => match empty {},
2367 }
2368 }
2369
2370 #[test]
2371 fn untyped_subst_present_correct_type() {
2372 let u = Unknown {
2373 name: "foo".into(),
2374 type_annotation: Some(Type::Long),
2375 };
2376 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2377 match r {
2378 Ok(e) => assert_eq!(e, Expr::val(1)),
2379 Err(empty) => match empty {},
2380 }
2381 }
2382
2383 #[test]
2384 fn untyped_subst_present_wrong_type() {
2385 let u = Unknown {
2386 name: "foo".into(),
2387 type_annotation: Some(Type::Bool),
2388 };
2389 let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2390 match r {
2391 Ok(e) => assert_eq!(e, Expr::val(1)),
2392 Err(empty) => match empty {},
2393 }
2394 }
2395
2396 #[test]
2397 fn untyped_subst_not_present() {
2398 let u = Unknown {
2399 name: "foo".into(),
2400 type_annotation: Some(Type::Bool),
2401 };
2402 let r = UntypedSubstitution::substitute(&u, None);
2403 match r {
2404 Ok(n) => assert_eq!(n, Expr::unknown(u)),
2405 Err(empty) => match empty {},
2406 }
2407 }
2408
2409 #[test]
2410 fn typed_subst_present() {
2411 let u = Unknown {
2412 name: "foo".into(),
2413 type_annotation: None,
2414 };
2415 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2416 assert_eq!(e, Expr::val(1));
2417 }
2418
2419 #[test]
2420 fn typed_subst_present_correct_type() {
2421 let u = Unknown {
2422 name: "foo".into(),
2423 type_annotation: Some(Type::Long),
2424 };
2425 let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2426 assert_eq!(e, Expr::val(1));
2427 }
2428
2429 #[test]
2430 fn typed_subst_present_wrong_type() {
2431 let u = Unknown {
2432 name: "foo".into(),
2433 type_annotation: Some(Type::Bool),
2434 };
2435 let r = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap_err();
2436 assert_matches!(
2437 r,
2438 SubstitutionError::TypeError {
2439 expected: Type::Bool,
2440 actual: Type::Long,
2441 }
2442 );
2443 }
2444
2445 #[test]
2446 fn typed_subst_not_present() {
2447 let u = Unknown {
2448 name: "foo".into(),
2449 type_annotation: None,
2450 };
2451 let r = TypedSubstitution::substitute(&u, None).unwrap();
2452 assert_eq!(r, Expr::unknown(u));
2453 }
2454}
2455
2456#[cfg(test)]
2457mod validate_test {
2458 use super::*;
2459
2460 fn ext_call(name: &str, args: Vec<Expr>) -> Expr {
2461 Expr::call_extension_fn(Name::parse_unqualified_name(name).unwrap(), args)
2462 }
2463
2464 #[test]
2465 fn valid_function_style_accepted() {
2466 assert!(ext_call("ip", vec![Expr::val("127.0.0.1")])
2467 .try_validate()
2468 .is_ok());
2469 }
2470
2471 #[test]
2472 fn valid_method_style_accepted() {
2473 let receiver = ext_call("ip", vec![Expr::val("127.0.0.1")]);
2474 assert!(ext_call("isIpv4", vec![receiver]).try_validate().is_ok());
2475 }
2476
2477 #[test]
2478 fn unknown_extension_fn_rejected() {
2479 let err = ext_call("notReal", vec![Expr::val("x")])
2480 .try_validate()
2481 .unwrap_err();
2482 assert!(
2483 err.to_string().contains("unknown extension function"),
2484 "got: {err}"
2485 );
2486 }
2487
2488 #[test]
2489 fn method_style_empty_args_rejected() {
2490 let err = ext_call("isIpv4", vec![]).try_validate().unwrap_err();
2491 assert!(
2492 err.to_string().contains("requires a receiver argument"),
2493 "got: {err}"
2494 );
2495 }
2496}