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