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