1use crate::ast::*;
18use crate::extensions::ExtensionFunctionLookupError;
19use crate::parser::Loc;
20use miette::Diagnostic;
21use nonempty::{nonempty, NonEmpty};
22use smol_str::SmolStr;
23use std::sync::Arc;
24use thiserror::Error;
25
26const TOO_MANY_ATTRS: usize = 5;
28
29#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
35pub enum EvaluationError {
36 #[error(transparent)]
39 #[diagnostic(transparent)]
40 EntityDoesNotExist(#[from] evaluation_errors::EntityDoesNotExistError),
41
42 #[error(transparent)]
45 #[diagnostic(transparent)]
46 EntityAttrDoesNotExist(#[from] evaluation_errors::EntityAttrDoesNotExistError),
47
48 #[error(transparent)]
51 #[diagnostic(transparent)]
52 RecordAttrDoesNotExist(#[from] evaluation_errors::RecordAttrDoesNotExistError),
53
54 #[error(transparent)]
56 #[diagnostic(transparent)]
57 FailedExtensionFunctionLookup(#[from] ExtensionFunctionLookupError),
58
59 #[error(transparent)]
62 #[diagnostic(transparent)]
63 TypeError(#[from] evaluation_errors::TypeError),
64
65 #[error(transparent)]
67 #[diagnostic(transparent)]
68 WrongNumArguments(#[from] evaluation_errors::WrongNumArgumentsError),
69
70 #[error(transparent)]
72 #[diagnostic(transparent)]
73 IntegerOverflow(#[from] evaluation_errors::IntegerOverflowError),
74
75 #[error(transparent)]
77 #[diagnostic(transparent)]
78 UnlinkedSlot(#[from] evaluation_errors::UnlinkedSlotError),
79
80 #[error(transparent)]
82 #[diagnostic(transparent)]
83 FailedExtensionFunctionExecution(#[from] evaluation_errors::ExtensionFunctionExecutionError),
84
85 #[error(transparent)]
89 #[diagnostic(transparent)]
90 NonValue(#[from] evaluation_errors::NonValueError),
91
92 #[cfg(feature = "tolerant-ast")]
94 #[error(transparent)]
95 #[diagnostic(transparent)]
96 ASTErrorExpr(#[from] evaluation_errors::ASTErrorExprError),
97
98 #[error(transparent)]
100 #[diagnostic(transparent)]
101 RecursionLimit(#[from] evaluation_errors::RecursionLimitError),
102}
103
104impl EvaluationError {
105 pub(crate) fn source_loc(&self) -> Option<&Loc> {
107 match self {
108 Self::EntityDoesNotExist(e) => e.source_loc.as_ref(),
109 Self::EntityAttrDoesNotExist(e) => e.source_loc.as_ref(),
110 Self::RecordAttrDoesNotExist(e) => e.source_loc.as_ref(),
111 Self::FailedExtensionFunctionLookup(e) => e.source_loc(),
112 Self::TypeError(e) => e.source_loc.as_ref(),
113 Self::WrongNumArguments(e) => e.source_loc.as_ref(),
114 Self::IntegerOverflow(e) => e.source_loc(),
115 Self::UnlinkedSlot(e) => e.source_loc.as_ref(),
116 Self::FailedExtensionFunctionExecution(e) => e.source_loc.as_ref(),
117 Self::NonValue(e) => e.source_loc.as_ref(),
118 Self::RecursionLimit(e) => e.source_loc.as_ref(),
119 #[cfg(feature = "tolerant-ast")]
120 Self::ASTErrorExpr(e) => e.source_loc.as_ref(),
121 }
122 }
123
124 pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
126 match self {
127 Self::EntityDoesNotExist(e) => {
128 Self::EntityDoesNotExist(evaluation_errors::EntityDoesNotExistError {
129 source_loc,
130 ..e
131 })
132 }
133 Self::EntityAttrDoesNotExist(e) => {
134 Self::EntityAttrDoesNotExist(evaluation_errors::EntityAttrDoesNotExistError {
135 source_loc,
136 ..e
137 })
138 }
139 Self::RecordAttrDoesNotExist(e) => {
140 Self::RecordAttrDoesNotExist(evaluation_errors::RecordAttrDoesNotExistError {
141 source_loc,
142 ..e
143 })
144 }
145 Self::FailedExtensionFunctionLookup(e) => {
146 Self::FailedExtensionFunctionLookup(e.with_maybe_source_loc(source_loc))
147 }
148 Self::TypeError(e) => Self::TypeError(evaluation_errors::TypeError { source_loc, ..e }),
149 Self::WrongNumArguments(e) => {
150 Self::WrongNumArguments(evaluation_errors::WrongNumArgumentsError {
151 source_loc,
152 ..e
153 })
154 }
155 Self::IntegerOverflow(e) => Self::IntegerOverflow(e.with_maybe_source_loc(source_loc)),
156 Self::UnlinkedSlot(e) => {
157 Self::UnlinkedSlot(evaluation_errors::UnlinkedSlotError { source_loc, ..e })
158 }
159 Self::FailedExtensionFunctionExecution(e) => Self::FailedExtensionFunctionExecution(
160 evaluation_errors::ExtensionFunctionExecutionError { source_loc, ..e },
161 ),
162 Self::NonValue(e) => {
163 Self::NonValue(evaluation_errors::NonValueError { source_loc, ..e })
164 }
165 Self::RecursionLimit(_) => {
166 Self::RecursionLimit(evaluation_errors::RecursionLimitError { source_loc })
167 }
168 #[cfg(feature = "tolerant-ast")]
169 Self::ASTErrorExpr(_) => {
170 Self::ASTErrorExpr(evaluation_errors::ASTErrorExprError { source_loc })
171 }
172 }
173 }
174
175 pub(crate) fn entity_does_not_exist(uid: Arc<EntityUID>, source_loc: Option<Loc>) -> Self {
177 evaluation_errors::EntityDoesNotExistError { uid, source_loc }.into()
178 }
179
180 pub(crate) fn entity_attr_does_not_exist<'a>(
184 entity: Arc<EntityUID>,
185 attr: SmolStr,
186 available_attrs: impl IntoIterator<Item = &'a SmolStr>,
187 does_attr_exist_as_a_tag: bool,
188 total_attrs: usize,
189 source_loc: Option<Loc>,
190 ) -> Self {
191 evaluation_errors::EntityAttrDoesNotExistError {
192 entity,
193 attr_or_tag: attr,
194 was_attr: true,
195 exists_the_other_kind: does_attr_exist_as_a_tag,
196 available_attrs_or_tags: available_attrs
197 .into_iter()
198 .take(TOO_MANY_ATTRS)
199 .cloned()
200 .collect::<Vec<_>>(),
201 total_attrs_or_tags: total_attrs,
202 source_loc,
203 }
204 .into()
205 }
206
207 pub(crate) fn entity_tag_does_not_exist<'a>(
211 entity: Arc<EntityUID>,
212 tag: SmolStr,
213 available_tags: impl IntoIterator<Item = &'a SmolStr>,
214 does_tag_exist_as_an_attr: bool,
215 total_tags: usize,
216 source_loc: Option<Loc>,
217 ) -> Self {
218 evaluation_errors::EntityAttrDoesNotExistError {
219 entity,
220 attr_or_tag: tag,
221 was_attr: false,
222 exists_the_other_kind: does_tag_exist_as_an_attr,
223 available_attrs_or_tags: available_tags
224 .into_iter()
225 .take(TOO_MANY_ATTRS)
226 .cloned()
227 .collect::<Vec<_>>(),
228 total_attrs_or_tags: total_tags,
229 source_loc,
230 }
231 .into()
232 }
233
234 pub(crate) fn record_attr_does_not_exist<'a>(
236 attr: SmolStr,
237 available_attrs: impl IntoIterator<Item = &'a SmolStr>,
238 total_attrs: usize,
239 source_loc: Option<Loc>,
240 ) -> Self {
241 evaluation_errors::RecordAttrDoesNotExistError {
242 attr,
243 available_attrs: available_attrs
244 .into_iter()
245 .take(TOO_MANY_ATTRS)
246 .cloned()
247 .collect(),
248 total_attrs,
249 source_loc,
250 }
251 .into()
252 }
253
254 pub(crate) fn type_error(expected: NonEmpty<Type>, actual: &Value) -> Self {
256 evaluation_errors::TypeError {
257 expected,
258 actual: actual.type_of(),
259 advice: None,
260 source_loc: actual.source_loc().cloned(),
261 }
262 .into()
263 }
264
265 pub(crate) fn type_error_single(expected: Type, actual: &Value) -> Self {
266 Self::type_error(nonempty![expected], actual)
267 }
268
269 pub(crate) fn type_error_with_advice(
271 expected: NonEmpty<Type>,
272 actual: &Value,
273 advice: String,
274 ) -> Self {
275 evaluation_errors::TypeError {
276 expected,
277 actual: actual.type_of(),
278 advice: Some(advice),
279 source_loc: actual.source_loc().cloned(),
280 }
281 .into()
282 }
283
284 pub(crate) fn type_error_with_advice_single(
285 expected: Type,
286 actual: &Value,
287 advice: String,
288 ) -> Self {
289 Self::type_error_with_advice(nonempty![expected], actual, advice)
290 }
291
292 pub(crate) fn wrong_num_arguments(
294 function_name: Name,
295 expected: usize,
296 actual: usize,
297 source_loc: Option<Loc>,
298 ) -> Self {
299 evaluation_errors::WrongNumArgumentsError {
300 function_name,
301 expected,
302 actual,
303 source_loc,
304 }
305 .into()
306 }
307
308 pub(crate) fn unlinked_slot(slot: SlotId, source_loc: Option<Loc>) -> Self {
310 evaluation_errors::UnlinkedSlotError { slot, source_loc }.into()
311 }
312
313 pub(crate) fn failed_extension_function_application(
315 extension_name: Name,
316 msg: String,
317 source_loc: Option<Loc>,
318 advice: Option<String>,
319 ) -> Self {
320 evaluation_errors::ExtensionFunctionExecutionError {
321 extension_name,
322 msg,
323 advice,
324 source_loc,
325 }
326 .into()
327 }
328
329 pub(crate) fn non_value(expr: Expr) -> Self {
331 let source_loc = expr.source_loc().cloned();
332 evaluation_errors::NonValueError { expr, source_loc }.into()
333 }
334
335 pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
337 evaluation_errors::RecursionLimitError { source_loc }.into()
338 }
339}
340
341pub mod evaluation_errors {
343 use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
344 use crate::parser::Loc;
345 use itertools::Itertools;
346 use miette::Diagnostic;
347 use nonempty::NonEmpty;
348 use smol_str::SmolStr;
349 use std::fmt::Write;
350 use std::sync::Arc;
351 use thiserror::Error;
352
353 use super::Name;
354
355 #[derive(Debug, PartialEq, Eq, Clone, Error)]
361 #[error("entity `{uid}` does not exist")]
362 pub struct EntityDoesNotExistError {
363 pub(crate) uid: Arc<EntityUID>,
365 pub(crate) source_loc: Option<Loc>,
367 }
368
369 impl Diagnostic for EntityDoesNotExistError {
379 impl_diagnostic_from_source_loc_opt_field!(source_loc);
380 }
381
382 #[derive(Debug, PartialEq, Eq, Clone, Error)]
389 #[error("`{entity}` does not have the {} `{attr_or_tag}`", if *.was_attr { "attribute" } else { "tag" })]
390 pub struct EntityAttrDoesNotExistError {
391 pub(crate) entity: Arc<EntityUID>,
393 pub(crate) attr_or_tag: SmolStr,
395 pub(crate) was_attr: bool,
397 pub(crate) exists_the_other_kind: bool,
400 pub(crate) available_attrs_or_tags: Vec<SmolStr>,
402 pub(crate) total_attrs_or_tags: usize,
404 pub(crate) source_loc: Option<Loc>,
406 }
407
408 impl Diagnostic for EntityAttrDoesNotExistError {
409 impl_diagnostic_from_source_loc_opt_field!(source_loc);
410
411 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
412 let mut help_text = if self.available_attrs_or_tags.is_empty() {
413 format!(
414 "`{}` does not have any {}",
415 &self.entity,
416 if self.was_attr { "attributes" } else { "tags" }
417 )
418 } else if self.available_attrs_or_tags.len() == self.total_attrs_or_tags {
419 format!(
420 "available {}: [{}]",
421 if self.was_attr { "attributes" } else { "tags" },
422 self.available_attrs_or_tags.iter().join(",")
423 )
424 } else {
425 format!(
426 "available {}: [{}, ... ({} more attributes) ]",
427 if self.was_attr { "attributes" } else { "tags" },
428 self.available_attrs_or_tags.iter().join(","),
429 self.total_attrs_or_tags - self.available_attrs_or_tags.len()
430 )
431 };
432 if self.exists_the_other_kind {
433 #[allow(clippy::unwrap_used)]
435 write!(
436 &mut help_text,
437 "; note that {} (not {}) named `{}` does exist",
438 if self.was_attr {
439 "a tag"
440 } else {
441 "an attribute"
442 },
443 if self.was_attr {
444 "an attribute"
445 } else {
446 "a tag"
447 },
448 self.attr_or_tag,
449 )
450 .unwrap()
451 }
452 Some(Box::new(help_text))
453 }
454 }
455
456 #[derive(Debug, PartialEq, Eq, Clone, Error)]
463 #[error("record does not have the attribute `{attr}`")]
464 pub struct RecordAttrDoesNotExistError {
465 pub(crate) attr: SmolStr,
467 pub(crate) available_attrs: Vec<SmolStr>,
469 pub(crate) total_attrs: usize,
471 pub(crate) source_loc: Option<Loc>,
473 }
474
475 impl Diagnostic for RecordAttrDoesNotExistError {
476 impl_diagnostic_from_source_loc_opt_field!(source_loc);
477
478 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
479 if self.available_attrs.is_empty() {
480 Some(Box::new("record does not have any attributes"))
481 } else if self.available_attrs.len() == self.total_attrs {
482 Some(Box::new(format!(
483 "available attributes: {:?}",
484 self.available_attrs
485 )))
486 } else {
487 Some(Box::new(format!(
488 "available attributes: [{}, ... ({} more attributes) ]",
489 self.available_attrs.iter().join(","),
490 self.total_attrs - self.available_attrs.len()
491 )))
492 }
493 }
494 }
495
496 #[derive(Debug, PartialEq, Eq, Clone, Error)]
503 pub struct TypeError {
504 pub(crate) expected: NonEmpty<Type>,
506 pub(crate) actual: Type,
508 pub(crate) advice: Option<String>,
510 pub(crate) source_loc: Option<Loc>,
512 }
513
514 impl Diagnostic for TypeError {
515 impl_diagnostic_from_source_loc_opt_field!(source_loc);
516
517 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
518 self.advice.as_ref().map(|advice| Box::new(advice) as _)
519 }
520 }
521
522 impl std::fmt::Display for TypeError {
523 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524 if self.expected.len() == 1 {
525 write!(
526 f,
527 "type error: expected {}, got {}",
528 self.expected.first(),
529 self.actual
530 )
531 } else {
532 write!(
533 f,
534 "type error: expected one of [{}], got {}",
535 self.expected.iter().join(", "),
536 self.actual
537 )
538 }
539 }
540 }
541
542 #[derive(Debug, PartialEq, Eq, Clone, Error)]
548 #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
549 pub struct WrongNumArgumentsError {
550 pub(crate) function_name: Name,
552 pub(crate) expected: usize,
554 pub(crate) actual: usize,
556 pub(crate) source_loc: Option<Loc>,
558 }
559
560 impl Diagnostic for WrongNumArgumentsError {
561 impl_diagnostic_from_source_loc_opt_field!(source_loc);
562 }
563
564 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
570 pub enum IntegerOverflowError {
571 #[error(transparent)]
573 #[diagnostic(transparent)]
574 BinaryOp(#[from] BinaryOpOverflowError),
575
576 #[error(transparent)]
578 #[diagnostic(transparent)]
579 UnaryOp(#[from] UnaryOpOverflowError),
580 }
581
582 impl IntegerOverflowError {
583 pub(crate) fn source_loc(&self) -> Option<&Loc> {
584 match self {
585 Self::BinaryOp(e) => e.source_loc.as_ref(),
586 Self::UnaryOp(e) => e.source_loc.as_ref(),
587 }
588 }
589
590 pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
591 match self {
592 Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
593 Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
594 }
595 }
596 }
597
598 #[derive(Debug, PartialEq, Eq, Clone, Error)]
604 #[error("integer overflow while attempting to {} the values `{arg1}` and `{arg2}`", match .op { BinaryOp::Add => "add", BinaryOp::Sub => "subtract", BinaryOp::Mul => "multiply", _ => "perform an operation on" })]
605 pub struct BinaryOpOverflowError {
606 pub(crate) op: BinaryOp,
608 pub(crate) arg1: Value,
610 pub(crate) arg2: Value,
612 pub(crate) source_loc: Option<Loc>,
614 }
615
616 impl Diagnostic for BinaryOpOverflowError {
617 impl_diagnostic_from_source_loc_opt_field!(source_loc);
618 }
619
620 #[derive(Debug, PartialEq, Eq, Clone, Error)]
626 #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
627 pub struct UnaryOpOverflowError {
628 pub(crate) op: UnaryOp,
630 pub(crate) arg: Value,
632 pub(crate) source_loc: Option<Loc>,
634 }
635
636 impl Diagnostic for UnaryOpOverflowError {
637 impl_diagnostic_from_source_loc_opt_field!(source_loc);
638 }
639
640 #[derive(Debug, PartialEq, Eq, Clone, Error)]
646 #[error("template slot `{slot}` was not linked")]
647 pub struct UnlinkedSlotError {
648 pub(crate) slot: SlotId,
650 pub(crate) source_loc: Option<Loc>,
652 }
653
654 impl Diagnostic for UnlinkedSlotError {
655 impl_diagnostic_from_source_loc_opt_field!(source_loc);
656 }
657
658 #[derive(Debug, PartialEq, Eq, Clone, Error)]
664 #[error("error while evaluating `{extension_name}` extension function: {msg}")]
665 pub struct ExtensionFunctionExecutionError {
666 pub(crate) extension_name: Name,
668 pub(crate) msg: String,
670 pub(crate) advice: Option<String>,
672 pub(crate) source_loc: Option<Loc>,
674 }
675
676 impl Diagnostic for ExtensionFunctionExecutionError {
677 impl_diagnostic_from_source_loc_opt_field!(source_loc);
678
679 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
680 self.advice.as_ref().map(|v| Box::new(v) as _)
681 }
682 }
683
684 impl ExtensionFunctionExecutionError {
685 pub fn extension_name(&self) -> String {
687 self.extension_name.to_string()
688 }
689 }
690
691 #[derive(Debug, PartialEq, Eq, Clone, Error)]
699 #[error("the expression contains unknown(s): `{expr}`")]
700 pub struct NonValueError {
701 pub(crate) expr: Expr,
703 pub(crate) source_loc: Option<Loc>,
705 }
706
707 impl Diagnostic for NonValueError {
708 impl_diagnostic_from_source_loc_opt_field!(source_loc);
709
710 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
711 Some(Box::new("consider using the partial evaluation APIs"))
712 }
713 }
714
715 #[cfg(feature = "tolerant-ast")]
721 #[derive(Debug, PartialEq, Eq, Clone, Error)]
722 #[error("the expression contains an error")]
723 pub struct ASTErrorExprError {
724 pub(crate) source_loc: Option<Loc>,
726 }
727
728 #[cfg(feature = "tolerant-ast")]
729 impl Diagnostic for ASTErrorExprError {
730 impl_diagnostic_from_source_loc_opt_field!(source_loc);
731
732 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
733 Some(Box::new("Represents an AST node that failed to parse"))
734 }
735 }
736
737 #[derive(Debug, PartialEq, Eq, Clone, Error)]
743 #[error("recursion limit reached")]
744 pub struct RecursionLimitError {
745 pub(crate) source_loc: Option<Loc>,
747 }
748
749 impl Diagnostic for RecursionLimitError {
750 impl_diagnostic_from_source_loc_opt_field!(source_loc);
751 }
752}
753
754pub type Result<T> = std::result::Result<T, EvaluationError>;