Saltar al contenido

ANTLR: ¿Hay un ejemplo sencillo?

Solución:

Nota: esta respuesta es para ANTLR3! Si estas buscando un ANTLR4 Por ejemplo, estas preguntas y respuestas demuestran cómo crear un analizador de expresiones simple y un evaluador utilizando ANTLR4.


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

Tenga en cuenta que esta gramática es muy básica: no maneja operadores unarios (el menos en: -1 + 9) o decimales como .99 (sin un número inicial), por nombrar solo dos deficiencias. Este es solo un ejemplo en el que puede trabajar usted 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 
         | "https://foroayuda.es/" 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 un lexer a partir de ella. Descargue el jar ANTLR y guárdelo en el mismo directorio que su archivo de gramática.

Ejecute el siguiente comando en su shell / símbolo del sistema:

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

No debería producir ningún mensaje de error y los archivos ExpLexer.java, ExpParser.java y Tokens de exp. ahora debería generarse.

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 compílelo:

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

// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java

y luego ejecútalo:

// *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 cambias "12*(5-6)" dentro "12*(5-6" y luego recompilarlo y ejecutarlo, debería 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 algo de 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. Puedes hacerlo agregando returns [double value] después de cada regla:

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

additionExp returns [double value]
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

// ...

lo que necesita poca explicación: se espera que cada regla devuelva un valor doble. Ahora para “interactuar” con el valor de retorno double value (que NO está dentro de un bloque de código Java simple {...}) desde el interior de un bloque de código, deberá agregar un signo de dólar delante 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); }
    ;

// ...

Aquí está la gramática, pero ahora con el código 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;} 
         | "https://foroayuda.es/" 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 nuestro eval La regla ahora devuelve un doble, cambie su ANTLRDemo.java a 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 parser a partir 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) impreso en su consola!

Nuevamente: esta es una explicación muy breve. Te animo a que navegues por la wiki de ANTLR y leas algunos tutoriales y / o juegues un poco con lo que acabo de publicar.

¡Buena suerte!

EDITAR:

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

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

El mega tutorial ANTLR de Gabriele Tomassetti es muy útil

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

EDITAR: otros artículos útiles de Gabriele Tomassetti en ANTLR

  • Introducción a ANTLR en C #
  • Introducción a ANTLR en C ++

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 el nombre de su jar en classpath en consecuencia.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *