astray_core/error/
parse_error.rs1use crate::{ConsumableToken, Parsable};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum ParseErrorType<T>
5where
6 T: ConsumableToken,
7{
8 UnexpectedToken { expected: T, found: T },
9 NoMoreTokens,
10 ParsedButUnmatching { err_msg: String }, ConjunctBranchParsingFailure { err_source: Box<ParseError<T>> },
12 DisjunctBranchParsingFailure { err_source: Vec<ParseError<T>> },
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ParseError<T>
18where
19 T: ConsumableToken,
20{
21 type_name: &'static str,
22 failed_at: usize,
23 pub failure_type: ParseErrorType<T>,
24}
25
26impl<T> ParseError<T>
27where
28 T: ConsumableToken,
29{
30 pub fn new<P>(failed_at: usize, failure_type: ParseErrorType<T>) -> Self
31 where
32 P: Parsable<T>,
33 {
34 Self {
35 type_name: P::identifier(),
36 failed_at,
37 failure_type,
38 }
39 }
40
41 pub fn parsed_but_unmatching<P>(
42 failed_at: usize,
43 result: &P,
44 pattern: Option<&'static str>,
45 ) -> Self
46 where
47 P: Parsable<T>,
48 {
49 let pattern = pattern.unwrap_or("(pattern not provided by user)");
50 let type_name = <P as Parsable<T>>::identifier();
51 let err_msg = format!(
52 "Parsed {:?}: {type_name}, but it did not match pattern '{pattern}'",
53 result
54 );
55 ParseError::new::<P>(failed_at, ParseErrorType::ParsedButUnmatching { err_msg })
56 }
57
58 pub fn no_more_tokens<P>(failed_at: usize) -> Self
59 where
60 P: Parsable<T>,
61 {
62 ParseError::new::<P>(failed_at, ParseErrorType::NoMoreTokens)
63 }
64
65 pub fn from_conjunct_error<P>(other: ParseError<T>) -> Self
66 where
67 P: Parsable<T>,
68 {
69 ParseError::new::<P>(
70 other.failed_at,
71 ParseErrorType::ConjunctBranchParsingFailure {
72 err_source: Box::new(other),
73 },
74 )
75 }
76
77 pub fn from_disjunct_errors<P>(failed_at: usize, err_source: Vec<ParseError<T>>) -> Self
78 where
79 P: Parsable<T>,
80 {
81 ParseError::new::<P>(
82 failed_at,
83 ParseErrorType::DisjunctBranchParsingFailure { err_source },
84 )
85 }
86
87 pub fn to_string(&self, indentation_level: usize) -> String {
88 let tabs = "\t".repeat(indentation_level);
89 match &self.failure_type {
90 ParseErrorType::UnexpectedToken { expected, found } => {
91 let more_tabs = "\t".repeat(indentation_level + 1);
92 format!("{tabs}Unexpected Token Error\n{more_tabs}Expected {:?}\n{more_tabs}Found {:?}\n", expected, found)
93 }
94 ParseErrorType::NoMoreTokens => {
95 format!("{tabs}Ran out of tokens\n")
96 }
97 ParseErrorType::ParsedButUnmatching { err_msg } => {
98 format!("{tabs}{err_msg}")
99 }
100 ParseErrorType::ConjunctBranchParsingFailure { err_source } => {
101 let err_source_str = err_source.to_string(indentation_level + 1);
102 format!(
103 "{tabs}Failed to parse {}:\n{err_source_str}",
104 self.type_name
105 )
106 }
107 ParseErrorType::DisjunctBranchParsingFailure { err_source } => {
108 let errors = err_source
109 .iter()
110 .map(|e| e.to_string(indentation_level + 1))
111 .reduce(|accum, curr| accum + "\n" + &curr)
112 .expect("Enums without variants cannot implement Parsable");
113 format!("{tabs}Failed to parse {}:\n{errors}", self.type_name)
114 }
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use crate::token::Token;
122 use crate::{t, Parsable, ParseError};
123
124 #[test]
125 fn to_string() {
126 let pattern1 = "Token::KwReturn";
127 let pattern2 = "Token::LiteralInt(_)";
128 let result =
129 ParseError::from_conjunct_error::<Token>(
130 ParseError::from_disjunct_errors::<Token>(
131 1,
132 vec![
133 ParseError::parsed_but_unmatching::<Token>(
134 1,
135 &t!(return),
136 Some(pattern1),
137 ),
138 ParseError::parsed_but_unmatching::<Token>(
139 1,
140 &t!(litint 3),
141 Some(pattern2),
142 ),
143 ],
144 ))
145 .to_string(0);
146 let expected_identifier = <Token as Parsable<Token>>::identifier();
147 let expected = format!(
148 "Failed to parse {expected_identifier}:
149\tFailed to parse {expected_identifier}:
150\t\tParsed {:?}: {expected_identifier}, but it did not match pattern '{pattern1}'
151\t\tParsed {:?}: {expected_identifier}, but it did not match pattern '{pattern2}'",
152 t!(return),
153 t!(litint 3),
154 );
155 assert_eq!(expected, result)
156 }
157}