Что такое "семантический предикат" в ANTLR?

Что такое семантический предикат в ANTLR?

Ответ 1

ANTLR 4

Для предикатов в ANTLR 4, проверьте эти стеки overflow Q & A's:


ANTLR 3

A семантический предикат - это способ обеспечения дополнительных (семантических) правил грамматики действия с использованием простого кода.

Существует 3 типа семантических предикатов:

  • проверка семантических предикатов;
  • стробированные семантические предикаты;
  • рассогласование семантических предикатов.

Пример грамматики

Скажем, у вас есть блок текста, состоящий только из чисел, разделенных запятая, игнорируя любые пробелы. Вы хотите проанализировать этот ввод что цифры не более 3 цифр "длинный" (не более 999). Следующие грамматика (Numbers.g) сделала бы такую ​​вещь:

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

Тестирование

Грамматика может быть протестирована следующим классом:

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 и запуск класса 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

При этом на консоль ничего не печатается, что указывает на то, что ничего пошло не так. Попробуйте изменить:

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

в

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

и повторите тест: вы увидите ошибку, появляющуюся на консоли сразу после строки 777.


Семантические предикаты

Это приводит нас к семантическим предикатам. Пусть говорят, что вы хотите разобрать номера от 1 до 10 цифр. Правило вроде:

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
  ;

станет громоздким. Семантические предикаты могут помочь упростить этот тип правил.


1. Проверка семантических предикатов

Подтверждающий семантический предикат - ничто более чем блок кода, за которым следует знак вопроса:

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

Чтобы решить проблему выше, используя проверяющий семантический предикат, измените правило number в грамматике на:

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

Части { int N = 0; } и { N++; } - это простые Java-инструкции, из которых первый инициализируется, когда парсер "вводит" правило number. Настоящий предикат: { N <= 10 }?, что заставляет парсер бросать FailedPredicateException каждый раз, когда число превышает 10 цифр.

Протестируйте его, используя следующий ANTLRStringStream:

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

который не производит никаких исключений, в то время как следующее исключает исключение:

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

2. Закрытые семантические предикаты

Закрытый семантический предикат похож на проверяющий семантический предикат, только стробированная версия генерирует синтаксическую ошибку вместо FailedPredicateException.

Синтаксис стробированного семантического предиката:

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

Чтобы решить эту проблему, используя предикаты gated для сопоставления чисел длиной до десяти цифр, вы должны написать:

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

Проверьте его снова с помощью обоих:

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

и

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

и вы увидите, что последний из них выдает ошибку.


3. Неоднозначные семантические предикаты

Конечным типом предиката является неоднозначный семантический предикат, который немного похож на проверяющий предикат ({boolean-expression}?), но действует скорее как стробированный семантический предикат (исключение не генерируется, когда булево выражение оценивается как false). Вы можете использовать его в начале правила для проверки некоторого свойства правила и пусть парсер соответствует указанному правилу или нет.

Скажем, примерная грамматика создает number tokens (правило lexer вместо правила парсера), которые будут соответствовать номерам в диапазоне 0..999. Теперь в синтаксическом анализаторе вы хотели бы провести различие между номерами с низким и высоким (низкий: 0..500, высокий: 501..999). Это можно сделать с использованием неоднозначного семантического предиката, где вы проверяете токен, следующий в потоке (input.LT(1)), чтобы проверить, является ли он ни низким, ни высоким.

Демонстрация:

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

Если вы теперь проанализируете строку "123, 999, 456, 700, 89, 0", вы увидите следующий вывод:

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

Ответ 2

Я всегда использовал краткую ссылку на предикаты ANTLR на wincent.com в качестве моего руководства.