associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

MiniCalculadora

Receita: MiniCalculadora

Apresento aqui um código de uma mini calculadora. O intuito é demonstrar o método de parsing top-down para tratamento de gramáticas.

A gramática dessa calculadora é bem simples:

expr -> term + term | term - term | nome = expr

term -> prim * prim | prim / prim

prim -> ( expr ) | num | nome

Lá vai ela:

Código

   1 # -*- encoding: iso-8859-1 -*-
   2 
   3 """ Mini-calculadora, feita como exercício em Python """
   4 
   5 class CalcError(Exception):
   6   pass
   7 
   8 class Calc(object):
   9   """ Implementa todo o processo da calculadora, desde a leitura até o parsing. """
  10   
  11   TOK_IMPR, TOK_FIM, TOK_NOME, TOK_NUM = range(4)
  12   
  13   def __init__(self, entrada):
  14     """ Inicializa a calculadora.
  15     entrada deve ser um objeto que tenha o método read().
  16     """
  17     self.entrada = entrada
  18     self.cur_tok = Calc.TOK_IMPR
  19     self.num_value = 0.0
  20     self.str_value = ''
  21     self.values = { 'pi': 3.14156 }
  22     import re
  23     self.letras = re.compile('[a-zA-Z]')
  24     self.numeros = re.compile('[0-9\.]')
  25 
  26 
  27   def run(self):
  28     while self.cur_tok != Calc.TOK_FIM:
  29       try:
  30         valor = self.expr()
  31         if valor is not None:
  32           print valor
  33       except CalcError, msg:
  34         print "Erro:", msg
  35 
  36   def expr(self):
  37     esq = self.term()
  38     while self.cur_tok in ('+', '-', '\n'):
  39       if self.cur_tok == '+':
  40         esq += self.term()
  41       elif self.cur_tok == '-':
  42         esq -= self.term()
  43       else:
  44         break
  45     if self.cur_tok == Calc.TOK_FIM:
  46       return None
  47     return esq
  48 
  49   def term(self):
  50     esq = self.prim()
  51     while self.cur_tok in ('*', '/'):
  52       if self.cur_tok == '*':
  53         esq *= self.prim()
  54       else:
  55         v = self.prim()
  56         if v == 0.0:
  57           raise CalcError, "Divisão por Zero"
  58         else:
  59           esq /= v
  60     return esq
  61 
  62   def prim(self):
  63     self.next_token()
  64     v = 0.0
  65     if self.cur_tok == Calc.TOK_NOME:
  66       nome = self.str_value
  67       self.next_token()
  68       if self.cur_tok == '=':
  69         v = self.expr()
  70         self.values[nome] = v
  71         return v
  72       else:
  73         v = self.values.get(nome, 0.0)
  74         return v
  75     elif self.cur_tok == Calc.TOK_NUM:
  76       v = self.num_value
  77     elif self.cur_tok == '(':
  78       v = self.expr()
  79       if self.cur_tok != ')':
  80         raise CalcError, "')' esperado"
  81     elif self.cur_tok == Calc.TOK_FIM:
  82       return 0
  83     else:
  84       raise CalcError, "Token inesperado"
  85     self.next_token()
  86     return v
  87     
  88   def next_token(self):
  89     if not hasattr(self,'ch'):
  90       ch = ' '
  91       while ch in (' ', '\t'):
  92         ch = self.entrada.read(1)
  93     else:
  94       ch = self.ch
  95       
  96     if self.numeros.match(ch):
  97       pontos = ch == '.' and 1 or 0
  98       s = ''
  99       while self.numeros.match(ch):
 100         s += ch
 101         ch = self.entrada.read(1)
 102         if ch == '.':
 103           pontos += 1
 104       if pontos > 1:
 105         raise CalcError, "Valor ponto flutuante inválido"
 106       self.num_value = float(s)
 107       self.cur_tok = Calc.TOK_NUM
 108     elif self.letras.match(ch):
 109       s = ''
 110       while self.letras.match(ch):
 111         s += ch
 112         ch = self.entrada.read(1)
 113       self.str_value = s
 114       self.cur_tok = Calc.TOK_NOME
 115     elif len(ch) == 0:
 116       self.cur_tok = Calc.TOK_FIM
 117     else:
 118       self.cur_tok = ch
 119       if ch == '\n':
 120         if hasattr(self,'ch'):
 121           del self.ch
 122         return
 123       ch = sys.stdin.read(1)
 124 
 125     while ch in (' ', '\t'):
 126       ch = sys.stdin.read(1)
 127       
 128     self.ch = ch
 129 
 130 if __name__ == "__main__":
 131   import sys
 132   calc = Calc(sys.stdin)
 133   calc.run()

Exemplo de Uso

$ python calc.py
x = 1.5 + 3.0
4.5
z = x + .5
5.0

Volta para CookBook.


João Paulo F Farias