Skip to main content

antlr4_runtime/
recognizer.rs

1use std::fmt;
2use std::sync::{Arc, Mutex};
3
4use crate::errors::{AntlrError, ConsoleErrorListener, ErrorListener};
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(
19        &self,
20        recognizer: &(dyn Recognizer + '_),
21        line: usize,
22        column: usize,
23        message: &str,
24        error: Option<&AntlrError>,
25    ) {
26        self.0
27            .lock()
28            .expect("error listener lock poisoned")
29            .syntax_error(recognizer, line, column, message, error);
30    }
31}
32
33impl fmt::Debug for ErrorListenerSlot {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        f.write_str("ErrorListener")
36    }
37}
38
39#[derive(Clone, Debug)]
40pub struct RecognizerData {
41    grammar_file_name: String,
42    rule_names: Vec<String>,
43    channel_names: Vec<String>,
44    mode_names: Vec<String>,
45    vocabulary: Vocabulary,
46    state: isize,
47    error_listeners: Vec<ErrorListenerSlot>,
48}
49
50impl RecognizerData {
51    pub fn new(grammar_file_name: impl Into<String>, vocabulary: Vocabulary) -> Self {
52        Self {
53            grammar_file_name: grammar_file_name.into(),
54            rule_names: Vec::new(),
55            channel_names: Vec::new(),
56            mode_names: Vec::new(),
57            vocabulary,
58            state: -1,
59            error_listeners: vec![ErrorListenerSlot::new(ConsoleErrorListener)],
60        }
61    }
62
63    #[must_use]
64    pub fn with_rule_names(
65        mut self,
66        rule_names: impl IntoIterator<Item = impl Into<String>>,
67    ) -> Self {
68        self.rule_names = rule_names.into_iter().map(Into::into).collect();
69        self
70    }
71
72    #[must_use]
73    pub fn with_channel_names(
74        mut self,
75        channel_names: impl IntoIterator<Item = impl Into<String>>,
76    ) -> Self {
77        self.channel_names = channel_names.into_iter().map(Into::into).collect();
78        self
79    }
80
81    #[must_use]
82    pub fn with_mode_names(
83        mut self,
84        mode_names: impl IntoIterator<Item = impl Into<String>>,
85    ) -> Self {
86        self.mode_names = mode_names.into_iter().map(Into::into).collect();
87        self
88    }
89
90    /// Rule names owned by this recognizer's metadata.
91    ///
92    /// Also available through [`Recognizer::rule_names`]; this inherent
93    /// accessor lets callers that already hold a `RecognizerData` field
94    /// borrow rule names without borrowing the whole recognizer.
95    #[must_use]
96    pub fn rule_names(&self) -> &[String] {
97        &self.rule_names
98    }
99
100    /// The token vocabulary for literal/symbolic name resolution.
101    #[must_use]
102    pub const fn vocabulary(&self) -> &Vocabulary {
103        &self.vocabulary
104    }
105
106    pub const fn state(&self) -> isize {
107        self.state
108    }
109
110    pub const fn set_state(&mut self, state: isize) {
111        self.state = state;
112    }
113
114    fn add_error_listener<L>(&mut self, listener: L)
115    where
116        L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
117    {
118        self.error_listeners.push(ErrorListenerSlot::new(listener));
119    }
120
121    fn remove_error_listeners(&mut self) {
122        self.error_listeners.clear();
123    }
124
125    fn notify_error_listeners(
126        &self,
127        recognizer: &dyn Recognizer,
128        line: usize,
129        column: usize,
130        message: &str,
131        error: Option<&AntlrError>,
132    ) {
133        for listener in &self.error_listeners {
134            listener.syntax_error(recognizer, line, column, message, error);
135        }
136    }
137}
138
139pub trait Recognizer {
140    fn data(&self) -> &RecognizerData;
141    fn data_mut(&mut self) -> &mut RecognizerData;
142
143    fn grammar_file_name(&self) -> &str {
144        &self.data().grammar_file_name
145    }
146
147    fn rule_names(&self) -> &[String] {
148        &self.data().rule_names
149    }
150
151    fn channel_names(&self) -> &[String] {
152        &self.data().channel_names
153    }
154
155    fn mode_names(&self) -> &[String] {
156        &self.data().mode_names
157    }
158
159    fn vocabulary(&self) -> &Vocabulary {
160        &self.data().vocabulary
161    }
162
163    fn state(&self) -> isize {
164        self.data().state()
165    }
166
167    fn set_state(&mut self, state: isize) {
168        self.data_mut().set_state(state);
169    }
170
171    /// Adds a listener for syntax and prediction diagnostics.
172    ///
173    /// Recognizers start with one [`ConsoleErrorListener`]. Call
174    /// [`Self::remove_error_listeners`] before adding a replacement when
175    /// diagnostics should not also be written to stderr.
176    fn add_error_listener<L>(&mut self, listener: L)
177    where
178        Self: Sized,
179        L: for<'a> ErrorListener<dyn Recognizer + 'a> + Send + 'static,
180    {
181        self.data_mut().add_error_listener(listener);
182    }
183
184    /// Removes every error listener, including the default console listener.
185    fn remove_error_listeners(&mut self) {
186        self.data_mut().remove_error_listeners();
187    }
188
189    /// Sends one diagnostic to every registered error listener.
190    fn notify_error_listeners(
191        &self,
192        line: usize,
193        column: usize,
194        message: &str,
195        error: Option<&AntlrError>,
196    ) where
197        Self: Sized,
198    {
199        self.data()
200            .notify_error_listeners(self, line, column, message, error);
201    }
202
203    fn sempred(&mut self, _rule_index: usize, _pred_index: usize) -> bool {
204        true
205    }
206
207    fn action(&mut self, _rule_index: usize, _action_index: usize) {}
208}
209
210#[cfg(test)]
211#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
212mod tests {
213    use super::*;
214
215    #[derive(Clone, Debug, Eq, PartialEq)]
216    struct RecordedError {
217        grammar_file_name: String,
218        line: usize,
219        column: usize,
220        message: String,
221        error: Option<AntlrError>,
222    }
223
224    #[derive(Clone, Debug)]
225    struct RecordingErrorListener {
226        errors: Arc<Mutex<Vec<RecordedError>>>,
227    }
228
229    impl<R> ErrorListener<R> for RecordingErrorListener
230    where
231        R: Recognizer + ?Sized,
232    {
233        fn syntax_error(
234            &mut self,
235            recognizer: &R,
236            line: usize,
237            column: usize,
238            message: &str,
239            error: Option<&AntlrError>,
240        ) {
241            self.errors
242                .lock()
243                .expect("recorded errors lock")
244                .push(RecordedError {
245                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
246                    line,
247                    column,
248                    message: message.to_owned(),
249                    error: error.cloned(),
250                });
251        }
252    }
253
254    #[derive(Clone, Debug)]
255    struct TestRecognizer {
256        data: RecognizerData,
257    }
258
259    impl Recognizer for TestRecognizer {
260        fn data(&self) -> &RecognizerData {
261            &self.data
262        }
263
264        fn data_mut(&mut self) -> &mut RecognizerData {
265            &mut self.data
266        }
267    }
268
269    fn test_recognizer() -> TestRecognizer {
270        TestRecognizer {
271            data: RecognizerData::new(
272                "Test.g4",
273                Vocabulary::new(
274                    std::iter::empty::<Option<&str>>(),
275                    std::iter::empty::<Option<&str>>(),
276                    std::iter::empty::<Option<&str>>(),
277                ),
278            ),
279        }
280    }
281
282    #[test]
283    fn recognizers_replace_the_default_console_error_listener() {
284        let mut recognizer = test_recognizer();
285        assert_eq!(recognizer.data.error_listeners.len(), 1);
286
287        recognizer.remove_error_listeners();
288        assert!(recognizer.data.error_listeners.is_empty());
289
290        let errors = Arc::new(Mutex::new(Vec::new()));
291        recognizer.add_error_listener(RecordingErrorListener {
292            errors: Arc::clone(&errors),
293        });
294        let error = AntlrError::ParserError {
295            line: 3,
296            column: 5,
297            message: "unexpected token".to_owned(),
298        };
299        recognizer.notify_error_listeners(3, 5, "unexpected token", Some(&error));
300
301        insta::assert_debug_snapshot!(
302            "recognizers_replace_the_default_console_error_listener",
303            *errors.lock().expect("recorded errors lock")
304        );
305    }
306
307    #[test]
308    fn recognizer_data_remains_send_and_sync() {
309        fn assert_send_and_sync<T: Send + Sync>() {}
310
311        assert_send_and_sync::<RecognizerData>();
312    }
313
314    #[test]
315    fn cloned_recognizers_can_reconfigure_their_listener_lists_independently() {
316        let mut original = test_recognizer();
317        let clone = original.clone();
318
319        original.remove_error_listeners();
320
321        assert!(original.data.error_listeners.is_empty());
322        assert_eq!(clone.data.error_listeners.len(), 1);
323    }
324}