veryl_parser/
parser_error.rs1use miette::{self, Diagnostic, NamedSource, SourceSpan};
2use parol_runtime::{ParolError, TokenVec};
3use thiserror::Error;
4
5#[derive(Error, Diagnostic, Debug)]
6pub enum ParserError {
7 #[error(transparent)]
8 #[diagnostic(transparent)]
9 SyntaxError(Box<SyntaxError>),
10
11 #[error(transparent)]
12 ParserError(#[from] parol_runtime::ParserError),
13
14 #[error(transparent)]
15 LexerError(#[from] parol_runtime::LexerError),
16
17 #[error(transparent)]
18 UserError(#[from] anyhow::Error),
19}
20
21#[derive(Error, Diagnostic, Debug)]
22#[diagnostic(code(ParserError::SyntaxError))]
23pub struct SyntaxError {
24 pub cause: String,
25 #[source_code]
26 input: NamedSource<FileSource>,
27 #[label("Error location")]
28 pub error_location: SourceSpan,
29 pub unexpected_tokens: Vec<UnexpectedToken>,
30 pub expected_tokens: ExpectedTokens,
31 #[help]
32 pub help: String,
33}
34
35impl std::fmt::Display for SyntaxError {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 if !self.unexpected_tokens.is_empty() {
38 let token = self.unexpected_tokens[0].token_type;
39 f.write_str(&format!("Unexpected token: '{token}'"))
40 } else {
41 f.write_str("Syntax Error")
42 }
43 }
44}
45
46fn l_angle(unexpected_token: TokenType, _expected_tokens: &ExpectedTokens) -> bool {
47 unexpected_token == TokenType::LAngle
48}
49
50fn r_angle(unexpected_token: TokenType, _expected_tokens: &ExpectedTokens) -> bool {
51 unexpected_token == TokenType::RAngle
52}
53
54fn colon_instead_of_in(unexpected_token: TokenType, expected_tokens: &ExpectedTokens) -> bool {
55 unexpected_token == TokenType::Colon && expected_tokens.any(TokenType::In)
56}
57
58fn comma_instead_of_assignment_operator(
59 unexpected_token: TokenType,
60 expected_tokens: &ExpectedTokens,
61) -> bool {
62 unexpected_token == TokenType::Comma && expected_tokens.any(TokenType::AssignmentOperator)
63}
64
65fn l_brace_instead_of_colon(unexpected_token: TokenType, expected_tokens: &ExpectedTokens) -> bool {
66 unexpected_token == TokenType::LBrace && expected_tokens.any(TokenType::Colon)
67}
68
69fn keyword_as_identifier(unexpected_token: TokenType, expected_tokens: &ExpectedTokens) -> bool {
70 unexpected_token.is_keyword() && expected_tokens.any(TokenType::Identifier)
71}
72
73impl From<parol_runtime::SyntaxError> for SyntaxError {
74 fn from(value: parol_runtime::SyntaxError) -> Self {
75 let unexpected_tokens: Vec<_> = UnexpectedTokens(value.unexpected_tokens).into();
76 let expected_tokens: ExpectedTokens = (&value.expected_tokens).into();
77
78 let mut help = String::new();
79 if !unexpected_tokens.is_empty() {
80 let token = unexpected_tokens[0].token_type;
81 if l_angle(token, &expected_tokens) {
82 help = "If you mean \"less than operator\", please use '<:'".to_string();
83 } else if r_angle(token, &expected_tokens) {
84 help = "If you mean \"greater than operator\", please use '>:'".to_string();
85 } else if colon_instead_of_in(token, &expected_tokens) {
86 help = "for declaration doesn't need type specifier (e.g. 'for i in 0..10 {')"
87 .to_string();
88 } else if comma_instead_of_assignment_operator(token, &expected_tokens) {
89 help = "single case statement with bit concatenation at the left-hand side is not allowed,\nplease surround it by '{}' (e.g. 'x: { {a, b} = 1; }')".to_string();
90 } else if l_brace_instead_of_colon(token, &expected_tokens) {
91 help =
92 "The first arm of generate-if declaration needs label (e.g. 'if x :label {')"
93 .to_string();
94 } else if keyword_as_identifier(token, &expected_tokens) {
95 help = format!(
96 "'{}' is a reserved keyword and cannot be used as an identifier",
97 token
98 );
99 }
100 }
101
102 Self {
103 cause: value.cause,
104 input: value.input.map(|e| FileSource(*e).into()).unwrap(),
105 error_location: Location(*value.error_location).into(),
106 unexpected_tokens,
107 expected_tokens,
108 help,
109 }
110 }
111}
112
113#[derive(Error, Diagnostic, Debug)]
114#[error("Unexpected token: {name} ({token_type})")]
115#[diagnostic(help("Unexpected token"), code(parol_runtime::unexpected_token))]
116pub struct UnexpectedToken {
117 name: String,
118 token_type: TokenType,
119 #[label("Unexpected token")]
120 pub(crate) token: SourceSpan,
121}
122
123include!("generated/token_type_generated.rs");
124
125impl From<ParolError> for ParserError {
126 fn from(x: ParolError) -> ParserError {
127 match x {
128 ParolError::ParserError(x) => match x {
129 parol_runtime::ParserError::SyntaxErrors { mut entries } if !entries.is_empty() => {
130 ParserError::SyntaxError(Box::new(entries.remove(0).into()))
131 }
132 _ => ParserError::ParserError(x),
133 },
134 ParolError::LexerError(x) => ParserError::LexerError(x),
135 ParolError::UserError(x) => ParserError::UserError(x),
136 }
137 }
138}
139
140struct FileSource(parol_runtime::FileSource);
141
142impl miette::SourceCode for FileSource {
143 fn read_span<'a>(
144 &'a self,
145 span: &SourceSpan,
146 context_lines_before: usize,
147 context_lines_after: usize,
148 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
149 <str as miette::SourceCode>::read_span(
150 &self.0.input,
151 span,
152 context_lines_before,
153 context_lines_after,
154 )
155 }
156}
157
158impl From<FileSource> for NamedSource<FileSource> {
159 fn from(file_source: FileSource) -> Self {
160 let file_name = file_source.0.file_name.clone();
161 let file_name = file_name.to_str().unwrap_or("<Bad file name>");
162 Self::new(file_name, file_source)
163 }
164}
165
166struct Location(parol_runtime::Location);
167
168impl From<Location> for SourceSpan {
169 fn from(location: Location) -> Self {
170 SourceSpan::new((location.0.start as usize).into(), location.0.len())
171 }
172}
173
174struct UnexpectedTokens(Vec<parol_runtime::UnexpectedToken>);
175
176impl From<UnexpectedTokens> for Vec<UnexpectedToken> {
177 fn from(value: UnexpectedTokens) -> Self {
178 value
179 .0
180 .into_iter()
181 .map(|v| UnexpectedToken {
182 name: v.name,
183 token_type: v.token_type.as_str().into(),
184 token: Location(v.token).into(),
185 })
186 .collect::<Vec<UnexpectedToken>>()
187 }
188}
189
190#[derive(Debug)]
191pub struct ExpectedTokens(Vec<TokenType>);
192
193impl ExpectedTokens {
194 pub fn any(&self, x: TokenType) -> bool {
195 self.0.contains(&x)
196 }
197}
198
199impl From<&TokenVec> for ExpectedTokens {
200 fn from(value: &TokenVec) -> Self {
201 ExpectedTokens(value.iter().map(|x| x.as_str().into()).collect())
202 }
203}