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