calc.py (3940B)
1#!/usr/bin/env python 2 3# ----------------------------------------------------------------------------- 4# calc.py 5# 6# A simple calculator with variables. This is from O'Reilly's 7# "Lex and Yacc", p. 63. 8# 9# Class-based example contributed to PLY by David McNab 10# ----------------------------------------------------------------------------- 11 12import sys 13sys.path.insert(0,"../..") 14 15import readline 16import ply.lex as lex 17import ply.yacc as yacc 18import os 19 20class Parser: 21 """ 22 Base class for a lexer/parser that has the rules defined as methods 23 """ 24 tokens = () 25 precedence = () 26 27 def __init__(self, **kw): 28 self.debug = kw.get('debug', 0) 29 self.names = { } 30 try: 31 modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__ 32 except: 33 modname = "parser"+"_"+self.__class__.__name__ 34 self.debugfile = modname + ".dbg" 35 self.tabmodule = modname + "_" + "parsetab" 36 #print self.debugfile, self.tabmodule 37 38 # Build the lexer and parser 39 lex.lex(module=self, debug=self.debug) 40 yacc.yacc(module=self, 41 debug=self.debug, 42 debugfile=self.debugfile, 43 tabmodule=self.tabmodule) 44 45 def run(self): 46 while 1: 47 try: 48 s = raw_input('calc > ') 49 except EOFError: 50 break 51 if not s: continue 52 yacc.parse(s) 53 54 55class Calc(Parser): 56 57 tokens = ( 58 'NAME','NUMBER', 59 'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS', 60 'LPAREN','RPAREN', 61 ) 62 63 # Tokens 64 65 t_PLUS = r'\+' 66 t_MINUS = r'-' 67 t_EXP = r'\*\*' 68 t_TIMES = r'\*' 69 t_DIVIDE = r'/' 70 t_EQUALS = r'=' 71 t_LPAREN = r'\(' 72 t_RPAREN = r'\)' 73 t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' 74 75 def t_NUMBER(self, t): 76 r'\d+' 77 try: 78 t.value = int(t.value) 79 except ValueError: 80 print "Integer value too large", t.value 81 t.value = 0 82 #print "parsed number %s" % repr(t.value) 83 return t 84 85 t_ignore = " \t" 86 87 def t_newline(self, t): 88 r'\n+' 89 t.lexer.lineno += t.value.count("\n") 90 91 def t_error(self, t): 92 print "Illegal character '%s'" % t.value[0] 93 t.lexer.skip(1) 94 95 # Parsing rules 96 97 precedence = ( 98 ('left','PLUS','MINUS'), 99 ('left','TIMES','DIVIDE'), 100 ('left', 'EXP'), 101 ('right','UMINUS'), 102 ) 103 104 def p_statement_assign(self, p): 105 'statement : NAME EQUALS expression' 106 self.names[p[1]] = p[3] 107 108 def p_statement_expr(self, p): 109 'statement : expression' 110 print p[1] 111 112 def p_expression_binop(self, p): 113 """ 114 expression : expression PLUS expression 115 | expression MINUS expression 116 | expression TIMES expression 117 | expression DIVIDE expression 118 | expression EXP expression 119 """ 120 #print [repr(p[i]) for i in range(0,4)] 121 if p[2] == '+' : p[0] = p[1] + p[3] 122 elif p[2] == '-': p[0] = p[1] - p[3] 123 elif p[2] == '*': p[0] = p[1] * p[3] 124 elif p[2] == '/': p[0] = p[1] / p[3] 125 elif p[2] == '**': p[0] = p[1] ** p[3] 126 127 def p_expression_uminus(self, p): 128 'expression : MINUS expression %prec UMINUS' 129 p[0] = -p[2] 130 131 def p_expression_group(self, p): 132 'expression : LPAREN expression RPAREN' 133 p[0] = p[2] 134 135 def p_expression_number(self, p): 136 'expression : NUMBER' 137 p[0] = p[1] 138 139 def p_expression_name(self, p): 140 'expression : NAME' 141 try: 142 p[0] = self.names[p[1]] 143 except LookupError: 144 print "Undefined name '%s'" % p[1] 145 p[0] = 0 146 147 def p_error(self, p): 148 print "Syntax error at '%s'" % p.value 149 150if __name__ == '__main__': 151 calc = Calc() 152 calc.run()