cas_vm/
error.rs

1use ariadne::Fmt;
2use cas_attrs::ErrorKind;
3use cas_error::EXPR;
4use cas_parser::parser::token::op::{BinOpKind, UnaryOpKind};
5use std::ops::Range;
6
7/// The given binary operation cannot be applied to the given operands.
8#[derive(Debug, Clone, ErrorKind, PartialEq)]
9#[error(
10    message = format!("cannot apply the `{:?}` operator to these operands", self.op),
11    labels = [
12        format!("this operand has type `{}`", self.left),
13        format!("this {}operator", if self.implicit { "(implicit) " } else { "" }),
14        format!("this operand has type `{}`", self.right),
15    ],
16)]
17pub struct InvalidBinaryOperation {
18    /// The operator that was used.
19    pub op: BinOpKind,
20
21    /// Whether the operator was implicitly inserted by the parser.
22    pub implicit: bool,
23
24    /// The type the left side evaluated to.
25    pub left: &'static str,
26
27    /// The type the right side evaluated to.
28    pub right: &'static str,
29}
30
31/// The given unary operation cannot be applied to the given operand.
32#[derive(Debug, Clone, ErrorKind, PartialEq)]
33#[error(
34    message = format!("cannot apply the `{:?}` operator to this operand", self.op),
35    labels = [
36        format!("this operand has type `{}`", self.expr_type),
37        "this operator".to_string(),
38    ],
39)]
40pub struct InvalidUnaryOperation {
41    /// The operator that was used.
42    pub op: UnaryOpKind,
43
44    /// The type the operand evaluated to.
45    pub expr_type: &'static str,
46}
47
48/// Attempted to bitshift an integer by too many bits.
49#[derive(Debug, Clone, ErrorKind, PartialEq)]
50#[error(
51    message = "maximum bitshift amount exceeded",
52    labels = ["this expression", "", "too many bits to shift by"],
53    help = "the maximum number of bits you can shift an integer by is equal to: `2^64 - 1`"
54)]
55pub struct BitshiftOverflow;
56
57/// Cannot differentiate a function using prime notation.
58#[derive(Debug, Clone, ErrorKind, PartialEq)]
59#[error(
60    message = format!(
61        "cannot differentiate `{}` function using prime notation",
62        self.name
63    ),
64    labels = ["this function"],
65    help = format!("only functions with a *single parameter* can be differentiated using prime notation; the `{}` function has {} parameter(s)", (&self.name).fg(EXPR), self.actual),
66    note = "consider partially applying the function (i.e. `f(x) = log(x, 2)`) to make it differentiable",
67)]
68pub struct InvalidDifferentiation {
69    /// The name of the function that was called.
70    pub name: String,
71
72    /// The number of arguments the function actually takes.
73    pub actual: usize,
74}
75
76/// Too many arguments were given to a function call.
77#[derive(Debug, Clone, ErrorKind, PartialEq)]
78#[error(
79    message = format!("too many arguments given to the `{}` function", self.name),
80    labels = ["this function call", "", "these argument(s) are extraneous"],
81    help = format!(
82        "the `{}` function takes {} argument(s); there are {} argument(s) provided here",
83        (&self.name).fg(EXPR),
84        self.expected,
85        self.given
86    ),
87    note = format!("function signature: `{}`", self.signature),
88)]
89pub struct TooManyArguments {
90    /// The name of the function that was called.
91    pub name: String,
92
93    /// The number of arguments that were expected.
94    pub expected: usize,
95
96    /// The number of arguments that were given.
97    pub given: usize,
98
99    /// The signature of the function.
100    pub signature: String,
101}
102
103impl From<cas_compute::numerical::builtin::error::TooManyArguments> for TooManyArguments {
104    fn from(err: cas_compute::numerical::builtin::error::TooManyArguments) -> Self {
105        Self {
106            name: err.name.to_string(),
107            expected: err.expected,
108            given: err.given,
109            signature: err.signature.to_string(),
110        }
111    }
112}
113
114/// An argument to a function call is missing.
115#[derive(Debug, Clone, ErrorKind, PartialEq)]
116#[error(
117    message = if self.indices.start + 1 == self.indices.end { // if range has one index
118        format!("missing required argument #{} for the `{}` function", self.indices.start + 1, self.name)
119    } else {
120        format!(
121            "missing required arguments {} for the `{}` function",
122            self.indices
123                .clone()
124                .map(|i| format!("#{}", i + 1))
125                .collect::<Vec<_>>()
126                .join(", "),
127            self.name
128        )
129    },
130    labels = ["this function call", ""],
131    help = format!(
132        "the `{}` function takes {} argument(s); there are {} argument(s) provided here",
133        (&self.name).fg(EXPR),
134        self.expected,
135        self.given
136    ),
137    note = format!("function signature: `{}`", self.signature),
138)]
139pub struct MissingArgument {
140    /// The name of the function that was called.
141    pub name: String,
142
143    /// The indices of the missing arguments.
144    pub indices: Range<usize>,
145
146    /// The number of arguments that were expected.
147    pub expected: usize,
148
149    /// The number of arguments that were given.
150    pub given: usize,
151
152    /// The signature of the function.
153    pub signature: String,
154}
155
156impl From<cas_compute::numerical::builtin::error::MissingArgument> for MissingArgument {
157    fn from(err: cas_compute::numerical::builtin::error::MissingArgument) -> Self {
158        Self {
159            name: err.name.to_string(),
160            indices: err.indices,
161            expected: err.expected,
162            given: err.given,
163            signature: err.signature.to_string(),
164        }
165    }
166}
167
168/// An argument to a function call has the wrong type.
169#[derive(Debug, Clone, ErrorKind, PartialEq)]
170#[error(
171    message = format!(
172        "incorrect type for argument #{} for the `{}` function",
173        self.index + 1,
174        self.name
175    ),
176    labels = [
177        "this function call".to_string(),
178        "".to_string(),
179        format!("this argument has type `{}`", self.given),
180    ],
181    help = format!("must be of type `{}`", self.expected),
182    note = format!("function signature: `{}`", self.signature),
183)]
184pub struct TypeMismatch {
185    /// The name of the function that was called.
186    pub name: &'static str,
187
188    /// The index of the argument that was mismatched.
189    pub index: usize,
190
191    /// The type of the argument that was expected.
192    pub expected: &'static str,
193
194    /// The type of the argument that was given.
195    pub given: &'static str,
196
197    /// The signature of the function.
198    pub signature: &'static str,
199}
200
201impl From<cas_compute::numerical::builtin::error::TypeMismatch> for TypeMismatch {
202    fn from(err: cas_compute::numerical::builtin::error::TypeMismatch) -> Self {
203        Self {
204            name: err.name,
205            index: err.index,
206            expected: err.expected,
207            given: err.given,
208            signature: err.signature,
209        }
210    }
211}
212
213/// The stack overflowed while evaluating a function call.
214#[derive(Debug, Clone, ErrorKind, PartialEq)]
215#[error(
216    message = "maximum stack depth exceeded",
217    labels = ["stack overflowed when executing this function call", ""],
218    help = "stack overflows occur when I have to keep track of too many function calls at once",
219    // help = "there might be an error in the function you're calling that's causing it to infinitely recurse",
220    note = "the maximum stack depth is equal to: `2^16`",
221)]
222pub struct StackOverflow;
223
224
225/// Encountered a non-numeric type while using prime notation to derivate a function call.
226#[derive(Debug, Clone, ErrorKind, PartialEq)]
227#[error(
228    message = "encountered a non-numeric type while differentiating this function",
229    labels = [
230        format!("during evaluation, this call evaluated to a `{}`", self.expr_type),
231        "".to_string(),
232    ],
233    help = "derivatives are evaluated numerically using the limit definition of a derivative; this can cause an error when evaluating near undefined points of the function"
234)]
235pub struct NonNumericDerivative {
236    /// The type of the expression that was differentiated.
237    pub expr_type: &'static str,
238}
239
240/// Cannot index into a non-list type.
241#[derive(Debug, Clone, ErrorKind, PartialEq)]
242#[error(
243    message = "cannot index into this type",
244    labels = [format!("this expression evaluated to `{}`", self.expr_type)],
245    help = "only lists can be indexed into",
246)]
247pub struct InvalidIndexTarget {
248    /// The type of the expression that was used as the target.
249    pub expr_type: &'static str,
250}
251
252/// Index must be an integer.
253#[derive(Debug, Clone, ErrorKind, PartialEq)]
254#[error(
255    message = "list index must be an integer",
256    labels = [
257        "for this list".to_string(),
258        format!("this expression evaluated to `{}`", self.expr_type),
259    ],
260)]
261pub struct InvalidIndexType {
262    /// The type of the expression that was used as an index.
263    pub expr_type: &'static str,
264}
265
266/// The index is too large to fit into a `usize`.
267#[derive(Debug, Clone, ErrorKind, PartialEq)]
268#[error(
269    message = "list index is out of valid range",
270    labels = ["for this list", "out of range"],
271    help = "the index must be positive and less than or equal to: `2^64 - 1`"
272)]
273pub struct IndexOutOfRange;
274
275/// The index is out of bounds for the given list.
276#[derive(Debug, Clone, ErrorKind, PartialEq)]
277#[error(
278    message = "list index is out of bounds",
279    labels = ["for this list".to_string(), format!("out of bounds (tried to index: {})", self.index)],
280    help = match self.len {
281        0 => "list is empty, so all indexing operations will fail".to_string(),
282        1 => "list has length `1`, so the index must be `0`".to_string(),
283        n => format!("list has length `{}`, so the index must be between `0-{}` (inclusive)", n, n - 1),
284    }
285)]
286pub struct IndexOutOfBounds {
287    /// The length of the list that was indexed into.
288    pub len: usize,
289
290    /// The index that was attempted to be accessed.
291    pub index: usize,
292}
293
294/// Length of a list must be an integer.
295#[derive(Debug, Clone, ErrorKind, PartialEq)]
296#[error(
297    message = "length of list must be an integer",
298    labels = [format!("this expression evaluated to `{}`", self.expr_type)],
299)]
300pub struct InvalidLengthType {
301    /// The type of the expression that was used as an index.
302    pub expr_type: &'static str,
303}
304
305/// The list length is too large to fit into a `usize`.
306#[derive(Debug, Clone, ErrorKind, PartialEq)]
307#[error(
308    message = "length of list is out of valid range",
309    labels = ["out of range"],
310    help = "the length must be positive and less than or equal to: `2^64 - 1`"
311)]
312pub struct LengthOutOfRange;
313
314/// Conditional expression in an `if` or `while` must evaluate to a boolean.
315#[derive(Debug, Clone, ErrorKind, PartialEq)]
316#[error(
317    message = "conditional expression must evaluate to a boolean...",
318    labels = [
319        "...used to determine whether to run this code".to_string(),
320        format!("this expression evaluated to `{}`", self.expr_type),
321    ],
322    help = "consider using the `bool()` function to convert the expression to a boolean based on \"truthiness\"",
323)]
324pub struct ConditionalNotBoolean {
325    /// The type of the expression that was used as the condition.
326    pub expr_type: &'static str,
327}
328
329/// An internal error that can't be resolved by the user.
330#[derive(Debug, Clone, ErrorKind, PartialEq)]
331#[error(
332    message = format!("an internal error occurred at instruction: `{:#?}`", self.instruction),
333    labels = spans.iter()
334        .enumerate()
335        .map(|(idx, span)| format!("{}: `{}..{}`", idx, span.start, span.end)),
336    help = "please copy your code and this error message and report them to the developer",
337    note = &self.data,
338)]
339pub struct InternalError {
340    /// The [`InstructionKind`] the virtual machine was executing when the error occurred.
341    ///
342    /// [`InstructionKind`]: cas_compiler::InstructionKind
343    pub instruction: String,
344
345    /// Arbitrary data that may help the developer diagnose the issue.
346    pub data: String,
347}