コンパイラ 2012年10月4日 酒居敬一@A468([email protected]) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html 1 コンパイラの構成と プログラム言語の形式的な記述 式(Expression)と文(Statement) 算術式の中置期法と後置記法 コンパイラの論理的な構成 コンパイラの物理的な構成 プログラム言語の形式的な記述方法 2 バッカス記法 構文図式(→次回) 式(Expression)と文(Statement) 式はそれ自体が値を持つもの。 一般的には値・変数・演算子・関数を組み合わせたもの。 式として認識される範囲は言語仕様に依存する。 文は手続きを表すことが多い。 改行文字まででひとつの文とする言語 区切り文字で区切る言語 Pascalは、';'が区切り文字になっている。 区切りが特に無い言語 3 特別な記号で区切ったり、次行と結合したりもできる。 文脈依存文法。 Cは、';'が式を文とする記号。 算術式の中置期法と後置記法 四則演算では演算には優先順位がある。 乗除算は加減算に優先する、括弧はそれらより優先する。 通常の式では変数と変数の間に演算子がある。 中に演算子を置くので中置記法という。 学群実験の経験より、計算機は優先順位など知らない。 機械語として書かれた順に処理するだけ。 通常の式を機械語に変換するに先立って、書き換える。 4 そのひとつが後置記法。 演算子を被演算対象の後に置く。 後置記法の例 中置記法のA+B*C-Dは、ABC*+D-という後置記法になる。 1. 2. 3. 優先順位に基づき((A+(B*C))-D)というように解釈する。 演算対象を読んだらそのまま出力する 演算子を読んだら、それより優先度の高い演算子があれば スタックから取り出し順に出力しておいて、スタックに積む。 式を読み終わったら全部順に取り出し出力する。 入力 ( ( 出力 A + ( B * C ) ) A B C * + ー D D * * ( スタック 5 ( ( ( + + + + + + ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ー ー ( ( ( ) ー コンパイラの場合 後置記法の式を機械語(中間語)に変換する。 ABC*+D-の場合 1. 2. 3. mul B,C, W1 add A,W1, W2 sub W2,D,W3 変数または定数を入力したらスタックに積む。 演算子を入力したらスタックから右辺・左辺を取り出し出力 最後にスタックに残ったものが式の答え 入力 A B C * + D ー C スタック A 出力 6 B B W1 A A A *,B,C W1 D W2 +,A,W1 W2 W2 W3 ー,W2,D W3 コンパイラの論理的な構成 コンパイラは図のように各フェーズに分かれて処理する。 字句解析 構文解析 意味解析 最適化とコード生成 その過程で中間情報として名前表や中間語を保持する。 ソース プログラム 字句 解析 構文 解析 意味 解析 最適化 中間情報(中間語、名前表) 7 コード 生成 目的 プログラム 字句解析 ソースプログラムを字句と呼ばれる基本要素に分解する。 int a,b,c,d;の例 予約語 int 名前 a 記号コンマ 記号コンマ 名前 d 記号セミコロン 記号コンマ 名前 c a=b+c*d;の例 名前 a 記号等号 記号セミコロン 8 名前 b 名前 b 記号加算 名前 c 記号乗算 名前 d 構文解析 分解された字句の並びが、構文規則に合うかどうか調べる。 関数名、変数名といった名前は名前表に登録される。 名前 a 名前 b 名前 c 名前 d 記号乗算 記号加算 記号等号 エントリ番号 名前 データ型 番地 領域長 1 a int 12 4 2 b int 16 4 3 c int 20 4 4 d int 24 4 5 $wk1 int 28 4 6 $wk2 int 32 4 9 意味解析 式は複数の演算に分解される。 式の場合、例えば、変数の型や型変換の可否を調べる。 最初の例にあったように、4つ組の中間語出力を生成 する際に $wk1や$wk2といった一時記憶を使う。 10 乗算 名前表 #3 名前表 #4 名前表 #5 加算 名前表 #2 名前表 #5 名前表 #6 代入 名前表 #1 名前表 #6 最適化 例えば、最適化前のフェーズの中間語表現 加算 名前表 #2 名前表 #5 代入 名前表 #1 名前表 #6 代入はデータを移動するだけなので、加算命令の結果の 行き先に指定すれば代入が不要になる(無用命令削除)。 加算 名前表 #6 名前表 #2 名前表 #5 名前表 #1 他に、共通部分式の括り出し、定数伝播、演算強度の 低減、ループ内不変式のループ外への括り出し、 などを行う。 11 コード生成 中間語として表されたプログラムを機械語に変換する。 コード生成するために、より前の段階で中間語生成に 制約を設けている。 この段階の中間語はプロセッサに依存しない仮想機械 の命令で、それを変換する。 演算命令にメモリオペランドの使えるアーキテクチャ Reg#1,Addr#20 Reg#1,Addr#24 Reg#1,Addr#16 Reg#1,Addr#12 演算命令にメモリオペランドの使えないアーキテクチャ 12 Load Multiply Add Store Load Load Multiply Load Add Store Reg#1,Addr#20 Reg#2,Addr#24 Reg#1,Reg#2,Reg#1 Reg#2,Addr#16 Reg#1,Reg#2,Reg#1 Reg#1,Addr#12 かなり違うので前段階で 制約として違いを盛り込む。 コンパイラの物理的な構成 パスとは、コンパイラ内部で中間語を順次出力する段階。 プログラムとして分離しているかどうかではない。 ワンパスコンパイラの例 最適化しないことが多い。 字句 解析 ソース プログラム 構文 解析 目的 プログラム 意味 解析 13 コード 生成 普通のコンパイラ 最適化のパスがコード生成前にある。 商用コンパイラではもっとパス数が多い。 3パスコンパイラの例 パス1 パス2 パス3 最適化 コード 生成 字句 解析 構文 解析 ソース プログラム 意味 解析 目的 プログラム 中間語 14 中間語 プログラム言語の形式的な記述方法 プログラミング言語の文法、つまり、生成規則。 プログラムを書くとは、文法に基づいてソースプログラムを 生成すること。だから、生成規則と呼んでいる。 コンパイラではソースプログラムが生成規則から生成されうる 文であるかどうかを判断し、目的プログラムを出力する。 生成規則に則らない記述はエラーとなる。 生成規則を意図的にゆるめている(シンタックスシュガー)場合もある。 生成規則が厳密であること。 もちろん、意図されないものはコンパイラの欠陥。 そのために、形式的な記述法が必要。 ソースプログラムが書きやすいこと。 15 素で書きやすいこと。シンタックスシュガーは必要悪。 バッカス記法(Backus Naur Form, BNF) <>で囲まれたものを構文要素と呼ぶ。 例では字句を定義しているが、字句解析済みの場合もある。 Javaのように名前に日本語文字集合が使える場合は、 こんなに単純に記述できない。ASCII文字集合なら簡単。 →の左側の要素は右側で構成される。 |は「または」を意味する。 <数字>→0|1|2|3|4|5|6|7|8|9 <英字>→a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z |A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z <名前>→<英字>|<名前><英字>|<名前><数字> 16 %token int_const char_const float_const id string enumeration_const %% translation_unit : external_decl | translation_unit external_decl ; external_decl : function_definition | decl ; function_definition : decl_specs declarator decl_list compound_stat | declarator decl_list compound_stat | decl_specs declarator compound_stat | declarator compound_stat ; decl : decl_specs init_declarator_list ';' | decl_specs ';' ; decl_list : decl | decl_list decl ; decl_specs : storage_class_spec decl_specs | storage_class_spec | type_spec decl_specs | type_spec | type_qualifier decl_specs | type_qualifier ; storage_class_spec : 'auto' | 'register' | 'static' | 'extern' | 'typedef' ; type_spec : 'void' | 'char' | 'short' | 'int' | 'long' | 'float' | 'double' | 'signed' | 'unsigned' | struct_or_union_spec | enum_spec | typedef_name 17 ; type_qualifier : 'const' | 'volatile'出典:http://www.cs.man.ac.uk/~pjj/bnf/c_syntax.bnf 拡張BNF {}:{}の中の要素を0個以上並べたもの。 []:[]の中の要素を0または1個書いたもの。 これを使うとこのように<名前>を書き直せる。 <名前>→<英字>{<英字>|<数字>} BNFで使う記号 <>{}[]|→ はメタ記号と呼ぶ。 <>で囲んだ構文要素を非終端記号 <>で囲まないものを終端記号と呼ぶ。 18 プログラマがソースプログラムに書けるのは終端記号だけ。 構文解析に先立って字句解析があるときは、終端記号の 一部は字句としてまとめられている。
© Copyright 2024 ExpyDoc