Skip to content

Commit ae22d4f

Browse files
authored
Merge pull request #1577 from chanicpanic/issue1576
Bugfix Earley: handle ambiguity from ignored tokens
2 parents 52ef09d + d6dae10 commit ae22d4f

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

lark/parsers/xearley.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from ..lexer import Token
2323
from ..grammar import Terminal
2424
from .earley import Parser as BaseParser
25+
from .earley_common import Item
2526
from .earley_forest import TokenNode
2627

2728
if TYPE_CHECKING:
@@ -109,7 +110,14 @@ def scan(i, to_scan):
109110
new_item.node = node_cache[label] if label in node_cache else node_cache.setdefault(label, self.SymbolNode(*label))
110111
new_item.node.add_family(new_item.s, item.rule, new_item.start, item.node, token_node)
111112
else:
112-
new_item = item
113+
# Handle items carried over due to ignores
114+
new_item = Item(item.rule, item.ptr, item.start)
115+
label = (new_item.s, new_item.start, i + 1)
116+
new_item.node = node_cache[label] if label in node_cache else node_cache.setdefault(label, self.SymbolNode(*label))
117+
if item.node:
118+
# new_item.node and item.node both represent the same symbol, so merge their children
119+
for child in item.node.children:
120+
new_item.node.add_family(new_item.s, child.rule, new_item.start, child.left, child.right)
113121

114122
if new_item.expect in self.TERMINALS:
115123
# add (B ::= Aai+1.B, h, y) to Q'

tests/test_parser.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,24 @@ def test_ambiguous_intermediate_node_conditionally_inlined_rule(self):
784784
self.assertEqual(ambig_tree.data, '_ambig')
785785
self.assertEqual(set(ambig_tree.children), expected)
786786

787+
@unittest.skipIf(LEXER=='basic', 'This ambiguity only occurs with the dynamic lexers')
788+
def test_ambiguous_ignores(self):
789+
grammar = """
790+
!start: a "b"
791+
!a: "a" | "a1" | "a12"
792+
%ignore "1"
793+
%ignore "2"
794+
"""
795+
796+
l = Lark(grammar, ambiguity='explicit', lexer=LEXER)
797+
tree = l.parse('a12b')
798+
799+
expected = Tree('_ambig', [
800+
Tree('start', [Tree('a', ['a']), 'b']),
801+
Tree('start', [Tree('a', ['a1']), 'b']),
802+
Tree('start', [Tree('a', ['a12']), 'b']),
803+
])
804+
self.assertEqual(tree, expected)
787805

788806
@unittest.skipIf(LEXER=='basic', "Requires dynamic lexer")
789807
def test_fruitflies_ambig(self):
@@ -876,6 +894,26 @@ def test_multiple_start_solutions(self):
876894
tree = l.parse('x')
877895
assert tree == Tree('start', [Tree('a', ['x'])])
878896

897+
@unittest.skipIf(LEXER=='basic', 'This scenario only occurs with the dynamic lexers')
898+
def test_multiple_start_solutions2(self):
899+
grammar = r"""
900+
!start: "foo1" | "foo" | "foo12"
901+
%ignore "1"
902+
%ignore "2"
903+
"""
904+
l = Lark(grammar, ambiguity='explicit', lexer=LEXER)
905+
tree = l.parse('foo12')
906+
907+
expected = Tree('_ambig', [
908+
Tree('start', ['foo1']),
909+
Tree('start', ['foo']),
910+
Tree('start', ['foo12']),
911+
])
912+
self.assertEqual(tree, expected)
913+
914+
l = Lark(grammar, ambiguity='resolve', lexer=LEXER)
915+
tree = l.parse('foo12')
916+
self.assertEqual(tree, Tree('start', ['foo1']))
879917

880918
def test_cycle(self):
881919
grammar = """

0 commit comments

Comments
 (0)