アルゴリズムとデータ構造1

コンパイラ
2012年10月18日
酒居敬一@A468([email protected])
http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html
1
構文解析
字句の列を入力とする。


字句の列は字句解析器から出力される。

字句の列として表されたプログラムが、文法のどの部分
に対応するかを解析する。

今回の内容



2
構文解析の概論
下向き構文解析法の説明
下向き構文解析法の問題点と解決法
構文解析の役割
プログラミング言語の文法はBNFなどで記述される。
それに基づいてソースプログラムの字句が解析される。
字句の列から文法で許される並びであるかどうか調べる。





許されなれない並びであればエラーとする。
許される並びであれば、解析木を生成し構文木を出力する。

さらに変数や関数の名前は名前表に出力する。
字句要求
ソース
プログラム
字句解析
字句出力
3
構文解析
構文木
名前表
構文規則(生成規則)
この例では、乗算は加算よりも
先に行うことを示している。


演算順序を文法で規定している。
【式の構文規則】
1.式
→ 項|式+項
2.項
→ 因子|項*因子
3.因子 → 名前|(式)
非終端記号


式・項・因子・名前
終端記号


a・b・c
→


4
左辺の非終端記号を右辺に
書き換え可能であることを示す。
【文法G1】
1.式
→
2.式
→
3.項
→
4.項
→
5.因子 →
6.因子 →
7.名前 →
式+項
項
項*因子
因子
(式)
名前
a|b|c
構文図式
BNFと記述力は変わらない。
直感的でわかりやすい。


[プログラム]
;
変数宣言
関数定義
[関数定義]
[変数宣言]
[ブロック]
5
返戻型
識別子
(
変数宣言
,
要素型
{
識別子
文
}
)
ブロック
[文]
変数宣言
=
識別子
;
式
;
関数呼出し
if
while
(
条件式
)
文
(
条件式
)
文
ブロック
return
[関数呼出し]
[式]
[項]
識別子
(
式
)
,
項
項
加減演算子
因数
因数
6
;
式
乗除演算子
[条件式]
式
式
==
[加減演算子]
!=
>
>=
<
<=
[乗除演算子]
+
-
[返戻型]
*
/
[数字]と[英字]は非終端記号なので、
本来はそれらも終端記号に至るまでの
定義が必要。しかし、省略されている。
前回のBNFの例でも省略されている。
要素型
void
英字
[識別子]
英字
数字
[整数]
7
数字
[要素型]
int
式a*(b+c)の解析木と構文木
式
項
項
文法解析ではこれらの木を生成する。
因子
解析木では葉に終端記号で
末端以外が非終端記号
式
因子
名前
式
項
項
因子
因子
*
名前
a
名前
a
*
(
b
解析木
8
+
c
)
+
b
構文木
c
構文解析法
一般にはふた通りに分類可能。


上向き構文解析。



解析木を下から生成していく。
右辺と一致する入力列を左辺に置き換える。
下向き構文解析。


解析木を上から生成していく。
生成可能な解析木を仮定し、一致するかどうかで解析をすすめる。
【文法G1】
1.式
→
2.式
→
3.項
→
4.項
→
5.因子 →
6.因子 →
7.名前 →
9
式+項
項
項*因子
因子
(式)
名前
a|b|c
上向き構文解析法
式
式
解析木の下のほう、
終端記号の非終端記号の
置き換えから始まる解析法。
右辺が一致し左辺に置き換えることを
還元という。
項
項
因子
因子
因子
因子
名前
名前
名前
a
10
項
+
記号列:
a+b*c
7を適用:
名前+名前*名前
6を適用:
因子+因子*因子
3,4を適用:
因子+項*因子
⇒
項+項*因子
1,2を適用:
式+項
⇒
式
b
⇒
*
c
項+項
下向き構文解析法

解析木を根のほうから生成する。


木の形を仮定して葉に至るかどうかを調べる。
葉に至るまで木が完成しなければ、読み込んだ字句は戻して
別の形を探っていく。別の形が尽きたらエラーになる。

仮定⇔後戻り、を繰り返す。
void S(){
aを読む;
A();
bを読む;
dを読む;
void A(){
aを読む;
B();
cを読む;
}
直接導出の例
/* Bを読む */
/* Aを読む */
S  aAbd
A  cb | c
}
void A(){
cを読む;
bを読む;
または
/* 解析が失敗して後戻りが発生したら */
cを読む;
}
11
acbdの解析手順例
解析手順
入力記号
文法の適用
解析結果
a c b d S→a A b d
1
c b d
A→c b|c
3
c b d
c b
d a A b d
○
×
5
c b d
A→c b|c
6
c b d
c
7
b d a A b d
○
○
最も左にある非終端記号から解析を始めている。


○
2
4

a c b d a A b d
最左導出という。
文法の展開に失敗したら後戻りがある。
12
下向き構文解析の問題点

再帰的なプログラムで実現できない文法がある。

左再帰性の問題という。

再帰呼び出しが無限ループする。
void 式(){
式();
+を読む;
項();
}

左辺の非終端記号に対し、右辺に複数の生成規則
があるときに、どれを仮定すべきか一意に決まらない。

バックトラック問題という。

13
前のページの3~6のところ。
左再帰性の除去

問題のある文法。
A  A | 

これを次のように書き換える。
A  A 
A  A | 

一般には次の形をとるときが問題で、
A  A1 | A 2 |  | A m | 1 |  2 |  |  n
このように書き換える。
A  1 A  |  2 A  |  |  n A 
A   1 A |  2 A |  |  m A | 
14
バックトラック


バックトラック法で解は求まるが、やはり速いほうがいい。
そこで、共通部分がある場合にくくりだす。
A  cb | c
次のように書き換える。

A  cA 
A  b | 
void A(){
cを読む;
bを読む;
または
/* 解析が失敗してバックトラックが発生したら */
何も読まない;
}
15
S  aAbd
A  cb | c
を
解析手順
S  aAbd
A  cA 
A  b | 
に置き換えて解析。
入力記号
文法の適用
解析結果
a c b d S→a A b d
1
16
a c b d a A b d
○
2
c b d
A → c A'
3
c b d
4
b d
5
b d
6
d
a A b d
7
b d
A' → b |ε
8
b d a A b d
c A'
○
A' → b |ε
b
○
×
○
A  1 |  2 |  |  m |  1 |  2 |  |  n
の形の場合、
A  A |  1 |  2 |  |  n
A   1 |  2 |  |  m
という形に文法を変形する。
バックトラックがなくなるわけではない。