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 #[error(transparent)]
94 #[diagnostic(transparent)]
95 RecursionLimit(#[from] evaluation_errors::RecursionLimitError),
96}
97
98impl EvaluationError {
99 pub(crate) fn source_loc(&self) -> Option<&Loc> {
101 match self {
102 Self::EntityDoesNotExist(e) => e.source_loc.as_ref(),
103 Self::EntityAttrDoesNotExist(e) => e.source_loc.as_ref(),
104 Self::RecordAttrDoesNotExist(e) => e.source_loc.as_ref(),
105 Self::FailedExtensionFunctionLookup(e) => e.source_loc(),
106 Self::TypeError(e) => e.source_loc.as_ref(),
107 Self::WrongNumArguments(e) => e.source_loc.as_ref(),
108 Self::IntegerOverflow(e) => e.source_loc(),
109 Self::UnlinkedSlot(e) => e.source_loc.as_ref(),
110 Self::FailedExtensionFunctionExecution(e) => e.source_loc.as_ref(),
111 Self::NonValue(e) => e.source_loc.as_ref(),
112 Self::RecursionLimit(e) => e.source_loc.as_ref(),
113 }
114 }
115
116 pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
118 match self {
119 Self::EntityDoesNotExist(e) => {
120 Self::EntityDoesNotExist(evaluation_errors::EntityDoesNotExistError {
121 source_loc,
122 ..e
123 })
124 }
125 Self::EntityAttrDoesNotExist(e) => {
126 Self::EntityAttrDoesNotExist(evaluation_errors::EntityAttrDoesNotExistError {
127 source_loc,
128 ..e
129 })
130 }
131 Self::RecordAttrDoesNotExist(e) => {
132 Self::RecordAttrDoesNotExist(evaluation_errors::RecordAttrDoesNotExistError {
133 source_loc,
134 ..e
135 })
136 }
137 Self::FailedExtensionFunctionLookup(e) => {
138 Self::FailedExtensionFunctionLookup(e.with_maybe_source_loc(source_loc))
139 }
140 Self::TypeError(e) => Self::TypeError(evaluation_errors::TypeError { source_loc, ..e }),
141 Self::WrongNumArguments(e) => {
142 Self::WrongNumArguments(evaluation_errors::WrongNumArgumentsError {
143 source_loc,
144 ..e
145 })
146 }
147 Self::IntegerOverflow(e) => Self::IntegerOverflow(e.with_maybe_source_loc(source_loc)),
148 Self::UnlinkedSlot(e) => {
149 Self::UnlinkedSlot(evaluation_errors::UnlinkedSlotError { source_loc, ..e })
150 }
151 Self::FailedExtensionFunctionExecution(e) => Self::FailedExtensionFunctionExecution(
152 evaluation_errors::ExtensionFunctionExecutionError { source_loc, ..e },
153 ),
154 Self::NonValue(e) => {
155 Self::NonValue(evaluation_errors::NonValueError { source_loc, ..e })
156 }
157 Self::RecursionLimit(_) => {
158 Self::RecursionLimit(evaluation_errors::RecursionLimitError { source_loc })
159 }
160 }
161 }
162
163 pub(crate) fn entity_does_not_exist(uid: Arc<EntityUID>, source_loc: Option<Loc>) -> Self {
165 evaluation_errors::EntityDoesNotExistError { uid, source_loc }.into()
166 }
167
168 pub(crate) fn entity_attr_does_not_exist<'a>(
172 entity: Arc<EntityUID>,
173 attr: SmolStr,
174 available_attrs: impl IntoIterator<Item = &'a SmolStr>,
175 does_attr_exist_as_a_tag: bool,
176 total_attrs: usize,
177 source_loc: Option<Loc>,
178 ) -> Self {
179 evaluation_errors::EntityAttrDoesNotExistError {
180 entity,
181 attr_or_tag: attr,
182 was_attr: true,
183 exists_the_other_kind: does_attr_exist_as_a_tag,
184 available_attrs_or_tags: available_attrs
185 .into_iter()
186 .take(TOO_MANY_ATTRS)
187 .cloned()
188 .collect::<Vec<_>>(),
189 total_attrs_or_tags: total_attrs,
190 source_loc,
191 }
192 .into()
193 }
194
195 pub(crate) fn entity_tag_does_not_exist<'a>(
199 entity: Arc<EntityUID>,
200 tag: SmolStr,
201 available_tags: impl IntoIterator<Item = &'a SmolStr>,
202 does_tag_exist_as_an_attr: bool,
203 total_tags: usize,
204 source_loc: Option<Loc>,
205 ) -> Self {
206 evaluation_errors::EntityAttrDoesNotExistError {
207 entity,
208 attr_or_tag: tag,
209 was_attr: false,
210 exists_the_other_kind: does_tag_exist_as_an_attr,
211 available_attrs_or_tags: available_tags
212 .into_iter()
213 .take(TOO_MANY_ATTRS)
214 .cloned()
215 .collect::<Vec<_>>(),
216 total_attrs_or_tags: total_tags,
217 source_loc,
218 }
219 .into()
220 }
221
222 pub(crate) fn record_attr_does_not_exist<'a>(
224 attr: SmolStr,
225 available_attrs: impl IntoIterator<Item = &'a SmolStr>,
226 total_attrs: usize,
227 source_loc: Option<Loc>,
228 ) -> Self {
229 evaluation_errors::RecordAttrDoesNotExistError {
230 attr,
231 available_attrs: available_attrs
232 .into_iter()
233 .take(TOO_MANY_ATTRS)
234 .cloned()
235 .collect(),
236 total_attrs,
237 source_loc,
238 }
239 .into()
240 }
241
242 pub(crate) fn type_error(expected: NonEmpty<Type>, actual: &Value) -> Self {
244 evaluation_errors::TypeError {
245 expected,
246 actual: actual.type_of(),
247 advice: None,
248 source_loc: actual.source_loc().cloned(),
249 }
250 .into()
251 }
252
253 pub(crate) fn type_error_single(expected: Type, actual: &Value) -> Self {
254 Self::type_error(nonempty![expected], actual)
255 }
256
257 pub(crate) fn type_error_with_advice(
259 expected: NonEmpty<Type>,
260 actual: &Value,
261 advice: String,
262 ) -> Self {
263 evaluation_errors::TypeError {
264 expected,
265 actual: actual.type_of(),
266 advice: Some(advice),
267 source_loc: actual.source_loc().cloned(),
268 }
269 .into()
270 }
271
272 pub(crate) fn type_error_with_advice_single(
273 expected: Type,
274 actual: &Value,
275 advice: String,
276 ) -> Self {
277 Self::type_error_with_advice(nonempty![expected], actual, advice)
278 }
279
280 pub(crate) fn wrong_num_arguments(
282 function_name: Name,
283 expected: usize,
284 actual: usize,
285 source_loc: Option<Loc>,
286 ) -> Self {
287 evaluation_errors::WrongNumArgumentsError {
288 function_name,
289 expected,
290 actual,
291 source_loc,
292 }
293 .into()
294 }
295
296 pub(crate) fn unlinked_slot(slot: SlotId, source_loc: Option<Loc>) -> Self {
298 evaluation_errors::UnlinkedSlotError { slot, source_loc }.into()
299 }
300
301 pub(crate) fn failed_extension_function_application(
303 extension_name: Name,
304 msg: String,
305 source_loc: Option<Loc>,
306 advice: Option<String>,
307 ) -> Self {
308 evaluation_errors::ExtensionFunctionExecutionError {
309 extension_name,
310 msg,
311 source_loc,
312 advice,
313 }
314 .into()
315 }
316
317 pub(crate) fn non_value(expr: Expr) -> Self {
319 let source_loc = expr.source_loc().cloned();
320 evaluation_errors::NonValueError { expr, source_loc }.into()
321 }
322
323 pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
325 evaluation_errors::RecursionLimitError { source_loc }.into()
326 }
327}
328
329pub mod evaluation_errors {
331 use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
332 use crate::parser::Loc;
333 use itertools::Itertools;
334 use miette::Diagnostic;
335 use nonempty::NonEmpty;
336 use smol_str::SmolStr;
337 use std::sync::Arc;
338 use thiserror::Error;
339
340 use super::Name;
341
342 #[derive(Debug, PartialEq, Eq, Clone, Error)]
348 #[error("entity `{uid}` does not exist")]
349 pub struct EntityDoesNotExistError {
350 pub(crate) uid: Arc<EntityUID>,
352 pub(crate) source_loc: Option<Loc>,
354 }
355
356 impl Diagnostic for EntityDoesNotExistError {
366 impl_diagnostic_from_source_loc_opt_field!(source_loc);
367 }
368
369 #[derive(Debug, PartialEq, Eq, Clone, Error)]
376 #[error("`{entity}` does not have the {} `{attr_or_tag}`", if *.was_attr { "attribute" } else { "tag" })]
377 pub struct EntityAttrDoesNotExistError {
378 pub(crate) entity: Arc<EntityUID>,
380 pub(crate) attr_or_tag: SmolStr,
382 pub(crate) was_attr: bool,
384 pub(crate) exists_the_other_kind: bool,
387 pub(crate) available_attrs_or_tags: Vec<SmolStr>,
389 pub(crate) total_attrs_or_tags: usize,
391 pub(crate) source_loc: Option<Loc>,
393 }
394
395 impl Diagnostic for EntityAttrDoesNotExistError {
396 impl_diagnostic_from_source_loc_opt_field!(source_loc);
397
398 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
399 let mut help_text = if self.available_attrs_or_tags.is_empty() {
400 format!(
401 "`{}` does not have any {}",
402 &self.entity,
403 if self.was_attr { "attributes" } else { "tags" }
404 )
405 } else if self.available_attrs_or_tags.len() == self.total_attrs_or_tags {
406 format!(
407 "available {}: [{}]",
408 if self.was_attr { "attributes" } else { "tags" },
409 self.available_attrs_or_tags.iter().join(",")
410 )
411 } else {
412 format!(
413 "available {}: [{}, ... ({} more attributes) ]",
414 if self.was_attr { "attributes" } else { "tags" },
415 self.available_attrs_or_tags.iter().join(","),
416 self.total_attrs_or_tags - self.available_attrs_or_tags.len()
417 )
418 };
419 if self.exists_the_other_kind {
420 help_text.push_str(&format!(
421 "; note that {} (not {}) named `{}` does exist",
422 if self.was_attr {
423 "a tag"
424 } else {
425 "an attribute"
426 },
427 if self.was_attr {
428 "an attribute"
429 } else {
430 "a tag"
431 },
432 self.attr_or_tag,
433 ));
434 }
435 Some(Box::new(help_text))
436 }
437 }
438
439 #[derive(Debug, PartialEq, Eq, Clone, Error)]
446 #[error("record does not have the attribute `{attr}`")]
447 pub struct RecordAttrDoesNotExistError {
448 pub(crate) attr: SmolStr,
450 pub(crate) available_attrs: Vec<SmolStr>,
452 pub(crate) total_attrs: usize,
454 pub(crate) source_loc: Option<Loc>,
456 }
457
458 impl Diagnostic for RecordAttrDoesNotExistError {
459 impl_diagnostic_from_source_loc_opt_field!(source_loc);
460
461 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
462 if self.available_attrs.is_empty() {
463 Some(Box::new("record does not have any attributes"))
464 } else if self.available_attrs.len() == self.total_attrs {
465 Some(Box::new(format!(
466 "available attributes: {:?}",
467 self.available_attrs
468 )))
469 } else {
470 Some(Box::new(format!(
471 "available attributes: [{}, ... ({} more attributes) ]",
472 self.available_attrs.iter().join(","),
473 self.total_attrs - self.available_attrs.len()
474 )))
475 }
476 }
477 }
478
479 #[derive(Debug, PartialEq, Eq, Clone, Error)]
486 pub struct TypeError {
487 pub(crate) expected: NonEmpty<Type>,
489 pub(crate) actual: Type,
491 pub(crate) advice: Option<String>,
493 pub(crate) source_loc: Option<Loc>,
495 }
496
497 impl Diagnostic for TypeError {
498 impl_diagnostic_from_source_loc_opt_field!(source_loc);
499
500 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
501 self.advice.as_ref().map(|advice| Box::new(advice) as _)
502 }
503 }
504
505 impl std::fmt::Display for TypeError {
506 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
507 if self.expected.len() == 1 {
508 write!(
509 f,
510 "type error: expected {}, got {}",
511 self.expected.first(),
512 self.actual
513 )
514 } else {
515 write!(
516 f,
517 "type error: expected one of [{}], got {}",
518 self.expected.iter().join(", "),
519 self.actual
520 )
521 }
522 }
523 }
524
525 #[derive(Debug, PartialEq, Eq, Clone, Error)]
531 #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
532 pub struct WrongNumArgumentsError {
533 pub(crate) function_name: Name,
535 pub(crate) expected: usize,
537 pub(crate) actual: usize,
539 pub(crate) source_loc: Option<Loc>,
541 }
542
543 impl Diagnostic for WrongNumArgumentsError {
544 impl_diagnostic_from_source_loc_opt_field!(source_loc);
545 }
546
547 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
553 pub enum IntegerOverflowError {
554 #[error(transparent)]
556 #[diagnostic(transparent)]
557 BinaryOp(#[from] BinaryOpOverflowError),
558
559 #[error(transparent)]
561 #[diagnostic(transparent)]
562 UnaryOp(#[from] UnaryOpOverflowError),
563 }
564
565 impl IntegerOverflowError {
566 pub(crate) fn source_loc(&self) -> Option<&Loc> {
567 match self {
568 Self::BinaryOp(e) => e.source_loc.as_ref(),
569 Self::UnaryOp(e) => e.source_loc.as_ref(),
570 }
571 }
572
573 pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
574 match self {
575 Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
576 Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
577 }
578 }
579 }
580
581 #[derive(Debug, PartialEq, Eq, Clone, Error)]
587 #[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" })]
588 pub struct BinaryOpOverflowError {
589 pub(crate) op: BinaryOp,
591 pub(crate) arg1: Value,
593 pub(crate) arg2: Value,
595 pub(crate) source_loc: Option<Loc>,
597 }
598
599 impl Diagnostic for BinaryOpOverflowError {
600 impl_diagnostic_from_source_loc_opt_field!(source_loc);
601 }
602
603 #[derive(Debug, PartialEq, Eq, Clone, Error)]
609 #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
610 pub struct UnaryOpOverflowError {
611 pub(crate) op: UnaryOp,
613 pub(crate) arg: Value,
615 pub(crate) source_loc: Option<Loc>,
617 }
618
619 impl Diagnostic for UnaryOpOverflowError {
620 impl_diagnostic_from_source_loc_opt_field!(source_loc);
621 }
622
623 #[derive(Debug, PartialEq, Eq, Clone, Error)]
629 #[error("template slot `{slot}` was not linked")]
630 pub struct UnlinkedSlotError {
631 pub(crate) slot: SlotId,
633 pub(crate) source_loc: Option<Loc>,
635 }
636
637 impl Diagnostic for UnlinkedSlotError {
638 impl_diagnostic_from_source_loc_opt_field!(source_loc);
639 }
640
641 #[derive(Debug, PartialEq, Eq, Clone, Error)]
647 #[error("error while evaluating `{extension_name}` extension function: {msg}")]
648 pub struct ExtensionFunctionExecutionError {
649 pub(crate) extension_name: Name,
651 pub(crate) msg: String,
653 pub(crate) advice: Option<String>,
655 pub(crate) source_loc: Option<Loc>,
657 }
658
659 impl Diagnostic for ExtensionFunctionExecutionError {
660 impl_diagnostic_from_source_loc_opt_field!(source_loc);
661
662 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
663 self.advice.as_ref().map(|v| Box::new(v) as _)
664 }
665 }
666
667 impl ExtensionFunctionExecutionError {
668 pub fn extension_name(&self) -> String {
670 self.extension_name.to_string()
671 }
672 }
673
674 #[derive(Debug, PartialEq, Eq, Clone, Error)]
682 #[error("the expression contains unknown(s): `{expr}`")]
683 pub struct NonValueError {
684 pub(crate) expr: Expr,
686 pub(crate) source_loc: Option<Loc>,
688 }
689
690 impl Diagnostic for NonValueError {
691 impl_diagnostic_from_source_loc_opt_field!(source_loc);
692
693 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
694 Some(Box::new("consider using the partial evaluation APIs"))
695 }
696 }
697
698 #[derive(Debug, PartialEq, Eq, Clone, Error)]
704 #[error("recursion limit reached")]
705 pub struct RecursionLimitError {
706 pub(crate) source_loc: Option<Loc>,
708 }
709
710 impl Diagnostic for RecursionLimitError {
711 impl_diagnostic_from_source_loc_opt_field!(source_loc);
712 }
713}
714
715pub type Result<T> = std::result::Result<T, EvaluationError>;