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

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

100
задан Bart Kiers 6 November 2013 в 06:57
поделиться

2 ответа

ANTLR 4

Для предикатов в ANTLR 4 проверьте эти стек переполнение Вопросы и ответы:


ANTLR 3

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

Существует 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'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();}
  ;

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

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

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
@init { int N = 0; }
  :  (Digit { N++; } )+ { N <= 10 }?
  ;

Детали {int N = 0; } и {N ++; } - простые операторы Java, из которых первая инициализируется, когда синтаксический анализатор «входит» в правило число . Настоящий предикат: {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.Стробированные семантические предикаты

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

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

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

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

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. Устранение неоднозначности семантических предикатов

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

Допустим, в примере грамматики создаются токены Number (правило лексического анализатора вместо правила синтаксического анализатора), которые будут соответствовать числам в диапазоне 0..999. Теперь в синтаксическом анализаторе вы хотите различать низкие и высокие числа (low: 0..500, high: 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
161
ответ дан 24 November 2019 в 04:53
поделиться

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

11
ответ дан 24 November 2019 в 04:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: