明示的なスタックを使用した非再帰的アプローチで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などのノード階層がありますが、それをインスタンス化する場所がわかりません。
まだ私はこれの簡単な例を見つけていません(たとえば算術言語を使って)。
本から引用されたテキストが説明しているように、それは起こります。文法規則(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))