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