私はコンパイラの概念に取り組んでいますが、少し混乱しています...グーグルは明確な答えをどこにも得ませんでした。
SLRとLR(0)パーサーは同じですか?そうでない場合、違いは何ですか?
LR(0)およびSLR(1)パーサーは両方ともbottom-up、指向性、予測パーサーです。この意味は
LR(0)とSLR(1)は両方ともshift/reduce parsersです。つまり、入力ストリームのトークンをスタックに配置して処理します。 、各ポイントでshiftingトークンをスタックにプッシュするか、reduceスタックの上にあるターミナルと非ターミナルのシーケンスが、非ターミナルシンボルに戻ります。 shift/reduceパーサーを使用して、すべての文法をボトムアップで解析できるが、パーサーはdeterministicではない可能性があることを示すことができます。つまり、パーサーは、シフトまたはリダクションを適用するかどうかを「推測」する必要があり、間違った選択をしたことを認識するためにバックトラックしなければならない場合があります。決定論的なシフト/リデュースパーサーをどれだけ強力に構築しても、すべての文法を解析することはできません。
確定的シフト/リデュースパーサーを使用して処理できない文法を解析すると、shift/reduce conflictsまたはreduce/reduce conflicts。パーサーは、実行するアクションを判断できない状態になる可能性があります。 shift/reduceの競合では、スタックに別のシンボルを追加する必要があるか、スタックの最上位シンボルで何らかの縮小を実行する必要があるかどうかを判断できません。 reduce/reduceの競合では、パーサーはスタックのトップシンボルを非終端記号に置き換える必要があることを知っていますが、どのリダクションを使用するかを判断できません。
これが長い説明である場合は申し訳ありませんが、LR(0)とSLR(1)の解析の違いに対処できるようにするためにこれが必要です。 LR(0)パーサーは、lookaheadのゼロトークンを使用して実行するアクションを決定する(したがって0)シフト/リデュースパーサーです。これは、パーサーのどの構成でも、パーサーが選択する明確なアクションを持っている必要があることを意味します-特定のシンボルをシフトするか、特定のリダクションを適用します。選択肢が2つ以上ある場合、パーサーは失敗し、文法はLR(0)ではないと言います。
考えられる2つのLR競合は、shift/reduceとreduce/reduceであることを思い出してください。どちらの場合も、LR(0)オートマトンが実行できるアクションが少なくとも2つあり、どちらを使用するかを判断できません。競合するアクションの少なくとも1つは削減であるため、特定の削減を実行するタイミングについてパーサーにもっと注意を払わせるようにすることは、合理的な攻撃ラインです。より具体的には、パーサーが入力の次のトークンを見て、シフトするか減らすかを決定できると仮定します。パーサーが「意味をなす」ときにのみ削減することを許可する場合(「意味をなす」の定義に対して)、オートマトンにシフトまたは縮小のいずれかを明確に選択させることにより、競合を排除できる可能性があります。特定のステップ。
SLR(1)( "Simplified LR(1)")では、パーサーはシフトするか縮小するかを決定するときに先読みのトークンを1つ見ることができます。特に、パーサーがA→w(非終端記号Aおよびストリングw)の形式の何かを縮小しようとするとき、入力の次のトークンを調べます。そのトークンが何らかの派生において非終端記号Aの後に合法的に現れる可能性がある場合、パーサーは縮小します。そうでなければ、そうではありません。ここでの直観は、これまで見てきたトークンと今後のトークンを考えると、削減を試みることは意味がない場合があるということです。
LR(0)とSLR(1)の唯一の違いは、競合が発生したときに実行するアクションを決定するのに役立つこの追加機能です。このため、LR(0)パーサーで解析できるすべての文法は、SLR(1)パーサーで解析できます。ただし、SLR(1)パーサーはLR(0)よりも多くの文法を解析できます。
ただし、実際には、SLR(1)は依然としてかなり弱い解析方法です。より一般的には、LALR(1)(「Lookahead LR(1)」)パーサーが使用されていることがわかります。これらもLR(0)パーサーで競合を解決しようとすることで機能しますが、競合の解決に使用するルールはSLR(1)で使用されるルールよりもはるかに正確であり、その結果、より多くの文法がLALR(1)ですSLR(1)よりももう少し具体的に言うと、SLR(1)パーサーは、文法の構造を見て、いつシフトし、いつ縮小するかについての詳細を学習することにより、競合を解決しようとします。 LALR(1)パーサーは、文法とLR(0)パーサーの両方を調べて、シフトするタイミングと縮小するタイミングに関するさらに具体的な情報を取得します。 LALR(1)はLR(0)パーサーの構造を調べることができるため、特定の競合がスプリアスである場合をより正確に識別できます。 Linuxユーティリティyacc
およびbison
は、デフォルトでLALR(1)パーサーを生成します。
歴史的に、LALR(1)パーサーは通常、はるかに強力なLR(1)パーサーに依存する別の方法で構築されていたため、LALR(1)がそのように記述されていることがよくあります。これを理解するには、LR(1)パーサーについて話す必要があります。 LR(0)パーサーでは、パーサーはプロダクションの途中の場所を追跡することにより機能します。プロダクションの終わりに達すると、削減を試みることを知っています。ただし、パーサーは、あるプロダクションの終わりと別のプロダクションの中間にあるかどうかを判断できない場合があります。これにより、シフト/リデュースの競合が発生するか、2つの異なるプロダクションのどちらが(プロデュース/競合を減らす)。 LR(0)では、これはすぐに競合につながり、パーサーは失敗します。 SLR(1)またはLALR(1)では、パーサーは次の先読みトークンに基づいて、シフトするか減らすかを決定します。
LR(1)パーサーでは、パーサーは動作中に追加情報を追跡します。パーサーが使用されていると考えているプロダクションを追跡することに加えて、そのプロダクションの完了後に表示される可能性のあるトークンを追跡します。パーサーは、決定を行う必要があるときだけでなく、各ステップでこの情報を追跡するため、LR(1)パーサーは、LR(0)、SLR(1)、またはこれまでに説明したLALR(1)パーサー。 LR(1)は非常に強力な解析手法であり、anyshift /によって決定論的に解析できる言語があることを、いくつかのトリッキーな数学を使用して示すことができます。 reduceパーサーには、LR(1)オートマトンで解析できるいくつかの文法があります。 (これは、決定論的に解析できるすべての文法がLR(1)であることを意味しないことに注意してください;これは、決定論的に解析できる言語のみを意味しますLR(1)文法があります)。ただし、このパワーには代償が伴い、生成されたLR(1)パーサーは動作するために非常に多くの情報を必要とするため、実際には使用できない可能性があります。たとえば、実際のプログラミング言語のLR(1)パーサーでは、正常に動作するために数十から数百メガバイトの追加情報が必要になる場合があります。このため、LR(1)は通常実際には使用されず、LALR(1)やSLR(1)などのより弱いパーサーが代わりに使用されます。
最近では、GLR(0)( "Generalized LR(0)")と呼ばれる新しい解析アルゴリズムが人気を集めています。 GLR(0)パーサーは、LR(0)パーサーに表示される競合を解決するのではなく、考えられるすべてのオプションを並行して試行することで機能します。巧妙なトリックを使用して、多くの文法で非常に効率的に実行できます。さらに、GLR(0)は、コンテキストなしの文法をすべて解析できます。さらに、kのLR(k)パーサーでは解析できない文法も解析できます。 GLR(0)は実際には高速になる傾向がありますが、他のパーサーもこれを実行できます(たとえば、EarleyパーサーまたはCYKパーサー)。
さらに学習することに興味がある場合は、この夏、コンパイラの入門コースを教えて、2週間足らずで解析手法について話しました。 LR(0)、SLR(1)、およびその他の強力な解析手法のより厳密な紹介が必要な場合は、解析に関する講義スライドと宿題をお楽しみください。すべてのコース資料が利用可能ですここでは私の個人サイト 。
お役に立てれば!
これは私が学んだことです。通常、LR(0)パーサーにはあいまいさがある場合があります。つまり、テーブルの1つのボックス(パーサーを作成するために派生する)に複数の値(または)を含めることができます。そのため、このあいまいさを取り除くためにSLRパーサーが作成されます。構築するには、goto状態につながるすべてのプロダクションを検索し、左側のプロダクションシンボルのフォローを検索し、フォローに存在するgotoステートのみを含めます。この順番は、元のグラマーを使用しては不可能なプロダクションを含めないことを意味します(状態がフォローセットにないため)