Microsoft Word - problem5.doc

Similar documents
Microsoft Word - problem3.doc

Microsoft PowerPoint ppt

Microsoft PowerPoint ppt

情報工学実験 C コンパイラ第 2 回説明資料 (2017 年度 ) 担当 : 笹倉 佐藤

Microsoft PowerPoint ppt

Microsoft PowerPoint - 計算機言語 第7回.ppt

Microsoft PowerPoint - prog03.ppt

Microsoft PowerPoint - CproNt02.ppt [互換モード]

(1) プログラムの開始場所はいつでも main( ) メソッドから始まる 順番に実行され add( a,b) が実行される これは メソッドを呼び出す ともいう (2)add( ) メソッドに実行が移る この際 add( ) メソッド呼び出し時の a と b の値がそれぞれ add( ) メソッド

Microsoft Word - CompA-Ex doc

Prog1_6th

プログラミング実習I

プログラミング入門1

メソッドのまとめ

memo

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

Microsoft PowerPoint - 03BNFScanner-print.ppt

Java プログラミング Ⅰ 3 回目変数 変数 変 数 一時的に値を記憶させておく機能型 ( データ型 ) と識別子をもつ 2 型 ( データ型 ) 変数の種類型に応じて記憶できる値の種類や範囲が決まる 型 値の種類 値の範囲 boolean 真偽値 true / false char 2バイト文

Microsoft Word - Javacc.docx

Microsoft PowerPoint - 03BNFScanner.ppt [互換モード]

プログラミング基礎I(再)

Microsoft Word - no11.docx

Javaプログラムの実行手順

PowerPoint Presentation

Javaによるアルゴリズムとデータ構造

ポインタ変数

Microsoft PowerPoint - 11.pptx

デジタル表現論・第4回

Microsoft PowerPoint Java基本技術PrintOut.ppt [互換モード]

Microsoft PowerPoint ppt

JavaプログラミングⅠ

Javaセキュアコーディングセミナー2013東京第1回 演習の解説

Microsoft PowerPoint - prog04.ppt

ex04_2012.ppt

デジタル表現論・第6回

プログラミング入門1

Microsoft Word - 3new.doc

Java講座

< F2D837C E95CF CF68A4A94C5816A2E6A>

Microsoft PowerPoint - ruby_instruction.ppt

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション

始めに, 最下位共通先祖を求めるための関数 LcaDFS( int v ) の処理を記述する. この関数は値を返さない再帰的な void 関数で, 点 v を根とする木 T の部分木を深さ優先探索する. 整数の引数 v は, 木 T の点を示す点番号で, 配列 NodeSpace[ ] へのカーソル

gengo1-11

PowerPoint プレゼンテーション

18/12/06 情報工学実験 C コンパイラ (2018 年度 ) 担当 : 笹倉 佐藤 その 3 yacc の構造 定義部 %% 定義部の終了 規則部 %% 規則部の終了 ユーザ定義サブルーチン部 :C のプログラムを書く 形は lex と同じ 1

メソッドのまとめ

プログラミング入門1

PowerPoint プレゼンテーション

コマンドラインから受け取った文字列の大文字と小文字を変換するプログラムを作成せよ 入力は 1 バイトの表示文字とし アルファベット文字以外は変換しない 1. #include <stdio.h> 2. #include <ctype.h> /*troupper,islower,isupper,tol

Microsoft PowerPoint - 09.pptx

Cプログラミング1(再) 第2回

Microsoft PowerPoint L07-Imperative Programming Languages-4-students ( )

PowerPoint プレゼンテーション

JavaプログラミングⅠ

PowerPoint Presentation

プログラミング入門1

オブジェクト指向プログラミング・同演習 5月21日演習課題

Microsoft PowerPoint - prog07.ppt

Microsoft Word - no202.docx

プログラミング入門1

Microsoft Word - VBA基礎(6).docx

Prog1_10th

基本情報STEP UP演習Java対策

char int float double の変数型はそれぞれ 文字あるいは小さな整数 整数 実数 より精度の高い ( 数値のより大きい より小さい ) 実数 を扱う時に用いる 備考 : 基本型の説明に示した 浮動小数点 とは数値を指数表現で表す方法である 例えば は指数表現で 3 書く

関数 C 言語は関数の言語 関数とは 関数の定義 : f(x) = x * x ; 使うときは : y = f(x) 戻り値 引数

プログラミング入門1

Method(C 言語では関数と呼ぶ ) メソッドを使うと 処理を纏めて管理することができる 処理 ( メソッド ) の再実行 ( 再利用 ) が簡単にできる y 元々はC 言語の関数であり 入力値に対する値を 定義するもの 数学では F(x) = 2x + 1 など F(x)=2x+1 入力値 (

Microsoft PowerPoint - prog09.ppt

memo

/*Source.cpp*/ #include<stdio.h> //printf はここでインクルードして初めて使えるようになる // ここで関数 average を定義 3 つの整数の平均値を返す double 型の関数です double average(int a,int b,int c){

memo

Microsoft PowerPoint - prog09.ppt

プログラミング実習I

ポインタ変数

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション

Java Scriptプログラミング入門 3.6~ 茨城大学工学部情報工学科 08T4018Y 小幡智裕

Microsoft PowerPoint - 第3回目.ppt [互換モード]

Microsoft PowerPoint - chap10_OOP.ppt

CプログラミングI

ポインタ変数

人工知能入門

情報処理Ⅰ演習

PowerPoint プレゼンテーション

プログラミングA

(Microsoft PowerPoint - \223\306\217KJAVA\221\346\202R\224\ ppt)

PowerPoint プレゼンテーション

Microsoft PowerPoint - Compiler03note.pptx

情報工学実験 C コンパイラ第 2 回説明資料 (2018 年度 ) 担当 : 笹倉 佐藤

プログラミングA

基礎計算機演習 実習課題No6

基礎プログラミング2015

JEB Plugin 開発チュートリアル 第4回

Taro-ファイル処理(公開版).jtd

6 関数 6-1 関数とは少し長いプログラムを作るようになると 同じ処理を何度も行う場面が出てくる そのたびに処 理を書いていたのでは明らかに無駄であるし プログラム全体の見通しも悪くなる そこで登場す るのが 関数 である 関数を使うことを 関数を呼び出す ともいう どのように使うのか 実際に見て

Java プログラミング Ⅰ 7 回目 switch 文と論理演算子 今日の講義講義で学ぶ内容 switch 文 論理演算子 条件演算子 条件判断文 3 switch 文 switch 文 式が case のラベルと一致する場所から直後の break; まで処理しますどれにも一致致しない場合 def

JavaプログラミングⅠ

gengo1-10

Transcription:

コンパイラ演習 : 作成問題 5 ( 最終課題 ) ( 担当 : 佐々木晃 ) 目的機械は hsm 仮想機械とする 昨年度までの講義資料 ( 中田先生 開先生による ) も参考にすること 演習問題 B5( 締め切り =2008/01/27) 問題番号 : B5 課題名 : コンパイラの作成 5 ( 昨年度の第 5 回とは問題が異なるので 間違わないようにしてください ) 問題 JavaCC を用いて, 文法 5 と下記の仕様をみたすプログラムを作成せよ. 斜体の部分 ( 配列機能 コメント機能 論理演算 ) は余力のある人向けです. ( 追加分 ) getint 文を処理できる (hsm の RDI 0 0 命令を使う ) 関数を処理できる. 関数名に英字で始まる英数字の文字列を利用できる. 関数の未定義, 二重定義, 引数の個数の不一致のエラーをユーザーに知らせる. 大域変数を処理できる. 大域の変数名, 配列名に英字で始まる英数字の文字列を利用できる. 大域の変数, 配列は, 全部で 256 個利用できる. 大域の変数, 配列の未定義, 不一致, 二重定義のエラーをユーザーに知らせる. 不一致とは int で宣言された変数を配列として使おうとする 関数として宣言された変数に int の値を代入しようとするなど /* から */ をコンパイルから除外する.( コメントの処理 ) 論理演算のサポート ただし,Java や C とは違い, カッコとして [] を使い, 優先順位は, ( 高 ) >=,>,<=,<,!=,==! && ^^, ( 低 ) とする 前回の ppt 資料や前期分の第 4 回課題の資料を参考にするとよい ( 変更分 ) 関数, 変数, 配列要素, 整数をオペランドとし, 四則演算, 単項マイナス, 括弧を含む式を右辺に持つ代入文を処理できる. ( 前回分 ) putnl 文を処理できる. do while 文を処理できる. If 文を処理できる ( 余力がある者は if-else も扱えるようにせよ ) 比較演算を処理できる. ( 前々回分まで ) 変数名に英字で始まる英数字の文字列を利用できる. putint 文が使える 変数名の未定義, 二重定義のエラーをユーザーに知らせる. 変数, 整数をオペランドとし, 四則演算, 単項マイナス, 括弧を含む式を右辺に持つ代入文を処理できる. プログラムの提出は提出指針に従うこと http://cis.k.hosei.ac.jp/~asasaki/lecturecompiler/guideline.htm 文法 5 ( 斜体部 ( 配列 ) は余力のある人向け ) <PROGRAM>::= <DECLS> <MAIN> <DECLS>::= empty <DECLS> <INTDECL> <DECLS> <FUNCDECL> <INTDECL>::= 'int' <IDENTLIST> ';' <FUNCDECL>::= 'int' <IDENT> '(' <FORMALLIST> ')' <BLOCK>

<FORMALLIST>::= empty <IDENTLIST> <IDENTLIST>::= <IDENT> <IDENT> '[' <NUMBER> ']' <IDENTLIST> ',' <IDENT> <IDENTLIST> ',' <IDENT> '[' <NUMBER> ']' <MAIN>::= 'int' 'main' '(' ')' <BLOCK> <BLOCK>::= '{' <INTDECLLIST> <STATEMENTLIST> '' <INTDECLLIST>::= empty <INTDECLLIST> <INTDECL> <STATEMENTLIST>::=empty <STATEMENTLIST> <STATEMENT> <STATEMENT>::= <SUBSTITUTION> '=' <EXPRESSION> ';' '{' <STATEMENTLIST> '' 'if' '(' <LOGICALEXPRESSION> ') <STATEMENT> 'do' <STATEMENT> 'while' '(' <LOGICALEXPRESSION> ')' 'return' <EXPRESSION> ';' 'putint' '(' <EXPRESSION> ')' ';' 'getint' '(' <SUBSTITUTION> ')' ';' 'putnl'';' <SUBSTITUTION>::= <IDENT> <IDENT> '[' <EXPRESSION> ']' <LOGICALEXPRESSION>::= <LOGICALFACTOR> <LOGICALFACTOR>::= <EXPRESSION> '==' <EXPRESSION> <EXPRESSION> '!=' <EXPRESSION> <EXPRESSION> '>=' <EXPRESSION> <EXPRESSION> '>' <EXPRESSION> <EXPRESSION> '<=' <EXPRESSION> <EXPRESSION> '<' <EXPRESSION> <EXPRESSION>::= <TERM> <EXPRESSION> '+' <TERM> <EXPRESSION> '-' <TERM> <TERM>::= <UNARY> <TERM> '*' <UNARY> <TERM> '/' <UNARY> <UNARY>::== <FACTOR> '-' <UNARY> <FACTOR>::= <IDENT> <IDENT> '[' <EXPRESSION> ']' <NUMBER> <IDENT> '(' <ACTUALLIST> ')' '(' <EXPRESSION> ')' <ACTUALLIST>::= empty <EXPRESSIONLIST> <EXPRESSIONLIST>::= <EXPRESSION> <EXPRESSIONLIST> ',' <EXPRESSION> <IDENT>::= 英字で始まる英数字の繰り返し文字列 <NUMBER>::= 数字の 1 回以上の繰り返し文字列 -------------------------------------------- putint <EXPRESSION> の値を整数で出力

getint <SUBSTITUTION> に割り当てられた場所にコンソールからの入力整数を代入する. putnl 改行コードをコンソールに出力する 論理演算の文法 ( 余力のある人向け ): 上記文法の <LOGICALEXPRESSION> 以下の部分が 次のように変更となる <LOGICALEXPRESSION>::= <LOGICALTERM> <LOGICALEXPRESSION> ' ' <LOGICALTERM> <LOGICALEXPRESSION> '^^' <LOGICALTERM> <LOGICALTERM>::= <LOGICALUNARY> <LOGICALTERM> '&&' <LOGICALUNARY> <LOGICALUNARY>::= <LOGICALFACTOR> '!' <LOGICALUNARY> <LOGICALFACTOR>::= <EXPRESSION> '!=' <EXPRESSION> <EXPRESSION> '>=' <EXPRESSION> <EXPRESSION> '>' <EXPRESSION> <EXPRESSION> '<=' <EXPRESSION> <EXPRESSION> '<' <EXPRESSION> '[' <LOGICALEXPRESSION> ']' 補足説明 コンパイラの作成問題 5 のヒント ( 講義資料を参照のこと また昨年度の解説ページも参考にすること ) 次の段階が考えられる この課題に関しては A,B の段階に応じて評価を行うことにする C は配列ができた人向け A: 引数なしの関数宣言と呼び出し B: 引数ありの関数宣言と呼び出し ( 引数は int) C( 余力のある者 ): 引数ありの関数宣言と呼び出し (B+ 配列の参照呼び )( 完成形 ) 指針 : A: 引数なしのなしの関数宣言関数宣言と呼び出し 前段階として (1) 大域変数宣言とその参照, (2)main 関数の宣言 呼び出しができるようにすることを目指すと良い (test-a0.h) int b; a = 1; b = 2; putint(a+b); (test-a1.h) int b; a = 1; b = 2; g = 3; putint(a);

return(0); 文法についてまず 大域変数の宣言を加えた場合を考える int i; この場合 int i; の宣言が大域変数の宣言なのか メインプログラムの宣言なのかは int の次のトークンまで読まないと判別がつかないので LOOKAHEAD(2) とするとよい DECLS : { { (LOOKAHEAD(2) INTDECL)* なお関数宣言を加える場合には 下記を考えると int i; int f(x){ int main(x){. int i の i が変数宣言か関数宣言なのかは その次のトークンが ';' か '(' で決まるため 括り出し等を用いない場合は LOOKAHEAD(3) が必要になる INTDECL, FUNCDECL をまとめて考えたときの JavaCC のコードの例は次の形になる void <PROGRAM>() : { { <DECLS>(); <MAIN>(); void <DECLS>() : { { ( LOOKAHEAD(2) <DECL>() { )* void <DECL>() : { { LOOKAHEAD(3) "int" <IDENTLIST>() ";" "int" <IDENT>() "(" <FORMALLIST>() ")" <BLOCK>() 記号表変数が大域 ( レベル 0) で宣言されたもの 関数内 ( レベル 1) で宣言されたものの区別をする必要がある まず エントリの中に level というフィールドを用意する 大域変数の宣言の場合は 0 局所 ( 関数内 ) での変数の場合は 1

以上 ( 今回の場合は 1 のみ ) に設定するようにする また 名前が変数 配列 関数のどれに利用されるかを区別する type フィールドを用意する class Name { String ident; int type; int address; int level; // 今回は 0 か 1 type には,0 または 1 または 2 を保持して, 変数 (0) と配列 (1) と関数 (2) の区別に利用する. ( 本来は このようにマジックナンバーを使うのは良くないので Enum を使うあるいは 定数変数を用いるべきである ) 表 ( テーブル ) は レベルごとに別に用意する方法と 別には用意しない方法がある ここでは別に用意する場合について説明する まず コンパイルの開始時には大域用 ( レベル 0) にテーブルを用意する 関数をコンパイルする場合は その都度新しいテーブル ( レベル 1) を用意する レベル 1 のテーブルからは レベル 0 へのテーブルを参照できるようにする class NameTable{ Name[] nametable; int level; // 記号表のレベル NameTable parent; // 親テーブル レベル 0の記号表の場合は null NameTable(int max, int lev, NameTable p){ // コンストラクタ テーブルに登録できるエントリ数は max とする // レベルを lev とする // 親テーブルを指定する レベル 0 の場合は null. Name searchname(string ident, int type) throws RuntimeException{ // テーブルにつづり ident をもつ名前 ( エントリ ) を探索し あればそれを返す // 自分のレベルに名前が登録されていない場合は 親テーブルを再帰的に探 // 索する エラーがあったら例外を投げる. 変数の参照の例 : FACTOR. <IDENT> {Name entry = nt.searchname(id, 0); // 0 は変数 (LDV, level - entry.level, entry.address) を生成 ( 配列に対応する場合は LDA を使う ) main 関数の定義 関数をコンパイルする際には BLOCK を処理する前に レベル 1 の記号表を用意する メイン関数の場合は 例えば次のようになるだろう <INT> <MAIN> "(" ")" {level++; nt = new NameTable(512, level, nt) BLOCK nt はネームテーブル,level は 現在のレベルである 今回の場合 level++ は level=1 でも良い ベースアドレスから 3 個分の記憶域は システム用に使われるため 局所変数の ( 相対 ) アドレスは 3 から始まることに注意する return( 式 ) で返り値を返すようにする

BLOCK { 局所変数を 3 番地から登録するように準備する "{" INTDECLS() { PUSH 0, n のコードを生成 STATEMENTLIST() "" { nt = nt.parent; level--; // ここでレベル 1 の記号表を開放 ( リセット ) する main 関数の呼び出し main 以外に関数定義がない場合は main 関数の呼び出しは次のようになる 他に関数定義がある場合 main 関数の開始番地は 4ではなくなるので 命令 1 の CALL 0 4 における飛び先はバックパッチで処理する必要がある 0: PUSH 0 n (n は大域変数の数に応じて ) 1: CALL 0 4 2: POP 0 n+1 (+1 は返り値の分 ) 3: HLT 0 0 4: PUSH 0 m main 関数のコード ( 局所変数の数 +3) : EF 0 0 返り値を 1 つスタックに積んでおく 引数なしのなしの関数宣言関数宣言と呼び出し次のようなプログラムがコンパイル 実行できることを目指す (test-a3.h) int test(){ a = 10; g = g + a; return a; g = 1; putint(test()); return 0; 宣言関数 f を宣言するときは 関数 f はレベル 0の記号表に登録する address には 関数 f の開始アドレスを登録する f をコンパイルする際には main と同様 レベル 1の記号表を用意する FUNCDECL <INT> <IDENT> { 関数 <IDENT> とその開始アドレスをレベル 0 の表に登録 "(" ")" {level++; nt = new NameTable(512, level, nt) BLOCK() 呼び出し文法に関しては 通常の変数参照と関数呼び出しの区別は <IDENT> の次が '(' かどうかで決まるので左くくりだしか

LOOKAHEAD(2) による先読み指定が必要 関数のエントリには開始アドレスが登録されているので そのアドレスを CALL すればよい p オペランドは今回は常に 1 であるが 将来関数の中でネストした関数を使えるようにした場合のために 下記のようにレベル差を指定する FACTOR. <IDENT> "(" ")" {Name entry = nt.searchname(id, 2); // 2 は関数 CALL, level - entry.level, entry.address を生成 B: 引数ありのありの関数宣言関数宣言と呼び出し ( 引数は int) 次のようなプログラムがコンパイル 実行できることを目指す (test-b1.h) int test(i){ int b; a = 100; return a+i; g = test(10); return 0; (test-b2.h) int test(i, j){ a = 100; return a+i-j; g = test(10,20); return 0; 記号表の変更点記号表には関数の引数の数を保存しておく 大域の記号表の場合は 0にしておけばよい class NameTable{. Name[] nametable; int argc; // 関数スコープ ( レベル 0 以外 ) の場合の引数の数 int level; // 記号表のレベル NameTable parent; // 親テーブル レベル 0の記号表の場合は null

エントリには 関数名の場合には引数の個数を残しておけるようにする また 変数名の場合 それが仮引数として登録されたものかどうかのフラグを残せるようにする class Name { String ident; int address; int type; int size; int level; // 今回は 0 か 1 int argc; // 関数の場合 引数の個数 ( 関数呼び出しの際の引数の数をチェックするため ) boolean isarg; // 仮引数かどうか address には,base からの相対アドレスを保持する. 配列の場合は, 第 0 要素の相対アドレスとする. size には, 変数や配列が必要とする段数を保持しておく. 変数の場合は 1 であるが, 配列の場合, 配列宣言文の <NUMBER> に対応する数になる. 関数の定義関数の定義の際 関数名自体 ( 名前 開始アドレス等 ) はレベル 0に 引数および局所変数の名前はレベル 1に登録しなければならない また 引数がある場合には 引数名に対するアドレスはベースからの相対値ではマイナスになるので ( 引数が a,b,c の3つの場合 それぞれ -3, -2, -1 が相対アドレスとなる ) 引数を全部登録した時点で アドレスを調整するとよい FUNCDECL <INT> <IDENT> { 関数 <IDENT> とその開始アドレスをレベル 0 の表に登録 level++; nt = new NameTable(512, level, nt) "(" FORMALLIST() ")" { 引数の個数を関数 <IDENT> のエントリ (argc) に登録 引数の個数はレベル 1 の記号表にも登録 (nt.argc= ) 引数のアドレスをマイナス値になるように調整する ( 先に調整しておく場合 ) BLOCK() return では 引数の個数を指定する必要がある STATMENT :: 'return' EXPRESSION { EF 0, nt.argc のコードを生成 C: 引数ありのありの関数宣言関数宣言と呼び出し ( 引数は int および配列配列アドレス ) 次のようなプログラムがコンパイル 実行できることを目指す (test-c1.h) int test(i[2]){ return i[0]+i[1]; int b; int a[2]; a[0]=10; a[1]=20; g = test(a);

return 0; パラメータの参照渡参照渡しについて int a[20]; int func(i[20]){. という宣言があった場合 f の呼び出しは f( 配列名 ) という呼び出しになることに注意 上記の場合なら例えば x := func(a); のように呼び出す すなわち 配列名を単独で参照してよいことになる なお 下記のように int func(i[20]){ int j[10];. x := i[10]; 引数の配列 i を参照する場合 i のアドレスを LDA でロードするのではなく i に 渡される アドレスを LDV でロードする必要がある 局所の j[10] を参照する場合 LDA 0, 0 (j のアドレス ) LDC 0, 10 AD 0, 0 LDA 0, 0 ( 代入なら STI) であるが 引数で与えられた i[10] を参照する場合 LDV 0, -1 (-1 番地 ( 第一引数のアドレス ) にはアクセスしたい配列のアドレスが入っている ) LDC 0, 10 AD 0, 0 LDA 0, 0 ( 代入なら STI) となることに留意する必要がある