Семантические предикаты ANTLR4, зависящие от контекста, не работают
Я разбираю объявление типа C++ с этой уменьшенной грамматикой (многие детали удалены, чтобы сделать ее полностью рабочим примером). Это не работает таинственно (по крайней мере для меня). Это связано с использованием контекстно-зависимого предиката? Если да, как правильно реализовать логику подсчета количества дочерних узлов?
grammar CPPProcessor;
cppCompilationUnit : decl_specifier_seq? init_declarator* ';' EOF;
init_declarator: declarator initializer?;
declarator: identifier;
initializer: '=0';
decl_specifier_seq
locals [int cnt=0]
@init { $cnt=0; }
: decl_specifier+ ;
decl_specifier : @init { System.out.println($decl_specifier_seq::cnt); }
'const'
| {$decl_specifier_seq::cnt < 1}? type_specifier {$decl_specifier_seq::cnt += 1;} ;
type_specifier: identifier ;
identifier:IDENTIFIER;
CRLF: '\r'? '\n' -> channel(2);
WS: [ \t\f]+ -> channel(1);
IDENTIFIER:[_a-zA-Z] [0-9_a-zA-Z]* ;
Мне нужно реализовать стандартное правило C++, которое не более 1 type_specifier
разрешено под decl_specifier_seq
,
Семантический предикат перед type_specifier
кажется, решение. И счет естественно объявлен как локальная переменная в decl_specifier_seq
так как вложенный decl_specifier_seq
возможны
Но кажется, что контекстно-зависимый семантический предикат, подобный тому, который я использовал, произведет неправильный синтаксический анализ, то есть семантический предикат, который ссылается на $ атрибуты. Сначала входной файл с правильным результатом (чтобы проиллюстрировать, как выглядит обычное дерево разбора):
int t=0;
и дерево разбора:
Но, вход без '=0', чтобы помочь анализу
int t;
0
1
line 1:4 no viable alternative at input 't'
1
синтаксический анализ завершился с ошибкой "нет жизнеспособной альтернативы" (числа, напечатанные в консоли, являются отладочной распечаткой $decl_specifier_cnt::cnt
значение в качестве проверки условия испытания). то есть семантический предикат не может предотвратить t
от разбора как type_specifier
а также t
больше не считается init_declarator
, В чем здесь проблема? Это потому, что контекстно-зависимый предикат, имеющий $decl_specifier_seq::cnt
используется?
Означает ли это, что контекстно-зависимый предикат нельзя использовать для реализации логики "подсчета количества дочерних узлов"?
РЕДАКТИРОВАТЬ
Я пробовал новые версии, чей предикат использует переменную-член вместо $decl_specifier_seq::cnt
и удивительно, что грамматика теперь работает, доказывая, что зависящий от контекста предикат действительно вызывал сбой предыдущей грамматики:
....
@parser::members {
public int cnt=0;
}
decl_specifier
@init {System.out.println("cnt:"+cnt); }
:
'const'
| {cnt<1 }? type_specifier {cnt++;} ;
Нормальное дерево разбора получается:
Это порождает вопрос о том, как поддерживать вложенное правило, если мы должны использовать переменные-члены для замены локальных переменных, чтобы избежать контекстно-зависимых предикатов?
И странный результат в том, что если я добавлю /*$ctx*/
после предиката снова происходит сбой:
decl_specifier
@init {System.out.println("cnt:"+cnt); }
:
'const'
| {cnt<1 /*$ctx*/ }? type_specifier {cnt++;} ;
line 1:4 no viable alternative at input 't'
Сбой при разборе no viable alternative
, Почему /*$ctx*/
вызывает синтаксический анализ сбой, как когда $decl_specifier_seq::cnt
используется, хотя фактическая логика использует только переменную-член? А без /*$ctx*/
еще одна проблема, связанная с предикатом, вызываемым до появления блока @init ( описано здесь)
1 ответ
ANTLR 4 оценивает семантические предикаты в двух случаях.
- Сгенерированный код оценивает семантический предикат во время синтаксического анализа и выдает исключение из оценки, возвращает false. Таким образом оцениваются все предикаты, пройденные во время синтаксического анализа, включая контекстно-зависимые предикаты и предикаты, которые не отображаются в левой части решения.
- Метод прогнозирования оценивает предикаты, чтобы принимать правильные решения во время синтаксического анализа. В этом случае предполагается, что предикаты, которые появляются где-либо кроме левого края оцениваемого решения, возвращают истину (то есть они игнорируются). Кроме того, контекстно-зависимые предикаты оцениваются, только если доступны контекстные данные. Алгоритм прогнозирования не будет создавать контекстные структуры, которые еще не были предоставлены кодом синтаксического анализа. Если во время прогнозирования встречается контекстно-зависимый предикат, а контекст недоступен, предполагается, что предикат возвращает true (т. Е. Он игнорируется для этого решения).
Генератор кода не оценивает семантику целевого языка, поэтому он не может знать, что $ctx
семантически не имеет значения, когда он появляется в /*$ctx*/
, В обоих случаях предикат рассматривается как контекстно-зависимый.