Universidad de Costa Rica
Escuela de Ciencias de la
Computación e Informática
Profesor Adolfo Di Mare
CI-1322
I Semestre 2004
[<=] [home] [<>] [\/] [=>]
CI-1322 Autómatas y compiladores

Tarea #2 [solución]

La calculadora de varios dígitos

      El programa de la Figura 1 es una calculadora que evalúa expresiones complejas, con paréntesis. Tiene la limitación de que trabaja con números de un solo dígito.

      Modifique este programa para que pueda procesar expresiones con números de varios dígitos. Limite su trabajo a los métodos "aparea()", "num()" y "Evaluar()".

// CLC.cpp  (C) adolfo@di-mare.com

/*  resultado
    Evalúa expresiones aritméticas simples en que los
    operandos son números del 0 al 9.
*/

#if defined(__BORLANDC__)  // Compilando con Borland C++
    #include <bool.h>      // Define bool para BC++ v3.1 o inferior
#endif

#include "Astring.h"       // class string

#include <iostream.h>
#include <cctype>


template <class T>
class Pila {
public:
    Pila() { _top = 0; }
    void   Push(T d);
    T      Pop();
    T      Top() { return _vec[_top-1]; }
private:
    enum { Capacidad = 132 };
    int    _top;            // tope de la pila
    T      _vec[Capacidad]; // vector para la pila
}; // Pila

template <class T>
inline void Pila<T>::Push(T d) {
    _vec[_top] = d;
    _top++;
}

template <class T>
inline T Pila<T>::Pop() {
    _top--;
    return _vec[_top];
}

typedef char Token; // OJO: Astring sólo funciona para "char"

class Calculadora {
public:
    Calculadora(const char* exp=0)    : _infijo  (exp)  { Trabaje(); }
    void operator = (const char* exp) { _infijo = exp;    Trabaje(); }
    long Evaluar();
                        //  expr ==>   term r1
private:                //  r1   ==> + term r1
    void expr();        //  r1   ==> - term r1 | 
    void r1();          //
    void term();        //  term ==>   factor r2
    void r2();          //  r2   ==> * factor r2 | 
    void factor();      //  r2   ==> / factor r2
    void num();         //
                        //  factor ==> ( expr ) | num
                        //
    void aparea(Token); //  num ==> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
    void Trabaje();
    void error(const char * msg);

private:
    Token  preAnalisis; // siguiente token

    Astring _infijo;    // hilera inicial
    Astring _posfijo;   // hilera resultado

    size_t  _cursor;    // posición en _infijo
};  // Calculadora


void Calculadora::Trabaje() {
/*  resultado
    Traduce a notación posfija la expresión almancenada
    en *this.
    - Para evaluarla, hay que invocar a Calculadora::Evaluar().
*/
/*  requiere
    - La expresión almacenada no debe tener errores de sintaxis.
*/
    _posfijo = "";
    if (_infijo == "") {    // No se digitó ninguna expresión
        return;
    }
    _cursor = 0;

    while (_infijo[_cursor] == ' ') { // ignora blancos al inicio
        _cursor++;
    }

    preAnalisis = _infijo[_cursor]; // inicializa preAnalisis
    expr();      // reconoce la expresión _infijo
} // Calculadora::Trabaje()

void Calculadora::error(const char * msg) {
/*  resultado
    Graba en "cout" un mensaje de error.
    - Indica la posición actual de proceso en al hilera de entrada.
*/
    cout << "ERROR(" << 1+_cursor << ")";
    if (msg != 0) {  // +1 porque _cursor comienza en 0
        if (msg[0] != 0) {
            cout << ": " << msg;
        }
    }
    cout << endl;
} // Calculadora::error()

void Calculadora::expr() {
//  expr ==> term r1
    term();
    r1();
} // Calculadora::expr()

void Calculadora::r1() {
//  r1 ==> + term r1
//  r1 ==> - term r1
//  r1 ==> 

    if (preAnalisis == '+') {              //  r1 ==> + term r1
        aparea('+');
        term(); {{ _posfijo += '+'; }}
        r1();
    } else if (preAnalisis == '-') {       //  r1 ==> - term r1
        aparea('-');
        term(); {{ _posfijo += '-'; }}
        r1();
    } else { }                             //  r1 ==> 
} // Calculadora::r1()

void Calculadora::term() {
//  term ==> factor r2
    factor();
    r2();
} // Calculadora::term()

void Calculadora::r2() {
//  r2 ==> * factor r2
//  r2 ==> / factor r2
//  r2 ==> 

    if (preAnalisis == '*') {              //  r2 ==> * factor r2
        aparea('*');
        factor(); {{ _posfijo += '*'; }}
        r2();
    } else if (preAnalisis == '/') {       //  r2 ==> / factor r2
        aparea('/');
        factor(); {{ _posfijo += '/'; }}
        r2();
    } else { }                             //  r2 ==> 
} // Calculadora::r2()

void Calculadora::factor() {
//  factor ==> ( expr )
//  factor ==>   num

    if (preAnalisis == '(') {              //  factor ==> ( expr )
        aparea('(');
        expr();
        aparea(')');
    } else if (isdigit(preAnalisis)) {     //  factor ==>   num
        num();
    } else {
        error("El factor no es dígito ni '('");
    }
} // Calculadora::factor()

void Calculadora::num() {
//  num ==> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

    if (isdigit(preAnalisis)) {
        {{ _posfijo += preAnalisis; }}
        aparea(preAnalisis);
    } else {
        error("El token no es dígito");
    }
} // Calculadora::num()

void Calculadora::aparea(Token ch) {
/*  resultado
    Consume el terminal, y avanza al siguiente lexema.
    - Si "ch" no coincide con el valor de "preAnalisis"
      emite un error.
*/
    if (preAnalisis != ch) {
        error("Token inesperado");
    }

    preAnalisis = _infijo[++_cursor];
    while (_infijo[_cursor] == ' ') { // ignora blancos
        preAnalisis = _infijo[++_cursor];
    }
} // Calculadora::aparea()

long Calculadora::Evaluar() {
/*  resultado
    Evalúa la expresión contenida en "*this".
*/
    Pila<long> P;       // pila usada para evaluar _posfijo
    size_t len = strlen(_posfijo.c_str());
    if (len==0) {
        return 0;
    }

    for (size_t i=0; i < len; ++i) {  // recorre toda la expresión
        long op1, op2;
        if (isdigit(_posfijo[i])) {
            // si es un dígito lo mete en la pila
            P.Push( _posfijo[i] - '0');
        } else if (_posfijo[i] == '+') { // Si es +, saca los operandos
            op1 = P.Pop();               // de la pila y los suma
            op2 = P.Pop();
            P.Push(op2 + op1); // mete el resultado intermedio en la pila
        } else if (_posfijo[i] == '-') { // Si es - resta
            op1 = P.Pop();
            op2 = P.Pop();
            P.Push(op2 - op1);  // lo mete en la pila
        } else if (_posfijo[i] == '*') {
            op1 = P.Pop();
            op2 = P.Pop();
            P.Push(op2 * op1);
        } else if (_posfijo[i] == '/') {
            op1 = P.Pop();
            op2 = P.Pop();
            if (op1 != 0) { // para no dividir entre 0
                P.Push(op2 / op1);
            } else {
                P.Push(0);
                error("División por cero");
            }
        }
    }
    return P.Pop();
} // Calculadora::Evaluar()

int main() {
    char str[200];
    Calculadora C;
    long        V;

    cout << endl << endl;
    strcpy(str, "(5 * 3)");
    C = str;
    V = C.Evaluar();
    cout << V  << " == " << str << endl;

    strcpy(str, "(1 + 2) * (3 - 4 - 5)");
    C = str;
    V = C.Evaluar();
    cout << V  << " == " << str << endl;

    strcpy(str, "(( (((((1 + 2))))) * ((((3 - 4 - 5)))) ))" );
    C = str;
    V = C.Evaluar();
    cout << V  << " == " << str << endl;

    strcpy(str, "1 / ( 3 - (2+1) )");
    C = str;

    cout << str << " == " << endl;
    V = C.Evaluar();
    cout << "== " << V  << endl;

    strcpy(str, ")" );
    cout << str << endl;
    C = str;

    strcpy(str, "1++" );
    cout << str << endl;
    C = str;

    strcpy(str, "1+2*)" );
    cout << str << endl;
    C = str;

    strcpy(str, "(1++" );
    cout << str << endl;
    C = str;

    strcpy(str, "(x +" );
    cout << str << endl;
    C = str;

    return 0;
} // main()

// EOF: CLC.cpp
Figura 1

      Entregue su tarea por correo electrónico, como lo hizo anteriormente.

[mailto:] Entrega de Tareas

Tiempo de entrega: 1 semana
Modalidad: En parejas

Soluciones

[mailto:] Adolfo Di Mare <adolfo@di-mare.com>.
Copyright © 2004
Derechos de autor reservados © 2004
[home] <> [/\]