calc.py revision 4479
14479Sbinkertn@umich.edu#!/usr/bin/env python 24479Sbinkertn@umich.edu 34479Sbinkertn@umich.edu# ----------------------------------------------------------------------------- 44479Sbinkertn@umich.edu# calc.py 54479Sbinkertn@umich.edu# 64479Sbinkertn@umich.edu# A simple calculator with variables. This is from O'Reilly's 74479Sbinkertn@umich.edu# "Lex and Yacc", p. 63. 84479Sbinkertn@umich.edu# 94479Sbinkertn@umich.edu# Class-based example contributed to PLY by David McNab. 104479Sbinkertn@umich.edu# 114479Sbinkertn@umich.edu# Modified to use new-style classes. Test case. 124479Sbinkertn@umich.edu# ----------------------------------------------------------------------------- 134479Sbinkertn@umich.edu 144479Sbinkertn@umich.eduimport sys 154479Sbinkertn@umich.edusys.path.insert(0,"../..") 164479Sbinkertn@umich.edu 174479Sbinkertn@umich.eduimport readline 184479Sbinkertn@umich.eduimport ply.lex as lex 194479Sbinkertn@umich.eduimport ply.yacc as yacc 204479Sbinkertn@umich.eduimport os 214479Sbinkertn@umich.edu 224479Sbinkertn@umich.educlass Parser(object): 234479Sbinkertn@umich.edu """ 244479Sbinkertn@umich.edu Base class for a lexer/parser that has the rules defined as methods 254479Sbinkertn@umich.edu """ 264479Sbinkertn@umich.edu tokens = () 274479Sbinkertn@umich.edu precedence = () 284479Sbinkertn@umich.edu 294479Sbinkertn@umich.edu 304479Sbinkertn@umich.edu def __init__(self, **kw): 314479Sbinkertn@umich.edu self.debug = kw.get('debug', 0) 324479Sbinkertn@umich.edu self.names = { } 334479Sbinkertn@umich.edu try: 344479Sbinkertn@umich.edu modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__ 354479Sbinkertn@umich.edu except: 364479Sbinkertn@umich.edu modname = "parser"+"_"+self.__class__.__name__ 374479Sbinkertn@umich.edu self.debugfile = modname + ".dbg" 384479Sbinkertn@umich.edu self.tabmodule = modname + "_" + "parsetab" 394479Sbinkertn@umich.edu #print self.debugfile, self.tabmodule 404479Sbinkertn@umich.edu 414479Sbinkertn@umich.edu # Build the lexer and parser 424479Sbinkertn@umich.edu lex.lex(module=self, debug=self.debug) 434479Sbinkertn@umich.edu yacc.yacc(module=self, 444479Sbinkertn@umich.edu debug=self.debug, 454479Sbinkertn@umich.edu debugfile=self.debugfile, 464479Sbinkertn@umich.edu tabmodule=self.tabmodule) 474479Sbinkertn@umich.edu 484479Sbinkertn@umich.edu def run(self): 494479Sbinkertn@umich.edu while 1: 504479Sbinkertn@umich.edu try: 514479Sbinkertn@umich.edu s = raw_input('calc > ') 524479Sbinkertn@umich.edu except EOFError: 534479Sbinkertn@umich.edu break 544479Sbinkertn@umich.edu if not s: continue 554479Sbinkertn@umich.edu yacc.parse(s) 564479Sbinkertn@umich.edu 574479Sbinkertn@umich.edu 584479Sbinkertn@umich.educlass Calc(Parser): 594479Sbinkertn@umich.edu 604479Sbinkertn@umich.edu tokens = ( 614479Sbinkertn@umich.edu 'NAME','NUMBER', 624479Sbinkertn@umich.edu 'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS', 634479Sbinkertn@umich.edu 'LPAREN','RPAREN', 644479Sbinkertn@umich.edu ) 654479Sbinkertn@umich.edu 664479Sbinkertn@umich.edu # Tokens 674479Sbinkertn@umich.edu 684479Sbinkertn@umich.edu t_PLUS = r'\+' 694479Sbinkertn@umich.edu t_MINUS = r'-' 704479Sbinkertn@umich.edu t_EXP = r'\*\*' 714479Sbinkertn@umich.edu t_TIMES = r'\*' 724479Sbinkertn@umich.edu t_DIVIDE = r'/' 734479Sbinkertn@umich.edu t_EQUALS = r'=' 744479Sbinkertn@umich.edu t_LPAREN = r'\(' 754479Sbinkertn@umich.edu t_RPAREN = r'\)' 764479Sbinkertn@umich.edu t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' 774479Sbinkertn@umich.edu 784479Sbinkertn@umich.edu def t_NUMBER(self, t): 794479Sbinkertn@umich.edu r'\d+' 804479Sbinkertn@umich.edu try: 814479Sbinkertn@umich.edu t.value = int(t.value) 824479Sbinkertn@umich.edu except ValueError: 834479Sbinkertn@umich.edu print "Integer value too large", t.value 844479Sbinkertn@umich.edu t.value = 0 854479Sbinkertn@umich.edu #print "parsed number %s" % repr(t.value) 864479Sbinkertn@umich.edu return t 874479Sbinkertn@umich.edu 884479Sbinkertn@umich.edu t_ignore = " \t" 894479Sbinkertn@umich.edu 904479Sbinkertn@umich.edu def t_newline(self, t): 914479Sbinkertn@umich.edu r'\n+' 924479Sbinkertn@umich.edu t.lexer.lineno += t.value.count("\n") 934479Sbinkertn@umich.edu 944479Sbinkertn@umich.edu def t_error(self, t): 954479Sbinkertn@umich.edu print "Illegal character '%s'" % t.value[0] 964479Sbinkertn@umich.edu t.lexer.skip(1) 974479Sbinkertn@umich.edu 984479Sbinkertn@umich.edu # Parsing rules 994479Sbinkertn@umich.edu 1004479Sbinkertn@umich.edu precedence = ( 1014479Sbinkertn@umich.edu ('left','PLUS','MINUS'), 1024479Sbinkertn@umich.edu ('left','TIMES','DIVIDE'), 1034479Sbinkertn@umich.edu ('left', 'EXP'), 1044479Sbinkertn@umich.edu ('right','UMINUS'), 1054479Sbinkertn@umich.edu ) 1064479Sbinkertn@umich.edu 1074479Sbinkertn@umich.edu def p_statement_assign(self, p): 1084479Sbinkertn@umich.edu 'statement : NAME EQUALS expression' 1094479Sbinkertn@umich.edu self.names[p[1]] = p[3] 1104479Sbinkertn@umich.edu 1114479Sbinkertn@umich.edu def p_statement_expr(self, p): 1124479Sbinkertn@umich.edu 'statement : expression' 1134479Sbinkertn@umich.edu print p[1] 1144479Sbinkertn@umich.edu 1154479Sbinkertn@umich.edu def p_expression_binop(self, p): 1164479Sbinkertn@umich.edu """ 1174479Sbinkertn@umich.edu expression : expression PLUS expression 1184479Sbinkertn@umich.edu | expression MINUS expression 1194479Sbinkertn@umich.edu | expression TIMES expression 1204479Sbinkertn@umich.edu | expression DIVIDE expression 1214479Sbinkertn@umich.edu | expression EXP expression 1224479Sbinkertn@umich.edu """ 1234479Sbinkertn@umich.edu #print [repr(p[i]) for i in range(0,4)] 1244479Sbinkertn@umich.edu if p[2] == '+' : p[0] = p[1] + p[3] 1254479Sbinkertn@umich.edu elif p[2] == '-': p[0] = p[1] - p[3] 1264479Sbinkertn@umich.edu elif p[2] == '*': p[0] = p[1] * p[3] 1274479Sbinkertn@umich.edu elif p[2] == '/': p[0] = p[1] / p[3] 1284479Sbinkertn@umich.edu elif p[2] == '**': p[0] = p[1] ** p[3] 1294479Sbinkertn@umich.edu 1304479Sbinkertn@umich.edu def p_expression_uminus(self, p): 1314479Sbinkertn@umich.edu 'expression : MINUS expression %prec UMINUS' 1324479Sbinkertn@umich.edu p[0] = -p[2] 1334479Sbinkertn@umich.edu 1344479Sbinkertn@umich.edu def p_expression_group(self, p): 1354479Sbinkertn@umich.edu 'expression : LPAREN expression RPAREN' 1364479Sbinkertn@umich.edu p[0] = p[2] 1374479Sbinkertn@umich.edu 1384479Sbinkertn@umich.edu def p_expression_number(self, p): 1394479Sbinkertn@umich.edu 'expression : NUMBER' 1404479Sbinkertn@umich.edu p[0] = p[1] 1414479Sbinkertn@umich.edu 1424479Sbinkertn@umich.edu def p_expression_name(self, p): 1434479Sbinkertn@umich.edu 'expression : NAME' 1444479Sbinkertn@umich.edu try: 1454479Sbinkertn@umich.edu p[0] = self.names[p[1]] 1464479Sbinkertn@umich.edu except LookupError: 1474479Sbinkertn@umich.edu print "Undefined name '%s'" % p[1] 1484479Sbinkertn@umich.edu p[0] = 0 1494479Sbinkertn@umich.edu 1504479Sbinkertn@umich.edu def p_error(self, p): 1514479Sbinkertn@umich.edu print "Syntax error at '%s'" % p.value 1524479Sbinkertn@umich.edu 1534479Sbinkertn@umich.eduif __name__ == '__main__': 1544479Sbinkertn@umich.edu calc = Calc() 1554479Sbinkertn@umich.edu calc.run() 156