2

セマンティック述語を取得しようとしています。これは簡単に思えますが、どういうわけか機能しません。ブール条件に基づいて、ルールを実行する (AST を吐き出す) か、または手動でルールを作成する必要があります。

以下はパーサールールです。

displayed_column
  :   
    {columnAliases.containsKey($text)}? 
    =>-> ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)])) 
  | sql_expression
  ;

私はすべてのゲートと曖昧さ回避も試みましたが、コードを実行している間、常に 2 番目のルール (sql_expression) に進みます。

誰でも私を助けてもらえますか?

ありがとう

編集: 述語の実行中に $text が空であることに気付きました。これが、常に2番目のルールに一致する理由です。ルールをこれに変更しましたが、機能します

displayed_column
  :
        sql_expression
        -> {columnAliases.containsKey($text)}? ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)])) 
        -> sql_expression

しかし、私は別の問題に遭遇しました。ツリーを手動で構築してもうまくいかないことに気付きました。新しいテキスト (columnAliases マップの値) でルールdisplayed_columnを再実行する必要があります。それは可能ですか?

これは私の最初の質問でした https://stackoverflow.com/questions/14170541/antlr-dynamic-input-stream-modification-during-parsing

基本的に、私はSQLのようなステートメントをインタラクティブに解析して解釈しようとしています。

select a.b.c from pool;
select min(abc.def[*]) from pool;

列名が少し長くなる可能性があるため、(別のコマンドを使用して) エイリアス列名をユーザーに設定しました。たとえば、ユーザーは設定を設定してコマンドを実行できます。

set column_alias a.b.c d;
select d from pool;

解析中に、生成されたパーサーに設定 (マップ) を挿入し、新しい列を元の列に置き換え/マップしてから、解釈を続けようとしています。列が複数のルールにまたがっているため、ツリーグラマーで処理するのは難しいと思っていたので、パーサーで処理することが唯一の選択肢のように思えました。

文法全体を投稿することもできますが、少し長すぎます。縮小版を次に示します。

select_stmt:
  : 'select' displayed_column 'from' pool
  ;

displayed_column
  : sql_expression 
  ;

sql_expression
  : term ( (PLUS^ | MINUS^) term)*
  ;

term  : factor ( (ASTERISK^ | DIVIDE^) factor)*
  ;

... <more_rules> ...

文字列テンプレートを使用して翻訳されたステートメントを出力し、再解析することが唯一のオプションのように思えますが、これには文法全体を書き直してテンプレートを出力する必要があります (現在、AST とそれを解釈する木の文法)。邪魔にならない方法を教えていただければ幸いです。

再度、感謝します。

4

1 に答える 1

4

文字列を値として保存する代わりに、実際の AST をマップに保存してみませんか? これらの ASTは、書き換えルールで{...内にラップすることで注入できます。}

デモ:

grammar T;

options {
  output=AST;
  ASTLabelType=CommonTree;
}

tokens {
  STATS;
  DISPLAYED_COLUMN;
  NAME;
  SELECT;
}

@parser::header {
  import java.util.Map;
  import java.util.HashMap;
}

@parser::members {
  private Map<String, CommonTree> aliases = new HashMap<String, CommonTree>();
}

parse
 : (stmt ';')+ EOF -> ^(STATS stmt+)
 ;

stmt
 : set_stmt
 | select_stmt
 ;

set_stmt
 : 'set' 'alias' name Id {aliases.put($Id.text, $name.tree);} -> /* AST can be omitted */
 ;

select_stmt
 : 'select' displayed_column 'from' name -> ^(SELECT displayed_column name)
 ;

displayed_column
 : sql_expression -> {aliases.containsKey($text)}? ^(DISPLAYED_COLUMN {aliases.get($text)})
                  ->                               ^(DISPLAYED_COLUMN sql_expression)
 ;

sql_expression
 : term (('+' | '-')^ term)*
 ;

term
 : factor (('*' | '/')^ factor)*
 ;

factor
 : Num
 | name
 | '(' sql_expression ')'
 ;

name
 : Id ('.' Id)* -> ^(NAME Id+)
 ;

Id    : 'a'..'z'+;
Num   : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};

入力の解析:

プールから d を選択します。
エイリアス abc d を設定します。
プールから d を選択します。

次の AST になります。

ここに画像の説明を入力

編集

ありがとうバート!CommonTree をシリアル化できることを期待して、ユーザーが再入力する必要がないように、これらの設定をデータ ストアに保持する必要があるだけです。

:( 残念ながら、シリアライズ可能ではありません。

その場合、値を文字列として格納し、小さなヘルパー メソッドを使用してオンザフライで AST を作成し、createNameAST(String alias)このメソッドが作成する AST を注入できます。

grammar T;

options {
  output=AST;
  ASTLabelType=CommonTree;
}

tokens {
  STATS;
  DISPLAYED_COLUMN;
  NAME;
  SELECT;
}

@parser::header {
  import java.util.Map;
  import java.util.HashMap;
}

@parser::members {
  private Map<String, String> aliases = new HashMap<String, String>();

  private CommonTree createNameAST(String alias) {
    try {
      TLexer lexer = new TLexer(new ANTLRStringStream(aliases.get(alias)));
      TParser parser = new TParser(new CommonTokenStream(lexer));
      return (CommonTree)parser.name().getTree();  
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
  }
}

parse
 : (stmt ';')+ EOF -> ^(STATS stmt+)
 ;

stmt
 : set_stmt
 | select_stmt
 ;

set_stmt
 : 'set' 'alias' name Id {aliases.put($Id.text, $name.text);} -> /* AST can be omitted */
 ;

select_stmt
 : 'select' displayed_column 'from' name -> ^(SELECT displayed_column name)
 ;

displayed_column
 : sql_expression -> {aliases.containsKey($text)}? ^(DISPLAYED_COLUMN {createNameAST($text)})
                  ->                               ^(DISPLAYED_COLUMN sql_expression)
 ;

sql_expression
 : term (('+' | '-')^ term)*
 ;

term
 : factor (('*' | '/')^ factor)*
 ;

factor
 : Num
 | name
 | '(' sql_expression ')'
 ;

name
 : Id ('.' Id)* -> ^(NAME Id+)
 ;

Id    : 'a'..'z'+;
Num   : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};

ANTLRWorks のデバッガーを使用している場合createNameAST: TParser. 小さなテスト ケースを手動で作成します。

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String src = 
        "select d from pool; \n" + 
        "set alias a.b.c.x d; \n" +
        "select d from pool;";
    TLexer lexer = new TLexer(new ANTLRStringStream(src));
    TParser parser = new TParser(new CommonTokenStream(lexer));
    CommonTree tree = (CommonTree)parser.parse().getTree();  
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT(tree);
    System.out.println(st);
  }
}

コマンドラインでこれをすべて実行します。

java -cp antlr-3.3.jar org.antlr.Tool Tg
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar メイン > ast.dot

以前に投稿したものと同じ AST を表す DOT ファイルを取得します。

于 2013-01-08T11:18:32.853 に答える