tutorial tomassetti intellij español csharp antlr antlr3 antlr4

tomassetti - ¿Qué es un "predicado semántico" en ANTLR?



antlr4 tutorial (2)

¿Qué es un predicado semántico en ANTLR?


ANTLR 4

Para los predicados en ANTLR 4, revisa estas preguntas y respuestas de desbordamiento de pila :

ANTLR 3

Un predicado semántico es una forma de aplicar reglas extra (semánticas) sobre acciones gramaticales usando código simple.

Hay 3 tipos de predicados semánticos:

  • validar predicados semánticos;
  • predicados semánticos cerrados ;
  • desambiguación de predicados semánticos.

Ejemplo de gramática

Digamos que tiene un bloque de texto que consiste en solo números separados por comas, ignorando los espacios en blanco. Le gustaría analizar esta entrada asegurándose de que los números sean como mucho 3 dígitos "largos" (como máximo 999). La siguiente gramática ( Numbers.g ) haría una cosa así:

grammar Numbers; // entry point of this parser: it parses an input string consisting of at least // one number, optionally followed by zero or more comma''s and numbers parse : number ('','' number)* EOF ; // matches a number that is between 1 and 3 digits long number : Digit Digit Digit | Digit Digit | Digit ; // matches a single digit Digit : ''0''..''9'' ; // ignore spaces WhiteSpace : ('' '' | ''/t'' | ''/r'' | ''/n'') {skip();} ;

Pruebas

La gramática se puede probar con la siguiente clase:

import org.antlr.runtime.*; public class Main { public static void main(String[] args) throws Exception { ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89"); NumbersLexer lexer = new NumbersLexer(in); CommonTokenStream tokens = new CommonTokenStream(lexer); NumbersParser parser = new NumbersParser(tokens); parser.parse(); } }

.java generando el lexer y el analizador, compilando todos los archivos .java y ejecutando la clase Main :

java -cp antlr-3.2.jar org.antlr.Tool Numbers.g javac -cp antlr-3.2.jar *.java java -cp .:antlr-3.2.jar Main

Al hacerlo, no se imprime nada en la consola, lo que indica que nada salió mal. Intenta cambiar:

ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");

dentro:

ANTLRStringStream in = new ANTLRStringStream("123, 456, 7777 , 89");

y vuelva a hacer la prueba: verá un error aparecer en la consola justo después de la cadena 777 .

Predicados semánticos

Esto nos lleva a los predicados semánticos. Digamos que quiere analizar números de 1 a 10 dígitos de longitud. Una regla como:

number : Digit Digit Digit Digit Digit Digit Digit Digit Digit Digit | Digit Digit Digit Digit Digit Digit Digit Digit Digit /* ... */ | Digit Digit Digit | Digit Digit | Digit ;

se volvería engorroso. Los predicados semánticos pueden ayudar a simplificar este tipo de regla.

1. Validación de predicados semánticos

Un predicado semántico de validación no es más que un bloque de código seguido de un signo de interrogación:

RULE { /* a boolean expression in here */ }?

Para resolver el problema anterior utilizando un predicado semántico de validación , cambie la regla de number en la gramática en:

number @init { int N = 0; } : (Digit { N++; } )+ { N <= 10 }? ;

Las partes { int N = 0; } { int N = 0; } y { N++; } { N++; } son declaraciones planas de Java, de las cuales la primera se inicializa cuando el analizador "ingresa" la regla number . El predicado real es: { N <= 10 }? , lo que hace que el analizador arroje una FailedPredicateException cuando un número tiene más de 10 dígitos de longitud.

ANTLRStringStream usando el siguiente ANTLRStringStream :

// all equal or less than 10 digits ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");

que no produce ninguna excepción, mientras que el siguiente hace una excepción:

// ''12345678901'' is more than 10 digits ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");

2. Predicados semánticos cerrados

Un predicado semántico con compuerta es similar a un predicado semántico de validación , solo la versión con compuerta produce un error de sintaxis en lugar de una FailedPredicateException .

La sintaxis de un predicado semántico cerrado es:

{ /* a boolean expression in here */ }?=> RULE

Para resolver el problema anterior utilizando predicados bloqueados para hacer coincidir números de hasta 10 dígitos, escribiría:

number @init { int N = 1; } : ( { N <= 10 }?=> Digit { N++; } )+ ;

Pruébelo de nuevo con ambos:

// all equal or less than 10 digits ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");

y:

// ''12345678901'' is more than 10 digits ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");

y verá que el último encendido arrojará un error.

3. La desambiguación de predicados semánticos

El tipo final de predicado es un predicado semántico de desambiguación , que se parece un predicado de validación ( {boolean-expression}? ), Pero actúa más como un predicado semántico cerrado (no se emite ninguna excepción cuando la expresión booleana se evalúa como false ). Puede usarlo al comienzo de una regla para verificar algunas propiedades de una regla y permitir que el analizador coincida con dicha regla o no.

Digamos que la gramática de ejemplo crea tokens Number (una regla lexer en lugar de una regla de analizador) que coincidirá con los números en el rango de 0..999. Ahora en el analizador sintáctico, le gustaría hacer una distinción entre números bajos y altos (bajo: 0..500, alto: 501 ... 999). Esto se puede hacer usando un predicado semántico de desambiguación donde se inspecciona el token siguiente en la secuencia ( input.LT(1) ) para verificar si es bajo o alto.

Una demostración

grammar Numbers; parse : atom ('','' atom)* EOF ; atom : low {System.out.println("low = " + $low.text);} | high {System.out.println("high = " + $high.text);} ; low : {Integer.valueOf(input.LT(1).getText()) <= 500}? Number ; high : Number ; Number : Digit Digit Digit | Digit Digit | Digit ; fragment Digit : ''0''..''9'' ; WhiteSpace : ('' '' | ''/t'' | ''/r'' | ''/n'') {skip();} ;

Si ahora analiza la cadena "123, 999, 456, 700, 89, 0" , verá el siguiente resultado:

low = 123 high = 999 low = 456 high = 700 low = 89 low = 0


Siempre utilicé la referencia breve a predicados ANTLR en wincent.com como mi guía.