diff --git a/docs/classes.rst b/docs/classes.rst index 0dd5954..14e9842 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -29,6 +29,17 @@ Visitor .. autoclass:: lark.visitors.VisitorBase -.. autoclass:: lark.Visitor +.. autoclass:: lark.visitors.Visitor -.. autoclass:: lark.visitors.Visitor_Recursive \ No newline at end of file +.. autoclass:: lark.visitors.Visitor_Recursive + +Interpreter +----------- + +.. autoclass:: lark.visitors.Interpreter + + +Transformer +----------- + +.. autoclass:: lark.visitors.Transformer \ No newline at end of file diff --git a/lark/visitors.py b/lark/visitors.py index 525645c..e561beb 100644 --- a/lark/visitors.py +++ b/lark/visitors.py @@ -42,12 +42,61 @@ class _Decoratable: class Transformer(_Decoratable): - """Visits the tree recursively, starting with the leaves and finally the root (bottom-up) + """Transformer visit each node of the tree, and run the appropriate method + on it according to the node's data. - Calls its methods (provided by user via inheritance) according to tree.data - The returned value replaces the old one in the structure. + Calls its methods (provided by user via inheritance) according to + ``tree.data``. The returned value replaces the old one in the structure. + + They work bottom-up (or depth-first), starting with the leaves and + ending at the root of the tree. Transformers can be used to + implement map & reduce patterns. Because nodes are reduced from leaf to + root, at any point the callbacks may assume the children have already been + transformed (if applicable). ``Transformer`` can do anything ``Visitor`` + can do, but because it reconstructs the tree, it is slightly less + efficient. + + All these classes implement the transformer interface: + + - ``Transformer`` - Recursively transforms the tree. This is the one you + probably want. + - ``Transformer_InPlace`` - Non-recursive. Changes the tree in-place + instead of returning new instances + - ``Transformer_InPlaceRecursive`` - Recursive. Changes the tree in-place + instead of returning new instances + + Example: + :: + + from lark import Tree, Transformer + + class EvalExpressions(Transformer): + def expr(self, args): + return eval(args[0]) + + t = Tree('a', [Tree('expr', ['1+2'])]) + print(EvalExpressions().transform( t )) + + # Prints: Tree(a, [3]) + + Args: + visit_tokens: By default, transformers only visit rules. + visit_tokens=True will tell ``Transformer`` to visit tokens + as well. This is a slightly slower alternative to lexer_callbacks + but it's easier to maintain and works for all algorithms + (even when there isn't a lexer). + + Example: + :: + + class T(Transformer): + INT = int + NUMBER = float + def NAME(self, name): + return lookup_dict.get(name, name) + + T(visit_tokens=True).transform(tree) - Can be used to implement map or reduce. """ __visit_tokens__ = True # For backwards compatibility @@ -235,7 +284,8 @@ class Visitor(VisitorBase): """Bottom-up visitor, non-recursive. Visits the tree, starting with the leaves and finally the root (bottom-up) - Calls its methods (provided by user via inheritance) according to tree.data + Calls its methods (provided by user via inheritance) according to + ``tree.data`` """ def visit(self, tree): @@ -253,7 +303,8 @@ class Visitor_Recursive(VisitorBase): """Bottom-up visitor, recursive. Visits the tree, starting with the leaves and finally the root (bottom-up) - Calls its methods (provided by user via inheritance) according to tree.data + Calls its methods (provided by user via inheritance) according to + ``tree.data`` """ def visit(self, tree): @@ -285,13 +336,29 @@ def visit_children_decor(func): class Interpreter(_Decoratable): - """Top-down visitor, recursive + """Interpreter walks the tree starting at the root. Visits the tree, starting with the root and finally the leaves (top-down) - Calls its methods (provided by user via inheritance) according to tree.data + Calls its methods (provided by user via inheritance) according to + ``tree.data`` + + Unlike ``Transformer`` and ``Visitor``, the Interpreter doesn't + automatically visit its sub-branches. The user has to explicitly call ``visit``, + ``visit_children``, or use the ``@visit_children_decor``. This allows the + user to implement branching and loops. + + Example: + :: + + class IncreaseSomeOfTheNumbers(Interpreter): + def number(self, tree): + tree.children[0] += 1 + + def skip(self, tree): + # skip this subtree. don't change any number node inside it. + pass - Unlike Transformer and Visitor, the Interpreter doesn't automatically visit its sub-branches. - The user has to explicitly call visit, visit_children, or use the @visit_children_decor + IncreaseSomeOfTheNumbers().visit(parse_tree) """ def visit(self, tree):