1use std::borrow::Cow;
4use std::fmt;
5use std::rc::Rc;
6
7#[derive(Debug, Clone, Copy)]
9pub struct Location {
10 pub byte: usize,
11 pub line: u32,
12 pub column: u32,
13}
14
15impl Location {
16 pub const UNINIT: Self = Self {
19 byte: 0,
20 line: 0,
21 column: 0,
22 };
23
24 pub fn is_uninit(&self) -> bool {
26 self.line == 0 && self.column == 0
27 }
28}
29
30impl Default for Location {
32 fn default() -> Self {
33 Self {
34 byte: 0,
35 line: 1,
36 column: 1,
37 }
38 }
39}
40
41impl std::fmt::Display for Location {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}:{}", self.line, self.column)
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Hash)]
51pub struct RenderedSignature {
52 pub name: Rc<str>,
53 pub arity: Option<u16>,
55 pub trait_name: Option<Rc<str>>,
58}
59
60impl RenderedSignature {
61 pub const INVALID_NAME: &'static str = "";
64
65 pub fn invalid() -> Self {
66 Self {
67 name: Rc::from(Self::INVALID_NAME),
68 arity: None,
69 trait_name: None,
70 }
71 }
72
73 pub fn is_invalid(&self) -> bool {
74 &*self.name == Self::INVALID_NAME
75 }
76}
77
78impl fmt::Display for RenderedSignature {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 if self.is_invalid() {
81 write!(f, "(invalid method)")?;
82 return Ok(());
83 }
84
85 if let Some(arity) = self.arity {
86 write!(f, "{}/{arity}", self.name)?;
87 } else {
88 write!(f, "{}/...", self.name)?;
89 }
90
91 if let Some(trait_name) = &self.trait_name {
92 write!(f, " (as {trait_name})")?;
93 }
94
95 Ok(())
96 }
97}
98
99#[derive(Debug)]
104pub enum ErrorKind {
105 InvalidCharacter(char),
107 MissingDigitsAfterDecimalPoint,
108 MissingExponent,
109 UnderscoresWithoutDigits,
110 MissingClosingQuote,
111 InvalidEscape(char),
112 LineBreakInStringIsNotAllowed,
113 UEscapeLeftBraceExpected,
114 UEscapeMissingRightBrace,
115 UEscapeEmpty,
116 UEscapeOutOfRange,
117 InvalidBackslashLiteral(char),
118 RawStringMissingOpeningQuote,
119 IntLiteralOutOfRange,
120 IntRadixOutOfRange,
121 ColonExpectedAfterRadix,
122 CharacterMissingOpeningApostrophe,
123 CharacterMissingClosingApostrophe,
124
125 InvalidPrefixToken,
127 InvalidInfixToken,
128 MissingDo,
129 MissingRightParen,
130 MissingEnd,
131 InvalidIfBranchToken,
132 BranchAfterElse,
133 IdentifierExpected,
134 LeftParenExpected,
135 UnexpectedEof,
136 CommaExpected,
137 ColonExpectedAfterDictKey,
138 RightBracketExpectedToCloseEmptyDict,
139 InExpectedAfterForBinding,
140
141 VariableDoesNotExist(Rc<str>),
143 InvalidAssignment,
144 TooManyLocals,
145 TooManyGlobals,
146 TooManyCaptures,
147 IfBranchTooLarge,
148 IfExpressionTooLarge,
149 OperatorRhsTooLarge,
150 LoopTooLarge,
151 BreakOutsideOfLoop,
152 TooManyFunctions,
153 TooManyArguments,
154 TooManyParameters,
155 TooManyMethods,
156 InvalidMethodName,
157 FunctionKindOutsideImpl,
158 MissingFunctionBody,
159 InvalidImplItem,
160 MissingMethodName,
161 TooManyImpls,
162 MethodAlreadyImplemented(RenderedSignature),
163 TooManyFields,
164 FieldDoesNotExist(Rc<str>),
165 FieldOutsideOfImpl,
166 MissingFields(Vec<Rc<str>>),
167 ListIsTooLong,
168 DictIsTooLarge,
169 TooManyTraits,
170 InvalidTraitItem,
171 TraitMethodCannotHaveBody,
172 TraitAlreadyHasMethod(RenderedSignature),
173 AsOutsideOfImpl,
174 TooManyTraitsInImpl,
175 AsCannotNest,
176 FunctionKindInTrait,
177 InvalidPattern,
178
179 TypeError {
181 expected: Cow<'static, str>,
182 got: Cow<'static, str>,
183 },
184 MethodDoesNotExist {
185 type_name: Rc<str>,
186 signature: RenderedSignature,
187 },
188 StructAlreadyImplemented,
189 UserDataAlreadyBorrowed,
190 DoubleMethodImplementation {
191 type_name: Rc<str>,
192 signature: RenderedSignature,
193 },
194 MethodsUnimplemented {
195 type_name: Rc<str>,
196 methods: Vec<RenderedSignature>,
197 },
198
199 User(Box<dyn std::error::Error>),
200}
201
202impl std::fmt::Display for ErrorKind {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 match self {
205 Self::InvalidCharacter(c) => write!(f, "invalid character: {c:?}"),
206 Self::MissingDigitsAfterDecimalPoint => write!(f, "missing digits after decimal point"),
207 Self::MissingExponent => write!(f, "number exponent expected"),
208 Self::UnderscoresWithoutDigits => write!(f, "at least one digit expected, got only underscores"),
209 Self::MissingClosingQuote => write!(f, "missing closing quote '\"'"),
210 Self::InvalidEscape(c) => write!(f, "invalid escape: \\{c}"),
211 Self::LineBreakInStringIsNotAllowed => write!(f, "line breaks are not allowed in string literals; use \\n or a long string literal \\\\"),
212 Self::UEscapeLeftBraceExpected => write!(f, "left brace '{{' expected after \\u escape"),
213 Self::UEscapeEmpty => write!(f, "\\u escape is empty"),
214 Self::UEscapeMissingRightBrace => {
215 write!(f, "missing right brace '}}' for this \\u escape")
216 }
217 Self::UEscapeOutOfRange => write!(
218 f,
219 "Unicode scalar value in \\u escape is out of range (must be <= 10FFFF and outside of D800..DFFF)"
220 ),
221 Self::InvalidBackslashLiteral(c) => write!(f, "invalid extended literal: \\{c}"),
222 Self::RawStringMissingOpeningQuote => write!(f, "missing opening quote '\"' in \\r string"),
223 Self::IntLiteralOutOfRange => write!(f, "integer literal out of range"),
224 Self::IntRadixOutOfRange => write!(f, "integer radix out of range; must be >= 2 and <= 36"),
225 Self::ColonExpectedAfterRadix => write!(f, "colon ':' expected after integer radix"),
226 Self::CharacterMissingOpeningApostrophe => write!(f, "apostrophe ' expected to begin character literal"),
227 Self::CharacterMissingClosingApostrophe => write!(f, "apostrophe ' expected to end character literal"),
228
229 Self::InvalidPrefixToken => write!(f, "invalid token in prefix position"),
230 Self::InvalidInfixToken => write!(f, "invalid token in infix position"),
231 Self::MissingDo => write!(f, "'do' expected"),
232 Self::MissingRightParen => write!(f, "missing right parenthesis ')'"),
233 Self::MissingEnd => write!(f, "missing 'end'"),
234 Self::InvalidIfBranchToken => write!(f, "'elif', 'else', or 'end' expected"),
235 Self::BranchAfterElse => write!(f, "extraneous branch after 'else'"),
236 Self::IdentifierExpected => write!(f, "identifier expected"),
237 Self::LeftParenExpected => write!(f, "left parenthesis '(' expected"),
238 Self::UnexpectedEof => write!(f, "unexpected end of file"),
239 Self::CommaExpected => write!(f, "comma ',' expected"),
240 Self::ColonExpectedAfterDictKey => write!(f, "colon ':' expected after dict key"),
241 Self::RightBracketExpectedToCloseEmptyDict => write!(f, "right bracket ']' expected to close empty dict literal [:]"),
242 Self::MissingFunctionBody => write!(f, "missing function body ('= expression')"),
243 Self::InExpectedAfterForBinding => write!(f, "'in' expected after 'for' loop variable binding"),
244
245 Self::VariableDoesNotExist(name) => write!(f, "variable '{name}' does not exist"),
246 Self::InvalidAssignment => write!(f, "invalid left hand side of assignment"),
247 Self::TooManyLocals => write!(f, "too many local variables"),
248 Self::TooManyGlobals => write!(f, "too many global variables"),
249 Self::TooManyCaptures => write!(f, "too many variables captured in the closure"),
250 Self::IfBranchTooLarge => write!(f, "'if' branch is too large"),
251 Self::IfExpressionTooLarge => write!(f, "'if' expression is too large"),
252 Self::OperatorRhsTooLarge => write!(f, "the right-hand side of the operator is too large"),
253 Self::LoopTooLarge => write!(f, "loop is too large"),
254 Self::BreakOutsideOfLoop => write!(f, "'break' cannot be used outside of a loop"),
255 Self::TooManyFunctions => write!(f, "too many unique functions"),
256 Self::TooManyArguments => write!(f, "too many arguments"),
257 Self::TooManyParameters => write!(f, "too many parameters"),
258 Self::TooManyMethods => write!(f, "too many instance functions with different signatures"),
259 Self::InvalidMethodName => write!(f, "method name must be an identifier"),
260 Self::FunctionKindOutsideImpl => write!(
261 f,
262 "function kinds (static, constructor) can only be used in 'impl' blocks"
263 ),
264 Self::InvalidImplItem => write!(f, "only functions and 'as' blocks are allowed in 'impl' blocks"),
265 Self::MissingMethodName => write!(f, "missing method name"),
266 Self::TooManyImpls => write!(f, "too many 'impl' blocks"),
267 Self::MethodAlreadyImplemented(signature) => {
268 write!(f, "method {signature} is already implemented")
269 }
270 Self::TooManyFields => write!(f, "too many fields"),
271 Self::FieldOutsideOfImpl => {
272 write!(f, "fields cannot be referenced outside of 'impl' blocks")
273 }
274 Self::FieldDoesNotExist(name) => write!(f, "field '@{name}' does not exist"),
275 Self::MissingFields(fields) => {
276 let fields: Vec<_> = fields.iter().map(|name| format!("@{name}")).collect();
277 let fields = fields.join(", ");
278 write!(
279 f,
280 "the following fields were not assigned in this constructor: {fields}"
281 )
282 }
283 Self::ListIsTooLong => write!(f, "list literal has too many elements"),
284 Self::DictIsTooLarge => write!(f, "dict literal has too many pairs"),
285 Self::TooManyTraits => write!(f, "too many traits"),
286 Self::InvalidTraitItem => write!(f, "only function prototypes are allowed in traits"),
287 Self::TraitMethodCannotHaveBody => write!(f, "trait functions cannot have bodies"),
288 Self::TraitAlreadyHasMethod(signature) => {
289 write!(f, "trait already declares the method {signature}")
290 }
291 Self::AsOutsideOfImpl => write!(f, "'as' is not allowed outside of 'impl' blocks"),
292 Self::TooManyTraitsInImpl => write!(f, "too many 'as' blocks in 'impl'"),
293 Self::AsCannotNest => write!(f, "'as' blocks cannot nest"),
294 Self::FunctionKindInTrait => write!(f, "trait functions must be instance methods (cannot be constructors nor statics)"),
295 Self::InvalidPattern => write!(f, "invalid pattern for destructuring into variables"),
296
297 Self::TypeError { expected, got } => {
298 write!(f, "type mismatch, expected {expected} but got {got}")
299 }
300 Self::MethodDoesNotExist { type_name, signature } => write!(f, "method {} is not defined for {}", signature, type_name),
301 Self::StructAlreadyImplemented => write!(f, "this struct is already implemented"),
302 Self::UserDataAlreadyBorrowed => write!(f, "this user data is already borrowed"),
303 Self::DoubleMethodImplementation { type_name, signature } => {
304 write!(f, "method {signature} is already implemented by {type_name}")
305 }
306 Self::MethodsUnimplemented { type_name, methods } => {
307 writeln!(f, "{type_name} is missing the following trait methods:")?;
308 for (i, signature) in methods.iter().enumerate() {
309 if i > 0 {
310 writeln!(f)?;
311 }
312 write!(f, " - {signature}")?;
313 }
314 Ok(())
315 }
316
317 Self::User(error) => write!(f, "{}", error),
318 }
319 }
320}
321
322#[derive(Debug)]
324pub struct StackTraceEntry {
325 pub function_name: Rc<str>,
327 pub module_name: Rc<str>,
329 pub location: Location,
331}
332
333#[derive(Debug)]
335pub enum Error {
336 Compile {
338 kind: ErrorKind,
339 module_name: Rc<str>,
340 location: Location,
341 },
342 Runtime {
344 kind: ErrorKind,
345 call_stack: Vec<StackTraceEntry>,
346 },
347}
348
349impl std::fmt::Display for Error {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 struct FileLocation<'a>(&'a str, Location);
352
353 impl std::fmt::Display for FileLocation<'_> {
354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
355 let Self(file, location) = self;
356 if location.is_uninit() {
357 write!(f, "{}", file)
358 } else {
359 write!(f, "{}:{}", file, location)
360 }
361 }
362 }
363
364 match self {
365 Error::Compile {
366 kind,
367 module_name,
368 location,
369 } => {
370 write!(f, "{module_name}:{location}: error: {kind}")
371 }
372 Error::Runtime { kind, call_stack } => {
373 writeln!(f, "error: {}", kind)?;
374 write!(f, "stack traceback (most recent call first):")?;
375 let file_location_width = call_stack
376 .iter()
377 .map(|entry| FileLocation(&entry.module_name, entry.location).to_string().len())
378 .max()
379 .unwrap_or(20);
380 for entry in call_stack.iter().rev() {
381 write!(
382 f,
383 "\n {:width$} {}",
384 FileLocation(&entry.module_name, entry.location).to_string(),
386 entry.function_name,
387 width = file_location_width,
388 )?;
389 }
390 Ok(())
391 }
392 }
393 }
394}