1

前方参照のある言語の文法を作成する必要があります。これを実現する最も簡単な方法は、生成されたASTを複数回パスすることだと思いますが、シンボル情報をツリーに格納する方法が必要です。

現在、私のパーサーはASTを正しく生成し、変数と関数定義のスコープを計算します。問題は、スコープ情報をツリーに保存する方法がわからないことです。

私の文法の断片:

composite_instruction
scope JScope;
@init {
    $JScope::symbols = new ArrayList();
    $JScope::name = "level "+ $JScope.size();
}
@after {
    System.out.println("code block scope " +$JScope::name + " = " + $JScope::symbols);
}
    : '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction*)
    ;

現在のスコープへの参照をツリーに入れたいと思います。

    : '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction* {$JScope::symbols})

それも可能ですか?生成されたツリーに現在のスコープを保存する他の方法はありますか?ツリー文法でスコープ情報を生成できますが、ツリーの2回目のパスのためにどこかに保存する必要があるため、何も変更されません。

4

1 に答える 1

2

私の知る限り、書き換えルールの構文では、暫定的なスニペットが示唆するように値を直接割り当てることはできません。これは、パーサーがツリー/ノードのどの部分に値を追加する必要があるかを実際には知らないという事実に一部起因しています。

ただし、ANTLRで生成されたASTの優れた機能の1つは、パーサーがノードのタイプについて何も想定していないことです。新しいノードのファクトリとして、またツリー構造のナビゲーターとして機能するTreeAdapatorを実装する必要があります。したがって、以下で説明するように、ノードで必要になる可能性のある情報を詰め込むことができます。

ANTLRは、デフォルトのツリーノード実装であるCommonTreeを提供します。ほとんどの場合(現在の状況のように)、必要なのは単に

  • いくつかのカスタムフィールドを追加してCommonTreeをサブクラス化します
  • CommonTreeAdaptorをサブクラス化して、create()メソッド、つまり新しいノードを生成する方法をオーバーライドします。

しかし、奇妙なグラフ構造などのために、まったく新しいタイプのノードを作成することもできます。当面の場合、以下で十分です(これがJavaでない場合は、特定のターゲット言語に適応します)

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

public class NodeWithScope extends CommonTree {

    /* Just declare the extra fields for the node */
    public ArrayList symbols;
    public string    name;
    public object    whatever_else;

    public NodeWithScope (Token t) {
        super(t);
    }
}

/* TreeAdaptor: we just need to override create method */
class NodeWithScopeAdaptor extends CommonTreeAdaptor {
    public Object create(Token standardPayload) {
        return new NodeWithScope(standardPayload);
    }
}

次に、解析プロセスの開始方法を少し変更して、ANTLR(またはANTLRで生成されたパーサー)がCommnTreeではなくNodeWithScopeAdaptorを使用することを認識できるようにする必要があります。
(以下のステップ4.1、残りはかなり標準的なANTLRテストリグの場合)

// ***** Typical ANTLR pipe rig  *****
//  ** 1. input stream 
ANTLRInputStream input = new ANTLRInputStream(my_input_file);
//  ** 2, Lexer 
MyGrammarLexer lexer = new MyGrammarLexer(input);
//  ** 3. token stream produced by lexer
CommonTokenStream tokens = new CommonTokenStream(lexer);
//  ** 4. Parser
MyGrammarParser parser = new MyGrammarParser(tokens);

//     4.1  !!! Specify the TreeAdapter
NodeWithScopeAdaptor  adaptor = new NodeWithScopeAdaptor();
parser.setTreeAdaptor(adaptor); // use my adaptor

//  ** 5. Start process by invoking the root rule
    r = parser.MyTopRule();
//  ** 6. AST tree
NodeWithScope  t = (NodeWithScope)r.getTree();
//  ** 7.  etc. parse the tree or do whatever is needed on it.

最後に、文法を次のようなものに適合させる必要があります
([現在のルールの]ノードは@afterセクションでのみ使用可能であることに注意してください。ただし、文法レベルからトークン属性やその他のコンテキスト変数を参照する場合があります。 、通常の$ rule.atrribute表記を使用)

composite_instruction
scope JScope;
@init {
    $JScope::symbols = new ArrayList();
    $JScope::name = "level "+ $JScope.size();
}
@after {
      ($composite_instruction.tree).symbols = $JScope::symbols;
      ($composite_instruction.tree).name    = $JScope::name;
      ($composite_instruction.tree).whatever_else
            = new myFancyObject($x.Text, $y.line, whatever, blah);
}
    : '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction*)
    ;
于 2010-11-18T02:54:05.813 に答える