Семантические предикаты 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 оценивает семантические предикаты в двух случаях.

  1. Сгенерированный код оценивает семантический предикат во время синтаксического анализа и выдает исключение из оценки, возвращает false. Таким образом оцениваются все предикаты, пройденные во время синтаксического анализа, включая контекстно-зависимые предикаты и предикаты, которые не отображаются в левой части решения.
  2. Метод прогнозирования оценивает предикаты, чтобы принимать правильные решения во время синтаксического анализа. В этом случае предполагается, что предикаты, которые появляются где-либо кроме левого края оцениваемого решения, возвращают истину (то есть они игнорируются). Кроме того, контекстно-зависимые предикаты оцениваются, только если доступны контекстные данные. Алгоритм прогнозирования не будет создавать контекстные структуры, которые еще не были предоставлены кодом синтаксического анализа. Если во время прогнозирования встречается контекстно-зависимый предикат, а контекст недоступен, предполагается, что предикат возвращает true (т. Е. Он игнорируется для этого решения).

Генератор кода не оценивает семантику целевого языка, поэтому он не может знать, что $ctx семантически не имеет значения, когда он появляется в /*$ctx*/, В обоих случаях предикат рассматривается как контекстно-зависимый.

Другие вопросы по тегам