1use 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#[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#[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#[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}