Pretty Scheme, ??? Python

Paul McGuire ptmcg at austin.rr.com
Tue Jul 3 09:38:29 EDT 2007


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




More information about the Python-list mailing list