Exercício Programa 7: Calculadora - Parte B

Descrição

Este é o segundo de uma série de exercícios-programas que têm como objetivo a construção de um interpretador de expressões simples em notação infixa que, como em um programa, também poderão conter variáveis. Exemplos de expressões que o interpretador executará são:

mp = (p1 + 2*p2 + 2*p3)/5
nota_final = 0.75*mp + 0.25*me
nr = (prec + nf)/2

Como pode ser notado, as expressões serão semelhantes àquelas encontradas em linguagens de programação. Assim como em Python, variáveis poderão ser criadas por meio de atribuições e reutilizadas em expressões futuras.

Python 3.4.3 (default, Mar 26 2015, 22:07:01)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 + 2
3
>>> 3.14 * 6
18.84
>>> p1 = 4
>>> p2 = 5
>>> p3 = 6
>>> mp = (p1 + 2*p2 + 2*p3)/5
>>> mp
5.2
>>> me = 6
>>> nota_final = 0.75*mp + 0.25*me
>>> nota_final
5.4
>>>
>>> nota_final = 4
>>> nota_final
4
>>> prec = 5.5
>>> nr = (prec + nota_final)/2
>>> nr
4.75
>>>

Durante o processo da construção desse interpretador aprenderemos e revisaremos vários conceitos de programação, algoritmos e estruturas de dados: orientação a objetos, pilhas, tabelas de símbolos, dicionários, etc.

Neste exercício programa você deverá fazer escrever uma classe chamada Token e adaptar a sua função tokeniza() para retornar uma lista de objetos da classe Token em vez de uma lista de objetos da forma [item,tipo]

Objetivos

  • Continuar o desenvolvimento da habilidade de resolver problemas computacionais a partir de tipos básicos como listas, strings e dicionários.
  • Utilizar novas estruturas (pilhas e filas).
  • Motivar o uso de classes de objetos na resolução de problemas.

O que você deve fazer

Primeiro, você deve baixar os arquivos main.py, operadores.py e esqueleto_tokeniza.py.

O módulo main.py contém a função main() que é a função principal do projeto (= função que coordena o programa). A função main() está completamente escrita e nada no módulo main.py deve ser alterado.

O módulo operadores.py contém um dicionário com uma descrição dos operadores aritméticos e parênteses. Esse módulo é utilizado apenas pela função main() e também não deve ser alterado.

Já o módulo tokeniza.py contém:

  • uma função de nome imprima_tokens() que está completa e não deve ser alterada;

    def imprima_tokens(tokens):
        ''' (list) -> None
    
        Recebe uma lista tokens em que cada objeto
        é da classe Token e imprime a descrição de cada
        objeto lista.
    
        Para funcionar, está função necessita que o método
        __str__() da classe Token tenha sido implementado.
        '''
    
  • o esqueleto da classe Token() que você deve fazer; e

    class Token:
        ''' Classe utilizada para representar itens léxicos.
    
        Cada objeto têm dois atributos: item e tipo.
    
        O atributo item de um objeto Token é
    
            - um float: no caso do item ser um número; ou
            - um string no caso do item ser um operador ou
                 uma variável ou um abre/fecha parenteses.
    
        O componente tipo de um objeto token indica é um inteiro
        que indica a que categoria ele pertence.
    
            - OPERADOR   = 1;
            - NUMERO     = 2;
            - VARIAVEL   = 3; ou
            - PARENTESES = 4.
        '''
    
  • o cabeçalho da função tokeniza() que você também deve fazer.

    def tokeniza(exp):
        """(str) -> list (de Tokens)
    
        Recebe uma string exp representando uma expressão e cria
        e retorna uma lista de objetos da classe Token representando
        os itens léxicos que formam a expressão.
    
        Cada item léxico é representado por um objeto Token que
        possui é formado por um atributos: item e tipo.
    
        O atributo item de um token faz referência a:
    
            - um float: no caso do item ser um número; ou
            - um string no caso do item ser um operador ou
                 uma variável ou um abre/fecha parenteses.
    
        O atributo tipo de um token faz referência à sua categoria
        (ver definição em token.py).
    
            - OPERADOR;
            - NUMERO;
            - VARIAVEL; ou
            - PARENTESES
    
        A funçao ignora tudo que esta na exp apos o caractere
        COMENTARIO (= "#").
        """
    

Sugestão de roteiro para fazer este EP

Faça o download dos arquivos do EP7 e depois escreva os métodos e função como sugeridos a seguir.

__init__()

Comece escrevendo o método __init__() que é o construtor da classe Token.

def __init__(self,item,tipo):
    '''(Token, float ou str, int) -> Token

    Construtor: cria e retorna um objeto Token.

    Magica: função retorna mas não tem return.
    '''
    print("Vixe! Ainda não fiz esse método.")

Depois de escrevê-la, execute o módulo (= Run -> Run Module) e teste-a no Python Shell.

Python 3.4.3 (default, Mar 26 2015, 22:07:01)
[GCC 4.9.2] on linux
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
>>> t1 = Token("+",OPERADOR)
>>> t1.item
'+'
>>> t1.tipo
1
>>> t2 = Token(3.14,NUMERO)
>>> t2.item
3.14
>>> t2.tipo
2
>>> t3 = Token("soma",VARIAVEL)
>>> t3.item
'soma'
>>> t3.tipo
3
>>> t4 = Token("(",PARENTESES)
>>> t4.item
'('
>>> t4.tipo
4

__str__()

Agora, escreva o método __str__()

def __str__(self):
    ''' (Token) -> str

    Retorna o string que print() usa para exibir
    um Token
    '''

O seu método __str__() deve produzir resultados iguais aos que estão a seguir.

>>>
>>> t1 = Token("+",OPERADOR)
>>> str(t1)
"O('+')"
>>> print(t1)
O('+')
>>> t2 = Token(3.14,NUMERO)
>>> str(t2)
'N(3.14)'
>>> print(t2)
N(3.14)
>>> t3 = Token("soma",VARIAVEL)
>>> str(t3)
"V('soma')"
>>> print(t3)
V('soma')
>>> t4 = Token("(",PARENTESES)
>>> str(t4)
"P('(')"
>>> print(t4)
P('(')
>>>

Depois que o seu método __str__() estiver se comportando como mostrado acima, a função imprima_tokens() poderá ser usada, pois ela utiliza a função str() para converter um objeto da classe Token``para um string. A função ``str(), para fazer o seu serviço, chama o método __str()__ que você escreveu.

>>> t1 = Token("+",OPERADOR)
>>> t2 = Token(3.14,NUMERO)
>>> t3 = Token("soma",VARIAVEL)
>>> t4 = Token("(",PARENTESES)
>>> lista_de_tokens = [t1,t2,t3,t4]
>>> imprima_tokens(lista_de_tokens)
[O('+'), N(3.14), V('soma'), P('(')]
>>>

get_item() e get_tipo()

Agora, escreva os métodos get_item() e get_tipo(). Com isso, a definição da classe Token estará completa.

def get_item(self):
    ''' (Token) -> str ou float

    Retorna o conteúdo do atributo item do Token.

    Se o atributo tipo é OPERADOR, PARENTESES OU VARIAVEL
    o item retornado será um string (class str).

    Se o atributo tipo é NUMERO o item retornado
    será um float.
    '''

def get_tipo(self):
    ''' (Token) -> int

    Retorna o conteúdo do atributo tipo do Token.

    Se o valor retornado é OPERADOR, PARENTESES,
    VARIAVEL ou NUMERO
    '''

A seguir estão exemplos que mostram como os métodos get_item() e get_tipo() devem se comportar.

>>> t1 = Token("+",OPERADOR)
>>> t1.get_item()
'+'
>>> t1.get_tipo()
1
>>> t2 = Token(3.14,NUMERO)
>>> t2.get_item()
3.14
>>> t2.get_tipo()
2
>>> t3 = Token("soma",VARIAVEL)
>>> t3.get_item()
'soma'
>>> t3.get_tipo()
3
>>> t4 = Token("(",PARENTESES)
>>> t4.get_item()
'('
>>> t4.get_tipo()
4

tokeniza()

Finalmente, você deve copiar a função tokeniza() escrita para o EP6 e adaptá-la para retornar objetos do tipo Token em vez de uma lista de objetos da forma [item,tipo].

Eis alguns exemplos de execução da “nova” função tokeniza().

>>>
>>> lista = tokeniza("+")
>>> imprima_tokens(lista)
[O('+')]
>>> lista = tokeniza("=^! - +/* %")
>>> imprima_tokens(lista)
[O('='), O('^'), O('!'), O('-'), O('+'), O('/'), O('*'), O('%')]
>>> lista = tokeniza("")
>>> imprima_tokens(lista)
[]
>>> lista = tokeniza("123")
>>> imprima_tokens(lista)
[N(123)]
>>> lista = tokeniza(".5")
>>> imprima_tokens(lista)
[N(0.5)]
>>> lista = tokeniza("12.5")
>>> imprima_tokens(lista)
[N(12.5)]
>>> lista = tokeniza("v")
>>> imprima_tokens(lista)
[V('v')]
>>> lista = tokeniza("v_1")
>>> imprima_tokens(lista)
[V('v_1')]
>>> lista = tokeniza("v_1 var nome_var")
>>> imprima_tokens(lista)
[V('v_1'), V('var'), V('nome_var')]
>>> lista = tokeniza("    ") # string com espaços
>>> imprima_tokens(lista)
[]
>>> lista = tokeniza("(()(") # string apenas com parenteses
>>> imprima_tokens(lista)
[P('('), P('('), P(')'), P('(')]
>>> lista = tokeniza("mp = (p1 + 2*p2 + 2*p3)/ 5")
>>> imprima_tokens(lista)
[V('mp'), O('='), P('('), V('p1'), O('+'), N(2), O('*'), V('p2'), O('+'), N(2), O('*'), V('p3'), P(')'), O('/'), N(5)]
>>> lista = tokeniza(" # esta string tem apenas comentários")
>>> imprima_tokens(lista)
[]
>>> lista = tokeniza(" nf = 4 # nota final < 5 indica rec")
>>> imprima_tokens(lista)
[V('nf'), O('='), N(4)]
>>>

Parte B: entrega até 05/10

A seguir esta um exemplo de execução do programa completo (= função main() do módulo main.py).

Python 3.4.3 (default, Mar 26 2015, 22:07:01)
[GCC 4.9.2] on linux
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
Entre como uma expressão ou tecle apenas ENTER para encerrar.
expressão >>> +
'+' : operador aritmético para adição
expressão >>> =^! - +/* %
'=' : operador para atribuição
'^' : operador aritmético para exponenciacao
'!' : operador aritmético 'menos unário'
'-' : operador aritmético para subtração
'+' : operador aritmético para adição
'/' : operador aritmético para divisão
'*' : operador aritmético para multiplicação
'%' : operador aritmético para resto de divisão
expressão >>> 123
123 : constante
expressão >>> .5
0.5 : constante
expressão >>> 12.5
12.5 : constante
expressão >>> v_1
'v_1' : nome de variável
expressão >>> v_1 var nome_var
'v_1' : nome de variável
'var' : nome de variável
'nome_var' : nome de variável
expressão >>>        # linha vazia
expressão >>>        # string apenas com espaços
expressão >>> (()(
'(' : abre parenteses
'(' : abre parenteses
')' : fecha parenteses
'(' : abre parenteses
expressão >>> mp = (p1 + 2*p2 + 2*p3)/ 5
'mp' : nome de variável
'=' : operador para atribuição
'(' : abre parenteses
'p1' : nome de variável
'+' : operador aritmético para adição
2 : constante
'*' : operador aritmético para multiplicação
'p2' : nome de variável
'+' : operador aritmético para adição
2 : constante
'*' : operador aritmético para multiplicação
'p3' : nome de variável
')' : fecha parenteses
'/' : operador aritmético para divisão
5 : constante
expressão >>> # esta string tem apenas comentários
expressão >>> nf = 4 # nota final < 5 indica rec
'nf' : nome de variável
'=' : operador para atribuição
4 : constante
expressão >>>
>>>