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 = "generate-for declaration doesn't need type specifier (e.g. 'for i in 0..10 {')".to_string();
87 } else if comma_instead_of_assignment_operator(token, &expected_tokens) {
88 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();
89 } else if l_brace_instead_of_colon(token, &expected_tokens) {
90 help =
91 "The first arm of generate-if declaration needs label (e.g. 'if x :label {')"
92 .to_string();
93 } else if keyword_as_identifier(token, &expected_tokens) {
94 help = format!(
95 "'{}' is a reserved keyword and cannot be used as an identifier",
96 token
97 );
98 }
99 }
100
101 Self {
102 cause: value.cause,
103 input: value.input.map(|e| FileSource(*e).into()).unwrap(),
104 error_location: Location(*value.error_location).into(),
105 unexpected_tokens,
106 expected_tokens,
107 help,
108 }
109 }
110}
111
112#[derive(Error, Diagnostic, Debug)]
113#[error("Unexpected token: {name} ({token_type})")]
114#[diagnostic(help("Unexpected token"), code(parol_runtime::unexpected_token))]
115pub struct UnexpectedToken {
116 name: String,
117 token_type: TokenType,
118 #[label("Unexpected token")]
119 pub(crate) token: SourceSpan,
120}
121
122include!("generated/token_type_generated.rs");
123
124impl From<ParolError> for ParserError {
125 fn from(x: ParolError) -> ParserError {
126 match x {
127 ParolError::ParserError(x) => match x {
128 parol_runtime::ParserError::SyntaxErrors { mut entries } if !entries.is_empty() => {
129 ParserError::SyntaxError(Box::new(entries.remove(0).into()))
130 }
131 _ => ParserError::ParserError(x),
132 },
133 ParolError::LexerError(x) => ParserError::LexerError(x),
134 ParolError::UserError(x) => ParserError::UserError(x),
135 }
136 }
137}
138
139struct FileSource(parol_runtime::FileSource);
140
141impl miette::SourceCode for FileSource {
142 fn read_span<'a>(
143 &'a self,
144 span: &SourceSpan,
145 context_lines_before: usize,
146 context_lines_after: usize,
147 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
148 <str as miette::SourceCode>::read_span(
149 &self.0.input,
150 span,
151 context_lines_before,
152 context_lines_after,
153 )
154 }
155}
156
157impl From<FileSource> for NamedSource<FileSource> {
158 fn from(file_source: FileSource) -> Self {
159 let file_name = file_source.0.file_name.clone();
160 let file_name = file_name.to_str().unwrap_or("<Bad file name>");
161 Self::new(file_name, file_source)
162 }
163}
164
165struct Location(parol_runtime::Location);
166
167impl From<Location> for SourceSpan {
168 fn from(location: Location) -> Self {
169 SourceSpan::new((location.0.start as usize).into(), location.0.len())
170 }
171}
172
173struct UnexpectedTokens(Vec<parol_runtime::UnexpectedToken>);
174
175impl From<UnexpectedTokens> for Vec<UnexpectedToken> {
176 fn from(value: UnexpectedTokens) -> Self {
177 value
178 .0
179 .into_iter()
180 .map(|v| UnexpectedToken {
181 name: v.name,
182 token_type: v.token_type.as_str().into(),
183 token: Location(v.token).into(),
184 })
185 .collect::<Vec<UnexpectedToken>>()
186 }
187}
188
189#[derive(Debug)]
190pub struct ExpectedTokens(Vec<TokenType>);
191
192impl ExpectedTokens {
193 pub fn any(&self, x: TokenType) -> bool {
194 self.0.contains(&x)
195 }
196}
197
198impl From<&TokenVec> for ExpectedTokens {
199 fn from(value: &TokenVec) -> Self {
200 ExpectedTokens(value.iter().map(|x| x.as_str().into()).collect())
201 }
202}