1use core::panic;
2use std::{collections::HashSet, io::Write, ops::Range};
3
4use crate::{
5 ast::{
6 TokenInput,
7 error::{pattern::Pattern, src::SrcId},
8 },
9 values::core_values::{
10 endpoint::InvalidEndpointError, error::NumberParseError,
11 },
12};
13
14#[derive(Debug, Clone, PartialEq)]
15
16pub enum SpanOrToken {
17 Span(Range<usize>),
18 Token(usize),
19}
20impl From<Range<usize>> for SpanOrToken {
21 fn from(value: Range<usize>) -> Self {
22 SpanOrToken::Span(value)
23 }
24}
25impl From<usize> for SpanOrToken {
26 fn from(value: usize) -> Self {
27 SpanOrToken::Token(value)
28 }
29}
30#[derive(Debug, PartialEq, Clone)]
31pub enum ErrorKind {
32 Custom(HashSet<String>),
33 InvalidListSize(String),
34 InvalidEndpoint(InvalidEndpointError),
35 NumberParseError(NumberParseError),
36 UnexpectedEnd,
37 Unexpected {
38 found: Option<Pattern>,
39 expected: Vec<Pattern>,
40 },
41 Unclosed {
42 start: Pattern,
43 before_span: SpanOrToken,
44 before: Option<Pattern>,
45 },
46}
47
48#[derive(Debug, Clone, PartialEq)]
49pub struct ParseError {
50 kind: ErrorKind,
51 span: SpanOrToken,
52 context: Option<(SpanOrToken, String)>,
53 note: Option<&'static str>,
54}
55impl From<NumberParseError> for ParseError {
56 fn from(value: NumberParseError) -> Self {
57 Self::new(ErrorKind::NumberParseError(value))
58 }
59}
60impl From<InvalidEndpointError> for ParseError {
61 fn from(value: InvalidEndpointError) -> Self {
62 Self::new(ErrorKind::InvalidEndpoint(value))
63 }
64}
65impl From<&str> for ParseError {
66 fn from(value: &str) -> Self {
67 Self::new_custom(value.to_string())
68 }
69}
70impl From<String> for ParseError {
71 fn from(value: String) -> Self {
72 Self::new_custom(value)
73 }
74}
75
76impl ParseError {
77 pub fn new(kind: ErrorKind) -> Self {
78 Self {
79 kind,
80 span: SpanOrToken::Token(0),
81 context: None,
82 note: None,
83 }
84 }
85 pub fn new_custom(message: String) -> Self {
86 Self::new(ErrorKind::Custom(HashSet::from([message])))
87 }
88 pub fn new_unexpected_end(span: SpanOrToken) -> Self {
89 Self::new(ErrorKind::UnexpectedEnd)
90 }
91 pub fn new_unexpected(found: Option<Pattern>) -> Self {
92 Self {
93 kind: ErrorKind::Unexpected {
94 found,
95 expected: Vec::new(),
96 },
97 span: SpanOrToken::Token(0),
98 context: None,
99 note: None,
100 }
101 }
102 pub(crate) fn new_unexpected_with_span<T: Into<SpanOrToken>>(
103 found: Option<Pattern>,
104 span: T,
105 ) -> Self {
106 Self {
107 kind: ErrorKind::Unexpected {
108 found,
109 expected: Vec::new(),
110 },
111 span: span.into(),
112 context: None,
113 note: None,
114 }
115 }
116 pub fn new_unclosed(
117 start: Pattern,
118 before_span: SpanOrToken,
119 before: Option<Pattern>,
120 ) -> Self {
121 Self::new(ErrorKind::Unclosed {
122 start,
123 before_span: before_span.clone(),
124 before,
125 })
126 }
127
128 pub fn with_context(
129 mut self,
130 span: SpanOrToken,
131 context: &'static str,
132 ) -> Self {
133 self.context = Some((span, context.to_string()));
134 self
135 }
136
137 pub fn with_note(mut self, note: &'static str) -> Self {
138 self.note = Some(note);
139 self
140 }
141}
142
143fn expected_items_to_string(expected: &[Pattern]) -> String {
144 let mut normal_items = Vec::new();
145 let mut has_something_else = false;
146
147 for expected in expected {
148 match expected {
149 Pattern::SomethingElse => has_something_else = true,
150 _ => normal_items.push(expected.as_string()),
151 }
152 }
153 if has_something_else {
154 normal_items.push(Pattern::SomethingElse.to_string());
155 }
156 match normal_items.len() {
157 0 => Pattern::SomethingElse.to_string(),
158 1 => normal_items[0].clone(),
159 2 => format!("{} or {}", normal_items[0], normal_items[1]),
160 _ => {
161 let last = normal_items.pop().unwrap();
162 format!("{}, or {}", normal_items.join(", "), last)
163 }
164 }
165}
166
167impl ParseError {
168 pub(crate) fn set_span(&mut self, span: Range<usize>) {
169 self.span = span.into();
170 }
171 pub(crate) fn set_token_pos(&mut self, pos: usize) {
172 self.span = pos.into();
173 }
174 pub fn kind(&self) -> &ErrorKind {
175 &self.kind
176 }
177 pub fn note(&self) -> Option<&'static str> {
178 self.note
179 }
180 pub fn span(&self) -> Option<Range<usize>> {
181 match &self.span {
182 SpanOrToken::Span(span) => Some(span.clone()),
183 SpanOrToken::Token(_) => None,
184 }
185 }
186 pub fn token_pos(&self) -> Option<usize> {
187 match &self.span {
188 SpanOrToken::Span(_) => None,
189 SpanOrToken::Token(pos) => Some(*pos),
190 }
191 }
192
193 pub fn message(&self) -> String {
194 match &self.kind {
195 ErrorKind::InvalidListSize(size) => {
196 format!("Invalid list size: {}", size)
197 }
198 ErrorKind::Custom(msg) => {
199 msg.iter().cloned().collect::<Vec<_>>().join(" | ")
200 }
201 ErrorKind::NumberParseError(err) => err.to_string(),
202 ErrorKind::UnexpectedEnd => "Unexpected end of input".to_string(),
203 ErrorKind::Unexpected { found, expected } => {
204 let mut msg = String::new();
205 if let Some(found) = found {
206 msg.push_str(&format!(
207 "Unexpected {}: {}",
208 found.kind(),
209 found
210 ));
211 } else {
212 msg.push_str("Unexpected end of input");
213 }
214 if !expected.is_empty() {
215 msg.push_str(", expected one of: ");
216 msg.push_str(&expected_items_to_string(expected));
217 }
218 msg
219 }
220 ErrorKind::Unclosed {
221 start,
222 before_span,
223 before,
224 } => {
225 let mut msg = format!("Unclosed delimiter: {}", start);
226 if let Some(before) = before {
227 msg.push_str(&format!(", before {}", before));
228 }
229 msg
230 }
231 ErrorKind::InvalidEndpoint(e) => {
232 format!("Parsing error: {}", e)
233 }
234 }
235 }
236
237 pub fn label(&self) -> String {
238 use ariadne::{Color, Fmt};
239 match &self.kind {
240 ErrorKind::InvalidListSize(size) => {
241 format!("Invalid list size: {}", size.fg(Color::Red))
242 }
243 ErrorKind::NumberParseError(_) => "Number parse error".to_string(),
244 ErrorKind::UnexpectedEnd => "End of input".to_string(),
245 ErrorKind::Unexpected { found, .. } => {
246 format!(
247 "Unexpected {}",
248 found.clone().unwrap().as_string().fg(Color::Red)
249 )
250 }
251 ErrorKind::Unclosed { start, .. } => {
252 format!("Delimiter {} is never closed", start.fg(Color::Red))
253 }
254 ErrorKind::Custom(_) => "Invalid syntax".to_string(),
255 ErrorKind::InvalidEndpoint(_) => "Invalid endpoint".to_string(),
256 }
257 }
258
259 pub fn write<C: ariadne::Cache<SrcId>>(self, cache: C, writer: impl Write) {
260 use ariadne::{Color, Fmt, Label, Report, ReportKind};
261
262 let span = (SrcId::test(), self.span().unwrap().clone());
263 let mut note: String = self
264 .note
265 .unwrap_or("Please check the syntax and try again.")
266 .to_string();
267 if !note.ends_with('.') {
268 note.push('.');
269 }
270 let mut report = Report::build(ReportKind::Error, span.clone())
271 .with_code("Syntax")
272 .with_message(self.message())
273 .with_note(note)
274 .with_label(
275 Label::new(span)
276 .with_message(self.label())
277 .with_color(Color::Red),
278 );
279 if let Some((_, context)) = self.context {
280 report = report.with_help(format!(
281 "In the context of: {}",
282 context.fg(Color::Yellow)
283 ));
284 }
285 report.finish().write(cache, writer).unwrap();
286 }
287}
288
289use crate::ast::lexer::Token;
290use chumsky::{
291 DefaultExpected,
292 error::{Error, LabelError},
293 span::SimpleSpan,
294 util::MaybeRef,
295};
296
297impl<'a> Error<'a, TokenInput<'a>> for ParseError {
298 fn merge(mut self, mut other: Self) -> Self {
299 match (&mut self.kind, &mut other.kind) {
300 (ErrorKind::Custom(msg1), ErrorKind::Custom(msg2)) => {
301 msg1.extend(msg2.drain());
302 }
303 (ErrorKind::UnexpectedEnd, ErrorKind::UnexpectedEnd) => {}
304 (
305 ErrorKind::Unexpected {
306 found: found1,
307 expected: expected1,
308 },
309 ErrorKind::Unexpected {
310 found: found2,
311 expected: expected2,
312 },
313 ) => {
314 if found1.is_none() {
315 *found1 = found2.take();
316 }
317 for exp in expected2.drain(..) {
318 if !expected1.contains(&exp) {
319 expected1.push(exp);
320 }
321 }
322 }
323 _ => {}
324 };
325
326 if other.context.is_some() {
329 if false {
331 let self_ctx_str =
332 self.context.as_ref().map(|(_, s)| s.clone());
333 let other_ctx_str = other.context.unwrap().1;
334 let new_ctx = format!(
335 "{} {}",
336 self_ctx_str.unwrap_or_default(),
337 other_ctx_str
338 );
339 self.context = Some((self.span.clone(), new_ctx));
340 } else {
341 self.context = other.context.take();
342 }
343 }
344
345 self
346 }
347}
348
349impl<'a>
350 chumsky::error::LabelError<'a, TokenInput<'a>, DefaultExpected<'a, Token>>
351 for ParseError
352{
353 fn expected_found<Iter: IntoIterator<Item = DefaultExpected<'a, Token>>>(
354 expected: Iter,
355 found: Option<MaybeRef<'a, Token>>,
356 span: chumsky::span::SimpleSpan<usize>,
357 ) -> Self {
358 let expected: Vec<Pattern> = expected
359 .into_iter()
360 .map(|e| match e {
361 DefaultExpected::Any => Pattern::Any,
362 DefaultExpected::Token(token) => {
363 Pattern::from(token.into_inner().clone())
364 }
365 DefaultExpected::EndOfInput => Pattern::EndOfInput,
366 DefaultExpected::SomethingElse => Pattern::SomethingElse,
367 _ => unreachable!("Unexpected expected variant: {:?}", e),
368 })
369 .collect();
370
371 if found.is_none() {
372 return ParseError::new_unexpected_end(span.start.into());
373 }
374 if span.end - span.start > 1 {
375 panic!("Span too large: {:?}", span);
376 }
377
378 ParseError {
379 kind: ErrorKind::Unexpected {
380 found: found.as_deref().cloned().map(Pattern::from),
381 expected,
382 },
383 span: SpanOrToken::Token(span.start),
384 context: None,
385 note: None,
386 }
387 }
388}
389
390impl<'a> LabelError<'a, TokenInput<'a>, Pattern> for ParseError {
391 fn label_with(&mut self, label: Pattern) {
392 }
394 fn in_context(&mut self, label: Pattern, span: SimpleSpan<usize>) {
395 self.context = Some((span.start.into(), label.to_string()));
396 }
397
398 fn expected_found<Iter: IntoIterator<Item = Pattern>>(
399 expected: Iter,
400 found: Option<MaybeRef<'a, Token>>,
401 span: SimpleSpan<usize>,
402 ) -> Self {
403 let expected: Vec<Pattern> = expected.into_iter().collect();
404 if found.is_none() {
405 return ParseError::new_unexpected_end(span.start.into());
406 }
407
408 ParseError {
410 kind: ErrorKind::Unexpected {
411 found: found.as_deref().cloned().map(Pattern::Token),
412 expected,
413 },
414 span: SpanOrToken::Token(span.start),
415 context: None,
416 note: None,
417 }
418 }
419}