calc.py revision 6498
1360SN/A#!/usr/bin/env python 21458SN/A 3360SN/A# ----------------------------------------------------------------------------- 4360SN/A# calc.py 5360SN/A# 6360SN/A# A simple calculator with variables. This is from O'Reilly's 7360SN/A# "Lex and Yacc", p. 63. 8360SN/A# 9360SN/A# Class-based example contributed to PLY by David McNab. 10360SN/A# 11360SN/A# Modified to use new-style classes. Test case. 12360SN/A# ----------------------------------------------------------------------------- 13360SN/A 14360SN/Aimport sys 15360SN/Asys.path.insert(0,"../..") 16360SN/A 17360SN/Aif sys.version_info[0] >= 3: 18360SN/A raw_input = input 19360SN/A 20360SN/Aimport ply.lex as lex 21360SN/Aimport ply.yacc as yacc 22360SN/Aimport os 23360SN/A 24360SN/Aclass Parser(object): 25360SN/A """ 26360SN/A Base class for a lexer/parser that has the rules defined as methods 272665Ssaidi@eecs.umich.edu """ 282665Ssaidi@eecs.umich.edu tokens = () 292665Ssaidi@eecs.umich.edu precedence = () 30360SN/A 31360SN/A 321354SN/A def __init__(self, **kw): 331354SN/A self.debug = kw.get('debug', 0) 34360SN/A self.names = { } 352764Sstever@eecs.umich.edu try: 362764Sstever@eecs.umich.edu modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__ 372064SN/A except: 38360SN/A modname = "parser"+"_"+self.__class__.__name__ 39360SN/A self.debugfile = modname + ".dbg" 40360SN/A self.tabmodule = modname + "_" + "parsetab" 41360SN/A #print self.debugfile, self.tabmodule 42360SN/A 43360SN/A # Build the lexer and parser 441354SN/A lex.lex(module=self, debug=self.debug) 45360SN/A yacc.yacc(module=self, 461809SN/A debug=self.debug, 471809SN/A debugfile=self.debugfile, 481809SN/A tabmodule=self.tabmodule) 493113Sgblack@eecs.umich.edu 503113Sgblack@eecs.umich.edu def run(self): 511999SN/A while 1: 52360SN/A try: 533113Sgblack@eecs.umich.edu s = raw_input('calc > ') 542474SN/A except EOFError: 55360SN/A break 562462SN/A if not s: continue 571354SN/A yacc.parse(s) 582474SN/A 592680Sktlim@umich.edu 602474SN/Aclass Calc(Parser): 612474SN/A 621354SN/A tokens = ( 63360SN/A 'NAME','NUMBER', 64360SN/A 'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS', 65360SN/A 'LPAREN','RPAREN', 66360SN/A ) 67360SN/A 68360SN/A # Tokens 69360SN/A 70360SN/A t_PLUS = r'\+' 71378SN/A t_MINUS = r'-' 721450SN/A t_EXP = r'\*\*' 733114Sgblack@eecs.umich.edu t_TIMES = r'\*' 74360SN/A t_DIVIDE = r'/' 75360SN/A t_EQUALS = r'=' 76360SN/A t_LPAREN = r'\(' 77360SN/A t_RPAREN = r'\)' 78360SN/A t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' 79360SN/A 80360SN/A def t_NUMBER(self, t): 81360SN/A r'\d+' 82360SN/A try: 832680Sktlim@umich.edu t.value = int(t.value) 84360SN/A except ValueError: 85360SN/A print("Integer value too large %s" % t.value) 86360SN/A t.value = 0 87360SN/A #print "parsed number %s" % repr(t.value) 88360SN/A return t 89360SN/A 90360SN/A t_ignore = " \t" 91360SN/A 92360SN/A def t_newline(self, t): 93360SN/A r'\n+' 94360SN/A t.lexer.lineno += t.value.count("\n") 953114Sgblack@eecs.umich.edu 96360SN/A def t_error(self, t): 97360SN/A print("Illegal character '%s'" % t.value[0]) 98360SN/A t.lexer.skip(1) 99360SN/A 100360SN/A # Parsing rules 101360SN/A 102360SN/A precedence = ( 103360SN/A ('left','PLUS','MINUS'), 104360SN/A ('left','TIMES','DIVIDE'), 105360SN/A ('left', 'EXP'), 106360SN/A ('right','UMINUS'), 107360SN/A ) 108360SN/A 109360SN/A def p_statement_assign(self, p): 110360SN/A 'statement : NAME EQUALS expression' 111360SN/A self.names[p[1]] = p[3] 112360SN/A 113360SN/A def p_statement_expr(self, p): 114360SN/A 'statement : expression' 115360SN/A print(p[1]) 116360SN/A 1172400SN/A def p_expression_binop(self, p): 118360SN/A """ 1192461SN/A expression : expression PLUS expression 120360SN/A | expression MINUS expression 121360SN/A | expression TIMES expression 122360SN/A | expression DIVIDE expression 123360SN/A | expression EXP expression 124360SN/A """ 125360SN/A #print [repr(p[i]) for i in range(0,4)] 1262400SN/A if p[2] == '+' : p[0] = p[1] + p[3] 127360SN/A elif p[2] == '-': p[0] = p[1] - p[3] 1282461SN/A elif p[2] == '*': p[0] = p[1] * p[3] 129360SN/A elif p[2] == '/': p[0] = p[1] / p[3] 130360SN/A elif p[2] == '**': p[0] = p[1] ** p[3] 131360SN/A 132360SN/A def p_expression_uminus(self, p): 133360SN/A 'expression : MINUS expression %prec UMINUS' 134360SN/A p[0] = -p[2] 135360SN/A 136360SN/A def p_expression_group(self, p): 137360SN/A 'expression : LPAREN expression RPAREN' 138360SN/A p[0] = p[2] 139360SN/A 140360SN/A def p_expression_number(self, p): 141360SN/A 'expression : NUMBER' 142360SN/A p[0] = p[1] 143360SN/A 144360SN/A def p_expression_name(self, p): 145360SN/A 'expression : NAME' 146360SN/A try: 147360SN/A p[0] = self.names[p[1]] 148360SN/A except LookupError: 149360SN/A print("Undefined name '%s'" % p[1]) 150360SN/A p[0] = 0 151360SN/A 152360SN/A def p_error(self, p): 153360SN/A if p: 154360SN/A print("Syntax error at '%s'" % p.value) 155360SN/A else: 156360SN/A print("Syntax error at EOF") 157360SN/A 158360SN/Aif __name__ == '__main__': 159360SN/A calc = Calc() 160360SN/A calc.run() 161502SN/A