1#!/usr/bin/env python
2
3# -----------------------------------------------------------------------------
4# calc.py
5#
6# A simple calculator with variables.   This is from O'Reilly's
7# "Lex and Yacc", p. 63.
8#
9# Class-based example contributed to PLY by David McNab.
10#
11# Modified to use new-style classes.   Test case.
12# -----------------------------------------------------------------------------
13
14import sys
15sys.path.insert(0, "../..")
16
17if sys.version_info[0] >= 3:
18    raw_input = input
19
20import ply.lex as lex
21import ply.yacc as yacc
22import os
23
24
25class Parser(object):
26    """
27    Base class for a lexer/parser that has the rules defined as methods
28    """
29    tokens = ()
30    precedence = ()
31
32    def __init__(self, **kw):
33        self.debug = kw.get('debug', 0)
34        self.names = {}
35        try:
36            modname = os.path.split(os.path.splitext(__file__)[0])[
37                1] + "_" + self.__class__.__name__
38        except:
39            modname = "parser" + "_" + self.__class__.__name__
40        self.debugfile = modname + ".dbg"
41        self.tabmodule = modname + "_" + "parsetab"
42        # print self.debugfile, self.tabmodule
43
44        # Build the lexer and parser
45        lex.lex(module=self, debug=self.debug)
46        yacc.yacc(module=self,
47                  debug=self.debug,
48                  debugfile=self.debugfile,
49                  tabmodule=self.tabmodule)
50
51    def run(self):
52        while 1:
53            try:
54                s = raw_input('calc > ')
55            except EOFError:
56                break
57            if not s:
58                continue
59            yacc.parse(s)
60
61
62class Calc(Parser):
63
64    tokens = (
65        'NAME', 'NUMBER',
66        'PLUS', 'MINUS', 'EXP', 'TIMES', 'DIVIDE', 'EQUALS',
67        'LPAREN', 'RPAREN',
68    )
69
70    # Tokens
71
72    t_PLUS = r'\+'
73    t_MINUS = r'-'
74    t_EXP = r'\*\*'
75    t_TIMES = r'\*'
76    t_DIVIDE = r'/'
77    t_EQUALS = r'='
78    t_LPAREN = r'\('
79    t_RPAREN = r'\)'
80    t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
81
82    def t_NUMBER(self, t):
83        r'\d+'
84        try:
85            t.value = int(t.value)
86        except ValueError:
87            print("Integer value too large %s" % t.value)
88            t.value = 0
89        # print "parsed number %s" % repr(t.value)
90        return t
91
92    t_ignore = " \t"
93
94    def t_newline(self, t):
95        r'\n+'
96        t.lexer.lineno += t.value.count("\n")
97
98    def t_error(self, t):
99        print("Illegal character '%s'" % t.value[0])
100        t.lexer.skip(1)
101
102    # Parsing rules
103
104    precedence = (
105        ('left', 'PLUS', 'MINUS'),
106        ('left', 'TIMES', 'DIVIDE'),
107        ('left', 'EXP'),
108        ('right', 'UMINUS'),
109    )
110
111    def p_statement_assign(self, p):
112        'statement : NAME EQUALS expression'
113        self.names[p[1]] = p[3]
114
115    def p_statement_expr(self, p):
116        'statement : expression'
117        print(p[1])
118
119    def p_expression_binop(self, p):
120        """
121        expression : expression PLUS expression
122                  | expression MINUS expression
123                  | expression TIMES expression
124                  | expression DIVIDE expression
125                  | expression EXP expression
126        """
127        # print [repr(p[i]) for i in range(0,4)]
128        if p[2] == '+':
129            p[0] = p[1] + p[3]
130        elif p[2] == '-':
131            p[0] = p[1] - p[3]
132        elif p[2] == '*':
133            p[0] = p[1] * p[3]
134        elif p[2] == '/':
135            p[0] = p[1] / p[3]
136        elif p[2] == '**':
137            p[0] = p[1] ** p[3]
138
139    def p_expression_uminus(self, p):
140        'expression : MINUS expression %prec UMINUS'
141        p[0] = -p[2]
142
143    def p_expression_group(self, p):
144        'expression : LPAREN expression RPAREN'
145        p[0] = p[2]
146
147    def p_expression_number(self, p):
148        'expression : NUMBER'
149        p[0] = p[1]
150
151    def p_expression_name(self, p):
152        'expression : NAME'
153        try:
154            p[0] = self.names[p[1]]
155        except LookupError:
156            print("Undefined name '%s'" % p[1])
157            p[0] = 0
158
159    def p_error(self, p):
160        if p:
161            print("Syntax error at '%s'" % p.value)
162        else:
163            print("Syntax error at EOF")
164
165if __name__ == '__main__':
166    calc = Calc()
167    calc.run()
168