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

コンパイラ
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
プログラマがソースプログラムに書けるのは終端記号だけ。
構文解析に先立って字句解析があるときは、終端記号の
一部は字句としてまとめられている。