Mostrando las entradas con la etiqueta evaluador de expresiones. Mostrar todas las entradas
Mostrando las entradas con la etiqueta evaluador de expresiones. Mostrar todas las entradas

miércoles, agosto 03, 2011

Evaluador de expresiones en posfijo

Este programa presenta un pequeño evaluador de expresiones en posfijo que son leídas una a una. Se pretende con él dar una breve introducción al curso de Estructuras de Datos 2, y de paso, revisar las nuevas funcionalidades de JAVA 7, entre las cuales están el uso del operador diamante y la evaluación de cadenas de caracteres en la instrucción switch.

import javax.swing.*; 
import java.util.*;
public class Evaluador {
    public static void main(String[] args) {
        int n;  // número de cadenas a evaluar
        String respuesta = JOptionPane.showInputDialog("Cuántos elementos va a procesar" ); 
        n = Integer.parseInt(respuesta); 
        // Evaluar las cadenas
        for(int i=0; i<n; i++) {
            Stack<Double> pila = new java.util.Stack<>();   // Nuevo en JAVA 7 - Operador diamante
            // Leer expresion
            String expresion = JOptionPane.showInputDialog("Expresión a evaluar" ); 
            // Evaluar expresion
            expresion        = expresion.trim();        // Elimina espacios al comienzo y al final
            String[]     exp = expresion.split("\\s+"); // Para separar por secuencias de uno o más espacios
            for(String x: exp) {                        // Para cada cadena 'x' del arreglo exp
                try {
                    double valor = Double.parseDouble(x);
                    pila.push(valor);                   // Meter valor en la pila
                }
                catch(NumberFormatException ex) {       // Excepcion si la cadena 'x' no se pudo pasar a número
                    double b = pila.pop();
                    double a = pila.pop();
                    switch(x) {         // NUEVO EN JAVA 7 - Evaluación de cadenas en los switch
                        case "+"    :   pila.push(a+b); break;
                        case "-"    :   pila.push(a-b); break;
                        case "*"    :   pila.push(a*b); break;
                        case "/"    :   pila.push(a/b); break;
                    } // end switch
                } // end catch
            } // end for
            // Mostrar respuesta
            JOptionPane.showMessageDialog(null,pila.pop(), "Respuesta", JOptionPane.PLAIN_MESSAGE);
        } // end for
    }
}

lunes, diciembre 06, 2010

Evaluación de Expresiones en Groovy

En groovy es posible evaluar una expresión escrita en una cadena de caracteres utilizando tan sólo la instrucción "evaluate". A continuación se presenta un ejemplo que muestra un pequeño formulario utilizando las características de SwingBuilder de groovy:

import groovy.swing.SwingBuilder
import javax.swing.*
import java.awt.*

swing = new SwingBuilder()
swing.edt {
    frame(title:'Frame', defaultCloseOperation:JFrame.EXIT_ON_CLOSE, pack:true, show:true) {
        gridLayout(cols:2, rows:4)
        label("Expresion a evaluar");       JTextField txtExp  = textField("")
        label("Valor de la variable X   "); JTextField txtVal  = textField("")
        label("Respuesta");                 JTextField txtResp = textField("")
        button(
            text:'Ok',
            actionPerformed: {
                x = Double.parseDouble(txtVal.getText())
                X = x
                txtResp.setText(evaluate(txtExp.getText())+"")
            }
        )
    }
}

Para probar el funcionamiento basta con escribir una expresión en java como por ejemplo: Math.pow(x,2)-3*x+1, un valor para la variable x y darle click al botón "Ok"

miércoles, noviembre 10, 2010

Gramática de Lenguaje con Acciones Semánticas en ANTLR

La siguiente gramática en AntLR permite reconocer un pequeño lenguaje de programación que implementa desde el punto de vista léxico, números (enteros o reales positivos), variables y cadenas de caracteres.

Desde el punto de vista sintáctico, los programas se ven como una lista de instrucciones que, a su vez, pueden ser del tipo asignación, escritura, condicional, iterativa, de incremento o de decremento. La asignación de variables se hace sobre expresiones algebraicas que permiten el uso de variables, números, sumas, restas, multiplicaciones, divisiones y potencias, al igual que subexpresiones entre paréntesis.

Desde el punto de vista semántico, esta gramática traduce el texto recibido en un conjunto de instrucciones para ser ejecutado por una máquina virtual.

@header{
 package analizadores;
 import java.util.Vector;
 import java.util.HashSet;
}
@lexer::header{
 package analizadores;
}


// FUNCIONES PROPIAS DEL LENGUAJE
@members {
 Vector< String > vectorSalida=new Vector< String >();
 HashSet< String > variablesInicializadas=new HashSet< String >();
 
 public void escribir(String mensaje) {
  vectorSalida.add(mensaje);
 }
 public String getSalida() {
  String salida="";
  for(String x: vectorSalida) {
   salida+=(x+"\n");
  }
  return salida;
 }
 String errores="";
 public void emitErrorMessage(String mensaje) {
  errores+=(mensaje+"\n");
 }
 public String getErrores() { return errores; }
}

// REGLAS GRAMATICALES O SINTACTICAS
programa    : instruccion+ { System.out.println(getSalida()); };

instruccion   : v=VARIABLE '=' expresion ';'   { escribir("store \t"+$v.text); variablesInicializadas.add($v.text); } 
      | WRITE elemento (',' elemento)* ';' { escribir("writeln"); }
      |  condicional
      | iterativa
      | incremento
      | decremento
      ;
      
condicional   : IF '(' condicion ')' { int linea1 = vectorSalida.size(); escribir("jeq"); }
         bloque      { int linea2 = vectorSalida.size(); escribir("jump"); }
                { vectorSalida.set(linea1, "jeq\t" + (linea2+2)) ; }
         ( ELSE bloque )?  { vectorSalida.set(linea2, "jump\t" + (vectorSalida.size()+1)); }
      ;
      
iterativa   : WHILE       { int linea1 = vectorSalida.size(); }
        '('condicion ')'   { int linea2 = vectorSalida.size(); escribir("jeq"); }
        bloque      { escribir("jump\t"+ (linea1+1)); vectorSalida.set(linea2, "jeq\t"+(vectorSalida.size()+1));}
      ;
      
bloque    : instruccion 
      | '{' instruccion* '}'
      ;      
      
elemento    : expresion { escribir("write"); }
      | s=STRING  { escribir("write\t"+ $s.text); }
      ;      
      
expresion   : termino ( '+' termino {escribir("add");}
                             | '-' termino {escribir("sub");})* 
                  ;
                                  
termino    : factor ( '*' factor  {escribir("mul");}
          | '/' factor  {escribir("div");})* 
          ;
          
factor    : valor ( '^' valor {escribir("exp");} )*
      ;          
          
valor     : a=NUMERO      { escribir("push \t"+ $a.text); }
      | v=VARIABLE     { if(variablesInicializadas.contains($v.text)) escribir("push \t"+ $v.text); 
                 else emitErrorMessage($v.line+", "+$v.getCharPositionInLine()+": Variable no inicializada: " + $v.text);}
      | '(' expresion ')'
      ;

condicion   : expresion ( '>=' expresion { escribir("ge"); }
           | '<=' expresion { escribir("le"); }
           | '>'  expresion  { escribir("gt"); }
           | '<'  expresion  { escribir("lt"); }
           | '==' expresion  { escribir("eq"); }
           | '!=' expresion  { escribir("ne"); }
           ) ;

incremento   : v=VARIABLE '++' ';' {escribir("inc\t"+$v.text); } ;
decremento   : v=VARIABLE '--' ';' {escribir("dec\t"+$v.text); } ;

// REGLAS LEXICAS
fragment DIGITO : '0'..'9';
fragment LETRA  : 'a'..'z'|'A'..'Z';
NUMERO    : DIGITO+ '.'? DIGITO* ;
WRITE     : 'write'|'escriba'|'imprima'|'print';
IF      : 'if'|'si';
ELSE     : 'sino'|'else';
WHILE     : 'while'|'mientras'|'mientras que';
VARIABLE    : LETRA ( LETRA|DIGITO)* ;
ESPACIO    : (' '|'\t'|'\r'|'\n')  { skip(); } ;

COMMENT    :   '//' ~('\n'|'\r')* '\r'? '\n'      {$channel=HIDDEN;}
         |   '/*' ( options {greedy=false;} :  . )* '*/' {$channel=HIDDEN;}
         ;

STRING    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'  ;

fragment ESC_SEQ :  '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
      |   UNICODE_ESC
      |   OCTAL_ESC
      ;
fragment UNICODE_ESC
      : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
      ;
fragment HEX_DIGIT: ('0'..'9'|'a'..'f'|'A'..'F') ;
fragment OCTAL_ESC: '\\' ('0'..'3') ('0'..'7') ('0'..'7')
      |  '\\' ('0'..'7') ('0'..'7')
      |   '\\' ('0'..'7')
      ;
OTROCARACTER  : .        { System.out.println("error"); } ;

En una próxima entrada se estará publicando el cógido en AntLR también de la máquina virtual y las instrucciones para compilar, ejecutar y probar el lenguaje.

domingo, octubre 17, 2010

Evaluador de Funciones con Constantes

En este versión del evaluador de funciones en Java se ha agregado la funcionalidad de usar las constantes definidas en la clase java.lang.Math (PI y E).

En el método main de ejemplo se crea una función que calcula el área de un círculo utilizando el valor de PI:


import java.util.*;
import java.util.regex.*;
import java.lang.reflect.*;

public class Expresion {
    enum TipoToken  {   NUMERO, VARIABLE, FUNCION, ADD, SUB, MUL, DIV,
                        EXP, P_IZQ, P_DER, ERROR };
    class Token {
        TipoToken tipo;
        String    texto;
        Token(TipoToken ti, String te) { tipo=ti; texto=te;}
        Token(TipoToken ti) { tipo = ti; }
    }
    Queue<Token>    colaTokens;
    String          cadenaFuncion;
    double          variableX;

    Expresion(String c) throws Exception {
        cadenaFuncion = c;
    }

    private void generarTokens() throws Exception {
        colaTokens = new LinkedList<Token>();
        StringBuffer entrada   = new StringBuffer(cadenaFuncion);
        Pattern pNumero = Pattern.compile("\\-?\\d+(\\.\\d+)?");
        Pattern pID     = Pattern.compile("\\p{Alpha}\\w+");
        Pattern pFuncion= Pattern.compile("\\p{Alpha}\\w+\\s+\\(");
        while(entrada.length()>0) {
            Matcher      m  = pNumero.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.NUMERO,m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            if(entrada.charAt(0) == 'x' || entrada.charAt(0) == 'X') {
                colaTokens.add(new Token(TipoToken.VARIABLE,"x"));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '+') {
                colaTokens.add(new Token(TipoToken.ADD));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '-') {
                colaTokens.add(new Token(TipoToken.SUB));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '*') {
                colaTokens.add(new Token(TipoToken.MUL));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '/') {
                colaTokens.add(new Token(TipoToken.DIV));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '(') {
                colaTokens.add(new Token(TipoToken.P_IZQ));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == ')') {
                colaTokens.add(new Token(TipoToken.P_DER));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '^') {
                colaTokens.add(new Token(TipoToken.EXP));
                entrada.deleteCharAt(0);
                continue;
            }
            m = pFuncion.matcher(entrada);
            if(m.lookingAt()) {
                String cadena = m.group();
                cadena = cadena.split("\\s+\\(")[0].trim();
                colaTokens.add(new Token(TipoToken.FUNCION, cadena));
                continue;
            }
            m = pID.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.VARIABLE, m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            throw new Exception("Elemento no reconocido en la entrada: "
                                + entrada.charAt(0));
        }
    }

    private double evaluar(double x) throws Exception {
        generarTokens();
        variableX = x;
        return expresion();
    }

    private double expresion() {
        double respuesta=termino();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case ADD:   colaTokens.remove();
                            respuesta+=termino();
                            continue;
                case SUB:   colaTokens.remove();
                            respuesta-=termino();
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double termino() {
        double respuesta=factor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case MUL:   colaTokens.remove();
                            respuesta*=factor();
                            continue;
                case DIV:   colaTokens.remove();
                            respuesta/=factor();
                            continue;
                default:
            }
            break;
        }
        return respuesta;
    }

    private double factor() {
        double respuesta=valor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case EXP:   colaTokens.remove();
                            respuesta=Math.pow(respuesta,valor());
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double valor() {
        Token token;
        try {
            double respuesta = 0;
            token     = colaTokens.poll();
            switch(token.tipo) {
                case P_IZQ:     respuesta = expresion();
                                leaToken(TipoToken.P_DER);
                                return respuesta;
                case NUMERO:    return Double.parseDouble(token.texto);
                case VARIABLE:  if(token.texto.toLowerCase().equals("x")) {
                                    return variableX;
                                }
                                Field f = java.lang.Math.class.
                                        getField(token.texto);
                                return f.getDouble(null);
                case FUNCION:   double argumento=expresion();
                                leaToken(TipoToken.P_DER);
                                Method m = java.lang.Math.class.
                                           getMethod(token.texto, Double.TYPE);
                                return (Double) m.invoke(null, argumento);
            }
            return respuesta;
        }
        catch(Exception ex) {
            System.err.println("Error: " + ex.getMessage());
            System.exit(0);
        }
        return 0;
    }

    private boolean leaToken(TipoToken t) {
        Token token = colaTokens.poll();
        if(token.tipo.equals(t)) {
            return true;
        }
        else {
            System.err.println("Error: elemento no permitido " + token.texto );
            return false;
        }
    }

    public static void main(String[] args) throws Exception {
        String funcion = "PI*x^2" ;
        Expresion  exp = new Expresion(funcion);
        for(int x=0; x<=10; x++) {
            System.out.println(x + " -> " + exp.evaluar(x));
        }
    }
}

domingo, octubre 10, 2010

Evaluador simple de funciones en java

Escribir un programa para evaluar una función que sea ingresada por el usuario como una cadena de caracteres es una labor que exige utilizar los pasos principales del proceso de compilación: análisis léxico, análisis sintáctico y análisis semántico.

En el programa en java que se transcribe a continuación, se define una clase llamada Expresion que puede ser incorporada fácilmente a cualquier otro programa, pues no requiere clases especiales diferentes a las ya existentes en la máquina virtual de java. Se requiere tan solo un uso reducido del paquete java.util.regex para el manejo de expresiones regulares, y del paquete java.lang.reflect para la invoación de métodos de la clase java.lang.Math como parte de la función ingresada por el usuario.

Los objetos de la clase Expresion se crean con una cadena que representa la función, la cual podría ser, por ejemplo "x^3-2*x^2+1", o incluso utilizar funciones matemáticas de la clase java.lang.Math que reciban un solo valor como parámetro, por lo que la función x*cos(x)^2 sería completamente válida.

import java.util.*;
import java.util.regex.*;
import java.lang.reflect.*;

public class Expresion {
    enum TipoToken  {   NUMERO, VARIABLE, FUNCION, ADD, SUB, MUL, DIV,
                        EXP, P_IZQ, P_DER, ERROR };
    class Token {
        TipoToken tipo;
        String    texto;
        Token(TipoToken ti, String te) { tipo=ti; texto=te;}
        Token(TipoToken ti) { tipo = ti; }
    }
    Queue<Token>    colaTokens;
    String          cadenaFuncion;
    double          variable;

    Expresion(String c) throws Exception {
        cadenaFuncion = c;
    }

    private void generarTokens() throws Exception {
        colaTokens = new LinkedList<Token>();
        StringBuffer entrada   = new StringBuffer(cadenaFuncion);
        Pattern pNumero = Pattern.compile("\\-?\\d+(\\.\\d+)?");
        Pattern pID     = Pattern.compile("\\p{Alpha}\\w+");
        while(entrada.length()>0) {
            Matcher      m  = pNumero.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.NUMERO,m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            if(entrada.charAt(0) == 'x' || entrada.charAt(0) == 'X') {
                colaTokens.add(new Token(TipoToken.VARIABLE,"x"));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '+') {
                colaTokens.add(new Token(TipoToken.ADD));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '-') {
                colaTokens.add(new Token(TipoToken.SUB));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '*') {
                colaTokens.add(new Token(TipoToken.MUL));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '/') {
                colaTokens.add(new Token(TipoToken.DIV));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '(') {
                colaTokens.add(new Token(TipoToken.P_IZQ));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == ')') {
                colaTokens.add(new Token(TipoToken.P_DER));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '^') {
                colaTokens.add(new Token(TipoToken.EXP));
                entrada.deleteCharAt(0);
                continue;
            }
            m = pID.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.FUNCION, m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            throw new Exception("Elemento no reconocido en la entrada: " 
                                 + entrada.charAt(0));
        }
    }

    private double evaluar(double x) throws Exception {
        generarTokens();
        variable = x;
        return expresion();
    }

    private double expresion() {
        double respuesta=termino();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case ADD:   colaTokens.remove();
                            respuesta+=termino();
                            continue;
                case SUB:   colaTokens.remove();
                            respuesta-=termino();
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double termino() {
        double respuesta=factor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case MUL:   colaTokens.remove();
                            respuesta*=factor();
                            continue;
                case DIV:   colaTokens.remove();
                            respuesta/=factor();
                            continue;
                default:
            }
            break;
        }
        return respuesta;
    }

    private double factor() {
        double respuesta=valor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case EXP:   colaTokens.remove();
                            respuesta=Math.pow(respuesta,valor());
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double valor() {
        Token token;
        try {
            double respuesta = 0;
            token     = colaTokens.poll();
            switch(token.tipo) {
                case P_IZQ:     respuesta = expresion();
                                leaToken(TipoToken.P_DER);
                                return respuesta;
                case NUMERO:    return Double.parseDouble(token.texto);
                case VARIABLE:  return variable;
                case FUNCION:   leaToken(TipoToken.P_IZQ);
                                double argumento=expresion();
                                leaToken(TipoToken.P_DER);
                                Method m = java.lang.Math.class.
                                           getMethod(token.texto, Double.TYPE);
                                return (Double) m.invoke(null, argumento);
            }
            return respuesta;
        }
        catch(Exception ex) {
            System.err.println("Error: "  + ex.getMessage());
            System.exit(0);
        }
        return 0;
    }

    private boolean leaToken(TipoToken t) {
        Token token = colaTokens.poll();
        if(token.tipo.equals(t)) {
            return true;
        }
        else {
            System.err.println("Error: elemento no permitido "  + token.texto );
            return false;
        }
    }

    public static void main(String[] args) throws Exception {
        String funcion = "x*cos(x)^2";
        Expresion  exp = new Expresion(funcion);
        for(int x=0; x<=10; x++  ) {
            System.out.println(exp.evaluar(x));
        }
    }
}

El método main es una muestra del uso que se le puede dar a esta clase. Es importante tener en cuenta que sólo acepta funciones definidas con la variable 'x' y que las funciones matemáticas de la clase java.lang.Math aceptadas son las que reciben un solo parametro como argumento. Sin embargo, es válido utilizar por ejemplo la fución "sin(cos(x^2+1))", ya que cada una de las funciones está recibiendo como argumento una función o expresión evaluable de un solo argumento.

jueves, octubre 07, 2010

Uso de reflexión para evaluar funciones matemáticas

Continuando con el uso del paquete java.lang.reflect, en este programa se muestra una forma fácil de invocar una función cualquiera de la clase java.lang.Math solicitada por el usuario en tiempo de ejecución.

El programa pregunta por pantalla la expresión matemática y, haciendo uso de la funcionalidad de expresiones regulares de java, verifica que la expresión esté bien escrita, que el método solicitado esté en la clase java.Math y que el argumento entre paréntesis sea un número.


import java.lang.reflect.*;
import static javax.swing.JOptionPane.*;


public class TestReflect {
    public static void main(String[] args) {
        ejecutarMetodoMatematico();
    }

    public static void ejecutarMetodoMatematico(){
        String cadena;
        String[] elem;
        boolean  valido=false;
        do {
            cadena = showInputDialog("Función matematica");
            if(cadena==null) break;
            if(!cadena.matches("\\w \\s*\\(\\s*\\-?\\d (\\.\\d )?\\s*\\)\\s*")){
                mostrarError("Cadena no permitida");
                continue;
            }
            elem   = cadena.split("\\(|\\)");
            elem[0] = elem[0].trim();
            elem[1] = elem[1].trim();
            if(elem.length!=2) {
                mostrarError("Cadena no valida");
                continue;
            }
            Class c = java.lang.Math.class;
            Method m;
            try {
                m = c.getMethod(elem[0], Double.TYPE);
            }
            catch(NoSuchMethodException x) {
                mostrarError("Metodo '" elem[0] "' no permitido");
                continue;
            }
            if(!elem[1].matches("\\-?\\d (\\.\\d*)?")) {
                mostrarError("Numero '" elem[1] "' no es valido");
                continue;
            }
            valido = true;
            double num = Double.parseDouble(elem[1]);
            try {
                Object respuesta = m.invoke(null, num);
                mostrarRespuesta(cadena " = "  respuesta);
            }
            catch(IllegalAccessException x) { x.printStackTrace(); }
            catch(InvocationTargetException x) { x.printStackTrace(); }
        } while(!valido);
    }

    public static void mostrarError(String msg) {
        showMessageDialog(null, msg, "Error", ERROR_MESSAGE);
    }

    public static void mostrarRespuesta(String msg) {
        showMessageDialog(null, msg, "Solución", PLAIN_MESSAGE);
    }
}

lunes, octubre 04, 2010

Evaluador de expresiones aritméticas en AntLR

Esta gramática en ANTLR y escrita utilizando el paquete AntLRWorks.jar permite evaluar expresiones aritméticas que incluyan operaciones de suma, resta, multiplicación, división y exponenciación de número enteros y reales. Se permiten igualmente las expresiones anidadas entre paréntesis.

Desde el punto de vista léxico se define el componente NUMERO formado a partir del componente DIGITO para reconocer solamente números positivos enteros o reales; se ignoran los espacios con la regla ESPACIO, y los demás caracteres no permitidos generarán un error de ejecución con el componente RESTO ya que éste no aparece en ninguna regla gramatical.

Desde el punto de vista gramatical y semántico, se define una regla principal (s) que direcciona a una expresion y muestra al final el resultado de la misma. Las demás reglas se encargan de definir los diferentes componentes gramaticales: expresión, término, factor y valor. Las reglas semánticas se encargan de ir haciendo las operaciones a medida que se van traduciendo.


grammar evaluador1;

// Componentes léxicos
fragment DIGITO : '0'..'9' ;
NUMERO    : DIGITO+ '.'? DIGITO* ;
ESPACIO   : ( ' ' | '\t' ) {$channel=HIDDEN;}   ;
RESTO     : . ;

// Componentes sintácticos y semánticos
// s: componente principal
s        :  a=exp { System.out.println($a.vlr); }
        ;
// exp: expresion
exp returns[double vlr] : a=ter {$exp.vlr = $a.vlr; }
         ( '+' b=ter { $exp.vlr+=$b.vlr; }
         | '-' c=ter { $exp.vlr-=$c.vlr; }
         )*;
// ter: termino
ter returns[double vlr] : a=fac {$ter.vlr = $a.vlr; }
         ( '*' b=fac { $ter.vlr*=$b.vlr; }
         | '/' c=fac { $ter.vlr/=$c.vlr; }
         )*;
// fac: factor
fac returns[double vlr] : a=val { $fac.vlr = $a.vlr; }
         ( '^' b=val { $fac.vlr=Math.pow($fac.vlr,$b.vlr); }
         )*;
// valor
val returns[double vlr] : n=NUMERO { $val.vlr = Double.parseDouble($n.text); } 
        | '(' exp ')' 
        ;


Esta gramática queda grabada en el archivo "evaluador1.g". Para generar los programas en JAVA se puede utilizar la opción de generar código existente en la herramienta AntLRWorks o por línea de comandos utilizando la instrucción:

java org.antlr.Tool evaluador1.g

Si es necesario se debe incluir en el classpath la ruta donde está instalado antlr, o en su defecto el mismo archivo de AntLRWorks puede servir, en cuyo caso la instrucción quedaría así:

java -cp c:\algunaruta\antlrworks.jar org.antlr.Tool evaluador1.g

Cualquiera sea el método utilizado, se generarán dos archivos: evaluador1Lexer.java y evaluador1Parser.java, los cuales contienen respectivamente los analizadores léxicos y sintácticos generados a partir de la gramática especificada.

Para correr la gramática, se puede hacer desde un programa en java que cargue adecuadamente las librerías de antlr. Este ejemplo muestra un programa muy simple que lee por consola una expresión aritmética y devuelve el resultado:


import org.antlr.runtime.*;
import java.io.*;

public class TestEvaluador1 {
 public static void main(String args[]) throws Exception {
  Console consola = System.console();
  String  mensaje = "Escriba una expresion aritmetica"; 
  String pregunta = "";

  if(consola!=null) {
   consola.printf(mensaje);
   pregunta = consola.readLine();
  }
  else {
   pregunta = javax.swing.JOptionPane.showInputDialog(mensaje);
  }

  evaluador1Lexer      lex = new evaluador1Lexer(
        new ANTLRStringStream(pregunta));
  CommonTokenStream tokens = new CommonTokenStream(lex);
  evaluador1Parser    gram = new evaluador1Parser(tokens);

  gram.s();
 }
}

Multiprocesamiento recursivo en JAVA 7

Una de las estrategias de diseño de algoritmos más comunes es la de "divide y vencerás", en la cual, un problema de tamaño relativ...