【问题标题】:antlr rewriting output of a ruleantlr 重写规则的输出
【发布时间】:2011-11-14 05:54:19
【问题描述】:

我正在尝试编写一个 antlr 脚本,其中 rule1 有一个子规则 rule2。我在规则 1 上使用 StringTemplate。

我想要做的是在 rule1 使用/使用之前重组 rule2 匹配的文本。我该怎么做?

options{
output=template;
}

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

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

这里的“meth”是字符串模板调用

如果说子规则 4 匹配“select * from dual;”,我希望将其传递给规则 1“#sql (select * from dual);”。

这是我的实际代码我希望将 select_statement 规则匹配的语句包装在“#sql()”中,并在“stats”列表中传递给“meth”模板的“body”属性:

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 )+
    ;

【问题讨论】:

    标签: rewrite antlr stringtemplate


    【解决方案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})
      ;
    

    【讨论】:

    • 谢谢巴特。这个问题是我必须为规则 1 和规则 2 之间的所有子规则定义返回类型和 {$x = ... }。我上面给出的示例是实际场景的精简版本。实际上,第一个和最后一个之间有 4/5 个子规则/级别。在这种情况下,还有其他方法可以实现吗?
    • 我已经用实际案例更新了原始问题描述,谢谢。
    • 谢谢巴特。这是一种方法,我尝试过并且有效。我仍然希望能够在 sql_statement 规则中执行此操作,我认为这会更干净。因为,如果您查看“语句”规则,它可以匹配很多子规则,例如 sql_statement、sql_block 等,在这种情况下,我必须在 @members 中有一个巨大的方法,处理每个子规则他们..这只是“声明”规则。语法中还有所有其他规则,它们将匹配某些文本或其他,这将再次需要通过“body”传递。这肯定会变得无法维护。
    • 一个更新,我尝试了一些东西,它成功了!修改后的“语句”规则如下:code 语句返回 [String xx]: label* (assign_or_call_statement .. | sql_statement {$xx = $sql_statement.x;} | while_loop_statement) {if ($xx==null) $xx = $statement.text;}; code 在“正文”规则中,BEGIN s=statement{ stats.add($s.xx); } ..
    • 这样,我不必为每个子规则定义 { $xx = ... ;}。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多