1use ariadne::Fmt;
2use cas_attrs::ErrorKind;
3use cas_error::{ErrorKind, EXPR};
4use crate::tokenizer::TokenKind;
5use std::{collections::HashSet, ops::Range};
6
7#[derive(Debug, Clone, ErrorKind, PartialEq)]
10#[error(
11 message = "an internal non-fatal error occurred while parsing",
12 labels = ["here"],
13 help = "you should never see this error; please report this as a bug"
14)]
15pub struct NonFatal;
16
17#[derive(Debug, Clone, ErrorKind, PartialEq)]
22#[error(
23 message = format!("expected {}", self.expected),
24 labels = [format!("I expected to see {} here", self.expected)],
25)]
26pub struct ExpectedExpr {
27 pub expected: &'static str,
29}
30
31#[derive(Debug, Clone, ErrorKind, PartialEq)]
33#[error(
34 message = "unexpected end of file",
35 labels = [format!("you might need to add another {} here", "expression".fg(EXPR))],
36)]
37pub struct UnexpectedEof;
38
39#[derive(Debug, Clone, ErrorKind, PartialEq)]
41#[error(
42 message = "unexpected end of expression",
43 labels = [format!("I expected to see more {} here", "expression".fg(EXPR))],
44)]
45pub struct UnexpectedEoExpr;
46
47#[derive(Debug, Clone, ErrorKind, PartialEq)]
49#[error(
50 message = "expected end of file",
51 labels = [format!("I could not understand the remaining {} here", "expression".fg(EXPR))],
52)]
53pub struct ExpectedEof;
54
55#[derive(Debug, Clone, ErrorKind, PartialEq)]
57#[error(
58 message = "unexpected token",
59 labels = [format!("expected one of: {}", self.expected.iter().map(|t| format!("{:?}", t)).collect::<Vec<_>>().join(", "))],
60 help = format!("found {:?}", self.found),
61)]
62pub struct UnexpectedToken {
63 pub expected: &'static [TokenKind],
65
66 pub found: TokenKind,
68}
69
70#[derive(Debug, Clone, ErrorKind, PartialEq)]
73#[error(
74 message = "expected symbol name",
75 labels = [format!("found keyword `{}`", self.keyword)],
76 help = "you cannot use keywords as symbol names"
77)]
78pub struct ExpectedSymbolName {
79 pub keyword: String,
81}
82
83#[derive(Debug, Clone, ErrorKind, PartialEq)]
85#[error(
86 message = "invalid base in radix notation",
87 labels = [if self.too_large {
88 "this value is too large"
89 } else {
90 "this value is too small"
91 }],
92 help = format!("the base must be {}", "between 2 and 64, inclusive".fg(EXPR)),
93)]
94pub struct InvalidRadixBase {
95 pub too_large: bool,
97}
98
99#[derive(Debug, Clone, PartialEq)]
101pub struct InvalidRadixDigit {
102 pub radix: u8,
104
105 pub allowed: &'static [char],
107
108 pub digits: HashSet<char>,
110
111 pub last_op_digit: Option<(char, Range<usize>)>,
117}
118
119impl ErrorKind for InvalidRadixDigit {
121 fn build_report<'a>(
122 &self,
123 src_id: &'a str,
124 spans: &[std::ops::Range<usize>],
125 ) -> ariadne::Report<(&'a str, Range<usize>)> {
126 let labels = spans
127 .iter()
128 .cloned()
129 .map(|span| {
130 if let Some((_, last_op_digit)) = self.last_op_digit.as_ref() {
131 if span.end == last_op_digit.end {
134 return span.start..span.end - 1;
135 }
136 }
137
138 span
139 })
140 .filter(|span| span.start < span.end) .map(|span| {
142 ariadne::Label::new((src_id, span))
143 .with_color(cas_error::EXPR)
144 })
145 .chain(
146 self.last_op_digit.as_ref().map(|(ch, span)| {
147 let operation = match ch {
148 '+' => "add",
149 '/' => "divide",
150 _ => unreachable!(),
151 };
152 ariadne::Label::new((src_id, span.clone()))
153 .with_message(format!(
154 "if you're trying to {} two values, add a space between each value and this operator",
155 operation
156 ))
157 .with_color(cas_error::EXPR)
158 }
159 ));
160
161 let mut builder =
162 ariadne::Report::build(ariadne::ReportKind::Error, src_id, spans[0].start)
163 .with_message(format!(
164 "invalid digits in radix notation: `{}`",
165 self.digits
166 .iter()
167 .map(|c| c.to_string())
168 .collect::<Vec<_>>()
169 .join("`, `"),
170 ))
171 .with_labels(labels);
172 builder.set_help(format!(
173 "base {} uses these digits (from lowest to highest value): {}",
174 self.radix,
175 self.allowed.iter().collect::<String>().fg(EXPR)
176 ));
177 builder.finish()
178 }
179}
180
181#[derive(Debug, Clone, ErrorKind, PartialEq)]
183#[error(
184 message = "missing value in radix notation",
185 labels = [format!("I was expecting to see a number in base {}, directly after this quote", self.radix)],
186 help = format!("base {} uses these digits (from lowest to highest value): {}", self.radix, self.allowed.iter().collect::<String>().fg(EXPR)),
187)]
188pub struct EmptyRadixLiteral {
189 pub radix: u8,
191
192 pub allowed: &'static [char],
194}
195
196#[derive(Debug, Clone, ErrorKind, PartialEq)]
198#[error(
199 message = "unclosed parenthesis",
200 labels = ["this parenthesis is not closed"],
201 help = if self.opening {
202 "add a closing parenthesis `)` somewhere after this"
203 } else {
204 "add an opening parenthesis `(` somewhere before this"
205 },
206)]
207pub struct UnclosedParenthesis {
208 pub opening: bool,
211}
212
213#[derive(Debug, Clone, ErrorKind, PartialEq)]
215#[error(
216 message = "invalid left-hand-side of assignment operator",
217 labels = ["(1) this expression should be a symbol or function header...", "(2) ...to work with this assignment operator"],
218 help = if self.is_call {
219 "(1) looks like a function *call*, not a function *header*"
220 } else {
221 "maybe you meant to compare expressions with `==`?"
222 }
223)]
224pub struct InvalidAssignmentLhs {
225 pub is_call: bool,
227}
228
229#[derive(Debug, Clone, ErrorKind, PartialEq)]
231#[error(
232 message = "default arguments must be placed at the end of the function argument list",
233 labels = spans.iter()
234 .enumerate()
235 .map(|(i, _)| format!("default argument #{}", i + 1)),
236 help = "move the default argument(s) to the end",
237)]
238pub struct DefaultArgumentNotLast;
239
240#[derive(Debug, Clone, ErrorKind, PartialEq)]
242#[error(
243 message = "invalid left-hand-side of compound assignment operator",
244 labels = ["(1) this expression must be a symbol...", "(2) ...to work with this compound assignment operator"],
245 help = "use the standard assignment operator (`=`) instead",
246)]
247pub struct InvalidCompoundAssignmentLhs;
248
249#[derive(Debug, Clone, ErrorKind, PartialEq)]
251#[error(
252 message = "cannot use compound assignment operator here",
253 labels = ["this operator"],
254 help = "only the standard assignment operator (`=`) is allowed in function headers",
255)]
256pub struct CompoundAssignmentInHeader;
257
258#[derive(Debug, Clone, ErrorKind, PartialEq)]
260#[error(
261 message = "too many derivatives in prime notation",
262 labels = ["you can only take at most 255 derivatives of a function"],
263 help = format!("I counted {} derivatives here", self.derivatives),
264)]
265pub struct TooManyDerivatives {
266 pub derivatives: usize,
268}
269
270#[derive(Debug, Clone, ErrorKind, PartialEq)]
272#[error(
273 message = "cannot use `return` keyword here",
274 labels = [""],
275 help = "`return` can only be used within a function definition"
276)]
277pub struct ReturnOutsideFunction;
278
279#[derive(Debug, Clone, ErrorKind, PartialEq)]
281#[error(
282 message = format!("missing `{}` in `if` expression", self.keyword),
283 labels = ["this `if` expression".to_string(), format!("I expected to see `{}` here", self.keyword)],
284)]
285pub struct MissingIfKeyword {
286 pub keyword: &'static str,
288}
289
290#[derive(Debug, Clone, ErrorKind, PartialEq)]
292#[error(
293 message = format!("missing `{}` branch in `if` expression", self.keyword),
294 labels = ["this `if` expression".to_string(), format!("I expected to see an `{}` branch here", self.keyword)],
295)]
296pub struct MissingIfBranch {
297 pub keyword: &'static str,
299}
300
301#[derive(Debug, Clone, ErrorKind, PartialEq)]
303#[error(
304 message = "cannot use `then` keyword here",
305 labels = [""],
306 help = "`then` can only be used directly after the condition in an `if` or `while` expression, or after the range in a `for` expression",
307)]
308pub struct ThenOutsideIfWhileFor;
309
310#[derive(Debug, Clone, ErrorKind, PartialEq)]
312#[error(
313 message = "cannot use `of` keyword here",
314 labels = [""],
315 help = "`of` can only be used directly after the range in a `sum` or `product` expression",
316)]
317pub struct OfOutsideSumProduct;
318
319#[derive(Debug, Clone, ErrorKind, PartialEq)]
321#[error(
322 message = "cannot use `break` keyword here",
323 labels = [""],
324 help = "`break` or `continue` can only be used within a `loop`, `while`, or `for` expression",
325)]
326pub struct BreakOutsideLoop;
327
328#[derive(Debug, Clone, ErrorKind, PartialEq)]
330#[error(
331 message = "cannot use `continue` keyword here",
332 labels = [""],
333 help = "`break` or `continue` can only be used within a `loop`, `while`, or `for` expression",
334)]
335pub struct ContinueOutsideLoop;