solscript_parser/
error.rs

1//! Parser error types
2
3use miette::{Diagnostic, SourceSpan};
4use thiserror::Error;
5
6/// A parsing error
7#[derive(Error, Debug, Diagnostic)]
8pub enum ParseError {
9    #[error("Syntax error: {message}")]
10    #[diagnostic(code(solscript::parse::syntax))]
11    Syntax {
12        message: String,
13        #[label("here")]
14        span: SourceSpan,
15        #[source_code]
16        src: String,
17    },
18
19    #[error("Unexpected token: expected {expected}, found {found}")]
20    #[diagnostic(code(solscript::parse::unexpected_token))]
21    UnexpectedToken {
22        expected: String,
23        found: String,
24        #[label("unexpected token")]
25        span: SourceSpan,
26        #[source_code]
27        src: String,
28    },
29
30    #[error("Unexpected end of input")]
31    #[diagnostic(code(solscript::parse::unexpected_eof))]
32    UnexpectedEof {
33        #[label("end of input")]
34        span: SourceSpan,
35        #[source_code]
36        src: String,
37    },
38
39    #[error("Invalid integer literal: {message}")]
40    #[diagnostic(code(solscript::parse::invalid_int))]
41    InvalidInt {
42        message: String,
43        #[label("invalid integer")]
44        span: SourceSpan,
45        #[source_code]
46        src: String,
47    },
48
49    #[error("Invalid float literal: {message}")]
50    #[diagnostic(code(solscript::parse::invalid_float))]
51    InvalidFloat {
52        message: String,
53        #[label("invalid float")]
54        span: SourceSpan,
55        #[source_code]
56        src: String,
57    },
58
59    #[error("Invalid escape sequence")]
60    #[diagnostic(code(solscript::parse::invalid_escape))]
61    InvalidEscape {
62        #[label("invalid escape")]
63        span: SourceSpan,
64        #[source_code]
65        src: String,
66    },
67}
68
69impl ParseError {
70    pub fn syntax(message: impl Into<String>, span: (usize, usize), src: &str) -> Self {
71        Self::Syntax {
72            message: message.into(),
73            span: SourceSpan::new(span.0.into(), span.1 - span.0),
74            src: src.to_string(),
75        }
76    }
77
78    pub fn unexpected_token(
79        expected: impl Into<String>,
80        found: impl Into<String>,
81        span: (usize, usize),
82        src: &str,
83    ) -> Self {
84        Self::UnexpectedToken {
85            expected: expected.into(),
86            found: found.into(),
87            span: SourceSpan::new(span.0.into(), span.1 - span.0),
88            src: src.to_string(),
89        }
90    }
91
92    pub fn unexpected_eof(pos: usize, src: &str) -> Self {
93        Self::UnexpectedEof {
94            span: SourceSpan::new(pos.into(), 0usize),
95            src: src.to_string(),
96        }
97    }
98
99    pub fn invalid_int(message: impl Into<String>, span: (usize, usize), src: &str) -> Self {
100        Self::InvalidInt {
101            message: message.into(),
102            span: SourceSpan::new(span.0.into(), span.1 - span.0),
103            src: src.to_string(),
104        }
105    }
106}
107
108/// Convert pest error to our ParseError
109impl From<pest::error::Error<crate::Rule>> for ParseError {
110    fn from(err: pest::error::Error<crate::Rule>) -> Self {
111        let message = err.to_string();
112        let (start, end) = match err.location {
113            pest::error::InputLocation::Pos(p) => (p, p + 1),
114            pest::error::InputLocation::Span((s, e)) => (s, e),
115        };
116
117        ParseError::Syntax {
118            message,
119            span: SourceSpan::new(start.into(), end - start),
120            src: String::new(), // Will be filled in by caller
121        }
122    }
123}