O que é um ‘predicado semântico’ no ANTLR?

O que é um predicado semântico no ANTLR?

    ANTLR 4

    Para predicados no ANTLR 4, confira estas perguntas e respostas de estouro de pilha :

    • Sintaxe de predicados semânticos em Antlr4
    • Predicados semânticos em ANTLR4?

    ANTLR 3

    Um predicado semântico é uma maneira de impor regras extras (semânticas) sobre ações gramaticais usando código simples.

    Existem 3 tipos de predicados semânticos:

    • validação de predicados semânticos;
    • predicados semânticos bloqueados ;
    • predicados semânticos de ambiguidade .

    Exemplo de gramática

    Digamos que você tenha um bloco de texto composto apenas por números separados por vírgula, ignorando qualquer espaço em branco. Você gostaria de analisar essa input certificando-se de que os números tenham no máximo 3 dígitos “longos” (no máximo 999). A gramática a seguir ( Numbers.g ) faria uma coisa dessas:

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

    Testando

    A gramática pode ser testada com a seguinte 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(); } } 

    Teste-o gerando o lexer e o analisador, compilando todos os arquivos .java e executando a 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 Principal
    

    Ao fazer isso, nada é impresso no console, o que indica que nada deu errado. Tente mudar:

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

    para dentro:

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

    e faça o teste novamente: você verá um erro aparecendo no console logo após a string 777 .


    Predicados semânticos

    Isso nos leva aos predicados semânticos. Digamos que você queira analisar números entre 1 e 10 dígitos. Uma regra como:

     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 ; 

    se tornaria pesado. Os predicados semânticos podem ajudar a simplificar esse tipo de regra.


    1. Validando Predicados Semânticos

    Um predicado semântico de validação nada mais é do que um bloco de código seguido por um ponto de interrogação:

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

    Para resolver o problema acima usando um predicado semântico de validação , altere a regra de number na gramática para:

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

    As partes { int N = 0; } { int N = 0; } e { N++; } { N++; } são instruções Java simples, das quais a primeira é inicializada quando o analisador "insere" a regra de number . O predicado real é: { N < = 10 }? , que faz com que o analisador lance uma FailedPredicateException sempre que um número tiver mais de 10 dígitos.

    Teste-o usando o seguinte ANTLRStringStream :

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

    que não produz nenhuma exceção, enquanto o seguinte faz uma exceção:

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

    2. Predicados semânticos bloqueados

    Um predicado semântico bloqueado é semelhante a um predicado semântico de validação , apenas a versão controlada produz um erro de syntax em vez de um FailedPredicateException .

    A syntax de um predicado semântico bloqueado é:

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

    Para resolver o problema acima usando predicados com delimitação para corresponder a números de até 10 dígitos, você escreveria:

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

    Teste novamente com os dois:

     // 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 você verá o último em que lançará um erro.


    3. Desambiguação de Predicados Semânticos

    O tipo final de predicado é um predicado semântico sem ambiguidade , que parece um predicado de validação ( {boolean-expression}? ), Mas age mais como um predicado semântico bloqueado (nenhuma exceção é lançada quando a expressão booleana é avaliada como false ). Você pode usá-lo no início de uma regra para verificar alguma propriedade de uma regra e deixar que o analisador corresponda à regra mencionada ou não.

    Digamos que a gramática de exemplo crie tokens de Number (uma regra de léxico em vez de uma regra de analisador) que corresponderá a números no intervalo de 0..999. Agora, no analisador, você gostaria de fazer uma distinção entre números baixos e altos (baixo: 0..500, alto: 501..999). Isso pode ser feito usando um predicado semântico de input.LT(1) ambiguidade no qual você inspeciona o token no stream ( input.LT(1) ) para verificar se ele é baixo ou alto.

    Uma demonstração:

     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 você agora analisar a string "123, 999, 456, 700, 89, 0" , verá a seguinte saída:

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

    Eu sempre usei a referência concisa aos predicados ANTLR em wincent.com como meu guia.