1use nom::error::{ContextError, ErrorKind};
2use nom_locate::LocatedSpan;
3use std::fmt::{Debug, Write};
4
5pub(crate) type Span<'a> = LocatedSpan<&'a str>;
6pub(crate) type IResult<'a, A, B> = nom::IResult<A, B, ParseError<Span<'a>>>;
7
8pub fn make_span(s: &str) -> Span {
9 Span::new(s)
10}
11
12#[derive(Debug)]
13pub enum ParseErrorKind {
14 Context(&'static str),
15 Char(char),
16 Nom(ErrorKind),
17 External { kind: ErrorKind, reason: String },
18}
19
20#[derive(Debug)]
21pub struct ParseError<I: Debug> {
22 pub errors: Vec<(I, ParseErrorKind)>,
23}
24
25impl<I: Debug> ContextError<I> for ParseError<I> {
26 fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self {
27 other.errors.push((input, ParseErrorKind::Context(ctx)));
28 other
29 }
30}
31
32impl<I: Debug> nom::error::ParseError<I> for ParseError<I> {
33 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
34 Self {
35 errors: vec![(input, ParseErrorKind::Nom(kind))],
36 }
37 }
38
39 fn append(input: I, kind: ErrorKind, mut other: Self) -> Self {
40 other.errors.push((input, ParseErrorKind::Nom(kind)));
41 other
42 }
43
44 fn from_char(input: I, c: char) -> Self {
45 Self {
46 errors: vec![(input, ParseErrorKind::Char(c))],
47 }
48 }
49}
50
51impl<I: Debug, E: std::fmt::Display> nom::error::FromExternalError<I, E> for ParseError<I> {
52 fn from_external_error(input: I, kind: ErrorKind, err: E) -> Self {
53 Self {
54 errors: vec![(
55 input,
56 ParseErrorKind::External {
57 kind,
58 reason: format!("{err:#}"),
59 },
60 )],
61 }
62 }
63}
64
65pub fn make_context_error<S: Into<String>>(
66 input: Span<'_>,
67 reason: S,
68) -> nom::Err<ParseError<Span<'_>>> {
69 nom::Err::Error(ParseError {
70 errors: vec![(
71 input,
72 ParseErrorKind::External {
73 kind: nom::error::ErrorKind::Fail,
74 reason: reason.into(),
75 },
76 )],
77 })
78}
79
80pub fn explain_nom(input: Span, err: nom::Err<ParseError<Span<'_>>>) -> String {
81 match err {
82 nom::Err::Error(e) => {
83 let mut result = String::new();
84 for (i, (span, kind)) in e.errors.iter().enumerate() {
85 if input.is_empty() {
86 match kind {
87 ParseErrorKind::Char(c) => {
88 write!(&mut result, "{i}: expected '{c}', got empty input\n\n")
89 }
90 ParseErrorKind::Context(s) => {
91 write!(&mut result, "{i}: in {s}, got empty input\n\n")
92 }
93 ParseErrorKind::External { kind, reason } => {
94 write!(&mut result, "{i}: {reason} {kind:?}, got empty input\n\n")
95 }
96 ParseErrorKind::Nom(e) => {
97 write!(&mut result, "{i}: in {e:?}, got empty input\n\n")
98 }
99 }
100 .ok();
101 continue;
102 }
103
104 let line_number = span.location_line();
105 let line = std::str::from_utf8(span.get_line_beginning())
106 .unwrap_or("<INVALID: line slice is not utf8!>");
107 let line: String = line
111 .chars()
112 .map(|c| match c {
113 '\t' => '\u{2409}',
114 '\r' => '\u{240d}',
115 '\n' => '\u{240a}',
116 _ => c,
117 })
118 .collect();
119 let column = span.get_utf8_column();
120 let mut caret = " ".repeat(column.saturating_sub(1));
121 caret.push('^');
122 for _ in 1..span.fragment().len() {
123 caret.push('_')
124 }
125
126 match kind {
127 ParseErrorKind::Char(expected) => {
128 if let Some(actual) = span.fragment().chars().next() {
129 write!(
130 &mut result,
131 "{i}: at line {line_number}:\n\
132 {line}\n\
133 {caret}\n\
134 expected '{expected}', found {actual}\n\n",
135 )
136 } else {
137 write!(
138 &mut result,
139 "{i}: at line {line_number}:\n\
140 {line}\n\
141 {caret}\n\
142 expected '{expected}', got end of input\n\n",
143 )
144 }
145 }
146 ParseErrorKind::Context(context) => {
147 write!(
148 &mut result,
149 "{i}: at line {line_number}, in {context}:\n\
150 {line}\n\
151 {caret}\n\n",
152 )
153 }
154 ParseErrorKind::External { kind, reason } => {
155 write!(
156 &mut result,
157 "{i}: at line {line_number}, {reason} {kind:?}:\n\
158 {line}\n\
159 {caret}\n\n",
160 )
161 }
162 ParseErrorKind::Nom(nom_err) => {
163 write!(
164 &mut result,
165 "{i}: at line {line_number}, in {nom_err:?}:\n\
166 {line}\n\
167 {caret}\n\n",
168 )
169 }
170 }
171 .ok();
172 }
173 result
174 }
175 _ => format!("{err:#}"),
176 }
177}