言語処理系構成論(4 構文解析法nobuya/lecture/plpc/plpc2012... · 2012-05-01 ·...

10
2012/05/01 言語処理系構成論(4岡山大学大学院自然科学研究科 渡邊 誠也 1 2012/05/01 構文解析法 2 2012/05/01 #4 ! プログラミング言語の構文解析 ! 様々な方法が考案 ! おおまかな分類 ! 上向き構文解析法(bottom-up parsing! 生成規則の右辺と一致 左辺の記号に 置換 ... 還元(reduction) ! 入力記号列を葉(終端記号)として,葉 (終端記号)から根(非終端記号)へ向 かって解析を進めながら解析木を生成 ! 下向き構文解析法(top-down parsing! 記号列として次に何がくるかを仮定しな がら解析を進めていく解析法 ! 解析木を上(根)から下(葉)へ向かっ て解析 構文解析法 3 E E T T F F i i i + * T F i + i * i の解析木 P1 = { } F ( E ) T T * F E E + T | T | F | i 2012/05/01 #4 さまざまな構文解析法 ! 再帰的下向き構文解析法(recursive descent parsing! LL(k)構文解析(Left-to-right scanning, Left-most derivation! LR構文解析法(LR parsing! Left-to-right scanning, Right-most derivation in reverse ! SLR(1)構文解析法(Simple LR parsing! LR(1)構文解析法 ! LALR(1)構文解析法(Lookahead LR parsing4 2012/05/01 #4 構文木(syntax tree)とその表現 ! 構文木 ! 構文解析の結果として生成さ れるプログラムの内部表現 ! : a = b * c + (d - e) 5 ! 構文木の表現 ! N組(N-tuple)と呼ばれる構造体 を使用して表現 = a c b - * + d e = + * - a b c d e N...N個のメンバを持つ構造体 2012/05/01 #4 構文木とその表現(続き) ! C言語でのN組の実現例 ! tuple: N組へのポインタ型 ! token: トークンへのポインタ型 6 x1 = make_tuple(“*”, b, c); x2 = make_tuple(“-”, d, e); x3 = make_tuple(“+”, x1, x2); x4 = make_tuple(“=”, a, x3); N組の生成例 int is_a(x) トークンの種別判 = + * - a b c d e tuple make_tuple(A, a1, ..., an) N組を生成する関数 節の値 トークン x が終端記号 a に対応するかを判定 【例】is_if ... キーワード if か?

Upload: others

Post on 02-Feb-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

言語処理系構成論(4)

岡山大学大学院自然科学研究科渡邊 誠也

1 2012/05/01

構文解析法

2

2012/05/01

�#4

�����������

! プログラミング言語の構文解析! 様々な方法が考案! おおまかな分類

! 上向き構文解析法(bottom-up parsing)! 生成規則の右辺と一致 → 左辺の記号に置換 ... 還元(reduction)

! 入力記号列を葉(終端記号)として,葉(終端記号)から根(非終端記号)へ向かって解析を進めながら解析木を生成

! 下向き構文解析法(top-down parsing)! 記号列として次に何がくるかを仮定しながら解析を進めていく解析法

! 解析木を上(根)から下(葉)へ向かって解析

構文解析法

3

E

E

T

T

F

F

i i

i

+

*T

F

i + i * i の解析木

P1 = {

}F � ( E )T � T * FE � E + T | T

| F| i

2012/05/01

�#4

�����������

さまざまな構文解析法! 再帰的下向き構文解析法(recursive descent parsing)

! LL(k)構文解析(Left-to-right scanning, Left-most derivation)

! LR構文解析法(LR parsing)! Left-to-right scanning, Right-most derivation in reverse 

! SLR(1)構文解析法(Simple LR parsing)! LR(1)構文解析法! LALR(1)構文解析法(Lookahead LR parsing)

4

2012/05/01

�#4

�����������

構文木(syntax tree)とその表現! 構文木

! 構文解析の結果として生成されるプログラムの内部表現! 例: a = b * c + (d - e)

5

! 構文木の表現! N組(N-tuple)と呼ばれる構造体を使用して表現

=

a

cb

-*

+

d e

=

+

* -

a

b c d e

N組...N個のメンバを持つ構造体

2012/05/01

�#4

�����������

構文木とその表現(続き)! C言語でのN組の実現例

! tuple: N組へのポインタ型! token: トークンへのポインタ型

6

x1 = make_tuple(“*”, b, c);x2 = make_tuple(“-”, d, e);x3 = make_tuple(“+”, x1, x2);x4 = make_tuple(“=”, a, x3);

N組の生成例

int is_a(x)トークンの種別判

=

+

* -

a

b c d etuple make_tuple(A, a1, ..., an)N組を生成する関数

節の値 枝

トークン x が終端記号 a に対応するかを判定【例】is_if ... キーワード if か?

Page 2: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

再帰的下向き構文解析

7 2012/05/01

�#4

�����������

再帰的下向き構文解析法(recursive descent parsing)

! 非終端記号ごとに,解析関数(parsing function)を作成! 関数本体では,非終端記号を左辺とする生成規則を忠実に反映したコードを与える! 文法と構文解析プログラムの対応が明確! 文法を変更した場合にも,プログラムの修正が容易

8

2012/05/01

�#4

�����������

解析関数! 生成規則

9

A� aAb | c

a1 a2 c1 b1 b2 $

p

Aに還元できるトークン列とは,■cのトークンが1つだけ,あるいは,■aのトークンで始まり,Aに還元できるトークン列が続き,bのトークンで終わる一連のトークン列

tuple parse_A() { token *prev = p, x, z; tuple y; if (is_a(x = *p++) && (y = parse_A()) && is_b(z = *p++)) return make_tuple(“A”, x, y, z); p = prev; if (is_c(x = *p++)) return x; p = prev; return NULL;}

トークンのベクタ

tuple parse() { tuple x = parse_A(); if (x && is_eof(*p)) return x; else syntax_error();}

入力トークン列全体を解析する関数2012/05/01

�#4

�����������

左再帰(left recursion)! 文法が左再帰を含む→解析関数の再帰呼出が停止しない! 例: 直接の左再帰(immediate left recursion)

!  

10

E � E + T | T tuple parse_E() { token *prev = p; tuple x, y; if ((x = parse_E()) && is_plus(*p++) && (y = parse_T())) return make_tuple(“+”, x, y); p = prev; if (x = parse_T()) return x; p = prev; return NULL;}

! 間接的な左再帰も同様A� Ba | c

B � Ab | d

2012/05/01

�#4

�����������

C言語における左再帰! 識別子のリスト

! 単純に右再帰に置き換えられない例

11

identifier-list:identifieridentifier-list , identifier

identifier-list:identifieridentifier , identifier-list

additive-expression:multiplicative-expressionadditive-expression + multiplicative-expressionadditive-expression - multiplicative-expression

右再帰に置き換え可能

左結合的が右結合的になってしまう

a - b + c = (a - b) + c ≠ a - (b + c) = a - b - c

C言語の文法に間接的な左再帰はない

2012/05/01

�#4

�����������

同じ終端記号列を生成するような左再帰のない生成規則

左再帰の除去(left recursion elimination)! 直接の左再帰の除去

12

A� Aa | b

A⇥lm

Aa⇥lm

Aaa⇥lm

Aaaa⇥lm

· · ·⇥lm

Aan ⇥lm

ban

A� bA�

A� � aA� | �

A⇥rm

bA� ⇥rm

baA� ⇥rm

baaA� ⇥rm

· · ·⇥rm

banA� ⇥rm

ban

Page 3: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

同じ終端記号列を生成するような左再帰のない生成規則

左再帰の除去(left recursion elimination)! 直接の左再帰の除去

12

A� Aa | b

A⇥lm

Aa⇥lm

Aaa⇥lm

Aaaa⇥lm

· · ·⇥lm

Aan ⇥lm

ban

A� bA�

A� � aA� | �

A⇥rm

bA� ⇥rm

baA� ⇥rm

baaA� ⇥rm

· · ·⇥rm

banA� ⇥rm

ban

tuple parse_A() { token *prev = p; if (is_b(y = *p++)) return parse_A1(y); p = prev; return NULL;}tuple parse_A1(tuple x) { token *prev = p, y; if (is_a(y = *p++)) return parse_A1(make_tuple(“A”, x, y)); p = prev; return x;}

2012/05/01

�#4

�����������

同じ終端記号列を生成するような左再帰のない生成規則

左再帰の除去(left recursion elimination)! 直接の左再帰の除去

12

A� Aa | b

A⇥lm

Aa⇥lm

Aaa⇥lm

Aaaa⇥lm

· · ·⇥lm

Aan ⇥lm

ban

A� bA�

A� � aA� | �

A⇥rm

bA� ⇥rm

baA� ⇥rm

baaA� ⇥rm

· · ·⇥rm

banA� ⇥rm

ban

tuple parse_A() { token *prev = p; if (is_b(y = *p++)) return parse_A1(y); p = prev; return NULL;}tuple parse_A1(tuple x) { token *prev = p, y; if (is_a(y = *p++)) return parse_A1(make_tuple(“A”, x, y)); p = prev; return x;}

入力トークンのポインタが進んでいる.まったく同じ状態で再帰呼出しされるわけでないので無限呼出にはならない

2012/05/01

�#4

�����������

左再帰の除去! 直接の左再帰を含む生成規則の一般形

13

A⇥ A�1 | · · · | A�n | ⇥1 | · · · | ⇥m

A⇥ ⇥1A� | · · · | ⇥mA�

A� ⇥ �1A� | · · · | �nA� | ⇤

�i � V � �i A �i � V +ここで    , は で始まらない.次で置き換えることで除去可能

【例】E � E + T | TT � T * F | FF � (E) | i

E � TE�

E� � + TE� | �T � FT �

T � � * FT � | �F � (E) | i

2012/05/01

�#4

�����������

バックトラック! いくつかの選択肢が考えられるときに,そのうちの1つを試してみて失敗だったと判断した時点でもとの(その選択肢を選ぶ直前)の状態に戻す処理

14

A� aBcB � b | bd

【例】 tuple parse_A() { token *prev = p, x, z; tuple y; if (is_a(x = *p++) && (y = parse_B()) && is_c(z = *p++)) return make_tuple(“A”, x, y, z); p = prev; return NULL;}tuple parse_B() { token *prev = p, x, y; if (is_b(x = *p++)) return x; p = prev; if (is_b(x = *p++) && is_d(y = *p++)) return make_tuple(“B”, x, y); p = prev; return NULL;}

正しく動作しない(例: abdc)

parse_Aが再度parse_B

を呼出して,もう1つの生成規則を試みるよう指示する必要がある一般的には複雑な機構が必要

2012/05/01

�#4

�����������

くくり出しによるバックトラックの回避! くくり出し(factoring)でバックトラックを回避可能

15

B � b | bd

B � b (d | �)

tuple parse_B() { token *prev = p, x, y; if (is_b(x = *p++)) { token *prev1 = p, y; if (is_d(y = *p++)) return make_tuple(“B”, x, y); p = prev1; return x; } p = prev; return NULL;}

 のくくり出しb

A⇥ aBcB ⇥ C | DC ⇥ · · ·D ⇥ · · ·

! 生成規則が終端記号で始まらない場合は適用が困難

バックトラックのもう1つの大きな問題:

適切なエラーメッセージ出力が難しい2012/05/01

�#4

�����������

LL(k)構文解析法! バックトラックをしない再帰的下向き構文解析法! k個の入力トークンを先読みし,どの生成規則を適用するかを決定する方法

16

B � C | D!       の解析関数で,次の入力トークン x を先読みし,1.もし      なら parse_Cを呼び出す2.もし      なら parse_Dを呼び出す3.どちらでもなければ,構文エラー

C+⇥ x · · ·

D+⇥ x · · ·

Page 4: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

LL(1)文法 ー first集合とfollow集合 ー

!  のfirst集合 ... から生成される記号列の先頭に現れる終端記号の全体

! A のfollow集合 ... Aの直後に現れる可能性のある終端記号の全体

!        なら,! 常に,

17

! 文法         とその記号列G = �VN , VT , P, S⇥ � � (VN ⇥ VT )�

� �

First(�) = {a | a ⇤ VT , ��⇥ a · · · }

Follow(A) = {a | a ⇤ VT , S$ �⇥ · · ·Aa · · · }

$ � Follow(A) S�⇥ · · · A

$ � Follow(S)

a

a

A

S

2012/05/01

�#4

�����������

LL(1)文法 ー director集合 ー!     のdirector集合 ... 次にこの生成規則を使った場合に,入力トークン列の先頭に現れる可能性のある終端記号の全体

18

A� �

Director(A,�) =

�First(�) (unless �

�� ⇥)First(�) ⇥ Follow(A) (if �

�� ⇥)LL(1)文法

a

A

S

a

A

$

右辺だけが異なる任意の生成規則    と    に対して,必ず

が成り立つ文法

A� � A� �Direcotr(A,�) ⇥Direcotr(A,⇥) = �

入力トークン列の先頭記号を見れば,次にどの生成規則を使用すべきか決定可能

2012/05/01

�#4

�����������

Director集合の例! 文法

19

First(T �) = {*}First(E�) = {+}Follow(E) = Follow(E�) = {$, )}Follow(T ) = Follow(T �) = {+, $, )}Follow(F ) = First(T �) � Follow(T �) = {*, +, $, )}

First(E) = First(T ) = First(F ) = {(, i}

Director(E�, +TE�) ⇥Director(E�, �) = �Director(T �, *FT �) ⇥Director(T �, �) = �Director(F, (E)) ⇥Director(F, i) = �

E � TE�

E� � + TE� | �T � FT �

T � � * FT � | �F � (E) | i

LL(1)文法

Director(E�, +TE�) = {+}

Director(T �, *FT �) = {*}

Director(F, (E)) = {(}Director(F, i) = {i}

Director(E�, �) = First(�) � Follow(E�) = {$, )}

Director(T �, �) = First(�) � Follow(T �) = {+, $, )}

Director(E, TE�) = First(T ) = {(, i}

Director(T, FT �) = First(F ) = {(, i}

2012/05/01

�#4

�����������

tuple parse_E() { if (is_lpar(*P) || is_i(*p)) { tuple x = parse_T(); return parse_E1(x); } else syntax_error();}tuple parse_E1(tuple x) { if (is_plus(*p)) { tuple y; p++; y = parse_T(); return parse_E1(make_tuple(“+”, x, y)); } else syntax_error();}

LL(1)構文解析法! Left-to-right scanning, Left-most derivation

! 入力トークン列を左から右へ読み込む(バックトラックがない)! 最左導出の順序で構文解析を行う

20

バックトラックがなくなったので1. 適切な時点で構文エラーを検出できる

2. pの値を保存しておく必要がない

非終端記号Aの解析関数入力トークンを調べ,Aを左辺とする生成規則のうちで,director集合にそのトークンを含むものを選択し(なければ構文エラー)構文解析を進める.

Director(E, TE�) = First(T ) = {(, i}

Director(E�, +TE�) = {+}

E � TE�

E� � + TE� | �T � FT �

T � � * FT � | �F � (E) | i

2012/05/01

�#4

�����������

LL(1)構文解析法(続き)! 再帰的下向き構文解析法の一種

! 文法が左再帰を含む場合,左再帰を除去した文法を使用! 左辺が同じで,右辺が同じ記号で始まる生成規則が複数存在する場合! director集合には共通の終端記号が含まれる(LL(1)でない)! くくり出しが必要

21

! 変換後の文法は,もとの文法より読みづらくなるのが普通! 結果的に複雑な構文解析プログラムとなってしまうことも

生成規則に左再帰がなければ,直感的に理解しやすい構文解析プログラムを作成可能

もとの文法をそのまま使用する構文解析法 → LR構文解析法

文法を変更(変換)した場合,

2012/05/01

LR構文解析

22

Page 5: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

LR構文解析法(LR parsing)! 上向き構文解析法

! 解析木を下(葉)から上(根)に構築! もとの文法をそのまま使用する構文解析法

! 再帰的下向き構文解析法より優れている! 左再帰の除去といった文法の変更が必要ない

! Left-to-right scanning, Right-most derivation in reverse! 入力トークンを先頭から順に読み込み,バックトラックしない! 最右導出と逆の順序で構文解析を行う

23 2012/05/01

�#4

�����������

LR構文解析! 文法          における    の最右導出

24

E �rm

E + i�rm

E + i + i�rm

i + i + i

注目点(・)を挿入E·⇥

rmE + i· � E + · i � E · + i

⇥rm

E + i · + i � E + · i + i � E · + i + i

⇥rm

i · + i + i � ·i+ i + i

i + i + i

  : 注目点を左に移動する動作�

注目点の直前の記号が! 非終端記号なら導出を行う.! 終端記号なら注目点を左へ移動.

⌅{E � E + i | i}, E⇧

2012/05/01

�#4

�����������

LR構文解析! 文法          における    の最右導出

24

E �rm

E + i�rm

E + i + i�rm

i + i + i

注目点(・)を挿入E·⇥

rmE + i· � E + · i � E · + i

⇥rm

E + i · + i � E + · i + i � E · + i + i

⇥rm

i · + i + i � ·i+ i + i

のLR構文解析i + i + i$

i + i + i

·i + i + i$⇥ i · + i + i$⇥ E · + i + i$⇥ E + · i + i$⇥ E + i · + i$⇥ E · + i$⇥ E + · i$⇥ E + i · $⇥ E · $還元(reduce): 注目点直前の記号列を還元シフト(shift): 注目点を1つ右に移動

  : 注目点を左に移動する動作�

注目点の直前の記号が! 非終端記号なら導出を行う.! 終端記号なら注目点を左へ移動.

最右導出の逆順で構文解析

⌅{E � E + i | i}, E⇧

2012/05/01

�#4

�����������

LR構文解析(続き)

!     で還元するとき!  の節を用意し, の各記号の節へ枝を張る

25

E

i

+ iE

+ iE

·i + i + i$⇥ i · + i + i$⇥ E · + i + i$⇥ E + · i + i$⇥ E + i · + i$⇥ E · + i$⇥ E + · i$⇥ E + i · $⇥ E · $

A� �

A �

1

2

3

上向き構文解析法(botton-up parsing)·x$初期状態:

u · v$解析中の状態:

S · $最終状態:

x : 入力トークン列

u � (VT ⇥ VN )� : 構築した部分解析木の記号列v � V �

T : 未処理の入力トークン列

S : 出発記号 最終状態に変換できなければ,構文エラー

注目点つきの記号列

→ 解析中の各時点における状態を表す

2012/05/01

�#4

�����������

LR構文解析(続き)! 解析中における処理

! 還元 ... ある生成規則   を使って,状態    の をAに還元して,状態    に移行

! シフト ... 状態    のときに,次の入力トークンaを読み込んで状態    に移行

26

A� � u � · v$ �

u A · v$

u a · v$u · a v$

·i + i + i$⇥ i · + i + i$⇥ E · + i + i$⇥ E + · i + i$⇥ E + i · + i$⇥ E · + i$⇥ E + · i$⇥ E + i · $⇥ E · $

2012/05/01

�#4

�����������

LR(0)項! 生成規則の右辺にドット(・)をつけたもの

27

E � E + i【例】     のLR(0)項

E ⇥ ·E + i E ⇥ E · + i E ⇥ E + · i E ⇥ E + i·導入項 完全項

E + i を読み込めばEに還元できる

Eを読み込んだ.

+ i を読み込めばEに還元できる

E+を読み込んだ.

i を読み込めばEに還元できる

E+i を読み込んだ. Eに還元できる

S ⇥ ·E$

S ⇥ E · $

E ... 文法上の出発記号S ... 新しいの出発記号

出発項

受理項Eを読み込んで$に達すれば受理できるEを読み込んだ.$に達していれば受理できる

Page 6: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

文法         におけるLR構文解析

29

・i+i+i$

i・+i+i$

i+・i+i$

i+i・+i$

i+i+・ +i$

i+i+i・$

i+i+E・$

i+E・$

E・ $

S → ・E $

E → ・i +E

E → ・i

E → i・+ E

E → i・

E → i +E・

i

E

+

i

$

accept

E → i・+E

E → i・

E → i +・E

E → ・i

E → ・i+E

i

+

E → i +・E

E → ・i

E → ・i+E

E → i +E・

E → i・+E

E → i・

E → i +E・

E

E

⌅{E � i + E | i}, E⇧

不都合な状態(inadequate state)

次の操作が一意に決定できない状態

2012/05/01

�#4

�����������

文法         におけるLR構文解析

29

・i+i+i$

i・+i+i$

i+・i+i$

i+i・+i$

i+i+・ +i$

i+i+i・$

i+i+E・$

i+E・$

E・ $

S → ・E $

E → ・i +E

E → ・i

E → i・+ E

E → i・

E → i +E・

i

E

+

i

$

accept

E → i・+E

E → i・

E → i +・E

E → ・i

E → ・i+E

i

+

E → i +・E

E → ・i

E → ・i+E

E → i +E・

E → i・+E

E → i・

E → i +E・

E

E

⌅{E � i + E | i}, E⇧

不都合な状態(inadequate state)

次の操作が一意に決定できない状態

還元すべきか,あるいはシフトすべきかを決定する方法によって,LR構文解析法にはいくつかのバリエーションが考案

2012/05/01

�#4

�����������

LR(0)項集合の閉包(closure)! Closure(I)

! LR(0)項の集合 I に対して,Iを部分集合として含み,かつ次の条件を満たすLR(0)項集合 I’ のうち最小のもの!        が I’ の要素ならば,任意の導入項    も I’ の要素

! 求め方! I’ の初期値を I とし,上の条件を満たすように,導入項を

I’ に追加していく

30

A⇥ �1 · B�2 B ⇥ ·�

⌅{E � i + E | i}, E⇧【例】文法Closure(S ⇥ ·E$) = {S ⇥ ·E$, E ⇥ ·i+E, E ⇥ ·i}

2012/05/01

�#4

�����������

Goto(I, a)

! LR(0)項の集合 I と,記号a に対して,LR(0)項の集合 Goto(I, a) を次のように定義!

31

Goto(I, a) = Closure({A⇥ �1a · �2 | (A⇥ �1 · a�2) ⇤ I})

I の要素で,ドットの直後にaがあるものすべてに対して,ドット位置を1つ進めたLR(0)項全体を求め,その閉包をとったもの

⌅{E � i + E | i}, E⇧【例】文法Goto({E ⇥ i · +E, E ⇥ i·}, +)= Closure({E ⇥ i+ · E})= {E ⇥ i+ · E, E ⇥ ·i+E, E ⇥ ·i}

2012/05/01

�#4

�����������

正準オートマトン(canonical automaton)! 初期状態          から始め,可能な遷移をすべて求める

32

Closure({S ⇥ ·E$})

⌅{E � i + E | i}, E⇧【例】文法          の正準オートマトンS → ・E $

E → ・i +E

E → ・i

i

E

+

$

accept

E → i・+E

E → i・E → i +・E

E → ・i

E → ・i+E

i

E → i +E・E

S � E · $

還元状態(完全項を含む状態)

不都合な状態正準集合:

正準オートマトンの状態集合

�(I, a) =�

Goto(I, a) (if Goto(I, a) �= ⇥)⇤ (if Goto(I, a) = ⇥)

2012/05/01

�#4

�����������

正準集合の例

33

の正準集合

・・・・・・・

・・

・・

・・・・・・・

・・・・・

・・・

・・

・・

【例】E � E + T | TT � T * F | FF � (E) | i

Page 7: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

正準オートマトンの例

34

例:文法 の正準オートマトン

I0

I1

I2

I3

I4

I5

I6

I7

I8

I10

I11

E

+

*T

F

i

(

E

TF

(

TF

(

i

F

( i

)+

*I9

i

受理 $

#

#

#

#

#

#

初期状態・

還元状態

不都合な状態(とりあえずシフトを優先):・ ・・ ・

【例】E � E + T | TT � T * F | FF � (E) | i

#: 還元状態

2012/05/01

�#4

�����������

配置(configuration)! LR構文解析のある時点 における配置

35

a1a2 · · · an · xkxk+1 · · · xm$(q0a1q1a2q2 · · · anqn, xkxk+1 · · · xm$)

初期状態 qi = Goto(qi�1, ai) = Goto(q0, a1a2 · · · an)

(q0, x1x2 · · · xm$)LR構文解析開始時点の配置:

(q0a1q1 · · · ai�1qi�1Aq, xkxk+1 · · · xm$)

(q0a1q1 · · · anqnxkq, xk+1 · · · xm$)

(q0Sq, $)

q = Goto(qi�1, A)

q = Goto(qn, xk)

A⇥ ai · · · an による還元後:

xkを読み込んでシフト後:

最終的な配置:

ai

2012/05/01

�#4

�����������

LR構文解析の例

36

(I0, i+i+i$)(I0iI1, +i+i$)(I0iI1+I2, i+i$)(I0iI1+I2iI1, +i$)(I0iI1+I2iI1+I2, i$)(I0iI1+I2iI1+I2iI1, $)(I0iI1+I2iI1+I2EI3, $)(I0iI1+I2EI3, $)(I0EI4, $)

S → ・E $

E → ・i +E

E → ・i

i

E

+

$

accept

E → i・+E

E → i・E → i +・E

E → ・i

E → ・i+E

i

E → i +E・E

S � E · $

I0

I1I2

I3

I4i の読み込み+の読み込み

+の読み込み

E → i で還元

i の読み込み

i の読み込み

E → i + Eで還元E → i + Eで還元受理

配置 次の操作

⌅{E � i + E | i}, E⇧【例】文法

2012/05/01

�#4

�����������

LR構文解析の例

36

(I0, i+i+i$)(I0iI1, +i+i$)(I0iI1+I2, i+i$)(I0iI1+I2iI1, +i$)(I0iI1+I2iI1+I2, i$)(I0iI1+I2iI1+I2iI1, $)(I0iI1+I2iI1+I2EI3, $)(I0iI1+I2EI3, $)(I0EI4, $)

S → ・E $

E → ・i +E

E → ・i

i

E

+

$

accept

E → i・+E

E → i・E → i +・E

E → ・i

E → ・i+E

i

E → i +E・E

S � E · $

I0

I1I2

I3

I4i の読み込み+の読み込み

+の読み込み

E → i で還元

i の読み込み

i の読み込み

E → i + Eで還元E → i + Eで還元受理

配置 次の操作

⌅{E � i + E | i}, E⇧【例】文法

2012/05/01

�#4

�����������

SLR(1)構文解析! LR構文解析において,入力トークンを1つだけ先読み(lookahead)し,follow集合を使って不都合な状態を解消

! SLR(1)文法: SLR(1)構文解析が可能な文法

37

I1 = {E ⇥ i · +E, E ⇥ i·}

⌅{E � i + E | i}, E⇧【例】文法 の不都合な状態 I1

次の入力トークンが1.         の要素なら,   で還元2. ‘+’なら, ‘+’を読み込んでシフト3. どちらでもなければ,構文エラー

E � iFollow(E) = {$}

Follow(E) = {$} ⇥� + → SLR(1)文法

S → ・E $

E → ・i +E

E → ・i

i

E

+

$

accept

E → i・+E

E → i・E → i +・E

E → ・i

E → ・i+E

i

E → i +E・E

S � E · $

I0

I1I2

I3

I4

2012/05/01

�#4

�����������

動作表と行先表! 文法

38

i + $I0

I1

I2

I3

I4

s1s2 r2

s1r1

accept

EI0

I2

I4

I3

動作表 行先表

sj: 状態 Ij にシフトrk: 生成規則 k で還元空欄: 構文エラー

還元後の状態(Goto(I, a))

⌅{E � i + E | i}, E⇧

生成規則の番号1 : E � i+E2 : E � i

Action(I, a)

Page 8: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

SLR(1) 構文解析プログラム

39

tuple parse() { int state = 0; for (;;) { action act = lookup_action(state, *p); if (act.todo == SHIFT) { push_state(state); push_value(*p++); state = act.num; } else if (act.todo == REDUCE) { int nt = reduce(act.num); state = lookup_goto(top_state(), nt); } else if (act.todo == ACCEPT) return pop_value(); else /* if (act.todo == UNDEFINED) */ syntax_error(); }}

typedef struct { enum { SHIFT, REDUCE, ACCEPT, UNDEFINED; } todo; int num;} action;

状態スタックと値スタックで配置を表現

動作表

行先表

文法に依存しないプログラム2012/05/01

�#4

�����������

構文解析プログラム(続き)! 生成規則を用いて還元

! 文法に依存

40

int reduce(int production) { tuple x, y; switch (production) { case 1: /* E -> i + E */ x = pop_value(); pop_value(); y = pop_value(); pop_state(); pop_state(); push_value(make_tuple(“+”, y, x)); case 2: /* E -> i */ return E; /* non terminal number */}

関数parseは,LR(1), LALR(1)でも同じ関数reduceは,文法が同じであれば,同一動作表と行先表は,構文解析法によって異なる

2012/05/01

�#4

�����������

! 基本的発想! SLR(1)構文解析

! 不都合な状態を解消するためにfollow集合を使用!     による還元を行うかどうかの決定は,次の入力が     に属するかどうかで調べた

!      は の直後に現れる可能性のあるすべての終端記号でありLR構文解析の状態とは無関係

! LR構文解析の状態を考慮して,不都合な状態を解消する終端記号集合を絞り込むことで,SLR(1)で解消できなかった不都合な状態を解消

! follow集合より正確な先読み記号を使用

LR(1)構文解析

41

A� � Follow(A)

Follow(A) A

2012/05/01

�#4

�����������

完全項を核とするLR(1)項        になって次の入力が なら,     で還元可能

LR(1)構文解析(続き)!     による還元

!  ! 次の入力 は       に限定! LR(1)項:

! 「次の入力が なら    で還元可能」を意味

42

A� �

S · $ �⇥rm

uA · v$ �⇥rm

u� · v$x x � First(v$)[A⇥ �·, x]

[A⇥ �1 · �2, x]

[A⇥ �1�2·, x]A� �1�2

I0 = Closure({[S� ⇥ ·S, $]})

Goto(I, a) = Closure({[A⇥ �1a · �2, c] | [A⇥ �1 · a�2, c] ⇤ I})

x A� �

LR(1)項の一般形 LR(1)項の核

先読み記号

x

初期状態:

2012/05/01

�#4

�����������

LR(1)構文解析 - 閉包の求め方! LR(1)項集合 の閉包

1. 2. の要素         に対して,生成規則    があれば,       であるすべての について,   _     を に追加

3. に追加する要素がなくなるまで,2. を繰り返す

43

I

I �

I � = Closure(I)

[A⇥ �1 · B�2, x] B � �x� � First(�2x) x�

[B ⇥ ·�, x�]

I � � I

I �

I �

【例】E � E + T | TT � T * F | FF � (E) | i

初期状態I0 = Closure({[S ⇥ ·E, $]})

= { [S ⇥ ·E, $],

[T ⇥ ·T*F, $], [T ⇥ ·F, $],[E ⇥ ·E+T, $], [E ⇥ ·T, $], [E ⇥ ·E+T, +], [E ⇥ ·T, +],

[T ⇥ ·T*F, +], [T ⇥ ·F, +],[T ⇥ ·T*F, *], [T ⇥ ·F, *],

[F ⇥ ·(E), *], [F ⇥ ·i, *] }[F ⇥ ·(E), +], [F ⇥ ·i, +],[F ⇥ ·(E), $], [F ⇥ ·i, $],

2012/05/01

�#4

�����������

LR(1)構文解析 - LR(1)項集合の記法

44

【例】E � E + T | TT � T * F | FF � (E) | i

初期状態I0 = Closure({[S ⇥ ·E, $]})

= { [S ⇥ ·E, $],

[T ⇥ ·T*F, $], [T ⇥ ·F, $],[E ⇥ ·E+T, $], [E ⇥ ·T, $],[E ⇥ ·E+T, +], [E ⇥ ·T, +],

[T ⇥ ·T*F, +], [T ⇥ ·F, +],[T ⇥ ·T*F, *], [T ⇥ ·F, *],

[F ⇥ ·(E), *], [F ⇥ ·i, *] }[F ⇥ ·(E), +], [F ⇥ ·i, +],[F ⇥ ·(E), $], [F ⇥ ·i, $],

= { [S ⇥ ·E, $],[E ⇥ ·E+T, $/+], [E ⇥ ·T, $/+],[T ⇥ ·T+F, $/+/*], [T ⇥ ·F, $/+/*],[F ⇥ ·(E), $/+/*], [F ⇥ ·i, $/+/*] }

核が同じであるLR(1)項をまとめる記法

Page 9: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

LR(1)構文解析のための正準オートマトン

! SLR(1)では,

46

構文解析のための正準オートマトンの作り方:

の場合とまったく同じ.

例: を 構文解析するためのオートマトン・ ・

・ ・・

・・ ・・ ・・ ・

・ ・・ ・

のときは だった.は,より詳細な構文解析が可能.

文法はすべて 文法.文法で, 文法でないものが存在する.

【例】E � E + T | TT � T * F | FF � (E) | i

I0 = Closure({[S� ⇥ ·S, $]})初期状態             から出発し,遷移可能な状態をすべて求める

Goto(I4, T ) = I2

I0からT を読み込んだ後の状態

(を読み込んでから,T を読み込んだ後の状態

2012/05/01

�#4

�����������

LR(1)構文解析のための正準オートマトン

! SLR(1)では,

46

構文解析のための正準オートマトンの作り方:

の場合とまったく同じ.

例: を 構文解析するためのオートマトン・ ・

・ ・・

・・ ・・ ・・ ・

・ ・・ ・

のときは だった.は,より詳細な構文解析が可能.

文法はすべて 文法.文法で, 文法でないものが存在する.

【例】E � E + T | TT � T * F | FF � (E) | i

I0 = Closure({[S� ⇥ ·S, $]})初期状態             から出発し,遷移可能な状態をすべて求める

Goto(I4, T ) = I2

LR(1)は,より詳細な構文解析が可能

I0からT を読み込んだ後の状態

(を読み込んでから,T を読み込んだ後の状態

例:文法 の正準オートマトン

I0

I1

I2

I3

I4

I5

I6

I7

I8

I10

I11

E

+

*T

F

i

(

E

TF

(

TF

(

i

F

( i

)+

*I9

i

受理 $

#

#

#

#

#

#

初期状態・

還元状態

不都合な状態(とりあえずシフトを優先):・ ・・ ・

2012/05/01

�#4

�����������

LALR(1)構文解析! LR(1)構文解析法は状態数が多過ぎて実用的でない! 核が同じであるLR(1)項を同一視して状態数を削減! 正確さ: SLR(1) < LALR(1) < LR(1)

! SLR(1)で構文解析できない文法でも,LALR(1)ならできることがある(SLR(1)に比べ広い範囲の文法に対して構文解析が可能)

! 状態数: SLR(1) = LALR(1) < LR(1)! 実用的な構文解析が可能! 構文解析に要する時間もSLR(1)と同じ

! C言語の構文 ... LALR(1)文法となるように与える

47 2012/05/01

�#4

�����������

LALR(1)構文解析! 状態の融合(核が同一のLR(1)項を同一視)

! LR(1)

! LALR(1)

48

構文解析構文解析法は,状態数が多過ぎて実用的でない.

核が同じである 項を同一視して状態数を減らす.

例:

・ ・・ ・

を融合し,

・ ・

さらに

は,正確さは より劣るが,よりは正確.

状態数は と同じ.

構文解析構文解析法は,状態数が多過ぎて実用的でない.

核が同じである 項を同一視して状態数を減らす.

例:

・ ・・ ・

を融合し,

・ ・

さらに

は,正確さは より劣るが,よりは正確.

状態数は と同じ.

Goto(I4, T ) = I2

LRLR(1):  正確さはLR(1)より劣るが,SLR(1)よりは正確  状態数はSLR(1)と同じ

2012/05/01

�#4

�����������

構文解析プログラムの自動生成!  

! 構文解析プログラムを機械的に生成するためのツール

! 与えられた文法に対するLALR(1)構文解析の表を自動生成

! 生成規則毎に,還元する際の動作をCコードで与える

49

yacc source

yacctoken definition

syntax analysis

program

C compiler

yacc library

lex

lex source

lexical analysis

program

lex library

parser.y

executable

y.tab.h

scanner.l

y.tab.c lex.yy.c

a.out

yacc (Yet Another Compiler Compiler)

2012/05/01

�#4

�����������

yaccに入力する文法と動作の定義ファイル! parser.y

50

%token PLUS TIMES LPAR RPAR IDENTIFIER%%E: E PLUS T { $$ = make_tuple("+", $1, $3); } | T { $$ = $1; }T: T TIMES F { $$ = make_tuple("*", $1, $3); } | F { $$ = $1; }F: LPAR E RPAR { $$ = $2; } | IDENTIFIER { $$ = $1; }%%   ... make_tuple の定義 ...

トークンの定義

生成規則と還元時の動作

【例】E � E + T | TT � T * F | FF � (E) | i

Page 10: 言語処理系構成論(4 構文解析法nobuya/lecture/plpc/PLPC2012... · 2012-05-01 · 2012/05/01 言語処理系構成論(4) 岡山大学大学院自然科学研究科

2012/05/01

�#4

�����������

関数 make_tuple の定義! ここでは,還元時に生成されるN組を表示するように定義

51

int prev_tuple = 0;int make_tuple(char *op, int arg1, int arg2) { printf(“T%d: (%s, “, ++prev_tuple, op); if (arg1 > 0) printf(“%s, “, arg1); else printf(“T%d, “, -arg1); if (arg2 > 0) printf(“%s“, arg2); else printf(“T%d, “, -arg2); printf(“)\n”); return -prev_tuple;}

2012/05/01

�#4

�����������

構文解析プログラムの生成! yacc の起動

! y.tab.h! yacc により生成されるファイル! トークンに関する情報(字句解析プログラムで必要)

52

#define PLUS 258#define TIMES 259#define LPAR 260#define RPAR 261#define IDENTIFIER 262

% yacc -d parser.y

-vを指定すると,y.output という名前のファイルが生成される 構文解析のための正準オートマトン,動作表,行先表の情報

yacc source

yacctoken definition

syntax analysis

program

parser.y

y.tab.h

y.tab.c

2012/05/01

�#4

�����������

字句解析プログラムの例

53

%{#include "y.tab.h"extern int yylval;%}%%[\t ]+ {}"+" { return PLUS; }"*" { return TIMES; }"(" { return LPAR; }")" { return RPAR; }[a-z][a-z0-9]* { yylval = copy_lexeme(); return IDENTIFIER; }\n { return 0; }. { printf("lexical error: `%c'\n", yytext[0]); return 0; }%%char StringTable[1000];char *STp = StringTable;int copy_lexeme() { int p = (int) STp; strcpy(STp, yytext); STp += strlen(yytext) + 1; return p;}

scanner.l

2012/05/01

�#4

�����������

字句解析プログラムの生成と実行形式ファイルの生成,および使用例

! lex の起動

! 実行形式ファイルの生成(コンパイル・リンク)

! 使用例

54

% lex scanner.l

% gcc y.tab.c lex.yy.c -ly -ll

% ./a.out x*y+z*wT1: (*, x, y)T2: (*, z, w)T3: (+, T1, T2)% ./a.outx+y+T1: (+, x, y)syntax error%

2012/05/01

�#4

����������� 55