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