That’s a runtime concept, we’re talking about the AST for such expressions. At runtime (as you can verify using the dis
module) an expression of the form a + b + c + d + ...
only need a stack depth of 2 – it’s basically “load a, load b, add, load c, add, load d, add, …”.
In the AST, however, such expressions lead to a stack of nodes – it’s like an extremely unbalanced binary tree:
>>> import ast
>>> t = ast.parse("a + b + c + d + e", mode="eval")
>>> print(ast.dump(t, indent=4))
Expression(
body=BinOp(
left=BinOp(
left=BinOp(
left=BinOp(
left=Name(id='a', ctx=Load()),
op=Add(),
right=Name(id='b', ctx=Load())),
op=Add(),
right=Name(id='c', ctx=Load())),
op=Add(),
right=Name(id='d', ctx=Load())),
op=Add(),
right=Name(id='e', ctx=Load())))
>>>
We could solve this (at least for such simple cases) by changing the grammar to somehow accept a series of alternating operands and operators, storing them in a list (our PEG parser generator is capable of doing that rather easily), and then having the code generation (and other analysis phases, if needed) use a simple stack-based operator precedence “parser” on that list. This would require a refactoring of the symbol table and the code generator, though, and I haven’t thought about how much effort this would be.
And of course this would not provide protection against generated attacks on the AST tree depth like "f("*1000 + "0" + ")"*1000
.