Tenga en cuenta que, dado que no pude encontrar un recurso en línea decente para publicar para alguien que quisiera saber qué es un predicado semántico , decidí publicar la pregunta aquí yo mismo (que también responderé en breve).
Bart Kiers
1
Gracias por hacer esto; Siempre me gusta cuando la gente responde sus propias preguntas, especialmente si hacen la pregunta específicamente para responderla de esta manera.
Daniel H
1
Leer el libro. El capítulo 11 de The Definitive ANTLR 4 Reference trata sobre predicados semánticos. ¿No tienes el libro? ¡Consíguelo! Vale cada dólar.
james.garriss
Respuestas:
169
ANTLR 4
Para los predicados en ANTLR 4, consulte estas preguntas y respuestas de desbordamiento de pila :
Un predicado semántico es una forma de hacer cumplir reglas adicionales (semánticas) sobre acciones gramaticales utilizando código simple.
Hay 3 tipos de predicados semánticos:
validar predicados semánticos;
predicados semánticos cerrados ;
predicados semánticos desambiguantes .
Gramática de ejemplo
Digamos que tiene un bloque de texto que consta solo de números separados por comas, ignorando los espacios en blanco. Le gustaría analizar esta entrada asegurándose de que los números tengan como máximo 3 dígitos de "longitud" (como máximo 999). La siguiente gramática ( Numbers.g) haría tal cosa:
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();
}
}
Pruébelo generando el lexer y parser, compilando todos los .javaarchivos y ejecutando la Mainclase:
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 usando un
predicado semántico de validación , cambie la numberregla en la gramática a:
number
@init { int N = 0; }
: (Digit { N++; } )+ { N <= 10 }?
;
Las partes { int N = 0; }y { N++; }son declaraciones simples de Java de las cuales la primera se inicializa cuando el analizador "ingresa" a la numberregla. El predicado real es:, { N <= 10 }?que hace que el analizador genere un
FailedPredicateException
siempre que un número tenga más de 10 dígitos.
Pruébelo utilizando lo siguiente ANTLRStringStream:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
que no produce ninguna excepción, mientras que lo 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 cerrado es similar a un predicado semántico de validación , solo que la versión cerrada produce un error de sintaxis en lugar de un FailedPredicateException.
La sintaxis de un predicado semántico cerrado es:
{ /* a boolean expression in here */ }?=> RULE
En su lugar, para resolver el problema anterior utilizando predicados cerrados 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 en arrojará un error.
3. Desambiguando predicados semánticos
El tipo final de predicado es un predicado semántico desambiguador , que se parece un poco a un predicado de validación ( {boolean-expression}?), pero actúa más como un predicado semántico cerrado (no se lanza ninguna excepción cuando la expresión booleana se evalúa como false). Puede usarlo al comienzo de una regla para verificar alguna propiedad de una regla y dejar que el analizador coincida con dicha regla o no.
Digamos que la gramática de ejemplo crea Numbertokens (una regla de lexer en lugar de una regla de analizador) que coincidirán con números en el rango de 0..999. Ahora, en el analizador, 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 que elimine la ambigüedad en el que inspeccione el siguiente token 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
¡Sí, un excelente enlace! Pero, como mencionas, puede ser un poco difícil para alguien (relativamente) nuevo en ANTLR. Solo espero que mi respuesta sea (un poco) más amigable para el ANTLR-grass-hopper. :)
Respuestas:
ANTLR 4
Para los predicados en ANTLR 4, consulte estas preguntas y respuestas de desbordamiento de pila :
ANTLR 3
Un predicado semántico es una forma de hacer cumplir reglas adicionales (semánticas) sobre acciones gramaticales utilizando código simple.
Hay 3 tipos de predicados semánticos:
Gramática de ejemplo
Digamos que tiene un bloque de texto que consta solo de números separados por comas, ignorando los espacios en blanco. Le gustaría analizar esta entrada asegurándose de que los números tengan como máximo 3 dígitos de "longitud" (como máximo 999). La siguiente gramática (
Numbers.g
) haría tal cosa:Pruebas
La gramática se puede probar con la siguiente clase:
Pruébelo generando el lexer y parser, compilando todos los
.java
archivos y ejecutando laMain
clase:Al hacerlo, no se imprime nada en la consola, lo que indica que nada salió mal. Intente cambiar:
dentro:
y vuelva a hacer la prueba: verá un error que aparece en la consola justo después de la cadena
777
.Predicados semánticos
Esto nos lleva a los predicados semánticos. Supongamos que desea analizar números de entre 1 y 10 dígitos. Una regla como:
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:
Para resolver el problema anterior usando un predicado semántico de validación , cambie la
number
regla en la gramática a:Las partes
{ int N = 0; }
y{ N++; }
son declaraciones simples de Java de las cuales la primera se inicializa cuando el analizador "ingresa" a lanumber
regla. El predicado real es:,{ N <= 10 }?
que hace que el analizador genere unFailedPredicateException
siempre que un número tenga más de 10 dígitos.Pruébelo utilizando lo siguiente
ANTLRStringStream
:que no produce ninguna excepción, mientras que lo siguiente hace una excepción:
2. Predicados semánticos cerrados
Un predicado semántico cerrado es similar a un predicado semántico de validación , solo que la versión cerrada produce un error de sintaxis en lugar de un
FailedPredicateException
.La sintaxis de un predicado semántico cerrado es:
En su lugar, para resolver el problema anterior utilizando predicados cerrados para hacer coincidir números de hasta 10 dígitos, escribiría:
Pruébelo de nuevo con ambos:
y:
y verá que el último en arrojará un error.
3. Desambiguando predicados semánticos
El tipo final de predicado es un predicado semántico desambiguador , que se parece un poco a un predicado de validación (
{boolean-expression}?
), pero actúa más como un predicado semántico cerrado (no se lanza ninguna excepción cuando la expresión booleana se evalúa comofalse
). Puede usarlo al comienzo de una regla para verificar alguna propiedad de una regla y dejar que el analizador coincida con dicha regla o no.Digamos que la gramática de ejemplo crea
Number
tokens (una regla de lexer en lugar de una regla de analizador) que coincidirán con números en el rango de 0..999. Ahora, en el analizador, 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 que elimine la ambigüedad en el que inspeccione el siguiente token en la secuencia (input.LT(1)
) para verificar si es bajo o alto.Una demostración:
Si ahora analiza la cadena
"123, 999, 456, 700, 89, 0"
, verá el siguiente resultado:fuente
input.LT(1)
esgetCurrentToken()
ahora :-)Siempre he utilizado la breve referencia a los predicados ANTLR en wincent.com como guía.
fuente