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

Tarea #1 [solución]

Calculadora en C# o Java

      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. Además, escriba su implementación en el lenguaje Java o en C#.

// 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__)         // Definida para Borland C++
    #if (__BORLANDC__ <= 0x0410)  // Versión v3.1 o inferior
        #include <bool.h>         // Define tipo bool
    #endif
#endif

#include "Astring.h"  // class string

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

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

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

inline long Pila::Pop() {
    _top--;
    return _vec[_top];
}


class Compilador {
public:
    Compilador(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();     //  num ==> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
    void Trabaje();
    void error(const char * msg);

private:
    char   preAnalisis; // siguiente token

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

    size_t  _cursor;    // posición en _infijo

};  // Compilador


void Compilador::Trabaje() {
/*  resultado
    Traduce a notación posfija la expresión almancenada
    en *this.
    - Para evaluarla, hay que invocar a Compilador::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] == ' ') { // ignorar blancos al inicio
        _cursor++;
    }

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

void Compilador::error(const char * msg) {
/*  resultado
    Graba un mensaje de error y cancela el programa.
*/
    if (msg != 0) {
        if (msg[0] != 0) {
            cout << "ERROR: " << msg;
        }
        else {
            cout << "ERROR";
        }
    }
    else {
        cout << "ERROR";
    }
    exit(1); // cancela el programa
}

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

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

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

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

void Compilador::r2() {
//  r2 ==> * factor r2
//  r2 ==> / factor r2
//  r2 ==> £

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

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

    if (preAnalisis == '(') {
        aparea();
        expr();

        if (preAnalisis == ')' ) {
            aparea();
        }
    } else if (isdigit(preAnalisis)) {
        num();
    } else { //
        error("El factor no es paréntesis ni dígito");
    }
}

void Compilador::aparea() {
/*  resultado
    Consume el terminal, y avanza al siguiente lexema.
*/
    if (preAnalisis != _infijo[_cursor]) {
        error(0);
    }

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

void Compilador::num() {
//  num ==> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
/*  resultado
    Este método sirve para reconocer números.
*/
    if (isdigit(preAnalisis)) {
        {{ _posfijo += preAnalisis; }}
        aparea();
        num();
    }
}

long Compilador::Evaluar() {
/*  resultado
    Evalúa la expresión contenida en "*this"
*/
    Pila 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 {
                error("No se puede dividir entre cero");
            }
        }
    }
    return P.Pop();
}

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

    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;

    return 0;
}

// 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: 2 semanas
Modalidad: Individual

Soluciones

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