diff --git a/lalrpop/src/lr1/lane_table/construct/merge.rs b/lalrpop/src/lr1/lane_table/construct/merge.rs index 43b8a5dfb..5931f1570 100644 --- a/lalrpop/src/lr1/lane_table/construct/merge.rs +++ b/lalrpop/src/lr1/lane_table/construct/merge.rs @@ -199,6 +199,7 @@ impl<'m> ContextSets<'m> { } fn union(&mut self, source: StateIndex, target: StateIndex) -> bool { + debug!("state_sets: {:?}", self.state_sets); let set1 = self.state_sets[&source]; let set2 = self.state_sets[&target]; let result = self.unify.unify_var_var(set1, set2).is_ok(); diff --git a/lalrpop/src/lr1/lane_table/lane/mod.rs b/lalrpop/src/lr1/lane_table/lane/mod.rs index 75bce5256..86ce53bb0 100644 --- a/lalrpop/src/lr1/lane_table/lane/mod.rs +++ b/lalrpop/src/lr1/lane_table/lane/mod.rs @@ -60,6 +60,7 @@ impl<'trace, 'grammar, L: Lookahead> LaneTracer<'trace, 'grammar, L> { Action::Reduce(prod) => { let item = Item::lr0(prod, prod.symbols.len()); + self.table.add_lookahead(state, conflict, &TokenSet::new()); self.continue_trace(state, conflict, item, &mut visited_set); } } @@ -72,6 +73,7 @@ impl<'trace, 'grammar, L: Lookahead> LaneTracer<'trace, 'grammar, L> { item: Lr0Item<'grammar>, visited: &mut Set<(StateIndex, Lr0Item<'grammar>)>, ) { + debug!("continue_trace: state={:?}, index={:?}", state, item.index); if !visited.insert((state, item)) { return; } diff --git a/lalrpop/src/lr1/lane_table/test.rs b/lalrpop/src/lr1/lane_table/test.rs index b6a18ab38..f060aaecc 100644 --- a/lalrpop/src/lr1/lane_table/test.rs +++ b/lalrpop/src/lr1/lane_table/test.rs @@ -104,6 +104,30 @@ Y: () = { ) } +/// A variation on G1 to omit the possibility of shifting +pub fn example_g2() -> Grammar { + normalized_grammar( + r#" +grammar; + +pub G = { + "a" X "d", + "a" Y "c", + "b" X "c", + "b" Y "d", +}; + +X = { + "e" +}; + +Y = { + "e" +}; +"#, + ) +} + fn build_table<'grammar>( grammar: &'grammar Grammar, goal: &str, @@ -294,6 +318,27 @@ P: () = { ) } +// The G1 example has a non-conflicting shift in the state with the reduce/reduce conflict. This +// test exercises the case where the reduce/reduce is the only difference. +#[test] +fn example_g2_build() { + let _tls = Tls::test(); + let grammar = example_g2(); + + let _lr1_tls = Lr1Tls::install(grammar.terminals.clone()); + let lr0_err = build::build_lr0_states(&grammar, nt("__G")).unwrap_err(); + let states = build_states(&grammar, nt("__G")).expect("failed to build lane table states"); + + // we require more *states* than LR(0), not just different lookahead + assert_eq!(states.len() - lr0_err.states.len(), 1); + + let tree = interpret::interpret(&states, tokens!["a", "e", "d"]).unwrap(); + expect_debug(&tree, r#"[__G: [G: "a", [X: "e"], "d"]]"#); + + let tree = interpret::interpret(&states, tokens!["b", "e", "d"]).unwrap(); + expect_debug(&tree, r#"[__G: [G: "b", [Y: "e"], "d"]]"#); +} + #[test] fn large_conflict_1() { let _tls = Tls::test(); @@ -320,7 +365,7 @@ fn large_conflict_1() { | S5 | | | ["a"] | ["r"] | {S16} | | S7 | | | ["c", "w"] | ["d"] | {S16} | | S16 | | | | | {S27} | -| S27 | ["s"] | ["k"] | | | {S32} | +| S27 | ["s"] | ["k"] | [] | [] | {S32} | | S32 | | | ["z"] | ["u"] | {S16} | "# .trim_start(),