1use crate::lexer::{SpannedToken, Token};
2use crate::num::ParseIntError;
3use crate::string::UnescapeError;
4use logos::Span;
5use miette::{Diagnostic, SourceOffset, SourceSpan};
6use std::error::Error;
7use std::fmt::{self, Debug, Display, Formatter};
8use std::num::ParseFloatError;
9use std::str::ParseBoolError;
10use thiserror::Error;
11
12#[derive(Error, Debug, Clone, Diagnostic)]
14pub enum ParseError {
15 #[error(transparent)]
16 #[diagnostic(transparent)]
17 UnexpectedToken(#[from] UnexpectedTokenError),
19 #[error(transparent)]
20 #[diagnostic(transparent)]
21 InvalidPrimitive(#[from] PrimitiveError),
23 #[error("Array key not valid for this position")]
24 #[diagnostic(transparent)]
25 UnexpectedArrayKey(ArrayKeyError),
27 #[error(transparent)]
28 #[diagnostic(transparent)]
29 TrailingCharacters(#[from] TrailingError),
31 #[error("{0}")]
32 #[diagnostic(code(php_literal_parser::serde))]
33 Serde(String),
35}
36
37impl serde::de::Error for ParseError {
38 fn custom<T>(msg: T) -> Self
39 where
40 T: Display,
41 {
42 ParseError::Serde(msg.to_string())
43 }
44}
45
46#[derive(Debug, Clone, Diagnostic)]
48#[diagnostic(code(php_literal_parser::unexpected_token))]
49pub struct UnexpectedTokenError {
50 #[source_code]
51 src: String,
52 #[label("Expected {}", self.expected)]
53 err_span: SourceSpan,
54 pub expected: TokenList,
55 pub found: Option<Token>,
56}
57
58impl UnexpectedTokenError {
59 pub fn new(
60 expected: &[Token],
61 found: Option<Token>,
62 src: String,
63 err_span: SourceSpan,
64 ) -> Self {
65 UnexpectedTokenError {
66 src,
67 err_span,
68 expected: expected.into(),
69 found,
70 }
71 }
72}
73
74#[derive(Clone)]
76pub struct TokenList(Vec<Token>);
77
78impl Debug for TokenList {
79 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
80 self.0.fmt(f)
81 }
82}
83
84impl From<&[Token]> for TokenList {
85 fn from(list: &[Token]) -> Self {
86 TokenList(list.into())
87 }
88}
89
90impl Display for TokenList {
91 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
92 match self.0.len() {
93 0 => {}
94 1 => write!(f, "{}", self.0[0])?,
95 _ => {
96 let mut tokens = self.0[0..self.0.len() - 1].iter();
97 write!(f, "{}", tokens.next().unwrap())?;
98 for token in tokens {
99 write!(f, ", {}", token)?;
100 }
101 if self.0.len() > 1 {
102 write!(f, " or {}", self.0.last().unwrap())?;
103 }
104 }
105 }
106 Ok(())
107 }
108}
109
110impl Display for UnexpectedTokenError {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match &self.found {
113 Some(Token::Error) => {
114 write!(f, "No valid token found, expected one of {}", self.expected)
115 }
116 Some(token) => write!(
117 f,
118 "Unexpected token, found {} expected one of {}",
119 token, self.expected
120 ),
121 None => write!(
122 f,
123 "Unexpected end of input expected one of {}",
124 self.expected
125 ),
126 }
127 }
128}
129
130impl Error for UnexpectedTokenError {}
131
132#[derive(Debug, Clone, Error, Diagnostic)]
134#[diagnostic(code(php_literal_parser::invalid_primitive))]
135#[error("{kind}")]
136pub struct PrimitiveError {
137 #[source_code]
138 src: String,
139 #[label("{}", self.kind.desc())]
140 err_span: SourceSpan,
141 pub kind: PrimitiveErrorKind,
142}
143
144#[derive(Error, Debug, Clone)]
145#[allow(clippy::enum_variant_names)]
146pub enum PrimitiveErrorKind {
147 #[error("Invalid boolean literal: {0}")]
148 InvalidBoolLiteral(#[from] ParseBoolError),
149 #[error("Invalid integer literal: {0}")]
150 InvalidIntLiteral(#[from] ParseIntError),
151 #[error("Invalid float literal: {0}")]
152 InvalidFloatLiteral(#[from] ParseFloatError),
153 #[error("Invalid string literal")]
154 InvalidStringLiteral,
155}
156
157impl PrimitiveErrorKind {
158 pub fn desc(&self) -> &str {
159 match self {
160 PrimitiveErrorKind::InvalidBoolLiteral(_) => "Not a boolean",
161 PrimitiveErrorKind::InvalidIntLiteral(err) => err.desc(),
162 PrimitiveErrorKind::InvalidFloatLiteral(_) => "Not a valid float",
163 PrimitiveErrorKind::InvalidStringLiteral => "Not a string literal",
164 }
165 }
166}
167
168impl From<UnescapeError> for PrimitiveErrorKind {
169 fn from(_: UnescapeError) -> Self {
170 PrimitiveErrorKind::InvalidStringLiteral
171 }
172}
173
174#[derive(Debug, Clone, Error, Diagnostic)]
175#[diagnostic(code(php_literal_parser::invalid_array_key))]
176#[error("Invalid array key")]
177pub struct ArrayKeyError {
178 #[source_code]
179 src: String,
180 #[label("{}", self.kind)]
181 err_span: SourceSpan,
182 kind: ArrayKeyErrorKind,
183}
184
185#[derive(Debug, Clone)]
186pub enum ArrayKeyErrorKind {
187 IntegerExpected,
188 NonConsecutive,
189}
190
191impl Display for ArrayKeyErrorKind {
192 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
193 write!(
194 f,
195 "{}",
196 match self {
197 ArrayKeyErrorKind::IntegerExpected => "Expected integer key",
198 ArrayKeyErrorKind::NonConsecutive => "Expected consecutive integer key",
199 }
200 )
201 }
202}
203
204impl ArrayKeyError {
205 pub fn new(kind: ArrayKeyErrorKind, source: &str, err_span: Span) -> Self {
206 ArrayKeyError {
207 src: source.into(),
208 err_span: map_span(&err_span),
209 kind,
210 }
211 }
212}
213
214#[derive(Debug, Clone, Error, Diagnostic)]
215#[diagnostic(code(php_literal_parser::trailing))]
216#[error("Trailing characters after parsing")]
217pub struct TrailingError {
218 #[source_code]
219 src: String,
220 #[label("end of parsed value")]
221 err_span: SourceSpan,
222}
223
224impl TrailingError {
225 pub fn new(source: &str, err_span: Span) -> Self {
226 TrailingError {
227 src: source.into(),
228 err_span: map_span(&err_span),
229 }
230 }
231}
232
233pub trait ExpectToken<'source> {
234 fn expect_token(
235 self,
236 expected: &[Token],
237 source: &str,
238 ) -> Result<SpannedToken<'source>, ParseError>;
239}
240
241impl<'source> ExpectToken<'source> for Option<SpannedToken<'source>> {
242 fn expect_token(
243 self,
244 expected: &[Token],
245 source: &str,
246 ) -> Result<SpannedToken<'source>, ParseError> {
247 self.ok_or_else(|| {
248 UnexpectedTokenError::new(
249 expected,
250 None,
251 source.into(),
252 map_span(&(source.len()..source.len())),
253 )
254 .into()
255 })
256 .and_then(|token| token.expect_token(expected, source))
257 }
258}
259
260impl<'a, 'source> ExpectToken<'source> for Option<&'a SpannedToken<'source>> {
261 fn expect_token(
262 self,
263 expected: &[Token],
264 source: &str,
265 ) -> Result<SpannedToken<'source>, ParseError> {
266 self.ok_or_else(|| {
267 UnexpectedTokenError::new(
268 expected,
269 None,
270 source.into(),
271 map_span(&(source.len()..source.len())),
272 )
273 .into()
274 })
275 .and_then(|token| token.clone().expect_token(expected, source))
276 }
277}
278
279impl<'source> ExpectToken<'source> for SpannedToken<'source> {
280 fn expect_token(
281 self,
282 expected: &[Token],
283 source: &str,
284 ) -> Result<SpannedToken<'source>, ParseError> {
285 if expected.iter().any(|expect| self.token.eq(expect)) {
286 Ok(self)
287 } else {
288 Err(UnexpectedTokenError::new(
289 expected,
290 Some(self.token),
291 source.into(),
292 map_span(&self.span),
293 )
294 .into())
295 }
296 }
297}
298
299fn map_span(span: &Span) -> SourceSpan {
300 SourceSpan::new(SourceOffset::from(span.start), span.end - span.start)
301}
302
303pub trait ResultExt<T> {
304 fn with_span(self, span: Span, source: &str) -> Result<T, ParseError>;
305}
306
307impl<T, E: Into<PrimitiveErrorKind>> ResultExt<T> for Result<T, E> {
308 fn with_span(self, span: Span, source: &str) -> Result<T, ParseError> {
309 self.map_err(|error| {
310 PrimitiveError {
311 src: source.into(),
312 err_span: map_span(&span),
313 kind: error.into(),
314 }
315 .into()
316 })
317 }
318}