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