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(crate) struct RecognizerMetadata {
44 grammar_file_name: String,
45 rule_names: Vec<String>,
46 channel_names: Vec<String>,
47 mode_names: Vec<String>,
48 vocabulary: Vocabulary,
49}
50
51impl RecognizerMetadata {
52 pub(crate) fn from_static(
53 grammar_file_name: &'static str,
54 rule_names: &'static [&'static str],
55 channel_names: &'static [&'static str],
56 mode_names: &'static [&'static str],
57 vocabulary: Vocabulary,
58 ) -> Self {
59 Self {
60 grammar_file_name: grammar_file_name.to_owned(),
61 rule_names: rule_names.iter().map(|name| (*name).to_owned()).collect(),
62 channel_names: channel_names
63 .iter()
64 .map(|name| (*name).to_owned())
65 .collect(),
66 mode_names: mode_names.iter().map(|name| (*name).to_owned()).collect(),
67 vocabulary,
68 }
69 }
70}
71
72#[derive(Clone, Debug)]
73pub struct RecognizerData {
74 metadata: Arc<RecognizerMetadata>,
75 state: isize,
76 console_error_listener: bool,
77 error_listeners: Vec<ErrorListenerSlot>,
78}
79
80impl RecognizerData {
81 pub fn new(grammar_file_name: impl Into<String>, vocabulary: Vocabulary) -> Self {
82 Self {
83 metadata: Arc::new(RecognizerMetadata {
84 grammar_file_name: grammar_file_name.into(),
85 rule_names: Vec::new(),
86 channel_names: Vec::new(),
87 mode_names: Vec::new(),
88 vocabulary,
89 }),
90 state: -1,
91 console_error_listener: true,
92 error_listeners: Vec::new(),
93 }
94 }
95
96 pub(crate) const fn from_shared(metadata: Arc<RecognizerMetadata>) -> Self {
97 Self {
98 metadata,
99 state: -1,
100 console_error_listener: true,
101 error_listeners: Vec::new(),
102 }
103 }
104
105 #[must_use]
106 pub fn with_rule_names(
107 mut self,
108 rule_names: impl IntoIterator<Item = impl Into<String>>,
109 ) -> Self {
110 Arc::make_mut(&mut self.metadata).rule_names =
111 rule_names.into_iter().map(Into::into).collect();
112 self
113 }
114
115 #[must_use]
116 pub fn with_channel_names(
117 mut self,
118 channel_names: impl IntoIterator<Item = impl Into<String>>,
119 ) -> Self {
120 Arc::make_mut(&mut self.metadata).channel_names =
121 channel_names.into_iter().map(Into::into).collect();
122 self
123 }
124
125 #[must_use]
126 pub fn with_mode_names(
127 mut self,
128 mode_names: impl IntoIterator<Item = impl Into<String>>,
129 ) -> Self {
130 Arc::make_mut(&mut self.metadata).mode_names =
131 mode_names.into_iter().map(Into::into).collect();
132 self
133 }
134
135 #[must_use]
141 pub fn rule_names(&self) -> &[String] {
142 &self.metadata.rule_names
143 }
144
145 #[must_use]
147 pub fn vocabulary(&self) -> &Vocabulary {
148 &self.metadata.vocabulary
149 }
150
151 pub const fn state(&self) -> isize {
152 self.state
153 }
154
155 pub const fn set_state(&mut self, state: isize) {
156 self.state = state;
157 }
158
159 fn add_error_listener<L>(&mut self, listener: L)
160 where
161 L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
162 {
163 self.error_listeners.push(ErrorListenerSlot::new(listener));
164 }
165
166 fn remove_error_listeners(&mut self) {
167 self.console_error_listener = false;
168 self.error_listeners.clear();
169 }
170
171 #[allow(clippy::too_many_arguments)] fn notify_error_listeners(
173 &self,
174 recognizer: &dyn Recognizer,
175 offending: Option<TokenView<'_>>,
176 line: usize,
177 column: usize,
178 message: &str,
179 error: Option<&AntlrError>,
180 ) {
181 if self.console_error_listener {
182 ConsoleErrorListener.syntax_error(recognizer, offending, line, column, message, error);
183 }
184 for listener in &self.error_listeners {
185 listener.syntax_error(recognizer, offending, line, column, message, error);
186 }
187 }
188}
189
190pub trait Recognizer {
191 fn data(&self) -> &RecognizerData;
192 fn data_mut(&mut self) -> &mut RecognizerData;
193
194 fn grammar_file_name(&self) -> &str {
195 &self.data().metadata.grammar_file_name
196 }
197
198 fn rule_names(&self) -> &[String] {
199 &self.data().metadata.rule_names
200 }
201
202 fn channel_names(&self) -> &[String] {
203 &self.data().metadata.channel_names
204 }
205
206 fn mode_names(&self) -> &[String] {
207 &self.data().metadata.mode_names
208 }
209
210 fn vocabulary(&self) -> &Vocabulary {
211 &self.data().metadata.vocabulary
212 }
213
214 fn state(&self) -> isize {
215 self.data().state()
216 }
217
218 fn set_state(&mut self, state: isize) {
219 self.data_mut().set_state(state);
220 }
221
222 fn add_error_listener<L>(&mut self, listener: L)
228 where
229 Self: Sized,
230 L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
231 {
232 self.data_mut().add_error_listener(listener);
233 }
234
235 fn remove_error_listeners(&mut self) {
237 self.data_mut().remove_error_listeners();
238 }
239
240 fn notify_error_listeners(
246 &self,
247 offending: Option<TokenView<'_>>,
248 line: usize,
249 column: usize,
250 message: &str,
251 error: Option<&AntlrError>,
252 ) where
253 Self: Sized,
254 {
255 self.data()
256 .notify_error_listeners(self, offending, line, column, message, error);
257 }
258
259 fn sempred(&mut self, _rule_index: usize, _pred_index: usize) -> bool {
260 true
261 }
262
263 fn action(&mut self, _rule_index: usize, _action_index: usize) {}
264}
265
266#[cfg(test)]
267#[allow(clippy::disallowed_methods)] mod tests {
269 use std::mem::size_of;
270
271 use super::*;
272 use crate::generated::GrammarMetadata;
273
274 static SHARED_METADATA: GrammarMetadata = GrammarMetadata::new(
275 "Shared.g4",
276 &["start"],
277 &[None, Some("'x'")],
278 &[None, Some("X")],
279 &[None, None],
280 &["DEFAULT_TOKEN_CHANNEL", "HIDDEN"],
281 &["DEFAULT_MODE"],
282 &[],
283 );
284
285 #[derive(Clone, Debug, Eq, PartialEq)]
286 struct RecordedError {
287 grammar_file_name: String,
288 offending_text: Option<String>,
289 line: usize,
290 column: usize,
291 message: String,
292 error: Option<AntlrError>,
293 }
294
295 #[derive(Clone, Debug)]
296 struct RecordingErrorListener {
297 errors: Arc<Mutex<Vec<RecordedError>>>,
298 }
299
300 impl<R> ErrorListener<R> for RecordingErrorListener
301 where
302 R: Recognizer + ?Sized,
303 {
304 fn syntax_error(
305 &mut self,
306 recognizer: &R,
307 offending: Option<TokenView<'_>>,
308 line: usize,
309 column: usize,
310 message: &str,
311 error: Option<&AntlrError>,
312 ) {
313 self.errors
314 .lock()
315 .expect("recorded errors lock")
316 .push(RecordedError {
317 grammar_file_name: recognizer.grammar_file_name().to_owned(),
318 offending_text: offending.and_then(|token| token.text().map(str::to_owned)),
319 line,
320 column,
321 message: message.to_owned(),
322 error: error.cloned(),
323 });
324 }
325 }
326
327 #[derive(Clone, Debug)]
328 struct TestRecognizer {
329 data: RecognizerData,
330 }
331
332 impl Recognizer for TestRecognizer {
333 fn data(&self) -> &RecognizerData {
334 &self.data
335 }
336
337 fn data_mut(&mut self) -> &mut RecognizerData {
338 &mut self.data
339 }
340 }
341
342 fn test_recognizer() -> TestRecognizer {
343 TestRecognizer {
344 data: RecognizerData::new(
345 "Test.g4",
346 Vocabulary::new(
347 std::iter::empty::<Option<&str>>(),
348 std::iter::empty::<Option<&str>>(),
349 std::iter::empty::<Option<&str>>(),
350 ),
351 ),
352 }
353 }
354
355 #[test]
356 fn recognizers_replace_the_default_console_error_listener() {
357 let mut recognizer = test_recognizer();
358 assert!(recognizer.data.console_error_listener);
359 assert!(recognizer.data.error_listeners.is_empty());
360
361 recognizer.remove_error_listeners();
362 assert!(!recognizer.data.console_error_listener);
363 assert!(recognizer.data.error_listeners.is_empty());
364
365 let errors = Arc::new(Mutex::new(Vec::new()));
366 recognizer.add_error_listener(RecordingErrorListener {
367 errors: Arc::clone(&errors),
368 });
369 let error = AntlrError::ParserError {
370 line: 3,
371 column: 5,
372 message: "unexpected token".to_owned(),
373 offending: None,
374 };
375 recognizer.notify_error_listeners(None, 3, 5, "unexpected token", Some(&error));
376
377 insta::assert_debug_snapshot!(
378 "recognizers_replace_the_default_console_error_listener",
379 *errors.lock().expect("recorded errors lock")
380 );
381 }
382
383 #[test]
384 fn recognizer_data_remains_send_and_sync() {
385 fn assert_send_and_sync<T: Send + Sync>() {}
386
387 assert_send_and_sync::<RecognizerData>();
388 }
389
390 #[test]
391 fn recognizer_data_keeps_shared_metadata_out_of_line() {
392 assert!(size_of::<RecognizerData>() < size_of::<RecognizerMetadata>());
393 }
394
395 #[test]
396 fn cloned_recognizers_can_reconfigure_their_listener_lists_independently() {
397 let mut original = test_recognizer();
398 let clone = original.clone();
399
400 original.remove_error_listeners();
401
402 assert!(!original.data.console_error_listener);
403 assert!(original.data.error_listeners.is_empty());
404 assert!(clone.data.console_error_listener);
405 assert!(clone.data.error_listeners.is_empty());
406 }
407
408 #[test]
409 fn generated_recognizers_share_metadata_but_not_instance_state() {
410 let mut first = SHARED_METADATA.recognizer_data();
411 let second = SHARED_METADATA.recognizer_data();
412
413 assert!(std::ptr::eq(first.rule_names(), second.rule_names()));
414 assert!(std::ptr::eq(first.vocabulary(), second.vocabulary()));
415 assert!(first.console_error_listener);
416 assert!(second.console_error_listener);
417
418 first.set_state(7);
419 first.remove_error_listeners();
420
421 assert_eq!(first.state(), 7);
422 assert_eq!(second.state(), -1);
423 assert!(!first.console_error_listener);
424 assert!(first.error_listeners.is_empty());
425 assert!(second.console_error_listener);
426 assert!(second.error_listeners.is_empty());
427 }
428
429 #[test]
430 fn customizing_shared_metadata_detaches_only_that_recognizer() {
431 let customized = SHARED_METADATA
432 .recognizer_data()
433 .with_rule_names(["replacement"]);
434 let shared = SHARED_METADATA.recognizer_data();
435
436 assert_eq!(customized.rule_names(), ["replacement"]);
437 assert_eq!(shared.rule_names(), ["start"]);
438 assert!(!std::ptr::eq(customized.rule_names(), shared.rule_names()));
439 }
440}