コンパイラ資料 4章 記号表 改訂1版(2014.5.20).

Slides:



Advertisements
Similar presentations
アルゴリズムとプログラミン グ (Algorithms and Programming) 第6回:クラスとインスタンス クラスの宣言 アクセス修飾子 インスタンスの生成 (new キーワード) this キーワード フィールドとメソッドの実際の定義と使い 方 クラスの宣言 アクセス修飾子 インスタンスの生成.
Advertisements

2.5 プログラムの構成要素 (1)文字セット ① ASCII ( American Standard Code for Interchange ) JIS コードと同じ ② EBCDIC ( Extended Binary Coded Decimal for Information Code ) 1.
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
プログラミング言語としてのR 情報知能学科 白井 英俊.
Ex7. Search for Vacuum Problem
プログラミング基礎I(再) 山元進.
Javaのための暗黙的に型定義される構造体
プログラミングパラダイム さまざまな計算のモデルにもとづく、 プログラミングの方法論 手続き型 関数型 オブジェクト指向 代数 幾何.
プログラミング言語論 第6回 型 情報工学科 篠埜 功.
アルゴリズムとデータ構造 2011年6月13日
プログラミング演習II 2004年12月 21日(第8回) 理学部数学科・木村巌.
  【事例演習6】  数式インタプリタ      解 説     “インタプリタの基本的な仕組み”.
ML 演習 第 7 回 新井淳也、中村宇佑、前田俊行 2011/05/31.
アルゴリズムとデータ構造 2011年6月20日
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
コンパイラの解析 (2) GCJのデータ構造 - 1.
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング言語入門 手続き型言語としてのJava
情報工学演習I 第12回 C++の演習4(インライン展開).
細かい粒度で コードの再利用を可能とする メソッド内メソッドと その効率の良い実装方法の提案
オブジェクト指向 プログラミング 第十一回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第十三回 知能情報学部 新田直也.
B演習(言語処理系演習)第8回 評価器 田浦.
FlexとBison+アルファ -実習編-
暗黙的に型付けされる構造体の Java言語への導入
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
Java8について 2014/03/07.
コンパイラ資料 1章 概要 更新.
オブジェクト指向言語論 第八回 知能情報学部 新田直也.
コンパイラ資料 実行時環境.
一時的な型 長谷川啓
ソフトウェア制作論 平成30年11月21日.
アルゴリズムとデータ構造 2010年7月26日
フロントエンドとバックエンドのインターフェース
記号表の検索と登録 長谷川啓
オブジェクト・プログラミング 第8回.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
独習Java ・ 5.7  静的変数と静的メソッド ・ 5.8  ローカル変数と変数のスコープ  11月20日    小笠原 一恵.
アルゴリズムとプログラミング (Algorithms and Programming)
C++ 構文解析 構文解析器の状態保存と復元
プログラミング言語論 第十三回 理工学部 情報システム工学科 新田直也.
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
アルゴリズムとデータ構造 2012年6月11日
アルゴリズムとデータ構造1 2008年7月24日
サブゼミ第7回 実装編① オブジェクト型とキャスト.
アルゴリズムとデータ構造1 2009年6月15日
第5回 プログラミングⅡ 第5回
高度プログラミング演習 (11).
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
統合開発環境のための プログラミング言語拡張 フレームワーク
JAVA入門⑥ クラスとインスタンス.
モジュール分割.
コンパイラ 2012年10月11日
アルゴリズムとデータ構造 2012年6月21日
コンパイラ 第12回 実行時環境 ― 変数と関数 ― 38号館4階N-411 内線5459
アルゴリズムとデータ構造 2010年6月17日
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
演算子のオーバーロード.
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
プログラミング演習I 2003年6月11日(第9回) 木村巌.
:: の扱い 長谷川啓.
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
オブジェクト指向言語における セキュリティ解析アルゴリズムの提案と実現
情報処理Ⅱ 小テスト 2005年2月1日(火).
オブジェクト指向言語論 第十回 知能情報学部 新田直也.
情報処理Ⅱ 第8回:2003年12月9日(火).
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
計算機プログラミングI 第2回 2002年10月17日(木) 履習登録 複習 ライブラリの利用 (2.6-7) 式・値・代入 (2.6-8)
Presentation transcript:

コンパイラ資料 4章 記号表 改訂1版(2014.5.20)

記号表とその利用 記号表は、意味解析(ASTを処理するフェーズ)において、識別子(ID)に関する情報を記録・保持する。特に インタプリタ(解釈器)では値 コンパイラでは型(次章)と配置(?章) 処理対象となる式の例: 2 + x * x 本章では意味解析の一種であるインタプリタの作成を通じてASTの処理と記号表の使い方をみていく。

意味解析と記号表 意味解析 記号表 インタプリタ 解釈 トークン AST 字句解析 構文解析 コンパイラ 型検査 属性 中間コード 値 型 配置

4,5章で取り扱う構文 kittyの構文のうち、 { int x,y; x:=3; y:=2+x*x; print y; } Statement ::= Type id (comma id)* semi | id assign Exp semi | lbrace Statement + rbrace | print Exp semi Type ::= int | bool Exp ::= 数式(前章参照) { int x,y; x:=3;  y:=2+x*x;   print y; }

statementのためのAST(追加) block list<Node*> y decl assign assign print y:int x:int id num id op id list<Bucket*> x 3 y + y { int x,y; x:=3;  y:=2+x*x;   print y; } num op 2 * id id x x

ノードクラスの階層 ProgramNode ArgsNode FunNode BlockNode NodeWithNodeList WhileNode IfNode Node NodeWithChildren PrintNode ReturnNode AssignNode DeclNode NopNode NumNode NotNode IDNode RelNode TruthNode OpNode CallNode

ASTを用いた意味解析の例:eval + * 2 x x Node::eval(void)はインタプリタにおいてASTの各ノードを 解釈する関数であり、 外部プログラムから 根ノードのメンバ関数を呼び出す。 各ノードの処理で子ノードのeval()を呼び その戻り値をもちいて 当該ノード固有の処理を行い、 結果を戻り値として返す。 int OpNode::eval(void) { int val; val = children[0]->eval() + children[1]->eval(); return val; } + 2 * x x

課題: 計算器 calc 1. numだけからなる数式の値を計算するプログラムを作成せよ。(とりあえずprint文のみで可) 2. Blockに対する処理を追加して複数のprint文が書けるようにせよ。 入力例)  { print 2+3*3; print 3+4*4; }

値の登録とルックアップ calcの数式に変数が使えるようにする。 { int x; // declノード: x(初期値任意)登録 x:=3; //assignノード:xの値を3に更新 print x+4; //idノード: xの値を表から引く } assignの第1子もidであることに注意 (Exp中に現れるidとは役割が異なる)

記号表のインターフェース binding:変数名(string)と値(int)の組 記号表:bindingの集合 登録 void put(string name, int val) 表にbinding (name,val)を追加 更新 void set(string name, int val) ルックアップ Bucket* lookup(string name) 表中nameを変数名としてもつbindingを返す。

記号表の実装 バインディング class Bucket { string varname; int value; }; ハッシュ表 x,3 バインディング class Bucket { string varname; int value; }; ハッシュ表 list<Bucket*>* hash_table[HASH_SIZE]; Bucket* list<Bucket*> HASH_SIZE

hash関数、登録操作 unsignend hash(char* s) (K&R 日本語訳 p.175) { unsignend hashval; for (hashval =0; !*s =‘\0’;s++) hashval = *s + 31 * hashval; return hashval % HASH_SIZE; } 登録: 変数名に対するhash関数の値(hash値)をiとする      hash_table[i]の先頭にbindingを挿入。 i i

課題: 変数つき計算器 varcalc 4ページの構文をもつStatementを実行するプログラムvarcalcを作成せよ。 入力例: { int x,y; x:=3; y:=2+x*x; print y-x; }

スコープ {(ブロックのはじめ)で{をスタックに積む。 宣言文で各変数を表に登録し、変数名とhash値の組をスタックに積む。 代入文で表の値書き込み。 }(ブロックのおわり)で{までをpopする。 変数をpopするとき hash_table[i] の先頭のbindingを削除 ただし、iは当該変数のhash値(popしたエントリから取得)

スコープ { int x,y; x:=3; y:=2+x*x; int x; x:=1; y:=x; } print y-x; x 43 { $

課題*:スコープつき計算器scopecalc スコープを実装して前ページのプログラムを実行できるようにせよ。 同一スコープ内での重複宣言を検出するにはどうしたらよいか?

課題:解釈器 interpreter varcalc(またはscopecalc)にif文、while文、print文を追加して   {     int n, v; n:=10; v:=1; while(n>0){ v:=v*n; n:=n-1; } print v;   }

visitorパターン デザインパターンのひとつ。データとアルゴリズムを分離して、機能追加を容易かつ安全にする。 コンパイラ・AST処理の定番とされ、生成系でサポートされることが多い。    cf. javacc/jjtree

prettyprinter(pp) / C OP + pp ID x NUM 3 ノードを引数として呼び出し 情報取得 処理 子供を引数に struct node{ int kind; int op_kind; union { int value; char varname[MAXSIZE]; struct node* children[2+1]; } attr; }; void pp(struct node* n) { indent(); switch(n->kind){ case(ID): printf(“%s\n”,n->attr.varname); break; ..... case(OP): printf(“%s\n”,op_name(n->op_kind)); pp(n->attr.children[0]); pp(n->attr.children[1]); } unindent(); ノードを引数として呼び出し OP + pp 情報取得 ID x NUM 3 処理 子供を引数に 再帰呼び出し + x 3

pp/C++/ポリモルフィズム + vtable 3 x ダイナミックディスパッチ 情報取得 OPNode struct Node { virtual void pp()=0; }; struct IDNode : public Node{ string varname; void pp() { indent(); cout<<n->varname<<“\n”;    unindent(); } struct OpNode : public Node{ int op_kind; Node* children[2+1]; cout<<n->opname(op_kind)<<“\n”; child[0]->pp(); child[1]->pp(); unindent(); + オブジェクトメソッド照会 vtable virtual Node::pp()呼び出し ダイナミックディスパッチ 実メソッド呼び出し NumNode IDNode 3  x 子供のメソッド 呼び出し + x 3 Nodeメンバ 無色 child[0]の具体的なクラスはコンパイル時は不明

PP/C++/Visitor “visitor.h” “pp_visitor.h” “node.h” “pp_visitor.cpp” struct Visitor { virtual void* visit(IDNode* n)=0; virtual void* visit(OpNode* n)=0; }; struct PPVisitor : public Visitor{ void* visit( IDNode* n); void* visit(OpNode* n); }; “node.h” class Visitor; struct Node { virtual void* accept(Visitor* v)=0; }; struct IDNode : public Node{ std::string varname; void* accept(Visitor* v); struct OpNode : public Node{ Operator op_kind; Node* children[2+1]; “pp_visitor.cpp” #include "node.h“ #include "visitor.h“ #include "pp_visitor.h“ using namespace std; void* PPVisitor::visit(IDNode* n) { indent(); cout<<n->varname<<“\n”; unindent(); }; void* PPVisitor::visit(OpNode* n) cout<<opname(n->op_kind)<<“\n”; n->children[0]->accept(this); n->children[1]->accept(this); “node.cpp” #include “node.h” #include "visitor.h“ void* IDNode::accept(Visitor* v) { return v->visit(this); } void* OpNode::accept(Visitor* v) { return v->visit(this); }

visit(IDNode*) vtable + accept vtable visit(OpNode*) vtable x accept Node::accpet(xx_visitor) visit(IDNode*) vtable Node::accpet(pp_visitor) PPVisitor OPNode depth + accept virtual virtual visit(IDNode*) visit(IDNode*) accept vtable virtual visit(OpNode*) visit(OpNode*) vtable visit(OpNode*) IDNode x virtual accept XXVisitor virtual visit(IDNode*) visit(IDNode*) virtual visit(OpNode*) visit(OpNode*)

Visitorパターンの利点と欠点 処理追加が容易で安全  機能改良が安全  ノードクラス追加は面倒 PPVisitor InterpreterVisitor 処理追加が容易で安全  処理ごとにvisitor追加(既存のノードクラス、処理(visitor)のコード変更不要・再コンパイル不要) 機能改良が安全  ノード引数別のvisitメンバ関数ごとに改良(派生クラスでメソッドオーバーライド) ノードクラス追加は面倒 基底クラスで追加ノードに該当するaccept(純粋)仮想関数追加 既存のvisitorは要再コンパイル(追加ノードに対するvisit) AST depth + virtual visit(IDNode*) virtual visit(IDNode*) visit(IDNode*) visit(IDNode*) virtual visit(OpNode*) virtual visit(OpNode*) x 3 visit(OpNode*) visit(OpNode*) PrettierPPVisitor ForNode virtual visit(IDNode*) PP2Visitor virtual visit(IDNode* n) accept virtual visit(ForNode*) visit(ForNode* n)

ポリモルフィズムとVisitorパターン 変更の種類 ポリモルフィズム Visitorパターン 処理の追加 面倒 メンバ関数追加・要再コンパイル 容易 ノードのコードは再コンパイル不要 ノードの追加 派生クラスで追加(既存ノードクラスは再コンパイル不要) 抽象クラスVisitor自体にaccept関数追加(既存Visitorの変更・再コンパイルが必要)

課題:pp_visitor visitorパターンを使ってkitty用ASTのためのpretty printerを実装せよ. ヒント: 21ページのコード(OpNode、IDNodeのみ)をもとにする 外部関数indent(),unindent(),op_name(int),main()を作成またはリンクして動作試験せよ。 構文解析でつくったAST用構造体に反映。 OpNodeは実際にはNodeWithChildrenから派生 ASTすべてのノードに対する、visitorのメンバ関数を追加