Skip to main content

tree_sitter_generate/build_tables/
build_parse_table.rs

1use std::{
2    cmp::Ordering,
3    collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
4    hash::BuildHasherDefault,
5};
6
7use indexmap::{map::Entry, IndexMap};
8use log::warn;
9use rustc_hash::FxHasher;
10use serde::Serialize;
11use thiserror::Error;
12
13use super::{
14    item::{ParseItem, ParseItemSet, ParseItemSetCore, ParseItemSetEntry},
15    item_set_builder::ParseItemSetBuilder,
16};
17use crate::{
18    grammars::{LexicalGrammar, PrecedenceEntry, ReservedWordSetId, SyntaxGrammar, VariableType},
19    node_types::VariableInfo,
20    rules::{Associativity, Precedence, Symbol, SymbolType, TokenSet},
21    tables::{
22        FieldLocation, GotoAction, ParseAction, ParseState, ParseStateId, ParseTable,
23        ParseTableEntry, ProductionInfo, ProductionInfoId,
24    },
25};
26
27// For conflict reporting, each parse state is associated with an example
28// sequence of symbols that could lead to that parse state.
29type SymbolSequence = Vec<Symbol>;
30
31type AuxiliarySymbolSequence = Vec<AuxiliarySymbolInfo>;
32pub type ParseStateInfo<'a> = (SymbolSequence, ParseItemSet<'a>);
33
34#[derive(Clone, PartialEq)]
35struct AuxiliarySymbolInfo {
36    auxiliary_symbol: Symbol,
37    parent_symbols: Vec<Symbol>,
38}
39
40#[derive(Debug, Default)]
41struct ReductionInfo {
42    precedence: Precedence,
43    symbols: Vec<Symbol>,
44    has_left_assoc: bool,
45    has_right_assoc: bool,
46    has_non_assoc: bool,
47}
48
49struct ParseStateQueueEntry {
50    state_id: ParseStateId,
51    preceding_auxiliary_symbols: AuxiliarySymbolSequence,
52}
53
54struct ParseTableBuilder<'a> {
55    item_set_builder: ParseItemSetBuilder<'a>,
56    syntax_grammar: &'a SyntaxGrammar,
57    lexical_grammar: &'a LexicalGrammar,
58    variable_info: &'a [VariableInfo],
59    core_ids_by_core: HashMap<ParseItemSetCore<'a>, usize>,
60    state_ids_by_item_set: IndexMap<ParseItemSet<'a>, ParseStateId, BuildHasherDefault<FxHasher>>,
61    parse_state_info_by_id: Vec<ParseStateInfo<'a>>,
62    parse_state_queue: VecDeque<ParseStateQueueEntry>,
63    non_terminal_extra_states: Vec<(Symbol, usize)>,
64    actual_conflicts: HashSet<Vec<Symbol>>,
65    parse_table: ParseTable,
66}
67
68pub type BuildTableResult<T> = Result<T, ParseTableBuilderError>;
69
70#[derive(Debug, Error, Serialize)]
71pub enum ParseTableBuilderError {
72    #[error("Unresolved conflict for symbol sequence:\n\n{0}")]
73    Conflict(#[from] ConflictError),
74    #[error("Extra rules must have unambiguous endings. Conflicting rules: {0}")]
75    AmbiguousExtra(#[from] AmbiguousExtraError),
76    #[error(
77        "The non-terminal rule `{0}` is used in a non-terminal `extra` rule, which is not allowed."
78    )]
79    ImproperNonTerminalExtra(String),
80    #[error("State count `{0}` exceeds the max value {max}.", max=u16::MAX)]
81    StateCount(usize),
82}
83
84#[derive(Default, Debug, Serialize, Error)]
85pub struct ConflictError {
86    pub symbol_sequence: Vec<String>,
87    pub conflicting_lookahead: String,
88    pub possible_interpretations: Vec<Interpretation>,
89    pub possible_resolutions: Vec<Resolution>,
90}
91
92#[derive(Default, Debug, Serialize, Error)]
93pub struct Interpretation {
94    pub preceding_symbols: Vec<String>,
95    pub variable_name: String,
96    pub production_step_symbols: Vec<String>,
97    pub step_index: u32,
98    pub done: bool,
99    pub conflicting_lookahead: String,
100    pub precedence: Option<String>,
101    pub associativity: Option<String>,
102}
103
104#[derive(Debug, Serialize)]
105pub enum Resolution {
106    Precedence { symbols: Vec<String> },
107    Associativity { symbols: Vec<String> },
108    AddConflict { symbols: Vec<String> },
109}
110
111#[derive(Debug, Serialize, Error)]
112pub struct AmbiguousExtraError {
113    pub parent_symbols: Vec<String>,
114}
115
116impl std::fmt::Display for ConflictError {
117    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
118        for symbol in &self.symbol_sequence {
119            write!(f, "  {symbol}")?;
120        }
121        writeln!(f, "  •  {}  …\n", self.conflicting_lookahead)?;
122
123        writeln!(f, "Possible interpretations:\n")?;
124        let mut interpretations = self
125            .possible_interpretations
126            .iter()
127            .map(|i| {
128                let line = i.to_string();
129                let prec_line = if let (Some(precedence), Some(associativity)) =
130                    (&i.precedence, &i.associativity)
131                {
132                    Some(format!(
133                        "(precedence: {precedence}, associativity: {associativity})",
134                    ))
135                } else {
136                    i.precedence
137                        .as_ref()
138                        .map(|precedence| format!("(precedence: {precedence})"))
139                };
140
141                (line, prec_line)
142            })
143            .collect::<Vec<_>>();
144        let max_interpretation_length = interpretations
145            .iter()
146            .map(|i| i.0.chars().count())
147            .max()
148            .unwrap();
149        interpretations.sort_unstable();
150        for (i, (line, prec_suffix)) in interpretations.into_iter().enumerate() {
151            write!(f, "  {}:", i + 1).unwrap();
152            write!(f, "{line}")?;
153            if let Some(prec_suffix) = prec_suffix {
154                write!(
155                    f,
156                    "{:1$}",
157                    "",
158                    max_interpretation_length.saturating_sub(line.chars().count()) + 2
159                )?;
160                write!(f, "{prec_suffix}")?;
161            }
162            writeln!(f)?;
163        }
164
165        writeln!(f, "\nPossible resolutions:\n")?;
166        for (i, resolution) in self.possible_resolutions.iter().enumerate() {
167            writeln!(f, "  {}:  {resolution}", i + 1)?;
168        }
169        Ok(())
170    }
171}
172
173impl std::fmt::Display for Interpretation {
174    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
175        for symbol in &self.preceding_symbols {
176            write!(f, "  {symbol}")?;
177        }
178        write!(f, "  ({}", self.variable_name)?;
179        for (i, symbol) in self.production_step_symbols.iter().enumerate() {
180            if i == self.step_index as usize {
181                write!(f, "  •")?;
182            }
183            write!(f, "  {symbol}")?;
184        }
185        write!(f, ")")?;
186        if self.done {
187            write!(f, "  •  {}  …", self.conflicting_lookahead)?;
188        }
189        Ok(())
190    }
191}
192
193impl std::fmt::Display for Resolution {
194    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
195        match self {
196            Self::Precedence { symbols } => {
197                write!(f, "Specify a higher precedence in ")?;
198                for (i, symbol) in symbols.iter().enumerate() {
199                    if i > 0 {
200                        write!(f, " and ")?;
201                    }
202                    write!(f, "`{symbol}`")?;
203                }
204                write!(f, " than in the other rules.")?;
205            }
206            Self::Associativity { symbols } => {
207                write!(f, "Specify a left or right associativity in ")?;
208                for (i, symbol) in symbols.iter().enumerate() {
209                    if i > 0 {
210                        write!(f, ", ")?;
211                    }
212                    write!(f, "`{symbol}`")?;
213                }
214            }
215            Self::AddConflict { symbols } => {
216                write!(f, "Add a conflict for these rules: ")?;
217                for (i, symbol) in symbols.iter().enumerate() {
218                    if i > 0 {
219                        write!(f, ", ")?;
220                    }
221                    write!(f, "`{symbol}`")?;
222                }
223            }
224        }
225        Ok(())
226    }
227}
228
229impl std::fmt::Display for AmbiguousExtraError {
230    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
231        for (i, symbol) in self.parent_symbols.iter().enumerate() {
232            if i > 0 {
233                write!(f, ", ")?;
234            }
235            write!(f, "{symbol}")?;
236        }
237        Ok(())
238    }
239}
240
241impl<'a> ParseTableBuilder<'a> {
242    fn new(
243        syntax_grammar: &'a SyntaxGrammar,
244        lexical_grammar: &'a LexicalGrammar,
245        item_set_builder: ParseItemSetBuilder<'a>,
246        variable_info: &'a [VariableInfo],
247    ) -> Self {
248        Self {
249            syntax_grammar,
250            lexical_grammar,
251            item_set_builder,
252            variable_info,
253            non_terminal_extra_states: Vec::new(),
254            state_ids_by_item_set: IndexMap::default(),
255            core_ids_by_core: HashMap::new(),
256            parse_state_info_by_id: Vec::new(),
257            parse_state_queue: VecDeque::new(),
258            actual_conflicts: syntax_grammar.expected_conflicts.iter().cloned().collect(),
259            parse_table: ParseTable {
260                states: Vec::new(),
261                symbols: Vec::new(),
262                external_lex_states: Vec::new(),
263                production_infos: Vec::new(),
264                max_aliased_production_length: 1,
265            },
266        }
267    }
268
269    fn build(mut self) -> BuildTableResult<(ParseTable, Vec<ParseStateInfo<'a>>)> {
270        // Ensure that the empty alias sequence has index 0.
271        self.parse_table
272            .production_infos
273            .push(ProductionInfo::default());
274
275        // Add the error state at index 0.
276        self.add_parse_state(&Vec::new(), &Vec::new(), ParseItemSet::default());
277
278        // Add the starting state at index 1.
279        self.add_parse_state(
280            &Vec::new(),
281            &Vec::new(),
282            ParseItemSet {
283                entries: vec![ParseItemSetEntry {
284                    item: ParseItem::start(),
285                    lookaheads: std::iter::once(Symbol::end()).collect(),
286                    following_reserved_word_set: ReservedWordSetId::default(),
287                }],
288            },
289        );
290
291        // Compute the possible item sets for non-terminal extras.
292        let mut non_terminal_extra_item_sets_by_first_terminal = BTreeMap::new();
293        for extra_non_terminal in self
294            .syntax_grammar
295            .extra_symbols
296            .iter()
297            .filter(|s| s.is_non_terminal())
298        {
299            let variable = &self.syntax_grammar.variables[extra_non_terminal.index];
300            for production in &variable.productions {
301                non_terminal_extra_item_sets_by_first_terminal
302                    .entry(production.first_symbol().unwrap())
303                    .or_insert_with(ParseItemSet::default)
304                    .insert(ParseItem {
305                        variable_index: extra_non_terminal.index as u32,
306                        production,
307                        step_index: 1,
308                        has_preceding_inherited_fields: false,
309                    })
310                    .lookaheads
311                    .insert(Symbol::end_of_nonterminal_extra());
312            }
313        }
314
315        let non_terminal_sets_len = non_terminal_extra_item_sets_by_first_terminal.len();
316        self.non_terminal_extra_states
317            .reserve(non_terminal_sets_len);
318        self.parse_state_info_by_id.reserve(non_terminal_sets_len);
319        self.parse_table.states.reserve(non_terminal_sets_len);
320        self.parse_state_queue.reserve(non_terminal_sets_len);
321        // Add a state for each starting terminal of a non-terminal extra rule.
322        for (terminal, item_set) in non_terminal_extra_item_sets_by_first_terminal {
323            if terminal.is_non_terminal() {
324                Err(ParseTableBuilderError::ImproperNonTerminalExtra(
325                    self.symbol_name(&terminal),
326                ))?;
327            }
328
329            // Add the parse state, and *then* push the terminal and the state id into the
330            // list of nonterminal extra states
331            let state_id = self.add_parse_state(&Vec::new(), &Vec::new(), item_set);
332            self.non_terminal_extra_states.push((terminal, state_id));
333        }
334
335        while let Some(entry) = self.parse_state_queue.pop_front() {
336            let item_set = self
337                .item_set_builder
338                .transitive_closure(&self.parse_state_info_by_id[entry.state_id].1);
339
340            self.add_actions(
341                self.parse_state_info_by_id[entry.state_id].0.clone(),
342                entry.preceding_auxiliary_symbols,
343                entry.state_id,
344                &item_set,
345            )?;
346        }
347
348        if !self.actual_conflicts.is_empty() {
349            warn!(
350                "unnecessary conflicts:\n  {}",
351                self.actual_conflicts
352                    .iter()
353                    .map(|conflict| {
354                        conflict
355                            .iter()
356                            .map(|symbol| format!("`{}`", self.symbol_name(symbol)))
357                            .collect::<Vec<_>>()
358                            .join(", ")
359                    })
360                    .collect::<Vec<_>>()
361                    .join("\n  ")
362            );
363        }
364
365        Ok((self.parse_table, self.parse_state_info_by_id))
366    }
367
368    fn add_parse_state(
369        &mut self,
370        preceding_symbols: &SymbolSequence,
371        preceding_auxiliary_symbols: &AuxiliarySymbolSequence,
372        item_set: ParseItemSet<'a>,
373    ) -> ParseStateId {
374        match self.state_ids_by_item_set.entry(item_set) {
375            // If an equivalent item set has already been processed, then return
376            // the existing parse state index.
377            Entry::Occupied(o) => *o.get(),
378
379            // Otherwise, insert a new parse state and add it to the queue of
380            // parse states to populate.
381            Entry::Vacant(v) => {
382                let core = v.key().core();
383                let core_count = self.core_ids_by_core.len();
384                let core_id = *self.core_ids_by_core.entry(core).or_insert(core_count);
385
386                let state_id = self.parse_table.states.len();
387                self.parse_state_info_by_id
388                    .push((preceding_symbols.clone(), v.key().clone()));
389
390                self.parse_table.states.push(ParseState {
391                    id: state_id,
392                    lex_state_id: 0,
393                    external_lex_state_id: 0,
394                    terminal_entries: IndexMap::default(),
395                    nonterminal_entries: IndexMap::default(),
396                    reserved_words: TokenSet::default(),
397                    core_id,
398                });
399                self.parse_state_queue.push_back(ParseStateQueueEntry {
400                    state_id,
401                    preceding_auxiliary_symbols: preceding_auxiliary_symbols.clone(),
402                });
403                v.insert(state_id);
404                state_id
405            }
406        }
407    }
408
409    fn add_actions(
410        &mut self,
411        mut preceding_symbols: SymbolSequence,
412        mut preceding_auxiliary_symbols: AuxiliarySymbolSequence,
413        state_id: ParseStateId,
414        item_set: &ParseItemSet<'a>,
415    ) -> BuildTableResult<()> {
416        let mut terminal_successors = BTreeMap::new();
417        let mut non_terminal_successors = BTreeMap::new();
418        let mut lookaheads_with_conflicts = TokenSet::new();
419        let mut reduction_infos = HashMap::<Symbol, ReductionInfo>::new();
420
421        // Each item in the item set contributes to either or a Shift action or a Reduce
422        // action in this state.
423        for ParseItemSetEntry {
424            item,
425            lookaheads,
426            following_reserved_word_set: reserved_lookaheads,
427        } in &item_set.entries
428        {
429            // If the item is unfinished, then this state has a transition for the item's
430            // next symbol. Advance the item to its next step and insert the resulting
431            // item into the successor item set.
432            if let Some(next_symbol) = item.symbol() {
433                let mut successor = item.successor();
434                let successor_set = if next_symbol.is_non_terminal() {
435                    let variable = &self.syntax_grammar.variables[next_symbol.index];
436
437                    // Keep track of where auxiliary non-terminals (repeat symbols) are
438                    // used within visible symbols. This information may be needed later
439                    // for conflict resolution.
440                    if variable.is_auxiliary() {
441                        preceding_auxiliary_symbols
442                            .push(self.get_auxiliary_node_info(item_set, next_symbol));
443                    }
444
445                    // For most parse items, the symbols associated with the preceding children
446                    // don't matter: they have no effect on the REDUCE action that would be
447                    // performed at the end of the item. But the symbols *do* matter for
448                    // children that are hidden and have fields, because those fields are
449                    // "inherited" by the parent node.
450                    //
451                    // If this item has consumed a hidden child with fields, then the symbols
452                    // of its preceding children need to be taken into account when comparing
453                    // it with other items.
454                    if variable.is_hidden()
455                        && !self.variable_info[next_symbol.index].fields.is_empty()
456                    {
457                        successor.has_preceding_inherited_fields = true;
458                    }
459
460                    non_terminal_successors
461                        .entry(next_symbol)
462                        .or_insert_with(ParseItemSet::default)
463                } else {
464                    terminal_successors
465                        .entry(next_symbol)
466                        .or_insert_with(ParseItemSet::default)
467                };
468                let successor_entry = successor_set.insert(successor);
469                successor_entry.lookaheads.insert_all(lookaheads);
470                successor_entry.following_reserved_word_set = successor_entry
471                    .following_reserved_word_set
472                    .max(*reserved_lookaheads);
473            }
474            // If the item is finished, then add a Reduce action to this state based
475            // on this item.
476            else {
477                let symbol = Symbol::non_terminal(item.variable_index as usize);
478                let action = if item.is_augmented() {
479                    ParseAction::Accept
480                } else {
481                    ParseAction::Reduce {
482                        symbol,
483                        child_count: item.step_index as usize,
484                        dynamic_precedence: item.production.dynamic_precedence,
485                        production_id: self.get_production_id(item),
486                    }
487                };
488
489                let precedence = item.precedence();
490                let associativity = item.associativity();
491                for lookahead in lookaheads.iter() {
492                    let table_entry = self.parse_table.states[state_id]
493                        .terminal_entries
494                        .entry(lookahead)
495                        .or_insert_with(ParseTableEntry::new);
496                    let reduction_info = reduction_infos.entry(lookahead).or_default();
497
498                    // While inserting Reduce actions, eagerly resolve conflicts related
499                    // to precedence: avoid inserting lower-precedence reductions, and
500                    // clear the action list when inserting higher-precedence reductions.
501                    if table_entry.actions.is_empty() {
502                        table_entry.actions.push(action);
503                    } else {
504                        match Self::compare_precedence(
505                            self.syntax_grammar,
506                            precedence,
507                            &[symbol],
508                            &reduction_info.precedence,
509                            &reduction_info.symbols,
510                        ) {
511                            Ordering::Greater => {
512                                table_entry.actions.clear();
513                                table_entry.actions.push(action);
514                                lookaheads_with_conflicts.remove(&lookahead);
515                                *reduction_info = ReductionInfo::default();
516                            }
517                            Ordering::Equal => {
518                                table_entry.actions.push(action);
519                                lookaheads_with_conflicts.insert(lookahead);
520                            }
521                            Ordering::Less => continue,
522                        }
523                    }
524
525                    reduction_info.precedence.clone_from(precedence);
526                    if let Err(i) = reduction_info.symbols.binary_search(&symbol) {
527                        reduction_info.symbols.insert(i, symbol);
528                    }
529                    match associativity {
530                        Some(Associativity::Left) => reduction_info.has_left_assoc = true,
531                        Some(Associativity::Right) => reduction_info.has_right_assoc = true,
532                        None => reduction_info.has_non_assoc = true,
533                    }
534                }
535            }
536        }
537
538        preceding_auxiliary_symbols.dedup();
539
540        // Having computed the successor item sets for each symbol, add a new
541        // parse state for each of these item sets, and add a corresponding Shift
542        // action to this state.
543        for (symbol, next_item_set) in terminal_successors {
544            preceding_symbols.push(symbol);
545            let next_state_id = self.add_parse_state(
546                &preceding_symbols,
547                &preceding_auxiliary_symbols,
548                next_item_set,
549            );
550            preceding_symbols.pop();
551
552            let entry = self.parse_table.states[state_id]
553                .terminal_entries
554                .entry(symbol);
555            if let Entry::Occupied(e) = &entry {
556                if !e.get().actions.is_empty() {
557                    lookaheads_with_conflicts.insert(symbol);
558                }
559            }
560
561            entry
562                .or_insert_with(ParseTableEntry::new)
563                .actions
564                .push(ParseAction::Shift {
565                    state: next_state_id,
566                    is_repetition: false,
567                });
568        }
569
570        for (symbol, next_item_set) in non_terminal_successors {
571            preceding_symbols.push(symbol);
572            let next_state_id = self.add_parse_state(
573                &preceding_symbols,
574                &preceding_auxiliary_symbols,
575                next_item_set,
576            );
577            preceding_symbols.pop();
578            self.parse_table.states[state_id]
579                .nonterminal_entries
580                .insert(symbol, GotoAction::Goto(next_state_id));
581        }
582
583        // For any symbol with multiple actions, perform conflict resolution.
584        // This will either
585        // * choose one action over the others using precedence or associativity
586        // * keep multiple actions if this conflict has been whitelisted in the grammar
587        // * fail, terminating the parser generation process
588        for symbol in lookaheads_with_conflicts.iter() {
589            self.handle_conflict(
590                item_set,
591                state_id,
592                &preceding_symbols,
593                &preceding_auxiliary_symbols,
594                symbol,
595                reduction_infos.get(&symbol).unwrap(),
596            )?;
597        }
598
599        // Add actions for the grammar's `extra` symbols.
600        let state = &mut self.parse_table.states[state_id];
601        let is_end_of_non_terminal_extra = state.is_end_of_non_terminal_extra();
602
603        // If this state represents the end of a non-terminal extra rule, then make sure that
604        // it doesn't have other successor states. Non-terminal extra rules must have
605        // unambiguous endings.
606        if is_end_of_non_terminal_extra {
607            if state.terminal_entries.len() > 1 {
608                let parent_symbols = item_set
609                    .entries
610                    .iter()
611                    .filter_map(|ParseItemSetEntry { item, .. }| {
612                        if !item.is_augmented() && item.step_index > 0 {
613                            Some(item.variable_index)
614                        } else {
615                            None
616                        }
617                    })
618                    .collect::<HashSet<_>>();
619                let parent_symbol_names = parent_symbols
620                    .iter()
621                    .map(|&variable_index| {
622                        self.syntax_grammar.variables[variable_index as usize]
623                            .name
624                            .clone()
625                    })
626                    .collect::<Vec<_>>();
627
628                Err(AmbiguousExtraError {
629                    parent_symbols: parent_symbol_names,
630                })?;
631            }
632        }
633        // Add actions for the start tokens of each non-terminal extra rule.
634        else {
635            for (terminal, state_id) in &self.non_terminal_extra_states {
636                state
637                    .terminal_entries
638                    .entry(*terminal)
639                    .or_insert(ParseTableEntry {
640                        reusable: true,
641                        actions: vec![ParseAction::Shift {
642                            state: *state_id,
643                            is_repetition: false,
644                        }],
645                    });
646            }
647
648            // Add ShiftExtra actions for the terminal extra tokens. These actions
649            // are added to every state except for those at the ends of non-terminal
650            // extras.
651            for extra_token in &self.syntax_grammar.extra_symbols {
652                if extra_token.is_non_terminal() {
653                    state
654                        .nonterminal_entries
655                        .insert(*extra_token, GotoAction::ShiftExtra);
656                } else {
657                    state
658                        .terminal_entries
659                        .entry(*extra_token)
660                        .or_insert(ParseTableEntry {
661                            reusable: true,
662                            actions: vec![ParseAction::ShiftExtra],
663                        });
664                }
665            }
666        }
667
668        if let Some(keyword_capture_token) = self.syntax_grammar.word_token {
669            let reserved_word_set_id = item_set
670                .entries
671                .iter()
672                .filter_map(|entry| {
673                    if let Some(next_step) = entry.item.step() {
674                        if next_step.symbol == keyword_capture_token {
675                            Some(next_step.reserved_word_set_id)
676                        } else {
677                            None
678                        }
679                    } else if entry.lookaheads.contains(&keyword_capture_token) {
680                        Some(entry.following_reserved_word_set)
681                    } else {
682                        None
683                    }
684                })
685                .max();
686            if let Some(reserved_word_set_id) = reserved_word_set_id {
687                state.reserved_words =
688                    self.syntax_grammar.reserved_word_sets[reserved_word_set_id.0].clone();
689            }
690        }
691
692        Ok(())
693    }
694
695    fn handle_conflict(
696        &mut self,
697        item_set: &ParseItemSet,
698        state_id: ParseStateId,
699        preceding_symbols: &SymbolSequence,
700        preceding_auxiliary_symbols: &[AuxiliarySymbolInfo],
701        conflicting_lookahead: Symbol,
702        reduction_info: &ReductionInfo,
703    ) -> BuildTableResult<()> {
704        let entry = self.parse_table.states[state_id]
705            .terminal_entries
706            .get_mut(&conflicting_lookahead)
707            .unwrap();
708
709        // Determine which items in the set conflict with each other, and the
710        // precedences associated with SHIFT vs REDUCE actions. There won't
711        // be multiple REDUCE actions with different precedences; that is
712        // sorted out ahead of time in `add_actions`. But there can still be
713        // REDUCE-REDUCE conflicts where all actions have the *same*
714        // precedence, and there can still be SHIFT/REDUCE conflicts.
715        let mut considered_associativity = false;
716        let mut shift_precedence = Vec::<(&Precedence, Symbol)>::new();
717        let mut conflicting_items = BTreeSet::new();
718        for ParseItemSetEntry {
719            item, lookaheads, ..
720        } in &item_set.entries
721        {
722            if let Some(step) = item.step() {
723                if item.step_index > 0
724                    && self
725                        .item_set_builder
726                        .first_set(&step.symbol)
727                        .contains(&conflicting_lookahead)
728                {
729                    if item.variable_index != u32::MAX {
730                        conflicting_items.insert(item);
731                    }
732
733                    let p = (
734                        item.precedence(),
735                        Symbol::non_terminal(item.variable_index as usize),
736                    );
737                    if let Err(i) = shift_precedence.binary_search(&p) {
738                        shift_precedence.insert(i, p);
739                    }
740                }
741            } else if lookaheads.contains(&conflicting_lookahead) && item.variable_index != u32::MAX
742            {
743                conflicting_items.insert(item);
744            }
745        }
746
747        if let ParseAction::Shift { is_repetition, .. } = entry.actions.last_mut().unwrap() {
748            // If all of the items in the conflict have the same parent symbol,
749            // and that parent symbols is auxiliary, then this is just the intentional
750            // ambiguity associated with a repeat rule. Resolve that class of ambiguity
751            // by leaving it in the parse table, but marking the SHIFT action with
752            // an `is_repetition` flag.
753            let conflicting_variable_index =
754                conflicting_items.iter().next().unwrap().variable_index;
755            if self.syntax_grammar.variables[conflicting_variable_index as usize].is_auxiliary()
756                && conflicting_items
757                    .iter()
758                    .all(|item| item.variable_index == conflicting_variable_index)
759            {
760                *is_repetition = true;
761                return Ok(());
762            }
763
764            // If the SHIFT action has higher precedence, remove all the REDUCE actions.
765            let mut shift_is_less = false;
766            let mut shift_is_equal = false;
767            let mut shift_is_more = false;
768            for p in shift_precedence {
769                match Self::compare_precedence(
770                    self.syntax_grammar,
771                    p.0,
772                    &[p.1],
773                    &reduction_info.precedence,
774                    &reduction_info.symbols,
775                ) {
776                    Ordering::Greater => shift_is_more = true,
777                    Ordering::Less => shift_is_less = true,
778                    Ordering::Equal => shift_is_equal = true,
779                }
780            }
781
782            if shift_is_more && !shift_is_less {
783                entry.actions.drain(0..entry.actions.len() - 1);
784            }
785            // If the REDUCE actions have higher precedence, remove the SHIFT action.
786            else if shift_is_less && !shift_is_more {
787                // Exception: if one SHIFT interpretation ties the REDUCE actions in
788                // precedence while another has lower precedence, and the REDUCE
789                // actions are purely right associative, honor that right
790                // associativity by shifting rather than reducing. The
791                // lower-precedence interpretation coexists with the tying one, so on
792                // its own it must not force a REDUCE that would flip the tie to left
793                // associative.
794                if shift_is_equal
795                    && matches!(
796                        (
797                            reduction_info.has_left_assoc,
798                            reduction_info.has_non_assoc,
799                            reduction_info.has_right_assoc,
800                        ),
801                        (false, false, true)
802                    )
803                {
804                    entry.actions.drain(0..entry.actions.len() - 1);
805                } else {
806                    entry.actions.pop();
807                    conflicting_items.retain(|item| item.is_done());
808                }
809            }
810            // If the SHIFT and REDUCE actions have the same predence, consider
811            // the REDUCE actions' associativity.
812            else if !shift_is_less && !shift_is_more {
813                considered_associativity = true;
814
815                // If all Reduce actions are left associative, remove the SHIFT action.
816                // If all Reduce actions are right associative, remove the REDUCE actions.
817                match (
818                    reduction_info.has_left_assoc,
819                    reduction_info.has_non_assoc,
820                    reduction_info.has_right_assoc,
821                ) {
822                    (true, false, false) => {
823                        entry.actions.pop();
824                        conflicting_items.retain(|item| item.is_done());
825                    }
826                    (false, false, true) => {
827                        entry.actions.drain(0..entry.actions.len() - 1);
828                    }
829                    _ => {}
830                }
831            }
832        }
833
834        // If all of the actions but one have been eliminated, then there's no problem.
835        let entry = self.parse_table.states[state_id]
836            .terminal_entries
837            .get_mut(&conflicting_lookahead)
838            .unwrap();
839        if entry.actions.len() == 1 {
840            return Ok(());
841        }
842
843        // Determine the set of parent symbols involved in this conflict.
844        let mut actual_conflict = Vec::new();
845        for item in &conflicting_items {
846            let symbol = Symbol::non_terminal(item.variable_index as usize);
847            if self.syntax_grammar.variables[symbol.index].is_auxiliary() {
848                actual_conflict.extend(
849                    preceding_auxiliary_symbols
850                        .iter()
851                        .rev()
852                        .find_map(|info| {
853                            if info.auxiliary_symbol == symbol {
854                                Some(&info.parent_symbols)
855                            } else {
856                                None
857                            }
858                        })
859                        .unwrap()
860                        .iter(),
861                );
862            } else {
863                actual_conflict.push(symbol);
864            }
865        }
866        actual_conflict.sort_unstable();
867        actual_conflict.dedup();
868
869        // If this set of symbols has been whitelisted, then there's no error.
870        if self
871            .syntax_grammar
872            .expected_conflicts
873            .contains(&actual_conflict)
874        {
875            self.actual_conflicts.remove(&actual_conflict);
876            return Ok(());
877        }
878
879        let mut conflict_error = ConflictError::default();
880        for symbol in preceding_symbols {
881            conflict_error
882                .symbol_sequence
883                .push(self.symbol_name(symbol));
884        }
885        conflict_error.conflicting_lookahead = self.symbol_name(&conflicting_lookahead);
886
887        let interpretations = conflicting_items
888            .iter()
889            .map(|item| {
890                let preceding_symbols = preceding_symbols
891                    .iter()
892                    .take(preceding_symbols.len() - item.step_index as usize)
893                    .map(|symbol| self.symbol_name(symbol))
894                    .collect::<Vec<_>>();
895
896                let variable_name = self.syntax_grammar.variables[item.variable_index as usize]
897                    .name
898                    .clone();
899
900                let production_step_symbols = item
901                    .production
902                    .steps
903                    .iter()
904                    .map(|step| self.symbol_name(&step.symbol))
905                    .collect::<Vec<_>>();
906
907                let precedence = match item.precedence() {
908                    Precedence::None => None,
909                    _ => Some(item.precedence().to_string()),
910                };
911
912                let associativity = item.associativity().map(|assoc| format!("{assoc:?}"));
913
914                Interpretation {
915                    preceding_symbols,
916                    variable_name,
917                    production_step_symbols,
918                    step_index: item.step_index,
919                    done: item.is_done(),
920                    conflicting_lookahead: self.symbol_name(&conflicting_lookahead),
921                    precedence,
922                    associativity,
923                }
924            })
925            .collect::<Vec<_>>();
926        conflict_error.possible_interpretations = interpretations;
927
928        let mut shift_items = Vec::new();
929        let mut reduce_items = Vec::new();
930        for item in conflicting_items {
931            if item.is_done() {
932                reduce_items.push(item);
933            } else {
934                shift_items.push(item);
935            }
936        }
937        shift_items.sort_unstable();
938        reduce_items.sort_unstable();
939
940        let get_rule_names = |items: &[&ParseItem]| -> Vec<String> {
941            let mut last_rule_id = None;
942            let mut result = Vec::with_capacity(items.len());
943            for item in items {
944                if last_rule_id == Some(item.variable_index) {
945                    continue;
946                }
947                last_rule_id = Some(item.variable_index);
948                result.push(self.symbol_name(&Symbol::non_terminal(item.variable_index as usize)));
949            }
950
951            result
952        };
953
954        if actual_conflict.len() > 1 {
955            if !shift_items.is_empty() {
956                let names = get_rule_names(&shift_items);
957                conflict_error
958                    .possible_resolutions
959                    .push(Resolution::Precedence { symbols: names });
960            }
961
962            for item in &reduce_items {
963                let name = self.symbol_name(&Symbol::non_terminal(item.variable_index as usize));
964                conflict_error
965                    .possible_resolutions
966                    .push(Resolution::Precedence {
967                        symbols: vec![name],
968                    });
969            }
970        }
971
972        if considered_associativity {
973            let names = get_rule_names(&reduce_items);
974            conflict_error
975                .possible_resolutions
976                .push(Resolution::Associativity { symbols: names });
977        }
978
979        conflict_error
980            .possible_resolutions
981            .push(Resolution::AddConflict {
982                symbols: actual_conflict
983                    .iter()
984                    .map(|s| self.symbol_name(s))
985                    .collect(),
986            });
987
988        self.actual_conflicts.insert(actual_conflict);
989
990        Err(conflict_error)?
991    }
992
993    fn compare_precedence(
994        grammar: &SyntaxGrammar,
995        left: &Precedence,
996        left_symbols: &[Symbol],
997        right: &Precedence,
998        right_symbols: &[Symbol],
999    ) -> Ordering {
1000        let precedence_entry_matches =
1001            |entry: &PrecedenceEntry, precedence: &Precedence, symbols: &[Symbol]| -> bool {
1002                match entry {
1003                    PrecedenceEntry::Name(n) => {
1004                        if let Precedence::Name(p) = precedence {
1005                            n == p
1006                        } else {
1007                            false
1008                        }
1009                    }
1010                    PrecedenceEntry::Symbol(n) => symbols
1011                        .iter()
1012                        .any(|s| &grammar.variables[s.index].name == n),
1013                }
1014            };
1015
1016        match (left, right) {
1017            // Integer precedences can be compared to other integer precedences,
1018            // and to the default precedence, which is zero.
1019            (Precedence::Integer(l), Precedence::Integer(r)) if *l != 0 || *r != 0 => l.cmp(r),
1020            (Precedence::Integer(l), Precedence::None) if *l != 0 => l.cmp(&0),
1021            (Precedence::None, Precedence::Integer(r)) if *r != 0 => 0.cmp(r),
1022
1023            // Named precedences can be compared to other named precedences.
1024            _ => grammar
1025                .precedence_orderings
1026                .iter()
1027                .find_map(|list| {
1028                    let mut saw_left = false;
1029                    let mut saw_right = false;
1030                    for entry in list {
1031                        let matches_left = precedence_entry_matches(entry, left, left_symbols);
1032                        let matches_right = precedence_entry_matches(entry, right, right_symbols);
1033                        if matches_left {
1034                            saw_left = true;
1035                            if saw_right {
1036                                return Some(Ordering::Less);
1037                            }
1038                        } else if matches_right {
1039                            saw_right = true;
1040                            if saw_left {
1041                                return Some(Ordering::Greater);
1042                            }
1043                        }
1044                    }
1045                    None
1046                })
1047                .unwrap_or(Ordering::Equal),
1048        }
1049    }
1050
1051    fn get_auxiliary_node_info(
1052        &self,
1053        item_set: &ParseItemSet,
1054        symbol: Symbol,
1055    ) -> AuxiliarySymbolInfo {
1056        let parent_symbols = item_set
1057            .entries
1058            .iter()
1059            .filter_map(|ParseItemSetEntry { item, .. }| {
1060                let variable_index = item.variable_index as usize;
1061                if item.symbol() == Some(symbol)
1062                    && !self.syntax_grammar.variables[variable_index].is_auxiliary()
1063                {
1064                    Some(Symbol::non_terminal(variable_index))
1065                } else {
1066                    None
1067                }
1068            })
1069            .collect();
1070        AuxiliarySymbolInfo {
1071            auxiliary_symbol: symbol,
1072            parent_symbols,
1073        }
1074    }
1075
1076    fn get_production_id(&mut self, item: &ParseItem) -> ProductionInfoId {
1077        let mut production_info = ProductionInfo {
1078            alias_sequence: Vec::new(),
1079            field_map: BTreeMap::new(),
1080        };
1081
1082        for (i, step) in item.production.steps.iter().enumerate() {
1083            production_info.alias_sequence.push(step.alias.clone());
1084            if let Some(field_name) = &step.field_name {
1085                production_info
1086                    .field_map
1087                    .entry(field_name.clone())
1088                    .or_default()
1089                    .push(FieldLocation {
1090                        index: i,
1091                        inherited: false,
1092                    });
1093            }
1094
1095            if step.symbol.kind == SymbolType::NonTerminal
1096                && !self.syntax_grammar.variables[step.symbol.index]
1097                    .kind
1098                    .is_visible()
1099            {
1100                let info = &self.variable_info[step.symbol.index];
1101                for field_name in info.fields.keys() {
1102                    production_info
1103                        .field_map
1104                        .entry(field_name.clone())
1105                        .or_default()
1106                        .push(FieldLocation {
1107                            index: i,
1108                            inherited: true,
1109                        });
1110                }
1111            }
1112        }
1113
1114        while production_info.alias_sequence.last() == Some(&None) {
1115            production_info.alias_sequence.pop();
1116        }
1117
1118        if item.production.steps.len() > self.parse_table.max_aliased_production_length {
1119            self.parse_table.max_aliased_production_length = item.production.steps.len();
1120        }
1121
1122        if let Some(index) = self
1123            .parse_table
1124            .production_infos
1125            .iter()
1126            .position(|seq| *seq == production_info)
1127        {
1128            index
1129        } else {
1130            self.parse_table.production_infos.push(production_info);
1131            self.parse_table.production_infos.len() - 1
1132        }
1133    }
1134
1135    fn symbol_name(&self, symbol: &Symbol) -> String {
1136        match symbol.kind {
1137            SymbolType::End | SymbolType::EndOfNonTerminalExtra => "EOF".to_string(),
1138            SymbolType::External => self.syntax_grammar.external_tokens[symbol.index]
1139                .name
1140                .clone(),
1141            SymbolType::NonTerminal => self.syntax_grammar.variables[symbol.index].name.clone(),
1142            SymbolType::Terminal => {
1143                let variable = &self.lexical_grammar.variables[symbol.index];
1144                if variable.kind == VariableType::Named {
1145                    variable.name.clone()
1146                } else {
1147                    format!("'{}'", variable.name)
1148                }
1149            }
1150        }
1151    }
1152}
1153
1154pub fn build_parse_table<'a>(
1155    syntax_grammar: &'a SyntaxGrammar,
1156    lexical_grammar: &'a LexicalGrammar,
1157    item_set_builder: ParseItemSetBuilder<'a>,
1158    variable_info: &'a [VariableInfo],
1159) -> BuildTableResult<(ParseTable, Vec<ParseStateInfo<'a>>)> {
1160    ParseTableBuilder::new(
1161        syntax_grammar,
1162        lexical_grammar,
1163        item_set_builder,
1164        variable_info,
1165    )
1166    .build()
1167}