Skip to main content

antlr4_runtime/
recognizer.rs

1use std::fmt;
2use std::sync::{Arc, Mutex};
3
4use crate::errors::{ConsoleErrorListener, ErrorListener, SyntaxErrorEvent};
5use crate::vocabulary::Vocabulary;
6
7#[derive(Clone)]
8struct ErrorListenerSlot(Arc<Mutex<dyn for<'a> ErrorListener<dyn Recognizer + 'a> + Send>>);
9
10impl ErrorListenerSlot {
11    fn new<L>(listener: L) -> Self
12    where
13        L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
14    {
15        Self(Arc::new(Mutex::new(listener)))
16    }
17
18    fn syntax_error(&self, recognizer: &(dyn Recognizer + '_), event: &SyntaxErrorEvent<'_>) {
19        self.0
20            .lock()
21            .expect("error listener lock poisoned")
22            .syntax_error(recognizer, event);
23    }
24}
25
26impl fmt::Debug for ErrorListenerSlot {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        f.write_str("ErrorListener")
29    }
30}
31
32#[derive(Clone, Debug)]
33pub(crate) struct RecognizerMetadata {
34    grammar_file_name: String,
35    rule_names: Vec<String>,
36    channel_names: Vec<String>,
37    mode_names: Vec<String>,
38    vocabulary: Vocabulary,
39}
40
41impl RecognizerMetadata {
42    pub(crate) fn from_static(
43        grammar_file_name: &'static str,
44        rule_names: &'static [&'static str],
45        channel_names: &'static [&'static str],
46        mode_names: &'static [&'static str],
47        vocabulary: Vocabulary,
48    ) -> Self {
49        Self {
50            grammar_file_name: grammar_file_name.to_owned(),
51            rule_names: rule_names.iter().map(|name| (*name).to_owned()).collect(),
52            channel_names: channel_names
53                .iter()
54                .map(|name| (*name).to_owned())
55                .collect(),
56            mode_names: mode_names.iter().map(|name| (*name).to_owned()).collect(),
57            vocabulary,
58        }
59    }
60}
61
62#[derive(Clone, Debug)]
63pub struct RecognizerData {
64    metadata: Arc<RecognizerMetadata>,
65    state: isize,
66    console_error_listener: bool,
67    error_listeners: Vec<ErrorListenerSlot>,
68}
69
70impl RecognizerData {
71    pub fn new(grammar_file_name: impl Into<String>, vocabulary: Vocabulary) -> Self {
72        Self {
73            metadata: Arc::new(RecognizerMetadata {
74                grammar_file_name: grammar_file_name.into(),
75                rule_names: Vec::new(),
76                channel_names: Vec::new(),
77                mode_names: Vec::new(),
78                vocabulary,
79            }),
80            state: -1,
81            console_error_listener: true,
82            error_listeners: Vec::new(),
83        }
84    }
85
86    pub(crate) const fn from_shared(metadata: Arc<RecognizerMetadata>) -> Self {
87        Self {
88            metadata,
89            state: -1,
90            console_error_listener: true,
91            error_listeners: Vec::new(),
92        }
93    }
94
95    #[must_use]
96    pub fn with_rule_names(
97        mut self,
98        rule_names: impl IntoIterator<Item = impl Into<String>>,
99    ) -> Self {
100        Arc::make_mut(&mut self.metadata).rule_names =
101            rule_names.into_iter().map(Into::into).collect();
102        self
103    }
104
105    #[must_use]
106    pub fn with_channel_names(
107        mut self,
108        channel_names: impl IntoIterator<Item = impl Into<String>>,
109    ) -> Self {
110        Arc::make_mut(&mut self.metadata).channel_names =
111            channel_names.into_iter().map(Into::into).collect();
112        self
113    }
114
115    #[must_use]
116    pub fn with_mode_names(
117        mut self,
118        mode_names: impl IntoIterator<Item = impl Into<String>>,
119    ) -> Self {
120        Arc::make_mut(&mut self.metadata).mode_names =
121            mode_names.into_iter().map(Into::into).collect();
122        self
123    }
124
125    /// Rule names owned by this recognizer's metadata.
126    ///
127    /// Also available through [`Recognizer::rule_names`]; this inherent
128    /// accessor lets callers that already hold a `RecognizerData` field
129    /// borrow rule names without borrowing the whole recognizer.
130    #[must_use]
131    pub fn rule_names(&self) -> &[String] {
132        &self.metadata.rule_names
133    }
134
135    /// The token vocabulary for literal/symbolic name resolution.
136    #[must_use]
137    pub fn vocabulary(&self) -> &Vocabulary {
138        &self.metadata.vocabulary
139    }
140
141    pub const fn state(&self) -> isize {
142        self.state
143    }
144
145    pub const fn set_state(&mut self, state: isize) {
146        self.state = state;
147    }
148
149    fn add_error_listener<L>(&mut self, listener: L)
150    where
151        L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
152    {
153        self.error_listeners.push(ErrorListenerSlot::new(listener));
154    }
155
156    fn remove_error_listeners(&mut self) {
157        self.console_error_listener = false;
158        self.error_listeners.clear();
159    }
160
161    fn notify_error_listeners(&self, recognizer: &dyn Recognizer, event: &SyntaxErrorEvent<'_>) {
162        if self.console_error_listener {
163            ConsoleErrorListener.syntax_error(recognizer, event);
164        }
165        for listener in &self.error_listeners {
166            listener.syntax_error(recognizer, event);
167        }
168    }
169}
170
171pub trait Recognizer {
172    fn data(&self) -> &RecognizerData;
173    fn data_mut(&mut self) -> &mut RecognizerData;
174
175    fn grammar_file_name(&self) -> &str {
176        &self.data().metadata.grammar_file_name
177    }
178
179    fn rule_names(&self) -> &[String] {
180        &self.data().metadata.rule_names
181    }
182
183    fn channel_names(&self) -> &[String] {
184        &self.data().metadata.channel_names
185    }
186
187    fn mode_names(&self) -> &[String] {
188        &self.data().metadata.mode_names
189    }
190
191    fn vocabulary(&self) -> &Vocabulary {
192        &self.data().metadata.vocabulary
193    }
194
195    fn state(&self) -> isize {
196        self.data().state()
197    }
198
199    fn set_state(&mut self, state: isize) {
200        self.data_mut().set_state(state);
201    }
202
203    /// Adds a listener for syntax and prediction diagnostics.
204    ///
205    /// Recognizers start with one [`ConsoleErrorListener`]. Call
206    /// [`Self::remove_error_listeners`] before adding a replacement when
207    /// diagnostics should not also be written to stderr.
208    fn add_error_listener<L>(&mut self, listener: L)
209    where
210        Self: Sized,
211        L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
212    {
213        self.data_mut().add_error_listener(listener);
214    }
215
216    /// Removes every error listener, including the default console listener.
217    fn remove_error_listeners(&mut self) {
218        self.data_mut().remove_error_listeners();
219    }
220
221    /// Sends one diagnostic to every registered error listener.
222    fn notify_error_listeners(&self, event: SyntaxErrorEvent<'_>)
223    where
224        Self: Sized,
225    {
226        self.data().notify_error_listeners(self, &event);
227    }
228
229    fn sempred(&mut self, _rule_index: usize, _pred_index: usize) -> bool {
230        true
231    }
232
233    fn action(&mut self, _rule_index: usize, _action_index: usize) {}
234}
235
236#[cfg(test)]
237#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
238mod tests {
239    use std::mem::size_of;
240
241    use super::*;
242    use crate::errors::AntlrError;
243    use crate::generated::GrammarMetadata;
244
245    static SHARED_METADATA: GrammarMetadata = GrammarMetadata::new(
246        "Shared.g4",
247        &["start"],
248        &[None, Some("'x'")],
249        &[None, Some("X")],
250        &[None, None],
251        &["DEFAULT_TOKEN_CHANNEL", "HIDDEN"],
252        &["DEFAULT_MODE"],
253        &[],
254    );
255
256    #[derive(Clone, Debug, Eq, PartialEq)]
257    struct RecordedError {
258        grammar_file_name: String,
259        offending_text: Option<String>,
260        line: usize,
261        column: usize,
262        span: Option<std::ops::Range<usize>>,
263        message: String,
264        error: Option<AntlrError>,
265    }
266
267    #[derive(Clone, Debug)]
268    struct RecordingErrorListener {
269        errors: Arc<Mutex<Vec<RecordedError>>>,
270    }
271
272    impl<R> ErrorListener<R> for RecordingErrorListener
273    where
274        R: Recognizer + ?Sized,
275    {
276        fn syntax_error(&mut self, recognizer: &R, event: &SyntaxErrorEvent<'_>) {
277            self.errors
278                .lock()
279                .expect("recorded errors lock")
280                .push(RecordedError {
281                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
282                    offending_text: event
283                        .offending
284                        .and_then(|token| token.text().map(str::to_owned)),
285                    line: event.line,
286                    column: event.column,
287                    span: event.span.clone(),
288                    message: event.message.to_owned(),
289                    error: event.error.cloned(),
290                });
291        }
292    }
293
294    #[derive(Clone, Debug)]
295    struct TestRecognizer {
296        data: RecognizerData,
297    }
298
299    impl Recognizer for TestRecognizer {
300        fn data(&self) -> &RecognizerData {
301            &self.data
302        }
303
304        fn data_mut(&mut self) -> &mut RecognizerData {
305            &mut self.data
306        }
307    }
308
309    fn test_recognizer() -> TestRecognizer {
310        TestRecognizer {
311            data: RecognizerData::new(
312                "Test.g4",
313                Vocabulary::new(
314                    std::iter::empty::<Option<&str>>(),
315                    std::iter::empty::<Option<&str>>(),
316                    std::iter::empty::<Option<&str>>(),
317                ),
318            ),
319        }
320    }
321
322    #[test]
323    fn recognizers_replace_the_default_console_error_listener() {
324        let mut recognizer = test_recognizer();
325        assert!(recognizer.data.console_error_listener);
326        assert!(recognizer.data.error_listeners.is_empty());
327
328        recognizer.remove_error_listeners();
329        assert!(!recognizer.data.console_error_listener);
330        assert!(recognizer.data.error_listeners.is_empty());
331
332        let errors = Arc::new(Mutex::new(Vec::new()));
333        recognizer.add_error_listener(RecordingErrorListener {
334            errors: Arc::clone(&errors),
335        });
336        let error = AntlrError::ParserError {
337            line: 3,
338            column: 5,
339            message: "unexpected token".to_owned(),
340            offending: None,
341        };
342        recognizer.notify_error_listeners(SyntaxErrorEvent {
343            offending: None,
344            line: 3,
345            column: 5,
346            span: Some(17..27),
347            message: "unexpected token",
348            error: Some(&error),
349        });
350
351        insta::assert_debug_snapshot!(
352            "recognizers_replace_the_default_console_error_listener",
353            *errors.lock().expect("recorded errors lock")
354        );
355    }
356
357    #[test]
358    fn recognizer_data_remains_send_and_sync() {
359        fn assert_send_and_sync<T: Send + Sync>() {}
360
361        assert_send_and_sync::<RecognizerData>();
362    }
363
364    #[test]
365    fn recognizer_data_keeps_shared_metadata_out_of_line() {
366        assert!(size_of::<RecognizerData>() < size_of::<RecognizerMetadata>());
367    }
368
369    #[test]
370    fn cloned_recognizers_can_reconfigure_their_listener_lists_independently() {
371        let mut original = test_recognizer();
372        let clone = original.clone();
373
374        original.remove_error_listeners();
375
376        assert!(!original.data.console_error_listener);
377        assert!(original.data.error_listeners.is_empty());
378        assert!(clone.data.console_error_listener);
379        assert!(clone.data.error_listeners.is_empty());
380    }
381
382    #[test]
383    fn generated_recognizers_share_metadata_but_not_instance_state() {
384        let mut first = SHARED_METADATA.recognizer_data();
385        let second = SHARED_METADATA.recognizer_data();
386
387        assert!(std::ptr::eq(first.rule_names(), second.rule_names()));
388        assert!(std::ptr::eq(first.vocabulary(), second.vocabulary()));
389        assert!(first.console_error_listener);
390        assert!(second.console_error_listener);
391
392        first.set_state(7);
393        first.remove_error_listeners();
394
395        assert_eq!(first.state(), 7);
396        assert_eq!(second.state(), -1);
397        assert!(!first.console_error_listener);
398        assert!(first.error_listeners.is_empty());
399        assert!(second.console_error_listener);
400        assert!(second.error_listeners.is_empty());
401    }
402
403    #[test]
404    fn customizing_shared_metadata_detaches_only_that_recognizer() {
405        let customized = SHARED_METADATA
406            .recognizer_data()
407            .with_rule_names(["replacement"]);
408        let shared = SHARED_METADATA.recognizer_data();
409
410        assert_eq!(customized.rule_names(), ["replacement"]);
411        assert_eq!(shared.rule_names(), ["start"]);
412        assert!(!std::ptr::eq(customized.rule_names(), shared.rule_names()));
413    }
414}