1# ----------------------------------------------------------------------------- 2# calc.py 3# 4# A calculator parser that makes use of closures. The function make_calculator() 5# returns a function that accepts an input string and returns a result. All 6# lexing rules, parsing rules, and internal state are held inside the function. 7# ----------------------------------------------------------------------------- 8 9import sys 10sys.path.insert(0,"../..") 11 12if sys.version_info[0] >= 3: 13 raw_input = input 14 15# Make a calculator function 16 17def make_calculator(): 18 import ply.lex as lex 19 import ply.yacc as yacc 20 21 # ------- Internal calculator state 22 23 variables = { } # Dictionary of stored variables 24 25 # ------- Calculator tokenizing rules 26 27 tokens = ( 28 'NAME','NUMBER', 29 ) 30 31 literals = ['=','+','-','*','/', '(',')'] 32 33 t_ignore = " \t" 34 35 t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' 36 37 def t_NUMBER(t): 38 r'\d+' 39 t.value = int(t.value) 40 return t 41 42 def t_newline(t): 43 r'\n+' 44 t.lexer.lineno += t.value.count("\n") 45 46 def t_error(t): 47 print("Illegal character '%s'" % t.value[0]) 48 t.lexer.skip(1) 49 50 # Build the lexer 51 lexer = lex.lex() 52 53 # ------- Calculator parsing rules 54 55 precedence = ( 56 ('left','+','-'), 57 ('left','*','/'), 58 ('right','UMINUS'), 59 ) 60 61 def p_statement_assign(p): 62 'statement : NAME "=" expression' 63 variables[p[1]] = p[3] 64 p[0] = None 65 66 def p_statement_expr(p): 67 'statement : expression' 68 p[0] = p[1] 69 70 def p_expression_binop(p): 71 '''expression : expression '+' expression 72 | expression '-' expression 73 | expression '*' expression 74 | expression '/' expression''' 75 if p[2] == '+' : p[0] = p[1] + p[3] 76 elif p[2] == '-': p[0] = p[1] - p[3] 77 elif p[2] == '*': p[0] = p[1] * p[3] 78 elif p[2] == '/': p[0] = p[1] / p[3] 79 80 def p_expression_uminus(p): 81 "expression : '-' expression %prec UMINUS" 82 p[0] = -p[2] 83 84 def p_expression_group(p): 85 "expression : '(' expression ')'" 86 p[0] = p[2] 87 88 def p_expression_number(p): 89 "expression : NUMBER" 90 p[0] = p[1] 91 92 def p_expression_name(p): 93 "expression : NAME" 94 try: 95 p[0] = variables[p[1]] 96 except LookupError: 97 print("Undefined name '%s'" % p[1]) 98 p[0] = 0 99 100 def p_error(p): 101 if p: 102 print("Syntax error at '%s'" % p.value) 103 else: 104 print("Syntax error at EOF") 105 106 107 # Build the parser 108 parser = yacc.yacc() 109 110 # ------- Input function 111 112 def input(text): 113 result = parser.parse(text,lexer=lexer) 114 return result 115 116 return input 117 118# Make a calculator object and use it 119calc = make_calculator() 120 121while True: 122 try: 123 s = raw_input("calc > ") 124 except EOFError: 125 break 126 r = calc(s) 127 if r: 128 print(r) 129 130 131