avid/errors.rs
1use std::{fmt::Display, sync};
2
3use crate::ObjectType;
4
5// These are so the docs link correctly.
6#[allow(unused_imports)]
7use crate::{Builder, Stack, PromiseBuilder, Promises};
8
9/// A shorthand for a Result type with Avid programs.
10///
11/// This is more for convenience than anything else, so it can be pretty safely ignored.
12pub type Result<T, E = Error> = std::result::Result<T, E>;
13
14/// An error in an Avid program, along with the location it occurred.
15///
16/// # Examples
17/// ```
18/// # let source_code = "";
19/// use avid::*;
20///
21/// if let Err(e) = Builder::new(source_code).build() {
22/// eprintln!("There was an error at {}!", e.loc);
23/// if let ErrorKind::UnclosedIf = e.kind {
24/// eprintln!("You forgot to close your if statement!");
25/// } else {
26/// eprintln!("All if statements are good.")
27/// }
28/// }
29/// ```
30#[derive(Debug, PartialEq)]
31pub struct Error {
32 /// The type of error that was thrown.
33 pub kind: ErrorKind,
34 /// The location where the error was thrown.
35 pub loc: Location,
36}
37
38impl Error {
39 pub(crate) fn new(kind: ErrorKind, loc: Location) -> Self {
40 Self {
41 kind,
42 loc
43 }
44 }
45}
46
47impl Display for Error {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 writeln!(f, "{}: Error: {}", self.loc, self.kind)
50 }
51}
52
53/// A location in a file.
54#[derive(Debug, PartialEq, Eq, Clone)]
55pub struct Location {
56 /// The line where the error occurred.
57 pub line: usize,
58 /// The column where the error occurred.
59 pub col: usize,
60 /// The file in which the error occurred.
61 ///
62 /// This file is sometimes not provided if the method [Builder::src_name] is not used. In
63 /// this case, `file_name` is `None`.
64 // TODO(#14): Make Location.file_name use &str instead of Arc<String>.
65 // This should be possible if the ownership of the file names is moved into the
66 // Avid instance/Ast.
67 pub file_name: Option<sync::Arc<String>>,
68}
69
70impl Display for Location {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{}:{}:{}", self.file_name.as_ref().map_or("<provided>", |a| a.as_str()), self.line, self.col)
73 }
74}
75
76impl Display for ErrorKind {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 ErrorKind::NotEnoughArgs { expected, got } => write!(f, "Not Enough Arguments: expected {expected}, got {got}!"),
80 ErrorKind::IncorrectType { expected, got } => write!(f, "Incorrect Argument Type: expected {expected:?}, got {got:?}!"),
81 ErrorKind::UnknownVar { name } => write!(f, "Unknown Variable: \"{name}\"!"),
82 ErrorKind::UnfulfilledPromise { expected } => write!(f, "Unfulfilled Promise: \"{expected}\"!\nNote: This is ususally the fault of the program interpreting the code. You should file a bug report if possible."),
83 ErrorKind::UnpairedEnd => write!(f, "`End` Without Start of Block!"),
84 ErrorKind::UnclosedIf => write!(f, "`if` Statement Without Matching `end`!"),
85 ErrorKind::UnclosedWhile => write!(f, "`while` Statement Without Matching `end`!"),
86 ErrorKind::WhileWithoutDo => write!(f, "`while` Statement Without `do`!"),
87 ErrorKind::DoWithoutWhile => write!(f, "Unexpected `do`!"),
88 ErrorKind::UnclosedString => write!(f, "Unclosed String!"),
89 ErrorKind::IncorrectNumber => write!(f, "Unparseable Number!"),
90 ErrorKind::UnknownEscape => write!(f, "Unknown Escaped Character!"),
91 }
92 }
93}
94
95/// Errors that can happen when running an Avid program.
96///
97/// These errors are caused by the authors of the Avid code and should be caught.
98/// When writing APIs with [register_fn](Builder::register_fn()), it is expected that you return the
99/// correct kind of error.
100#[derive(Debug, PartialEq)]
101// TODO(#15): Separate out errors into compile-time errors and runtime errors.
102// Maybe also collect the compile time errors into a Vec<CompileError> or something.
103pub enum ErrorKind {
104 /// When not enough arguments are present on the stack for an operation.
105 ///
106 /// This error is automatically returned by [pop](Stack::pop) and [pop_typed](Stack::pop_typed) when
107 /// there are insufficient arguments.
108 NotEnoughArgs {
109 /// The number of items expected on the stack.
110 expected: usize,
111 /// The number of items actually on the stack.
112 got: usize
113 },
114 /// When the arguments on the stack are the incorrect kind for the operation.
115 IncorrectType {
116 /// The types expected on the stack.
117 ///
118 /// Currently, they must be requested statically, but they will be able to be requested
119 /// dynamically in a later update.
120 // TODO(#16): Make ErrorKind::IncorrectType.expected use Vec<ObjectType> instead of &'static [ObjectType].
121 //
122 // This will make it much easier for functions that might have dynamic signatures.
123 expected: &'static [ObjectType],
124 /// The types actually found on the stack.
125 got: Vec<ObjectType>,
126 },
127 /// When the user tries to use a variable or function that does not exist.
128 UnknownVar {
129 /// The name of the unknown variable or function.
130 name: String
131 },
132 /// When promises do not get fulfilled.
133 ///
134 /// Promises can be made with [Builder::promise] and fulfilled by passing them into
135 /// a [Promises] struct with [PromiseBuilder::add_promise].
136 UnfulfilledPromise {
137 /// The promise that was made in [Builder::build] but not found at runtime.
138 expected: String
139 },
140 /// An unpaired `end`, which is expected to finish a statement but there is no statement found.
141 UnpairedEnd,
142 /// An unclosed `if` statement, such as `true if print`.
143 UnclosedIf,
144 /// An unclosed `while` statement, such as `true while do print`.
145 UnclosedWhile,
146 /// A while loop without the `do`. For example: `while true end`.
147 WhileWithoutDo,
148 /// A `do ... end` statement without a starting `while`.
149 DoWithoutWhile,
150 /// An unclosed string. The location in the source code will point to the end of
151 /// the data of the string, right before where closing quotes should be.
152 UnclosedString,
153 /// An unparseable number. The location in the source code will point to the start
154 /// of where the number should be.
155 IncorrectNumber,
156 /// An invalid escape code. The current valid escapes in Avid are: `\n`, `\\n`
157 /// (cancels out a new line in a string), `\t`, `\\`, `\"`, `\'`, and `\r`.
158 UnknownEscape,
159}