PPT

平成19年度卒業研究
PASCALコンパイラについて
福永研究室
山川 武志
畑中 陽介
佐藤 遼
目次



コンパイラとは?
開発の背景
開発フロー






字句解析
構文解析
型検査
中間コード
コード生成
実行とまとめ
開発の背景(1)



以前、当研究室ではSRC(SRC2)と呼ばれるCPUを製作し
た。SRC2は基本演算の他にFPU(浮動小数点計算機構)を
搭載した汎用型CPUである。
SRC2を使ってハードウェア上でのアプリケーションを開発
するにはアセンブリ言語でプログラミングする必要があっ
た。
SRC2を利用しやすくするため、Pascalのコードをアセンブ
リコードに変換するコンパイラ作成を行った。
プログラミング例

アセンブリ
Loop:
.start
la
r2
la
r3
la
r4
add r1
sub r2
brnz r4
st
r1
stop

10
1
Loop
r1
r2
r2
r3
r2
100
Pascal
progarm sum
var i, total : integer;
begin
total := 0;
for i:=1 to 10 do
total = total+i
end.
コンパイラとは・・・?

ある言語で書かれたプログラムをそれと「等価な」別の言
語のプログラムに「翻訳」すること。
例) Cコンパイラや電卓 etc…
私たちの作ったコンパイラ
Pascal → アセンブリ言語(SRC2対応の)
コンパイラの位置づけ
原始プログラム(入力)
前処理系
コンパイラ
アセンブラ
機械コード
コンパイラの概要(1)


解析部
入力エラーの指摘
生成部・合成部
目的コード生成とコードの最適化
コンパイラの概要(2)






字句解析
構文解析
意味解析(型検査など)
中間コード生成
コード最適化
コード生成
解析部
生成部
仕様・特徴



Pascalコンパイラ
1パス(アセンブラコードの出力まで)
中間コードを作った
開発フロー

コンパイラの開発は以下のようなフローを
経て行った。





字句解析
構文解析
型検査
中間コード生成
目的コード生成
字句解析



ソースコードをトークン(意味を持つ最小の
単位)に分ける。
空白、タブ、改行コードを取り除く。
識別子(名前)を記号表に登録する。
字句解析
program call (input, output);
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:=1 to 10 do
begin
read (m);
writeln(cube(m))
end
end.
program call ( input , output
) ; var i , m , : integer ;
function cube := p * p * p
end ; begin for i := 1 to
10 do begin read ( m ) ;
cube ; writeln ( cube ( m
) ) end end .
字句解析
記号表
token
:
call
id(名前)
i
id(名前)
m
id(名前)
cube
id(名前)
p
id(名前)
:
type
value
・・
字句解析
字句解析の実現には有限オートマトンがよく利用
される。
Pascalの字句解析用に作成した有限オートマトンで
array[1..15] of integer が解析される様子を見る。

array[1..15] of integer
字句解析

「 a 」を読むことで図のように状態が
遷移する。
array[1..15] of integer
字句解析

「◎」の状態は受理状態を表すが、
遷移できなくなるまで読み込みを続ける。
array[1..15] of integer
字句解析

以降の文字は図のように解析され、
array[1..15] of integer
字句解析

『array』の後の『 [ 』を読み込んだ段階で、
遷移できないため、文字列「array」が確定
される。
array[1..15] of integer
字句解析

次は『 [ 』から解析を始める。
array[1..15] of integer
字句解析

しかしその次が『 1 』となり、次の遷移先が
無いため、『 [ 』だけで確定される。
array[1..15] of integer
字句解析

次は数字の『1』から解析を始める。
array[1..15] of integer
字句解析

次は数字の『1』から解析を始める。
array[1..15] of integer
字句解析

次は数字の『1』から解析を始める。
array[1..15] of integer
字句解析

次の『.』を見て図のように遷移するが、
array[1..15] of integer
字句解析

次の『.』を見て図のように遷移するが、
array[1..15] of integer
字句解析

その次の『.』では遷移先が無いため、直前の受
理状態までの文字列(数字の『1』)を確定し、そ
の直後の文字から再び解析する。
array[1..15] of integer
字句解析

数字『1』の直後の『.』から解析を始める。
array[1..15] of integer
字句解析

数字『1』の直後の『.』から解析を始める。
array[1..15] of integer
字句解析

数字『1』の直後の『.』から解析を始める。
array[1..15] of integer
字句解析

二つ目の『.』で図のように遷移。
array[1..15] of integer
字句解析

二つ目の『.』で図のように遷移。
array[1..15] of integer
字句解析

次の数字の『1』で遷移先が無いため『..』を
確定する。以下同じように解析し、
array [ 1 .. 15 ] of integer となる。
array[1..15] of integer
字句解析

字句解析では、Pascalで使うことのない
記号が現れてしまっているときエラーとし
て検出できる。
例) ¥ $ あ etc・・
構文解析


字句解析で分割されたプログラム文が、
Pascalの構文規則に従っているか
チェックする。
もし、従っていない場合はその箇所を
エラーとして検出する。
構文解析

『プログラム』 は 『プログラム頭部』『;』『ブロック』
『.』から成る。
プログラム = プログラム頭部 ; ブロック .
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
構文解析
プログラム頭部 =
program 名前 [プログラムパラメタ並び]
([ ]はあってもなくてもよい)
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
構文解析
プログラムパラメタ並び
= ( 名前並び )
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
構文解析
名前並び = 名前 { , 名前 }
{ }は0回以上の繰り返し
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
構文解析
ブロック = ラベル宣言部 定数宣言部
変数宣言部 手続き宣言部
実行部
変数宣言部
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
実行部
begin read (m) ; writeln(cube(m)) end
end .
手続き・
関数宣言部
構文解析

構文解析は非終端記号(構文規則
の左辺)を関数とすることで実現した。
構文解析
変数宣言部 =
[ var 変数宣言 ; { 変数宣言 ; } ]
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
変数宣言部(){
if(token == “var” ){
match();
変数宣言();
if(token != “ ; ” ){
error();
}
if(token == “var”){
変数宣言();
}
}
}
構文解析
変数宣言 =
名前並び : 型
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
i,m,がそれぞれ変数名であり
integer型を持つという情報を
記号表に保存する。
変数宣言(){
名前並び();
if(token != “:”){
error();
}
match();
型();
}
構文解析
変数宣言部 =
[ var 変数宣言 ; { 変数宣言 ; } ]
program call (input, output) ;
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i:= 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end .
変数宣言部(){
if(token == “var” ){
match();
変数宣言();
if(token != “ ; ” ){
error();
}
if(token == “var”){
変数宣言();
}
}
}
構文解析


構文解析が終了した時点での記号表は右図のよ
うになる。
各宣言のトークン、それぞれの型などが決定でき
ているのがわかる。
型検査

記号表の型情報をもとに、変数などが正し
く使われているか検査するフェーズ。
型検査
program call (input, output);
引数の数と型
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i := 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end.
戻り値の型
型検査
program call (input, output);
引数の数と型
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i := 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end.
戻り値の型
型検査
program call (input, output);
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i := 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end.
型検査
program call (input, output);
引数の数と型
var i, m : integer;
function cube (p : integer) : integer;
begin cube := p*p*p end;
begin for i := 1 to 10 do
begin read (m) ; writeln(cube(m)) end
end.
型検査

各変数の型以外にも、
if文やwhile文などの
条件部がboolean型
かどうか、代入文の
左辺と右辺の整合性
などの検査も行う。
例)
左辺と右辺の
整合性
・if x < y then z:=x else z:=y
・while i <> 1 do begin i := i-1
論理式(boolean型)
でなくてはならない
コード生成


解析部(字句解析、構文解析、型検査)に
よってプログラムが言語の通り書かれてい
るかどうかチェックできた。
生成部では、解析部で作られた記号表を
参照しながら、コードを生成していく。
中間コード

SRC2では(一般のCPUでも)二項演算しか許され
ないが、通常プログラミング言語では自由な演算
が可能になっている。
i := 2 * 3 + 8 div 5
二項演算以外の自由な演算が可能

そのため、プログラム上の計算を全て二項演算
のみに直すことでプログラムを単純にする作業を
施す。それによって作成されるコードを「中間コー
ド」という。
三番地コード


中間表現には、三番地コードと呼ぶ表現を
用いるのが一般的。
三番地コードでは、各文が、演算数に二つ
の番地、結果に一つの番地、合わせて三
つの番地を用いる。
三番地コードの実現


三番地コードの実現に、四つ組という、四
つの欄を持つレコード構造を用いる。
四つ組には、一つの演算子の欄「op」と、
二つの演算数の欄「arg1」「arg2」と、結果
の欄[result]がある。
中間コード
i := 2 * 3 + 8 div 5
t0 := 2
t1 := 3
t2 := t0 * t1
t3 := 8
t4 := 5
t5 := t3 div t4
t6 := t2 + t5
i := t6
二項演算のみで
計算を定義する。
→中間コード
四つ組

先ほどの中間コードを、四つ組を用いて表現する
と、以下のようになる。
i := 2 * 3 + 8 div 5
中間コード

中間コードを生成することで、様々なCPUで動作するコン
パイラを容易に作成することができる。
CPUそれぞれのコ
ンパイラを作成しな
くてはならない。
中間コード

中間コードを生成することで、様々なCPUで動作するコン
パイラを容易に作成することができる。
中間コードまで
各CPUへの変
を作成しておく
換が容易にで
ことで、
きるようになる
コード生成

中間コードで表され
たプログラムを、実
際に使用するCPU
のアセンブリ言語に
変換していく。
コード生成の例

例
掛け算の場合
番地
op
2
*
L-2:
ld
ld
mul
st
arg1 arg2 result
T-0
r1
r2
r3
r3
T-1
T-2
T-0
T-1
r1
T-2
(2*3)
r2
実行

エラトステネスのふるい(素数を見つけるア
ルゴリズム)
考察


今回SRC2用のPASCALコンパイラを開発したこと
で、今までよりもSRC2を容易に扱うことができる
ようになった。
今後はハードウェア上でのアプリケーション開発
などを可能にしたということは、今回の研究で得
た大きな財産であろう。