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