1use core::fmt;
4
5pub use arithmetic_parser::UnsupportedType;
6use arithmetic_parser::{BinaryOp, LocatedSpan, Location, LvalueLen, Op, UnaryOp};
7
8use crate::{
9 alloc::{format, vec, Arc, Box, HashSet, String, ToOwned, ToString, Vec},
10 exec::ModuleId,
11 fns::FromValueError,
12 Value,
13};
14
15#[derive(Debug)]
19#[non_exhaustive]
20pub enum ArithmeticError {
21 IntegerOverflow,
23 DivisionByZero,
25 InvalidExponent,
30 NoInverse,
34 InvalidOp(anyhow::Error),
39}
40
41impl ArithmeticError {
42 pub fn invalid_op(message: impl Into<String>) -> Self {
44 Self::InvalidOp(anyhow::Error::msg(message.into()))
45 }
46}
47
48impl fmt::Display for ArithmeticError {
49 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::IntegerOverflow => formatter.write_str("integer overflow or underflow"),
52 Self::DivisionByZero => formatter.write_str("integer division by zero"),
53 Self::InvalidExponent => formatter.write_str("exponent is too large or negative"),
54 Self::NoInverse => formatter.write_str("integer has no multiplicative inverse"),
55 Self::InvalidOp(err) => write!(formatter, "invalid operation: {err}"),
56 }
57 }
58}
59
60#[cfg(feature = "std")]
61impl std::error::Error for ArithmeticError {
62 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
63 match self {
64 Self::InvalidOp(e) => Some(e.as_ref()),
65 _ => None,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum TupleLenMismatchContext {
73 BinaryOp(BinaryOp),
75 Assignment,
77}
78
79impl fmt::Display for TupleLenMismatchContext {
80 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 Self::BinaryOp(op) => write!(formatter, "{op}"),
83 Self::Assignment => formatter.write_str("assignment"),
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum RepeatedAssignmentContext {
91 FnArgs,
93 Assignment,
95}
96
97impl fmt::Display for RepeatedAssignmentContext {
98 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
99 formatter.write_str(match self {
100 Self::FnArgs => "function args",
101 Self::Assignment => "assignment",
102 })
103 }
104}
105
106#[derive(Debug)]
108#[non_exhaustive]
109pub enum ErrorKind {
110 TupleLenMismatch {
112 lhs: LvalueLen,
114 rhs: usize,
116 context: TupleLenMismatchContext,
118 },
119
120 FieldsMismatch {
122 lhs_fields: HashSet<String>,
124 rhs_fields: HashSet<String>,
126 op: BinaryOp,
128 },
129
130 ArgsLenMismatch {
132 def: LvalueLen,
134 call: usize,
136 },
137
138 CannotDestructure,
140
141 RepeatedAssignment {
143 context: RepeatedAssignmentContext,
145 },
146
147 RepeatedField,
150
151 Undefined(String),
153 Uninitialized(String),
155
156 InvalidFieldName(String),
158
159 CannotCall,
161 CannotIndex,
163 CannotAccessFields,
165
166 IndexOutOfBounds {
168 index: usize,
170 len: usize,
172 },
173 NoField {
175 field: String,
177 available_fields: Vec<String>,
179 },
180
181 NativeCall(String),
183
184 Wrapper(FromValueError),
186
187 UnexpectedOperand {
189 op: Op,
191 },
192
193 CannotCompare,
196
197 Unsupported(UnsupportedType),
199
200 Arithmetic(ArithmeticError),
202}
203
204impl fmt::Display for ErrorKind {
205 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
206 match self {
207 Self::TupleLenMismatch { context, lhs, rhs } => {
208 write!(
209 formatter,
210 "Mismatch between length of tuples in {context}: LHS has {lhs} element(s), whereas RHS has {rhs}"
211 )
212 }
213 Self::FieldsMismatch {
214 op,
215 lhs_fields,
216 rhs_fields,
217 } => {
218 write!(
219 formatter,
220 "Cannot perform {op} on objects: LHS has fields {lhs_fields:?}, whereas RHS has fields {rhs_fields:?}"
221 )
222 }
223 Self::ArgsLenMismatch { def, call } => {
224 write!(
225 formatter,
226 "Mismatch between the number of arguments in the function definition and its call: \
227 definition requires {def} arg(s), call has {call}"
228 )
229 }
230 Self::CannotDestructure => {
231 formatter.write_str("Cannot destructure a non-tuple variable")
232 }
233 Self::RepeatedAssignment { context } => write!(
234 formatter,
235 "Repeated assignment to the same variable in {context}"
236 ),
237 Self::RepeatedField => formatter.write_str("Repeated object field"),
238 Self::Undefined(var) => write!(formatter, "Variable `{var}` is not defined"),
239 Self::Uninitialized(var) => write!(formatter, "Variable `{var}` is not initialized"),
240 Self::InvalidFieldName(name) => write!(formatter, "`{name}` is not a valid field name"),
241 Self::CannotCall => formatter.write_str("Value is not callable"),
242 Self::CannotIndex => formatter.write_str("Value cannot be indexed"),
243 Self::CannotAccessFields => {
244 formatter.write_str("Fields cannot be accessed for the object")
245 }
246 Self::IndexOutOfBounds { index, len } => write!(
247 formatter,
248 "Attempting to get element {index} from tuple with length {len}"
249 ),
250 Self::NoField { field, .. } => write!(formatter, "Object does not have field {field}"),
251 Self::NativeCall(err) => write!(formatter, "Failed executing native function: {err}"),
252 Self::Wrapper(err) => write!(
253 formatter,
254 "Failed converting arguments for native function wrapper: {err}"
255 ),
256 Self::UnexpectedOperand { op } => write!(formatter, "Unexpected operand type for {op}"),
257 Self::CannotCompare => formatter.write_str("Value cannot be compared to other values"),
258 Self::Unsupported(ty) => write!(formatter, "Unsupported {ty}"),
259 Self::Arithmetic(err) => write!(formatter, "Arithmetic error: {err}"),
260 }
261 }
262}
263
264impl ErrorKind {
265 pub fn native(message: impl Into<String>) -> Self {
267 Self::NativeCall(message.into())
268 }
269
270 pub fn unsupported<T: Into<UnsupportedType>>(ty: T) -> Self {
272 Self::Unsupported(ty.into())
273 }
274
275 pub fn to_short_string(&self) -> String {
277 match self {
278 Self::TupleLenMismatch { context, .. } => {
279 format!("Mismatch between length of tuples in {context}")
280 }
281 Self::FieldsMismatch { op, .. } => {
282 format!("Mismatch between object shapes during {op}")
283 }
284 Self::ArgsLenMismatch { .. } => {
285 "Mismatch between the number of arguments in the function definition and its call"
286 .to_owned()
287 }
288 Self::CannotDestructure => "Cannot destructure a non-tuple variable".to_owned(),
289 Self::RepeatedAssignment { context } => {
290 format!("Repeated assignment to the same variable in {context}")
291 }
292 Self::RepeatedField => "Repeated object field".to_owned(),
293 Self::Undefined(name) => format!("Variable `{name}` is not defined"),
294 Self::Uninitialized(name) => format!("Variable `{name}` is not initialized"),
295 Self::InvalidFieldName(name) => format!("`{name}` is not a valid field name"),
296 Self::CannotCall => "Value is not callable".to_owned(),
297 Self::CannotIndex => "Value cannot be indexed".to_owned(),
298 Self::CannotAccessFields => "Value has no fields".to_owned(),
299 Self::IndexOutOfBounds { len, .. } => {
300 format!("Index out of bounds for tuple with length {len}")
301 }
302 Self::NoField { field, .. } => format!("Object does not have field {field}"),
303 Self::NativeCall(message) => message.clone(),
304 Self::Wrapper(err) => err.to_string(),
305 Self::UnexpectedOperand { op } => format!("Unexpected operand type for {op}"),
306 Self::CannotCompare => "Value is not comparable".to_owned(),
307 Self::Unsupported(_) => "Grammar construct not supported".to_owned(),
308 Self::Arithmetic(_) => "Arithmetic error".to_owned(),
309 }
310 }
311
312 pub fn main_span_info(&self) -> String {
314 match self {
315 Self::TupleLenMismatch { context, lhs, .. } => {
316 format!("LHS of {context} with {lhs} element(s)")
317 }
318 Self::FieldsMismatch { lhs_fields, .. } => {
319 format!("LHS with fields {lhs_fields:?}")
320 }
321 Self::ArgsLenMismatch { call, .. } => format!("Called with {call} arg(s) here"),
322 Self::CannotDestructure => "Failed destructuring".to_owned(),
323 Self::RepeatedAssignment { .. } => "Re-assigned variable".to_owned(),
324 Self::RepeatedField => "Repeated object field".to_owned(),
325 Self::Undefined(_) => "Undefined variable occurrence".to_owned(),
326 Self::Uninitialized(_) => "Uninitialized value".to_owned(),
327 Self::InvalidFieldName(_) => "Invalid field".to_owned(),
328 Self::CannotIndex | Self::IndexOutOfBounds { .. } => "Indexing operation".to_owned(),
329 Self::CannotAccessFields | Self::NoField { .. } => "Field access".to_owned(),
330 Self::CannotCall | Self::NativeCall(_) | Self::Wrapper(_) => "Failed call".to_owned(),
331 Self::UnexpectedOperand { .. } => "Operand of wrong type".to_owned(),
332 Self::CannotCompare => "Cannot be compared".to_owned(),
333 Self::Unsupported(ty) => format!("Unsupported {ty}"),
334 Self::Arithmetic(e) => e.to_string(),
335 }
336 }
337
338 pub fn help(&self) -> Option<String> {
340 Some(match self {
341 Self::TupleLenMismatch { context, .. } => format!(
342 "If both args of {context} are tuples, the number of elements in them must agree"
343 ),
344 Self::FieldsMismatch { op, .. } => {
345 format!("If both args of {op} are objects, their field names must be the same")
346 }
347 Self::CannotDestructure => "Only tuples can be destructured".to_owned(),
348 Self::RepeatedAssignment { context } => {
349 format!("In {context}, all assigned variables must have different names")
350 }
351 Self::RepeatedField => "Field names in objects must be unique".to_owned(),
352 Self::InvalidFieldName(_) => "Field names must be `usize`s or identifiers".to_owned(),
353 Self::CannotCall => "Only functions are callable, i.e., can be used as `fn_name` \
354 in `fn_name(...)` expressions"
355 .to_owned(),
356 Self::CannotIndex => "Only tuples can be indexed".to_owned(),
357 Self::CannotAccessFields => "Only objects have fields".to_owned(),
358
359 Self::UnexpectedOperand { op: Op::Binary(op) } if op.is_arithmetic() => {
360 "Operands of binary arithmetic ops must be primitive values or tuples / objects \
361 consisting of primitive values"
362 .to_owned()
363 }
364 Self::UnexpectedOperand { op: Op::Binary(op) } if op.is_comparison() => {
365 "Operands of comparison ops must be primitive values".to_owned()
366 }
367 Self::UnexpectedOperand {
368 op: Op::Binary(BinaryOp::And | BinaryOp::Or),
369 } => "Operands of binary boolean ops must be boolean".to_owned(),
370 Self::UnexpectedOperand {
371 op: Op::Unary(UnaryOp::Neg),
372 } => "Operand of negation must be primitive values or tuples / objects \
373 consisting of primitive values"
374 .to_owned(),
375 Self::UnexpectedOperand {
376 op: Op::Unary(UnaryOp::Not),
377 } => "Operand of boolean negation must be boolean".to_owned(),
378
379 Self::CannotCompare => {
380 "Only primitive values can be compared; complex values cannot".to_owned()
381 }
382
383 _ => return None,
384 })
385 }
386}
387
388#[cfg(feature = "std")]
389impl std::error::Error for ErrorKind {
390 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
391 match self {
392 Self::Wrapper(error) => Some(error),
393 Self::Arithmetic(error) => Some(error),
394 _ => None,
395 }
396 }
397}
398
399#[derive(Debug, Clone, PartialEq, Eq)]
401#[non_exhaustive]
402pub enum AuxErrorInfo {
403 FnArgs,
405
406 PrevAssignment,
408
409 Rvalue,
412
413 UnbalancedRhsTuple(usize),
415 UnbalancedRhsObject(HashSet<String>),
417
418 InvalidArg,
420
421 ArgValue(String),
423}
424
425impl AuxErrorInfo {
426 pub(crate) fn arg_value<T: fmt::Display>(value: &Value<T>) -> Self {
427 Self::ArgValue(value.to_string())
428 }
429}
430
431impl fmt::Display for AuxErrorInfo {
432 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
433 match self {
434 Self::FnArgs => formatter.write_str("Function arguments declared here"),
435 Self::PrevAssignment => formatter.write_str("Previous declaration"),
436 Self::Rvalue => formatter.write_str("RHS containing the invalid assignment"),
437 Self::UnbalancedRhsTuple(size) => {
438 write!(formatter, "RHS with the {size}-element tuple")
439 }
440 Self::UnbalancedRhsObject(fields) => {
441 write!(formatter, "RHS object with fields {fields:?}")
442 }
443 Self::InvalidArg => formatter.write_str("Invalid argument"),
444 Self::ArgValue(val) => write!(formatter, "Has value: {val}"),
445 }
446 }
447}
448
449#[derive(Debug)]
451pub struct Error {
452 kind: Box<ErrorKind>,
453 main_location: LocationInModule,
454 aux_locations: Vec<LocationInModule<AuxErrorInfo>>,
455}
456
457impl Error {
458 pub(crate) fn new<Span, T>(
459 module_id: Arc<dyn ModuleId>,
460 main_span: &LocatedSpan<Span, T>,
461 kind: ErrorKind,
462 ) -> Self
463 where
464 Span: Copy,
465 Location: From<LocatedSpan<Span>>,
466 {
467 Self {
468 kind: Box::new(kind),
469 main_location: LocationInModule::new(module_id, main_span.with_no_extra().into()),
470 aux_locations: vec![],
471 }
472 }
473
474 pub(crate) fn from_parts(main_span: LocationInModule, kind: ErrorKind) -> Self {
475 Self {
476 kind: Box::new(kind),
477 main_location: main_span,
478 aux_locations: vec![],
479 }
480 }
481
482 #[must_use]
485 pub fn with_location<T>(mut self, location: &Location<T>, info: AuxErrorInfo) -> Self {
486 self.aux_locations.push(LocationInModule {
487 module_id: self.main_location.module_id.clone(),
488 location: location.copy_with_extra(info),
489 });
490 self
491 }
492
493 pub fn kind(&self) -> &ErrorKind {
495 &self.kind
496 }
497
498 pub fn location(&self) -> &LocationInModule {
500 &self.main_location
501 }
502
503 pub fn aux_spans(&self) -> &[LocationInModule<AuxErrorInfo>] {
505 &self.aux_locations
506 }
507}
508
509impl fmt::Display for Error {
510 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
511 self.main_location.fmt_location(formatter)?;
512 write!(formatter, ": {}", self.kind)
513 }
514}
515
516#[cfg(feature = "std")]
517impl std::error::Error for Error {
518 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
519 Some(&self.kind)
520 }
521}
522
523pub type EvalResult<T> = Result<Value<T>, Error>;
525
526#[derive(Debug, Clone)]
528pub struct LocationInModule<T = ()> {
529 module_id: Arc<dyn ModuleId>,
530 location: Location<T>,
531}
532
533impl LocationInModule {
534 pub(crate) fn new(module_id: Arc<dyn ModuleId>, location: Location) -> Self {
535 Self {
536 module_id,
537 location,
538 }
539 }
540}
541
542impl<T> LocationInModule<T> {
543 pub fn module_id(&self) -> &dyn ModuleId {
545 self.module_id.as_ref()
546 }
547
548 pub fn in_module(&self) -> &Location<T> {
551 &self.location
552 }
553
554 pub(crate) fn fmt_location(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
555 write!(
556 formatter,
557 "{}:{}:{}",
558 self.module_id,
559 self.location.location_line(),
560 self.location.get_column()
561 )
562 }
563}
564
565#[derive(Debug, Clone)]
567#[non_exhaustive]
568pub struct BacktraceElement {
569 pub fn_name: String,
571 pub def_location: Option<LocationInModule>,
573 pub call_location: LocationInModule,
575}
576
577#[derive(Debug, Default)]
579pub(crate) struct Backtrace {
580 calls: Vec<BacktraceElement>,
581}
582
583impl Backtrace {
584 pub fn push_call(
586 &mut self,
587 fn_name: &str,
588 def_location: Option<LocationInModule>,
589 call_location: LocationInModule,
590 ) {
591 self.calls.push(BacktraceElement {
592 fn_name: fn_name.to_owned(),
593 def_location,
594 call_location,
595 });
596 }
597
598 pub fn pop_call(&mut self) {
600 self.calls.pop();
601 }
602}
603
604#[derive(Debug)]
606pub struct ErrorWithBacktrace {
607 inner: Error,
608 backtrace: Backtrace,
609}
610
611impl ErrorWithBacktrace {
612 pub(crate) fn new(inner: Error, backtrace: Backtrace) -> Self {
613 Self { inner, backtrace }
614 }
615
616 pub fn source(&self) -> &Error {
618 &self.inner
619 }
620
621 pub fn backtrace(&self) -> impl Iterator<Item = &BacktraceElement> + '_ {
623 self.backtrace.calls.iter().rev()
624 }
625}
626
627impl fmt::Display for ErrorWithBacktrace {
628 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
629 fmt::Display::fmt(&self.inner, formatter)?;
630
631 if formatter.alternate() && !self.backtrace.calls.is_empty() {
632 writeln!(formatter, "\nBacktrace (most recent call last):")?;
633 for (index, call) in self.backtrace.calls.iter().enumerate() {
634 write!(formatter, "{:>4}: {} ", index + 1, call.fn_name)?;
635
636 if let Some(ref def_span) = call.def_location {
637 write!(formatter, "(module `{}`)", def_span.module_id)?;
638 } else {
639 formatter.write_str("(native)")?;
640 }
641
642 write!(formatter, " called at ")?;
643 call.call_location.fmt_location(formatter)?;
644 writeln!(formatter)?;
645 }
646 }
647 Ok(())
648 }
649}
650
651#[cfg(feature = "std")]
652impl std::error::Error for ErrorWithBacktrace {
653 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
654 std::error::Error::source(&self.inner)
655 }
656}
657
658#[cfg(test)]
659mod tests {
660 use super::*;
661 use crate::alloc::ToString;
662
663 #[test]
664 fn display_for_eval_error() {
665 let err = ErrorKind::Undefined("test".to_owned());
666 assert_eq!(err.to_string(), "Variable `test` is not defined");
667
668 let err = ErrorKind::ArgsLenMismatch {
669 def: LvalueLen::AtLeast(2),
670 call: 1,
671 };
672 assert!(err
673 .to_string()
674 .ends_with("definition requires at least 2 arg(s), call has 1"));
675 }
676
677 #[test]
678 fn display_for_spanned_eval_error() {
679 let input = "(_, test) = (1, 2);";
680 let main_span = Location::from_str(input, 4..8);
681 let err = Error::new(
682 Arc::new("test_module"),
683 &main_span,
684 ErrorKind::Undefined("test".to_owned()),
685 );
686 let err_string = err.to_string();
687 assert_eq!(
688 err_string,
689 "test_module:1:5: Variable `test` is not defined"
690 );
691 }
692
693 #[test]
694 fn display_for_error_with_backtrace() {
695 let input = "(_, test) = (1, 2);";
696 let main_span = Location::from_str(input, 4..8);
697 let err = Error::new(
698 Arc::new("test"),
699 &main_span,
700 ErrorKind::Undefined("test".to_owned()),
701 );
702
703 let mut err = ErrorWithBacktrace::new(err, Backtrace::default());
704 let call_span = LocationInModule::new(Arc::new("test"), Location::from_str(input, ..));
705 err.backtrace.push_call("test_fn", None, call_span);
706
707 let err_string = err.to_string();
708 assert_eq!(err_string, "test:1:5: Variable `test` is not defined");
709
710 let expanded_err_string = format!("{err:#}");
711 assert!(expanded_err_string.starts_with("test:1:5: Variable `test` is not defined"));
712 assert!(expanded_err_string.contains("\nBacktrace"));
713 assert!(expanded_err_string.contains("\n 1: test_fn (native) called at test:1:1"));
714 }
715}