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

Tarea #1 [solución]

Calculadora en C++

      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 dígitos simples.

      Modifique este programa para que pueda procesar expresiones con números de varios dígitos.

// 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

      Luego de imprimir la documentación de su programa, y entregarla en clase, envíe su trabajo por correo electrónico. Para esto, haga un archivo empacado .zip cuyo nombre sea su número de carnet. Incluya en ese archivo lo siguiente:

  1. Un documento en formato HTML que describa el trabajo que realizó. Incluya el nombre del compilador que usó.
  2. La especificación de su programa.
  3. El código fuente de su programa de prueba.
  4. Los datos de prueba para su programa.

      Las cuentas de computador en la ECCI se asignan de acuerdo al número de carnet. Por ejemplo, si su carnet es el número 95-28-09, para entregar su tarea usted debe crear el archivo 952809.zip para enviarlo por correo electrónico.

      Luego haga en su cuenta personal un subdirectorio llamado public_html, que es bajo el que se instalan todas sus páginas Internet. Por ejemplo, si su solución está en el archivo HTML llamado "OLP/t3sol952809.htm", entonces usted debe instalar esa página en el archivo
      public_html/OLP/t3sol952809.htm
de su cuenta. Luego, para acceder esa página Internet, debe entrar a este sitio:
      http://anubis.ecci.ucr.ac.cr/~e952809/OLP/t3sol952809.htm

      Como todas las cuentas de estudiante son la letra "e" seguida del número de carnet, para el estudiante de carnet "952809" la cuenta es "e952809". Para indicarle al servidor Internet a cuál cuenta entrar se usa el caracter "~" (Alt-126), seguido del nombre de la cuenta: "~e952809".

      Después de la fecha de entrega del programa, puede usted instalar en su cuenta personal su solución (no instale antes su solución en Internet, pues en ese caso sería usted culpable de facilitar la copia de su trabajo, y en consecuencia se haría acreedor a la sanción respectiva).

[mailto:] Entrega de Tareas

Tiempo de entrega: 1 semana
Modalidad: En parejas

Soluciones

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