web-dev-qa-db-ja.com

AST LL1非再帰パーサの構築について

明示的なスタックを使用した非再帰的アプローチでLL1パーサーを実装しました。

次のアルゴリズムはDragonBookからのものです。

set zp to point to the first symbol of w;
set X to the top stack symbol;
while ( X != $ ) { /* stack is not empty */
    if ( X is a ) 
       pop the stack and advance zp;
    else if ( X is a terminal )
       error();
    else if ( M[X, a] is an error entry )
       error();
    else if ( M[X,a] = X -+ Y1Y2 Yk ) {
       output the production X -+ YlY2 - . Yk;
       pop the stack;
       Push Yk, Yk-1,. . . , Yl onto the stack, with Yl on top;

    set X to the top stack symbol;
}

本は言う:

パーサーは、スタックの最上位のシンボルであるXと、現在の入力シンボルであるaを考慮するプログラムによって制御されます。 Xが非終端記号である場合、パーサーは、構文解析テーブルIMのエントリM [X、a]を参照して、Xプロダクションを選択します。 (ここでは、解析ツリーでノードを構築するコードなど、追加のコードを実行できます。)それ以外の場合は、ターミナルXと現在の入力シンボルaの一致をチェックします。

ただし、このアプローチで式ツリーノードを構築する方法についての詳細が必要です。 UnaryOperator、BinaryOperatorなどのノード階層がありますが、それをインスタンス化する場所がわかりません。

まだ私はこれの簡単な例を見つけていません(たとえば算術言語を使って)。

3
Wyvern666

本から引用されたテキストが説明しているように、それは起こります。文法規則(M[X, a]によって与えられる)を介して非終端記号を展開すると、対応するノードを作成できます。

次の形式のルールがあるとします。

Term -> Factor Term'
Term' -> * Term | / Term | ε

Factor -> x | y | ... (simplified for individual numbers, letters, what-have-you)

次に、Term -> Factor Term'を展開すると、2つの子ノードを持つTermノードを作成できます。 Factor -> ...ルールを使用して最初の数値を正常に解析すると(これはサンプルコードの最初のifです)、この数値を既に作成されているFactorノードに関連付けることができます。

次に、たとえばTerm' -> * Termを介してM[Term',*]を展開し、新しいTermノードを作成します。

続けて、*を解析し、Term'ノードで注釈を付け、Term -> Factor Term'をもう一度展開して、さらに2つのノードを作成し、Factorを正常に解析し、その番号を2番目のFactorノードに注釈を付けます。最後に、入力の最後に、イプシロンプロダクション(Term')を介してM[Term',$] = εを解析します。これにより、そのTerm'ノードを削除できることがわかります(オプションの場合もあります)。

3 * 4のような入力文字列を取得すると、次のようなツリーになります。

Term ( Factor(3), Term' (*, Term ( Factor(4) ) ) )

後処理ステップでは、Term'のような非終端記号が文法を非左再帰にすることから生じるため、結果のツリーを単純化できますが、それ以外の場合は結果のASTには不適切であるため、結果の文法変換を逆にする必要があります。このようなものを取得するためのツリー:

Mult (Number(3), Number(4))

3
Frank