1use core::fmt;
4
5use nom::{
6 error::{ContextError, ErrorKind as NomErrorKind, FromExternalError, ParseError},
7 Input,
8};
9
10use crate::{
11 BinaryOp, ExprType, InputSpan, LocatedSpan, Location, LvalueType, Op, Spanned, StatementType,
12 UnaryOp,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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(err) => write!(formatter, "Invalid literal: {err}"),
111 Self::LiteralName => formatter.write_str("Literal used in place of an identifier"),
112
113 Self::Type(err) => write!(formatter, "Invalid type annotation: {err}"),
114
115 Self::UnsupportedOp(op) => write!(
116 formatter,
117 "Encountered operation switched off in the parser features: {op}"
118 ),
119
120 Self::UnexpectedChar { context: Some(ctx) } => {
121 write!(formatter, "Unexpected character in {ctx}")
122 }
123 Self::UnexpectedChar { .. } => formatter.write_str("Unexpected character"),
124
125 Self::UnexpectedTerm { context: Some(ctx) } => write!(formatter, "Unfinished {ctx}"),
126 Self::UnexpectedTerm { .. } => formatter.write_str("Unfinished expression"),
127
128 Self::Leftovers => formatter.write_str("Uninterpreted characters after parsing"),
129 Self::Incomplete => formatter.write_str("Incomplete input"),
130 Self::UnfinishedComment => formatter.write_str("Unfinished comment"),
131 Self::ChainedComparison => formatter.write_str("Chained comparisons"),
132 Self::Other { .. } => write!(formatter, "Cannot parse sequence"),
133 }
134 }
135}
136
137impl ErrorKind {
138 pub fn literal<T: Into<anyhow::Error>>(error: T) -> Self {
140 Self::Literal(error.into())
141 }
142
143 fn context_mut(&mut self) -> Option<&mut Option<Context>> {
144 match self {
145 Self::UnexpectedChar { context }
146 | Self::UnexpectedTerm { context }
147 | Self::Other { context, .. } => Some(context),
148 _ => None,
149 }
150 }
151
152 pub fn context(&self) -> Option<Context> {
154 match self {
155 Self::UnexpectedChar { context }
156 | Self::UnexpectedTerm { context }
157 | Self::Other { context, .. } => *context,
158 _ => None,
159 }
160 }
161
162 pub fn is_incomplete(&self) -> bool {
164 matches!(self, Self::Incomplete)
165 }
166
167 #[doc(hidden)]
168 pub fn with_span<T>(self, span: &Spanned<'_, T>) -> Error {
169 Error {
170 inner: span.copy_with_extra(self).map_fragment(str::len),
171 }
172 }
173}
174
175#[cfg(feature = "std")]
176impl std::error::Error for ErrorKind {
177 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
178 match self {
179 Self::Literal(err) | Self::Type(err) => Some(err.as_ref()),
180 _ => None,
181 }
182 }
183}
184
185#[derive(Debug)]
190pub struct Error {
191 inner: Location<ErrorKind>,
192}
193
194impl Error {
195 pub(crate) fn new(span: InputSpan<'_>, kind: ErrorKind) -> Self {
196 Self {
197 inner: LocatedSpan::from(span)
198 .map_fragment(str::len)
199 .copy_with_extra(kind),
200 }
201 }
202
203 pub fn kind(&self) -> &ErrorKind {
205 &self.inner.extra
206 }
207
208 pub fn location(&self) -> Location {
210 self.inner.with_no_extra()
211 }
212}
213
214impl fmt::Display for Error {
215 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
216 write!(
217 formatter,
218 "{}:{}: {}",
219 self.inner.location_line(),
220 self.inner.get_column(),
221 self.inner.extra
222 )
223 }
224}
225
226#[cfg(feature = "std")]
227impl std::error::Error for Error {
228 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
229 std::error::Error::source(&self.inner.extra)
230 }
231}
232
233impl<'a> ParseError<InputSpan<'a>> for Error {
234 fn from_error_kind(mut input: InputSpan<'a>, kind: NomErrorKind) -> Self {
235 if kind == NomErrorKind::Char && !input.fragment().is_empty() {
236 input = input.take(1);
238 }
239
240 let error_kind = if kind == NomErrorKind::Char {
241 if input.fragment().is_empty() {
242 ErrorKind::UnexpectedTerm { context: None }
243 } else {
244 ErrorKind::UnexpectedChar { context: None }
245 }
246 } else {
247 ErrorKind::Other {
248 kind,
249 context: None,
250 }
251 };
252
253 Error::new(input, error_kind)
254 }
255
256 fn append(_: InputSpan<'a>, _: NomErrorKind, other: Self) -> Self {
257 other
258 }
259}
260
261impl<'a> ContextError<InputSpan<'a>> for Error {
262 fn add_context(input: InputSpan<'a>, ctx: &'static str, mut target: Self) -> Self {
263 let ctx = Context::new(ctx);
264 if ctx == Context::Comment {
265 target.inner.extra = ErrorKind::UnfinishedComment;
266 }
267
268 if input.location_offset() < target.inner.location_offset() {
269 if let Some(context) = target.inner.extra.context_mut() {
270 *context = Some(ctx);
271 }
272 }
273 target
274 }
275}
276
277impl<'a> FromExternalError<InputSpan<'a>, ErrorKind> for Error {
278 fn from_external_error(input: InputSpan<'a>, _: NomErrorKind, err: ErrorKind) -> Self {
279 Self::new(input, err)
280 }
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
286#[non_exhaustive]
287pub enum UnsupportedType {
288 UnaryOp(UnaryOp),
290 BinaryOp(BinaryOp),
292 Expr(ExprType),
294 Statement(StatementType),
296 Lvalue(LvalueType),
298}
299
300impl fmt::Display for UnsupportedType {
301 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
302 match self {
303 Self::UnaryOp(op) => write!(formatter, "unary op: {op}"),
304 Self::BinaryOp(op) => write!(formatter, "binary op: {op}"),
305 Self::Expr(expr) => write!(formatter, "expression: {expr}"),
306 Self::Statement(statement) => write!(formatter, "statement: {statement}"),
307 Self::Lvalue(lvalue) => write!(formatter, "lvalue: {lvalue}"),
308 }
309 }
310}
311
312impl From<UnaryOp> for UnsupportedType {
313 fn from(value: UnaryOp) -> Self {
314 Self::UnaryOp(value)
315 }
316}
317
318impl From<BinaryOp> for UnsupportedType {
319 fn from(value: BinaryOp) -> Self {
320 Self::BinaryOp(value)
321 }
322}
323
324impl From<ExprType> for UnsupportedType {
325 fn from(value: ExprType) -> Self {
326 Self::Expr(value)
327 }
328}
329
330impl From<StatementType> for UnsupportedType {
331 fn from(value: StatementType) -> Self {
332 Self::Statement(value)
333 }
334}
335
336impl From<LvalueType> for UnsupportedType {
337 fn from(value: LvalueType) -> Self {
338 Self::Lvalue(value)
339 }
340}