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