うさぎでもわかるコンパイラ 第3羽 First・Follow・Director集合とLL(1)文法の判定

スポンサードリンク

こんにちは、ももやまです。

コンパイラ(言語処理系)の勉強をしていると、

  • LL(1)文法
  • First集合
  • Follow集合
  • Director集合

などの不思議な用語が出てきますよね。しかも、これらの定義(特にFirst集合やFollow集合)の定義は少し難解です。

文法 G=(VN,VT,P,S)に関して、記号列 α(VNVT)、非終端記号 AVT について、First (α), および Follow は次のように定義される。First (α)={aaVT,αa}Follow (A)={aaVT,SAa}ただし αε なら εFirst (α) とする。

コンパイラ補助資料 佐々木晃 より

そこで今回は、

  • LL(1)文法とはそもそも何か?
  • First, Follow, Director集合を求めて何がうれしいのか
  • First, Follow, Director集合の求め方

の3点を中心に、LL(1)文法とFirst・Follow・Director集合のお勉強をしていきましょう。

文字表記に関する注意

本記事では特に指示がない限り、文章中に出てくる小文字・大文字・ギリシャ文字は

  • 小文字 a, b, c, d, … → 終端記号
  • 大文字 S, A, B, C, … → 非終端記号
  • ε → 空文字

を表します。

スポンサードリンク

1. LL(1)文法とは

ある文字列 α が文法規則を満たすかどうかを一番単純に調べる方法は、下のように「出発記号 S から変換規則を総当たりで試していき、試した中に α が含まれるか」を調べることです。

f:id:momoyama1192:20190906161619g:plain
一番単純に文字列が文法規則を満たすか調べる方法

しかし、やみくもに総当たりで試す方法は、処理的にかなり無駄が発生してしまいます[1] … Continue reading

そこで、構文解析をする際に1文字だけ先読みし、どの生成規則を使えばよいかを決めてから実際に変換していく方法が考えられました。

このように、1文字だけ先読みすれば、文字列を後戻りすることなく構文解析ができる(文法を満たすか確認できる)文法からなる文法のことをLL(1)文法と呼びます[2] … Continue reading

LL(1)文法とは言えない文法(=1文字だけ先読みするだけでは文字列を後戻りせずに構文解析ができない)文法の例も見てみましょう。

例えば、非終端記号(かつ出発記号) S、終端記号 a, b、生成規則SaaS,  SabS,  Sbからなる文法規則があるとします。

この文法規則に abab という文字列が当てはまるかどうかを1文字先読みで見ていきましょう。

しかし、1文字目 a が出てくる生成規則には、

  • SaaS
  • SabS

の2つがあるため、1文字先読みしただけでは SaaS, SabS どちらの生成規則を適用すればいいかわかりません。

この例から分かる通り、LL(1)文法かどうかは文法の生成規則のみで決まります。

ここで、LL(1)文法を満たす生成規則であるかを機械的に確認する手法として導入されたのがFirst・Follow・Director集合という概念なのです!

つぎの第2章からは、

  • 実際にFirst, Follow, Director集合の求め方の解説
  • なぜ機械的にLL(1)文法を満たすのかがわかるのか

の2点について解説していきます。

スポンサードリンク

2. First集合

(1) First 集合とはなにか

First集合 First(α) は、「ある文字列 α に対して生成規則を適用して(複数回適用OK)全て終端記号にした際に、先頭(1文字目)に出てくる可能性がある文字を集めた集合」を表します。

数式で書くと、First(α)={aaVT,αa}となります。(色部分を日本語の説明に対応させています)

First集合のイメージ

例えば、First(α)={a,b,c} の場合、α に生成規則を適用していった際に出来る文字列の先頭(1文字目)が a, b, c のいずれかであることを表します。

(2) First 集合で頭にいれておくべき公式

※ 公式を丸暗記するのではなく、理屈を理解してから頭に入れましょう。

規則1[R1] First(a) … 終端記号1文字 a の場合

終端記号 a は、これ以上生成規則を適用することが出来ない文字を表すのでしたね。そのため、a はどう頑張っても他の文字に変換されることはありません。

よって、終端記号1文字 a のFirst集合の要素は終端記号 a だけとなります。First(a)=a※ 空文字 ε のFirst集合は、First(ε)={ε} となります。言い換えると、First(α)={ε} は、α が空文字であることを表します。

※ 以後、この規則を用いた変換をする場合は =R1 と = の上にR1(規則1)を用いていることを示します。

規則2[R2] First(A) … 非終端記号1文字の場合

非終端記号 A のFirst集合 First(A) を求める際、下のように樹形図を書いていって先頭1文字目に出てる文字を地味に探しても正しい答えは出てきますが、列挙ミスが発生する場合があります。

一番単純な FirstA の求め方

なのでもう少し機械的に First(A) を求めてみましょう。

例えば、ABC という生成規則があったとします。これを言い換えると、「BC に対して生成規則を適用し、すべて終端記号に変換した際に出てくる文字列」は、必ず「A に対して生成規則を適用し、すべて終端記号に変換した際に出てくる文字列」 として出てきますね。

言い換えると、「BC に生成先を適用してすべて終端記号にした際に出てくる先頭文字 First(BC) の集合」の要素に、「A に生成先を適用してすべて終端記号にした際に出てくる先頭文字 First(A) の集合」が含まれますね。

つまり、 First(A) を機械的に求める手順としては、A から生成規則 Aα, Aβ, … を探していき、その生成先の文字列 α, β, … のFirst集合 First(α), First(β) の要素をすべてFirst集合 First(A) の要素とすればOKです。

1つ例を見てみましょう。A から始まる生成規則 Aε, AaB, ACb が3つあったとします。

この場合、First(A) の要素は、First(ε), First(aB), First(Cb) の和First(A)=First(ε)AεFirst(aB)AaBFirst(Cb)ACbとなります。

ここで1点注意が必要です非終端記号 A から空文字 ε が生成される可能性があるときには、First(A) の要素に ε が加わる点に注意してください。

※ 以後、この規則を用いた変換をする場合は =R2 と = の上にR2(規則2)を用いていることを示します。

規則3[R3] First(αβ…) … 2文字以上の場合

First集合は、「文字列に生成規則を加えて全て終端記号にした際に、先頭に来る可能性がある終端記号」を表しているのでしたね。

そのため、(First集合を求めるだけであれば)最初の1文字さえわかってしまえば、(最初の1文字が空文字とはならない限り)残りの後ろは何が来ようが関係ありません。

よって、文字列 αβ のFirst集合 First(αβ) は、α に空文字が来ない限り(= εFirst(α) の場合)は First(α) と等しくなります。

一方、α に空文字が来る可能性があるとき(= εFirst(α) の場合)は、2文字目が先頭になる可能性があるため、1文字目に加えて2文字目 β の先頭文字 First(β) も追加で調べます。つまり、First(αβ)=(First(α){ε})First(β)が成立します。

ε を引いた理由は、2文字目 β の先頭に空文字 ε が来ない場合、文字列 αββ の文字列が残り、文字列 αβ が空文字 ε になることはないからです。

まとめると、2文字以上の文字列 αβ のFirst集合 First(αβ) は、First(αβ)={First(α)εFirst(α)(First(α){ε})First(β)εFirst(α)で求めることができます。

※ 以後、この規則を用いた変換をする場合は =R3 と = の上にR3(規則3)を用いていることを示します。

注意: 左再帰 A → Aα が含まれる場合のFirst集合

AAbc のように「生成先の文字列の先頭生成元の文字列になっている」場合、First集合を求めていくとFirst(A)=R2First(Abc)=R3First(A)となり、求めたい集合 First(A) が計算途中に出てくるというよくわからないことが起こります。

この形になった場合は、計算途中に出てくる First(A) は無視してOKです。例えば、First(A)=First(A)First(b)となった場合は、左辺にも右辺にも出てくる First(A) を無視し、First(A)=First(b)として計算してください[3] AAb のように左再帰文法となる規則を適用しても、先頭文字列は A のまま変わらないため、無視しても問題ない。

また、ABb,   BAa,   Acのように、見た目に左再帰になる生成規則は含まれてはいないものの、変形していくと、ABbAabのように間接的に左再帰の形が出てくる場合、First(A)=First(B)First(c)First(B)=First(A)のように、一意には答えを決定できないような形が出てくることがあります。

しかし、左再帰になる規則の部分だけに着目してみると、First(A)=First(B), First(B)=First(A) 以外の式が出てこず、特に新しい要素が足される気配(例えば First(c) はありません。

そのため、間接的な左再帰が出てきた際(=一意に答えが決定できないような形が出てきた場合)には、間接的な左再帰が出てくる式(例えば ABb, BAa)をすべて無視し、残りの生成規則からFirst集合を求めてください。ただし、間接的な左再帰に寄与している非終端記号(今回の例だと A, B)同士のFirst集合は等しくなる点は忘れないでください。

今回の場合は、ABb, BAa, Ac のうちの赤文字部分が間接的な左再帰なため、まずは間接的な左再帰とは関係ない生成規則 Ac からFirst(A)=R1First(c)={c}と求めます。さらに、間接的な左再帰に寄与している A, B のFirst集合は等しくなるため、First(B)=First(A)={c}と求めることができます[4]いったん\[\begin{align*}\mathrm{First} (A) & = \mathrm{First} (B) \cup \mathrm{First} (c)\ & = \underbrace{ \mathrm{First} (A) }_{ \mathrm{無視} } \cup \mathrm{First} (c)\ & = … Continue reading

First集合を求める際に使う3つの公式まとめ

公式1[R1] 終端記号1文字 a のFirst集合の要素はそのまま a First(a)=R1{a}

※1: 例題・練習問題で公式1を適用する場合は =R1 と表記
※2: First(ε)={ε} である。
※3: First(α)={ε} は、α が空文字であることを表す( α=ε )

公式2[R2] 非終端記号1文字 A のFirst集合の要素は、A から生成される生成規則 Aα, Aβ, Aγ, … の生成先(右辺)のFirst集合の和First(A)=R2First(α)AαFirst(β)AβFirst(γ)Aγ

※1. 例題・練習問題で公式2を適用する場合は =R2 と表記
※2. AAα のような左再帰が含まれる文法が出てきた場合は無視する(間接的な左再帰、例えば ABα, BAβ など)が出てきた場合も無視するが、間接的な左再帰に寄与する非終端記号(例の場合だと A, B)同士のFirst集合は等しくなる。)。

公式3[R3] 2文字以上の文字列 αβ のFirst集合の要素は、1文字目 α に空文字の可能性がなければ1文字目のFirst集合、1文字目 α に空文字の可能性があれば1文字目 α と2文字目 β のFirst集合First(αβ)=R3{First(α)(εFirst(α))First(α)First(β)(εFirst(α))

※1: 例題・練習問題で公式3を適用する場合は =R3 と表記
※2: 1文字目も2文字目も空文字の可能性があれば、1文字目~3文字目のFirst集合の和を取ればOK[5]一般化すると、1文字目からn文字目までの文字すべてに空文字があれば、1文字目~n+1文字目の和を取ればOK。

(3) 例題で確認!

ここからは、1題例題を実際に解いてみましょう。

例題1

次の文法 G がある。G=( {S,A,B} , {a,b,c,d} , P , S )P={    SaAAbBa | εBSc | d       }この文法 G に対し、

(1) First(S)
(2) First(A)
(3) First(B)

をそれぞれ求めなさい。

解説の前に、First集合を求める手順について確認しておきましょう。

First集合求める手順

ある非終端記号 A のFirst集合 First(A) は次の手順で求める。

Step1. A が生成元となる規則AαAβAγ  をすべて探す。

Step2. Step1で探したすべての生成規則の生成先(右辺)α, β, γ のFirst集合 First(α), First(β), First(γ) を求める。

Step3. Step2で求めたFirst集合の和を取る。First(A)=First(α)AαFirst(β)AβFirst(γ)Aγ

First(S) の求め方

S が生成元になる規則は、SaA ただ1つですね。

そのため、First(S)=R2First(aA)SaA=R3First(a)=R1{a}で計算できます。

First(A) の求め方

A が生成元になる規則は、AbBa | ε、つまり AbBaAε の2つですね。

そのため、First(A)=R2First(bBa)AbBaFirst(ε)Aεを求めればOKです。

ここで、それぞれのFirst集合は、First(bBa)=R3First(b)=R1{b}First(ε)=R1{ε}となるため、求めたいFirst集合はFirst(A)=R2First(bBa)First(ε)={b}{ε}={b,ε}と計算できます。

First(B) の求め方

B が生成元になる規則は、BSc | d、つまり BScBd の2つですね。

そのため、First(B)=R2First(Sc)BScFirst(d)Bdを求めればOKです。

ここで、それぞれのFirst集合は、First(Sc)=R3First(S)=(1){a}First(d)=R1{d}となるため、求めたいFirst集合はFirst(B)=R2First(Sc)First(d)={a}{d}={a,d}と計算できます。

=(1) は、(1)の答え First (S)={a} を使っていることを表しています。

スポンサードリンク

3. Follow集合

(1) Follow 集合とはなにか

Follow集合 Follow(A) は、「ある非終端記号1文字 A 以降の文字をの直後に来る可能性がある文字終端記号)の候補を集めた集合」を表します。

数式で書くと、Follow(A)={aaVT,αAa}となります。(色部分を日本語の説明に対応させています)

Follow集合のイメージ
(水色の丸部分に来る終端記号の文字の候補を表す)

例えば、Follow(A)={a,b} の場合、A の後ろの文字列をすべて終端記号にした際、A の直後に来る文字列が a, b のいずれかであることを表します。

1点注意が必要なのは、Follow集合の要素に「空文字 ε」が出てくることは絶対にありません。

その代わりに、「A の直後に来る文字はありません(=A が文字列の終端になりますよ)」というのを表す記号 $ がFollow集合の要素で使われます。

(2) Follow 集合で頭にいれておくべき3つの公式

各非終端記号に対してFollow集合を求めていくときには、公式1~公式3で出てきた要素すべての和を取ることで求めます。

そこで、この「(2) Follow集合で頭にいれておくべき3つの公式」では、公式1~公式3の式の紹介だけでなく、何故この式でFollow集合が求められるのかまで説明していきます。

公式1. 出発記号 S の直後は必ず文字列の終端 $

文脈自由文法では、下のようにどの文字列も出発記号 S から与えられた生成規則により変換されていき、導出されます。SSAaBAaBAaAaAaBAaaaaBBbaaab

そのため、出発記号の後ろは必ず文字列の終端となりますね。

そのため、最初に出発記号に対するFollow集合 Follow(S) に対しては、文字列の終端を表す $ を要素に追加します。Follow(S)+={$}

本記事では、Follow集合の要素追加をこんな感じに図でも記します!

※1: 出発記号ではない非終端記号に対しては何もしなくてOKです。
※2: X+=Y は、(集合 X に含まれない)集合 Y の要素を集合 X に加えることを表します。数式で書くと、X=XY です。

公式2. 直後の文字が空文字にならない場合

Follow集合は、「ある非終端記号の直後に来る(終端記号の)文字の候補」を表すため、Follow集合を求める際には、まずは生成規則の生成元ではなく、生成先側の文字列に着目します。

例えば、SaAcB という規則があったとします。この規則を適用すると、A の直後には必ず cB という文字が来ますよね。

このように、SaAcB のような求めたい非終端記号 A が生成されている規則を見つけ、その直後に来る文字(今回は cを見つけ出す(+非終端記号であれば終端記号に変換する)ことでFollow集合を求められそうですね。

もう少し一般化して、Follow(A) の求め方を公式化していきましょう。

まず、求めたい非終端記号 A が生成されている規則を探し出し、生成先の文字列を A より前にある文字列 αA より後ろにある文字列 β に分け、SαAβ の形にします。

例えば、SaAcB であれば、SaαAcBβより、α=a, β=cB となります。

※1 α はFollow集合の計算ではいらない子なので、求めなくてもOK。
※2 α, β に空文字が来てもOK。例えば、SaA であれば、α=a, β=ε となる。

ここで、Follow(A) というのは、A の直後に出てくる文字列でしたね。

また、先ほど生成規則を SαAβ と書き換えましたね。つまり、「 A の直後に出てくる文字列」というのは、「β の先頭に来る文字列」と言い換えることができますね。さらにもう1段階言い換えると、「β の先頭に来る文字列」は First(β) と書くことができますね。

そのため、生成規則 SαAβ に対して、Follow集合にFollow(A)+=First(β)First(β) を追加します。

注意: 追加時に ε を無視すること

ただし、First集合の要素に ε が含まれる場合は、ε を無視してからFollow集合に追加してください。例えば、SαAβ に対して、First(β)={b,c,ε} であれば、Follow(A)+=First(β)={b,c}と、Follow集合に b, c の要素を加えます。そのため、SαA のように A の直後の文字列 β がそもそも存在しない場合は、この公式でFollow集合の追加は行われません[6]β がそもそも存在しない、つまり β=ε のときは、\( \mathrm{First} ( \beta ) = \mathrm{First} ( \varepsilon )  \overset{ \mathrm{R1} }{=} \{ … Continue reading

さらに、First(β)ε が含まれる(= β が空文字になる可能性がある)場合や、SαA のように、 そもそも A が生成先の文字列の末尾になる場合は公式2を確認します。

公式3. 直後の文字が空文字になる可能性がある場合

生成規則 SαAβ において、

  • β 部分がそもそも存在しない
    (つまり AαA となるとき)
  • β 部分が存在していた場合でも SααAB, Bε のように β に空文字が来る可能性がある場合
    (つまり εFirst(β) となる場合)

は、生成先 αAβ に着目するだけでは、A の直後に来る文字列(= Follow(A))がわかりません。

ここで、S の直後の文字を X としてから、SX に生成規則 SaA を適用することを考えましょう。すると、SXaAXとなり、「A の直後の文字」が「S の直後の文字」と同じになっていることがわかりますね。

青丸部分が空文字なら、A の直後の文字列は S の直後の文字列となる。

つまり、SεFirst(β) 、つまり β 部分が空文字になる(つまり SaA となる)可能性がある場合はFollow(A)+=Follow(S)と、生成元 S の非終端記号の直後の文字、つまり Follow(S)Follow(A) に追加します。

εFirst(β \) のとき、で1つにまとめてもOK
(本記事では分かりやすさ重視のため、2つに分けています)

注意: 右再帰 A → αAが含まれる場合

AabA のように「生成先の文字列の終端生成元の文字列になっている」生成規則の場合、Follow(A)+=Follow(A)=Follow(A)Follow(A)となるため、Follow(A) に要素が一切追加されません。

そのため、右再帰 AαA となる文法に対しては、Follow集合を求める際には無視してください。

特に注意が必要なのが、AaB,   BbAのように見た目は右再帰な規則は含まれていないものの、変形してみるとAaBabAのように間接的に右再帰な規則が含まれる場合です。

この場合、生成規則 AaB に対してはAaB よりFollow(B)+=Follow(A)=Follow(B)Follow(A)が成立するため、Follow(B) に対して、Follow(A) の要素を追加されるような式が出てきます。

さらに、Follow(A) を求めていきましょう。BbA よりFollow(A)+=Follow(B)=Follow(A)Follow(B) なり、Follow(A) に対して Follow(B) を追加するような式が出てきます。

すると、

  • Follow(A) を求めるためには追加される要素である Follow(B) を求める必要がある
  • Follow(B) を求めるためには追加される要素である Follow(A) を求める必要がある

という「無限ループ」が発生し、訳が分からないことが起こってしまいます。

ここで、式ではなく意味的に考えてみましょう。ある非終端記号 A Follow集合 Follow(A) とは、「A の直後に来る非終端記号の候補を集めた集合」でしたね。

そこで、ある非終端記号 A を間接的な右再帰規則 AaB, BbA だけで変換していくことで、Follow集合がどうなるかを見てみましょう。

すると、AaB の変化から、A の直後の非終端記号(Follow(A))と B の直後の非終端記号(Follow(B) には変化がないことがわかりますね。またaBabA の変化から、B の直後の非終端記号(Follow(B))と A の直後の非終端記号(Follow(A) にも変化がないことがわかりますね。

よって、AaB, BbA のような間接的な右再帰規則が含まれている場合、Follow集合に追加されるような要素は存在しないこと、および Follow(A), Follow(B) が等しくなることがわかりますね[7]AaB より、\[\begin{align*}\mathrm{Follow} ( \textcolor{magenta}{B} ) + & = \mathrm{Follow} ( \textcolor{magenta}{A} )\ & = … Continue reading

そのため、Follow集合を求める際には、直接的な右再帰規則 AαA だけでなく、間接的な右再帰規則 AaB, BbA が含まれている場合(=一意に答えが決定できないような形が出てきた場合)でも、間接的な左再帰が出てくる式(例えば AaB, BbA )をすべて無視し、残りの生成規則からFollow集合を求めてください。ただし、間接的な左再帰に寄与している非終端記号(今回の例だと A, B)同士のFirst集合は等しくなる点は忘れないでください。

間接的な左再帰に寄与している非終端記号同士の
Follow集合は等しくなる(=一心同体)
Follow集合を求める際に使う3つの公式まとめ

Follow集合は[公式1]~[公式3]で出てきた要素の和で求められる。

※ ただし、X+=Y は、X=XYを表す。(集合 Y の要素を集合 X に加える)

[公式1] 出発記号 S の場合は文字列の終端を表す $ を追加する。Follow(S)+={$}

公式1
[公式1]適用後、各非終端記号 A ごとに、生成先に A が含まれる生成規則を

  • A より前の文字列 α
  • A より後ろの文字列 β

に分けて SαAβ の形に分けたあと、公式1・公式2のどちらか(もしくは両方)を適用し、Follow(A) に集合を追加する。

αβ は空文字でもOK。

例1: BAc のとき: α=ε, β=c
例2: BdA のとき: α=d, β=ε
例3: BA のとき: α=ε, β=ε

[公式2]

β (空文字ではない)何かしらの文字列があれば、First(β) を除くすべての集合を Follow(A) に加える。Follow(A)+=First(β){ε}β がそもそも空文字(つまり SαA)の場合はこの公式を適用しない。

公式2(First集合を足す際には ε を無視!)

[公式3]

β がそもそも空文字(つまり SαA)もしくは空文字ではないが First(β) の要素に ε が含まれる場合は、集合 S のFollow集合 Follow(S)Follow(A) に加える。 Follow(A)+=Follow(S)

公式3

AαA のような→再帰が含まれる文法が出てきた場合は無視する(間接的な右再帰、例えば AαB, BβA など)が出てきた場合も無視するが、間接的な左再帰に寄与する非終端記号(例の場合だと A, B)同士のFollow集合は等しくなる。)。

(3) 例題で確認!

ここからは、例題1でも出てきた文法を使って、実際にFollow集合を求め方を見ていきましょう。

例題2

次の文法 G がある。G=( {S,A,B} , {a,b,c,d} , P , S )P={    SaAAbBa | εBSc | d       }この文法 G に対し、

(1) Follow(S)
(2) Follow(A)
(3) Follow(B)

をそれぞれ求めなさい。

※ 必要であれば例題1で求めたFirst(S)={a}First(A)={b,ε}First(B)={a,d}を用いてもよい。

解説の前に、もう1度Follow集合を求める手順について確認しておきましょう。

Follow集合を求める手順

ある非終端記号 A のFollow集合 Follow(A) はつぎの「準備」の後、下に示す公式[R1]と公式[R2]で出てくる要素を全て追加することで求められる。

[公式1] 出発記号かどうかの確認

A が出発記号であるか確認し、出発記号であれば要素に $ を加えた状態でスタート。それ以外の場合は要素なしでスタート。

  • A が出発記号である: Follow(A)+={$}
  • A が出発記号でない: なにもしない
公式1

[公式2], [公式3] では SabABc のような生成先に S が出てくる規則それぞれに対し、SαAβ のように A の前の文字列 αA の後の文字列 β に分けてから考える。

α, β は空文字OK。例えば、SA であれば、α, β ともに空文字 ε となる。

[公式2] βε であれば適用

生成規則 SαAβ に対し、β が空文字ではない場合、First(β) の要素を加える。ただし ε は加えない。Follow(A)+=First(β){ε}

公式2

[公式3] εFirst(β) であれば適用

SαA のように β=ε となる場合や、SαAβ の形だが εFirst(β) となる場合は、Follow(S) の要素を加える。Follow(A)+=Follow(S)

(1) Follow(S) の求め方

まず、S は出発記号なので、初期状態として要素 $ を加えます。

つぎに、S が生成先に出てくる規則は、BSc 1つだけなので、この生成規則に着目しましょう。

着目した生成規則の生成先 Sc において、S より後の文字列は c ですね。よって、β=c となります。

[公式2] β=cε なので〇

生成規則 BScβ より、Follow(S) に追加される要素は、Follow(S)+=First(c){ε}=R1{c}{ε}={c}と求められます。よって、Follow(S)c が追加されます。

[公式3] εFirst(c)={c} なので×

よって、Follow(S)={$}{c}={c,$}となります。

Follow(S) の計算過程

(2) Follow(A) の求め方

[公式1] A は出発記号ではないため×

つぎに、A が生成先に出てくる規則は、SaA, AbBA の2つがありますが、AbBA右再帰なので無視します。そのため、[公式2], [公式3] に当てはめる生成規則は SaA だけでOKです。

着目した規則の生成先 aA において、A より後の文字列はありません(=空文字です)ね。よって、 β=ε です。

[公式2] β=ε なので×

[公式3] εFirst(ε)={ε} なので〇

生成規則 SaA より、Follow(A) に追加される要素は、

Follow(A)+=Follow(S)=(1){c,$}と求められます。よって、Follow(A)c,$ が追加されます。

したがって、Follow(A)Follow(A)={c,$}と計算できます。

Follow(A) の計算過程

(3) Follow(B) の求め方

[公式1] B は出発記号ではないため×

ここで、B が生成先に出てくる規則は、AbBA の1つだけですね。なので [公式2], [公式3] に当てはめる生成規則は AbBA だけです。

着目した生成規則の生成先 bBA において、B より後の文字列は A ですね。よって、β=A となります。

[公式2] β=Aε なので〇

生成規則 AbBAβ より、Follow(B) に追加される要素は、Follow(B)+=First(A){ε}={b,ε}{ε}={b}と求められます。よって、Follow(B)b が追加されます。

[公式3] εFirst(A)={b,ε} なので〇

生成規則 AbBA より、Follow(B) に追加される要素は、

Follow(B)+=Follow(A)=(2){c,$}と求められます。よって、Follow(B)c,$ が追加されます。

したがって、Follow(B)Follow(B)={b}{c,$}={b,c,$}と計算できます。

Follow(B) の計算過程

(4) Follow集合の計算ミスを防ぐコツ:図を書く

※ このアイデアは、国島 丈生様のこちらのサイトを参考にさせていただきました。
FOLLOW()の計算を間違えにくくする工夫

Follow集合は、First集合に比べて計算が複雑になるため、計算ミスがかなり出てきます[8]実際に私もよく計算ミスします。

そこで、今回の記事では下の図ように「Follow集合にどの集合の要素が追加されているか」を図で表現しています[9]下の図の場合「Follow(S) を求めるためには First(c){$} の要素を追加すればOK」ということを表しています。

Follow(S) の計算図示化
(例題2の(1)に相当)

さらに、各非終端記号ごとに書いたFollow集合の計算図示を下のように1つにまとめることで、Follow集合同士の関係(どのFollow集合の要素がどのFollow集合に加えられているのか)も明確にすることができます。

図を書くことで、「矢印に従って集合を追加していく」するだけで簡単にFollow集合を求めることができます!

※ 追加時に ε が出てくる場合は無視してください。

さらに図を書くもう1つメリットは検算が容易にできることです。

理由は、「ある集合 A の要素を集合 B に追加」した場合、集合の包含関係 AB は必ず成立するからです[10]集合 B に集合 A の要素を追加したのに、集合 B の要素の中に集合 A … Continue reading

例えば、下の図は First(c) の要素を Follow(S) に追加しているため、First(c)Follow(S) が必ず成立します。

この包含関係を、書いた図の中に出てくる矢印それぞれ(下の図の場合は5か所)でチェックします[11]具体的には、{$}Follow(S), First(c)Follow(S), Follow(S)Follow(A), \( \mathrm{First} (A) … Continue reading。矢印すべての箇所で包含関係が成り立っていればOKです。

※ 1つでも成り立っていないものがあれば、計算ミスをしています。

5つの矢印 → それぞれで包含関係を調べれば検算完了!

4. Director集合とLL(1)文法の判定

(1) Director集合とは

ある文法がLL(1)文法、つまり「1文字の先読みをするだけで、与えられた文字列を構文解析できる文法」とはどのような文法なのかをもう少し詳しく見ていきましょう。

例えば、文字列 baba が生成規則SaSSbASbAbAaの文法を満たすか「1文字の先読み」で判定することを考えましょう。

まず、1文字目を先読みすると b となりますね。出発記号 S から b で始まる規則を探そうとしますが、SbASbAb の2つが存在してしまっているため、どちらの文法規則を使っていいかわかりません。

このように、同じ非終端記号から生成される文字列を終端記号にした文字列の先頭がSbA,   SbAbのように重複していると、「1文字の先読みだけでは、どの構文規則を適用すればよいかわからなくなるため、与えられた文字列を構文解析できない」ことがわかりますね。

言い換えると、LL(1)文法であるかを判定するためには、同一非終端記号から生成されるすべての生成規則に対し、生成される可能性がある「終端記号の文字列の先頭文字」がすべて異なっていればOKですね。 

(2) Director集合の定義と計算公式

LL(1)文法であるかを機械的に判定するために登場したのがDirector集合です。

Director集合は、生成規則 Aα に対し、「A から生成される文字列 α をすべて終端記号にしたときの先頭に来る文字の候補」を表しており、Director(A,α)と表記します。

[Director集合の計算公式1] αに空文字が来ない場合

ここで、生成規則 Aα の生成先 α をすべて終端記号にしたときの先頭に来る文字の候補というのは、α に空文字が来ない限り First(α と書き換えることができます。

よって、α に空文字が来ない場合(=εFirst(α))は、Director(A,α)=First(α)でDirector集合を求めることが可能です。

[Director集合の計算公式2] αに空文字が来る可能性がある場合

しかし、Aε が代入される可能性がある(=εFirst(α))場合、下のように Aε が適用されることで A のつぎの文字列 X が先頭に出てくる可能性があります。AXAεXそのため、εFirst(α) となる場合は First(α) に加えて A の直後に来る終端記号の文字を表す Follow(A) もDirector集合に加えます。

よって、α に空文字が来る場合(=εFirst(α))は、Director(A,α)=Follow(A)でDirector集合を求めることが可能です。

Director集合の定義

ある生成規則 Aα に対し、A を変形した際に先頭に来る文字列の候補をDirector集合と呼び、Director(A,α) で記す。

ここで、Director集合はつぎのように計算ができる。

[公式1] α が空文字とならない場合( εFirst(α)Director(A,α)=First(α){ε} [公式2] α が空文字となりうる場合( εFirst(α)Director(A,α)=First(α)Follow(A){ε}

※ 特に Aε の場合、Director(A,α)=Follow(A)と計算できる。([公式2]の変形)

(3) Director集合を用いたLL(1)文法の判定

LL(1)文法であるかどうかは、同一非終端記号から生成されるすべての生成規則に対し、生成される可能性がある「非終端記号の文字列の先頭文字」がすべて異なっていればOKでしたね。

このLL(1)文法であるかの判定をDirector集合を用いて書くと下のようになります。

Director集合を用いたLL(1)文法の判定法

ある文法がLL(1)文法であることを確認するためには、2つ以上の生成元を持つすべての非終端記号に対し、同じ生成元 A のどの2つの規則 Aαi, Aαj を選んでもDirector(A,αi)Director(A,αj)=ϕが成立すればLL(1)文法である。(1つでも成り立たないものがあった時点でLL(1)文法ではない。)

[例]

生成規則Aα1| α2Bβ1Cγ1| γ2 | γ3からなる文法の場合、2つ以上の生成元を持つ非終端記号は A(2つ)と C(3つ)である。

(i) A で確認する内容Director(A,α1)Director(A,α2)=ϕ

(ii) C で確認する内容Director(C,γ1)Director(C,γ2)=ϕDirector(C,γ1)Director(C,γ3)=ϕDirector(C,γ2)Director(C,γ3)=ϕ※ 3つの生成規則の中から2つを選ぶ組み合わせは、(生成規則1, 生成規則2), (生成規則1, 生成規則3), (生成規則2, 生成規則3) の3通りあるので、3通りとも調べる必要あり。

この(i), (ii)で出てきた式(合計4つ)がすべて成立すればLL(1)文法であることが言えます。一方、どれか1つでも成立しなかった時点でLL(1)文法ではありません。

(4) 例題で確認してみよう

Director集合の算出、およびLL(1)文法の確認を例題で確認していきましょう。

例題3

次の文法 G がある。G=( {S,A,B} , {a,b,c,d} , P , S )P={    SaAAbBa | εBSc | d       }この文法 G がLL(1)文法であることを確認しなさい。

※ 必要であれば例題1, 例題2で求めたFirst(S)={a}First(A)={b,ε}First(B)={a,d}Follow(S)={c,$}Follow(A)={c,$}Follow(B)={b.c,$}を用いてもよい。

[解説]

LL(1)文法であるか判定するためには、2つ以上の生成規則を持つ各非終端記号に対して、Director集合を取り、その積を確認します。

(i) A が生成元の生成規則 AbBa, Aε に対してDirector集合確認

AbBa のDirector集合Director(A,bBa)=First(bBa)=R3First(b)=R1{b}

Aε のDirector集合Director(A,ε)=Follow(A)={c,$}

A に関する各Director集合の積を取ると、Director(A,bBa)Director(A,ε)={b}{c,$}=ϕとなるためOK。

(ii) B が生成元の生成規則 BSc, Bd に対してDirector集合確認

BSc のDirector集合Director(B,Sc)=First(Sc)=R3First(S)={a}

Bd のDirector集合Director(B,d)=First(d)=R1{d}

B に関する各Director集合の積を取ると、Director(B,Sc)Director(B,d)={a}{d}=ϕとなるためOK。

(i), (ii)よりLL(1)文法であることが確認できた。

S に対しては、SaA の1つの生成規則しかないため、Director集合は求めなくてOK。

5. 練習問題

それでは、ここまでの学習内容を練習問題を通じておさらいしていきましょう。

今回は、2問の練習問題を用意しています。

練習1.

練習1

次の文法 G がある。G=( {S,A,B} , {a,b,c,d} , P , S )P={   SaAaAbB | SBBcB | dAb | ε  }つぎの(1)~(3)の問いに答えなさい。

(1) First(S), First(A), First(B) を求めなさい。
(2) Follow(S), Follow(A), Follow(B) を求めなさい。
(3) この文法 G はLL(1)文法かどうかを理由を踏まえて答えなさい。

練習2.

練習2

次の文法 G がある。G=( {S,A,B,C} , {a,b,c,d} , P , S )P={   SAaBAcCA | cBS | BA | εCBc              }つぎの(1)~(4)の問いに答えなさい。

(1) First(S), First(A), First(B), First(C) を求めなさい。
(2) Follow(S), Follow(A), Follow(B), Follow(C) を求めなさい。
(3) この文法 G はLL(1)文法かどうかを理由を踏まえて答えなさい。

6. 練習問題の答え

解答1.

規則P={   SaAaAbB | SBBcB | dAb | ε  }で表される文法に対してFirst集合、Follow集合、Director集合を求めてLL(1)文法を判定する。

(1) First集合の算出

[略解]First(S)={a}First(A)={a,b}First(B)={c,d,ε}

(i) First(S) の計算

S が生成元の規則は SaAa の1つだけなので、SaAa だけに着目すればOK。

First(S)=R2First(aAa)SaAa=R3First(a)=R1{a}

(ii) First(A) の計算

A が生成元の規則は AbBASB の2つがある。

そのため、First(A) を求める際には AbBASB の2つの規則に着目すればOK。First(A)=R2First(bB)AbBFirst(SB)ASB

ここでそれぞれの項のFirst集合は以下のように計算できる。

First(bB)=R3First(b)=R1{b}

First(SB)=R3First(S)=(1){a}

よって、First(A)=R2First(bB)AbBFirst(SB)ASB={b}{a}={a,b}となる。

(iii) First(B) の計算

B が生成元の規則は BcB, BdAb, Bε の3つがある。

そのため、First(B) を求める際には BcB, BdAb, Bε の3つの規則に着目すればOK。First(B)=R2First(cB)BcBFirst(dAb)BdAbFirst(ε)Bε

ここでそれぞれの項のFirst集合は以下のように計算できる。

First(cB)=R3First(c)=R1{c}

First(dAb)=R3First(d)=(1){d}

First(ε)=R1{ε}

よって、First(B)=R2First(cB)First(dAb)First(ε)={c}{d}{ε}={c,d,ε}となる。

(2) Follow集合の算出

[略解]Follow(S)={a,b,c,d,$}Follow(A)={a,b}Follow(B)={a,b}

(i) Follow(S) の算出式

[公式1] S は出発記号? → Yes。なので、Follow(S)$ を追加。Follow(S)+={$}

S が生成先に含まれる規則は、ASB のみなので、ASB にのみ着目すればOK。

ここで、ASBβ より、β=B とおく。

[公式2] βε? → β=Bε なのでYes。

なので、ASB に対し、以下の集合の要素を追加。\( AFollow(S)+=First(B){ε}={c,d,ε}{ε}={c,d}

[公式3] εFirst(β)? → First(β)={c,d,ε} なのでYes。

なので、ASB に対し、以下の集合の要素を追加。Follow(S)+=Follow(A)

よって、Follow(S) に追加される要素は以下の図で示す通り。

Follow(S) で追加される要素の図示

(ii) Follow(A) の算出式

[公式1] A は出発記号? → No。

A が生成先に含まれる規則は、SaAa, BdAb の2つなので、この2つの規則に着目すればOK。

[a] SaAa に着目したとき

SaAaβ とおく。(つまり β=a

[公式2] βε? → β=aε なのでYes。

なので、SaAa に対し、以下の集合の要素を追加。Follow(A)+=First(a)={a}

[公式3] εFirst(β)? → First(β)=First(a)={a} なのでNo。

[b] BdAb に着目したとき

[公式2] βε? → β=bε なのでYes。

なので、SdAb に対し、以下の集合の要素を追加。Follow(A)+=First(b)={b}

[公式3] εFirst(β)? → First(β)=First(b)={b} なのでNo。

よって、Follow(A) に追加される要素は以下の図で示す通り。

(iii) Follow(B) の算出式

[公式1] B は出発記号? → No。

B が生成先に含まれる規則は、AbB, , ASB ,BcB の3つ。ただし、BcB は右再帰規則のため無視。なので、AbB, ASB の2つの規則に着目すればOK。

[a] AbB に着目したとき

AbBB の後ろの文字が存在しない。そのため、β=ε である。

[公式2] βε? → β=ε なのでNo。

[公式3] εFirst(β)? → First(β)=First(ε)={ε} なのでYes。

なので、AbB に対し、以下の集合の要素を追加。Follow(B)+=Follow(A)

[b] ASB に着目したとき

ASBB の後ろの文字が存在しない。そのため、β=ε である。

[公式2] βε? → β=ε なのでNo。

[公式3] εFirst(β)? → First(β)=First(ε)={ε} なのでYes。

なので、ASB に対し、以下の集合の要素を追加。Follow(B)+=Follow(A)

※ 式から察している人もいるかもしれませんが、[a] の生成式と全く同じ結果が出てきます。

よって、Follow(B) に追加される要素は以下の図で示す通り。

Follow(A) で追加される要素の図示
(代表として ASB を選択)

それぞれの計算結果をまとめてFollow集合を算出

先ほど出した Follow(S), Follow(A), Follow(B) の計算過程を1つの図に表すと、下のようになる。

あとは、求められるFollow集合から順にFollow集合を求めていけばOK。

※ 今回の場合は、Follow(A) を求めてから、Follow(S)Follow(B) を求めていく流れになりますね(Follow(S)Follow(B) はどっちから求めてもOK)。

(ii) Follow(A) の計算結果

[計算式]Follow(A)=First(a)First(b){ε}={a}{b}={a,b}

(i) Follow(S) の計算結果

[計算式]Follow(S)=First(B)Follow(A){$}{ε}={c,d,ε}{a,b}{$}{ε}={a,b,c,d,$}ε を無視するのを忘れずに

(iii) Follow(B) の計算結果

[計算式]Follow(B)=Follow(A)={a,b}※Follow集合の要素には ε が出てこないので、ε を引いていません。(引いてもOKです。)

(3) Director集合の算出とLL(1)文法の判定

各非終端記号に対して、生成元から2つ以上の生成規則を持つものは、

  1. A が生成元となる規則:AbB, ASB
  2. B が生成元となる規則:BcB, BdAb, Bε

である。あとは、非終端記号ごとに「どの生成規則同士の積をとっても、Director集合が空集合になること」を確認すればOK。

1) A が生成元の生成規則 AbB, ASB に対してDirector集合確認

AbB のDirector集合Director(A,bB)=First(bB)=R3First(b)=R1{b}

ASB のDirector集合Director(A,SB)=First(SB)=R3First(S)={a}

A に関する各Director集合同士の積を取ると、Director(A,bB)Director(A,SB)={b}{a}=ϕとなるためOK。

2) B が生成元の生成規則 BcB, BdAb, Bεに対してDirector集合確認

BcB のDirector集合Director(B,cB)=First(cB)=R3First(c)=R1{c}

BdAb のDirector集合Director(B,dAb)=First(dAb)=R3First(d)=R1{d}

Bε のDirector集合Director(B,ε)=Follow(B)={a,b}

B に関する各Director集合同士の積を取ると、Director(B,cB)Director(B,dAb)={c}{d}=ϕDirector(B,cB)Director(B,ε)={c}{a,b}=ϕDirector(B,dAb)Director(B,ε)={d}{a,b}=ϕとなるためOK。

1), 2) より、題意の文法はLL(1)文法である。

解答2.

規則P={   SAaBAcCA | cBS | BA | εCBc              }で表される文法に対してFirst集合、Follow集合、Director集合を求めてLL(1)文法を判定する。

(1) First集合の算出

[略解]First(S)={c}First(A)={c}First(B)={c,ε}First(C)={c}

(i) First(S) の計算 (途中まで)

S が生成元の規則は SAaB の1つだけなので、SAaB だけに着目すればOK。

First(S)=R2First(AaB)SAaB=R3First(A)※ まだ First(A) が求まっていないため保留

(ii) First(A) の計算(と First(S) の計算再開)

A が生成元の規則は AcCAAc の2つがある。

そのため、First(A) を求める際には AcCAAc の2つの規則に着目すればOK。First(A)=R2First(cCA)AcCAFirst(c)Ac

ここでそれぞれの項のFirst集合は以下のように計算できる。

First(cCA)=R3First(c)=R1{c}

First(c)=R1{c}

よって、First(A)=R2First(cCA)First(c)={c}{c}={c}となる。また、First(S)

First(S)=R2First(AaB)=R3First(A)={c}と求められる。

(iii) First(B) の計算

B が生成元の規則は BS, BBA, Bε の3つがある。

ただし、BBA は左再帰の文法なので無視する。そのため、First(B) を求める際には BcB, BdAb の2つの規則に着目すればOK。First(B)=R2First(cB)BcBFirst(dAb)BdAb

ここで、First(ε)=R1{ε}なので、First(B)First(B)=R2First(S)First(ε)={c}{ε}={c,ε}と計算できる。

(iv) First(C) の計算

C が生成元の規則は CBc だけなので、この規則のみに着目する。

よって、First(C)=R2First(Bc)CBc=R3(First(B){ε}){c}  (εFirst(B))={c}{c}={c}First(B)ε が含まれているため、公式が変化する点に注意!

(2) Follow集合の算出

[略解]Follow(S)={c,$}Follow(A)={a,c,$}Follow(B)={c,$}Follow(C)={c}

(i) Follow(S) の算出式

[公式1] S は出発記号? → Yes。なので、Follow(S)$ を追加。Follow(S)+={$}

ここで、S が生成先に含まれる規則は、BS のみなので、BS にのみ着目すればOK。

BS において、 S の後ろの文字が存在しない。そのため、β=ε である。

[公式2] βε? → β=ε なのでNo。

[公式3] εFirst(β)? → First(β)=First(ε)={ε} なのでYes。

なので、BS に対し、以下の集合の要素を追加。Follow(S)+=Follow(B)

よって、Follow(S) で追加する要素(計算過程)は以下の図の通りである。

Follow(S) の計算過程の図示

(ii) Follow(A) の算出式

[公式1] A は出発記号? →No。なにもしない。

ここで、A が生成先に含まれる規則は、SAaB, AcCA, BBA の3つがあるが、AcCA は右再帰な規則なので無視。よって、SAaB, BBA の2つに着目すればOK。

[ii-a] SaAaB に着目したとき

SaAaBβ とおく。(つまり β=a

[公式2] βε? → β=aBε なのでYes。

なので、SaAaB に対し、以下の集合の要素を追加。Follow(A)+=First(aB)=R3First(a)=R1{a}={a}

[公式3] εFirst(β)? → First(β)=First(aB)={ε} = \{ a \} \) なのでNo。

[ii-b] BBA に着目したとき

この規則では、S の後ろの文字が存在しない。そのため、β=ε である。

[公式2] βε? → β=ε なのでNo。

[公式3] εFirst(β)? → First(β)={ε} なのでYes。

なので、BBA に対し、以下の集合の要素を追加。Follow(A)+=Follow(B)

よって、Follow(A) に追加される要素は以下の図で示す通り。

Follow(A) の計算過程の図示

(iii) Follow(B) の算出式

[公式1] B は出発記号? →No。なにもしない。

ここで、B が生成先に含まれる規則は、SAaB, BBA, CBc の3つがあるため、この3つの規則に着目すればOK。

[iii-a] SAaB に着目したとき

この規則では、B の後ろの文字が存在しない。そのため、β=ε である。

[公式2] βε? → β=ε なのでNo。

[公式3] εFirst(β)? → First(β)=First(ε)={ε} なのでYes。

なので、SAaB に対し、以下の集合の要素を追加。Follow(B)+=Follow(S)

[iii-b] BBA に着目したとき

BBAβ とおく。(つまり β=A

[公式2] βε? → β=Aε なのでYes。

なので、BaBA に対し、以下の集合の要素を追加。Follow(B)+=First(A)={c}

[公式3] εFirst(β)? → First(β)=First(A)={c} なのでNo。

[iii-c] CBc に着目したとき

CBcβ とおく。(つまり β=c

[公式2] βε? → β=cε なのでYes。

なので、CaBc に対し、以下の集合の要素を追加。Follow(B)+=First(c)={c}

[公式3] εFirst(β)? → First(β)=First(c)={c} なのでNo。

[iii-a]~[iii-c] より、Follow(B) に追加される要素は以下の図で示す通り。

Follow(B) の計算過程の図示

(iv) Follow(C) の算出式

[公式1] C は出発記号? →No。なにもしない。

ここで、C が生成先に含まれる規則は、AcCA のみ。なのでこの規則に着目すればOK。

この規則について、AcCAβ とおく。(つまり β=A

[公式2] βε? → β=Aε なのでYes。

なので、CcCA に対し、以下の集合の要素を追加。Follow(C)+=First(A)={c}

[公式3] εFirst(β)? → First(β)=First(A)={c} なのでNo。

よって、Follow(C) に追加される要素は以下の図で示す通り。

Follow(B) の計算過程の図示

それぞれの計算結果をまとめてFollow集合を算出

先ほど出した Follow(S), Follow(A), Follow(B), Follow(C) の計算過程を1つの図に表すと、下のようになる。

※ 点線部分は、間接的な右再帰により、2つのFollow集合が等しい(=一心同体)になっていることを表す。つまり Follow(S)=Follow(B)

あとは、求められるFollow集合から順にFollow集合を求めていけばOK。

※ 今回の場合は、Follow(A) 集合は点線部分のFollow集合が求まらないと計算できません。それ以外の集合は自由な順番で計算できます。

(iv) Follow(C) の計算結果

[計算式]Follow(C)=First(a)={c}

(i), (iii) Follow(S), Follow(B) の計算結果(2つのFollow集合は等しい)

[計算式]Follow(S)={$}First(A)First(c)={$}{c}{c}={c,$}Follow(B) 加えられている要素もいったん Follow(S) に足している。

Follow(B)=Follow(S)={c,$}

(ii) Follow(A) の計算結果

[計算式]Follow(A)=Follow(B)First(a)={c,$}{a}={a,c,$}Follow(B)Follow(S) としてもOK。

(3) Director集合の計算・LL(1)文法の判定

各非終端記号に対して、生成元から2つ以上の生成規則を持つものは、

  1. A が生成元となる規則:AcCA, Ac
  2. B が生成元となる規則:BS, BBA, Bε

である。あとは、非終端記号ごとに「どの生成規則同士の積をとっても、Director集合が空集合になること」を確認すればOK。

1) A が生成元の生成規則 AcCA, Ac に対してDirector集合確認

AcCA のDirector集合Director(A,cCA)=First(cCA)=R3First(c)=R1{c}

Ac のDirector集合Director(A,c)=First(c)=R1{c}

A に関する各Director集合同士の積を取ると、Director(A,cCA)Director(A,c)={c}{c}={c}となるためNG。

よって、題意の文法はLL(1)文法ではない。

※ 1つでも ϕ にならないものがあればその時点でLL(1)文法ではないことが確認できるため、他のDirector集合は求めなくてOKです。

7. さいごに

かなり長い記事となっていましたが、以上が

  • LL(1)文法
  • First集合
  • Follow集合
  • Director集合

に関する説明でした。

この記事を見て、少しでもLL(1)文法やFirst, Follow, Director集合の計算の方法について理解いただけたのであれば非常にうれしいです。

次回は、左再帰な文法についての「問題点」と「左再帰を解消する方法」について説明する予定です。

[練習問題の中に入れられなかったおまけ問題] 生成規則 AB に対するDirector集合 Director(A,B) を求めなさい。ただし、First(A)={a}First(B)={b,ε}Follow(A)={c}Follow(B)={d,$}とする。解答はこの注釈に書いています[12]εFirst(B) なので、B が空文字になる可能性を考慮するのがミソです。\[\begin{align*}\mathrm{Director} (A,B) & = \mathrm{First}(B) \cup … Continue reading

注釈

注釈
1 10文字弱、かつ生成規則が4つであればやみくもにやってもそこまで時間はかかりませんが、実際のプログラムに対して構文解析をする場合、膨大な文字数かつ膨大な生成規則があるため、やみくもにやっていると日が暮れてしまいます。
2 もう少し詳しく言うと、LL(1)文法はトップダウン構文解析(出発記号を起点として、どんどん生成規則を用いて分解していきながら解析する方法)において、1文字だけ先読みすることで文字列を後戻りすることなく構文解析ができる文法です。
3 AAb のように左再帰文法となる規則を適用しても、先頭文字列は A のまま変わらないため、無視しても問題ない。
4 いったんFirst(A)=First(B)First(c)=First(A)First(c)=First(c)のように、左辺に出てくるFirst集合(今回は First(A) )が右辺にも出てくる形にしてから、左辺にも右辺にも出てきたFirst集合を無視して計算してもOKです。
5 一般化すると、1文字目からn文字目までの文字すべてに空文字があれば、1文字目~n+1文字目の和を取ればOK。
6 β がそもそも存在しない、つまり β=ε のときは、First(β)=First(ε)=R1{ε} となるため、ε を無視すると追加できる集合がなくなる。
7 AaB より、Follow(B)+=Follow(A)=Follow(B)Follow(A)の式が導出でき、BbA より、Follow(A)+=Follow(B)=Follow(A)Follow(B)となるため、数式的にも Follow(A)=Follow(B) が言える。\
8 実際に私もよく計算ミスします。
9 下の図の場合「Follow(S) を求めるためには First(c){$} の要素を追加すればOK」ということを表しています。
10 集合 B に集合 A の要素を追加したのに、集合 B の要素の中に集合 A の一部の要素は実は入っていない、なんてことは起こりませんよね…?
11 具体的には、{$}Follow(S), First(c)Follow(S), Follow(S)Follow(A), First(A)Follow(B), Follow(A)Follow(B) 5つが成立しているかすべて確かめればOKです。
12 εFirst(B) なので、B が空文字になる可能性を考慮するのがミソです。Director(A,B)=First(B)Follow(A){ε}={a,b}

関連広告・スポンサードリンク

おすすめの記事