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