Universidad de Costa Rica
Escuela de Ciencias de la
Computación e Informática
Profesor Adolfo Di Mare
CI-1201
I Semestre 2001
[<=] [home] [<>] [\/] [=>]
CI-1201 Programación II

Tarea #4 [solución]

La calculadora de mega-números

      La primera tarea programada de los estudiantes del curso CI-1322 Autómatas y Compiladores de este semestre consistió en implementar una calculadora para expresiones aritméticas con paréntesis:
      http://anubis.ecci.ucr.ac.cr/~ci1322/2001-1/ac-ta-1.htm

      Su trabajo consiste en tomar la calculadora que implementaron sus compañeros del curso de compiladores y luego reprogramarla usando los números super enormes que implementó en la tarea programada anterior. Para facililtar su trabajo, use como base alguna de las dos soluciones que los asistentes del curso han preparado:
      http://anubis.ecci.ucr.ac.cr/~e962047/Calculadora.zip
      http://anubis.ecci.ucr.ac.cr/~e972856

      Después de terminar su trabajo, instale la documentación en Internet como lo hizo en las tareas anteriores, y envíe su trabajo a los asistentes del curso por correo electrónico. No se olvide de entregar en clase la documentación impresa de su trabajo.

[mailto:] Kenneth Mora y Tomás Rodriguez

Tiempo de entrega: 10 días
Modalidad: En parejas

// Parser.cpp  (C) adolfo@di-mare.com
//   - Como base puede usar esta código, pero debe
//     eliminarle los errores de compilación.

using namespace std; // ANSII

#include "stdafx.h" // class CString
#include "string"   // class string

class Pila {
    Pila() { _top = 0; }
    void   P.Push(double d);
    double P.Pop();
    double Top() { return vector[_top-1]; }
private;
    static const Capacidad = 132;
    int    _top;        // tope de la pila
    double vector[Capacidad]; // vector para la pila
}; // Pila

inline void Pila::P.Push(double d) {
    vector[_top] = d;
    _top++;
}

inline double Pila::P.Pop() {
    _top--;
    return vector[_top];
}


class Expression {
public:
    void Trabaje(const CString& exp);
    Expression() : P() , infix("") {}

private:
                       //  expr ==> term r1
    void expr();       //  r1 ==> + term r1
    void r1();         //  r1 ==> - term r1
    void r2();         //
    void term();       //  term ==> factor r2
    void factor();     //  r2 ==> * factor r2
    void match();      //  r2 ==> / factor r2
    void Evaluar();    //
                       //  factor ==> ( expr )
                       //  factor ==>   num
    void num();        //
                       //  num ==> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

    void OnConvertir();
    OnPad();
    OnBorrar();
    OnReset();

private;
    char   lookahead;

    CString infix;    // hilera inicial
    CString posfix;   // hilera resultado

    Pila P;
    bool error;
    int totPuntos;    // para controlar que no entre más de un punto
    int k_infix;     // k_infix para accesar el arreglo infix
    int k_posfix;    // puntero de la k_posfixición actual de infix
};  // Expression


void Expression::Trabaje(const char *exp) {
/*  resultado
    Calcula el resultado de evaluar la expresión "exp"
    y lo almacena en un campo de la clase.
    - Para recuperar el valor calculado, hay que invocar a
      Expression::Posfija().
*/
/*  requiere
    - "exp" no debe tener errores de sintaxis.
*/
    infix = exp;
    if (infix == "") {    // No se digitó ninguna expresión
        cout << "Debe digitar una expresión infix válida\n";
        return -1;
    }
    posfix = "";
    k_infix  = 0;
    k_posfix = 0;
    error = false;
    totPuntos = 0;

    while (infix[infix] == ' ') { // ignorar blancos al inicio
        k_infix++;
        k_posfix++;
    }

    lookahead = infix[k_infix]; //iniciar lookahead
    expr();    //reconocer la expresión infix
    if (!error) {
       Evaluar();    // evaluar la expresión posfix
    }
    else {
        posfix = "";
    }
}

void Expression::expr() {
//  expr ==> term r1
    if (!error) {
        term();
        r1();
    }
}

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

    if (!error) {
        if (lookahead == '+') {
            match();
            term();
            posfix += '+';
            r1();
        } else if (lookahead == '-') {
            match();
            term();
            posfix += '-';
            r1();
        }
    }
}

void Expression::r2() {
//  r2 ==> * factor r2
//  r2 ==> / factor r2
    if (!error) {
        if (lookahead == '*') {
            match();
            factor();
            posfix += '*';
            r2();
        } else if (lookahead == '/') {
            match();
            factor();
            posfix += '/';
            r2();
        }
    }
}


void Expression::term() {
//  term ==> factor r2
    if (!error) {
        factor();
        r2();
    }
}

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

    if (!error) {
        if (lookahead == '(') {
            match();
            if (k_posfix >= strlen(infix)) {
                // si no se ha terminado la hilera infix
                AfxMessageBox("ERROR: Los parétesis no coinciden");
                m_CtrlInfija.SetFocus();
                error = true;
            } else {
                expr();
            }
            if (lookahead == ')' ) {
                match();
            } else { //No se puso el paréntesis que cierra.
                if (!error) {
                    AfxMessageBox("ERROR: Los parétesis no coinciden");
                    m_CtrlInfija.SetFocus();
                    error = true;
                }
            }
        } else if (isdigit(lookahead)) {
            if (posfix != "") posfix += ' ';
            totPuntos = 0;
            num();
        } else { //No es paréntesis ni dígito
            if (!error) {
                AfxMessageBox("Expresión no válida");
                m_CtrlInfija.SetFocus();
                error = true;
            }
        }
    }
}

void Expression::match() {
    if (!error) {
        k_posfix++;
        if (k_posfix < strlen(infix)) {
            // si no se ha terminado la hilera infix

            lookahead = infix[++k_infix];
            while (infix[k_infix] == ' ') { //ignorar blancos
                lookahead = infix[++k_infix];
                k_posfix++;
            }
        }
    }
}


void Expression::Evaluar() {
// Evalúa la expresión que estú en "this->posfix"
    int i = 0;
    double op1, op2;
    CString s;

    while (i < strlen(posfix)) {  // recorrer toda la expresión
        if (isdigit(posfix[i])) { // si es un dígito meterlo en la pila
            // reconocer números de más de un dígito
            while ( (i < strlen(posfix)) && (posfix[i] != ' ')
                   && (posfix[i] != '*') && (posfix[i] != '/')
                   && (posfix[i] != '+') && (posfix[i] != '-')  )
            {
              // los dígitos de cada número se agregan en s
                   s += posfix[i++];
            }
            P.Push(atof(s));    // convertir s a double y meterlo en la pila
            s = "";
        } else if (posfix[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
            i++;
        } else if (posfix[i] == '-') { // Si es - resta
            op1 = P.Pop();
            op2 = P.Pop();
            P.Push(op2 - op1);  // lo mete en la pila
            i++;
        } else if (posfix[i] == '*') {
            op1 = P.Pop();
            op2 = P.Pop();
            P.Push(op2 * op1);
            i++;
        } else if (posfix[i] == '/') {
            op1 = P.Pop();
            op2 = P.Pop();
            if (op1 != 0) { // para no dividir entre 0
                P.Push(op2 / op1);
            } else {
                AfxMessageBox("ERROR: No se puede dividir entre cero");
                m_CtrlInfija.SetFocus();
                error = true;
            }
            i++;
        }
        if ( (i < strlen(posfix)) && (posfix[i] == ' ')) i++;
    }
    if (!error) m_fresultado = vector[0];
}

void Expression::num() {
/*  resultado
    Este método sirve para reconocer números.
    Regla: num -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
*/
    if (!error) {
        if (isdigit(lookahead)) {
            posfix += lookahead;
            match();
            if (k_posfix < strlen(infix)) {
                num();
            }
        } else if ( (lookahead == '.') ) {    //punto decimal
            if (totPuntos > 0) {
                AfxMessageBox("Expresión no válida");
                m_CtrlInfija.SetFocus();
                error = true;
            } else {
                totPuntos++;
                posfix += lookahead;
                match();
                if (k_posfix < strlen(infix)) {
                    num();
                }
            }
        } else if (lookahead == ',') { //para aceptar comas
            if ( (strlen(infix)-k_posfix > 3)
                 && isdigit(infix[ k_infix+1 ])
                 && isdigit(infix[ k_infix+2 ])
                 && isdigit(infix[ k_infix+3 ]) )
            {
                match();
                num();
            } else {
                AfxMessageBox("ERROR: Expresión no válida");
                m_CtrlInfija.SetFocus();
                error = true;
            }
        }
    }
}


void Expression::OnPad() {
    UpdateData(true);
    CWnd *pWnd = GetFocus();  // Puntero al objeto que tiene el foco
    int m_IdWnd = pWnd->GetDlgCtrlID();

    if (m_IdWnd == IDC_ABRE) {
        infix += '(';
    } else if (m_IdWnd == IDC_CIERRA) {
        infix += ')';
    } else if (m_IdWnd == IDC_CERO) {
        infix += '0';
    } else if (m_IdWnd == IDC_PUNTO) {
        infix += '.';
    } else if (m_IdWnd == IDC_COMA) {
        infix += ',';
    } else if (m_IdWnd == IDC_MAS) {
        infix += '+';
    } else if (m_IdWnd == IDC_MENOS) {
        infix += '-';
    } else if (m_IdWnd == IDC_DIV) {
        infix += '/';
    } else if (m_IdWnd == IDC_POR) {
        infix += '*';
    } else if (m_IdWnd == IDC_UNO) {
        infix += '1';
    } else if (m_IdWnd == IDC_DOS) {
        infix += '2';
    } else if (m_IdWnd == IDC_TRES) {
        infix += '3';
    } else if (m_IdWnd == IDC_CUATRO) {
        infix += '4';
    } else if (m_IdWnd == IDC_CINCO) {
        infix += '5';
    } else if (m_IdWnd == IDC_SEIS) {
        infix += '6';
    } else if (m_IdWnd == IDC_SIETE) {
        infix += '7';
    } else if (m_IdWnd == IDC_OCHO) {
        infix += '8';
    } else if (m_IdWnd == IDC_NUEVE) {
        infix += '9';
    }
    UpdateData(false);
}

void Expression::OnReset() {
    posfix = "";
    infix = "";
    m_fresultado = 0;
    UpdateData(false);
    m_CtrlInfija.SetFocus();
}

void Expression::OnBorrar() {
    if (strlen(infix) > 0) {
        UpdateData(true);
        CString nueva = "";
        int l;

        for (int i = 0; i < strlen(infix)-1; i++) {
            l = infix[i];
            nueva += l;
        }
        infix = nueva;
        UpdateData(false);
    }
}

// EOF: Parser.cpp

Soluciones

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