1pub use arithmetic_parser::UnsupportedType;
4
5use derive_more::Display;
6use hashbrown::HashSet;
7
8use core::fmt;
9
10use crate::{
11 alloc::{format, vec, Box, String, ToOwned, ToString, Vec},
12 fns::FromValueError,
13 ModuleId, Value,
14};
15use arithmetic_parser::{
16 BinaryOp, CodeFragment, LocatedSpan, LvalueLen, MaybeSpanned, Op, StripCode, UnaryOp,
17};
18
19#[derive(Debug)]
23#[non_exhaustive]
24pub enum ArithmeticError {
25 IntegerOverflow,
27 DivisionByZero,
29 InvalidExponent,
34 NoInverse,
38 InvalidOp(anyhow::Error),
43}
44
45impl ArithmeticError {
46 pub fn invalid_op(message: impl Into<String>) -> Self {
48 Self::InvalidOp(anyhow::Error::msg(message.into()))
49 }
50}
51
52impl fmt::Display for ArithmeticError {
53 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 Self::IntegerOverflow => formatter.write_str("Integer overflow or underflow"),
56 Self::DivisionByZero => formatter.write_str("Integer division by zero"),
57 Self::InvalidExponent => formatter.write_str("Exponent is too large or negative"),
58 Self::NoInverse => formatter.write_str("Integer has no multiplicative inverse"),
59 Self::InvalidOp(e) => write!(formatter, "Invalid operation: {}", e),
60 }
61 }
62}
63
64#[cfg(feature = "std")]
65impl std::error::Error for ArithmeticError {
66 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
67 match self {
68 Self::InvalidOp(e) => Some(e.as_ref()),
69 _ => None,
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum TupleLenMismatchContext {
77 BinaryOp(BinaryOp),
79 Assignment,
81}
82
83impl fmt::Display for TupleLenMismatchContext {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Self::BinaryOp(op) => write!(formatter, "{}", op),
87 Self::Assignment => formatter.write_str("assignment"),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum RepeatedAssignmentContext {
95 FnArgs,
97 Assignment,
99}
100
101impl fmt::Display for RepeatedAssignmentContext {
102 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103 formatter.write_str(match self {
104 Self::FnArgs => "function args",
105 Self::Assignment => "assignment",
106 })
107 }
108}
109
110#[derive(Debug, Display)]
112#[non_exhaustive]
113pub enum ErrorKind {
114 #[display(
116 fmt = "Mismatch between length of tuples in {}: LHS has {} element(s), whereas RHS has {}",
117 context,
118 lhs,
119 rhs
120 )]
121 TupleLenMismatch {
122 lhs: LvalueLen,
124 rhs: usize,
126 context: TupleLenMismatchContext,
128 },
129
130 #[display(
132 fmt = "Cannot perform {} on objects: LHS has fields {:?}, whereas RHS has fields {:?}",
133 op,
134 lhs_fields,
135 rhs_fields
136 )]
137 FieldsMismatch {
138 lhs_fields: HashSet<String>,
140 rhs_fields: HashSet<String>,
142 op: BinaryOp,
144 },
145
146 #[display(
148 fmt = "Mismatch between the number of arguments in the function definition and its call: \
149 definition requires {} arg(s), call has {}",
150 def,
151 call
152 )]
153 ArgsLenMismatch {
154 def: LvalueLen,
156 call: usize,
158 },
159
160 #[display(fmt = "Cannot destructure a non-tuple variable")]
162 CannotDestructure,
163
164 #[display(fmt = "Repeated assignment to the same variable in {}", context)]
166 RepeatedAssignment {
167 context: RepeatedAssignmentContext,
169 },
170
171 #[display(fmt = "Repeated object field")]
174 RepeatedField,
175
176 #[display(fmt = "Variable `{}` is not defined", _0)]
178 Undefined(String),
179
180 #[display(fmt = "`{}` is not a valid field name", _0)]
182 InvalidFieldName(String),
183
184 #[display(fmt = "Value is not callable")]
186 CannotCall,
187 #[display(fmt = "Value cannot be indexed")]
189 CannotIndex,
190 #[display(fmt = "Fields cannot be accessed for the object")]
192 CannotAccessFields,
193
194 #[display(
196 fmt = "Attempting to get element {} from tuple with length {}",
197 index,
198 len
199 )]
200 IndexOutOfBounds {
201 index: usize,
203 len: usize,
205 },
206 #[display(fmt = "Object does not have field {}", field)]
208 NoField {
209 field: String,
211 available_fields: Vec<String>,
213 },
214
215 #[display(fmt = "Failed executing native function: {}", _0)]
217 NativeCall(String),
218
219 #[display(
221 fmt = "Failed converting arguments for native function wrapper: {}",
222 _0
223 )]
224 Wrapper(FromValueError),
225
226 #[display(fmt = "Unexpected operand type for {}", op)]
228 UnexpectedOperand {
229 op: Op,
231 },
232
233 #[display(fmt = "Value cannot be compared to other values")]
236 CannotCompare,
237
238 #[display(fmt = "Unsupported {}", _0)]
240 Unsupported(UnsupportedType),
241
242 #[display(fmt = "Arithmetic error: {}", _0)]
244 Arithmetic(ArithmeticError),
245}
246
247impl ErrorKind {
248 pub fn native(message: impl Into<String>) -> Self {
250 Self::NativeCall(message.into())
251 }
252
253 pub fn unsupported<T: Into<UnsupportedType>>(ty: T) -> Self {
255 Self::Unsupported(ty.into())
256 }
257
258 pub fn to_short_string(&self) -> String {
260 match self {
261 Self::TupleLenMismatch { context, .. } => {
262 format!("Mismatch between length of tuples in {}", context)
263 }
264 Self::FieldsMismatch { op, .. } => {
265 format!("Mismatch between object shapes during {}", op)
266 }
267 Self::ArgsLenMismatch { .. } => {
268 "Mismatch between the number of arguments in the function definition and its call"
269 .to_owned()
270 }
271 Self::CannotDestructure => "Cannot destructure a non-tuple variable".to_owned(),
272 Self::RepeatedAssignment { context } => {
273 format!("Repeated assignment to the same variable in {}", context)
274 }
275 Self::RepeatedField => "Repeated object field".to_owned(),
276 Self::Undefined(name) => format!("Variable `{}` is not defined", name),
277 Self::InvalidFieldName(name) => format!("`{}` is not a valid field name", name),
278 Self::CannotCall => "Value is not callable".to_owned(),
279 Self::CannotIndex => "Value cannot be indexed".to_owned(),
280 Self::CannotAccessFields => "Value has no fields".to_owned(),
281 Self::IndexOutOfBounds { len, .. } => {
282 format!("Index out of bounds for tuple with length {}", len)
283 }
284 Self::NoField { field, .. } => format!("Object does not have field {}", field),
285 Self::NativeCall(message) => message.clone(),
286 Self::Wrapper(err) => err.to_string(),
287 Self::UnexpectedOperand { op } => format!("Unexpected operand type for {}", op),
288 Self::CannotCompare => "Value is not comparable".to_owned(),
289 Self::Unsupported(_) => "Grammar construct not supported".to_owned(),
290 Self::Arithmetic(_) => "Arithmetic error".to_owned(),
291 }
292 }
293
294 pub fn main_span_info(&self) -> String {
296 match self {
297 Self::TupleLenMismatch { context, lhs, .. } => {
298 format!("LHS of {} with {} element(s)", context, lhs)
299 }
300 Self::FieldsMismatch { lhs_fields, .. } => {
301 format!("LHS with fields {:?}", lhs_fields)
302 }
303 Self::ArgsLenMismatch { call, .. } => format!("Called with {} arg(s) here", call),
304 Self::CannotDestructure => "Failed destructuring".to_owned(),
305 Self::RepeatedAssignment { .. } => "Re-assigned variable".to_owned(),
306 Self::RepeatedField => "Repeated object field".to_owned(),
307 Self::Undefined(_) => "Undefined variable occurrence".to_owned(),
308 Self::InvalidFieldName(_) => "Invalid field".to_owned(),
309 Self::CannotIndex | Self::IndexOutOfBounds { .. } => "Indexing operation".to_owned(),
310 Self::CannotAccessFields | Self::NoField { .. } => "Field access".to_owned(),
311 Self::CannotCall | Self::NativeCall(_) | Self::Wrapper(_) => "Failed call".to_owned(),
312 Self::UnexpectedOperand { .. } => "Operand of wrong type".to_owned(),
313 Self::CannotCompare => "Cannot be compared".to_owned(),
314 Self::Unsupported(ty) => format!("Unsupported {}", ty),
315 Self::Arithmetic(e) => e.to_string(),
316 }
317 }
318
319 pub fn help(&self) -> Option<String> {
321 Some(match self {
322 Self::TupleLenMismatch { context, .. } => format!(
323 "If both args of {} are tuples, the number of elements in them must agree",
324 context
325 ),
326 Self::FieldsMismatch { op, .. } => format!(
327 "If both args of {} are objects, their field names must be the same",
328 op
329 ),
330 Self::CannotDestructure => "Only tuples can be destructured".to_owned(),
331 Self::RepeatedAssignment { context } => format!(
332 "In {}, all assigned variables must have different names",
333 context
334 ),
335 Self::RepeatedField => "Field names in objects must be unique".to_owned(),
336 Self::InvalidFieldName(_) => "Field names must be `usize`s or identifiers".to_owned(),
337 Self::CannotCall => "Only functions are callable, i.e., can be used as `fn_name` \
338 in `fn_name(...)` expressions"
339 .to_owned(),
340 Self::CannotIndex => "Only tuples can be indexed".to_owned(),
341 Self::CannotAccessFields => "Only objects have fields".to_owned(),
342
343 Self::UnexpectedOperand { op: Op::Binary(op) } if op.is_arithmetic() => {
344 "Operands of binary arithmetic ops must be primitive values or tuples / objects \
345 consisting of primitive values"
346 .to_owned()
347 }
348 Self::UnexpectedOperand { op: Op::Binary(op) } if op.is_comparison() => {
349 "Operands of comparison ops must be primitive values".to_owned()
350 }
351 Self::UnexpectedOperand {
352 op: Op::Binary(BinaryOp::And),
353 }
354 | Self::UnexpectedOperand {
355 op: Op::Binary(BinaryOp::Or),
356 } => "Operands of binary boolean ops must be boolean".to_owned(),
357 Self::UnexpectedOperand {
358 op: Op::Unary(UnaryOp::Neg),
359 } => "Operand of negation must be primitive values or tuples / objects \
360 consisting of primitive values"
361 .to_owned(),
362 Self::UnexpectedOperand {
363 op: Op::Unary(UnaryOp::Not),
364 } => "Operand of boolean negation must be boolean".to_owned(),
365
366 Self::CannotCompare => {
367 "Only primitive values can be compared; complex values cannot".to_owned()
368 }
369
370 _ => return None,
371 })
372 }
373}
374
375#[cfg(feature = "std")]
376impl std::error::Error for ErrorKind {
377 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
378 match self {
379 Self::Wrapper(error) => Some(error),
380 Self::Arithmetic(error) => Some(error),
381 _ => None,
382 }
383 }
384}
385
386#[derive(Debug, Clone, PartialEq)]
388#[non_exhaustive]
389pub enum AuxErrorInfo {
390 FnArgs,
392
393 PrevAssignment,
395
396 Rvalue,
399
400 UnbalancedRhsTuple(usize),
402 UnbalancedRhsObject(HashSet<String>),
404
405 InvalidArg,
407
408 ArgValue(String),
410}
411
412impl AuxErrorInfo {
413 pub(crate) fn arg_value<T: fmt::Display>(value: &Value<'_, T>) -> Self {
414 Self::ArgValue(value.to_string())
415 }
416}
417
418impl fmt::Display for AuxErrorInfo {
419 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
420 match self {
421 Self::FnArgs => formatter.write_str("Function arguments declared here"),
422 Self::PrevAssignment => formatter.write_str("Previous declaration"),
423 Self::Rvalue => formatter.write_str("RHS containing the invalid assignment"),
424 Self::UnbalancedRhsTuple(size) => {
425 write!(formatter, "RHS with the {}-element tuple", size)
426 }
427 Self::UnbalancedRhsObject(fields) => {
428 write!(formatter, "RHS object with fields {:?}", fields)
429 }
430 Self::InvalidArg => formatter.write_str("Invalid argument"),
431 Self::ArgValue(val) => write!(formatter, "Has value: {}", val),
432 }
433 }
434}
435
436#[derive(Debug)]
456pub struct Error<'a> {
457 kind: ErrorKind,
458 main_span: CodeInModule<'a>,
459 aux_spans: Vec<CodeInModule<'a, AuxErrorInfo>>,
460}
461
462impl<'a> Error<'a> {
463 pub(crate) fn new<Span, T>(
464 module_id: &dyn ModuleId,
465 main_span: &LocatedSpan<Span, T>,
466 kind: ErrorKind,
467 ) -> Self
468 where
469 Span: Copy + Into<CodeFragment<'a>>,
470 {
471 Self {
472 kind,
473 main_span: CodeInModule::new(
474 module_id,
475 main_span.with_no_extra().map_fragment(Into::into),
476 ),
477 aux_spans: vec![],
478 }
479 }
480
481 pub(crate) fn from_parts(main_span: CodeInModule<'a>, kind: ErrorKind) -> Self {
482 Self {
483 kind,
484 main_span,
485 aux_spans: vec![],
486 }
487 }
488
489 pub fn with_span<T>(mut self, span: &MaybeSpanned<'a, T>, info: AuxErrorInfo) -> Self {
492 self.aux_spans.push(CodeInModule {
493 module_id: self.main_span.module_id.clone_boxed(),
494 code: span.copy_with_extra(info),
495 });
496 self
497 }
498
499 pub fn kind(&self) -> &ErrorKind {
501 &self.kind
502 }
503
504 pub fn main_span(&self) -> &CodeInModule<'a> {
506 &self.main_span
507 }
508
509 pub fn aux_spans(&self) -> &[CodeInModule<'a, AuxErrorInfo>] {
511 &self.aux_spans
512 }
513}
514
515impl fmt::Display for Error<'_> {
516 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
517 self.main_span.fmt_location(formatter)?;
518 write!(formatter, ": {}", self.kind)
519 }
520}
521
522#[cfg(feature = "std")]
523impl std::error::Error for Error<'_> {
524 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
525 Some(&self.kind)
526 }
527}
528
529impl StripCode for Error<'_> {
530 type Stripped = Error<'static>;
531
532 fn strip_code(self) -> Self::Stripped {
533 Error {
534 kind: self.kind,
535 main_span: self.main_span.strip_code(),
536 aux_spans: self
537 .aux_spans
538 .into_iter()
539 .map(StripCode::strip_code)
540 .collect(),
541 }
542 }
543}
544
545pub type EvalResult<'a, T> = Result<Value<'a, T>, Error<'a>>;
547
548#[derive(Debug)]
550pub struct CodeInModule<'a, T = ()> {
551 module_id: Box<dyn ModuleId>,
552 code: MaybeSpanned<'a, T>,
553}
554
555impl<T: Clone> Clone for CodeInModule<'_, T> {
556 fn clone(&self) -> Self {
557 Self {
558 module_id: self.module_id.clone_boxed(),
559 code: self.code.clone(),
560 }
561 }
562}
563
564impl<'a> CodeInModule<'a> {
565 pub(crate) fn new(module_id: &dyn ModuleId, span: MaybeSpanned<'a>) -> Self {
566 Self {
567 module_id: module_id.clone_boxed(),
568 code: span,
569 }
570 }
571}
572
573impl<'a, T> CodeInModule<'a, T> {
574 pub fn module_id(&self) -> &dyn ModuleId {
576 self.module_id.as_ref()
577 }
578
579 pub fn code(&self) -> &MaybeSpanned<'a, T> {
582 &self.code
583 }
584
585 fn fmt_location(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
586 write!(
587 formatter,
588 "{}:{}:{}",
589 self.module_id,
590 self.code.location_line(),
591 self.code.get_column()
592 )
593 }
594}
595
596impl<T: Clone + 'static> StripCode for CodeInModule<'_, T> {
597 type Stripped = CodeInModule<'static, T>;
598
599 fn strip_code(self) -> Self::Stripped {
600 CodeInModule {
601 module_id: self.module_id,
602 code: self.code.strip_code(),
603 }
604 }
605}
606
607#[derive(Debug, Clone)]
609#[non_exhaustive]
610pub struct BacktraceElement<'a> {
611 pub fn_name: String,
613 pub def_span: Option<CodeInModule<'a>>,
615 pub call_span: CodeInModule<'a>,
617}
618
619impl StripCode for BacktraceElement<'_> {
620 type Stripped = BacktraceElement<'static>;
621
622 fn strip_code(self) -> Self::Stripped {
623 BacktraceElement {
624 fn_name: self.fn_name,
625 def_span: self.def_span.map(StripCode::strip_code),
626 call_span: self.call_span.strip_code(),
627 }
628 }
629}
630
631#[derive(Debug, Default)]
633pub(crate) struct Backtrace<'a> {
634 calls: Vec<BacktraceElement<'a>>,
635}
636
637impl<'a> Backtrace<'a> {
638 pub fn push_call(
640 &mut self,
641 fn_name: &str,
642 def_span: Option<CodeInModule<'a>>,
643 call_span: CodeInModule<'a>,
644 ) {
645 self.calls.push(BacktraceElement {
646 fn_name: fn_name.to_owned(),
647 def_span,
648 call_span,
649 });
650 }
651
652 pub fn pop_call(&mut self) {
654 self.calls.pop();
655 }
656}
657
658impl StripCode for Backtrace<'_> {
659 type Stripped = Backtrace<'static>;
660
661 fn strip_code(self) -> Self::Stripped {
662 Backtrace {
663 calls: self.calls.into_iter().map(StripCode::strip_code).collect(),
664 }
665 }
666}
667
668#[derive(Debug)]
673pub struct ErrorWithBacktrace<'a> {
674 inner: Error<'a>,
675 backtrace: Backtrace<'a>,
676}
677
678impl<'a> ErrorWithBacktrace<'a> {
679 pub(crate) fn new(inner: Error<'a>, backtrace: Backtrace<'a>) -> Self {
680 Self { inner, backtrace }
681 }
682
683 pub fn source(&self) -> &Error<'a> {
685 &self.inner
686 }
687
688 pub fn backtrace(&self) -> impl Iterator<Item = &BacktraceElement<'a>> + '_ {
690 self.backtrace.calls.iter().rev()
691 }
692}
693
694impl fmt::Display for ErrorWithBacktrace<'_> {
695 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
696 fmt::Display::fmt(&self.inner, formatter)?;
697
698 if formatter.alternate() && !self.backtrace.calls.is_empty() {
699 writeln!(formatter, "\nBacktrace (most recent call last):")?;
700 for (index, call) in self.backtrace.calls.iter().enumerate() {
701 write!(formatter, "{:>4}: {} ", index + 1, call.fn_name)?;
702
703 if let Some(ref def_span) = call.def_span {
704 write!(formatter, "(module `{}`)", def_span.module_id)?;
705 } else {
706 formatter.write_str("(native)")?;
707 }
708
709 write!(formatter, " called at ")?;
710 call.call_span.fmt_location(formatter)?;
711 writeln!(formatter)?;
712 }
713 }
714 Ok(())
715 }
716}
717
718impl StripCode for ErrorWithBacktrace<'_> {
719 type Stripped = ErrorWithBacktrace<'static>;
720
721 fn strip_code(self) -> Self::Stripped {
722 ErrorWithBacktrace {
723 inner: self.inner.strip_code(),
724 backtrace: self.backtrace.strip_code(),
725 }
726 }
727}
728
729#[cfg(feature = "std")]
730impl std::error::Error for ErrorWithBacktrace<'_> {
731 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
732 std::error::Error::source(&self.inner)
733 }
734}
735
736#[cfg(test)]
737mod tests {
738 use super::*;
739 use crate::alloc::ToString;
740
741 #[test]
742 fn display_for_eval_error() {
743 let err = ErrorKind::Undefined("test".to_owned());
744 assert_eq!(err.to_string(), "Variable `test` is not defined");
745
746 let err = ErrorKind::ArgsLenMismatch {
747 def: LvalueLen::AtLeast(2),
748 call: 1,
749 };
750 assert!(err
751 .to_string()
752 .ends_with("definition requires at least 2 arg(s), call has 1"));
753 }
754
755 #[test]
756 fn display_for_spanned_eval_error() {
757 let input = "(_, test) = (1, 2);";
758 let main_span = MaybeSpanned::from_str(input, 4..8);
759 let err = Error::new(
760 &"test_module",
761 &main_span,
762 ErrorKind::Undefined("test".to_owned()),
763 );
764 let err_string = err.to_string();
765 assert_eq!(
766 err_string,
767 "test_module:1:5: Variable `test` is not defined"
768 );
769 }
770
771 #[test]
772 fn display_for_error_with_backtrace() {
773 let input = "(_, test) = (1, 2);";
774 let main_span = MaybeSpanned::from_str(input, 4..8);
775 let err = Error::new(&"test", &main_span, ErrorKind::Undefined("test".to_owned()));
776
777 let mut err = ErrorWithBacktrace::new(err, Backtrace::default());
778 let call_span = CodeInModule::new(&"test", MaybeSpanned::from_str(input, ..));
779 err.backtrace.push_call("test_fn", None, call_span);
780
781 let err_string = err.to_string();
782 assert_eq!(err_string, "test:1:5: Variable `test` is not defined");
783
784 let expanded_err_string = format!("{:#}", err);
785 assert!(expanded_err_string.starts_with("test:1:5: Variable `test` is not defined"));
786 assert!(expanded_err_string.contains("\nBacktrace"));
787 assert!(expanded_err_string.contains("\n 1: test_fn (native) called at test:1:1"));
788 }
789}