lucia_lang/
errors.rs

1//! Errors of this crate.
2
3use std::{
4    fmt,
5    hash::{Hash, Hasher},
6    ops::{Bound, RangeBounds},
7};
8
9use gc_arena::Collect;
10use thiserror::Error;
11
12use crate::{
13    compiler::opcode::OpCode,
14    frame::{Frame, FrameMode},
15    meta_ops::MetaMethod,
16    objects::{Closure, Value, ValueType},
17};
18
19/// Lucia error.
20#[derive(Error, Debug, Clone, Collect)]
21#[collect(no_drop)]
22#[error("{kind}")]
23pub struct Error<'gc> {
24    pub kind: ErrorKind<'gc>,
25    pub traceback: Option<Vec<Frame<'gc>>>,
26}
27
28impl<'gc> PartialEq for Error<'gc> {
29    fn eq(&self, other: &Self) -> bool {
30        self.kind == other.kind
31    }
32}
33
34impl<'gc> Eq for Error<'gc> {}
35
36impl<'gc> Hash for Error<'gc> {
37    fn hash<H: Hasher>(&self, state: &mut H) {
38        self.kind.hash(state);
39    }
40}
41
42impl<'gc> Error<'gc> {
43    pub fn new(kind: ErrorKind<'gc>) -> Self {
44        Error {
45            kind,
46            traceback: None,
47        }
48    }
49
50    pub fn with_traceback(kind: ErrorKind<'gc>, traceback: Vec<Frame<'gc>>) -> Self {
51        Error {
52            kind,
53            traceback: Some(traceback),
54        }
55    }
56}
57
58impl<'gc> From<Value<'gc>> for Error<'gc> {
59    fn from(value: Value<'gc>) -> Self {
60        Error::new(ErrorKind::LuciaError(value))
61    }
62}
63
64/// Kind of Lucia Error.
65#[derive(Error, Debug, Clone, Collect, PartialEq, Eq, Hash)]
66#[collect(no_drop)]
67pub enum ErrorKind<'gc> {
68    #[error("bad frame mode (expected {expected}, found {found})")]
69    BadFrameMode {
70        expected: FrameMode,
71        found: FrameMode,
72    },
73    #[error("{0}")]
74    LuciaError(Value<'gc>),
75    #[error("{0}")]
76    UserPanic(Value<'gc>),
77    #[error("assert error: {0}")]
78    AssertError(Value<'gc>),
79    #[error("unexpected type error (expected {expected}, found {found})")]
80    UnexpectedType {
81        expected: ValueType,
82        found: ValueType,
83    },
84    #[error("call arguments error (required {required} arguments, but {given} was given)")]
85    CallArguments {
86        value: Option<Closure<'gc>>,
87        required: CallArgumentsErrorKind,
88        given: usize,
89    },
90    #[error("operator error (unsupported operand type(s) for {operator}: {operand})")]
91    UnOperator {
92        operator: OpCode,
93        operand: ValueType,
94    },
95    #[error("operator error (unsupported operand type(s) for {operator}: {} and {})", .operand.0, .operand.1)]
96    BinOperator {
97        operator: OpCode,
98        operand: (ValueType, ValueType),
99    },
100    #[error("operator error (unsupported operand type(s) for {operator}: {operand})")]
101    MetaUnOperator {
102        operator: MetaMethod,
103        operand: ValueType,
104    },
105    #[error("operator error (unsupported operand type(s) for {operator}: {} and {})", .operand.0, .operand.1)]
106    MetaBinOperator {
107        operator: MetaMethod,
108        operand: (ValueType, ValueType),
109    },
110}
111
112impl<'gc> ErrorKind<'gc> {
113    pub const fn recoverable(&self) -> bool {
114        !matches!(
115            self,
116            ErrorKind::BadFrameMode { .. } | ErrorKind::UserPanic(_)
117        )
118    }
119}
120
121/// Kind of CallArgumentsError.
122#[derive(Debug, Clone, Collect, PartialEq, Eq, Hash)]
123#[collect(require_static)]
124pub struct CallArgumentsErrorKind {
125    pub start: usize,
126    pub end: Option<usize>,
127}
128
129impl CallArgumentsErrorKind {
130    pub fn new(start: usize, end: Option<usize>) -> Self {
131        Self { start, end }
132    }
133
134    pub fn more_then(start: usize) -> Self {
135        Self { start, end: None }
136    }
137}
138
139impl From<usize> for CallArgumentsErrorKind {
140    fn from(value: usize) -> Self {
141        CallArgumentsErrorKind {
142            start: value,
143            end: Some(value),
144        }
145    }
146}
147
148impl From<(usize, usize)> for CallArgumentsErrorKind {
149    fn from(value: (usize, usize)) -> Self {
150        CallArgumentsErrorKind {
151            start: value.0,
152            end: Some(value.1),
153        }
154    }
155}
156
157impl From<(usize, Option<usize>)> for CallArgumentsErrorKind {
158    fn from(value: (usize, Option<usize>)) -> Self {
159        CallArgumentsErrorKind {
160            start: value.0,
161            end: value.1,
162        }
163    }
164}
165
166impl RangeBounds<usize> for CallArgumentsErrorKind {
167    fn start_bound(&self) -> Bound<&usize> {
168        Bound::Included(&self.start)
169    }
170
171    fn end_bound(&self) -> Bound<&usize> {
172        if let Some(end) = &self.end {
173            Bound::Included(end)
174        } else {
175            Bound::Unbounded
176        }
177    }
178}
179
180impl CallArgumentsErrorKind {
181    pub fn contains(&self, item: &usize) -> bool {
182        <Self as RangeBounds<usize>>::contains(self, item)
183    }
184}
185
186impl fmt::Display for CallArgumentsErrorKind {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        if let Some(end) = self.end {
189            if self.start == end {
190                write!(f, "{}", end)
191            } else {
192                write!(f, "[{}, {}]", self.start, end)
193            }
194        } else {
195            write!(f, "at least {}", self.start)
196        }
197    }
198}