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