@@ -5,16 +5,15 @@ use crate::generate::grammars::{
55 InlinedProductionMap , LexicalGrammar , SyntaxGrammar , VariableType ,
66} ;
77use crate :: generate:: node_types:: VariableInfo ;
8- use crate :: generate:: rules:: { Associativity , Symbol , SymbolType , TokenSet } ;
8+ use crate :: generate:: rules:: { Associativity , Precedence , Symbol , SymbolType , TokenSet } ;
99use crate :: generate:: tables:: {
1010 FieldLocation , GotoAction , ParseAction , ParseState , ParseStateId , ParseTable , ParseTableEntry ,
1111 ProductionInfo , ProductionInfoId ,
1212} ;
13- use core:: ops:: Range ;
14- use std:: collections:: hash_map:: Entry ;
1513use std:: collections:: { BTreeMap , HashMap , HashSet , VecDeque } ;
1614use std:: fmt:: Write ;
1715use std:: u32;
16+ use std:: { cmp:: Ordering , collections:: hash_map:: Entry } ;
1817
1918// For conflict reporting, each parse state is associated with an example
2019// sequence of symbols that could lead to that parse state.
@@ -29,6 +28,14 @@ struct AuxiliarySymbolInfo {
2928 parent_symbols : Vec < Symbol > ,
3029}
3130
31+ #[ derive( Debug , Default ) ]
32+ struct ReductionInfo {
33+ precedence : Precedence ,
34+ has_left_assoc : bool ,
35+ has_right_assoc : bool ,
36+ has_non_assoc : bool ,
37+ }
38+
3239struct ParseStateQueueEntry {
3340 state_id : ParseStateId ,
3441 preceding_auxiliary_symbols : AuxiliarySymbolSequence ,
@@ -118,8 +125,6 @@ impl<'a> ParseTableBuilder<'a> {
118125 ) ?;
119126 }
120127
121- self . remove_precedences ( ) ;
122-
123128 Ok ( ( self . parse_table , self . parse_state_info_by_id ) )
124129 }
125130
@@ -179,6 +184,7 @@ impl<'a> ParseTableBuilder<'a> {
179184 let mut terminal_successors = BTreeMap :: new ( ) ;
180185 let mut non_terminal_successors = BTreeMap :: new ( ) ;
181186 let mut lookaheads_with_conflicts = TokenSet :: new ( ) ;
187+ let mut reduction_infos = HashMap :: < Symbol , ReductionInfo > :: new ( ) ;
182188
183189 // Each item in the item set contributes to either or a Shift action or a Reduce
184190 // action in this state.
@@ -217,31 +223,50 @@ impl<'a> ParseTableBuilder<'a> {
217223 ParseAction :: Reduce {
218224 symbol : Symbol :: non_terminal ( item. variable_index as usize ) ,
219225 child_count : item. step_index as usize ,
220- precedence : item. precedence ( ) ,
221- associativity : item. associativity ( ) ,
222226 dynamic_precedence : item. production . dynamic_precedence ,
223227 production_id : self . get_production_id ( item) ,
224228 }
225229 } ;
226230
231+ let precedence = item. precedence ( ) ;
232+ let associativity = item. associativity ( ) ;
227233 for lookahead in lookaheads. iter ( ) {
228- let entry = self . parse_table . states [ state_id]
234+ let table_entry = self . parse_table . states [ state_id]
229235 . terminal_entries
230- . entry ( lookahead) ;
231- let entry = entry. or_insert_with ( || ParseTableEntry :: new ( ) ) ;
236+ . entry ( lookahead)
237+ . or_insert_with ( || ParseTableEntry :: new ( ) ) ;
238+ let reduction_info = reduction_infos. entry ( lookahead) . or_default ( ) ;
232239
233240 // While inserting Reduce actions, eagerly resolve conflicts related
234241 // to precedence: avoid inserting lower-precedence reductions, and
235242 // clear the action list when inserting higher-precedence reductions.
236- if entry. actions . is_empty ( ) {
237- entry. actions . push ( action) ;
238- } else if action. precedence ( ) > entry. actions [ 0 ] . precedence ( ) {
239- entry. actions . clear ( ) ;
240- entry. actions . push ( action) ;
241- lookaheads_with_conflicts. remove ( & lookahead) ;
242- } else if action. precedence ( ) == entry. actions [ 0 ] . precedence ( ) {
243- entry. actions . push ( action) ;
244- lookaheads_with_conflicts. insert ( lookahead) ;
243+ if table_entry. actions . is_empty ( ) {
244+ table_entry. actions . push ( action) ;
245+ } else {
246+ match Self :: compare_precedence (
247+ & self . syntax_grammar ,
248+ precedence,
249+ & reduction_info. precedence ,
250+ ) {
251+ Ordering :: Greater => {
252+ table_entry. actions . clear ( ) ;
253+ table_entry. actions . push ( action) ;
254+ lookaheads_with_conflicts. remove ( & lookahead) ;
255+ * reduction_info = ReductionInfo :: default ( ) ;
256+ }
257+ Ordering :: Equal => {
258+ table_entry. actions . push ( action) ;
259+ lookaheads_with_conflicts. insert ( lookahead) ;
260+ }
261+ Ordering :: Less => continue ,
262+ }
263+ }
264+
265+ reduction_info. precedence = precedence. clone ( ) ;
266+ match associativity {
267+ Some ( Associativity :: Left ) => reduction_info. has_left_assoc = true ,
268+ Some ( Associativity :: Right ) => reduction_info. has_right_assoc = true ,
269+ None => reduction_info. has_non_assoc = true ,
245270 }
246271 }
247272 }
@@ -302,6 +327,7 @@ impl<'a> ParseTableBuilder<'a> {
302327 & preceding_symbols,
303328 & preceding_auxiliary_symbols,
304329 symbol,
330+ reduction_infos. get ( & symbol) . unwrap ( ) ,
305331 ) ?;
306332 }
307333
@@ -381,6 +407,7 @@ impl<'a> ParseTableBuilder<'a> {
381407 preceding_symbols : & SymbolSequence ,
382408 preceding_auxiliary_symbols : & Vec < AuxiliarySymbolInfo > ,
383409 conflicting_lookahead : Symbol ,
410+ reduction_info : & ReductionInfo ,
384411 ) -> Result < ( ) > {
385412 let entry = self . parse_table . states [ state_id]
386413 . terminal_entries
@@ -393,9 +420,8 @@ impl<'a> ParseTableBuilder<'a> {
393420 // sorted out ahead of time in `add_actions`. But there can still be
394421 // REDUCE-REDUCE conflicts where all actions have the *same*
395422 // precedence, and there can still be SHIFT/REDUCE conflicts.
396- let reduce_precedence = entry. actions [ 0 ] . precedence ( ) ;
397423 let mut considered_associativity = false ;
398- let mut shift_precedence: Option < Range < i32 > > = None ;
424+ let mut shift_precedence: Vec < & Precedence > = Vec :: new ( ) ;
399425 let mut conflicting_items = HashSet :: new ( ) ;
400426 for ( item, lookaheads) in & item_set. entries {
401427 if let Some ( step) = item. step ( ) {
@@ -409,15 +435,9 @@ impl<'a> ParseTableBuilder<'a> {
409435 conflicting_items. insert ( item) ;
410436 }
411437
412- let precedence = item. precedence ( ) ;
413- if let Some ( range) = & mut shift_precedence {
414- if precedence < range. start {
415- range. start = precedence;
416- } else if precedence > range. end {
417- range. end = precedence;
418- }
419- } else {
420- shift_precedence = Some ( precedence..precedence) ;
438+ let p = item. precedence ( ) ;
439+ if let Err ( i) = shift_precedence. binary_search ( & p) {
440+ shift_precedence. insert ( i, p) ;
421441 }
422442 }
423443 }
@@ -429,8 +449,6 @@ impl<'a> ParseTableBuilder<'a> {
429449 }
430450
431451 if let ParseAction :: Shift { is_repetition, .. } = entry. actions . last_mut ( ) . unwrap ( ) {
432- let shift_precedence = shift_precedence. unwrap_or ( 0 ..0 ) ;
433-
434452 // If all of the items in the conflict have the same parent symbol,
435453 // and that parent symbols is auxiliary, then this is just the intentional
436454 // ambiguity associated with a repeat rule. Resolve that class of ambiguity
@@ -448,40 +466,37 @@ impl<'a> ParseTableBuilder<'a> {
448466 }
449467
450468 // If the SHIFT action has higher precedence, remove all the REDUCE actions.
451- if shift_precedence. start > reduce_precedence
452- || ( shift_precedence. start == reduce_precedence
453- && shift_precedence. end > reduce_precedence)
454- {
469+ let mut shift_is_less = false ;
470+ let mut shift_is_more = false ;
471+ for p in shift_precedence {
472+ match Self :: compare_precedence ( & self . syntax_grammar , p, & reduction_info. precedence )
473+ {
474+ Ordering :: Greater => shift_is_more = true ,
475+ Ordering :: Less => shift_is_less = true ,
476+ Ordering :: Equal => { }
477+ }
478+ }
479+
480+ if shift_is_more && !shift_is_less {
455481 entry. actions . drain ( 0 ..entry. actions . len ( ) - 1 ) ;
456482 }
457483 // If the REDUCE actions have higher precedence, remove the SHIFT action.
458- else if shift_precedence. end < reduce_precedence
459- || ( shift_precedence. end == reduce_precedence
460- && shift_precedence. start < reduce_precedence)
461- {
484+ else if shift_is_less && !shift_is_more {
462485 entry. actions . pop ( ) ;
463486 conflicting_items. retain ( |item| item. is_done ( ) ) ;
464487 }
465488 // If the SHIFT and REDUCE actions have the same predence, consider
466489 // the REDUCE actions' associativity.
467- else if shift_precedence == ( reduce_precedence..reduce_precedence ) {
490+ else if !shift_is_less && !shift_is_more {
468491 considered_associativity = true ;
469- let mut has_left = false ;
470- let mut has_right = false ;
471- let mut has_non = false ;
472- for action in & entry. actions {
473- if let ParseAction :: Reduce { associativity, .. } = action {
474- match associativity {
475- Some ( Associativity :: Left ) => has_left = true ,
476- Some ( Associativity :: Right ) => has_right = true ,
477- None => has_non = true ,
478- }
479- }
480- }
481492
482493 // If all Reduce actions are left associative, remove the SHIFT action.
483494 // If all Reduce actions are right associative, remove the REDUCE actions.
484- match ( has_left, has_non, has_right) {
495+ match (
496+ reduction_info. has_left_assoc ,
497+ reduction_info. has_non_assoc ,
498+ reduction_info. has_right_assoc ,
499+ ) {
485500 ( true , false , false ) => {
486501 entry. actions . pop ( ) ;
487502 conflicting_items. retain ( |item| item. is_done ( ) ) ;
@@ -595,7 +610,7 @@ impl<'a> ParseTableBuilder<'a> {
595610 "(precedence: {}, associativity: {:?})" ,
596611 precedence, associativity
597612 ) )
598- } else if precedence != 0 {
613+ } else if !precedence . is_none ( ) {
599614 Some ( format ! ( "(precedence: {})" , precedence) )
600615 } else {
601616 None
@@ -714,6 +729,47 @@ impl<'a> ParseTableBuilder<'a> {
714729 Err ( Error :: new ( msg) )
715730 }
716731
732+ fn compare_precedence (
733+ grammar : & SyntaxGrammar ,
734+ left : & Precedence ,
735+ right : & Precedence ,
736+ ) -> Ordering {
737+ match ( left, right) {
738+ // Integer precedences can be compared to other integer precedences,
739+ // and to the default precedence, which is zero.
740+ ( Precedence :: Integer ( l) , Precedence :: Integer ( r) ) => l. cmp ( r) ,
741+ ( Precedence :: Integer ( l) , Precedence :: None ) => l. cmp ( & 0 ) ,
742+ ( Precedence :: None , Precedence :: Integer ( r) ) => 0 . cmp ( & r) ,
743+
744+ // Named precedences can be compared to other named precedences.
745+ ( Precedence :: Name ( l) , Precedence :: Name ( r) ) => grammar
746+ . precedence_orderings
747+ . iter ( )
748+ . find_map ( |list| {
749+ let mut saw_left = false ;
750+ let mut saw_right = false ;
751+ for name in list {
752+ if name == l {
753+ saw_left = true ;
754+ if saw_right {
755+ return Some ( Ordering :: Less ) ;
756+ }
757+ } else if name == r {
758+ saw_right = true ;
759+ if saw_left {
760+ return Some ( Ordering :: Greater ) ;
761+ }
762+ }
763+ }
764+ None
765+ } )
766+ . unwrap_or ( Ordering :: Equal ) ,
767+
768+ // Other combinations of precedence types are not comparable.
769+ _ => Ordering :: Equal ,
770+ }
771+ }
772+
717773 fn get_auxiliary_node_info (
718774 & self ,
719775 item_set : & ParseItemSet ,
@@ -739,26 +795,6 @@ impl<'a> ParseTableBuilder<'a> {
739795 }
740796 }
741797
742- fn remove_precedences ( & mut self ) {
743- for state in self . parse_table . states . iter_mut ( ) {
744- for ( _, entry) in state. terminal_entries . iter_mut ( ) {
745- for action in entry. actions . iter_mut ( ) {
746- match action {
747- ParseAction :: Reduce {
748- precedence,
749- associativity,
750- ..
751- } => {
752- * precedence = 0 ;
753- * associativity = None ;
754- }
755- _ => { }
756- }
757- }
758- }
759- }
760- }
761-
762798 fn get_production_id ( & mut self , item : & ParseItem ) -> ProductionInfoId {
763799 let mut production_info = ProductionInfo {
764800 alias_sequence : Vec :: new ( ) ,
0 commit comments