use std::cell::RefCell;
use std::collections::{BTreeSet, HashSet};
use std::hash::BuildHasherDefault;
use crate::atn::lexer_dfa::{CompiledLexerAccept, CompiledLexerDfa, DEAD_STATE, ESCAPE_STATE};
use crate::atn::{Atn, AtnStateKind, LexerAction, Transition};
use crate::char_stream::{CharStream, TextInterval};
use crate::int_stream::EOF;
use crate::lexer::{
BaseLexer, Lexer, LexerCustomAction, LexerDfaActionKey, LexerDfaCachedAccept,
LexerDfaCachedState, LexerDfaCachedTransition, LexerDfaConfigKey, LexerDfaKey, LexerPredicate,
LexerSemCtx,
};
use crate::parser::SemanticHooks;
use crate::prediction::PredictionFxHasher;
use crate::token::{CommonToken, DEFAULT_CHANNEL, INVALID_TOKEN_TYPE, TokenFactory};
#[allow(clippy::disallowed_types)]
type FxHashSet<K> = HashSet<K, BuildHasherDefault<PredictionFxHasher>>;
const MIN_CHAR_VALUE: i32 = 0;
const MAX_CHAR_VALUE: i32 = 0x0010_FFFF;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(super) struct LexerConfig {
pub(super) state: usize,
pub(super) position: usize,
pub(super) consumed_eof: bool,
pub(super) alt_rule_index: Option<usize>,
pub(super) passed_non_greedy: bool,
pub(super) stack: Vec<usize>,
pub(super) actions: Vec<LexerActionTrace>,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(super) struct LexerActionTrace {
pub(super) action_index: usize,
pub(super) position: usize,
pub(super) rule_index: usize,
}
#[derive(Clone, Debug)]
pub(super) struct AcceptState {
pub(super) position: usize,
pub(super) rule_index: usize,
pub(super) consumed_eof: bool,
pub(super) actions: Vec<LexerActionTrace>,
}
#[derive(Clone, Debug)]
enum MatchResult {
Accept(AcceptState),
NoViableAlt { stop: usize },
}
#[derive(Clone, Debug)]
pub(super) struct ClosureResult {
pub(super) configs: Vec<LexerConfig>,
pub(super) has_semantic_context: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct LexerActionResult {
token_type: i32,
channel: i32,
skip: bool,
more: bool,
}
impl LexerActionResult {
const fn new(token_type: i32, channel: i32) -> Self {
Self {
token_type,
channel,
skip: false,
more: false,
}
}
fn apply<I, F>(&mut self, action: &LexerAction, lexer: &mut BaseLexer<I, F>)
where
I: CharStream,
F: TokenFactory,
{
match action {
LexerAction::Channel(channel) => self.channel = *channel,
LexerAction::Custom { .. } => {}
LexerAction::Mode(mode) => lexer.set_mode(*mode),
LexerAction::More => self.more = true,
LexerAction::PopMode => {
lexer.pop_mode();
}
LexerAction::PushMode(mode) => lexer.push_mode(*mode),
LexerAction::Skip => self.skip = true,
LexerAction::Type(token_type) => self.token_type = *token_type,
}
}
}
struct ClosureState {
seen: FxHashSet<LexerConfig>,
closed: Vec<LexerConfig>,
has_semantic_context: bool,
}
pub fn next_token<I, F>(lexer: &mut BaseLexer<I, F>, atn: &Atn) -> CommonToken
where
I: CharStream,
F: TokenFactory,
{
next_token_with_cache(lexer, atn, |_, _| {}, |_, _| true, |_, _, _| {})
}
pub fn next_token_with_actions<I, F, A>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
custom_action: A,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
A: FnMut(&mut BaseLexer<I, F>, LexerCustomAction),
{
next_token_with_hooks(lexer, atn, custom_action, |_, _| true, |_, _, _| {})
}
pub fn next_token_with_accept_adjuster<I, F, E>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
accept_adjuster: E,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
E: FnMut(&mut BaseLexer<I, F>, i32, usize),
{
next_token_with_hooks(lexer, atn, |_, _| {}, |_, _| true, accept_adjuster)
}
pub fn next_token_with_actions_and_predicates<I, F, A, P>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
mut custom_action: A,
mut semantic_predicate: P,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
A: FnMut(&mut BaseLexer<I, F>, LexerCustomAction),
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
{
next_token_with_hooks(
lexer,
atn,
&mut custom_action,
&mut semantic_predicate,
|_, _, _| {},
)
}
pub fn next_token_with_hooks<I, F, A, P, E>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
mut custom_action: A,
mut semantic_predicate: P,
mut accept_adjuster: E,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
A: FnMut(&mut BaseLexer<I, F>, LexerCustomAction),
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
E: FnMut(&mut BaseLexer<I, F>, i32, usize),
{
next_token_with_hooks_impl(
lexer,
atn,
&mut custom_action,
&mut semantic_predicate,
&mut accept_adjuster,
LexerMatchStrategy {
compiled: None,
use_cache: false,
},
)
}
fn dispatch_lexer_action_hook<I, F, H>(
hooks: &RefCell<&mut H>,
lexer: &mut BaseLexer<I, F>,
action: LexerCustomAction,
) where
I: CharStream,
F: TokenFactory,
H: SemanticHooks,
{
let Ok(rule_index) = usize::try_from(action.rule_index()) else {
return;
};
let Ok(action_index) = usize::try_from(action.action_index()) else {
return;
};
let mut ctx = LexerSemCtx::new_mut(lexer, rule_index, action_index, action.position());
let _ = hooks.borrow_mut().lexer_action(&mut ctx, action);
}
fn dispatch_lexer_predicate_hook<I, F, H>(
hooks: &RefCell<&mut H>,
lexer: &BaseLexer<I, F>,
predicate: LexerPredicate,
) -> bool
where
I: CharStream,
F: TokenFactory,
H: SemanticHooks,
{
let mut ctx = LexerSemCtx::new(
lexer,
predicate.rule_index(),
predicate.pred_index(),
predicate.position(),
);
hooks
.borrow_mut()
.lexer_sempred(&mut ctx, predicate.rule_index(), predicate.pred_index())
.unwrap_or(true)
}
pub fn next_token_with_semantic_hooks<I, F, H>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
hooks: &mut H,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
H: SemanticHooks,
{
let hooks = RefCell::new(hooks);
next_token_with_hooks(
lexer,
atn,
|lexer, action| dispatch_lexer_action_hook(&hooks, lexer, action),
|lexer, predicate| dispatch_lexer_predicate_hook(&hooks, lexer, predicate),
|_, _, _| {},
)
}
pub fn next_token_compiled<I, F>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
dfa: &CompiledLexerDfa,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
{
next_token_with_hooks_impl(
lexer,
atn,
&mut |_, _| {},
&mut |_, _| true,
&mut |_, _, _| {},
LexerMatchStrategy {
compiled: Some(dfa),
use_cache: true,
},
)
}
pub fn next_token_compiled_with_hooks<I, F, A, P, E>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
dfa: &CompiledLexerDfa,
mut custom_action: A,
mut semantic_predicate: P,
mut accept_adjuster: E,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
A: FnMut(&mut BaseLexer<I, F>, LexerCustomAction),
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
E: FnMut(&mut BaseLexer<I, F>, i32, usize),
{
next_token_with_hooks_impl(
lexer,
atn,
&mut custom_action,
&mut semantic_predicate,
&mut accept_adjuster,
LexerMatchStrategy {
compiled: Some(dfa),
use_cache: false,
},
)
}
pub fn next_token_compiled_with_semantic_hooks<I, F, H>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
dfa: &CompiledLexerDfa,
hooks: &mut H,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
H: SemanticHooks,
{
let hooks = RefCell::new(hooks);
next_token_compiled_with_hooks(
lexer,
atn,
dfa,
|lexer, action| dispatch_lexer_action_hook(&hooks, lexer, action),
|lexer, predicate| dispatch_lexer_predicate_hook(&hooks, lexer, predicate),
|_, _, _| {},
)
}
fn next_token_with_cache<I, F, A, P, E>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
mut custom_action: A,
mut semantic_predicate: P,
mut accept_adjuster: E,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
A: FnMut(&mut BaseLexer<I, F>, LexerCustomAction),
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
E: FnMut(&mut BaseLexer<I, F>, i32, usize),
{
next_token_with_hooks_impl(
lexer,
atn,
&mut custom_action,
&mut semantic_predicate,
&mut accept_adjuster,
LexerMatchStrategy {
compiled: None,
use_cache: true,
},
)
}
#[derive(Clone, Copy)]
struct LexerMatchStrategy<'a> {
compiled: Option<&'a CompiledLexerDfa>,
use_cache: bool,
}
fn match_token_with_strategy<I, F, P>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
mode: i32,
start: usize,
semantic_predicate: &mut P,
strategy: LexerMatchStrategy<'_>,
) -> MatchResult
where
I: CharStream,
F: TokenFactory,
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
{
if let Some(dfa) = strategy.compiled
&& !lexer.force_interpreted()
&& let Some(start_state) = dfa.mode_start(mode)
&& let Some(result) = match_token_compiled(lexer, dfa, start_state, start)
{
return result;
}
if strategy.use_cache {
match_token_cached(lexer, atn, mode, start, semantic_predicate)
} else {
match_token(lexer, atn, mode, start, semantic_predicate)
}
}
fn next_token_with_hooks_impl<I, F, A, P, E>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
custom_action: &mut A,
semantic_predicate: &mut P,
accept_adjuster: &mut E,
strategy: LexerMatchStrategy<'_>,
) -> CommonToken
where
I: CharStream,
F: TokenFactory,
A: FnMut(&mut BaseLexer<I, F>, LexerCustomAction),
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
E: FnMut(&mut BaseLexer<I, F>, i32, usize),
{
let mut continuing_more = false;
loop {
if lexer.hit_eof() {
return lexer.eof_token();
}
if !continuing_more {
lexer.begin_token();
}
let mode = lexer.mode();
let start = lexer.input().index();
let token_match =
match_token_with_strategy(lexer, atn, mode, start, semantic_predicate, strategy);
let accept = match token_match {
MatchResult::Accept(accept) => accept,
MatchResult::NoViableAlt { stop } => {
lexer.input_mut().seek(start);
if lexer.input_mut().la(1) == EOF {
lexer.set_hit_eof(true);
return lexer.eof_token();
}
record_token_recognition_error(lexer, start, stop);
while lexer.input().index() < stop {
lexer.consume_char();
}
continuing_more = false;
continue;
}
};
lexer.input_mut().seek(start);
while lexer.input().index() < accept.position {
lexer.consume_char();
}
if accept.consumed_eof {
lexer.set_hit_eof(true);
}
let token_type = atn
.rule_to_token_type()
.get(accept.rule_index)
.copied()
.unwrap_or(INVALID_TOKEN_TYPE);
let mut result = LexerActionResult::new(token_type, DEFAULT_CHANNEL);
for trace in accept.actions {
if !lexer_action_belongs_to_accept(atn, accept.rule_index, trace.rule_index) {
continue;
}
if let Some(action) = atn.lexer_actions().get(trace.action_index) {
match action {
LexerAction::Custom {
rule_index,
action_index,
} => {
custom_action(
lexer,
LexerCustomAction::new(*rule_index, *action_index, trace.position),
);
}
other => result.apply(other, lexer),
}
}
}
if result.skip {
continuing_more = false;
continue;
}
if result.more {
continuing_more = true;
continue;
}
accept_adjuster(lexer, result.token_type, accept.position);
let emit_position = lexer.input().index();
let stop = emit_position.checked_sub(1).unwrap_or(usize::MAX);
let text = if accept.consumed_eof && start == emit_position {
Some("<EOF>".to_owned())
} else {
None
};
return lexer.emit_with_stop(result.token_type, result.channel, stop, text);
}
}
pub(super) fn lexer_action_belongs_to_accept(
atn: &Atn,
accept_rule: usize,
action_rule: usize,
) -> bool {
action_rule == accept_rule
|| atn
.rule_to_token_type()
.get(action_rule)
.copied()
.unwrap_or(INVALID_TOKEN_TYPE)
== INVALID_TOKEN_TYPE
}
fn match_token<I, F, P>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
mode: i32,
start: usize,
semantic_predicate: &mut P,
) -> MatchResult
where
I: CharStream,
F: TokenFactory,
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
{
let Some(mode_index) = usize::try_from(mode).ok() else {
return MatchResult::NoViableAlt { stop: start };
};
let Some(start_state) = atn.mode_to_start_state().get(mode_index).copied() else {
return MatchResult::NoViableAlt { stop: start };
};
let start_closure = epsilon_closure(
atn,
[LexerConfig {
state: start_state,
position: start,
consumed_eof: false,
alt_rule_index: None,
passed_non_greedy: false,
stack: Vec::new(),
actions: Vec::new(),
}],
&mut |predicate| semantic_predicate(lexer, predicate),
);
let mut active = prune_after_accepts(atn, start_closure.configs);
let mut dfa_state = lexer.lexer_dfa_state(
lexer_dfa_key(&active, start),
accept_prediction(atn, &active),
);
let mut dfa_state_has_semantic_context = start_closure.has_semantic_context;
let mut best = best_accept(atn, &active);
let mut error_stop = start;
while !active.is_empty() {
let mut next = Vec::new();
let source_dfa_state = dfa_state;
let source_has_semantic_context = dfa_state_has_semantic_context;
let mut edge_symbol = None;
for config in active {
let symbol = symbol_at(lexer, config.position);
if symbol != EOF {
error_stop = error_stop.max(config.position.saturating_add(1));
edge_symbol = Some(symbol);
}
let Some(state) = atn.state(config.state) else {
continue;
};
for transition in &state.transitions {
if !transition.matches(symbol, MIN_CHAR_VALUE, MAX_CHAR_VALUE) {
continue;
}
let mut advanced = config.clone();
set_config_state(atn, &mut advanced, transition.target());
if symbol == EOF {
advanced.consumed_eof = true;
} else {
advanced.position += 1;
}
next.push(advanced);
}
}
let closure = epsilon_closure(atn, next, &mut |predicate| {
semantic_predicate(lexer, predicate)
});
let target_has_semantic_context = closure.has_semantic_context;
let suppress_edge = source_has_semantic_context || target_has_semantic_context;
active = prune_after_accepts(atn, closure.configs);
if !active.is_empty() {
dfa_state = lexer.lexer_dfa_state(
lexer_dfa_key(&active, start),
accept_prediction(atn, &active),
);
dfa_state_has_semantic_context = target_has_semantic_context;
if !suppress_edge {
if let Some(symbol) = edge_symbol {
lexer.record_lexer_dfa_edge(source_dfa_state, symbol, dfa_state);
}
}
}
if let Some(accept) = best_accept(atn, &active) {
if best.as_ref().is_none_or(|current| {
accept.position > current.position
|| (accept.position == current.position
&& accept.rule_index < current.rule_index)
}) {
best = Some(accept);
}
}
}
best.map_or(
MatchResult::NoViableAlt { stop: error_stop },
MatchResult::Accept,
)
}
fn match_token_cached<I, F, P>(
lexer: &mut BaseLexer<I, F>,
atn: &Atn,
mode: i32,
start: usize,
semantic_predicate: &mut P,
) -> MatchResult
where
I: CharStream,
F: TokenFactory,
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
{
let Some((mut dfa_state, mode_start_has_semantic_context)) =
cached_mode_start_state(lexer, atn, mode, start, semantic_predicate)
else {
return MatchResult::NoViableAlt { stop: start };
};
if mode_start_has_semantic_context {
return match_token(lexer, atn, mode, start, semantic_predicate);
}
let mut position = start;
let mut best = None;
let mut error_stop = start;
loop {
let Some(cached_state) = lexer.cached_lexer_dfa_state(dfa_state) else {
return match_token(lexer, atn, mode, start, semantic_predicate);
};
if cached_state.has_semantic_context {
return match_token(lexer, atn, mode, start, semantic_predicate);
}
if let Some(accept) = cached_state.accept.as_ref() {
let accept = cached_accept_state(accept, start, position);
if best.as_ref().is_none_or(|current: &AcceptState| {
accept.position > current.position
|| (accept.position == current.position
&& accept.rule_index < current.rule_index)
}) {
best = Some(accept);
}
}
let symbol = symbol_at(lexer, position);
if symbol != EOF {
error_stop = error_stop.max(position.saturating_add(1));
}
if !cached_state.has_semantic_context {
if let Some(cached) = lexer.cached_lexer_dfa_transition(dfa_state, symbol) {
dfa_state = cached.target_state;
position += cached.position_delta;
continue;
}
}
let source_dfa_state = dfa_state;
let source_has_semantic_context = cached_state.has_semantic_context;
let active = cached_configs_to_configs(&cached_state.configs, start, position);
let mut next = Vec::new();
for config in active {
let Some(state) = atn.state(config.state) else {
continue;
};
for transition in &state.transitions {
if !transition.matches(symbol, MIN_CHAR_VALUE, MAX_CHAR_VALUE) {
continue;
}
let mut advanced = config.clone();
set_config_state(atn, &mut advanced, transition.target());
if symbol == EOF {
advanced.consumed_eof = true;
} else {
advanced.position += 1;
}
next.push(advanced);
}
}
let closure = epsilon_closure(atn, next, &mut |predicate| {
semantic_predicate(lexer, predicate)
});
let target_has_semantic_context = closure.has_semantic_context;
if target_has_semantic_context {
return match_token(lexer, atn, mode, start, semantic_predicate);
}
let suppress_edge = source_has_semantic_context || target_has_semantic_context;
let active = prune_after_accepts(atn, closure.configs);
if active.is_empty() {
break;
}
let Some(target_position) = shared_config_position(&active) else {
return match_token(lexer, atn, mode, start, semantic_predicate);
};
dfa_state = cache_dfa_state(
lexer,
atn,
&active,
target_has_semantic_context,
start,
target_position,
);
if !suppress_edge && symbol != EOF {
lexer.record_lexer_dfa_edge(source_dfa_state, symbol, dfa_state);
lexer.cache_lexer_dfa_transition(
source_dfa_state,
symbol,
LexerDfaCachedTransition {
target_state: dfa_state,
position_delta: target_position.saturating_sub(position),
},
);
}
position = target_position;
}
best.map_or(
MatchResult::NoViableAlt { stop: error_stop },
MatchResult::Accept,
)
}
const MAX_COMPILED_EOF_EDGES: u32 = 8;
fn match_token_compiled<I, F>(
lexer: &mut BaseLexer<I, F>,
dfa: &CompiledLexerDfa,
start_state: u16,
start: usize,
) -> Option<MatchResult>
where
I: CharStream,
F: TokenFactory,
{
let mut state = start_state;
let mut position = start;
let mut best: Option<AcceptState> = None;
let mut error_stop = start;
let mut eof_edges = 0_u32;
loop {
if let Some(accept) = dfa.accept(state) {
record_compiled_accept(accept, position, &mut best);
}
let symbol = symbol_at(lexer, position);
let target = if symbol == EOF {
eof_edges += 1;
if eof_edges > MAX_COMPILED_EOF_EDGES {
return None;
}
dfa.eof_target(state)
} else {
error_stop = error_stop.max(position.saturating_add(1));
dfa.char_target(state, symbol)
};
if target == DEAD_STATE {
break;
}
if target == ESCAPE_STATE {
return None;
}
if symbol != EOF {
position += 1;
}
state = target;
}
Some(best.map_or(
MatchResult::NoViableAlt { stop: error_stop },
MatchResult::Accept,
))
}
fn record_compiled_accept(
accept: &CompiledLexerAccept,
position: usize,
best: &mut Option<AcceptState>,
) {
let replaces = best.as_ref().is_none_or(|current| {
position > current.position
|| (position == current.position && accept.rule_index < current.rule_index)
});
if !replaces {
return;
}
*best = Some(AcceptState {
position,
rule_index: accept.rule_index,
consumed_eof: accept.consumed_eof,
actions: accept
.actions
.iter()
.map(|trace| LexerActionTrace {
action_index: trace.action_index,
position: position.saturating_sub(trace.behind),
rule_index: trace.rule_index,
})
.collect(),
});
}
fn cached_mode_start_state<I, F, P>(
lexer: &BaseLexer<I, F>,
atn: &Atn,
mode: i32,
start: usize,
semantic_predicate: &mut P,
) -> Option<(usize, bool)>
where
I: CharStream,
F: TokenFactory,
P: FnMut(&BaseLexer<I, F>, LexerPredicate) -> bool,
{
if let Some(state) = lexer.cached_lexer_mode_start(mode) {
return Some((state, false));
}
let mode_index = usize::try_from(mode).ok()?;
let start_state = atn.mode_to_start_state().get(mode_index).copied()?;
let start_closure = epsilon_closure(
atn,
[LexerConfig {
state: start_state,
position: start,
consumed_eof: false,
alt_rule_index: None,
passed_non_greedy: false,
stack: Vec::new(),
actions: Vec::new(),
}],
&mut |predicate| semantic_predicate(lexer, predicate),
);
let active = prune_after_accepts(atn, start_closure.configs);
let state = cache_dfa_state(
lexer,
atn,
&active,
start_closure.has_semantic_context,
start,
start,
);
if !start_closure.has_semantic_context {
lexer.cache_lexer_mode_start(mode, state);
}
Some((state, start_closure.has_semantic_context))
}
fn cache_dfa_state<I, F>(
lexer: &BaseLexer<I, F>,
atn: &Atn,
active: &[LexerConfig],
has_semantic_context: bool,
token_start: usize,
position: usize,
) -> usize
where
I: CharStream,
F: TokenFactory,
{
let state = lexer.lexer_dfa_state(
lexer_dfa_key(active, token_start),
accept_prediction(atn, active),
);
if !has_semantic_context {
lexer.cache_lexer_dfa_state(
state,
LexerDfaCachedState {
has_semantic_context,
configs: active
.iter()
.map(|config| normalized_config_key(config, token_start))
.collect(),
accept: best_accept(atn, active).map(|accept| LexerDfaCachedAccept {
position_delta: accept.position.saturating_sub(position),
rule_index: accept.rule_index,
consumed_eof: accept.consumed_eof,
actions: accept
.actions
.iter()
.map(|action| LexerDfaActionKey {
action_index: action.action_index,
position_delta: action.position.saturating_sub(token_start),
rule_index: action.rule_index,
})
.collect(),
}),
},
);
}
state
}
fn cached_accept_state(
accept: &LexerDfaCachedAccept,
token_start: usize,
state_position: usize,
) -> AcceptState {
let position = state_position + accept.position_delta;
AcceptState {
position,
rule_index: accept.rule_index,
consumed_eof: accept.consumed_eof,
actions: accept
.actions
.iter()
.map(|action| LexerActionTrace {
action_index: action.action_index,
position: token_start + action.position_delta,
rule_index: action.rule_index,
})
.collect(),
}
}
pub(super) fn epsilon_closure<P>(
atn: &Atn,
configs: impl IntoIterator<Item = LexerConfig>,
semantic_predicate: &mut P,
) -> ClosureResult
where
P: FnMut(LexerPredicate) -> bool,
{
let mut state = ClosureState {
seen: FxHashSet::default(),
closed: Vec::new(),
has_semantic_context: false,
};
for config in configs {
close_config(atn, config, &mut state, semantic_predicate);
}
ClosureResult {
configs: state.closed,
has_semantic_context: state.has_semantic_context,
}
}
fn close_config<P>(
atn: &Atn,
config: LexerConfig,
closure: &mut ClosureState,
semantic_predicate: &mut P,
) where
P: FnMut(LexerPredicate) -> bool,
{
if !closure.seen.insert(config.clone()) {
return;
}
let Some(state) = atn.state(config.state) else {
return;
};
if state.kind == AtnStateKind::RuleStop {
if let Some((&follow_state, rest)) = config.stack.split_last() {
let mut returned = config.clone();
set_config_state(atn, &mut returned, follow_state);
returned.stack = rest.to_vec();
close_config(atn, returned, closure, semantic_predicate);
}
closure.closed.push(config);
return;
}
for transition in &state.transitions {
match transition {
Transition::Epsilon { target } => {
let mut next = config.clone();
set_config_state(atn, &mut next, *target);
next.passed_non_greedy |= state.non_greedy;
close_config(atn, next, closure, semantic_predicate);
}
Transition::Rule {
target,
follow_state,
..
} => {
let mut next = config.clone();
set_config_state(atn, &mut next, *target);
next.passed_non_greedy |= state.non_greedy;
next.stack.push(*follow_state);
close_config(atn, next, closure, semantic_predicate);
}
Transition::Predicate {
target,
rule_index,
pred_index,
..
} => {
closure.has_semantic_context = true;
if semantic_predicate(LexerPredicate::new(
*rule_index,
*pred_index,
config.position,
)) {
let mut next = config.clone();
set_config_state(atn, &mut next, *target);
next.passed_non_greedy |= state.non_greedy;
close_config(atn, next, closure, semantic_predicate);
}
}
Transition::Precedence { target, .. } => {
let mut next = config.clone();
set_config_state(atn, &mut next, *target);
next.passed_non_greedy |= state.non_greedy;
close_config(atn, next, closure, semantic_predicate);
}
Transition::Action {
target,
action_index,
rule_index,
..
} => {
let mut next = config.clone();
set_config_state(atn, &mut next, *target);
next.passed_non_greedy |= state.non_greedy;
if let Some(action_index) = action_index {
next.actions.push(LexerActionTrace {
action_index: *action_index,
position: config.position,
rule_index: *rule_index,
});
}
close_config(atn, next, closure, semantic_predicate);
}
Transition::Atom { .. }
| Transition::Range { .. }
| Transition::Set { .. }
| Transition::NotSet { .. }
| Transition::Wildcard { .. } => {}
}
}
if state
.transitions
.iter()
.any(|transition| !transition.is_epsilon())
{
closure.closed.push(config);
}
}
pub(super) fn prune_after_accepts(atn: &Atn, configs: Vec<LexerConfig>) -> Vec<LexerConfig> {
let mut accepted_rules = BTreeSet::new();
let mut pruned = Vec::with_capacity(configs.len());
for config in configs {
let Some(rule_index) = config.alt_rule_index else {
pruned.push(config);
continue;
};
if accepted_rules.contains(&rule_index) {
continue;
}
let is_top_level_accept = config.stack.is_empty()
&& atn
.state(config.state)
.is_some_and(crate::atn::AtnState::is_rule_stop);
if is_top_level_accept && config.passed_non_greedy {
accepted_rules.insert(rule_index);
}
pruned.push(config);
}
pruned
}
pub(super) fn best_accept(atn: &Atn, configs: &[LexerConfig]) -> Option<AcceptState> {
configs
.iter()
.filter_map(|config| {
let state = atn.state(config.state)?;
if !state.is_rule_stop() || !config.stack.is_empty() {
return None;
}
Some(AcceptState {
position: config.position,
rule_index: config.alt_rule_index.or(state.rule_index)?,
consumed_eof: config.consumed_eof,
actions: config.actions.clone(),
})
})
.min_by_key(|accept| accept.rule_index)
}
fn accept_prediction(atn: &Atn, configs: &[LexerConfig]) -> Option<i32> {
best_accept(atn, configs)
.and_then(|accept| atn.rule_to_token_type().get(accept.rule_index).copied())
}
fn lexer_dfa_key(configs: &[LexerConfig], token_start: usize) -> LexerDfaKey {
LexerDfaKey::new(
configs
.iter()
.map(|config| normalized_config_key(config, token_start))
.collect::<Vec<_>>(),
)
}
fn normalized_config_key(config: &LexerConfig, token_start: usize) -> LexerDfaConfigKey {
LexerDfaConfigKey::new(
config.state,
config.alt_rule_index,
config.consumed_eof,
config.passed_non_greedy,
config.stack.clone(),
config
.actions
.iter()
.map(|action| {
debug_assert!(
action.position >= token_start,
"lexer DFA action position must be relative to the current token"
);
LexerDfaActionKey {
action_index: action.action_index,
position_delta: action.position.saturating_sub(token_start),
rule_index: action.rule_index,
}
})
.collect(),
)
}
fn shared_config_position(configs: &[LexerConfig]) -> Option<usize> {
let position = configs.first()?.position;
configs
.iter()
.all(|config| config.position == position)
.then_some(position)
}
fn cached_configs_to_configs(
configs: &[LexerDfaConfigKey],
token_start: usize,
position: usize,
) -> Vec<LexerConfig> {
configs
.iter()
.map(|config| LexerConfig {
state: config.state,
position,
consumed_eof: config.consumed_eof,
alt_rule_index: config.alt_rule_index,
passed_non_greedy: config.passed_non_greedy,
stack: config.stack.clone(),
actions: config
.actions
.iter()
.map(|action| LexerActionTrace {
action_index: action.action_index,
position: token_start + action.position_delta,
rule_index: action.rule_index,
})
.collect(),
})
.collect()
}
pub(super) fn set_config_state(atn: &Atn, config: &mut LexerConfig, state_number: usize) {
config.state = state_number;
if config.alt_rule_index.is_none() {
config.alt_rule_index = atn.state(state_number).and_then(|state| state.rule_index);
}
}
fn record_token_recognition_error<I, F>(lexer: &mut BaseLexer<I, F>, start: usize, stop: usize)
where
I: CharStream,
F: TokenFactory,
{
let stop = stop.saturating_sub(1);
let text = display_error_text(&lexer.input().text(TextInterval::new(start, stop)));
lexer.record_error(
lexer.line(),
lexer.column(),
format!("token recognition error at: '{text}'"),
);
}
fn display_error_text(text: &str) -> String {
let mut out = String::new();
for ch in text.chars() {
match ch {
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
other => out.push(other),
}
}
out
}
fn symbol_at<I, F>(lexer: &mut BaseLexer<I, F>, position: usize) -> i32
where
I: CharStream,
F: TokenFactory,
{
lexer.input_mut().seek(position);
lexer.input_mut().la(1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::atn::AtnType;
use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
use crate::char_stream::InputStream;
use crate::recognizer::RecognizerData;
use crate::token::{TOKEN_EOF, Token};
use crate::vocabulary::Vocabulary;
#[test]
fn predicate_sensitive_lexer_state_is_not_replay_cached() {
let atn = Atn::new(AtnType::Lexer, 1);
let data = RecognizerData::new(
"T",
Vocabulary::new([None, Some("T")], [None, Some("T")], [None::<&str>, None]),
);
let lexer = BaseLexer::new(InputStream::new(""), data);
let predicate_state = cache_dfa_state(&lexer, &atn, &[], true, 0, 0);
assert!(lexer.cached_lexer_dfa_state(predicate_state).is_none());
let plain_state = cache_dfa_state(&lexer, &atn, &[], false, 0, 0);
assert_eq!(predicate_state, plain_state);
assert!(lexer.cached_lexer_dfa_state(plain_state).is_some());
}
#[test]
fn lexer_action_hook_context_can_change_mode() {
let data = RecognizerData::new(
"T",
Vocabulary::new([None, Some("T")], [None, Some("T")], [None::<&str>, None]),
);
let mut lexer = BaseLexer::new(InputStream::new(""), data);
{
let mut ctx = LexerSemCtx::new_mut(&mut lexer, 0, 0, 0);
assert_eq!(ctx.mode(), 0);
assert!(ctx.push_mode(2), "action context applies push_mode");
assert_eq!(ctx.mode(), 2);
assert!(ctx.set_mode(3), "action context applies set_mode");
assert_eq!(ctx.mode(), 3);
assert_eq!(ctx.pop_mode(), Some(0), "pop restores the pushed-from mode");
assert_eq!(ctx.mode(), 0);
}
assert_eq!(lexer.mode(), 0, "mutations went through to the lexer");
{
let mut ctx = LexerSemCtx::new(&lexer, 0, 0, 0);
assert!(!ctx.push_mode(2), "predicate context does not mutate");
assert!(!ctx.set_mode(3), "predicate context does not mutate");
assert_eq!(ctx.pop_mode(), None, "predicate context does not mutate");
}
assert_eq!(lexer.mode(), 0, "shared-context calls left the lexer unchanged");
}
#[test]
fn lexer_matches_longest_token_and_skips() {
let atn = AtnDeserializer::new(&SerializedAtn::from_i32(&[
4, 0, 2, 9, 6, -1, 2, 0, 1, 0, 1, 0, 7, 0, 2, 1, 1, 1, 1, 1, 7, 1, 0, 0, 2, 1, 1, 5, 2, 1, 0, 0, 8, 0, 1, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 1, 2, 5, 'a' as i32, 0, 0, 2, 3, 5, 'b' as i32, 0, 0, 3, 4, 1, 0, 0, 0, 5, 6, 5,
' ' as i32, 0, 0, 6, 7, 1, 0, 0, 0, 7, 8, 6, 1, 0, 0, 1, 0, 1, 6, 0, 0, ]))
.deserialize()
.expect("artificial lexer ATN should deserialize");
let data = RecognizerData::new(
"T",
Vocabulary::new(
[None, Some("'ab'"), Some("' '")],
[None, Some("AB"), Some("WS")],
[None::<&str>, None, None],
),
);
let mut lexer = BaseLexer::new(InputStream::new(" ab"), data);
let token = next_token(&mut lexer, &atn);
assert_eq!(token.token_type(), 1);
assert_eq!(token.text(), "ab");
assert_eq!(next_token(&mut lexer, &atn).token_type(), TOKEN_EOF);
}
#[test]
fn lexer_more_extends_original_token_start() {
let atn = AtnDeserializer::new(&SerializedAtn::from_i32(&[
4, 0, 1, 8, 6, -1, 2, 0, 1, 0, 1, 0, 7, 0, 2, 1, 1, 1, 7, 1, 0, 0, 2, 1, 1, 5, 1, 1, 0, 0, 6, 0, 1, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 1, 2, 5, 'a' as i32, 0, 0, 2, 4, 6, 0, 0, 0, 5, 6, 5, 'b' as i32, 0, 0, 6, 7, 1, 0, 0, 0, 1, 0, 1, 3, 0, 0, ]))
.deserialize()
.expect("artificial lexer ATN with more action should deserialize");
let data = RecognizerData::new(
"T",
Vocabulary::new([None, Some("AB")], [None, Some("AB")], [None::<&str>, None]),
);
let mut lexer = BaseLexer::new(InputStream::new("ab"), data);
let token = next_token(&mut lexer, &atn);
assert_eq!(token.token_type(), 1);
assert_eq!(token.start(), 0);
assert_eq!(token.stop(), 1);
assert_eq!(token.text(), "ab");
}
}