web-dev-qa-db-fra.com

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

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

99
Bart Kiers

ANTLR 4

Pour les prédicats dans ANTLR 4, consultez ces piledébordement Questions et réponses:


ANTLR 3

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

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

  • validation prédicats sémantiques;
  • gated prédicats sémantiques;
  • sans ambiguïté 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, en ignorant les espaces blancs. Vous souhaitez analyser cette entrée en vous assurant que les nombres sont au plus de 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();}
  ;

Essai

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 fichiers .Java Et en exécutant la classe 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 

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: une erreur apparaîtra sur la console juste après la chaîne 777.


Prédicats sémantiques

Cela nous amène aux prédicats sémantiques. Supposons que vous souhaitiez analyser des nombres compris 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.


1. Validation des prédicats sémantiques

A validation du prédicat sémantique n'est rien de plus 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 validation, modifiez la règle number dans la grammaire dans:

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

Les parties { int N = 0; } Et { N++; } Sont simples Java dont la première est initialisée lorsque l'analyseur "entre" la règle number. La 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 le ANTLRStringStream suivant:

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

qui ne produit aucune exception, tandis que ce qui suit fait une exception:

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

2. Prédicats sémantiques fermés

Un prédicat sémantique déclenché est similaire à un validant un prédicat sémantique, 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 le problème ci-dessus à la place en utilisant gated les prédicats pour faire correspondre les nombres jusqu'à 10 chiffres, vous écririez:

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 déclenchera une erreur.


3. Des prédicats sémantiques ambigus

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 est évaluée à 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 correspondre ou non à cette règle.

Supposons que l'exemple de grammaire crée des jetons Number (une règle de lexer au lieu d'une règle d'analyse) qui correspondront à des nombres compris entre 0 et 999. Maintenant, dans l'analyseur, vous souhaitez faire une distinction entre les nombres bas et les nombres élevés (bas: 0..500, haut: 501..999). Cela pourrait être fait en utilisant un prédicat sémantique sans ambiguïté où vous inspectez le jeton suivant dans le flux (input.LT(1)) pour vérifier s'il est faible 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
161
Bart Kiers

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

11
Kaleb Pederson