lalrpop 0.12.2

convenient LR(1) parser generator
Documentation
use intern::intern;
use grammar::repr::*;
use lr1::build_states;
use lr1::core::*;
use lr1::first::FirstSets;
use lr1::tls::Lr1Tls;
use test_util::{expect_debug, normalized_grammar};
use tls::Tls;

use super::super::Tracer;

fn nt(t: &str) -> NonterminalString {
    NonterminalString(intern(t))
}

#[test]
fn shift_backtrace_1() {
    // This grammar yields a S/R conflict. Is it `(int -> int) -> int`
    // or `int -> (int -> int)`?

    let _tls = Tls::test();
    let grammar = normalized_grammar(r#"
grammar;
pub Ty: () = {
    "int" => (),
    "bool" => (),
    <t1:Ty> "->" <t2:Ty> => (),
};
"#);
    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
    let first_sets = FirstSets::new(&grammar);
    let err = build_states(&grammar, nt("Ty")).unwrap_err();
    let conflict = err.conflicts[0].clone();
    println!("conflict={:?}", conflict);

    // Gin up the LR0 item involved in the shift/reduce conflict:
    //
    //     Ty = Ty (*) -> Ty (shift)
    //
    // from the item that we can reduce:
    //
    //     Ty = Ty -> Ty (*) (reduce)

    assert!(conflict.production.symbols.len() == 3);
    let item = Item::lr0(conflict.production, 1);
    println!("item={:?}", item);
    let tracer = Tracer::new(&first_sets, &err.states);
    let graph = tracer.backtrace_shift(conflict.state, item);
    expect_debug(&graph, r#"
[
    (Nonterminal(Ty) -([], Some(Ty), ["->", Ty])-> Nonterminal(Ty)),
    (Nonterminal(Ty) -([Ty], Some("->"), [Ty])-> Item(Ty = Ty (*) "->" Ty)),
    (Item(Ty = Ty "->" (*) Ty) -([Ty, "->"], Some(Ty), [])-> Nonterminal(Ty))
]
"#.trim());

    let list: Vec<_> =
        graph.lr0_examples(item)
             .map(|example| example.paint_unstyled())
             .collect();
    expect_debug(&list, r#"
[
    [
        "  Ty "->" Ty "->" Ty",
        "  │       └─Ty─────┤",
        "  └─Ty─────────────┘"
    ]
]
"#.trim());
}