tutorial java antlr antlr4 antlr3

java - tutorial - ANTLR: ¿Hay un ejemplo simple?



antlr runtime 3.4 jar (4)

Me gustaría comenzar con ANTLR, pero después de pasar unas horas revisando los ejemplos en el sitio de antlr.org , todavía no puedo entender con claridad el proceso de gramática a Java.

¿Hay algún ejemplo simple, algo así como una calculadora de cuatro operaciones implementada con ANTLR pasando por la definición del analizador y hasta el código fuente de Java?


En https://github.com/BITPlan/com.bitplan.antlr encontrará una biblioteca java ANTLR con algunas clases de ayuda útiles y algunos ejemplos completos. Está listo para ser usado con maven y si te gusta eclipse y maven.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/Exp.g4

es un lenguaje de expresión simple que puede multiplicar y agregar operaciones. https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java tiene las pruebas de unidad correspondientes para ello.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 es un analizador IRI que se ha dividido en las tres partes:

  1. gramática del analizador
  2. gramatica lexer
  3. Gramática LexBasic importada

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java tiene las pruebas unitarias para ello.

Personalmente, encontré que esta es la parte más difícil de hacer bien. Consulte http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

https://github.com/BITPlan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr

contiene tres ejemplos más que se han creado para un problema de rendimiento de ANTLR4 en una versión anterior. Mientras tanto, estos problemas se han solucionado como el testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java muestra.


Para Antlr 4, el proceso de generación de código java es el siguiente:

java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g

Actualice su nombre de jar en classpath en consecuencia.


Para mí, este tutorial fue muy útil: https://tomassetti.me/antlr-mega-tutorial

Tiene ejemplos de gramática, ejemplos de visitantes en diferentes idiomas (Java, JavaScript, C # y Python) y muchas otras cosas. Muy recomendable.


Primero creas una gramática. A continuación se muestra una pequeña gramática que puede usar para evaluar expresiones que se crean utilizando los 4 operadores matemáticos básicos: +, -, * y /. También puedes agrupar expresiones usando paréntesis.

Tenga en cuenta que esta gramática es solo muy básica: no maneja operadores unarios (el menos en: -1 + 9) o decimales como .99 (sin un número inicial), para mencionar solo dos defectos. Este es solo un ejemplo que puedes trabajar en ti mismo.

Aquí está el contenido del archivo de gramática Exp.g :

grammar Exp; /* This will be the entry point of our parser. */ eval : additionExp ; /* Addition and subtraction have the lowest precedence. */ additionExp : multiplyExp ( ''+'' multiplyExp | ''-'' multiplyExp )* ; /* Multiplication and division have a higher precedence. */ multiplyExp : atomExp ( ''*'' atomExp | ''/'' atomExp )* ; /* An expression atom is the smallest part of an expression: a number. Or when we encounter parenthesis, we''re making a recursive call back to the rule ''additionExp''. As you can see, an ''atomExp'' has the highest precedence. */ atomExp : Number | ''('' additionExp '')'' ; /* A number: can be an integer value, or a decimal value */ Number : (''0''..''9'')+ (''.'' (''0''..''9'')+)? ; /* We''re going to ignore all white space characters */ WS : ('' '' | ''/t'' | ''/r''| ''/n'') {$channel=HIDDEN;} ;

(Las reglas del analizador comienzan con una letra minúscula, y las reglas del lexer comienzan con una letra mayúscula)

Después de crear la gramática, querrá generar un analizador y lexer a partir de ella. Descargue el tarro de ANTLR y guárdelo en el mismo directorio que su archivo de gramática.

Ejecute el siguiente comando en su shell / comando:

java -cp antlr-3.2.jar org.antlr.Tool Exp.g

No debería generar ningún mensaje de error, y ahora deberían generarse los archivos ExpLexer.java , ExpParser.java y Exp.tokens .

Para ver si todo funciona correctamente, cree esta clase de prueba:

import org.antlr.runtime.*; public class ANTLRDemo { public static void main(String[] args) throws Exception { ANTLRStringStream in = new ANTLRStringStream("12*(5-6)"); ExpLexer lexer = new ExpLexer(in); CommonTokenStream tokens = new CommonTokenStream(lexer); ExpParser parser = new ExpParser(tokens); parser.eval(); } }

y compilarlo:

// *nix/MacOS javac -cp .:antlr-3.2.jar ANTLRDemo.java // Windows javac -cp .;antlr-3.2.jar ANTLRDemo.java

y luego ejecutarlo:

// *nix/MacOS java -cp .:antlr-3.2.jar ANTLRDemo // Windows java -cp .;antlr-3.2.jar ANTLRDemo

Si todo va bien, no se imprime nada en la consola. Esto significa que el analizador no encontró ningún error. Cuando cambie "12*(5-6)" a "12*(5-6" y luego vuelva a compilarlo y ejecutarlo, debe imprimirse lo siguiente:

line 0:-1 mismatched input ''<EOF>'' expecting '')''

Bien, ahora queremos agregar un poco de código Java a la gramática para que el analizador realmente haga algo útil. Se puede agregar código colocando { y } dentro de su gramática con algún código Java simple dentro de ella.

Pero primero: todas las reglas del analizador en el archivo de gramática deben devolver un valor doble primitivo. Puede hacerlo agregando returns [double value] después de cada regla:

grammar Exp; eval returns [double value] : additionExp ; additionExp returns [double value] : multiplyExp ( ''+'' multiplyExp | ''-'' multiplyExp )* ; // ...

que necesita poca explicación: se espera que cada regla devuelva un valor doble. Ahora, para "interactuar" con el valor de retorno del valor double value (que NO está dentro de un bloque de código Java liso {...} ) desde dentro de un bloque de código, deberá agregar un signo de dólar delante del value :

grammar Exp; /* This will be the entry point of our parser. */ eval returns [double value] : additionExp { /* plain code block! */ System.out.println("value equals: "+$value); } ; // ...

Aquí está la gramática pero ahora con el código de Java agregado:

grammar Exp; eval returns [double value] : exp=additionExp {$value = $exp.value;} ; additionExp returns [double value] : m1=multiplyExp {$value = $m1.value;} ( ''+'' m2=multiplyExp {$value += $m2.value;} | ''-'' m2=multiplyExp {$value -= $m2.value;} )* ; multiplyExp returns [double value] : a1=atomExp {$value = $a1.value;} ( ''*'' a2=atomExp {$value *= $a2.value;} | ''/'' a2=atomExp {$value /= $a2.value;} )* ; atomExp returns [double value] : n=Number {$value = Double.parseDouble($n.text);} | ''('' exp=additionExp '')'' {$value = $exp.value;} ; Number : (''0''..''9'')+ (''.'' (''0''..''9'')+)? ; WS : ('' '' | ''/t'' | ''/r''| ''/n'') {$channel=HIDDEN;} ;

y como nuestra regla eval ahora devuelve un doble, cambia tu ANTLRDemo.java en esto:

import org.antlr.runtime.*; public class ANTLRDemo { public static void main(String[] args) throws Exception { ANTLRStringStream in = new ANTLRStringStream("12*(5-6)"); ExpLexer lexer = new ExpLexer(in); CommonTokenStream tokens = new CommonTokenStream(lexer); ExpParser parser = new ExpParser(tokens); System.out.println(parser.eval()); // print the value } }

Nuevamente (re) genere un nuevo lexer y analizador de su gramática (1), compile todas las clases (2) y ejecute ANTLRDemo (3):

// *nix/MacOS java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1 javac -cp .:antlr-3.2.jar ANTLRDemo.java // 2 java -cp .:antlr-3.2.jar ANTLRDemo // 3 // Windows java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1 javac -cp .;antlr-3.2.jar ANTLRDemo.java // 2 java -cp .;antlr-3.2.jar ANTLRDemo // 3

y ahora verá el resultado de la expresión 12*(5-6) impresa en su consola.

Una vez más: esta es una explicación muy breve. Lo invito a navegar por la wiki de ANTLR y leer algunos tutoriales y / o jugar un poco con lo que acabo de publicar.

¡Buena suerte!

EDITAR:

Esta publicación muestra cómo extender el ejemplo anterior para que se pueda proporcionar un Map<String, Double> que contenga variables en la expresión provista.

Y esta sección de Q&A demuestra cómo crear un analizador de expresiones y un evaluador simples utilizando ANTLR4 .

Para que este código funcione con una versión actual de Antlr (junio de 2014), necesitaba realizar algunos cambios. ANTLRStringStream necesitaba convertirse en ANTLRInputStream , el valor devuelto necesario para cambiar de parser.eval() a parser.eval().value , y necesitaba eliminar la cláusula WS al final, porque ya no se permiten valores de atributo como $channel Para aparecer en las acciones del lexer.