def test_prefer_shifts_no_sr_conflicts():
    """
    Test that grammar with S/R conflict will be resolved to SHIFT actions
    if prefer_shift option is used.
    """
    # This grammar has S/R conflict as B+ may consume multiple single "a" A
    # because "b" is optional. Thus, parser can't decide if it should shift "a"
    # or reduce by 'B: "b"? A+' and later by 'S: B+'; Most of the time we want
    # gready behavior so in case of doubt parser will choose shift if
    # prefer_shift is set to `True`. This means that the parser will first
    # consume all "a" using A+ and that reduce B at the end.
    grammar = r"""
    S: B+;
    B: "b"? A+;

    terminals
    A: "a";
    """
    g = Grammar.from_string(grammar)

    # There is a shift reduce conflict so we can't use LR parser.
    table = create_table(g)
    assert len(table.sr_conflicts) == 1

    # But we can eliminate conflict by prefer_shifts strategy.
    table = create_table(g, prefer_shifts=True)
    assert len(table.sr_conflicts) == 0

    # With prefer_shifts we get a greedy behavior
    input_str = 'b a a a b a a'
    output = [['b', ['a', 'a', 'a']], ['b', ['a', 'a']]]
    parser = Parser(g, prefer_shifts=True)
    result = parser.parse(input_str)
    assert result == output

    # GLR parser can parse without prefer_shifts strategy. This grammar is
    # ambiguous and yields 8 solutions for the given input.
    parser = GLRParser(g)
    results = [parser.call_actions(tree) for tree in parser.parse(input_str)]

    expected = [[['b', ['a']], [None, ['a']], [None, ['a']], ['b', ['a']],
                 [None, ['a']]],
                [['b', ['a', 'a']], [None, ['a']], ['b', ['a']], [None,
                                                                  ['a']]],
                [['b', ['a']], [None, ['a', 'a']], ['b', ['a']], [None,
                                                                  ['a']]],
                [['b', ['a', 'a', 'a']], ['b', ['a']], [None, ['a']]],
                [['b', ['a']], [None, ['a']], [None, ['a']], ['b', ['a',
                                                                    'a']]],
                [['b', ['a', 'a']], [None, ['a']], ['b', ['a', 'a']]],
                [['b', ['a']], [None, ['a', 'a']], ['b', ['a', 'a']]],
                [['b', ['a', 'a', 'a']], ['b', ['a', 'a']]]]
    assert results == expected

    # But if `prefer_shift` is used we get only one solution
    parser = GLRParser(g, prefer_shifts=True)
    result = parser.parse(input_str)
    assert len(result) == 1
    assert parser.call_actions(result[0]) == output
Example #2
0
def test_parse_context_call_actions():
    """
    Test that valid context attributes are available when calling
    actions using `call_actions`.
    """
    global called
    called = [False, False]

    actions["E"][0] = act_sum(is_tree=True)
    parser = Parser(g, build_tree=True, actions=actions)

    tree = parser.parse("   1 + 2  ")

    parser.call_actions(tree)

    assert all(called)
Example #3
0
def test_action_override():
    """
    Explicitely provided action in `actions` param overrides default or
    grammar provided.
    """
    grammar = """
    S: Foo Bar;
    @pass_nochange
    Bar: "1" a;

    terminals
    @pass_nochange
    Foo: 'foo';
    a: "a";
    """

    g = Grammar.from_string(grammar)
    p = Parser(g)
    input_str = "foo 1 a"
    result = p.parse(input_str)
    assert result == ["foo", ["1", "a"]]

    actions = {"Foo": lambda _, __: "eggs", "Bar": lambda _, __: "bar reduce"}

    p = Parser(g, actions=actions)
    result = p.parse(input_str)
    assert result == ["eggs", "bar reduce"]

    # Test with actions call postponing
    p = Parser(g, build_tree=True, actions=actions)
    tree = p.parse(input_str)
    result = p.call_actions(tree)
    assert result == ["eggs", "bar reduce"]
Example #4
0
def test_parse_context_call_actions():
    """
    Test that valid context attributes are available when calling
    actions using `call_actions`.
    """
    global called
    called = [False, False, False]

    parser = Parser(g, build_tree=True, actions=actions, debug=True)

    tree = parser.parse("   1 + 2  ")
    context = Context()
    context.call_actions = True
    parser.call_actions(tree, context=context)

    assert all(called)
    assert node_exists[0]
Example #5
0
def test_actions_manual():
    """
    Actions may be called as a separate step.
    """

    grammar = get_grammar()
    p = Parser(grammar, build_tree=True, actions=get_actions())

    result = p.parse("""34.7+78*34 +89+
    12.223*4""")

    assert type(result) is NodeNonTerm

    assert p.call_actions(result) == \
        34.7 + 78 * 34 + 89 + 12.223 * 4