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();
 }
}

1 comentario:

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