microcad_syntax/parser/error/
mod.rs1mod rich;
5
6use crate::Span;
7use crate::parser::error::rich::RichPattern;
8use crate::tokens::Token;
9use miette::{Diagnostic, LabeledSpan};
10pub use rich::{Rich, RichReason};
11use std::error::Error;
12use std::fmt::{Display, Formatter};
13use std::iter::once;
14use thiserror::Error;
15
16#[derive(Debug)]
18pub struct ParseError {
19 pub span: Span,
21 error: Rich<'static, Token<'static>, Span, ParseErrorKind>,
22}
23
24impl ParseError {
25 pub(crate) fn new<'tokens>(error: Rich<'tokens, Token<'tokens>, Span, ParseErrorKind>) -> Self {
26 Self {
27 span: error.span().clone(),
28 error: error.map_token(Token::into_owned).into_owned(),
29 }
30 }
31}
32
33#[derive(Debug, Error, Clone, Diagnostic)]
34pub enum ParseErrorKind {
35 #[error("'{0}' is a reserved keyword")]
36 ReservedKeyword(&'static str),
37 #[error("'{0}' is a reserved keyword and can't be used as an identifier")]
38 ReservedKeywordAsIdentifier(&'static str),
39 #[error("'{0}' is a keyword and can't be used as an identifier")]
40 KeywordAsIdentifier(&'static str),
41 #[error("unclosed string")]
42 UnterminatedString,
43 #[error("Unclosed {kind}")]
44 UnclosedBracket {
45 #[label("{kind} opened here")]
46 open: Span,
47 #[label("expected {kind} to be closed by here with a '{close_token}'")]
48 end: Span,
49 kind: &'static str,
50 close_token: Token<'static>,
51 },
52}
53
54impl Display for ParseError {
55 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56 match self.error.reason() {
57 RichReason::Custom(error) => write!(f, "{error}"),
58 RichReason::ExpectedFound { expected, .. } => {
59 write!(f, "Expected ")?;
60 let mut expected = expected.iter().filter(|pat| match pat {
61 RichPattern::Label(label) if expected.len() > 1 => label != "whitespace",
63 _ => true,
64 });
65 if let Some(pattern) = expected.next() {
66 write!(f, "{pattern}")?;
67 }
68 let last = expected.next_back();
69 for pattern in expected {
70 write!(f, ", {pattern}")?;
71 }
72 if let Some(pattern) = last {
73 write!(f, " or {pattern}")?;
74 }
75 Ok(())
76 }
77 }
78 }
79}
80
81impl Error for ParseError {}
82
83impl Diagnostic for ParseError {
84 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
85 match self.error.reason() {
86 RichReason::Custom(error) => error.help(),
87 _ => None,
88 }
89 }
90
91 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
92 let msg = match self.error.reason() {
93 RichReason::Custom(error) => {
94 if let Some(labels) = error.labels() {
95 return Some(labels);
96 }
97 error.to_string()
98 }
99 RichReason::ExpectedFound {
100 found: Some(found), ..
101 } if found.is_error() => found.kind().into(),
102 RichReason::ExpectedFound {
103 found: Some(found), ..
104 } => format!("unexpected {}", found.kind()),
105 RichReason::ExpectedFound { found: None, .. } => "unexpected token".into(),
106 };
107 Some(Box::new(once(LabeledSpan::new(
108 Some(msg),
109 self.span.start,
110 self.span.len(),
111 ))))
112 }
113}