Está en la página 1de 8

Cmo crear un

intrprete de
expresiones en
.NET?
Alberto Poblacin

Cmo crear un intrprete de expresiones en .NET?


Nivel: Intermedio-Avanzado

por Alberto Poblacin


En un artculo anterior dbamos respuesta a las preguntas de este tipo:
Deseo introducir en un TextBox una exresin tal como 5*x-3*(x+1) y que mi
programa calcule el resultado al pasarle el valor de x.

Y comentbamos que una forma de resolverlo, entre las distintas opciones


disponibles, consista en escribir un intrprete o compilador basado en un
analizador sintctico hecho por nosotros mismos. En este artculo vamos a dar unas
indicaciones generales acerca de cmo se escribe un programa de este tipo para
interpretar expresiones sencillas.
El primer paso consiste en elaborar un diagrama sintctico que sirva para
representar las expresiones que queremos evaluar. La Figura 1 representa un
diagrama muy sencillo, que define la sintaxis de las frmulas matemticas que
aceptan nmeros parntesis, las cuatro operaciones matemticas, y la variable x.

Figura 1.- Diagrama sintctico sencillo.

En este diagrama, se han encerrado en un crculo los elementos finales, que se


leen tal cual en la expresin tecleada. Normalmente se escribe un fragmento de
programa que en ingls recibe el nombre de parser (analizador), que va
devolviendo cada uno de los smbolos que componen la expresin. En el listado 1
que se acompaa ms abajo, esto est implementado en la subrutina
ObtenerSiguienteSmbolo.
Las cajas rectangulares sirven para enlazar entre s las distintas partes del diagrama,
y normalmente se traduce cada una de ellas en un mtodo de nuestro programa
(en el listado 1 llevan el mismo nombre que figura escrito dentro de cada caja).
Las flechas indican la estructura del lenguaje (qu elemento puede venir detrs de
qu otro), y a la hora de programar se convierten en una secuencia de instrucciones
llamando a otro mtodo (cuando las flechas tropiezan con una caja) o llamando al
parser para que obtenga el siguiente smbolo (cuando tropiezan con un crculo).
Cuando una flecha se bifurca en dos, el programa fuente debe contener
instrucciones para leer el siguiente smbolo, y una instruccin condicional (if o
switch) para decidir por cul de los dos caminos hay que seguir, examinando
smbolo obtenido. Por supuesto, no todos los diagramas sintcticos cumplen el
requisito de que en cada bifurcacin se pueda decidir el camino a seguir
examinando nicamente el smbolo siguiente. En los casos en que esto s que se
cumple (como el diagrama de la figura 1), podremos construir un compilador de
un solo paso sin marcha atrs, que es el tipo ms simple y el nico que vamos a
ver aqu.
Finalmente, nuestro cdigo fuente contendr las instrucciones necesarias para
hacer la operacin indicada por el diagrama (suma, resta, multiplicacin, etc.) cada
vez que pase por uno de los puntos en que se ha reunido la informacin necesaria.
Si la operacin se realiza inmediatamente al pasar por ese punto, tenemos un
intrprete; si lo que se hace es generar cdigo ejecutable que ms tarde al ser
ejecutado realice la operacin deseada, entonces lo que tenemos es un
compilador.
El programa del listado 1 contiene un intrprete de este tipo, realizado conforme
con el esquema de la figura 1. Por razones de claridad y simplicidad, no contiene
apenas nada de cdigo para el control de errores, que lgicamente habra que
aadir en un programa destinado a ser puesto en produccin.
Este diagrama es sumamente sencillo, y ha sido fcil dibujarlo en un pequeo
grfico. Cuando la sintaxis se complica ms, y el diagrama empieza a crecer, se
suele recurrir a otros tipos de notacin ms formales, tales como BNF (Bakus-Naur
Form). Aunque resulta menos claro a simple vista, se trata de un metalenguaje que
define la sintaxis exactamente igual que nuestro grfico, y sirve para escribir
nuestro intrprete o compilador de la misma manera.

Listado 1:
public class Intrprete
{
private string expresion;
private int posicion;
private Simbolo ltimoSimbolo;
private double ltimaConstante;
private Stack<double> pila;
private double valorDeX;
public Intrprete(string expresion)
{
this.expresion = expresion;
}
public double Evaluar(double x)
{
this.valorDeX = x;
this.posicion = 0;
this.pila = new Stack<double>();
ltimoSimbolo = ObtenerSiguienteSmbolo();
Expresion();
if (pila.Count<1)
throw new Exception("Error al leer de la pila");
return pila.Pop();
}
private Simbolo ObtenerSiguienteSmbolo()
{
char c;
do
{
if (posicion>=expresion.Length)
return Simbolo.FinDeLaExpresin;
c = expresion[posicion++];
}
while (c==' '); //Despreciamos los espacios en blanco
switch (c)
{
case 'x': case 'X': return Simbolo.Variable;
case '(': return Simbolo.AbrirParntesis;
case ')': return Simbolo.CerrarParntesis;
case '+': return Simbolo.Suma;
case '-': return Simbolo.Resta;
case '*': return
Simbolo.Multiplicacin;
case '/': return Simbolo.Divisin;
}
Regex re = new Regex(@"^\d+([,\.]\d+)?");
string exp = expresion.Substring(posicion-1);
if (re.IsMatch(exp))
{
Match m = re.Match(exp);
string s = m.Value;
posicion += m.Length-1;
ltimaConstante =
double.Parse(s.Replace(".",","));
return Simbolo.Constante;
}
throw new Exception(
"Simbolo no reconocido en la posicin " + posicion);
}
Contina...

private void Expresion()


{
Termino();
while (true)
{
switch (ltimoSimbolo)
{
case Simbolo.Suma:
ltimoSimbolo =
ObtenerSiguienteSmbolo();
Termino();
OperacionSuma();
break;
case Simbolo.Resta:
ltimoSimbolo =
ObtenerSiguienteSmbolo();
Termino();
OperacionResta();
break;
default: return;
}
}
}
private void Termino()
{
Factor();
while (true)
{
switch (ltimoSimbolo)
{
case Simbolo.Multiplicacin:
ltimoSimbolo =
ObtenerSiguienteSmbolo();
Factor();
OperacionMultiplicacion();
break;
case Simbolo.Divisin:
ltimoSimbolo =
ObtenerSiguienteSmbolo();
Factor();
OperacionDivision();
break;
default: return;
}
}
}
private void Factor()
{
if (ltimoSimbolo==Simbolo.AbrirParntesis)
{
ltimoSimbolo = ObtenerSiguienteSmbolo();
Expresion();
if (ltimoSimbolo!=Simbolo.CerrarParntesis)
throw new Exception("Falta ')'");
ltimoSimbolo = ObtenerSiguienteSmbolo();
}
else if (ltimoSimbolo==Simbolo.Constante)
{
OperacionConstante();
ltimoSimbolo = ObtenerSiguienteSmbolo();
}
Contina...

else if (ltimoSimbolo==Simbolo.Variable)
{
OperacionVariable();
ltimoSimbolo = ObtenerSiguienteSmbolo();
}
else
throw new Exception("Factor");
}
private void OperacionConstante()
{
pila.Push(ltimaConstante);
}
private void OperacionVariable()
{
pila.Push(valorDeX);
}
private void OperacionSuma()
{
pila.Push(pila.Pop()+pila.Pop());
}
private void OperacionResta()
{
double op2 = pila.Pop();
double op1 = pila.Pop();
pila.Push(op1-op2);
}
private void OperacionMultiplicacion()
{
pila.Push(pila.Pop()*pila.Pop());
}
private void OperacionDivision()
{
double op2 = pila.Pop();
double op1 = pila.Pop();
pila.Push(op1/op2);
}
}
enum Simbolo
{
Ninguno,
Suma, Resta, Multiplicacin, Divisin,
AbrirParntesis, CerrarParntesis,
Constante, Variable,
FinDeLaExpresin
}

Este listado contiene una clase Intrprete, que recibe la expresin a interpretar a
travs de su constructor. Para evaluar la expresin se llama al mtodo Evaluar,
que recibe el valor para la variable x que hemos previsto admitir en nuestras
expresiones. Mediante este mecanismo, se puede ir llamando repetidamente al
intrprete para que evale la expresin con distintos valores de x (por ejemplo,
para dibujar una grfica).
Aplicando procedimientos similares a los que hemos visto aqu, se pueden procesar
expresiones tan complejas como deseemos, pudiendo llegar incluso a crear un
lenguaje de programacin completo.

Acerca del autor


Alberto Poblacin lleva 27 aos desarrollando software. Ha sido reconocido por Microsoft
como MVP (Most Valuable Professional) de C#. Cuenta, entre otras, con las certificaciones
MCT, MCSE, MCDBA, MCITP, MCSD y MCPD en sus tres variantes (Desarrollador Web,
Desarrollador Windows y Desarrollador de Aplicaciones Empresariales). En la actualidad se
dedica principalmente a la formacin, asesoramiento y desarrollo de aplicaciones. Es tutor
de campusMVP.

Acerca de campusMVP
CampusMVP te ofrece la mejor formacin en tecnologa Microsoft a travs de nuestros
cursos online y nuestros libros especializados, impartidos y escritos por conocidos MVP de
Microsoft. Visita nuestra pgina y prueba nuestros cursos y libros gratuitamente. wwwcampusmvp.com

Reconocimiento - NoComercial - CompartirIgual (by-nc-sa):


No se permite un uso comercial de este documento ni de las posibles obras
derivadas, la distribucin de las cuales se debe hacer con una licencia igual a la que
regula esta obra original. Se debe citar la fuente.

También podría gustarte