Che cos’è un “predicato semantico” in ANTLR?

Cos’è un predicato semantico in ANTLR?

ANTLR 4

Per i predicati in ANTLR 4, controlla queste domande e risposte di overflow dello stack :

  • Sintassi dei predicati semantici in Antlr4
  • Predicati semantici in ANTLR4?

ANTLR 3

Un predicato semantico è un modo per applicare regole extra (semantiche) sulle azioni grammaticali usando codice semplice.

Esistono 3 tipi di predicati semantici:

  • convalida dei predicati semantici;
  • predicati semantici controllati;
  • disambiguazione dei predicati semantici.

Esempio di grammatica

Diciamo che hai un blocco di testo composto da soli numeri separati da virgola, ignorando eventuali spazi bianchi. Vorresti analizzare questo input assicurandoti che i numeri siano al massimo di 3 cifre “lunghi” (al massimo 999). La seguente grammatica ( Numbers.g ) farebbe una cosa del genere:

 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();} ; 

analisi

La grammatica può essere testata con la seguente class:

 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 il lexer e il parser, compilando tutti i file .java ed eseguendo la class 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

Nel fare ciò, non viene stampato nulla sulla console, il che indica che non è andato tutto storto. Prova a cambiare:

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

in:

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

e ripeti il ​​test: vedrai apparire un errore sulla console subito dopo la stringa 777 .


Predicati semantici

Questo ci porta ai predicati semantici. Diciamo che vuoi analizzare numeri compresi tra 1 e 10 cifre. Una regola come:

 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 ; 

diventerebbe ingombrante. I predicati semantici possono aiutare a semplificare questo tipo di regole.


1. Convalida dei predicati semantici

Un predicato semantico di convalida non è altro che un blocco di codice seguito da un punto interrogativo:

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

Per risolvere il problema sopra utilizzando un predicato semantico di convalida , modifica la regola del number nella grammatica in:

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

Le parti { int N = 0; } { int N = 0; } e { N++; } { N++; } sono semplici istruzioni Java di cui il primo è inizializzato quando il parser "inserisce" la regola del number . Il predicato effettivo è: { N <= 10 }? , che fa sì che il parser lanci una FailedPredicateException ogni volta che un numero è lungo più di 10 cifre.

ANTLRStringStream usando il seguente ANTLRStringStream :

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

che non produce alcuna eccezione, mentre il seguente fa un'eccezione:

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

2. Predicati semantici Gated

Un predicato semantico gated è simile a un predicato semantico di convalida , solo la versione gated genera un errore di syntax invece di una FailedPredicateException .

La syntax di un predicato semantico gated è:

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

Per risolvere invece il problema precedente utilizzando i predicati gated per abbinare numeri lunghi fino a 10 cifre, scrivere:

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

Provalo di nuovo con entrambi:

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

e:

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

e vedrai che l'ultimo su genererà un errore.


3. Predicati predicati semantici

Il tipo finale di predicato è un predicato semantico disambiguante , che assomiglia un po 'a un predicato di convalida ( {boolean-expression}? ), Ma agisce più come un predicato semantico gated (non viene generata alcuna eccezione quando l'espressione booleana diventa false ). Puoi usarlo all'inizio di una regola per controllare alcune proprietà di una regola e lasciare che il parser corrisponda a detta regola oppure no.

Diciamo che la grammatica di esempio crea token Number (una regola lexer invece di una regola parser) che corrisponderà a numeri nell'intervallo 0..999. Ora nel parser, ti piacerebbe fare una distinzione tra numeri bassi e alti (bassi: 0. 500, alti: 501..999). Questo potrebbe essere fatto usando un predicato semantico disambiguante in cui ispezionare il token successivo nello stream ( input.LT(1) ) per verificare se è basso o alto.

Una demo:

 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();} ; 

Se ora analizzi la stringa "123, 999, 456, 700, 89, 0" , vedresti il ​​seguente output:

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

Ho sempre usato il riferimento più chiaro ai predicati ANTLR su wincent.com come mia guida.