Для предикатов в ANTLR 4 проверьте эти стек переполнение Вопросы и ответы:
семантический предикат - это способ применения дополнительных (семантических) правил к грамматике действия с использованием простого кода.
Существует 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
;
стало бы громоздким. Семантические предикаты могут помочь упростить этот тип правил.
Проверка семантических предикатов - это ничто больше, чем блок кода, за которым следует вопросительный знак:
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");
Стробованный семантический предикат подобен проверяющему семантическому предикату ,
только версия 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");
, и вы увидите, что последний из них выдаст ошибку.
Последний тип предиката - это семантический предикат устранения неоднозначности , который немного похож на проверяющий предикат ( {логическое-выражение}?
), но действует скорее как стробированный семантический предикат (исключение не создается, если логическое выражение принимает значение ложь
). Вы можете использовать его в начале правила, чтобы проверить какое-либо свойство правила и позволить синтаксическому анализатору соответствовать указанному правилу или нет.
Допустим, в примере грамматики создаются токены 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
В качестве руководства я всегда использовал краткую ссылку на предикаты ANTLR на сайте wincent.com.