@@ -217,8 +217,16 @@ def prev_sibling(self) -> NL | None:
217217 return self .parent .prev_sibling_map [id (self )]
218218
219219 def leaves (self ) -> Iterator ["Leaf" ]:
220- for child in self .children :
221- yield from child .leaves ()
220+ # Walk iteratively rather than recursing with `yield from`, whose per-node
221+ # generator delegation is O(depth) and makes a full traversal quadratic on
222+ # deeply nested trees (e.g. a long `a ** b ** c ** ...` chain).
223+ stack : list [NL ] = list (reversed (self .children ))
224+ while stack :
225+ node = stack .pop ()
226+ if isinstance (node , Leaf ):
227+ yield node
228+ else :
229+ stack .extend (reversed (node .children ))
222230
223231 def depth (self ) -> int :
224232 if self .parent is None :
@@ -301,15 +309,26 @@ def clone(self) -> "Node":
301309
302310 def post_order (self ) -> Iterator [NL ]:
303311 """Return a post-order iterator for the tree."""
304- for child in self .children :
305- yield from child .post_order ()
306- yield self
312+ # Walk iteratively rather than recursing with `yield from`. The recursive
313+ # form resumes one delegating generator per level on every step, so a full
314+ # traversal costs O(depth) per node and is quadratic on deeply nested trees.
315+ stack : list [tuple [NL , bool ]] = [(self , False )]
316+ while stack :
317+ node , expanded = stack .pop ()
318+ if expanded :
319+ yield node
320+ else :
321+ stack .append ((node , True ))
322+ stack .extend ((child , False ) for child in reversed (node .children ))
307323
308324 def pre_order (self ) -> Iterator [NL ]:
309325 """Return a pre-order iterator for the tree."""
310- yield self
311- for child in self .children :
312- yield from child .pre_order ()
326+ # See `post_order` for why this is iterative instead of recursive.
327+ stack : list [NL ] = [self ]
328+ while stack :
329+ node = stack .pop ()
330+ yield node
331+ stack .extend (reversed (node .children ))
313332
314333 @property
315334 def prefix (self ) -> str :
0 commit comments