1use crate::syntax::ast::{position::Position, Node};
4use crate::syntax::lexer::{Error as LexError, Token, TokenKind};
5use std::fmt;
6
7pub type ParseResult = Result<Node, ParseError>;
9
10pub(crate) trait ErrorContext {
11 fn context(self, context: &'static str) -> Self;
12}
13
14impl<T> ErrorContext for Result<T, ParseError> {
15 fn context(self, context: &'static str) -> Self {
16 self.map_err(|e| e.context(context))
17 }
18}
19
20impl From<LexError> for ParseError {
21 fn from(e: LexError) -> ParseError {
22 ParseError::lex(e)
23 }
24}
25
26#[derive(Debug)]
28pub enum ParseError {
29 Expected {
31 expected: Box<[TokenKind]>,
32 found: Token,
33 context: &'static str,
34 },
35 Unexpected {
37 found: Token,
38 message: Option<&'static str>,
39 },
40 AbruptEnd,
42 Lex { err: LexError },
44 General {
46 message: &'static str,
47 position: Position,
48 },
49 Unimplemented {
51 message: &'static str,
52 position: Position,
53 },
54}
55
56impl ParseError {
57 fn context(self, new_context: &'static str) -> Self {
59 match self {
60 Self::Expected {
61 expected, found, ..
62 } => Self::expected(expected, found, new_context),
63 e => e,
64 }
65 }
66
67 pub(super) fn expected<E>(expected: E, found: Token, context: &'static str) -> Self
69 where
70 E: Into<Box<[TokenKind]>>,
71 {
72 Self::Expected {
73 expected: expected.into(),
74 found,
75 context,
76 }
77 }
78
79 pub(super) fn unexpected<C>(found: Token, message: C) -> Self
81 where
82 C: Into<Option<&'static str>>,
83 {
84 Self::Unexpected {
85 found,
86 message: message.into(),
87 }
88 }
89
90 pub(super) fn general(message: &'static str, position: Position) -> Self {
92 Self::General { message, position }
93 }
94
95 pub(super) fn lex(e: LexError) -> Self {
97 Self::Lex { err: e }
98 }
99
100 #[allow(dead_code)]
102 pub(super) fn unimplemented(message: &'static str, position: Position) -> Self {
103 Self::Unimplemented { message, position }
104 }
105}
106
107impl fmt::Display for ParseError {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 Self::Expected {
111 expected,
112 found,
113 context,
114 } => write!(
115 f,
116 "expected {}, got '{}' in {} at line {}, col {}",
117 if expected.len() == 1 {
118 format!(
119 "token '{}'",
120 expected.first().map(TokenKind::to_string).unwrap()
121 )
122 } else {
123 format!(
124 "one of {}",
125 expected
126 .iter()
127 .enumerate()
128 .map(|(i, t)| {
129 format!(
130 "{}'{}'",
131 if i == 0 {
132 ""
133 } else if i == expected.len() - 1 {
134 " or "
135 } else {
136 ", "
137 },
138 t
139 )
140 })
141 .collect::<String>()
142 )
143 },
144 found,
145 context,
146 found.span().start().line_number(),
147 found.span().start().column_number()
148 ),
149 Self::Unexpected { found, message } => write!(
150 f,
151 "unexpected token '{}'{} at line {}, col {}",
152 found,
153 if let Some(m) = message {
154 format!(", {}", m)
155 } else {
156 String::new()
157 },
158 found.span().start().line_number(),
159 found.span().start().column_number()
160 ),
161 Self::AbruptEnd => f.write_str("abrupt end"),
162 Self::General { message, position } => write!(
163 f,
164 "{} at line {}, col {}",
165 message,
166 position.line_number(),
167 position.column_number()
168 ),
169 Self::Lex { err } => fmt::Display::fmt(err, f),
170 Self::Unimplemented { message, position } => write!(
171 f,
172 "{} not yet implemented at line {}, col {}",
173 message,
174 position.line_number(),
175 position.column_number()
176 ),
177 }
178 }
179}