antlr как определить необязательные детали в любом порядке

Предположим, нужна грамматика для разбора следующих шаблонов:

1. REPORT
2. BEGIN
3.   QUERY
4.   BEGIN
5.     AGGREGATION: day
6.     DIMENSION: department
7.   END
8. END

Где строки № 5 и № 6 являются необязательными, и порядок 2 строк не имеет значения. Как я могу указать это в моем файле грамматики? Ниже мое решение (см. Строку № 12):

1. grammar PRL;
2. report
3.  : REPORT
4.      BEGIN 
5.          query
6.      END
7.  ;
8.
9. query
10.  : QUERY 
11.     BEGIN
12.         (aggregation_decl dimension_decl | dimension_decl aggregation_decl)? 
13.     END
14. ;

Так что это работает, однако выглядит уродливо, и если у меня будет более 2 частей, это станет очень быстро неуправляемым? Любой совет?

2 ответа

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

grammar PRL;
report
  : REPORT
      BEGIN 
          query
      END
  ;

query
  : QUERY 
     BEGIN
       body_decl* 
     END
 ;

body_decl :
   aggregation_decl dimension_decl
 | dimension_decl aggregation_decl;

Как уже упоминалось Адамом: обычно это делается после того, как анализатор создал некое (абстрактное) дерево разбора. Вы просто собираете все типы объявлений следующим образом:

grammar PRL;

report
 : REPORT BEGIN query END
 ;

query
 : QUERY BEGIN decl* END
 ;

decl
 : NAME ':' NAME
 ;

REPORT : 'REPORT';
BEGIN  : 'BEGIN';
END    : 'END';
QUERY  : 'QUERY';
NAME   : ('a'..'z' | 'A'..'Z')+;

SPACE  : (' ' | '\t' | '\r' | '\n')+ {skip();};

и после этого проверьте наличие дубликатов в decl* в вашем АСТ.

Но если вы действительно хотите сделать это во время разбора, вам нужно взять левую часть decl и добавить их в Set и когда вы наткнетесь на дубликат, создайте исключение предиката:

grammar PRL;

@parser::header {
  import java.util.Set;
  import java.util.HashSet;
}

report
 : REPORT BEGIN query END
 ;

query
 : QUERY BEGIN unique_decls END
 ;

unique_decls
@init{Set<String> set = new HashSet<String>();}
 : (decl {set.add($decl.key)}?)*
 ;

decl returns[String key]
 : k=NAME ':' NAME {$key = $k.text;}
 ;

REPORT : 'REPORT';
BEGIN  : 'BEGIN';
END    : 'END';
QUERY  : 'QUERY';
NAME   : ('a'..'z' | 'A'..'Z')+;

SPACE  : (' ' | '\t' | '\r' | '\n')+ {skip();};

{set.add($decl.key)}?, называемый проверяющими семантическими предикатами, вызовет исключение, когда код внутри него (set.add($decl.key)) оценивает false, В этом случае он оценивается как ложный, когда набор уже содержит определенный key,

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