Home | History | Annotate | Download | only in classcalc
      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 
     12 import sys
     13 sys.path.insert(0, "../..")
     14 
     15 if sys.version_info[0] >= 3:
     16     raw_input = input
     17 
     18 import ply.lex as lex
     19 import ply.yacc as yacc
     20 import os
     21 
     22 
     23 class 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 
     60 class 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 
    163 if __name__ == '__main__':
    164     calc = Calc()
    165     calc.run()
    166