php_literal_parser/
error.rs

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/// Any error that occurred while trying to parse the php literal
13#[derive(Error, Debug, Clone, Diagnostic)]
14pub enum ParseError {
15    #[error(transparent)]
16    #[diagnostic(transparent)]
17    /// A token that wasn't expected was found while parsing
18    UnexpectedToken(#[from] UnexpectedTokenError),
19    #[error(transparent)]
20    #[diagnostic(transparent)]
21    /// A malformed integer, float, boolean or string literal was found
22    InvalidPrimitive(#[from] PrimitiveError),
23    #[error("Array key not valid for this position")]
24    #[diagnostic(transparent)]
25    /// An array key was found that is invalid for this position
26    UnexpectedArrayKey(ArrayKeyError),
27    #[error(transparent)]
28    #[diagnostic(transparent)]
29    /// Trailing characters after parsing
30    TrailingCharacters(#[from] TrailingError),
31    #[error("{0}")]
32    #[diagnostic(code(php_literal_parser::serde))]
33    /// Error while populating serde type
34    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/// A token that wasn't expected was found while parsing
47#[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/// List of expected tokens
75#[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/// A malformed integer, float, boolean or string literal was found
133#[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}