Qu'est-ce qu'un prédicat sémantique dans ANTLR?
Qu'est-ce qu'un prédicat sémantique dans ANTLR?
Réponses:
Pour les prédicats dans ANTLR 4, consultez ces questions et réponses sur le débordement de pile :
Un prédicat sémantique est un moyen d'imposer des règles (sémantiques) supplémentaires aux actions de grammaire en utilisant du code brut.
Il existe 3 types de prédicats sémantiques:
Disons que vous avez un bloc de texte composé uniquement de chiffres séparés par des virgules, en ignorant les espaces blancs. Vous souhaitez analyser cette entrée en vous assurant que les nombres sont au plus 3 chiffres "longs" (au plus 999). La grammaire suivante ( Numbers.g
) ferait une telle chose:
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();}
;
La grammaire peut être testée avec la classe suivante:
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();
}
}
Testez-le en générant le lexer et l'analyseur, en compilant tous les .java
fichiers et en exécutant la Main
classe:
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
Ce faisant, rien n'est imprimé sur la console, ce qui indique que rien ne s'est mal passé. Essayez de changer:
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
dans:
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7777 , 89");
et refaites le test: vous verrez une erreur apparaître sur la console juste après la chaîne 777
.
Cela nous amène aux prédicats sémantiques. Supposons que vous souhaitiez analyser des nombres entre 1 et 10 chiffres. Une règle comme:
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
;
deviendrait encombrant. Les prédicats sémantiques peuvent aider à simplifier ce type de règle.
Un prédicat sémantique de validation n'est rien d'autre qu'un bloc de code suivi d'un point d'interrogation:
RULE { /* a boolean expression in here */ }?
Pour résoudre le problème ci-dessus en utilisant un
prédicat sémantique de validation , changez la number
règle de la grammaire en:
number
@init { int N = 0; }
: (Digit { N++; } )+ { N <= 10 }?
;
Les parties { int N = 0; }
et { N++; }
sont des instructions Java simples dont la première est initialisée lorsque l'analyseur "entre" la number
règle. Le prédicat réel est:, { N <= 10 }?
ce qui oblige l'analyseur à lancer un
FailedPredicateException
chaque fois qu'un nombre comporte plus de 10 chiffres.
Testez-le en utilisant ce qui suit ANTLRStringStream
:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
qui ne produit aucune exception, alors que ce qui suit fait une exception:
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
Un prédicat sémantique gated est similaire à un prédicat sémantique de validation , seule la version gated produit une erreur de syntaxe au lieu d'un FailedPredicateException
.
La syntaxe d'un prédicat sémantique gated est:
{ /* a boolean expression in here */ }?=> RULE
Pour résoudre à la place le problème ci-dessus en utilisant des prédicats fermés pour faire correspondre des nombres jusqu'à 10 chiffres, vous écririez:
number
@init { int N = 1; }
: ( { N <= 10 }?=> Digit { N++; } )+
;
Testez à nouveau avec les deux:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
et:
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
et vous verrez le dernier sur lancera une erreur.
Le dernier type de prédicat est un prédicat sémantique sans ambiguïté , qui ressemble un peu à un prédicat de validation ( {boolean-expression}?
), mais agit plus comme un prédicat sémantique fermé (aucune exception n'est levée lorsque l'expression booléenne évalue à false
). Vous pouvez l'utiliser au début d'une règle pour vérifier certaines propriétés d'une règle et laisser l'analyseur faire correspondre cette règle ou non.
Disons que l'exemple de grammaire crée des Number
jetons (une règle de lexer au lieu d'une règle d'analyseur) qui correspondent à des nombres compris entre 0 et 999. Maintenant, dans l'analyseur, vous aimeriez faire une distinction entre les nombres faibles et élevés (bas: 0..500, haut: 501..999). Cela pourrait être fait en utilisant un prédicat sémantique sans ambiguïté dans lequel vous inspectez le jeton suivant dans le flux ( input.LT(1)
) pour vérifier s'il est bas ou élevé.
Une démo:
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 vous analysez maintenant la chaîne "123, 999, 456, 700, 89, 0"
, vous verrez la sortie suivante:
low = 123
high = 999
low = 456
high = 700
low = 89
low = 0
input.LT(1)
c'est getCurrentToken()
maintenant :-)
J'ai toujours utilisé la référence laconique aux prédicats ANTLR sur wincent.com comme guide.