Pregunta

Me gustaría empezar con antlr, pero después de pasar unas cuantas horas revisando los ejemplos al antlr.org sitio, todavía no puede obtener una clara comprensión de la gramática al proceso de Java.

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

¿Fue útil?

Solución

Nota: : esta respuesta es para ANTLR3 ! Si usted está buscando un ANTLR4 ejemplo, entonces esto Q & a muestra cómo crear un simple analizador de expresiones, y el evaluador usando ANTLR4 .


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

Tenga en cuenta que esta gramática es sólo un uno muy básico: no maneja operadores unitarios (al menos en: -1 + 9) o decimales como 0.99 (sin un número de líderes), por citar sólo dos defectos. Esto es sólo un ejemplo que puede 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;}
    ;

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

Después de la creación de la gramática, tendrá que generar un analizador sintáctico y léxico de ella. Descargar el antlr frasco y almacenarlo en el mismo directorio que el archivo de gramática.

Ejecutar el siguiente comando en el intérprete de comandos / comando:

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

No debe producir ningún mensaje de error y los archivos ExpLexer.java , ExpParser.java y Exp.tokens ahora debe ser generada .

Para ver si todo funciona correctamente, crear 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, nada se imprime en la consola. Esto significa que el programa de análisis no encontró ningún error. Cuando se cambia "12*(5-6)" en "12*(5-6" y luego volver a compilar y ejecutarlo, no debe ser impreso el siguiente:

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

Bien, ahora queremos añadir un poco de código Java para la gramática para que el analizador realmente hace algo útil. Agregar código puede hacerse colocando { y } dentro de su gramática con algo de código Java normal dentro de él.

Pero primero: todas las reglas del analizador en el archivo de gramática deben devolver un valor doble primitiva. Puede hacerlo mediante la adición de 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 todas las reglas para devolver un valor doble. Ahora a "interactuar" con el double value valor de retorno (que no está dentro de un bloque de código Java {...} normal) desde el interior de un bloque de código, tendrá que añadir un signo de dólar en frente de 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); }
    ;

// ...

Esta es la gramática, pero ahora con el código Java ha añadido:

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 desde nuestra regla eval devuelve ahora un doble, cambiar su 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
    }
}

Una vez más (re) generar un analizador léxico analizador fresco y de su gramática (1), compilar todas las clases (2) y ejecutar 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 a la consola!

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

Buena suerte!

EDIT:

Este post muestra cómo extender el ejemplo anterior de modo que un Map<String, Double> puede estar previsto que sostiene variables en la expresión proporcionada.

Para obtener este código se trabaja con una versión actual de Antlr (junio de 2014) que tenía que hacer algunos cambios. ANTLRStringStream necesaria para convertirse en ANTLRInputStream, el retuvalor rned necesario para cambiar de parser.eval() a parser.eval().value, y necesitaba para eliminar la cláusula WS al final, porque los valores de atributos tales como $channel ya no se les permite aparecer en acciones analizadoras.

Otros consejos

antlr Mega tutorial de Gabriele Tomassetti es muy útil

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

EDIT: otros artículos útiles por Gabriele Tomassetti en antlr

Para Antlr 4 el proceso de generación de código Java es a continuación: -

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

Actualizar el nombre del jar en la ruta de clase en consecuencia.

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

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 hacer multiplicar y añadir 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 el tres partes:

  1. analizador gramatical
  2. lexer gramática
  3. importada gramática LexBasic

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

En lo personal me encontré con esta la parte más difícil de hacerlo bien. Ver 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 estas cuestiones se ha fijado como el caso de prueba https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java espectáculos.

versión 4.7.1 era ligeramente diferente: para la importación:

import org.antlr.v4.runtime.*;

para el segmento principal - en cuenta los CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top