1use std::cell::RefCell;
2use std::collections::{BTreeSet, HashMap};
3use std::hash::BuildHasherDefault;
4use std::rc::Rc;
5
6use crate::atn::Atn;
7use crate::char_stream::{CharStream, TextInterval};
8use crate::int_stream::EOF;
9use crate::prediction::PredictionFxHasher;
10use crate::recognizer::{Recognizer, RecognizerData};
11use crate::token::{CommonToken, CommonTokenFactory, TokenFactory, TokenSourceError, TokenSpec};
12
13#[allow(clippy::disallowed_types)]
14type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PredictionFxHasher>>;
15
16pub const SKIP: i32 = -3;
17pub const MORE: i32 = -2;
18pub const DEFAULT_MODE: i32 = 0;
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub struct LexerMode(pub i32);
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub struct LexerCustomAction {
31 rule_index: i32,
32 action_index: i32,
33 position: usize,
34}
35
36impl LexerCustomAction {
37 pub const fn new(rule_index: i32, action_index: i32, position: usize) -> Self {
39 Self {
40 rule_index,
41 action_index,
42 position,
43 }
44 }
45
46 pub const fn rule_index(self) -> i32 {
48 self.rule_index
49 }
50
51 pub const fn action_index(self) -> i32 {
53 self.action_index
54 }
55
56 pub const fn position(self) -> usize {
58 self.position
59 }
60}
61
62#[derive(Clone, Copy, Debug, Eq, PartialEq)]
64pub struct LexerPredicate {
65 rule_index: usize,
66 pred_index: usize,
67 position: usize,
68}
69
70impl LexerPredicate {
71 pub const fn new(rule_index: usize, pred_index: usize, position: usize) -> Self {
73 Self {
74 rule_index,
75 pred_index,
76 position,
77 }
78 }
79
80 pub const fn rule_index(self) -> usize {
82 self.rule_index
83 }
84
85 pub const fn pred_index(self) -> usize {
87 self.pred_index
88 }
89
90 pub const fn position(self) -> usize {
92 self.position
93 }
94}
95
96#[derive(Debug)]
101enum LexerRef<'a, I, F>
102where
103 I: CharStream,
104 F: TokenFactory,
105{
106 Shared(&'a BaseLexer<I, F>),
107 Mut(&'a mut BaseLexer<I, F>),
108}
109
110impl<I, F> LexerRef<'_, I, F>
111where
112 I: CharStream,
113 F: TokenFactory,
114{
115 const fn get(&self) -> &BaseLexer<I, F> {
116 match self {
117 LexerRef::Shared(lexer) => lexer,
118 LexerRef::Mut(lexer) => lexer,
119 }
120 }
121}
122
123#[derive(Debug)]
125pub struct LexerSemCtx<'a, I, F = CommonTokenFactory>
126where
127 I: CharStream,
128 F: TokenFactory,
129{
130 lexer: LexerRef<'a, I, F>,
131 rule_index: usize,
132 coordinate_index: usize,
133 position: usize,
134}
135
136impl<'a, I, F> LexerSemCtx<'a, I, F>
137where
138 I: CharStream,
139 F: TokenFactory,
140{
141 pub(crate) const fn new(
142 lexer: &'a BaseLexer<I, F>,
143 rule_index: usize,
144 coordinate_index: usize,
145 position: usize,
146 ) -> Self {
147 Self {
148 lexer: LexerRef::Shared(lexer),
149 rule_index,
150 coordinate_index,
151 position,
152 }
153 }
154
155 pub(crate) const fn new_mut(
158 lexer: &'a mut BaseLexer<I, F>,
159 rule_index: usize,
160 coordinate_index: usize,
161 position: usize,
162 ) -> Self {
163 Self {
164 lexer: LexerRef::Mut(lexer),
165 rule_index,
166 coordinate_index,
167 position,
168 }
169 }
170
171 #[must_use]
173 pub const fn rule_index(&self) -> usize {
174 self.rule_index
175 }
176
177 #[must_use]
179 pub const fn coordinate_index(&self) -> usize {
180 self.coordinate_index
181 }
182
183 #[must_use]
185 pub const fn position(&self) -> usize {
186 self.position
187 }
188
189 #[must_use]
191 pub fn mode(&self) -> i32 {
192 self.lexer.get().mode()
193 }
194
195 #[must_use]
197 pub const fn column(&self) -> usize {
198 self.lexer.get().column()
199 }
200
201 #[must_use]
203 pub fn position_column(&self) -> usize {
204 self.lexer.get().column_at(self.position)
205 }
206
207 #[must_use]
209 pub const fn token_start_column(&self) -> usize {
210 self.lexer.get().token_start_column()
211 }
212
213 #[must_use]
215 pub fn text_so_far(&self) -> String {
216 self.lexer.get().token_text_until(self.position)
217 }
218
219 pub fn set_mode(&mut self, mode: i32) -> bool {
226 match &mut self.lexer {
227 LexerRef::Mut(lexer) => {
228 lexer.set_mode(mode);
229 true
230 }
231 LexerRef::Shared(_) => false,
232 }
233 }
234
235 pub fn push_mode(&mut self, mode: i32) -> bool {
238 match &mut self.lexer {
239 LexerRef::Mut(lexer) => {
240 lexer.push_mode(mode);
241 true
242 }
243 LexerRef::Shared(_) => false,
244 }
245 }
246
247 pub fn pop_mode(&mut self) -> Option<i32> {
251 match &mut self.lexer {
252 LexerRef::Mut(lexer) => lexer.pop_mode(),
253 LexerRef::Shared(_) => None,
254 }
255 }
256}
257
258pub trait Lexer: Recognizer {
259 fn mode(&self) -> i32;
260 fn set_mode(&mut self, mode: i32);
261 fn push_mode(&mut self, mode: i32);
262 fn pop_mode(&mut self) -> Option<i32>;
263}
264
265#[derive(Clone, Debug)]
266pub struct BaseLexer<I, F = CommonTokenFactory> {
267 input: I,
268 data: RecognizerData,
269 factory: F,
270 mode: i32,
271 mode_stack: Vec<i32>,
272 token_start: usize,
273 token_start_line: usize,
274 token_start_column: usize,
275 line: usize,
276 column: usize,
277 hit_eof: bool,
278 force_interpreted: bool,
279 errors: RefCell<Vec<TokenSourceError>>,
280 semantic_error_coordinates: RefCell<BTreeSet<(u8, usize, usize, usize)>>,
281 dfa_cache: Rc<RefCell<LexerDfaCache>>,
282}
283
284#[derive(Clone, Debug, Default)]
292struct LexerDfaCache {
293 state_numbers: FxHashMap<LexerDfaKey, usize>,
294 accept_predictions: FxHashMap<usize, i32>,
295 edges: BTreeSet<LexerDfaEdge>,
299 cached_states: Vec<Option<Rc<LexerDfaCachedState>>>,
301 dense_edges: Vec<Option<Box<DenseEdgeRow>>>,
306 sparse_edges: FxHashMap<(usize, i32), LexerDfaCachedTransition>,
308 mode_starts: FxHashMap<i32, usize>,
309}
310
311const DENSE_EDGE_SYMBOLS: usize = 128;
313
314type DenseEdgeRow = [LexerDfaCachedTransition; DENSE_EDGE_SYMBOLS];
315
316const EMPTY_DENSE_EDGE: LexerDfaCachedTransition = LexerDfaCachedTransition {
319 target_state: usize::MAX,
320 position_delta: 0,
321};
322
323thread_local! {
324 static SHARED_LEXER_DFA_CACHES: RefCell<HashMap<usize, Rc<RefCell<LexerDfaCache>>>> =
327 RefCell::new(HashMap::new());
328}
329
330#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
332pub(crate) struct LexerDfaKey {
333 configs: Vec<LexerDfaConfigKey>,
334}
335
336impl LexerDfaKey {
337 pub(crate) fn new(mut configs: Vec<LexerDfaConfigKey>) -> Self {
338 configs.sort_unstable();
339 Self { configs }
340 }
341}
342
343#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
345pub(crate) struct LexerDfaConfigKey {
346 pub(crate) state: usize,
347 pub(crate) alt_rule_index: Option<usize>,
348 pub(crate) consumed_eof: bool,
349 pub(crate) passed_non_greedy: bool,
350 pub(crate) stack: Vec<usize>,
351 pub(crate) actions: Vec<LexerDfaActionKey>,
352}
353
354#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
355pub(crate) struct LexerDfaActionKey {
356 pub(crate) action_index: usize,
357 pub(crate) position_delta: usize,
358 pub(crate) rule_index: usize,
359}
360
361impl LexerDfaConfigKey {
362 pub(crate) const fn new(
363 state: usize,
364 alt_rule_index: Option<usize>,
365 consumed_eof: bool,
366 passed_non_greedy: bool,
367 stack: Vec<usize>,
368 actions: Vec<LexerDfaActionKey>,
369 ) -> Self {
370 Self {
371 state,
372 alt_rule_index,
373 consumed_eof,
374 passed_non_greedy,
375 stack,
376 actions,
377 }
378 }
379}
380
381#[derive(Clone, Copy, Debug)]
382pub(crate) struct LexerDfaCachedTransition {
383 pub(crate) target_state: usize,
384 pub(crate) position_delta: usize,
385}
386
387#[derive(Clone, Debug)]
388pub(crate) struct LexerDfaCachedAccept {
389 pub(crate) position_delta: usize,
390 pub(crate) rule_index: usize,
391 pub(crate) consumed_eof: bool,
392 pub(crate) actions: Vec<LexerDfaActionKey>,
393}
394
395#[derive(Clone, Debug)]
396pub(crate) struct LexerDfaCachedState {
397 pub(crate) has_semantic_context: bool,
398 pub(crate) configs: Vec<LexerDfaConfigKey>,
399 pub(crate) accept: Option<LexerDfaCachedAccept>,
400}
401
402#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
405struct LexerDfaEdge {
406 from: usize,
407 symbol: i32,
408 to: usize,
409}
410
411impl<I> BaseLexer<I>
412where
413 I: CharStream,
414{
415 pub fn new(input: I, data: RecognizerData) -> Self {
417 Self::with_factory(input, data, CommonTokenFactory)
418 }
419}
420
421impl<I, F> BaseLexer<I, F>
422where
423 I: CharStream,
424 F: TokenFactory,
425{
426 pub fn with_factory(input: I, data: RecognizerData, factory: F) -> Self {
428 Self {
429 input,
430 data,
431 factory,
432 mode: DEFAULT_MODE,
433 mode_stack: Vec::new(),
434 token_start: 0,
435 token_start_line: 1,
436 token_start_column: 0,
437 line: 1,
438 column: 0,
439 hit_eof: false,
440 force_interpreted: false,
441 errors: RefCell::new(Vec::new()),
442 semantic_error_coordinates: RefCell::new(BTreeSet::new()),
443 dfa_cache: Rc::new(RefCell::new(LexerDfaCache::default())),
444 }
445 }
446
447 #[must_use]
457 pub fn with_shared_dfa(mut self, atn: &'static Atn) -> Self {
458 let ptr: *const Atn = atn;
459 let key = ptr as usize;
460 self.dfa_cache = SHARED_LEXER_DFA_CACHES
461 .with(|caches| Rc::clone(caches.borrow_mut().entry(key).or_insert_with(Rc::default)));
462 self
463 }
464
465 pub const fn input(&self) -> &I {
466 &self.input
467 }
468
469 pub const fn input_mut(&mut self) -> &mut I {
470 &mut self.input
471 }
472
473 pub fn begin_token(&mut self) {
476 self.semantic_error_coordinates.get_mut().clear();
477 self.token_start = self.input.index();
478 self.token_start_line = self.line;
479 self.token_start_column = self.column;
480 }
481
482 pub const fn token_start(&self) -> usize {
484 self.token_start
485 }
486
487 pub const fn token_start_line(&self) -> usize {
489 self.token_start_line
490 }
491
492 pub const fn token_start_column(&self) -> usize {
494 self.token_start_column
495 }
496
497 pub fn consume_char(&mut self) {
504 let la = self.input.la(1);
505 if la == EOF {
506 return;
507 }
508 self.input.consume();
509 if char::from_u32(la.cast_unsigned()) == Some('\n') {
510 self.line += 1;
511 self.column = 0;
512 } else {
513 self.column += 1;
514 }
515 }
516
517 pub fn reset_accept_position(&mut self, index: usize) {
524 let target = index.max(self.token_start);
525 self.input.seek(self.token_start);
526 self.line = self.token_start_line;
527 self.column = self.token_start_column;
528 while self.input.index() < target && self.input.la(1) != EOF {
529 self.consume_char();
530 }
531 }
532
533 pub fn emit(&self, token_type: i32, channel: i32, text: Option<String>) -> CommonToken {
541 let stop = self.input.index().checked_sub(1).unwrap_or(usize::MAX);
542 self.emit_with_stop(token_type, channel, stop, text)
543 }
544
545 pub fn emit_with_stop(
551 &self,
552 token_type: i32,
553 channel: i32,
554 stop: usize,
555 text: Option<String>,
556 ) -> CommonToken {
557 let text = text.or_else(|| {
558 if stop == usize::MAX {
559 Some("<EOF>".to_owned())
560 } else {
561 None
562 }
563 });
564 let source_interval = if text.is_none() && stop != usize::MAX && self.token_start <= stop {
565 self.input
566 .text_source_interval(TextInterval::new(self.token_start, stop))
567 } else {
568 None
569 };
570 let source_text = source_interval
571 .as_ref()
572 .and_then(|(input, start_byte, stop_byte)| {
573 Some(crate::token::TokenSourceText {
574 input: Rc::clone(input),
575 start_byte: u32::try_from(*start_byte).ok()?,
576 stop_byte: u32::try_from(*stop_byte).ok()?,
577 })
578 });
579 let source_byte_span = source_text
580 .as_ref()
581 .map(|source_text| (source_text.start_byte, source_text.stop_byte));
582 let text = text.or_else(|| {
583 source_text
584 .is_none()
585 .then(|| self.input.text(TextInterval::new(self.token_start, stop)))
586 });
587 let mut token = self.factory.create(TokenSpec {
588 token_type,
589 channel,
590 start: self.token_start,
591 stop,
592 line: self.token_start_line,
593 column: self.token_start_column,
594 text,
595 source_text,
596 source_name: self.input.source_name(),
597 });
598 if let Some((start_byte, stop_byte)) =
599 source_byte_span.or_else(|| self.token_byte_span(stop))
600 {
601 token = token.with_byte_span(start_byte, stop_byte);
602 }
603 token
604 }
605
606 pub fn token_text(&self) -> String {
609 self.token_text_until(self.input.index())
610 }
611
612 pub fn token_text_until(&self, stop_exclusive: usize) -> String {
620 if stop_exclusive <= self.token_start {
621 return String::new();
622 }
623 self.input
624 .text(TextInterval::new(self.token_start, stop_exclusive - 1))
625 }
626
627 pub fn column_at(&self, position: usize) -> usize {
630 let mut column = self.token_start_column;
631 if position <= self.token_start {
632 return column;
633 }
634 for ch in self
635 .input
636 .text(TextInterval::new(self.token_start, position - 1))
637 .chars()
638 {
639 if ch == '\n' {
640 column = 0;
641 } else {
642 column += 1;
643 }
644 }
645 column
646 }
647
648 pub fn eof_token(&self) -> CommonToken {
650 let token = CommonToken::eof(
651 self.input.source_name(),
652 self.input.index(),
653 self.line,
654 self.column,
655 );
656 match self.eof_byte_offset() {
657 Some(byte_offset) => token.with_byte_span(byte_offset, byte_offset),
658 None => token,
659 }
660 }
661
662 fn eof_byte_offset(&self) -> Option<u32> {
663 self.byte_offset_at(self.input.index())
664 }
665
666 fn token_byte_span(&self, stop: usize) -> Option<(u32, u32)> {
667 if stop != usize::MAX && self.token_start <= stop {
668 let (_, start_byte, stop_byte) = self
669 .input
670 .text_source_interval(TextInterval::new(self.token_start, stop))?;
671 return Some((
672 u32::try_from(start_byte).ok()?,
673 u32::try_from(stop_byte).ok()?,
674 ));
675 }
676 let byte_offset = self.byte_offset_at(self.token_start)?;
677 Some((byte_offset, byte_offset))
678 }
679
680 fn byte_offset_at(&self, index: usize) -> Option<u32> {
681 let byte_offset = if index == 0 {
682 0
683 } else {
684 let previous = TextInterval::new(index - 1, index - 1);
685 self.input.text_source_interval(previous)?.2
686 };
687 u32::try_from(byte_offset).ok()
688 }
689}
690
691impl<I, F> Recognizer for BaseLexer<I, F>
692where
693 I: CharStream,
694 F: TokenFactory,
695{
696 fn data(&self) -> &RecognizerData {
697 &self.data
698 }
699
700 fn data_mut(&mut self) -> &mut RecognizerData {
701 &mut self.data
702 }
703}
704
705impl<I, F> Lexer for BaseLexer<I, F>
706where
707 I: CharStream,
708 F: TokenFactory,
709{
710 fn mode(&self) -> i32 {
711 self.mode
712 }
713
714 fn set_mode(&mut self, mode: i32) {
715 self.mode = mode;
716 }
717
718 fn push_mode(&mut self, mode: i32) {
719 self.mode_stack.push(self.mode);
720 self.mode = mode;
721 }
722
723 fn pop_mode(&mut self) -> Option<i32> {
724 let mode = self.mode_stack.pop()?;
725 self.mode = mode;
726 Some(mode)
727 }
728}
729
730impl<I, F> BaseLexer<I, F>
731where
732 I: CharStream,
733 F: TokenFactory,
734{
735 pub const fn line(&self) -> usize {
736 self.line
737 }
738
739 pub const fn column(&self) -> usize {
740 self.column
741 }
742
743 pub fn source_name(&self) -> &str {
744 self.input.source_name()
745 }
746
747 pub const fn hit_eof(&self) -> bool {
748 self.hit_eof
749 }
750
751 pub const fn set_hit_eof(&mut self, hit_eof: bool) {
752 self.hit_eof = hit_eof;
753 }
754
755 pub const fn set_force_interpreted(&mut self, force_interpreted: bool) {
763 self.force_interpreted = force_interpreted;
764 }
765
766 pub const fn force_interpreted(&self) -> bool {
768 self.force_interpreted
769 }
770
771 pub fn record_error(&self, line: usize, column: usize, message: impl Into<String>) {
774 self.errors
775 .borrow_mut()
776 .push(TokenSourceError::new(line, column, message));
777 }
778
779 pub fn record_semantic_error(
781 &self,
782 action: bool,
783 rule_index: usize,
784 coordinate_index: usize,
785 ) {
786 let kind = u8::from(action);
787 if !self.semantic_error_coordinates.borrow_mut().insert((
788 kind,
789 rule_index,
790 coordinate_index,
791 self.token_start,
792 )) {
793 return;
794 }
795 let label = if action { "action" } else { "predicate" };
796 self.record_error(
797 self.token_start_line,
798 self.token_start_column,
799 format!(
800 "unhandled lexer semantic {label}: rule={rule_index} index={coordinate_index}"
801 ),
802 );
803 }
804
805 pub fn drain_errors(&mut self) -> Vec<TokenSourceError> {
807 std::mem::take(self.errors.get_mut())
808 }
809
810 pub(crate) fn lexer_dfa_state(
813 &self,
814 key: LexerDfaKey,
815 accept_prediction: Option<i32>,
816 ) -> usize {
817 let mut cache = self.dfa_cache.borrow_mut();
818 let next = cache.state_numbers.len();
819 let state = *cache.state_numbers.entry(key).or_insert(next);
820 if let Some(prediction) = accept_prediction {
821 cache.accept_predictions.insert(state, prediction);
822 }
823 state
824 }
825
826 pub fn record_lexer_dfa_edge(&self, from: usize, symbol: i32, to: usize) {
828 self.dfa_cache
829 .borrow_mut()
830 .edges
831 .insert(LexerDfaEdge { from, symbol, to });
832 }
833
834 pub(crate) fn cached_lexer_dfa_transition(
835 &self,
836 state: usize,
837 symbol: i32,
838 ) -> Option<LexerDfaCachedTransition> {
839 let cache = self.dfa_cache.borrow();
840 if let Ok(sym) = usize::try_from(symbol)
841 && sym < DENSE_EDGE_SYMBOLS
842 {
843 let transition = cache.dense_edges.get(state)?.as_ref()?[sym];
844 return (transition.target_state != usize::MAX).then_some(transition);
845 }
846 cache.sparse_edges.get(&(state, symbol)).copied()
847 }
848
849 pub(crate) fn cache_lexer_dfa_transition(
850 &self,
851 state: usize,
852 symbol: i32,
853 transition: LexerDfaCachedTransition,
854 ) {
855 let mut cache = self.dfa_cache.borrow_mut();
856 if let Ok(sym) = usize::try_from(symbol)
857 && sym < DENSE_EDGE_SYMBOLS
858 {
859 if cache.dense_edges.len() <= state {
860 cache.dense_edges.resize_with(state + 1, || None);
861 }
862 let row = cache.dense_edges[state]
863 .get_or_insert_with(|| Box::new([EMPTY_DENSE_EDGE; DENSE_EDGE_SYMBOLS]));
864 if row[sym].target_state == usize::MAX {
866 row[sym] = transition;
867 }
868 return;
869 }
870 cache
871 .sparse_edges
872 .entry((state, symbol))
873 .or_insert(transition);
874 }
875
876 pub(crate) fn cached_lexer_dfa_state(&self, state: usize) -> Option<Rc<LexerDfaCachedState>> {
877 self.dfa_cache
878 .borrow()
879 .cached_states
880 .get(state)
881 .cloned()
882 .flatten()
883 }
884
885 pub(crate) fn cache_lexer_dfa_state(&self, state: usize, cached_state: LexerDfaCachedState) {
886 let mut cache = self.dfa_cache.borrow_mut();
887 if cache.cached_states.len() <= state {
888 cache.cached_states.resize_with(state + 1, || None);
889 }
890 cache.cached_states[state].get_or_insert_with(|| Rc::new(cached_state));
891 }
892
893 pub(crate) fn cached_lexer_mode_start(&self, mode: i32) -> Option<usize> {
894 self.dfa_cache.borrow().mode_starts.get(&mode).copied()
895 }
896
897 pub(crate) fn cache_lexer_mode_start(&self, mode: i32, state: usize) {
898 self.dfa_cache
899 .borrow_mut()
900 .mode_starts
901 .entry(mode)
902 .or_insert(state);
903 }
904
905 pub fn lexer_dfa_string(&self) -> String {
907 let mut out = String::new();
908 let cache = self.dfa_cache.borrow();
909 for edge in &cache.edges {
910 let Some(label) = lexer_dfa_edge_label(edge.symbol) else {
911 continue;
912 };
913 out.push_str(&self.lexer_dfa_state_string(edge.from));
914 out.push('-');
915 out.push_str(&label);
916 out.push_str("->");
917 out.push_str(&self.lexer_dfa_state_string(edge.to));
918 out.push('\n');
919 }
920 out
921 }
922
923 fn lexer_dfa_state_string(&self, state: usize) -> String {
924 self.dfa_cache
925 .borrow()
926 .accept_predictions
927 .get(&state)
928 .map_or_else(
929 || format!("s{state}"),
930 |prediction| format!(":s{state}=>{prediction}"),
931 )
932 }
933}
934
935fn lexer_dfa_edge_label(symbol: i32) -> Option<String> {
936 char::from_u32(symbol.cast_unsigned()).map(|ch| format!("'{ch}'"))
937}
938
939#[cfg(test)]
940mod tests {
941 use super::*;
942 use crate::char_stream::InputStream;
943 use crate::recognizer::RecognizerData;
944 use crate::token::{DEFAULT_CHANNEL, Token};
945 use crate::vocabulary::Vocabulary;
946
947 #[test]
948 fn eof_token_uses_utf8_byte_offset_after_non_ascii_input() {
949 let data = RecognizerData::new(
950 "T",
951 Vocabulary::new(
952 std::iter::empty::<Option<&str>>(),
953 std::iter::empty::<Option<&str>>(),
954 std::iter::empty::<Option<&str>>(),
955 ),
956 );
957 let mut lexer = BaseLexer::new(InputStream::new("β"), data);
958 lexer.consume_char();
959
960 let token = lexer.eof_token();
961
962 assert_eq!(token.start(), 1);
963 assert_eq!(token.stop(), 0);
964 assert_eq!(token.text(), "<EOF>");
965 assert_eq!(token.byte_span(), 2..2);
966 }
967
968 #[test]
969 fn eof_rule_token_uses_utf8_byte_offset_after_non_ascii_input() {
970 let data = RecognizerData::new(
971 "T",
972 Vocabulary::new(
973 std::iter::empty::<Option<&str>>(),
974 std::iter::empty::<Option<&str>>(),
975 std::iter::empty::<Option<&str>>(),
976 ),
977 );
978 let mut lexer = BaseLexer::new(InputStream::new("β"), data);
979 lexer.consume_char();
980 lexer.begin_token();
981
982 let token = lexer.emit_with_stop(1, DEFAULT_CHANNEL, 0, Some("<EOF>".to_owned()));
983
984 assert_eq!(token.start(), 1);
985 assert_eq!(token.stop(), 0);
986 assert_eq!(token.text(), "<EOF>");
987 assert_eq!(token.byte_span(), 2..2);
988 }
989
990 #[test]
991 fn emit_implicit_text_uses_utf8_byte_span_for_non_ascii_input() {
992 let data = RecognizerData::new(
993 "T",
994 Vocabulary::new(
995 std::iter::empty::<Option<&str>>(),
996 std::iter::empty::<Option<&str>>(),
997 std::iter::empty::<Option<&str>>(),
998 ),
999 );
1000 let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1001 lexer.begin_token();
1002 lexer.consume_char();
1003
1004 let token = lexer.emit(1, DEFAULT_CHANNEL, None);
1005
1006 assert_eq!(token.start(), 0);
1007 assert_eq!(token.stop(), 0);
1008 assert_eq!(token.text(), "β");
1009 assert_eq!(token.byte_span(), 0..2);
1010 }
1011
1012 #[test]
1013 fn semantic_hook_errors_are_deduplicated_per_token_coordinate() {
1014 let data = RecognizerData::new(
1015 "T",
1016 Vocabulary::new(
1017 std::iter::empty::<Option<&str>>(),
1018 std::iter::empty::<Option<&str>>(),
1019 std::iter::empty::<Option<&str>>(),
1020 ),
1021 );
1022 let mut lexer = BaseLexer::new(InputStream::new("a"), data);
1023 lexer.begin_token();
1024 lexer.record_semantic_error(false, 3, 7);
1025 lexer.record_semantic_error(false, 3, 7);
1026
1027 let errors = lexer.drain_errors();
1028 assert_eq!(errors.len(), 1);
1029 assert_eq!(
1030 errors[0].message,
1031 "unhandled lexer semantic predicate: rule=3 index=7"
1032 );
1033
1034 lexer.begin_token();
1035 lexer.record_semantic_error(false, 3, 7);
1036 assert_eq!(
1037 lexer.drain_errors().len(),
1038 1,
1039 "deduplication resets at every token boundary, even after rewinding"
1040 );
1041 }
1042}