Skip to main content

scarf_parser/
error.rs

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