1use nom::{
4 error::{ContextError, ErrorKind as NomErrorKind, FromExternalError, ParseError},
5 Slice,
6};
7
8use core::fmt;
9
10use crate::{
11 BinaryOp, ExprType, InputSpan, LocatedSpan, LvalueType, Op, Spanned, StatementType, StripCode,
12 UnaryOp,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq)]
18#[non_exhaustive]
19pub enum Context {
20 Var,
22 Fun,
24 Expr,
26 Comment,
28}
29
30impl fmt::Display for Context {
31 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32 formatter.write_str(match self {
33 Self::Var => "variable",
34 Self::Fun => "function call",
35 Self::Expr => "arithmetic expression",
36 Self::Comment => "comment",
37 })
38 }
39}
40
41impl Context {
42 pub(crate) fn new(s: &str) -> Self {
43 match s {
44 "var" => Self::Var,
45 "fn" => Self::Fun,
46 "expr" => Self::Expr,
47 "comment" => Self::Comment,
48 _ => unreachable!(),
49 }
50 }
51
52 pub(crate) fn to_str(self) -> &'static str {
53 match self {
54 Self::Var => "var",
55 Self::Fun => "fn",
56 Self::Expr => "expr",
57 Self::Comment => "comment",
58 }
59 }
60}
61
62#[derive(Debug)]
64#[non_exhaustive]
65pub enum ErrorKind {
66 NonAsciiInput,
68 Literal(anyhow::Error),
70 LiteralName,
75 Type(anyhow::Error),
77 UnsupportedOp(Op),
79 UnexpectedChar {
81 context: Option<Context>,
83 },
84 UnexpectedTerm {
86 context: Option<Context>,
88 },
89 Leftovers,
91 Incomplete,
93 UnfinishedComment,
95 ChainedComparison,
97 Other {
99 kind: NomErrorKind,
101 context: Option<Context>,
103 },
104}
105
106impl fmt::Display for ErrorKind {
107 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
108 match self {
109 Self::NonAsciiInput => formatter.write_str("Non-ASCII inputs are not supported"),
110 Self::Literal(e) => write!(formatter, "Invalid literal: {}", e),
111 Self::LiteralName => formatter.write_str("Literal used in place of an identifier"),
112
113 Self::Type(e) => write!(formatter, "Invalid type annotation: {}", e),
114
115 Self::UnsupportedOp(op) => write!(
116 formatter,
117 "Encountered operation switched off in the parser features: {}",
118 op
119 ),
120
121 Self::UnexpectedChar { context: Some(ctx) } => {
122 write!(formatter, "Unexpected character in {}", ctx)
123 }
124 Self::UnexpectedChar { .. } => formatter.write_str("Unexpected character"),
125
126 Self::UnexpectedTerm { context: Some(ctx) } => write!(formatter, "Unfinished {}", ctx),
127 Self::UnexpectedTerm { .. } => formatter.write_str("Unfinished expression"),
128
129 Self::Leftovers => formatter.write_str("Uninterpreted characters after parsing"),
130 Self::Incomplete => formatter.write_str("Incomplete input"),
131 Self::UnfinishedComment => formatter.write_str("Unfinished comment"),
132 Self::ChainedComparison => formatter.write_str("Chained comparisons"),
133 Self::Other { .. } => write!(formatter, "Cannot parse sequence"),
134 }
135 }
136}
137
138impl ErrorKind {
139 pub fn literal<T: Into<anyhow::Error>>(error: T) -> Self {
141 Self::Literal(error.into())
142 }
143
144 fn context_mut(&mut self) -> Option<&mut Option<Context>> {
145 match self {
146 Self::UnexpectedChar { context }
147 | Self::UnexpectedTerm { context }
148 | Self::Other { context, .. } => Some(context),
149 _ => None,
150 }
151 }
152
153 pub fn context(&self) -> Option<Context> {
155 match self {
156 Self::UnexpectedChar { context }
157 | Self::UnexpectedTerm { context }
158 | Self::Other { context, .. } => *context,
159 _ => None,
160 }
161 }
162
163 pub fn is_incomplete(&self) -> bool {
165 matches!(self, Self::Incomplete)
166 }
167
168 #[doc(hidden)]
169 pub fn with_span<'a, T>(self, span: &Spanned<'a, T>) -> Error<'a> {
170 Error {
171 inner: span.copy_with_extra(self),
172 }
173 }
174}
175
176#[cfg(feature = "std")]
177impl std::error::Error for ErrorKind {
178 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
179 match self {
180 Self::Literal(err) | Self::Type(err) => Some(err.as_ref()),
181 _ => None,
182 }
183 }
184}
185
186#[derive(Debug)]
191pub struct SpannedError<Span> {
192 inner: LocatedSpan<Span, ErrorKind>,
193}
194
195pub type Error<'a> = SpannedError<&'a str>;
197
198impl<'a> Error<'a> {
199 pub(crate) fn new(span: InputSpan<'a>, kind: ErrorKind) -> Self {
200 Self {
201 inner: Spanned::new(span, kind),
202 }
203 }
204}
205
206impl<Span> SpannedError<Span> {
207 pub fn kind(&self) -> &ErrorKind {
209 &self.inner.extra
210 }
211}
212
213impl<Span: Copy> SpannedError<Span> {
214 pub fn span(&self) -> LocatedSpan<Span> {
216 self.inner.with_no_extra()
217 }
218}
219
220impl<Span> fmt::Display for SpannedError<Span> {
221 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
222 write!(
223 formatter,
224 "{}:{}: {}",
225 self.inner.location_line(),
226 self.inner.get_column(),
227 self.inner.extra
228 )
229 }
230}
231
232#[cfg(feature = "std")]
233impl<Span: fmt::Debug> std::error::Error for SpannedError<Span> {
234 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
235 std::error::Error::source(&self.inner.extra)
236 }
237}
238
239impl StripCode for Error<'_> {
240 type Stripped = SpannedError<usize>;
241
242 fn strip_code(self) -> Self::Stripped {
243 SpannedError {
244 inner: self.inner.map_fragment(str::len),
245 }
246 }
247}
248
249impl<'a> ParseError<InputSpan<'a>> for Error<'a> {
250 fn from_error_kind(mut input: InputSpan<'a>, kind: NomErrorKind) -> Self {
251 if kind == NomErrorKind::Char && !input.fragment().is_empty() {
252 input = input.slice(..1);
254 }
255
256 let error_kind = if kind == NomErrorKind::Char {
257 if input.fragment().is_empty() {
258 ErrorKind::UnexpectedTerm { context: None }
259 } else {
260 ErrorKind::UnexpectedChar { context: None }
261 }
262 } else {
263 ErrorKind::Other {
264 kind,
265 context: None,
266 }
267 };
268
269 Error::new(input, error_kind)
270 }
271
272 fn append(_: InputSpan<'a>, _: NomErrorKind, other: Self) -> Self {
273 other
274 }
275}
276
277impl<'a> ContextError<InputSpan<'a>> for Error<'a> {
278 fn add_context(input: InputSpan<'a>, ctx: &'static str, mut target: Self) -> Self {
279 let ctx = Context::new(ctx);
280 if ctx == Context::Comment {
281 target.inner.extra = ErrorKind::UnfinishedComment;
282 }
283
284 if input.location_offset() < target.inner.location_offset() {
285 if let Some(context) = target.inner.extra.context_mut() {
286 *context = Some(ctx);
287 }
288 }
289 target
290 }
291}
292
293impl<'a> FromExternalError<InputSpan<'a>, ErrorKind> for Error<'a> {
294 fn from_external_error(input: InputSpan<'a>, _: NomErrorKind, err: ErrorKind) -> Self {
295 Self::new(input, err)
296 }
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
302#[non_exhaustive]
303pub enum UnsupportedType {
304 UnaryOp(UnaryOp),
306 BinaryOp(BinaryOp),
308 Expr(ExprType),
310 Statement(StatementType),
312 Lvalue(LvalueType),
314}
315
316impl fmt::Display for UnsupportedType {
317 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
318 match self {
319 Self::UnaryOp(op) => write!(formatter, "unary op: {}", op),
320 Self::BinaryOp(op) => write!(formatter, "binary op: {}", op),
321 Self::Expr(expr) => write!(formatter, "expression: {}", expr),
322 Self::Statement(statement) => write!(formatter, "statement: {}", statement),
323 Self::Lvalue(lvalue) => write!(formatter, "lvalue: {}", lvalue),
324 }
325 }
326}
327
328impl From<UnaryOp> for UnsupportedType {
329 fn from(value: UnaryOp) -> Self {
330 Self::UnaryOp(value)
331 }
332}
333
334impl From<BinaryOp> for UnsupportedType {
335 fn from(value: BinaryOp) -> Self {
336 Self::BinaryOp(value)
337 }
338}
339
340impl From<ExprType> for UnsupportedType {
341 fn from(value: ExprType) -> Self {
342 Self::Expr(value)
343 }
344}
345
346impl From<StatementType> for UnsupportedType {
347 fn from(value: StatementType) -> Self {
348 Self::Statement(value)
349 }
350}
351
352impl From<LvalueType> for UnsupportedType {
353 fn from(value: LvalueType) -> Self {
354 Self::Lvalue(value)
355 }
356}