Что такое семантический предикат в 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 в качестве моего руководства.