Browse Source

Added CollapseAmbiguities

tags/gm/2021-09-23T00Z/github.com--lark-parser-lark/0.8.2
Erez Sh 5 years ago
parent
commit
287b10504c
3 changed files with 94 additions and 1 deletions
  1. +51
    -0
      docs/recipes.md
  2. +22
    -0
      lark/utils.py
  3. +21
    -1
      lark/visitors.py

+ 51
- 0
docs/recipes.md View File

@@ -74,3 +74,54 @@ Prints out:
```

*Note: We don't have to return a token, because comments are ignored*

## CollapseAmbiguities

Parsing ambiguous texts with earley and `ambiguity='explicit'` produces a single tree with `_ambig` nodes to mark where the ambiguity occured.

However, it's sometimes more convenient instead to work with a list of all possible unambiguous trees.

Lark provides a utility transformer for that purpose:

```python
from lark import Lark, Tree, Transformer
from lark.visitors import CollapseAmbiguities

grammar = """
!start: x y

!x: "a" "b"
| "ab"
| "abc"

!y: "c" "d"
| "cd"
| "d"

"""
parser = Lark(grammar, ambiguity='explicit')

t = parser.parse('abcd')
for x in CollapseAmbiguities().transform(t):
print(x.pretty())
```

This prints out:

start
x
a
b
y
c
d

start
x ab
y cd

start
x abc
y d

While convenient, this should be used carefully, as highly ambiguous trees will soon create an exponential explosion of such unambiguous derivations.

+ 22
- 0
lark/utils.py View File

@@ -1,4 +1,5 @@
import sys
from functools import reduce
from ast import literal_eval
from collections import deque

@@ -265,3 +266,24 @@ def eval_escaping(s):
raise ValueError(s, e)

return s


def combine_alternatives(lists):
"""
Accepts a list of alternatives, and enumerates all their possible concatinations.

Examples:
>>> combine_alternatives([range(2), [4,5]])
[[0, 4], [0, 5], [1, 4], [1, 5]]

>>> combine_alternatives(["abc", "xy", '$'])
[['a', 'x', '$'], ['a', 'y', '$'], ['b', 'x', '$'], ['b', 'y', '$'], ['c', 'x', '$'], ['c', 'y', '$']]

>>> combine_alternatives([])
[[]]
"""
if not lists:
return [[]]
assert all(l for l in lists), lists
init = [[x] for x in lists[0]]
return reduce(lambda a,b: [i+[j] for i in a for j in b], lists[1:], init)

+ 21
- 1
lark/visitors.py View File

@@ -1,6 +1,6 @@
from functools import wraps

from .utils import smart_decorator
from .utils import smart_decorator, combine_alternatives
from .tree import Tree
from .exceptions import VisitError, GrammarError
from .lexer import Token
@@ -345,3 +345,23 @@ def v_args(inline=False, meta=False, tree=False, wrapper=None):


###}


#--- Visitor Utilities ---

class CollapseAmbiguities(Transformer):
"""
Transforms a tree that contains any number of _ambig nodes into a list of trees,
each one containing an unambiguous tree.

The length of the resulting list is the product of the length of all _ambig nodes.

Warning: This may quickly explode for highly ambiguous trees.

"""
def _ambig(self, options):
return sum(options, [])
def __default__(self, data, children_lists, meta):
return [Tree(data, children, meta) for children in combine_alternatives(children_lists)]
def __default_token__(self, t):
return [t]

Loading…
Cancel
Save