1use std::fmt;
2use std::sync::{Arc, Mutex};
3
4use crate::errors::{AntlrError, ConsoleErrorListener, ErrorListener};
5use crate::token::TokenView;
6use crate::vocabulary::Vocabulary;
7
8#[derive(Clone)]
9struct ErrorListenerSlot(Arc<Mutex<dyn for<'a> ErrorListener<dyn Recognizer + 'a> + Send>>);
10
11impl ErrorListenerSlot {
12 fn new<L>(listener: L) -> Self
13 where
14 L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
15 {
16 Self(Arc::new(Mutex::new(listener)))
17 }
18
19 #[allow(clippy::too_many_arguments)] fn syntax_error(
21 &self,
22 recognizer: &(dyn Recognizer + '_),
23 offending: Option<TokenView<'_>>,
24 line: usize,
25 column: usize,
26 message: &str,
27 error: Option<&AntlrError>,
28 ) {
29 self.0
30 .lock()
31 .expect("error listener lock poisoned")
32 .syntax_error(recognizer, offending, line, column, message, error);
33 }
34}
35
36impl fmt::Debug for ErrorListenerSlot {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 f.write_str("ErrorListener")
39 }
40}
41
42#[derive(Clone, Debug)]
43pub struct RecognizerData {
44 grammar_file_name: String,
45 rule_names: Vec<String>,
46 channel_names: Vec<String>,
47 mode_names: Vec<String>,
48 vocabulary: Vocabulary,
49 state: isize,
50 error_listeners: Vec<ErrorListenerSlot>,
51}
52
53impl RecognizerData {
54 pub fn new(grammar_file_name: impl Into<String>, vocabulary: Vocabulary) -> Self {
55 Self {
56 grammar_file_name: grammar_file_name.into(),
57 rule_names: Vec::new(),
58 channel_names: Vec::new(),
59 mode_names: Vec::new(),
60 vocabulary,
61 state: -1,
62 error_listeners: vec![ErrorListenerSlot::new(ConsoleErrorListener)],
63 }
64 }
65
66 #[must_use]
67 pub fn with_rule_names(
68 mut self,
69 rule_names: impl IntoIterator<Item = impl Into<String>>,
70 ) -> Self {
71 self.rule_names = rule_names.into_iter().map(Into::into).collect();
72 self
73 }
74
75 #[must_use]
76 pub fn with_channel_names(
77 mut self,
78 channel_names: impl IntoIterator<Item = impl Into<String>>,
79 ) -> Self {
80 self.channel_names = channel_names.into_iter().map(Into::into).collect();
81 self
82 }
83
84 #[must_use]
85 pub fn with_mode_names(
86 mut self,
87 mode_names: impl IntoIterator<Item = impl Into<String>>,
88 ) -> Self {
89 self.mode_names = mode_names.into_iter().map(Into::into).collect();
90 self
91 }
92
93 #[must_use]
99 pub fn rule_names(&self) -> &[String] {
100 &self.rule_names
101 }
102
103 #[must_use]
105 pub const fn vocabulary(&self) -> &Vocabulary {
106 &self.vocabulary
107 }
108
109 pub const fn state(&self) -> isize {
110 self.state
111 }
112
113 pub const fn set_state(&mut self, state: isize) {
114 self.state = state;
115 }
116
117 fn add_error_listener<L>(&mut self, listener: L)
118 where
119 L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
120 {
121 self.error_listeners.push(ErrorListenerSlot::new(listener));
122 }
123
124 fn remove_error_listeners(&mut self) {
125 self.error_listeners.clear();
126 }
127
128 #[allow(clippy::too_many_arguments)] fn notify_error_listeners(
130 &self,
131 recognizer: &dyn Recognizer,
132 offending: Option<TokenView<'_>>,
133 line: usize,
134 column: usize,
135 message: &str,
136 error: Option<&AntlrError>,
137 ) {
138 for listener in &self.error_listeners {
139 listener.syntax_error(recognizer, offending, line, column, message, error);
140 }
141 }
142}
143
144pub trait Recognizer {
145 fn data(&self) -> &RecognizerData;
146 fn data_mut(&mut self) -> &mut RecognizerData;
147
148 fn grammar_file_name(&self) -> &str {
149 &self.data().grammar_file_name
150 }
151
152 fn rule_names(&self) -> &[String] {
153 &self.data().rule_names
154 }
155
156 fn channel_names(&self) -> &[String] {
157 &self.data().channel_names
158 }
159
160 fn mode_names(&self) -> &[String] {
161 &self.data().mode_names
162 }
163
164 fn vocabulary(&self) -> &Vocabulary {
165 &self.data().vocabulary
166 }
167
168 fn state(&self) -> isize {
169 self.data().state()
170 }
171
172 fn set_state(&mut self, state: isize) {
173 self.data_mut().set_state(state);
174 }
175
176 fn add_error_listener<L>(&mut self, listener: L)
182 where
183 Self: Sized,
184 L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
185 {
186 self.data_mut().add_error_listener(listener);
187 }
188
189 fn remove_error_listeners(&mut self) {
191 self.data_mut().remove_error_listeners();
192 }
193
194 fn notify_error_listeners(
200 &self,
201 offending: Option<TokenView<'_>>,
202 line: usize,
203 column: usize,
204 message: &str,
205 error: Option<&AntlrError>,
206 ) where
207 Self: Sized,
208 {
209 self.data()
210 .notify_error_listeners(self, offending, line, column, message, error);
211 }
212
213 fn sempred(&mut self, _rule_index: usize, _pred_index: usize) -> bool {
214 true
215 }
216
217 fn action(&mut self, _rule_index: usize, _action_index: usize) {}
218}
219
220#[cfg(test)]
221#[allow(clippy::disallowed_methods)] mod tests {
223 use super::*;
224
225 #[derive(Clone, Debug, Eq, PartialEq)]
226 struct RecordedError {
227 grammar_file_name: String,
228 offending_text: Option<String>,
229 line: usize,
230 column: usize,
231 message: String,
232 error: Option<AntlrError>,
233 }
234
235 #[derive(Clone, Debug)]
236 struct RecordingErrorListener {
237 errors: Arc<Mutex<Vec<RecordedError>>>,
238 }
239
240 impl<R> ErrorListener<R> for RecordingErrorListener
241 where
242 R: Recognizer + ?Sized,
243 {
244 fn syntax_error(
245 &mut self,
246 recognizer: &R,
247 offending: Option<TokenView<'_>>,
248 line: usize,
249 column: usize,
250 message: &str,
251 error: Option<&AntlrError>,
252 ) {
253 self.errors
254 .lock()
255 .expect("recorded errors lock")
256 .push(RecordedError {
257 grammar_file_name: recognizer.grammar_file_name().to_owned(),
258 offending_text: offending.and_then(|token| token.text().map(str::to_owned)),
259 line,
260 column,
261 message: message.to_owned(),
262 error: error.cloned(),
263 });
264 }
265 }
266
267 #[derive(Clone, Debug)]
268 struct TestRecognizer {
269 data: RecognizerData,
270 }
271
272 impl Recognizer for TestRecognizer {
273 fn data(&self) -> &RecognizerData {
274 &self.data
275 }
276
277 fn data_mut(&mut self) -> &mut RecognizerData {
278 &mut self.data
279 }
280 }
281
282 fn test_recognizer() -> TestRecognizer {
283 TestRecognizer {
284 data: RecognizerData::new(
285 "Test.g4",
286 Vocabulary::new(
287 std::iter::empty::<Option<&str>>(),
288 std::iter::empty::<Option<&str>>(),
289 std::iter::empty::<Option<&str>>(),
290 ),
291 ),
292 }
293 }
294
295 #[test]
296 fn recognizers_replace_the_default_console_error_listener() {
297 let mut recognizer = test_recognizer();
298 assert_eq!(recognizer.data.error_listeners.len(), 1);
299
300 recognizer.remove_error_listeners();
301 assert!(recognizer.data.error_listeners.is_empty());
302
303 let errors = Arc::new(Mutex::new(Vec::new()));
304 recognizer.add_error_listener(RecordingErrorListener {
305 errors: Arc::clone(&errors),
306 });
307 let error = AntlrError::ParserError {
308 line: 3,
309 column: 5,
310 message: "unexpected token".to_owned(),
311 offending: None,
312 };
313 recognizer.notify_error_listeners(None, 3, 5, "unexpected token", Some(&error));
314
315 insta::assert_debug_snapshot!(
316 "recognizers_replace_the_default_console_error_listener",
317 *errors.lock().expect("recorded errors lock")
318 );
319 }
320
321 #[test]
322 fn recognizer_data_remains_send_and_sync() {
323 fn assert_send_and_sync<T: Send + Sync>() {}
324
325 assert_send_and_sync::<RecognizerData>();
326 }
327
328 #[test]
329 fn cloned_recognizers_can_reconfigure_their_listener_lists_independently() {
330 let mut original = test_recognizer();
331 let clone = original.clone();
332
333 original.remove_error_listeners();
334
335 assert!(original.data.error_listeners.is_empty());
336 assert_eq!(clone.data.error_listeners.len(), 1);
337 }
338}