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 ) -> Self {
307 evaluation_errors::ExtensionFunctionExecutionError {
308 extension_name,
309 msg,
310 source_loc,
311 }
312 .into()
313 }
314
315 pub(crate) fn non_value(expr: Expr) -> Self {
317 let source_loc = expr.source_loc().cloned();
318 evaluation_errors::NonValueError { expr, source_loc }.into()
319 }
320
321 #[cfg(not(target_arch = "wasm32"))]
323 pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
324 evaluation_errors::RecursionLimitError { source_loc }.into()
325 }
326}
327
328pub mod evaluation_errors {
330 use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
331 use crate::parser::Loc;
332 use itertools::Itertools;
333 use miette::Diagnostic;
334 use nonempty::NonEmpty;
335 use smol_str::SmolStr;
336 use std::sync::Arc;
337 use thiserror::Error;
338
339 use super::Name;
340
341 #[derive(Debug, PartialEq, Eq, Clone, Error)]
347 #[error("entity `{uid}` does not exist")]
348 pub struct EntityDoesNotExistError {
349 pub(crate) uid: Arc<EntityUID>,
351 pub(crate) source_loc: Option<Loc>,
353 }
354
355 impl Diagnostic for EntityDoesNotExistError {
365 impl_diagnostic_from_source_loc_opt_field!(source_loc);
366 }
367
368 #[derive(Debug, PartialEq, Eq, Clone, Error)]
375 #[error("`{entity}` does not have the {} `{attr_or_tag}`", if *.was_attr { "attribute" } else { "tag" })]
376 pub struct EntityAttrDoesNotExistError {
377 pub(crate) entity: Arc<EntityUID>,
379 pub(crate) attr_or_tag: SmolStr,
381 pub(crate) was_attr: bool,
383 pub(crate) exists_the_other_kind: bool,
386 pub(crate) available_attrs_or_tags: Vec<SmolStr>,
388 pub(crate) total_attrs_or_tags: usize,
390 pub(crate) source_loc: Option<Loc>,
392 }
393
394 impl Diagnostic for EntityAttrDoesNotExistError {
395 impl_diagnostic_from_source_loc_opt_field!(source_loc);
396
397 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
398 let mut help_text = if self.available_attrs_or_tags.is_empty() {
399 format!(
400 "`{}` does not have any {}",
401 &self.entity,
402 if self.was_attr { "attributes" } else { "tags" }
403 )
404 } else if self.available_attrs_or_tags.len() == self.total_attrs_or_tags {
405 format!(
406 "available {}: [{}]",
407 if self.was_attr { "attributes" } else { "tags" },
408 self.available_attrs_or_tags.iter().join(",")
409 )
410 } else {
411 format!(
412 "available {}: [{}, ... ({} more attributes) ]",
413 if self.was_attr { "attributes" } else { "tags" },
414 self.available_attrs_or_tags.iter().join(","),
415 self.total_attrs_or_tags - self.available_attrs_or_tags.len()
416 )
417 };
418 if self.exists_the_other_kind {
419 help_text.push_str(&format!(
420 "; note that {} (not {}) named `{}` does exist",
421 if self.was_attr {
422 "a tag"
423 } else {
424 "an attribute"
425 },
426 if self.was_attr {
427 "an attribute"
428 } else {
429 "a tag"
430 },
431 self.attr_or_tag,
432 ));
433 }
434 Some(Box::new(help_text))
435 }
436 }
437
438 #[derive(Debug, PartialEq, Eq, Clone, Error)]
445 #[error("record does not have the attribute `{attr}`")]
446 pub struct RecordAttrDoesNotExistError {
447 pub(crate) attr: SmolStr,
449 pub(crate) available_attrs: Vec<SmolStr>,
451 pub(crate) total_attrs: usize,
453 pub(crate) source_loc: Option<Loc>,
455 }
456
457 impl Diagnostic for RecordAttrDoesNotExistError {
458 impl_diagnostic_from_source_loc_opt_field!(source_loc);
459
460 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
461 if self.available_attrs.is_empty() {
462 Some(Box::new("record does not have any attributes"))
463 } else if self.available_attrs.len() == self.total_attrs {
464 Some(Box::new(format!(
465 "available attributes: {:?}",
466 self.available_attrs
467 )))
468 } else {
469 Some(Box::new(format!(
470 "available attributes: [{}, ... ({} more attributes) ]",
471 self.available_attrs.iter().join(","),
472 self.total_attrs - self.available_attrs.len()
473 )))
474 }
475 }
476 }
477
478 #[derive(Debug, PartialEq, Eq, Clone, Error)]
485 pub struct TypeError {
486 pub(crate) expected: NonEmpty<Type>,
488 pub(crate) actual: Type,
490 pub(crate) advice: Option<String>,
492 pub(crate) source_loc: Option<Loc>,
494 }
495
496 impl Diagnostic for TypeError {
497 impl_diagnostic_from_source_loc_opt_field!(source_loc);
498
499 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
500 self.advice.as_ref().map(|advice| Box::new(advice) as _)
501 }
502 }
503
504 impl std::fmt::Display for TypeError {
505 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506 if self.expected.len() == 1 {
507 write!(
508 f,
509 "type error: expected {}, got {}",
510 self.expected.first(),
511 self.actual
512 )
513 } else {
514 write!(
515 f,
516 "type error: expected one of [{}], got {}",
517 self.expected.iter().join(", "),
518 self.actual
519 )
520 }
521 }
522 }
523
524 #[derive(Debug, PartialEq, Eq, Clone, Error)]
530 #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
531 pub struct WrongNumArgumentsError {
532 pub(crate) function_name: Name,
534 pub(crate) expected: usize,
536 pub(crate) actual: usize,
538 pub(crate) source_loc: Option<Loc>,
540 }
541
542 impl Diagnostic for WrongNumArgumentsError {
543 impl_diagnostic_from_source_loc_opt_field!(source_loc);
544 }
545
546 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
552 pub enum IntegerOverflowError {
553 #[error(transparent)]
555 #[diagnostic(transparent)]
556 BinaryOp(#[from] BinaryOpOverflowError),
557
558 #[error(transparent)]
560 #[diagnostic(transparent)]
561 UnaryOp(#[from] UnaryOpOverflowError),
562 }
563
564 impl IntegerOverflowError {
565 pub(crate) fn source_loc(&self) -> Option<&Loc> {
566 match self {
567 Self::BinaryOp(e) => e.source_loc.as_ref(),
568 Self::UnaryOp(e) => e.source_loc.as_ref(),
569 }
570 }
571
572 pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
573 match self {
574 Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
575 Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
576 }
577 }
578 }
579
580 #[derive(Debug, PartialEq, Eq, Clone, Error)]
586 #[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" })]
587 pub struct BinaryOpOverflowError {
588 pub(crate) op: BinaryOp,
590 pub(crate) arg1: Value,
592 pub(crate) arg2: Value,
594 pub(crate) source_loc: Option<Loc>,
596 }
597
598 impl Diagnostic for BinaryOpOverflowError {
599 impl_diagnostic_from_source_loc_opt_field!(source_loc);
600 }
601
602 #[derive(Debug, PartialEq, Eq, Clone, Error)]
608 #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
609 pub struct UnaryOpOverflowError {
610 pub(crate) op: UnaryOp,
612 pub(crate) arg: Value,
614 pub(crate) source_loc: Option<Loc>,
616 }
617
618 impl Diagnostic for UnaryOpOverflowError {
619 impl_diagnostic_from_source_loc_opt_field!(source_loc);
620 }
621
622 #[derive(Debug, PartialEq, Eq, Clone, Error)]
628 #[error("template slot `{slot}` was not linked")]
629 pub struct UnlinkedSlotError {
630 pub(crate) slot: SlotId,
632 pub(crate) source_loc: Option<Loc>,
634 }
635
636 impl Diagnostic for UnlinkedSlotError {
637 impl_diagnostic_from_source_loc_opt_field!(source_loc);
638 }
639
640 #[derive(Debug, PartialEq, Eq, Clone, Error)]
646 #[error("error while evaluating `{extension_name}` extension function: {msg}")]
647 pub struct ExtensionFunctionExecutionError {
648 pub(crate) extension_name: Name,
650 pub(crate) msg: String,
652 pub(crate) source_loc: Option<Loc>,
654 }
655
656 impl Diagnostic for ExtensionFunctionExecutionError {
657 impl_diagnostic_from_source_loc_opt_field!(source_loc);
658 }
659
660 impl ExtensionFunctionExecutionError {
661 pub fn extension_name(&self) -> String {
663 self.extension_name.to_string()
664 }
665 }
666
667 #[derive(Debug, PartialEq, Eq, Clone, Error)]
675 #[error("the expression contains unknown(s): `{expr}`")]
676 pub struct NonValueError {
677 pub(crate) expr: Expr,
679 pub(crate) source_loc: Option<Loc>,
681 }
682
683 impl Diagnostic for NonValueError {
684 impl_diagnostic_from_source_loc_opt_field!(source_loc);
685
686 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
687 Some(Box::new("consider using the partial evaluation APIs"))
688 }
689 }
690
691 #[derive(Debug, PartialEq, Eq, Clone, Error)]
697 #[error("recursion limit reached")]
698 pub struct RecursionLimitError {
699 pub(crate) source_loc: Option<Loc>,
701 }
702
703 impl Diagnostic for RecursionLimitError {
704 impl_diagnostic_from_source_loc_opt_field!(source_loc);
705 }
706}
707
708pub type Result<T> = std::result::Result<T, EvaluationError>;