Transcript Compilers
Compilers ~chapter2~ 電子情報学科 4年 早津 政和 概要 コンパイラの基礎知識 コンパイラ フェーズ フロント・バックエンド 単純なコンパイラ 文法解釈の前に 文法指向翻訳 語彙解釈 シンボルテーブル 中間コードと スタックマシン 実装 The phase of a compiler いくつかのフェーズに分けられる 語彙解析 構文解析 意味解析 中間コード生成 コード最適化 コード生成 シンボルテーブル管理 エラー処理 Front End Back End Front & Back Ends Source language Target machine Front end 依存する 依存しない Back end 依存しない (中間コードに依存する) 依存する 作ってみよう 単純なコンパイラの作成を通して、様々な テクニックを紹介する(front endのみ) “infix→postfix translator”を作ろう +,-で分けられた数字のみの表現を考える ex) 9 - 5 + 2 後からもう少し一般的な表現(数、変数、その 他の演算子)も扱えるように拡張していく ex) (153*m + 5) mod 4 + d 文法解釈の前に そもそもInfix/postfix notationとは? 文法を記述するには? →Context-free grammar & Parse tree 文法の曖昧さの排除 Infix/Postfix Notation infix notation(中置記法=挿入記法) オペランドの間に演算子を置く書き方 数学の世界で一般に用いられている postfix notation(後置記法=逆ポーランド記法) オペランドの後に演算子を置く書き方 解釈が一意に決まるため、括弧を必要としない ex) (9 – 5) + 2 → 9 5 - 2 +, 9 – (5 + 2) → 9 5 2 + - →直接機械語に変換することが可能 Postfix Notation Eの後置記法の定義 Eの後置記法表現をE’とすると 1.Eが変数または定数のとき、E’ = E 2.EがE1 op E2と表されるとき(opは演算 子) E’ = E1’ E2’ op 3.Eが(E1)と表されるとき、E’=E1’ Context-free grammar 4つの構成要素 1.終端記号 2.非終端記号 3.production (右→左) 4.開始記号(非終端 記号のひとつ) ex)「+、-で分けられた数 字」 list → list + digit list → list -digit list → digit digit → 0|1|2|3|4|5|6|7|8|9 Parse Tree parse treeの性質 1.根は開始記号 2.葉はトークンまたは 空 3.節は非終端記号 4.Aの子の節が左から X1,…,Xnとなっている とき、A→X1・・・Xnは production list list list digit digit digit 9 - 5 + 2 Ex) parse tree for 9-5+2 Ambiguity 1) 9-5+2 の解釈は (9-5)+2 or 9-(5+2) の2通り ⇒+, -, *, / は左と関連付けられる (左から実行される) cf) = (代入)は 右 2) 9+5*2の解釈は (9+5)*2 or 9+(5*2) の2通り ⇒*, / は+, - より先行される Syntax-Directed Translation コンパイラの front end を設計するのに非 常に役立つテクニック 文法的構成要素+属性 「属性」・・・型、文字列、格納場所、等 意味的規則→属性の値を計算、統合 Semantic Rule & Semantic Action この章では、深さ優先で節を訪問し、意味 的規則に基づいて属性を評価していく 意味的動作を production の右側に埋め 込むことで、翻訳結果を表示できる ⇒ parse tree を作成する必要がなくなる Parsing トークンの列が文法に合っているか決める 3 2 文脈自由文法だとO(n )、O(n )を実現するには コストがかかるが、プログラミング言語を導入す ると、O(n) も可能に 文法解析手法 top-down : 手作業で効果的な解析機が設計できる bottom-up : 多くの種類の文法や翻訳手法が扱える Predictive Parsing top-down parsing の一種 初めの記号がproductionの右側で生成されうる か、という情報を用いる 手順 A→αのとき、FIRST(α):αの初めの記号群 1.記号を先読みし、FIRST(α)と比較 2.非終端記号に当たるごとにproductionの右側 の役目をする関数を呼び出し、文法規則に当て はまるかをチェックする Left Recursion 非終端記号で始まる production は、無限 ループを引き起こす可能性がある Predictive parser はこの左再帰形文法を 扱えないため、これを排除する必要がある ex) A → Aα| Aβ|γ (左再帰形) ⇒A → γR R → αR |βR |ε ※ε:空 Lexical analysis 入力をトークンに変換し、parser に渡す ひとつのトークンを構成する連続した入力 文字列を lexeme と呼ぶ read character input push back character Lexical analyzer pass token & it’s attributes parser Lexical Analyzer 「空白」とコメントの除去 トークン間の「空白」(空白、タブ、改行)とコメントを無視する 定数 数(連続した数字)を、<num, 31> のように数を表す トークンとその値を表す属性の組、とする 変数とキーワードの認識 count = count + increment ; ⇒ id = id + id ; のように認識、属性としてsymbol table へのポインタを持つ、とする。 begin, end, if, …のキーワードを変数と区別するためには、それらを 予約語とし、変数名として使えないようにする Symbol Table symbol Table は lexeme の保存と検索を請け負う 関数 insert(s, t) : 新しく格納した文字列 s&トークンtの インデックスを返す lookup(s) : 文字列sが格納されていればその インデックス、なければ0を返す キーワード 予約語として扱うため、初めに insert で格納する。 Stack machines L-values & R-values l-values : "locations" r-values : "values“ Stack Manipulation push ν, pop rvalue l , lvalue l := , copy Control Flow 絶対ジャンプ、相対ジャンプ、 シンボリックジャンプがある label l , goto l gofalse l , gotrue l halt Emitting a Translation emit では実装 Infix expressions init.c lexer.c symbol.c parser.c emitter.c Postfix expressions error.c Putting the techniques together(1) The Lexical Analysis Module : lexer.c 関数 lexan() は parser から呼ばれ、トークンを返す。変 数があった場合には symtable に格納する。改行があっ たらlineno の値を増やす。 The Parser Module : parser.c 演算子の先行順を考慮し、左再帰形を排除。関数 parse() が文法の初めの記号から実行を始め、新しいトークンが 必要になったら関数 lexan() を呼ぶ。表示に関数 emit() を呼ぶ。文法違反の際は関数 error を呼ぶ。 Putting the techniques together(2) The Emitter Module : emitter.c 関数 emit(t, tval) でトークン、または属性の値を出力する。 The Symbol-Table Module : symbol.c & init.c 関数 lookup(s, t)、insert(s) に関しては “Symbol Table” の項で述べた通り。関数 init() はキーワードをsymbol table に格納する。 The Error Module : error.c エラーメッセージとその行番号を表示して死ぬ。 GO! E EXE