Another Engineering Notebook entry. Feel free to ignore.
The new code consists of easy, obvious, clear generator patterns. There are "yield" and "yield from" statements "everywhere". So subclasses are unlikely ever to need to override the node visitors directly.
Here, I want to emphasize that the code contains no recursion whatever, despite appearances. For example:
def do_Module(self, node):
self.begin_visitor(node)
for z in node.body:
yield from self.visitor(z)
self.end_visitor(node)
Above, "yield from self.visitor()" replaces "visit(z)" in the old recursive code.
Crucially, self.visitor returns a generator. It does not recursively call anything. Here it is:
def visitor(self, node):
"""Given an ast node, return a *generator* from its visitor."""
# We *do* want to crash if the visitor doesn't exist.
method = getattr(self, 'do_' + node.__class__.__name__)
# method(node) is a generator, not a recursive call!
return method(node)
We can prove that no recursion happens by inserting traces:
def visitor(self, node):
method = getattr(self, 'do_' + node.__class__.__name__)
print('BEFORE', node.__class__.__name__)
it = method(node)
print('AFTER', node.__class__.__name__,'\n')
return it
Nothing ever appears between these two traces, even with traces enabled in the begin/end_visitor methods.
This proves that there are no recursive calls anywhere in the TokenOrderGenerator class.
Edward
P.S. The entire class actually "yields" nothing at all. Everything happens by way of side effects, namely the properly ordered calls to eat. Everything (except eat) is dirt simple. This is the way it is written in The Book.
EKR