コンパイラ資料 2章 字句解析 字句解析の役割 原始プログラム int fact(int n) { int x,y; if(n=0) x:=1 ... 字句解析 ルーチン トークン列 構文解析 ルーチン 〈int〉〈id,”fact” 〉〈lparen〉〈int〉〈id,”n”〉 〈rparen〉〈lbrace〉〈int〉〈id,”x”〉 〈comma〉〈id,”y”〉〈semi〉〈if〉〈lparen〉 〈id,”n”〉〈rel,EQ〉〈num,0〉〈rparen〉 〈id,”x”〉〈assign〉〈num,1〉 .... 原始プログラムを字句の種類を表すトークンの列に変換 字句、トークン、パターン、 • 字句は原始プログラムに現れる文字列で 意味を持つ最小の単位 • トークンは字句の種類をあらわし、構文解 析で利用される • パターンはトークンがあらわす種類の字句 の集合 (kittyコンパイラの) トークン、パターン、字句 トークン パターン if のみ if <または … または>= rel id assign op semi num literal 字句の例 if <,<=,=,<>,>,>= 英字で始まる英数字列 pi, count, D2 := のみ := +, -, *, /, * ; のみ ; 任意の数値定数(整数) 314,0,6455 “core dumped” “と”の間の文字列 トークンの属性 • トークンだけではもとの情報(数値など)は 失われる。トークンに付随してもと情報を 保持するものがトークンの属性 • トークンの属性は構文解析では使われな いが記号表参照やコード生成で使われる トークンの属性の例 x := x*2 ; のトークンとその属性の対は <id , “x” > <assign > <id, “x” > <op, prod > <num, 2> <semi> パターンの記述 パターンの記述には正規表現を用いる。 正規表現とその意味: a はアルファベットの記号を意味するメタ記号、 r(L),s (L)をそれぞれ正規表現r,sのあらわすパターンとして、 正規表現 パターン f f ε {ε} a r |s rs r* {a } r(L) ∪ s (L) r(L) s (L) r(L)* 正規表現の例 kittyの識別子: 英字で始まる英数字 (A|B|…|Z|a|b|…|z) (A|B|…|Z|a|b|…|z|0|1|…|9)* 正規定義 正規表現に名前をつけて、別の正規表現の中で使 えるようにする。 d1 r1 d 2 r2 ... d n rn riは {d1 , d 2 ,, di 1}上の正規表現 正規定義の例 kittyの識別子(正規定義版) letter → A|B|…|Z|a|b|…|z digit → 0|1|…|9 id → letter(letter|digit)* 略記法 rr *を r r|εをr? A|B|Cを[ABC],A|B|…|Zを[A-Z] と略記する。 略記による正規定義の例 Pascalの符号なし数の正規定義 digit → [0-9] digits → digit+ optional_fraction → ( . digits)? optional_exponent → (E(+|-)? digits)? num → digits optional_fraction optional_exponent 問い: C言語の浮動小数点定数のパターンを正規定義で書け。 課題: iddfa • 英字で始まる英数字を受理する有限オー トマトンを状態遷移図で書け。 • 実装せよ。(標準入力をテープとし、受理し たときYes,受理しないときNoと印字する) 字句の認識:オートマトンとの違い 1. 複数の字句が連続 x>0 どうやって切り分けるか? 開始 ⇒ 字句の切り出し 2. xyz 0 x y z3変数なのかxyzで1つか? 英字 オートマトンならすべて受理 1 ⇒ 字句定義で最長一致に限定 英字 字句切り出し 1. 入力バッファの途中で有限オートマトンを模倣 するにはヘッドに対応する走査用のポインタ (前進ポインタ)の他にテープ左端をあらわす 字句先頭ポインタを用いる。 2. パターンごとに状態遷移図を用い、 1. 受理に失敗したら前進ポインタを字句先頭ポインタ 位置に戻して別のトークンの状態遷移図を調べる。 2. 成功したら字句先頭ポインタを前進ポインタの位置 に移して次の字句を調べる。 字句先頭ポインタと前進ポインタ buf x p x < 0 bp left buf 1 p 1 xp1受理後 < 0 left : 字句先頭ポインタ(左端) left bp bp: 前進ポインタ (ヘッド) 開始 最長一致 0 • xyz 最も長くなるように切り取る 英字 英字 1 other 2 * 出る矢印がない状態: 確定 *つき状態で終ったときは1文字オーバーラン ⇒ヘッドを1文字戻し(UNREAD), 手前の状態で確定 最長一致の例2 開始 > 0 = 3 6 * other relの認識(属性値つき) 開始 < 0 LE NE LT EQ > 3 = other 6 GE * GT 字句解析用オートマトンの 動作まとめ 1. 状態遷移先がある限りヘッドを進めながら遷移を繰り 返す。 2. 最後に到達した状態に*がついていたら最後の状態 遷移とヘッド移動をなかったことにして戻す。 3. 現在の状態が受理状態なら属性値を大域変数 1. 2. string yy_val int yy_ival //文字列用 //整数値用 に書き込み、その状態の番号(≧0)を返す。 受理状態でなければヘッドを左端に戻し、 REJECT(=-1)を返す。 属性値の型 enum Relation {//関係演算子(不等号)の種類 LT, EQ, GT, LE, NE, GE }; union Attribute {//属性値用共用体 int i_value; Relation relation; }; DFAクラス class DFA{ virtual int move(int state, char c)=0; public: bool accept(void); //acceptは受理したとき //token,yy_val,attributeをセットして //trueを返す。 } class IDDFA : public DFA{ int move(int state, char c); } 字句解析手順(inr限定) 1. 文字バッファに読み込む。 2. ヘッドをバッファ先頭にセットし次を繰り返す。 a. id, num, relの各オートマトンで受理するか否かを 順にテストする。(ヘッド位置を左端として開始。左 端が終端なら1へ) ① どれか受理したら(結果を格納してから)左端をヘッド位 置まで進めて次の字句へ()。(受理しなかったときにヘ ッドを左端まで戻すのはオートマトンの責任) ② どれも受理しなかったらエラーメッセージ 課題:inr 標準入力からid, num, relの3種類の字句を すべて認識して、おのおののトークン名(と、 あれば属性値)を印字するプログラムinrを かけ。 inr実装ヒント • トークン型をenum Tokenで定義 • すべてのオートマトン共通の仕事(ヘッドの 制御など)はDFAクラスで。オートマトン固 有の仕事(状態遷移など)はIDDFAなどの 派生クラスで。 • 状態遷移関数そのものでmoveをつくると 遷移ごとに受理判定のコストがかかる。 ⇒ 受理状態か否かは遷移時にわかるは ず。受理したら直ちに制御を返す。 要求駆動 demand driven トークン 要求 字句解析 ルーチン 構文解析 ルーチン トークン, 属性値 • 外部モジュールからのトークン要求は get_token()呼び出しで行う。 • get_token()の戻り値でトークン(および属性値)を 要求元に伝達。 課題:ddinr(要求駆動版inr) 前課題を要求駆動型字句解析器で実現せよ。 “inrlexer.cpp” get_token() は標準入力の次のトークン,属性値 からなる構造体を返す。 “ddinr.cpp” get_token()をつかってinrとおなじことをやる。 ddinr実装ヒント • get_token()呼び出し時に – バッファが処理済なら再読み込み – 左端の更新 を行ってから受理テスト kittyの字句仕様 • • • • • • • • 識別子: 英字で始まる英数字 数値: 数字列 関係演算子: =、 <>、 <=、 >=、 <、 > 代入演算子: := 算術演算子: +、-、*、/ (別々のトークン) 論理演算子: &、|、 ! 、=> (別々のトークン) 括弧など: (、 )、 {、 }、 ;、 , (別々) 予約語: if else while true false return bool int print (別々) kitty字句解析器トークン一覧 id 識別子 num 数値 rel 関係演算子 =, .... assign 代入演算子 := plus + minus - prod * div / and & or | not ! impl => lparen rparen lbrace rbrace semi comma ( ) { } ; , if else while true false return bool int print 予約語とその他の記号 • while 英数以外(‘\0’ 含む) * 開始 w h i l e 英数 reject 与えられた予約語文字列を検出するDFAの acceptを模倣する関数 bool accept_by_as(Token t, string* str) を作る。(IDの検出より優先。) 英字以外の記号からなる字句の場合も含めて考えよ 課題:字句解析器 lexer • kittyの字句解析器lexerを実装せよ。 • lexerのget_tokenを利用するプログラム(ク ライアント)を適宜作成してテストせよ。 注意:コンパイラでは(次章で学ぶ)構文解析 器がクライアントになる
© Copyright 2024 ExpyDoc