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}