• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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