101 votes

Qu'est-ce qu'un "prédicat sémantique" dans ANTLR ?

Qu'est-ce qu'un prédicat sémantique dans ANTLR ?

163voto

Bart Kiers Points 79069

ANTLR 4

Pour les prédicats dans ANTLR 4, vérifiez ceux-ci pile débordement Questions et réponses :


ANTLR 3

A prédicat sémantique est un moyen d'appliquer des règles (sémantiques) supplémentaires sur la grammaire en utilisant du code simple.

Il existe 3 types de prédicats sémantiques :

  • validation de prédicats sémantiques ;
  • portail prédicats sémantiques ;
  • désambiguïsation prédicats sémantiques.

Exemple de grammaire

Disons que vous avez un bloc de texte composé uniquement de chiffres séparés par des virgules. virgules, sans tenir compte des espaces blancs. Vous souhaitez analyser cette entrée en vous assurant que les nombres ont au maximum 3 chiffres de "longueur" (au maximum 999). La commande suivante grammaire ( 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();}
  ;

Essais

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 syntaxique, en compilant tous les éléments de l'application .java et l'exécution des 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

En faisant cela, rien n'est imprimé sur la console, ce qui indique que rien ne s'est que rien ne s'est produit. 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 de caractères 777 .


Prédicats sémantiques

Cela nous amène aux prédicats sémantiques. Disons que vous voulez 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 contribuer à simplifier ce type de règle.


1. Validation des prédicats sémantiques

A validation du prédicat sémantique n'est rien 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 validation de prédicat sémantique, modifier le number dans la grammaire en :

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

Les parties { int N = 0; } y { N++; } sont des instructions Java simples, dont la première est initialisée lorsque l'analyseur syntaxique " entre " dans le fichier number règle. Le prédicat réel prédicat est : { N <= 10 }? ce qui entraîne l'apparition d'un FailedPredicateException lorsqu'un nombre est supérieur à 10 chiffres.

Testez-le en utilisant les éléments suivants ANTLRStringStream :

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

qui ne produit aucune exception, alors que la suivante en produit une :

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

2. Prédicats sémantiques à grille

A prédicat sémantique déclenché est similaire à un validation du prédicat sémantique , seulement le portail produit une erreur de syntaxe au lieu d'une FailedPredicateException .

La syntaxe d'un prédicat sémantique déclenché est :

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

Pour résoudre le problème ci-dessus en utilisant portail pour faire correspondre des nombres jusqu'à 10 chiffres, vous écrirez :

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

Testez-le à 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 que le dernier on lancera une erreur.


3. Désambiguïsation des prédicats sémantiques

Le dernier type de prédicat est un désambiguïsation du prédicat sémantique qui ressemble un peu à un prédicat de validation ( {boolean-expression}? ), mais agit plutôt comme un prédicat sémantique déclenché (aucune exception n'est levée lorsque l'expression booléenne donne la valeur false ). Vous pouvez l'utiliser au début d'une règle pour vérifier une certaine propriété d'une règle et laisser l'analyseur syntaxique correspondre à cette règle ou non.

Disons que l'exemple de grammaire crée Number (une règle de lexème au lieu d'une règle d'analyse syntaxique) qui correspondra aux nombres compris entre 0 et 999. Maintenant, dans l'analyseur syntaxique, vous voudriez faire une distinction entre les nombres faibles et les nombres élevés (faible : 0..500, élevé : 501..999). Cela peut être fait en utilisant un désambiguïsation du prédicat sémantique où vous inspectez le jeton suivant dans le flux ( input.LT(1) ) pour vérifier si elle est basse ou haute.

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 de caractères "123, 999, 456, 700, 89, 0" vous obtiendrez le résultat suivant :

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

11voto

Kaleb Pederson Points 22428

J'ai toujours utilisé la référence laconique à Prédicats ANTLR sur wincent.com comme guide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X