parallel class structures for AST-based objects

Steve Howell showell30 at
Sun Nov 22 02:19:46 CET 2009

On Nov 21, 4:33 pm, Richard Thomas <chards... at> wrote:
> This looks more structurally sound:
> class Node(object):
>    def eval(self):
>       raise NotImplementedError
>    def pprint(self):
>       raise NotImplementedError

My objection to the interface you describe is that Node defines the
type of operations that can be done to it by third-party code, which
is something that I cannot predict, and which (pscouples every
subclass of Node to eval() and pprint(), even though those subclasses
might not care about either operation.  They might be reimplementing
only one of those operations, or some completely different operation.

> class Sum(BinaryOperatorNode):
>    operator = lambda x, y: x + y
> class Product(BinaryOperatorNode):
>    operator = lambda x, y: x * y

I do like the notion of driving up Sum/Product abstractions like
"operator" into BinaryOperatorNode, but that is sort of an artifact of
my toy example, not my main design dilemma.

> I don't know what you're doing exactly but if all you need is to be
> able to parse and evaluate expressions then you can get very decent
> mileage out of overriding operators, to the extent that the whole
> thing you are trying to do could be a single class...
> class Expression(object):
>    def __init__(self, func):
>       self.func = func
>    def __call__(self, **context):
>       while isinstance(self, Expression):
>          self = self.func(context)
>       return self
>    def __add__(self, other):
>       return Expression(lambda context: self.func(context) + other)
>    def __mul__(self, other):
> [snipped]

It is my fault for muddying the conversation with a toy example that
evaluates arithmetic expressions.  My particular problem involves a
mini-language that describes how you want to descend Python data

> But maybe that's not what you need. No need to overengineer if it is
> though, keep it simple, simple is better than complex.

Yep!  I am trying to keep things simple, but my wish to extend is not
speculative at this point; it is real.  I have an expression syntax,
which I call pyDTL, that I want these operations on:

  * generate two different versions of Python code that expresses the
  * eagerly evaluate the expression on some object
  * pretty-print the expression itself
  * use the expression as a prototype for stub objects to determine
what operations they allow
  * etc....

All of those requirements create the need to somehow create new
objects or functions that correspond to the same AST.

I describe pyDTL here:

Here is a simple example:

 echo '{.foo, .bar(){.spam, .eggs} }' | python dtl/
     foo =,
     bar = (lambda bar:
             spam = bar.spam,
             eggs = bar.eggs,


More information about the Python-list mailing list