Neil - >> The above shadows 'id'; I suppose 'ident' would be better. Doh! I found the id() shadowing later, changed my var to id_ so as not to stray from your BNF too much.
>> How can I make it barf for testcases like '(+ 2 3))'? It doesn't >> seem to expect an Eof. To force parsing to the end of string, add a StringEnd instance where you expect there to be the end of the input string. Change: waeTree = wae.parseString(t) to: waeTree = (wae + StringEnd()).parseString(t) >> The muss is that, >> since all the __init__ functions now expect a token list instead >> of named arguments, they are now cryptic, and creating AST's >> manually became annoying. The fuss is that I do have to create >> one in With's calc function. It should be unnecessary for the >> AST objects to be so dependent upon the grammar to work >> correctly. I agree 1000%. The pyparsing method for this is to use setResultsName. Here is the grammar, with results names defined to match those in your original. And you are absolutely correct, using named fields like this makes your code MUCH more robust, and less dependent on the grammar. num = Combine( Optional("-") + Word(nums) ).setResultsName("n") id_ = oneOf( list(alphas) ).setResultsName("v") addwae = Group( LPAR + "+" + wae.setResultsName("lhs") + wae.setResultsName("rhs") + RPAR ) subwae = Group( LPAR + "-" + wae.setResultsName("lhs") + wae.setResultsName("rhs") + RPAR ) withwae = Group( LPAR + "with" + LPAR + id_.setResultsName("bound_id") + wae.setResultsName("named_expr") + RPAR + wae.setResultsName("bound_body") + RPAR ) Now your calc methods can refer to them using: self.tokens.lhs self.tokens.bound_id etc. Here is my alternative solution (not using results names). I used the base WAE class to accept the tokens as the initialization var, then unpack the list into variables in each respective calc() method. I did not see the need for a subst() method. There is a complete s-exp parser at the pyparsing wiki examples page: http://pyparsing.wikispaces.com/space/showimage/sexpParser.py -- Paul class WAE(object): ids = {} def __init__(self,tokens): # need to deref element 0 because of Groups self.tokens = tokens[0] class NumWAE(WAE): def calc(self): return int(self.tokens) class IdWAE(WAE): def getId(self): return self.tokens def calc(self): return WAE.ids[self.getId()][-1] class BinOpWAE(WAE): def calc(self): op,a,b = self.tokens return self.opfn(a.calc(), b.calc()) class AddWAE(BinOpWAE): opfn = staticmethod(lambda a,b:a+b) class SubWAE(BinOpWAE): opfn = staticmethod(lambda a,b:a-b) class WithWAE(WAE): def calc(self): op,varid,varexpr,withexpr = self.tokens varname = varid.getId() if varname not in WAE.ids: WAE.ids[varname] = [] WAE.ids[varname].append( varexpr.calc() ) ret = withexpr.calc() WAE.ids[varname].pop() return ret for expr,cls in zip((num,id_,addwae,subwae,withwae), (NumWAE,IdWAE,AddWAE,SubWAE,WithWAE)): expr.setParseAction(cls) for t in tests: print t waeTree = wae.parseString(t)[0] print waeTree.calc() print -- http://mail.python.org/mailman/listinfo/python-list