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