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>(
170 entity: Arc<EntityUID>,
171 attr: SmolStr,
172 available_attrs: impl IntoIterator<Item = &'a SmolStr>,
173 total_attrs: usize,
174 source_loc: Option<Loc>,
175 ) -> Self {
176 evaluation_errors::EntityAttrDoesNotExistError {
177 entity,
178 attr,
179 available_attrs: available_attrs
180 .into_iter()
181 .take(TOO_MANY_ATTRS)
182 .cloned()
183 .collect::<Vec<_>>(),
184 total_attrs,
185 source_loc,
186 }
187 .into()
188 }
189
190 pub(crate) fn record_attr_does_not_exist<'a>(
192 attr: SmolStr,
193 available_attrs: impl IntoIterator<Item = &'a SmolStr>,
194 total_attrs: usize,
195 source_loc: Option<Loc>,
196 ) -> Self {
197 evaluation_errors::RecordAttrDoesNotExistError {
198 attr,
199 available_attrs: available_attrs
200 .into_iter()
201 .take(TOO_MANY_ATTRS)
202 .cloned()
203 .collect(),
204 total_attrs,
205 source_loc,
206 }
207 .into()
208 }
209
210 pub(crate) fn type_error(expected: NonEmpty<Type>, actual: &Value) -> Self {
212 evaluation_errors::TypeError {
213 expected,
214 actual: actual.type_of(),
215 advice: None,
216 source_loc: actual.source_loc().cloned(),
217 }
218 .into()
219 }
220
221 pub(crate) fn type_error_single(expected: Type, actual: &Value) -> Self {
222 Self::type_error(nonempty![expected], actual)
223 }
224
225 pub(crate) fn type_error_with_advice(
227 expected: NonEmpty<Type>,
228 actual: &Value,
229 advice: String,
230 ) -> Self {
231 evaluation_errors::TypeError {
232 expected,
233 actual: actual.type_of(),
234 advice: Some(advice),
235 source_loc: actual.source_loc().cloned(),
236 }
237 .into()
238 }
239
240 pub(crate) fn type_error_with_advice_single(
241 expected: Type,
242 actual: &Value,
243 advice: String,
244 ) -> Self {
245 Self::type_error_with_advice(nonempty![expected], actual, advice)
246 }
247
248 pub(crate) fn wrong_num_arguments(
250 function_name: Name,
251 expected: usize,
252 actual: usize,
253 source_loc: Option<Loc>,
254 ) -> Self {
255 evaluation_errors::WrongNumArgumentsError {
256 function_name,
257 expected,
258 actual,
259 source_loc,
260 }
261 .into()
262 }
263
264 pub(crate) fn unlinked_slot(slot: SlotId, source_loc: Option<Loc>) -> Self {
266 evaluation_errors::UnlinkedSlotError { slot, source_loc }.into()
267 }
268
269 pub(crate) fn failed_extension_function_application(
271 extension_name: Name,
272 msg: String,
273 source_loc: Option<Loc>,
274 ) -> Self {
275 evaluation_errors::ExtensionFunctionExecutionError {
276 extension_name,
277 msg,
278 source_loc,
279 }
280 .into()
281 }
282
283 pub(crate) fn non_value(expr: Expr) -> Self {
285 let source_loc = expr.source_loc().cloned();
286 evaluation_errors::NonValueError { expr, source_loc }.into()
287 }
288
289 #[cfg(not(target_arch = "wasm32"))]
291 pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
292 evaluation_errors::RecursionLimitError { source_loc }.into()
293 }
294}
295
296pub mod evaluation_errors {
298 use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
299 use crate::parser::Loc;
300 use itertools::Itertools;
301 use miette::Diagnostic;
302 use nonempty::NonEmpty;
303 use smol_str::SmolStr;
304 use std::sync::Arc;
305 use thiserror::Error;
306
307 use super::Name;
308
309 #[derive(Debug, PartialEq, Eq, Clone, Error)]
315 #[error("entity `{uid}` does not exist")]
316 pub struct EntityDoesNotExistError {
317 pub(crate) uid: Arc<EntityUID>,
319 pub(crate) source_loc: Option<Loc>,
321 }
322
323 impl Diagnostic for EntityDoesNotExistError {
333 impl_diagnostic_from_source_loc_opt_field!(source_loc);
334 }
335
336 #[derive(Debug, PartialEq, Eq, Clone, Error)]
343 #[error("`{entity}` does not have the attribute `{attr}`")]
344 pub struct EntityAttrDoesNotExistError {
345 pub(crate) entity: Arc<EntityUID>,
347 pub(crate) attr: SmolStr,
349 pub(crate) available_attrs: Vec<SmolStr>,
351 pub(crate) total_attrs: usize,
353 pub(crate) source_loc: Option<Loc>,
355 }
356
357 impl Diagnostic for EntityAttrDoesNotExistError {
358 impl_diagnostic_from_source_loc_opt_field!(source_loc);
359
360 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
361 if self.available_attrs.is_empty() {
362 Some(Box::new("entity does not have any attributes"))
363 } else if self.available_attrs.len() == self.total_attrs {
364 Some(Box::new(format!(
365 "Available attributes: {:?}",
366 self.available_attrs
367 )))
368 } else {
369 Some(Box::new(format!(
370 "available attributes: [{}, ... ({} more attributes) ]",
371 self.available_attrs.iter().join(","),
372 self.total_attrs - self.available_attrs.len()
373 )))
374 }
375 }
376 }
377
378 #[derive(Debug, PartialEq, Eq, Clone, Error)]
385 #[error("record does not have the attribute `{attr}`")]
386 pub struct RecordAttrDoesNotExistError {
387 pub(crate) attr: SmolStr,
389 pub(crate) available_attrs: Vec<SmolStr>,
391 pub(crate) total_attrs: usize,
393 pub(crate) source_loc: Option<Loc>,
395 }
396
397 impl Diagnostic for RecordAttrDoesNotExistError {
398 impl_diagnostic_from_source_loc_opt_field!(source_loc);
399
400 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
401 if self.available_attrs.is_empty() {
402 Some(Box::new("record does not have any attributes"))
403 } else if self.available_attrs.len() == self.total_attrs {
404 Some(Box::new(format!(
405 "available attributes: {:?}",
406 self.available_attrs
407 )))
408 } else {
409 Some(Box::new(format!(
410 "available attributes: [{}, ... ({} more attributes) ]",
411 self.available_attrs.iter().join(","),
412 self.total_attrs - self.available_attrs.len()
413 )))
414 }
415 }
416 }
417
418 #[derive(Debug, PartialEq, Eq, Clone, Error)]
425 pub struct TypeError {
426 pub(crate) expected: NonEmpty<Type>,
428 pub(crate) actual: Type,
430 pub(crate) advice: Option<String>,
432 pub(crate) source_loc: Option<Loc>,
434 }
435
436 impl Diagnostic for TypeError {
437 impl_diagnostic_from_source_loc_opt_field!(source_loc);
438
439 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
440 self.advice.as_ref().map(|advice| Box::new(advice) as _)
441 }
442 }
443
444 impl std::fmt::Display for TypeError {
445 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446 if self.expected.len() == 1 {
447 write!(
448 f,
449 "type error: expected {}, got {}",
450 self.expected.first(),
451 self.actual
452 )
453 } else {
454 write!(
455 f,
456 "type error: expected one of [{}], got {}",
457 self.expected.iter().join(", "),
458 self.actual
459 )
460 }
461 }
462 }
463
464 #[derive(Debug, PartialEq, Eq, Clone, Error)]
470 #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
471 pub struct WrongNumArgumentsError {
472 pub(crate) function_name: Name,
474 pub(crate) expected: usize,
476 pub(crate) actual: usize,
478 pub(crate) source_loc: Option<Loc>,
480 }
481
482 impl Diagnostic for WrongNumArgumentsError {
483 impl_diagnostic_from_source_loc_opt_field!(source_loc);
484 }
485
486 #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
492 pub enum IntegerOverflowError {
493 #[error(transparent)]
495 #[diagnostic(transparent)]
496 BinaryOp(#[from] BinaryOpOverflowError),
497
498 #[error(transparent)]
500 #[diagnostic(transparent)]
501 UnaryOp(#[from] UnaryOpOverflowError),
502 }
503
504 impl IntegerOverflowError {
505 pub(crate) fn source_loc(&self) -> Option<&Loc> {
506 match self {
507 Self::BinaryOp(e) => e.source_loc.as_ref(),
508 Self::UnaryOp(e) => e.source_loc.as_ref(),
509 }
510 }
511
512 pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
513 match self {
514 Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
515 Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
516 }
517 }
518 }
519
520 #[derive(Debug, PartialEq, Eq, Clone, Error)]
526 #[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" })]
527 pub struct BinaryOpOverflowError {
528 pub(crate) op: BinaryOp,
530 pub(crate) arg1: Value,
532 pub(crate) arg2: Value,
534 pub(crate) source_loc: Option<Loc>,
536 }
537
538 impl Diagnostic for BinaryOpOverflowError {
539 impl_diagnostic_from_source_loc_opt_field!(source_loc);
540 }
541
542 #[derive(Debug, PartialEq, Eq, Clone, Error)]
548 #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
549 pub struct UnaryOpOverflowError {
550 pub(crate) op: UnaryOp,
552 pub(crate) arg: Value,
554 pub(crate) source_loc: Option<Loc>,
556 }
557
558 impl Diagnostic for UnaryOpOverflowError {
559 impl_diagnostic_from_source_loc_opt_field!(source_loc);
560 }
561
562 #[derive(Debug, PartialEq, Eq, Clone, Error)]
568 #[error("template slot `{slot}` was not linked")]
569 pub struct UnlinkedSlotError {
570 pub(crate) slot: SlotId,
572 pub(crate) source_loc: Option<Loc>,
574 }
575
576 impl Diagnostic for UnlinkedSlotError {
577 impl_diagnostic_from_source_loc_opt_field!(source_loc);
578 }
579
580 #[derive(Debug, PartialEq, Eq, Clone, Error)]
586 #[error("error while evaluating `{extension_name}` extension function: {msg}")]
587 pub struct ExtensionFunctionExecutionError {
588 pub(crate) extension_name: Name,
590 pub(crate) msg: String,
592 pub(crate) source_loc: Option<Loc>,
594 }
595
596 impl Diagnostic for ExtensionFunctionExecutionError {
597 impl_diagnostic_from_source_loc_opt_field!(source_loc);
598 }
599
600 impl ExtensionFunctionExecutionError {
601 pub fn extension_name(&self) -> String {
603 self.extension_name.to_string()
604 }
605 }
606
607 #[derive(Debug, PartialEq, Eq, Clone, Error)]
615 #[error("the expression contains unknown(s): `{expr}`")]
616 pub struct NonValueError {
617 pub(crate) expr: Expr,
619 pub(crate) source_loc: Option<Loc>,
621 }
622
623 impl Diagnostic for NonValueError {
624 impl_diagnostic_from_source_loc_opt_field!(source_loc);
625
626 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
627 Some(Box::new("consider using the partial evaluation APIs"))
628 }
629 }
630
631 #[derive(Debug, PartialEq, Eq, Clone, Error)]
637 #[error("recursion limit reached")]
638 pub struct RecursionLimitError {
639 pub(crate) source_loc: Option<Loc>,
641 }
642
643 impl Diagnostic for RecursionLimitError {
644 impl_diagnostic_from_source_loc_opt_field!(source_loc);
645 }
646}
647
648pub type Result<T> = std::result::Result<T, EvaluationError>;