LR、SLR、およびLALRパーサーの違いは何ですか?


103

LR、SLR、およびLALRパーサーの実際の違いは何ですか?SLRとLALRはLRパーサーのタイプであることを知っていますが、それらの解析テーブルに関する限り、実際の違いは何ですか?

また、文法がLR、SLR、またはLALRであるかどうかを示す方法は?LL文法の場合、解析テーブルのセルに複数のプロダクションルールが含まれていてはならないことを示す必要があります。LALR、SLR、およびLRに同様のルールはありますか?

たとえば、どのように文法を示すことができますか

S --> Aa | bAc | dc | bda
A --> d

LALR(1)ではなくSLR(1)ですか?


編集(ybungalobill):LALRとLRの違いは何なのか、満足のいく答えが得られませんでした。したがって、LALRのテーブルはサイズが小さくなりますが、認識できるのはLR文法のサブセットのみです。誰かがLALRとLRの違いについて詳しく説明できますか?LALR(1)とLR(1)で回答できます。どちらも1トークンの先読みを使用し、どちらもテーブル駆動型です。それらはどのように違うのですか?


まあ、私がこれについて適切な答えを探しているとしても、LALR(1)はLR(1)のわずかな変更であり、メモリ使用量を最小化できるようにテーブルサイズが縮小されます...
vikkyhacks

回答:


64

SLR、LALR、およびLRパーサーはすべて、まったく同じテーブル駆動機構を使用して実装できます。

基本的に、解析アルゴリズムは次の入力トークンTを収集し、現在の状態S(および関連する先読み、GOTO、および削減テーブル)を調べて、何をすべきかを決定します。

  • SHIFT:現在のテーブルがトークンTをSHIFTするように指示すると、ペア(S、T)が解析スタックにプッシュされ、GOTOテーブルが現在のトークンに対して言っていること(GOTO(T)など)に従って状態が変更されます。 )、別の入力トークンT 'がフェッチされ、プロセスが繰り返されます
  • 削減:すべての状態には0、1、または状態で発生する可能性のある多くの可能な削減があります。パーサーがLRまたはLALRの場合、トークンは、状態のすべての有効な削減について先読みセットに対してチェックされます。トークンが文法規則G = R1 R2 .. Rnの削減の先読みセットと一致する場合、スタックの削減とシフトが発生します。Gのセマンティックアクションが呼び出され、スタックが(Rnから)n回ポップされ、ペア( S、G)がスタックにプッシュされ、新しい状態S 'がGOTO(G)に設定され、同じトークンTでサイクルが繰り返されます。パーサーがSLRパーサーの場合、最大で1つの削減ルールがあります。状態など、どの削減が適用されるかを検索することなく、削減アクションを盲目的に実行できます。SLRパーサーが存在するかどうかを知るのに役立ちます削減かどうか。これは、各状態がそれに関連付けられた削減の数を明示的に記録しているかどうかを簡単に判断でき、その数は実際のL(AL)Rバージョンに必要です。
  • エラー:SHIFTもREDUCEもできない場合、構文エラーが宣言されます。

では、それらすべてが同じ機械を使用しているとしたら、何が重要なのでしょうか。

SLRで意図されている値は、実装が単純であることです。ルックアヘッドセットは1つしか存在しないため、可能な削減をチェックしてスキャンする必要はありません。これは、状態からのSHIFT出口がない場合に実行可能な唯一のアクションです。どの削減が適用されるかは、特に州に関連付けることができるため、SLR解析機械はそれを探す必要がありません。実際には、L(AL)Rパーサーは有用なより多くの言語のセットを処理し、学術的な演習以外には誰もSLRを実装しないほど実装する余分な作業はほとんどありません。

LALRとLRの違いは、テーブルジェネレーターと関係があります。。LRパーサージェネレーターは、特定の状態からのすべての可能な削減とそれらの正確な先読みセットを追跡します。最終的に、すべての削減が左側のコンテキストからの正確な先読みセットに関連付けられた状態になります。これは、かなり大きな状態のセットを構築する傾向があります。LALRパーサージェネレーターは、GOTOテーブルと縮約用のルックヘッドセットに互換性があり、競合しない場合、状態を組み合わせてもかまいません。これは、LRが区別できる特定のシンボルシーケンスを区別できないという犠牲を払って、かなり少ない数の状態を生成します。したがって、LRパーサーはLALRパーサーよりも多くの言語セットを解析できますが、非常に大きなパーサーテーブルがあります。実際には、ステートマシンのサイズを最適化する価値があるターゲット言語に十分近いLALR文法を見つけることができます。

つまり:3つすべてが同じ機械を使用します。SLRは、機械のごく一部を無視できるという意味で「簡単」ですが、問題を起こすだけの価値はありません。LRはより幅広い言語セットを解析しますが、状態テーブルはかなり大きくなる傾向があります。LALRは実用的な選択肢です。

以上のことをすべて述べた上で、GLRパーサーは、より複雑な機構を使用して、まったく同じテーブル(LALRで使用される小さいバージョンを含む)を使用して、コンテキストフリー言語を解析できることを知っておく価値があります。つまり、GLRはLR、LALR、SLRよりも厳密に強力です。標準のBNF文法を記述できれば、GLRはそれに従って構文解析します。機構の違いは、GOTOテーブルと先読みセットの間に競合がある場合、GLRが複数の解析を喜んで試行することです。(GLRがこれを効率的に行う方法は、まさに天才です(私のものではありません)が、このSOポストには適合しません)。

それは私にとって非常に役立つ事実です。私はプログラムアナライザーをビルドし、コードトランスフォーマーとパーサーは必要ですが、「面白くない」です。興味深い作業は、解析された結果を使用して行うことなので、解析後の作業を行うことに焦点が当てられています。GLRを使用すると、文法をハッキングしてLALRの使用可能な形式にするよりも、実用的な文法を比較的簡単に作成できます。これは、C ++やFortranなどの非アカデミックな言語に対処しようとするときに非常に重要です。C++やFortranは、言語全体を適切に処理するために文字通り何千ものルールを必要とし、文法ルールをハックしようとして人生を費やしたくない場合に重要です。 LALR(またはLR)の制限を満たします。

一種の有名な例として、C ++はLALR構文解析をしている人たちにとって、構文解析が非常に難しいと考えられています。C ++は、C ++リファレンスマニュアルの裏に記載されているほとんどの規則を使用して、GLR機構を使用して簡単に解析できます。(私はまさにそのようなパーサーを持っており、それはバニラC ++だけでなく、さまざまなベンダーの方言も扱います。これは、GLRパーサー、IMHOを使用しているため、実際にのみ可能です)。

[2011年11月編集:すべてのC ++ 11を処理できるようにパーサーを拡張しました。GLRはそれをはるかに簡単にしました。2014年8月の編集:C ++ 17のすべてを処理するようになりました。何も壊れたり悪化したりすることはなく、GLRはまだ猫の鳴き声です。]


AFAIK C ++は、LRで解析できません。無限の先読みが必要だからです。だから、LRでそれを解析することを可能にするハックを考えることはできません。また、LREパーサーは有望に聞こえます。
Yakov Galka

5
GCCは、Bison == LALRを使用してC ++を解析するために使用されていました。いつでもパーサーに追加のgooを追加して、心に痛むケース(先読み、is-this-a-typename)を処理できます。問題は「ハックがどれほど痛いか」です。GCCにとってそれはかなり苦痛でしたが、彼らはそれを機能させました。これは、これが推奨されるという意味ではありません。これが、GLRの使用に関する私のポイントです。
Ira Baxter、

GLRの使用がC ++でどのように役立つかわかりません。何かがタイプ名であるかどうかわからない場合は、パーサーの方法がわからないだけですx * y;。GLRを使用すると、どのように役立ちますか。
user541686 2012年

2
重要なのは、GLRパーサーが両方の解析を(「あいまいなサブツリー」として)統合された解析「ツリー」(実際にはDAG)で生成するということです。後で保持するサブリーを解決するには、他のサブリーをコンテキスト情報。この問題については、C ++パーサーが非常に単純に考えています。問題を解決しようとするものではありません。つまり、構文解析でシンボルテーブルの構造を複雑にする必要がないため、C ++のパーサーとシンボルテーブルの構造の両方を使用できます。個々にクリーンであり、その結果、それぞれが構築および維持することが多くなります
Ira Baxter

18

LALRパーサーは、LR文法内の同様の状態をマージして、同等のSLR文法と正確に同じサイズのパーサー状態テーブルを生成します。これは通常、純粋なLR構文解析テーブルより1桁小さいサイズです。ただし、LALRにするには複雑すぎるLR文法の場合、これらのマージされた状態により、パーサーの競合が発生するか、元のLR文法を完全には認識しないパーサーが生成されます。

ところで、私はここで私のMLR(k)解析テーブルアルゴリズムこれについていくつか言及しています

補遺

簡単に言えば、LALR解析テーブルは小さいですが、パーサーの機構は同じです。特定のLALR文法は、すべてのLR状態が生成され、冗長な(ほぼ同一の)状態が多数ある場合、はるかに大きな解析テーブルを生成します。

LALRテーブルは小さくなります。これは、類似した(冗長な)状態がマージされ、個別の状態がエンコードするコンテキスト/先読み情報を効果的に破棄するためです。利点は、同じ文法に対してはるかに小さな解析テーブルが得られることです。

欠点は、すべてのLR文法をLALRテーブルとしてエンコードできるわけではないことです。これは、より複雑な文法にはより複雑な先読みがあり、単一のマージされた状態ではなく2つ以上の状態になるためです。

主な違いは、LRテーブルを作成するアルゴリズムは、状態から状態への遷移間でより多くの情報を運ぶのに対し、LALRアルゴリズムはそうではないということです。したがって、LALRアルゴリズムは、特定のマージされた状態を2つ以上の個別の状態のままにする必要があるかどうかを判断できません。


3
+1私はホナリーのアイデアが好きです。私のG / L(AL)Rパーサージェネレーターには、このようなものの種が含まれていました。それは最小限のLALRマシンを生成し、それから私は衝突があった状態を分割するつもりでしたが、私は実行しませんでした。これは、解析テーブルのセットのような最小サイズの「LR」を生成するための良い方法のように見えます。何を解析できるかという点ではGLRの助けにはなりませんが、GLRが実行しなければならない並列解析の数を減らす可能性があり、それは有用でしょう。
Ira Baxter、

12

さらに別の答え(YAA)。

SLR(1)、LALR(1)、およびLR(1)の解析アルゴリズムは、Ira Baxterが述べたように同一です
が、パーサー生成アルゴリズムのため、パーサーテーブルは異なる場合があります。

SLRパーサージェネレーターはLR(0)ステートマシンを作成し、文法(FIRSTおよびFOLLOWセット)から先読みを計算します。これは簡略化されたアプローチであり、LR(0)状態マシンには実際には存在しない競合を報告する可能性があります。

LALRパーサージェネレーターはLR(0)ステートマシンを作成し、LR(0)ステートマシンから先読みを計算します(端末遷移を介して)。これは正しいアプローチですが、LR(1)状態マシンには存在しない競合を時々報告します。

Canonical LRパーサージェネレーターはLR(1)ステートマシンを計算し、先読みはすでにLR(1)ステートマシンの一部です。これらのパーサーテーブルは非常に大きくなる可能性があります。

最小LRパーサージェネレーターはLR(1)ステートマシンを計算しますが、プロセス中に互換性のある状態をマージしてから、最小LR(1)ステートマシンからの先読みを計算します。これらのパーサーテーブルは、LALRパーサーテーブルと同じサイズか少し大きいため、最適なソリューションが得られます。

LRSTAR 10.0は、文法に必要なものは何でも、C ++でLALR(1)、LR(1)、CLR(1)、またはLR(*)パーサーを生成できます。LRパーサー間の違いを示すこの図を参照してください。

[完全な開示:LRSTARは私の製品です]


5

先読みのないパーサーが文法の文字列を楽しく解析していると仮定します。

あなたの与えられた例を使用して、それは文字列dcに出くわします、それは何をしますか?この文法によって生成される有効な文字列なSので、それはに減少しdcますか?またはbdc、それでも許容される文字列であるため、解析しようとしていたのでしょうか?

人間は答えが簡単であることを知っているので、解析したbかどうかを覚えておく必要があります。しかし、コンピュータは愚かです:)

SLR(1)パーサーには、先読みを実行するためのLR(0)よりも強力な機能があったため、この場合、先読みの量によっては何をすべきかを指示できないことがわかります。代わりに、過去を振り返る必要があります。したがって、正規のLRパーサーが役に立ちます。過去の状況を記憶しています。

このコンテキストを覚えている方法は、自分自身を統制していることです。1つの可能性としてb、に遭遇するたびに、を読むための道を歩き始めるということbdcです。ですから、それを見ると、dそれがすでに道を歩いているかどうかがわかります。したがって、CLR(1)パーサーはSLR(1)パーサーができないことを実行できます!

しかし、今は非常に多くのパスを定義する必要があったため、マシンの状態は非常に大きくなります。

したがって、同じように見えるパスをマージしますが、予想通り、混乱の問題が発生する可能性があります。ただし、サイズを縮小する代わりにリスクを負うことをいとわない。

これはLALR(1)パーサーです。


それをアルゴリズム的に行う方法。

上記の言語の設定セットを描画すると、2つの状態でShift-Reduceの競合が発生します。それらを削除するには、SLR(1)を検討することをお勧めします。SLR(1)は、フォローを見ながら決定を下しますが、それでもまだできないことに注意してください。したがって、構成セットを再度描画しますが、今回は、クロージャーを計算するときは常に、追加される追加のプロダクションには厳密なフォローが必要であるという制限があります。これらがどうあるべきかについてのテキストを参照してください。


これは正確ではありません

4

SLRとLRで生成されたパーサーテーブルの基本的な違いは、reduceアクションはSLRテーブルに設定されたFollowsに基づいているということです。これは過度に制限され、最終的にはシフトと削減の競合を引き起こす可能性があります。

一方、LRパーサーは、削減される非端末を実際に追跡できる端末のセットにのみ基づいて決定を削減します。この端末のセットは、多くの場合、このような非端末のフォローセットの適切なサブセットであるため、シフトアクションと競合する可能性が低くなります。

このため、LRパーサーはより強力です。ただし、LR解析テーブルは非常に大きくなる可能性があります。

LALRパーサーは、LR解析テーブルを作成するというアイデアから始まりますが、生成された状態を結合して、テーブルサイズを大幅に削減します。短所は、LRテーブルが別の方法で回避していたいくつかの文法に対して、競合の小さな可能性が導入されることです。

LALRパーサーはLRパーサーよりわずかに強力ではありませんが、SLRパーサーよりも強力です。YACCおよび他のそのようなパーサージェネレーターは、この理由からLALRを使用する傾向があります。

PS簡潔にするために、上記のSLR、LALRおよびLRは、実際にはSLR(1)、LALR(1)、およびLR(1)を意味するため、1つのトークンルックアヘッドが暗示されます。


4

SLRパーサーは、LALR(1)パーサーによって認識可能な文法の適切なサブセットを認識します。LALR(1)パーサーは、LR(1)パーサーによって認識可能な文法の適切なサブセットを認識します。

これらはそれぞれ状態マシンとして構築され、各状態は、入力を解析するときに、文法の生成規則(およびそれぞれの位置)のセットを表します。

SLRではないLALR(1)文法のDragon Bookの例は次のとおりです。

S → L = R | R
L → * R | id
R → L

この文法の状態の1つを次に示します。

S → L•= R
R → L•

可能なプロダクションの各々におけるパーサの位置を示しています。それが最後に達して縮小しようとするまで、実際にどのプロダクションに入っているかはわかりません。

ここで、パーサーはシフト=または削減のいずれかを行うことができますR → L

SLR(LR(0)別名)パーサは次の入力シンボルがである場合、それは確認することで減らすことができるかどうかを決定するであろう次のセットR(続くことができる文法内の全ての端末のすなわち、セットR)。以来=、このセットでもあり、SLRパーサーはシフト-reduce衝突が発生しました。

ただし、LALR(1)パーサーは、Rのこの特定の生成に従うことができるすべての端末のセットを使用し$ます。したがって、競合はありません。

以前のコメントで述べたように、LALR(1)パーサーはSLRパーサーと同じ数の状態を持っています。先読み伝播アルゴリズムを使用して、対応するLR(1)状態からのSLR状態生成に先読みを付加します。結果のLALR(1)パーサーは、LR(1)パーサーには存在しないreduce-reduce競合を導入できますが、shift-reduce競合を導入することはできません。

この例では、次のLALR(1)状態により、SLR実装でShift-Reduce競合が発生します。

S → b d•a / $
A → d• / c

後の記号/は、LALR(1)パーサーの各プロダクションのフォローセットです。SLRでは、follow(A)に includeが含まれますがa、これもシフトできます。


2

上記の回答に加えて、この図はさまざまなパーサーがどのように関連しているかを示しています。

ここに画像の説明を入力してください


-2

簡単な答えの1つは、すべてのLR(1)文法はLALR(1)文法であるということです。LALR(1)と比較して、LR(1)は関連する有限状態マシンでより多くの状態を持っています(状態の2倍以上)。そして、それがLALR(1)文法がLR(1)文法よりも構文エラーを検出するためにより多くのコードを必要とする主な理由です。そして、これら2つの文法に関して知っておくべきもう1つの重要なことは、LR(1)文法では、競合の削減/削減が少なくなる可能性があるということです。しかし、LALR(1)では、競合を減らしたり減らしたりする可能性が高くなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.