Skip to main content

scarf_parser/
error.rs

1// =======================================================================
2// error.rs
3// =======================================================================
4//! Errors used in parsing
5
6use crate::report::Report;
7use crate::*;
8use core::ops::Range;
9use lexer::Token;
10use scarf_syntax::*;
11use std::fmt;
12use std::fs;
13use winnow::{
14    error::{AddContext, ParserError},
15    stream::Stream,
16};
17
18/// A trait for displaying a short representation of an object as a [`String`]
19pub(crate) trait DisplayShort {
20    fn to_short_string(&self) -> String;
21}
22
23/// Something the parser expected to find instead of what was found,
24/// in the case of an error
25#[derive(Debug, Clone, PartialEq)]
26pub enum Expectation<'s> {
27    /// A particular lexed token
28    Token(Token<'s>),
29    /// A human-readable expectation
30    Label(&'s str),
31    /// The end of a file
32    EOI,
33}
34
35impl<'a> std::fmt::Display for Expectation<'a> {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            Expectation::Token(token) => token.fmt(f),
39            Expectation::Label(label) => write!(f, "{}", label),
40            Expectation::EOI => write!(f, "end of input"),
41        }
42    }
43}
44
45/// A verbose error message describing the error location, what was
46/// found, and what was expected instead
47#[derive(Debug, Clone, PartialEq)]
48pub struct VerboseError<'s> {
49    /// The [`Span`] where the error occurred
50    pub span: Span<'s>,
51    /// What token was found (if any - [`None`] indicates the end of a file)
52    pub found: Option<Token<'s>>,
53    /// What was expected instead of what was found
54    pub expected: Vec<Expectation<'s>>,
55}
56
57impl<'s> ParserError<Tokens<'s>> for VerboseError<'s> {
58    type Inner = Self;
59    fn from_input(input: &Tokens<'s>) -> Self {
60        match input.peek_token() {
61            Some(token) => VerboseError {
62                span: token.1.clone(),
63                found: Some(token.0),
64                expected: vec![],
65            },
66            None => {
67                // Use the last token instead, indicate EOF
68                match input.previous_tokens().next() {
69                    Some(token) => {
70                        let mut curr_span: &Span = &token.1;
71                        let root_file = loop {
72                            if let Some(included_from_span) =
73                                curr_span.included_from
74                            {
75                                curr_span = included_from_span;
76                            } else {
77                                break curr_span.file;
78                            }
79                        };
80                        VerboseError {
81                            span: Span {
82                                file: root_file,
83                                bytes: Range {
84                                    start: token.1.bytes.end,
85                                    end: token.1.bytes.end,
86                                },
87                                expanded_from: None,
88                                included_from: None,
89                            },
90                            found: None,
91                            expected: vec![],
92                        }
93                    }
94                    None => {
95                        // No tokens ever present in input - use defaults
96                        VerboseError {
97                            span: Span::default(),
98                            found: None,
99                            expected: vec![],
100                        }
101                    }
102                }
103            }
104        }
105    }
106    fn into_inner(self) -> winnow::Result<Self::Inner, Self> {
107        Ok(self)
108    }
109    fn or(mut self, mut other: Self) -> Self {
110        // Prefer errors that got to the end of the input
111        match (self.found, other.found) {
112            (None, Some(_)) => self,
113            (Some(_), None) => other,
114            (None, None) => {
115                self.expected.append(&mut other.expected);
116                self
117            }
118            (Some(_), Some(_)) => {
119                // Prefer the one with a later span (a.k.a. got farther)
120                match self.span.compare(&other.span) {
121                    SpanRelation::Later => self,
122                    SpanRelation::Earlier => other,
123                    SpanRelation::Same => {
124                        self.expected.append(&mut other.expected);
125                        self
126                    }
127                }
128            }
129        }
130    }
131}
132
133impl<'s> VerboseError<'s> {
134    /// Similar to [`VerboseError::or`], but modifies an existing
135    /// error instead of creating a new one
136    pub(crate) fn or_in_place(&mut self, mut other: Self) {
137        // Prefer errors that got to the end of the input
138        match (self.found, other.found) {
139            (None, Some(_)) => (),
140            (Some(_), None) => *self = other,
141            (None, None) => {
142                self.expected.append(&mut other.expected);
143            }
144            (Some(_), Some(_)) => {
145                // Prefer the one with a later span (a.k.a. got farther)
146                match self.span.compare(&other.span) {
147                    SpanRelation::Later => (),
148                    SpanRelation::Earlier => *self = other,
149                    SpanRelation::Same => {
150                        self.expected.append(&mut other.expected);
151                    }
152                }
153            }
154        }
155    }
156}
157
158impl<'s> AddContext<Tokens<'s>, Token<'s>> for VerboseError<'s> {
159    fn add_context(
160        mut self,
161        _input: &Tokens<'s>,
162        _token_start: &<Tokens<'s> as Stream>::Checkpoint,
163        _context: Token<'s>,
164    ) -> Self {
165        self.expected.push(Expectation::Token(_context));
166        self
167    }
168}
169impl<'s> AddContext<Tokens<'s>, &'s str> for VerboseError<'s> {
170    fn add_context(
171        mut self,
172        _input: &Tokens<'s>,
173        _token_start: &<Tokens<'s> as Stream>::Checkpoint,
174        _context: &'s str,
175    ) -> Self {
176        self.expected.push(Expectation::Label(_context));
177        self
178    }
179}
180
181impl<'a> fmt::Display for VerboseError<'a> {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        write!(f, "found ")?;
184        match self.found {
185            Some(tok) => tok.fmt(f)?,
186            None => write!(f, "end of input")?,
187        };
188        write!(f, ", expected ")?;
189        let mut dedup_expected: Vec<Expectation<'a>> = vec![];
190        for expected in self.expected.iter() {
191            if !dedup_expected.contains(expected) {
192                dedup_expected.push(expected.clone());
193            }
194        }
195        match &dedup_expected[..] {
196            [] => write!(f, "something else"),
197            [expected] => expected.fmt(f),
198            _ => {
199                for expected in &dedup_expected[..dedup_expected.len() - 1] {
200                    expected.fmt(f)?;
201                    write!(f, ", ")?;
202                }
203                write!(f, "or ")?;
204                dedup_expected.last().unwrap().fmt(f)
205            }
206        }
207    }
208}
209
210impl<'a> DisplayShort for VerboseError<'a> {
211    fn to_short_string(&self) -> String {
212        match self.found {
213            Some(tok) => format!("Didn't expect {}", tok),
214            None => "Didn't expect end of input".to_owned(),
215        }
216    }
217}
218
219impl<'s> VerboseError<'s> {
220    /// Generate an error report for the [`VerboseError`]
221    pub fn report<C>(&self, code: C) -> Report
222    where
223        C: fmt::Display,
224    {
225        let error_span = if self.found.is_none() {
226            let file_len = fs::metadata(self.span.file)
227                .expect("TODO: Handle file read error")
228                .len();
229            let byte_span = Range {
230                start: file_len as usize,
231                end: file_len as usize,
232            };
233            Span {
234                file: self.span.file,
235                bytes: byte_span,
236                expanded_from: None,
237                included_from: self.span.included_from,
238            }
239        } else {
240            self.span.clone()
241        };
242        Report::new(
243            report::ReportKind::Error,
244            &error_span,
245            code,
246            self.to_string(),
247        )
248        .with_label(
249            &error_span,
250            report::ReportKind::Error,
251            self.to_short_string(),
252        )
253    }
254}