Aquí te paso el programa. Viene dividido en 8 pequeños archivos. Notarás que viene codificado en el antiguo K&R C, esto es, el C como viene descrito en el libro
El Lenguaje de Programación C de los autores
Brian Kernigham y
Dennis Ritchie, este último creador del lenguaje. En él, por ejemplo, al definir los argumentos de las funciones no especifican los tipos dentro de los paréntesis, si una función no regresa algún valor no lo especifican explícitamente con la palabra reservada
void, etc. Te menciono esto por si al correrlo en tu compilador te da errores. Tienes que correrlo en C y no en C++, como algunos compiladores lo hacen por defecto y, en caso de tener la opción, indicarle que lo vas a codificar en el modo K&R. La otra opción es modificarlo ligeramente para que siga los estándares de ahora. De hecho, para correrlo en el Dev C++, tuve que modificar el archivo de encabezado
global.h y agregarles a varias líneas la palabra reservada
extern, las cuales no vienen en el libro. Aquí simplemente creé un proyecto en modo consola en lenguaje C, quité el archivo
main.c que pone por defecto y le agregué los 8 archivos y me corrió sin problemas.
Código:
/**** global.h ****************************************************************/
#include <stdio.h> /* carga las rutinas de entrada y salida */
#include <ctype.h> /* carga las rutinas de prueba de caracteres */
#define TAMBUFF 128 /* tamaño del buffer */
#define NINGUNO -1
#define FDC '\0'
#define NUM 256
#define DIV 257
#define MOD 258
#define ID 259
#define FIN 260
extern int valcomplex; /* valor del atributo del componente léxico */
extern int numlinea;
struct entrada { /* forma del elemento de entrada de la tabla de símbolos */
char *aplex;
int complex;
};
extern struct entrada tablasimb[]; /* tabla de símbolos */
/**** analizlex.c *************************************************************/
#include "global.h"
char buflex[TAMBUFF];
int numlinea = 1;
int valcomplex = NINGUNO;
int analex () /* analizador lexico */
{
int t;
while (1) {
t = getchar ();
if (t == ' ' || t == '\t')
; /* elimina espacios en blanco */
else if (t == '\n')
numlinea = numlinea + 1;
else if (isdigit (t)) { /* t es un dígito */
ungetc (t, stdin);
scanf ("%d", &valcomplex);
return NUM;
} else if (isalpha (t)) { /* t es una letra */
int p, b = 0;
while (isalnum (t)) { /* t es alfanumérico */
buflex[b] = t;
t = getchar ();
b = b + 1;
if (b >= TAMBUFF)
error ("error del compilador");
}
buflex[b] = FDC;
if (t != EOF)
ungetc (t, stdin);
p = busca (buflex);
if (p == 0)
p = inserta (buflex, ID);
valcomplex = p;
return tablasimb[p].complex;
} else if (t == EOF)
return FIN;
else {
valcomplex = NINGUNO;
return t;
}
}
}
/**** analizsint.c ************************************************************/
#include "global.h"
int preanalisis;
analsint () /* analiza sintácticamente y traduce la lista de la expresión */
{
preanalisis = analex ();
while (preanalisis != FIN) {
expr (); parea (';');
}
}
expr ()
{
int t;
termino ();
while (1)
switch (preanalisis) {
case '+': case '-':
t = preanalisis;
parea (preanalisis); termino (); emite (t, NINGUNO);
continue;
default:
return;
}
}
termino ()
{
int t;
factor ();
while (1)
switch (preanalisis) {
case '*': case '/': case DIV: case MOD:
t = preanalisis;
parea (preanalisis); factor (); emite (t, NINGUNO);
continue;
default:
return;
}
}
factor ()
{
switch (preanalisis) {
case '(':
parea ('('); expr (); parea (')'); break;
case NUM:
emite (NUM, valcomplex); parea (NUM); break;
case ID:
emite (ID, valcomplex); parea (ID); break;
default:
error ("error de sintaxis");
}
}
parea (t)
int t;
{
if (preanalisis == t)
preanalisis = analex ();
else
error ("error de sintaxis");
}
/**** emisor.c ****************************************************************/
#include "global.h"
emite (t, tval) /* genera la salida */
int t;
int tval;
{
switch (t) {
case '+': case '-': case '*': case '/':
printf ("%c\n", t); break;
case DIV:
printf ("DIV\n"); break;
case MOD:
printf ("MOD\n"); break;
case NUM:
printf ("%d\n", tval); break;
case ID:
printf ("%s\n", tablasimb[tval].aplex); break;
default:
printf ("complex %d, valcomplex %d\n", t, tval);
}
}
/**** error.c *****************************************************************/
#include "global.h"
error (m) /* genera todos los mensajes de error */
char *m;
{
fprintf (stderr, "linea %d: %s\n", numlinea, m);
exit (1); /* terminación sin éxito */
}
/**** inic.c ******************************************************************/
#include "global.h"
struct entrada palsclave[] = {
"div", DIV,
"mod", MOD,
0, 0
};
inic () /* carga las palabras claves en la tabla de símbolos */
{
struct entrada *p;
for (p = palsclave; p->complex; p++)
inserta (p->aplex, p->complex);
}
/**** simbolos.c **************************************************************/
#include "global.h"
#define MAXLEN 999 /* tamaño de la matriz de lexemas */
#define MAXSIMB 100 /* tamaño de tablasimb */
char lexemas[MAXLEN];
int ultcar = -1; /* última posición usada en los lexemas */
struct entrada tablasimb[MAXSIMB];
int ultent = 0; /* última posición usada en tablasimb */
int busca (s) /* devuelve la posición del elemento de entrada de s */
char s[];
{
int p;
for (p = ultent; p > 0; p = p - 1)
if (strcmp (tablasimb[p].aplex, s) == 0)
return p;
return 0;
}
int inserta (s, clex) /* devuelve la posición del elemento de entrada de s */
char s[];
int clex;
{
int lon;
lon = strlen (s); /* strlen avalúa la longitud de s */
if (ultent + 1 >= MAXSIMB)
error ("tabla de simbolos llena");
if (ultcar + lon + 1 >= MAXLEN)
error ("matriz de lexemas llena");
ultent = ultent + 1;
tablasimb[ultent].complex = clex;
tablasimb[ultent].aplex = &lexemas[ultcar + 1];
ultcar = ultcar + lon + 1;
strcpy (tablasimb[ultent].aplex, s);
return ultent;
}
/**** principal.c *************************************************************/
#include "global.h"
main ()
{
inic ();
analsint ();
exit (0); /* terminación con éxito */
}
El programa simplemente convierte una expresión infija a su forma postfija. La expresión debe de estar formada por números enteros, identificadores (deben de comenzar con una letra y después pueden ser letras o números, y no pasar de TAMBUF de caracteres), y las 4 operaciones aritméticas +, -, *, /. Además permite DIV y MOD y puedes usar los paréntesis en cualquier nivel de profundidad. Las operaciones * y / tienen prioridad sobre + y -. El programa toma la cadena desde la entrada estandar y muestra el resultado en la pantalla. Debes de terminar cada expresión con punto y coma, y el programa termina cuando ve el fin de archivo. En caso de que estés usando el teclado para terminar debes de darle ^Z, o sea, manteniendo presionada la tecla Ctrl presionar también tecla Z.
La expresión infija
tiene la expresión postfija siguiente
Disponer una expresión de esta manera es muy conveniente para propósitos de evaluarla, pues puedes poner sus elementos en una estructura de datos como la pila y generar el resultado con instrucciones
pop.
Si quieres profundizar en lo de teoría de compiladores el último libro que te mencioné es indispensable. La editorial es
Addison-Wesley Iberoamericana por si te interesa buscarlo en las librerías. Otro libro que podrías conseguir es
C: Guía para usuarios expertos de
Herbert Schildt de la editorial
Osborne/McGraw-Hill. En el capítulo 8 desarrolla un intérprete de un BASIC reducido. Otro libro muy bueno, sólo que en ínglés, es
Writing Compilers & Interpreters. An Applied Approach de
Ronald Mak de la editorial
John Wiley & Sons, Inc.. En él desarrollan, a lo largo de todos sus capítulo, un intérprete de Pascal completo, implementando depuración con puntos de ruptura, puntos de observación, traza paso a paso, modificación de valores, etc. Y además desarrollan un compilador completo de Pascal con generación de código para el ensamblador 8086.