LALR(1) パーサーでは、文法のルールが解析テーブルに変換され、「これまでにこの入力があり、先読みトークンが X である場合、状態 Y に移行するか、ルール R によって削減されます」と効果的に記述されます。 .
ジェネレーターを使用するのではなく、実行時に解析テーブルを計算し、その解析テーブルを使用して入力を評価する、インタープリター言語 (ruby) で LALR(1) パーサーを正常に構築しました。これは驚くほどうまく機能し、テーブルの生成は非常に簡単で (これには少し驚きました)、自己参照ルールと左右の関連付けがサポートされています。
ただし、理解するのに少し苦労していることの 1 つは、yacc/bison が空のルール定義を概念的に処理する方法です。テーブルを生成する際に各ルールの各シンボルを再帰的に調べ、「空」はレクサーから来るものでも、ルールによって削減されるものでもないため、私のパーサーはそれらを処理できません。では、LALR(1) パーサーは空のルールをどのように処理するのでしょうか? 彼らはそれを特別に扱っていますか、それとも、そのような概念を特に意識する必要さえなくても、有効なアルゴリズムがうまく機能する「自然な」概念ですか?
たとえば、途中に何もないペアの括弧の任意の数に一致できるルールを考えてみましょう:
expr: /* empty */
| '(' expr ')'
;
次のような入力は、このルールに一致します。
((((()))))
これは、'(' を読み取り、先読みトークンで ')' を確認すると、パーサーが以下を選択することを意味します。
- ')' をシフトする (不可)
- 他のルールに従って入力を減らす (不可能)
- 何か他の...
「シフト」または「縮小」のコアアルゴリズムには完全には適合しません。パーサーは事実上、スタックに何もシフトせず、「何もない」を に還元してからexpr
、次のトークン をシフトし')'
、 を与え'(' expr ')'
、もちろんこれは に還元さexpr
れます。
私を混乱させているのは「何もシフトしない」ことです。解析テーブルはそのような概念をどのように伝えますか? また、空の値を減らすと値を返す何らかのセマンティック アクションを呼び出すことができるはずであることも考慮してください。そのため、解析テーブルからそれをスキップして、スタックと先読みで単に変換する必要が$$
あるというかなり単純なビューシフトは、真に sequenceを生成するのではなく、単に sequence を生成します。'('
')'
'(' expr ')'
'(' ')'