calc.py revision 6498
1360SN/A#!/usr/bin/env python
21458SN/A
3360SN/A# -----------------------------------------------------------------------------
4360SN/A# calc.py
5360SN/A#
6360SN/A# A simple calculator with variables.   This is from O'Reilly's
7360SN/A# "Lex and Yacc", p. 63.
8360SN/A#
9360SN/A# Class-based example contributed to PLY by David McNab.
10360SN/A#
11360SN/A# Modified to use new-style classes.   Test case.
12360SN/A# -----------------------------------------------------------------------------
13360SN/A
14360SN/Aimport sys
15360SN/Asys.path.insert(0,"../..")
16360SN/A
17360SN/Aif sys.version_info[0] >= 3:
18360SN/A    raw_input = input
19360SN/A
20360SN/Aimport ply.lex as lex
21360SN/Aimport ply.yacc as yacc
22360SN/Aimport os
23360SN/A
24360SN/Aclass Parser(object):
25360SN/A    """
26360SN/A    Base class for a lexer/parser that has the rules defined as methods
272665Ssaidi@eecs.umich.edu    """
282665Ssaidi@eecs.umich.edu    tokens = ()
292665Ssaidi@eecs.umich.edu    precedence = ()
30360SN/A
31360SN/A
321354SN/A    def __init__(self, **kw):
331354SN/A        self.debug = kw.get('debug', 0)
34360SN/A        self.names = { }
352764Sstever@eecs.umich.edu        try:
362764Sstever@eecs.umich.edu            modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__
372064SN/A        except:
38360SN/A            modname = "parser"+"_"+self.__class__.__name__
39360SN/A        self.debugfile = modname + ".dbg"
40360SN/A        self.tabmodule = modname + "_" + "parsetab"
41360SN/A        #print self.debugfile, self.tabmodule
42360SN/A
43360SN/A        # Build the lexer and parser
441354SN/A        lex.lex(module=self, debug=self.debug)
45360SN/A        yacc.yacc(module=self,
461809SN/A                  debug=self.debug,
471809SN/A                  debugfile=self.debugfile,
481809SN/A                  tabmodule=self.tabmodule)
493113Sgblack@eecs.umich.edu
503113Sgblack@eecs.umich.edu    def run(self):
511999SN/A        while 1:
52360SN/A            try:
533113Sgblack@eecs.umich.edu                s = raw_input('calc > ')
542474SN/A            except EOFError:
55360SN/A                break
562462SN/A            if not s: continue
571354SN/A            yacc.parse(s)
582474SN/A
592680Sktlim@umich.edu
602474SN/Aclass Calc(Parser):
612474SN/A
621354SN/A    tokens = (
63360SN/A        'NAME','NUMBER',
64360SN/A        'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS',
65360SN/A        'LPAREN','RPAREN',
66360SN/A        )
67360SN/A
68360SN/A    # Tokens
69360SN/A
70360SN/A    t_PLUS    = r'\+'
71378SN/A    t_MINUS   = r'-'
721450SN/A    t_EXP     = r'\*\*'
733114Sgblack@eecs.umich.edu    t_TIMES   = r'\*'
74360SN/A    t_DIVIDE  = r'/'
75360SN/A    t_EQUALS  = r'='
76360SN/A    t_LPAREN  = r'\('
77360SN/A    t_RPAREN  = r'\)'
78360SN/A    t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
79360SN/A
80360SN/A    def t_NUMBER(self, t):
81360SN/A        r'\d+'
82360SN/A        try:
832680Sktlim@umich.edu            t.value = int(t.value)
84360SN/A        except ValueError:
85360SN/A            print("Integer value too large %s" % t.value)
86360SN/A            t.value = 0
87360SN/A        #print "parsed number %s" % repr(t.value)
88360SN/A        return t
89360SN/A
90360SN/A    t_ignore = " \t"
91360SN/A
92360SN/A    def t_newline(self, t):
93360SN/A        r'\n+'
94360SN/A        t.lexer.lineno += t.value.count("\n")
953114Sgblack@eecs.umich.edu
96360SN/A    def t_error(self, t):
97360SN/A        print("Illegal character '%s'" % t.value[0])
98360SN/A        t.lexer.skip(1)
99360SN/A
100360SN/A    # Parsing rules
101360SN/A
102360SN/A    precedence = (
103360SN/A        ('left','PLUS','MINUS'),
104360SN/A        ('left','TIMES','DIVIDE'),
105360SN/A        ('left', 'EXP'),
106360SN/A        ('right','UMINUS'),
107360SN/A        )
108360SN/A
109360SN/A    def p_statement_assign(self, p):
110360SN/A        'statement : NAME EQUALS expression'
111360SN/A        self.names[p[1]] = p[3]
112360SN/A
113360SN/A    def p_statement_expr(self, p):
114360SN/A        'statement : expression'
115360SN/A        print(p[1])
116360SN/A
1172400SN/A    def p_expression_binop(self, p):
118360SN/A        """
1192461SN/A        expression : expression PLUS expression
120360SN/A                  | expression MINUS expression
121360SN/A                  | expression TIMES expression
122360SN/A                  | expression DIVIDE expression
123360SN/A                  | expression EXP expression
124360SN/A        """
125360SN/A        #print [repr(p[i]) for i in range(0,4)]
1262400SN/A        if p[2] == '+'  : p[0] = p[1] + p[3]
127360SN/A        elif p[2] == '-': p[0] = p[1] - p[3]
1282461SN/A        elif p[2] == '*': p[0] = p[1] * p[3]
129360SN/A        elif p[2] == '/': p[0] = p[1] / p[3]
130360SN/A        elif p[2] == '**': p[0] = p[1] ** p[3]
131360SN/A
132360SN/A    def p_expression_uminus(self, p):
133360SN/A        'expression : MINUS expression %prec UMINUS'
134360SN/A        p[0] = -p[2]
135360SN/A
136360SN/A    def p_expression_group(self, p):
137360SN/A        'expression : LPAREN expression RPAREN'
138360SN/A        p[0] = p[2]
139360SN/A
140360SN/A    def p_expression_number(self, p):
141360SN/A        'expression : NUMBER'
142360SN/A        p[0] = p[1]
143360SN/A
144360SN/A    def p_expression_name(self, p):
145360SN/A        'expression : NAME'
146360SN/A        try:
147360SN/A            p[0] = self.names[p[1]]
148360SN/A        except LookupError:
149360SN/A            print("Undefined name '%s'" % p[1])
150360SN/A            p[0] = 0
151360SN/A
152360SN/A    def p_error(self, p):
153360SN/A        if p:
154360SN/A            print("Syntax error at '%s'" % p.value)
155360SN/A        else:
156360SN/A            print("Syntax error at EOF")
157360SN/A
158360SN/Aif __name__ == '__main__':
159360SN/A    calc = Calc()
160360SN/A    calc.run()
161502SN/A