コンパイラ-LL(1)-最初のセットとフォロー セット

First(A) は、A の開始シンボルまたは最初のシンボル セットです。

Follow(A) は、特定の文型で A の右側にすぐ続く終端記号のセットを指します。

次の正しい再帰文法の最初のセットとフォローのセットを見つけます。

1行目: Expr → Term Expr '

2 行目: Expr' → + Term Expr ' | - Term Expr ' | ∈

3行目: 項 → 因数項 '

4行目:項 ' → * 因数項 ' | / 因数項 ' | ∈

5行目: Factor → ident | num | ( Expr )

First(Factor) = { ident num ( } //Factor → ident | num | ( Expr ) 何から始めるかは明らかです
First(Term ') = { * / ∈ } //Term ' が含まれる文法行を見てください同様に上に配置されます
First(Term) = { ident num ( } //項は Factor で始まり、Factor は ident num ( で始まります
) First(Expr ') = { + - ∈ } First(Expr) = { ident num ( }
//Expr は Term で始まります Term の先頭には Factor が、Factor の先頭には ident num (

//Expr には常に開始位置として eof が含まれ、構文の最後の行は Expr に従うことができます)
Follow(Expr) = { eof ) }
// Expr の最初の行は Expr の終わりとして使用でき、次に Expr ' Expr のすべてのフォローセットが含まれています
// 行の右側に 2 番目の Expr ' が Expr ' として含まれています 終了 Expr ' には Expr ' 自体が含まれているため役に立ちません
Follow(Expr ') = { eof ) }
// 最初の行 Term は Expr の前に表示されます' したがって、Follow(Term) には First(Expr ') が含まれます 
/ /同時に、Expr ' は ∈ である可能性があるため、Term が Expr の最後に現れる可能性があることを意味するため、Follow(Expr) Follow
(Term) = { + - eof )
// 3 行目で Term ' が Term の最後になる可能性があることがわかります。そのため、Follow(Term) を含めます。
// 4 行目の右側の Term ' は、Term の最後にある Term ' として使用されます。 Term ' には Term ' 自体が含まれているため、役に立ちません
Follow(Term ') = { + - eof ) }
//3 行目の Factor の後には Term ' が続くので、First(Term ') を含めます
//同時に、 3 行目の Term ' が ∈ の場合は、Factor が Term の最後に現れる可能性があることを意味するため、follow(Term) を含める必要があります。
Follow(Factor) = { * / + - eof ) }

⚠️: これらのコレクションにはすべて終端が装備されています。つまり、文法で置き換える必要のない eof は、式の終了記号を表します。最初のコレクションは非常に単純ですが、次のコレクションは次のコレクションであることに注意してください


主に文法矢印の右側の内容を参照して、判定する必要がある記号の位置を見つけます。記号文字列の最後に、次のすべての記号の集合
S → A | B// follow(A) を見つける必要があるため、follow(A) には follow(B) が含まれます

次に、記号列の末尾にない場合は、次の記号の最初のセットが含まれますが、最初のセットで次の記号が ∈ になれる場合は、次の記号が消えてもよいことを意味することに注意してください。 ∈ を含まないシンボルの最初のセットが見つかるまで、次のシンボルの最初のセットを含め続ける必要があります。トラバース後に後続のすべてのシンボルの最初のセットが空の場合、次のセット S →ACbS | ∈ // follow(A ) A の後に
C が続く場合は first(C) が含まれますが、C の最初のセットに ∈ がある場合 
                              // それは C ができることを意味します消えて次の b に置き換えられます。 b は終端詞であり、間違いなく ∈ ではないため、
                              b が含まれています。したがって、プロセスは逆方向にトラバースし続け、空の最初のセットがない場合は停止します。 //If
                              you don't横断しても見つからない?つまり、A の後ろにあるものはすべて消える可能性があります。A のフォローは
                              //矢印の左側の S のフォローです。


        すべてのプロダクションの 場合、文法は LL(1) です。 
        \\X \rightarrow \beta _1|\beta _2|....|\beta _n \\First^+(X\rightarrow \beta _i) \cap (X\rightarrow \beta _j) =\oslash \\for 1\leqslant i< j\leqslant n

定義する:
        First^+(X\rightarrow \beta _i) = \begin{cases} First(\beta _i) & \text{ if } \epsilon \notin First(\beta_i)\\ First(\beta _i) \cup Follow( X) & \text{ if } それ以外の場合 \end{cases}

例2:

S→A | B

A→B

B → BACb | a //この行は左再帰なので右再帰にする必要があります

C → AD |
D → BC | 紀元前 

変換後: 

1行目:S→A | B

2段目:A→b

3行目:B→aB'

4行目:B'→ACbB' | ∈

5行目:C → AD | ∈
6行目:D → BC | bC 

最初(A) = {b}
最初(B) = {a}
最初(B ') = {b ∈}
最初(C) = {b ∈}
最初(D) = {ab}
最初(S) = {ba }

//開始位置からフォローを検索します。eof は固定の開始位置です。右側に S がなければ終了です。Follow
(S) = {eof}
//最初の行の A が、シンボルの終わりなので、Follow( S) を含める必要があります
//同時に、4 行目 A の後に C が続くので、First(C) を含める必要があります。もちろん、中に ∈ があるため、∈ は削除する必要がありますC の最初の行では、 //逆方向トラバースを続ける必要があり、bb が明らかに ∈ ではないことが確認できるので、停止します
/ / 5 行目の A の後に D が続くため、first(D) を含める必要があり、中に ∈ はありませんfirst(D) なので最後
Follow(A) = {eof ba}
//1 行目の記号 B は記号列の最後にあるので、Follow(S )
//6 行目では B が続きますby Cなのでfirst(C)が入っているがfirst(C)には∈があるので続くが記号列以降は何もない このとき、 //6行目のB以降は全てできることを意味
する消えるので、D のフォローは B のフォローなので、 follow(D) Follow
(B) = {eof b}
// 4 行目の C の後に端子 b があります 
// 6 行目の Cは記号文字列の末尾なので、D のフォローは C のフォローであるため、follow(D) Follow
(C) = {b }
//5 行目は、D に C のフォローが含まれていることを示しています
Follow(D) = { b}
// 3 行目は、B ' に B のフォローが含まれていることを示しています
Follow(B') = {eof b}

ここで興味深い循環依存関係がわかります。 B' は B に依存するものです B は D に依存するものです D は C に依存するものです C は D に依存します 


        すべてのプロダクションの  場合、文法は LL(1) です。 
        \\X \rightarrow \beta _1|\beta _2|....|\beta _n \\First^+(X\rightarrow \beta _i) \cap (X\rightarrow \beta _j) =\oslash \\for 1\leqslant i< j\leqslant n

定義する:
        First^+(X\rightarrow \beta _i) = \begin{cases} First(\beta _i) & \text{ if } \epsilon \notin First(\beta_i)\\ First(\beta _i) \cup Follow( X) & \text{ if } それ以外の場合 \end{cases}

これが LL1 の最初の行に適合するかどうかを確認してみましょう
: S → A | B 開始:
まずは^+(S → A) = First(A) = {b}
まずは^+(S → B) = First(B) = {a}
まずは^+(S → A)  \キャップ まずは^+(S → B) = \o        パス

2 行目と 3 行目は選択肢が 1 つしかないため、チェックする必要はありません。

4行目 B' → ACbB' | ∈
まずは^+(B' → ACbB') = {b}
まずは^+(B' → ∈) = Follow(B') = {eof b}
まずは^+(B' → ACbB')  \キャップまずは^+(B' → ∈ ) = {b} \neq \O
したがって、エラーは LL1 が適用されないことを証明します

以前、左再帰から右再帰に変更して、LL1に適用できる文法にしました。特定の文法がLL1であるかどうかを確認できます。これは、上記の処理と同じです。つまり、LL1に適用できない文法であることを意味します。 LL1 とは、何らかの変換を行うことで
あらゆる文法を LL1 に適用できるという意味ですか? 明らかにそうではありません。一部の文法は、どのように変換しても LL1 解析スキルに適していません。 
また、特定の文法が LL1 に正常に変換できるかどうかを直接判断できるアルゴリズムはありません。努力し続けるしかありません。おそらく 1 回だけ変換されるでしょう。成功するか失敗するかは成功しません。変換後にのみ検証できます。大きな問題のように聞こえますが、実際には、ほとんどのコンテキストフリー言語は経験によって LL1 に変換できます。したがって、ロジックは必要ありません実際には、経験を通じて最大値を得ることができます 


LL(1) よりも一般的な文法は LR(1) であり、    
その最も中心となるのは正規言語であり、正規言語はそれを制約するための文法さえ必要とせず、有限オートマトンのみを使用して表現することができます。
この輪を回して出かけましょう 言語の文法はますます複雑になり、表現を制限することはますます困難になります 表現を制限するのが最も難しいのは、人間の日常的な自然言語に違いないと想像できますなぜなら、人々が話す自然言語は、文法に従っていない、または文法を追加していないことが多いからです。一連の厳格な文法やコードで感情や連想を抑制することは、ほとんど不可能です。これも本質的には、現代の人工知能にとって乗り越えられないギャップ。

たとえば、次の 2 つの文です。

群衆の中にあなたがいるのは
起源によるものです 群衆の中にあなたがいるのは運命によるものです

CPUが燃えて煙が出ると思いますが、芸術的な概念が理解できません

おすすめ

転載: blog.csdn.net/weixin_43754049/article/details/126284719