datex_core/ast/error/
error.rs

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        //
327
328        if other.context.is_some() {
329            // TODO #370, if Pattern::Reset, clear context, otherwise keep
330            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        //self.context = Some((self.span.clone(), label.to_string()));
393    }
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        // let context = span.context();
409        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}