calc.py revision 6498
12SN/A#!/usr/bin/env python
21762SN/A
32SN/A# -----------------------------------------------------------------------------
42SN/A# calc.py
52SN/A#
62SN/A# A simple calculator with variables.   This is from O'Reilly's
72SN/A# "Lex and Yacc", p. 63.
82SN/A#
92SN/A# Class-based example contributed to PLY by David McNab.
102SN/A#
112SN/A# Modified to use new-style classes.   Test case.
122SN/A# -----------------------------------------------------------------------------
132SN/A
142SN/Aimport sys
152SN/Asys.path.insert(0,"../..")
162SN/A
172SN/Aif sys.version_info[0] >= 3:
182SN/A    raw_input = input
192SN/A
202SN/Aimport ply.lex as lex
212SN/Aimport ply.yacc as yacc
222SN/Aimport os
232SN/A
242SN/Aclass Parser(object):
252SN/A    """
262SN/A    Base class for a lexer/parser that has the rules defined as methods
272665SN/A    """
282665SN/A    tokens = ()
292SN/A    precedence = ()
302SN/A
312SN/A
322SN/A    def __init__(self, **kw):
332SN/A        self.debug = kw.get('debug', 0)
342SN/A        self.names = { }
352SN/A        try:
3611263Sandreas.sandberg@arm.com            modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__
3711263Sandreas.sandberg@arm.com        except:
382SN/A            modname = "parser"+"_"+self.__class__.__name__
392SN/A        self.debugfile = modname + ".dbg"
402SN/A        self.tabmodule = modname + "_" + "parsetab"
4111263Sandreas.sandberg@arm.com        #print self.debugfile, self.tabmodule
422SN/A
432SN/A        # Build the lexer and parser
442SN/A        lex.lex(module=self, debug=self.debug)
452SN/A        yacc.yacc(module=self,
462SN/A                  debug=self.debug,
472SN/A                  debugfile=self.debugfile,
484981SN/A                  tabmodule=self.tabmodule)
492SN/A
502SN/A    def run(self):
514981SN/A        while 1:
522SN/A            try:
532SN/A                s = raw_input('calc > ')
542SN/A            except EOFError:
554981SN/A                break
564981SN/A            if not s: continue
572SN/A            yacc.parse(s)
582SN/A
594981SN/A
604981SN/Aclass Calc(Parser):
614981SN/A
622SN/A    tokens = (
634981SN/A        'NAME','NUMBER',
641152SN/A        'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS',
652SN/A        'LPAREN','RPAREN',
662SN/A        )
671152SN/A
682566SN/A    # Tokens
691152SN/A
702566SN/A    t_PLUS    = r'\+'
714419SN/A    t_MINUS   = r'-'
724419SN/A    t_EXP     = r'\*\*'
734419SN/A    t_TIMES   = r'\*'
742SN/A    t_DIVIDE  = r'/'
752SN/A    t_EQUALS  = r'='
7611263Sandreas.sandberg@arm.com    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] == '+'  : p[0] = p[1] + p[3]
127        elif p[2] == '-': p[0] = p[1] - p[3]
128        elif p[2] == '*': p[0] = p[1] * p[3]
129        elif p[2] == '/': p[0] = p[1] / p[3]
130        elif p[2] == '**': p[0] = p[1] ** p[3]
131
132    def p_expression_uminus(self, p):
133        'expression : MINUS expression %prec UMINUS'
134        p[0] = -p[2]
135
136    def p_expression_group(self, p):
137        'expression : LPAREN expression RPAREN'
138        p[0] = p[2]
139
140    def p_expression_number(self, p):
141        'expression : NUMBER'
142        p[0] = p[1]
143
144    def p_expression_name(self, p):
145        'expression : NAME'
146        try:
147            p[0] = self.names[p[1]]
148        except LookupError:
149            print("Undefined name '%s'" % p[1])
150            p[0] = 0
151
152    def p_error(self, p):
153        if p:
154            print("Syntax error at '%s'" % p.value)
155        else:
156            print("Syntax error at EOF")
157
158if __name__ == '__main__':
159    calc = Calc()
160    calc.run()
161