fn compute_conflict_status()

in cli/src/generate/build_tables/token_conflicts.rs [223:348]


fn compute_conflict_status(
    cursor: &mut NfaCursor,
    grammar: &LexicalGrammar,
    following_chars: &Vec<CharacterSet>,
    i: usize,
    j: usize,
) -> (TokenConflictStatus, TokenConflictStatus) {
    let mut visited_state_sets = HashSet::new();
    let mut state_set_queue = vec![vec![
        grammar.variables[i].start_state,
        grammar.variables[j].start_state,
    ]];
    let mut result = (
        TokenConflictStatus::default(),
        TokenConflictStatus::default(),
    );

    while let Some(state_set) = state_set_queue.pop() {
        // Don't pursue states where there's no potential for conflict.
        if grammar.variable_indices_for_nfa_states(&state_set).count() > 1 {
            cursor.reset(state_set);
        } else {
            continue;
        }

        let has_sep = cursor.transition_chars().any(|(_, sep)| sep);

        // Examine each possible completed token in this state.
        let mut completion = None;
        for (id, precedence) in cursor.completions() {
            if has_sep {
                if id == i {
                    result.0.does_match_separators = true;
                } else {
                    result.1.does_match_separators = true;
                }
            }

            // If the other token has already completed, then this is
            // a same-string conflict.
            if let Some((prev_id, prev_precedence)) = completion {
                if id == prev_id {
                    continue;
                }

                // Determine which of the two tokens is preferred.
                let preferred_id;
                if TokenConflictMap::prefer_token(
                    grammar,
                    (prev_precedence, prev_id),
                    (precedence, id),
                ) {
                    preferred_id = prev_id;
                } else {
                    preferred_id = id;
                    completion = Some((id, precedence));
                }

                if preferred_id == i {
                    result.0.matches_same_string = true;
                } else {
                    result.1.matches_same_string = true;
                }
            } else {
                completion = Some((id, precedence));
            }
        }

        // Examine each possible transition from this state to detect substring conflicts.
        for transition in cursor.transitions() {
            let mut can_advance = true;

            // If there is already a completed token in this state, then determine
            // if the next state can also match the completed token. If so, then
            // this is *not* a conflict.
            if let Some((completed_id, completed_precedence)) = completion {
                let mut advanced_id = None;
                let mut successor_contains_completed_id = false;
                for variable_id in grammar.variable_indices_for_nfa_states(&transition.states) {
                    if variable_id == completed_id {
                        successor_contains_completed_id = true;
                        break;
                    } else {
                        advanced_id = Some(variable_id);
                    }
                }

                // Determine which action is preferred: matching the already complete
                // token, or continuing on to try and match the other longer token.
                if let (Some(advanced_id), false) = (advanced_id, successor_contains_completed_id) {
                    if TokenConflictMap::prefer_transition(
                        grammar,
                        &transition,
                        completed_id,
                        completed_precedence,
                        has_sep,
                    ) {
                        can_advance = true;
                        if advanced_id == i {
                            result.0.does_match_continuation = true;
                            if transition.characters.does_intersect(&following_chars[j]) {
                                result.0.does_match_valid_continuation = true;
                            }
                        } else {
                            result.1.does_match_continuation = true;
                            if transition.characters.does_intersect(&following_chars[i]) {
                                result.1.does_match_valid_continuation = true;
                            }
                        }
                    } else {
                        if completed_id == i {
                            result.0.matches_prefix = true;
                        } else {
                            result.1.matches_prefix = true;
                        }
                    }
                }
            }

            if can_advance && visited_state_sets.insert(transition.states.clone()) {
                state_set_queue.push(transition.states);
            }
        }
    }
    result
}