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

Tarea #2 [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

[mailto:] Andrés Arias
Tiempo de entrega: 2 semanas
Modalidad: Individual

Soluciones

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