antlr переписывает вывод правила

Я пытаюсь написать сценарий antlr, в котором в правиле1 есть подправило, правило2. Я использую StringTemplate по правилу 1.

Что я хочу сделать, так это реструктурировать текст, соответствующий правилу2, прежде чем он будет использован/использован правилом1. Как мне это сделать ?

options{
output=template;
}

rule1 :
  begin sub-rule2 end ';' -> meth(body={rule1.text})

sub-rule2 :
   sub-rule3
 | sub-rule4
 | sub-rule5;

здесь "мет" - это вызов шаблона строки

Если, скажем, подправило 4 соответствует «выбрать * из двойного;», я бы хотел, чтобы это было передано в правило 1 «#sql (выбрать * из двойного);».

Вот мой фактический код. Я хотел бы, чтобы операторы, соответствующие правилу select_statement, были заключены в «#sql()» и переданы в списке «статистика» в атрибут «body» шаблона «meth»:

body 
@init {
      List stats = new ArrayList();
    }   :   
    BEGIN s=statement{ stats.add($s.text); } SEMI ( s=statement{ stats.add($s.text); } SEMI | pragma SEMI )*
    ( EXCEPTION exception_handler+ )? END ID? -> method(modifiers={"public"},returnType={"void"},name={"execute"},body={stats})
    ;


statement :
    label*
    ( assign_or_call_statement
    | case_statement
    | close_statement
    | continue_statement
    | basic_loop_statement
    | execute_immediate_statement
    | exit_statement
    | fetch_statement
    | for_loop_statement
    | forall_statement
    | goto_statement
    | if_statement
    | null_statement
    | open_statement
    | plsql_block
    | raise_statement
    | return_statement
    | sql_statement 
    | while_loop_statement
    ) 
    ;

    sql_statement
    : (commit_statement
    | delete_statement
    | insert_statement
    | lock_table_statement
    | rollback_statement
    | savepoint_statement
    | select_statement 
    | set_transaction_statement
    | update_statement )
    ;


    select_statement :
        SELECT swallow_to_semi 
    ;

    SELECT  :   'select';

    swallow_to_semi :
        ~( SEMI )+
    ;

person Abhay Chaware    schedule 14.11.2011    source источник


Ответы (1)


Вы можете конкретно определить, что может возвращать правило, следующим образом:

sub_rule2 returns [String x] 
  :  sub_rule3 {$x = ... }
  |  sub_rule4 {$x = "#sql (" + $sub_rule4.text + ");";}
  |  sub_rule5 {$x = ... }
  ;

Теперь sub_rule2 возвращает String x, который вы можете использовать следующим образом:

rule1
  :  ... sub_rule2 ... -> meth(body={sub_rule2.x})
  ;

Обратите внимание на sub_rule2.x.

РЕДАКТИРОВАТЬ

Вы также можете создать собственный метод, который проверяет, начинается ли текст, добавляемый в List, с "select ", например:

grammar YourGrammarName;

options{
  output=template;
}

@parser::members {
  private void addStat(String stat, List<String statList>) {
    // 1. if `stat` starts with "select ", wrap "#sql(...)" around it.
    // 2. add `stat` to `statList`
  }
}

body 
@init {
  List<String> stats = new ArrayList<String>();
}   
  :  BEGIN s=statement { addStat($s.text, stats); } SEMI 
     ( s=statement     { addStat($s.text, stats); } SEMI 
     | pragma                                      SEMI
     )*
     (EXCEPTION exception_handler+)? END ID? 

     -> method(modifiers={"public"},returnType={"void"},name={"execute"},body={stats})
  ;
person Bart Kiers    schedule 14.11.2011
comment
Спасибо, Барт. Проблема с этим в том, что мне придется определить тип возвращаемого значения и {$x = ...} для всех подправил между правилом 1 и правилом 2. Пример, который я привел выше, был урезанной версией фактического сценария. На самом деле между первым и последним есть 4/5 подправил/уровней. Есть ли другой способ добиться этого в такой ситуации? - person Abhay Chaware; 14.11.2011
comment
Я обновил исходное описание проблемы реальным случаем, спасибо. - person Abhay Chaware; 14.11.2011
comment
Спасибо, Барт. Это один из способов сделать это, который я попробовал и работает. Я все еще хотел бы иметь возможность сделать это в правиле sql_statement, что, я думаю, было бы намного чище. Поскольку, если вы посмотрите на правило «оператор», оно может соответствовать множеству подправил, таких как sql_statement, sql_block и т. д., и в этом случае мне придется иметь огромный метод в @members, обрабатывающий случаи для каждого из них. их .. и это просто правило «утверждения». Внутри грамматика есть все остальные правила, которые будут соответствовать тому или иному тексту, который снова нужно будет передать через «тело». Это станет неуправляемым наверняка. - person Abhay Chaware; 15.11.2011
comment
Обновление, я попробовал кое-что, и это сработало! Изменено правило «оператора», например: code оператор возвращает [String xx]: label* ( assign_or_call_statement .. | sql_statement {$xx = $sql_statement.x;} | while_loop_statement) {if ($xx==null) $xx = $ оператор.текст;} ; code и в правиле 'body' НАЧАЛО s=statement{ stats.add($s.xx); } .. - person Abhay Chaware; 15.11.2011
comment
таким образом, мне не нужно определять { $xx = ... ;} для каждого субправила. - person Abhay Chaware; 15.11.2011
comment
Теперь вопрос в том, как заменить это на StringTemplate. Я хотел бы определить шаблон для sql_statement в шаблоне строки ( .stg ) следующим образом: sql(st) ::= ‹‹ #sql (‹st›); ›› и использовать его в грамматике. - person Abhay Chaware; 15.11.2011
comment
опять же, с некоторыми исследованиями, я смог сделать это следующим образом: | sql_statement-> sql(stt={$sql_statement.text}) в правиле 'statement', а затем используя атрибут st правила.... stats.add($s.st) в правиле 'body'. Список здесь, так что может помочь другим, читающим эту тему .. - person Abhay Chaware; 15.11.2011