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