web-dev-qa-db-ja.com

ANTLRを使用して構築されたASTを出力する方法は?

私はC用の静的アナライザーを作成しています。Javaコードを生成するANTLRを使用して、レクサーとパーサーを実行しました。

ANTLRはASTをoptions {output=AST;}によって自動的に構築しますか?または、ツリーを自分で作成する必要がありますか?作成する場合、そのASTのノードを吐き出す方法は?

私は現在、そのAST上のノードがSSAを作成するために使用され、続いて静的フローアナライザーを作成するためにデータフロー分析が使用されると考えています。正しいパスにいますか?

27
Raphael

ラファエルは書きました:

AntlrはASTをオプション{output = AST;}によって自動的に構築しますか?または、自分でツリーを作成する必要がありますか?作成する場合は、そのASTのノードを吐き出す方法?

いいえ、パーサーはパーサールールごとにルートおよびリーフとして何が必要かわからないため、文法に_options { output=AST; }_を置くだけではなく、もう少し多くのことを行う必要があります。

たとえば、文法から生成されたパーサーを使用してソース"true && (false || true && (true || false))"を解析する場合:

_grammar ASTDemo;

options { 
  output=AST; 
}

parse
  :  orExp
  ;

orExp
  :  andExp ('||' andExp)*
  ;

andExp
  :  atom ('&&' atom)*
  ;

atom
  :  'true'
  |  'false'
  |  '(' orExp ')'
  ;

// ignore white space characters
Space
  :  (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}
  ;
_

次の解析ツリー が生成されます:

enter image description here

(つまり、トークンのフラットな1次元リスト)

ANTLRに、文法のどのトークンがルートになるか、リーフになるか、または単にツリーから除外されるかを伝えます。

ASTの作成は、次の2つの方法で実行できます。

  1. 次のような書き換えルールを使用します:foo : A B C D -> ^(D A B);。ここで、fooはトークン_A B C D_に一致するパーサールールです。したがって、_->_の後のすべてが実際の書き換えルールです。ご覧のとおり、トークンCは書き換えルールで使用されていません。つまり、ASTからは省略されています。 _^(_の直後に配置されたトークンがツリーのルートになります。
  2. ツリー演算子_^_および_!_after内のトークンを使用する_^_がトークンをルートにし、_!_がツリーからトークンを削除するパーサー規則。 foo : A B C D -> ^(D A B);と同等のものは_foo : A B C! D^;_になります。

foo : A B C D -> ^(D A B);と_foo : A B C! D^;_はどちらも次のASTを生成します。

enter image description here

これで、次のように文法を書き直すことができます。

_grammar ASTDemo;

options { 
  output=AST; 
}

parse
  :  orExp
  ;

orExp
  :  andExp ('||'^ andExp)* // Make `||` root
  ;

andExp
  :  atom ('&&'^ atom)* // Make `&&` root
  ;

atom
  :  'true'
  |  'false'
  |  '(' orExp ')' -> orExp // Just a single token, no need to do `^(...)`, 
                            // we're removing the parenthesis. Note that
                            // `'('! orExp ')'!` will do exactly the same.
  ;

// ignore white space characters
Space
  :  (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}
  ;
_

これは 次のAST をソースから作成します"true && (false || true && (true || false))"

enter image description here

関連ANTLR wikiリンク:

ラファエルは書きました:

私は現在、そのAST上のノードがSSAを作成するために使用され、続いて静的フローアナライザーを作成するためにデータフロー分析が使用されると考えています。正しいパスにいますか?

そのようなことをしたことは一度もありませんが、IMOあなたが最初に望むのはASTソースからのものなので、そうです、そうです、あなたは正しい道を進んでいると思います!:)

編集する

生成されたレクサーとパーサーを使用する方法は次のとおりです。

_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 = "true && (false || true && (true || false))";
    ASTDemoLexer lexer = new ASTDemoLexer(new ANTLRStringStream(src));
    ASTDemoParser parser = new ASTDemoParser(new CommonTokenStream(lexer));
    CommonTree tree = (CommonTree)parser.parse().getTree();
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT(tree);
    System.out.println(st);
  }
}
_
58
Bart Kiers