How to parse multiple expressions when using the rply library
Question: I created a parser using the rply library for python and can currently perform
basic arithmetic. The problem is that I cannot parse more than one line when
reading from a file. Say I have: 5 + 4 on a single line.

That parses with no errors. But if I have something like the following over
two lines.

5 + 4

7 * 3

I get this error: rply.errors.ParsingError.

I have set my lexer to ignore newlines and spaces:

    
    
    lg.ignore('\n')
    lg.ignore('\s+')
    

And these are my productions:

    
    
    @pg.production('main : expression')
    def main(p):
        return p[0]
    
    @pg.production(’expression : NUMBER’)
    def expression_number(p):
        return Number(int(p[0].getstr()))
    
    @pg.production(’expression : expression PLUS expression’)
    def expression_binop(p):
    left = p[0]
    right = p[2]
    if p[1].gettokentype() == ’AND’:
        return Add(left, right)
    

Any help would be greatly appreciated! Thanks!


Answer: This will work, you had no multiplication setup:

    
    
    from rply import ParserGenerator, LexerGenerator
    from rply.token import BaseBox
    
    lg = LexerGenerator()
    # Add takes a rule name, and a regular expression that defines the rule.
    lg.add("PLUS", r"\+")
    lg.add("MINUS", r"-")
    lg.add("NUMBER", r"\d+")
    lg.add('MUL', r'\*') # added MUL here
    
    lg.ignore(r"\s+")
    
    # This is a list of the token names. precedence is an optional list of
    # tuples which specifies order of operation for avoiding ambiguity.
    # precedence must be one of "left", "right", "nonassoc".
    # cache_id is an optional string which specifies an ID to use for
    # caching. It should *always* be safe to use caching,
    # RPly will automatically detect when your grammar is
    # changed and refresh the cache for you.
    pg = ParserGenerator(["NUMBER", "PLUS", "MINUS",'MUL'], # added MUL here
            precedence=[("left", ['PLUS', 'MINUS'])], cache_id="myparser")
    
    @pg.production("main : expr")
    def main(p):
        # p is a list, of each of the pieces on the right hand side of the
        # grammar rule
        return p[0]
    @pg.production("expr : expr MUL expr") # added MUL here
    @pg.production("expr : expr PLUS expr")
    @pg.production("expr : expr MINUS expr")
    def expr_op(p):
        lhs = p[0].getint()
        rhs = p[2].getint()
        if p[1].gettokentype() == "PLUS":
            return BoxInt(lhs + rhs)
        elif p[1].gettokentype() == "MINUS":
            return BoxInt(lhs - rhs)
        elif p[1].gettokentype() == 'MUL': # added Mul here
            return BoxInt(lhs * rhs)
        else:
            raise AssertionError("This is impossible, abort the time machine!")
    
    @pg.production("expr : NUMBER")
    def expr_num(p):
        return BoxInt(int(p[0].getstr()))
    
    lexer = lg.build()
    parser = pg.build()
    
    class BoxInt(BaseBox):
        def __init__(self, value):
            self.value = value
    
        def getint(self):
            return self.value
    with open("hello.txt") as f:
        for line in f:
            if line.strip():
                print parser.parse(lexer.lex(line)).value
    21
    9
    

