diff --git a/MANIFEST.in b/MANIFEST.in index 8bdeaba..16e8d31 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include README.md LICENSE docs/* examples/*.py tests/*.py +include README.md LICENSE docs/* examples/*.py tests/*.py examples/*.png diff --git a/README.md b/README.md index 1ab447f..df8065b 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,44 @@ Then, the transformer calculates the tree and returns a number: 1337.0 ``` +### Fruit Flies Like Bananas + +Lark can automatically resolve ambiguity by choosing the simplest solution. Or, you can ask it to return all the possible parse trees, wrapped in a meta "\_ambig" node. + +Here's a toy example to parse the famously ambiguous phrase: "fruit flies like bananas" + +```python +from lark import Lark + +grammar = """ + sentence: noun verb noun -> simple + | noun verb "like" noun -> comparative + + noun: adj? NOUN + verb: VERB + adj: ADJ + + NOUN: "flies" | "bananas" | "fruit" + VERB: "like" | "flies" + ADJ: "fruit" + + %import common.WS + %ignore WS +""" + +parser = Lark(grammar, start='sentence', ambiguity='explicit') # Explicit ambiguity in parse tree! +tree = fruitflies.parser.parse('fruit flies like bananas') + +from lark.tree import pydot__tree_to_png # Just a neat utility function +pydot__tree_to_png(tree, "examples/fruitflies.png") +``` +![fruitflies.png](examples/fruitflies.png) + + ## Learn more about using Lark - **Read the [tutorial](/docs/json_tutorial.md)**, which shows how to write a JSON parser in Lark. + - Read a blog post: [How to write a DSL with Lark](http://blog.erezsh.com/how-to-write-a-dsl-in-python-with-lark/) - Read the [reference](/docs/reference.md) - Browse the [examples](/examples), which include a calculator, and a Python-code parser. - Check out the [tests](/tests/test_parser.py) for more examples. diff --git a/examples/fruitflies.png b/examples/fruitflies.png new file mode 100644 index 0000000..b1aba42 Binary files /dev/null and b/examples/fruitflies.png differ diff --git a/examples/fruitflies.py b/examples/fruitflies.py index b7026b6..d37af17 100644 --- a/examples/fruitflies.py +++ b/examples/fruitflies.py @@ -4,12 +4,13 @@ from lark import Lark -g = """ +grammar = """ sentence: noun verb noun -> simple | noun verb "like" noun -> comparative - noun: ADJ? NOUN + noun: adj? NOUN verb: VERB + adj: ADJ NOUN: "flies" | "bananas" | "fruit" VERB: "like" | "flies" @@ -19,11 +20,12 @@ g = """ %ignore WS """ -lark = Lark(g, start='sentence', ambiguity='explicit') +parser = Lark(grammar, start='sentence', ambiguity='explicit') -print(lark.parse('fruit flies like bananas').pretty()) +if __name__ == '__main__': + print(parser.parse('fruit flies like bananas').pretty()) -# Outputs: +# Output: # # _ambig # comparative @@ -36,4 +38,5 @@ print(lark.parse('fruit flies like bananas').pretty()) # flies # verb like # noun bananas - +# +# (or view a nicer version at "./fruitflies.png") diff --git a/lark/tree.py b/lark/tree.py index e8b75c3..8d58af6 100644 --- a/lark/tree.py +++ b/lark/tree.py @@ -147,3 +147,35 @@ class Transformer_NoRecurse(Transformer): def __default__(self, t): return t + +def pydot__tree_to_png(tree, filename): + import pydot + graph = pydot.Dot(graph_type='digraph', rankdir="LR") + + i = [0] + + def new_leaf(leaf): + node = pydot.Node(i[0], label=repr(leaf)) + i[0] += 1 + graph.add_node(node) + return node + + def _to_pydot(subtree): + color = hash(subtree.data) & 0xffffff + if not (color & 0x808080): + color |= 0x808080 + + subnodes = [_to_pydot(child) if isinstance(child, Tree) else new_leaf(child) + for child in subtree.children] + node = pydot.Node(i[0], style="filled", fillcolor="#%x"%color, label=subtree.data) + i[0] += 1 + graph.add_node(node) + + for subnode in subnodes: + graph.add_edge(pydot.Edge(node, subnode)) + + return node + + _to_pydot(tree) + graph.write_png(filename) + diff --git a/lark/utils.py b/lark/utils.py index 6f1e8b4..f363234 100644 --- a/lark/utils.py +++ b/lark/utils.py @@ -80,3 +80,7 @@ except NameError: return 1 else: return -1 + + + +