More Related Content More from Kenta Hattori (20) 言語処理系入門12. 2009 年 10 月 23 日 言語処理系入門1 2
本講座の目的
コンパイラやインタプリタの構成を学ぶ
処理系の中身を理解することで,より効率の良い
(理にかなった)プログラムが書けるようになる
複雑な処理を行なうソフトウェアの構築手法
様々な言語機構や概念を理解する
既存のプログラミング言語をより深く理解できる
新しいプログラミング言語を習得する際の助けと
なる
言語処理系を書くのは楽しい
自分のプログラミング言語を作るのはもっと楽し
い
3. 2009 年 10 月 23 日 言語処理系入門1 3
進め方
この講義期間中に実際に処理系を作成していく
簡単な電卓プログラムから始めて,プログラミング言語に
必要な様々な機能を少しずつ追加していく
必要な理論や技法はその都度説明する
使用言語: Ocaml
今回の講座を通じて,コンパイラの作り方だけでな
く, Ocaml も学べる!
Ocaml の文法や必要な機能などもその都度説明する
サンプルプログラムを毎週提供する予定
各自コンパイルして動かしてみること
できれば,自分でソースコードを眺めて,改造したりして
欲しい
実習用マシン
nielsen.lb.isp.co.jp を使ってください( ssh でログイン可
能)
4. 2009 年 10 月 23 日 言語処理系入門1 4
Ocaml について
ML ファミリーの一員
Standard ML, Caml, F#, …
コンパイラを書くのに適した言語機能
…パターンマッチ,多相型,高階関数,型推論,
参考になるサイト
本家 http://caml.inria.fr/
OCaml.jp ― マニュアルの日本語訳があるので重宝する
http://ocaml.jp/
Objective Caml 入門
http://www.sato.kuis.kyoto-
u.ac.jp/~igarashi/class/isle4/mltext/ocaml.html
Ocaml プログラミング入門
http://www.i.kyushu-u.ac.jp/~bannai/ocaml-intro/
5. 2009 年 10 月 23 日 言語処理系入門1 5
電卓プログラム( Calc )の概要
以下のように四則演算の式を入力すると,計算結果が表示され
る
電卓プログラムの動作: Read-Eval-Print-Loop ( REPL )とい
う
プロンプト記号( # )を表示し,ユーザの入力を待つ
ユーザが入力した式を読み込む( READ )
複数行にわたって式を入力できるように,セミコロン( ; )で式の
入力が完了したことを伝える.
入力が完了すると,式を評価( EVAL )する
結果を表示( PRINT )し,次の入力を待つために再度プロンプ
トを表示
# 3 * (4 + 2) ;
==> 18
# -4 / 2 + 13 ;
==> 11
6. 2009 年 10 月 23 日 言語処理系入門1 6
Calc に必要な処理( 1 )
「式」を解析する
ユーザから入力された式(単なる文字の並び)を
解析し,式の構造を表現したデータ(構文木)を
作る
字句解析器( lexer )と構文解析器( parser )
‘1’ ‘+’ ‘2’ ‘*’ ‘3’ ‘-’ ‘(’ ‘4’ ‘+’ ‘5’ ‘)’
+
×1
2 3
-
+
4 5演算子の結合規則,優先度
を反映した構文木を作らな
いと正しい結果にならない
7. 2009 年 10 月 23 日 言語処理系入門1 7
Calc に必要な処理( 2 )
「式」を評価する
構文木を根から再帰的に辿っていき,結果を求め
る
評価器( evaluator )
+
×1
2 3
-
+
4 5
int eval(node_t *node) {
switch (node->type) {
case VALUE:
return node->value;
case PLUS:
return eval(node->left)
+ eval(node->right);
case MINUS: …
…
}
8. 2009 年 10 月 23 日 言語処理系入門1 8
字句解析器と構文解析器
字句解析器( Lexer )
文字列を解析し,トークン( Token )の列に分解
する
構文解析器( Parser )
トークン列を解析し,構文木を作成する
ソース
プログラム
字句解析器
(lexer)
構文解析器
(parser)
記号表
(symbol table)
トークン
次のトークン
の要求
識別子など
を登録する
ためのテー
ブル
9. 2009 年 10 月 23 日 言語処理系入門1 9
字句解析器の役割
文字列を最小限の意味を持つ単位( token )に分解
する
コメントや空白など,プログラムの意味に関係のな
い文字列は,ここで読み飛ばしておくと後が楽
‘1’ ‘+’ ‘2’ ‘3’ ‘4’ ‘-’ ‘4’ ‘5’‘3’ ‘ ’ ‘n’ ‘n’ ‘0’
INT(13)
PLUS
INT(234) INT(45)
MINUS
INT(13),PLUS,INT(234),MINUS,INT(45)
‘i’ ‘ ‘ ‘e’ ‘n’ ‘>’ ‘0’ ‘ ’ ‘ ’‘f’ ‘l’ ‘ ’ ‘t’ ‘h’
IF
ID(“len”)
GT THEN
INT(0)
‘e’ ‘n’ ‘ ’ ‘r ’ ‘e ’ ‘t ’ ‘u ’ ‘r ’ ‘n ’ ‘; ’
RETURN
SEMI
IF,ID(“len”),GT,INT(0),THEN,RETURN,SEMI
10. 2009 年 10 月 23 日 言語処理系入門1 10
正規表現によるトークンの規定
正規表現を用いるとトークンの持つパターンを上手
く規定してやることができる
例:
‘整数: -’? (‘0’ | ‘1’ | … | ‘9’)+
C の識別子 : (alpha | ’_’) (alpha | digit | ’_’)*
ただし, digit = ‘0’ | ‘1’ | … | ‘9’ ,
alpha = ‘a’ | ‘b’ | … | ‘z’ | ‘A’ | … | ‘Z’ | とする.
日付 : d d d d ‘/’ d d ‘/’ d d
ただし, d = digit
演習:浮動小数点数のパタンを正規表現で定義せよ
11. 2009 年 10 月 23 日 言語処理系入門1 11
正規表現とオートマトン
正規表現は決定性有限オートマトンに機械的
に変換することが可能
正規表現で記述したパターンを認識(判別)する
プログラムを自動生成することができる
例: (a|b)*abb を受理する決定性オートマト
ン
0 1 2 3
開始
a b
b
a
b
a
a
b
12. 2009 年 10 月 23 日 言語処理系入門1 12
有限オートマトンの数学的モデル
<S,Σ,δ , s0, F> の 5 つ組み
S: 状態の有限集合
Σ :入力記号の有限集合(アルファベット)
δ :遷移関数 <S,Σ>→S
非決定性の場合, <S,Σ>→2S
s0 :開始状態 s0 S∈
F: 終了状態 F S⊆
ちなみに,正規表現, NFA, DFA がパターン
を認識する能力は等価であることが知られて
いる
詳しくはオートマトンの講座を受講してください
13. 2009 年 10 月 23 日 言語処理系入門1 13
正規表現・有限オートマトンの限
界
有限の状態しか持てないので,数を数えたり
,比較したりすることができない
例 1 :開き括弧と閉じ括弧が対応するようなパ
ターン
‘(‘ ’(‘ ‘(‘ ‘)’ ‘(‘ ‘(‘ ‘)’ ‘)’ ‘(‘ ‘)’ ‘)’ ‘)’
例 2 :文字 a の並びの後に同じ数だけ文字 b が続
く
ab, aabb, aaabbb, aaaabbbb, …
このようなパターンを判別させるには,より表現
力の高い文脈自由文法,それに対応するプッシュ
ダウンオートマトンが必要
構文解析器( parser )は文脈自由文法にもとづいてる
14. 2009 年 10 月 23 日 言語処理系入門1 14
字句解析器生成系( lex )
Lex を使うと,字句解析器が簡単に作れる
正規表現でトークンのパターンを記述すると,字句解析の
ルーチンを自動で作ってくれる
Ocaml の場合, ocamllex というツールが添付され
ている
http://ocaml.jp/archive/ocaml-manual-3.06-
ja/manual026.html
ocamllex
字句解析
パターン
の記述
字句解析
ルーチン
lexer.mll lexer.ml
15. 2009 年 10 月 23 日 言語処理系入門1 15
OCamllex 記述例
(* 字句解析規則の定義 *)
rule token = parse
[‘ ’ ‘t’] { token lexbuf } (* 空白を飛ばす *)
| [‘n’] { exit 0 } (* 改行を読み込むと終了 *)
| ['0'-'9']+
{ let n = int_of_string(Lexing.lexeme lexbuf)
in
Printf.sprintf “[INT(%d)]” n }
| '+' { “[PLUS]” } | '-' { “[MINUS]” }
| '*' { “[TIMES]” } | '/' { “[DIV]” }
| '(' { “[LPAREN]” } | ')' { “[RPAREN]” }
16. 2009 年 10 月 23 日 言語処理系入門1 16
演習課題
今週のサンプルプログラムをコンパイルして実行せよ
以下のようなトークンを追加してみる
true, false, if, then, else
浮動小数点
識別子をトークンとして扱うにはどうするか
x や foo ”といった識別子となる文字列を入力すると, ID(x)”,
“ID(foo)” のようなトークンを出力する.
true や false のようなキーワードの場合にはちゃんと TRUE,FALSE
トークンを出力するように注意せよ.
“hoge” のような文字列リテラルをトークンを扱うにはどうすれ
ばよいか?
エスケープ文字はどうする?
/* ~ */ で囲まれた部分をコメントとして読みとばす処理を追加
せよ
17. 2009 年 10 月 23 日 言語処理系入門1 17
次回予定
日時:
2009 年 10 月 30 日(金) 17 : 00 - 18 : 30
毎週この時間帯で良いですか?
場所:
LB2 3F/A
内容:
構文解析, BNF 記法, OCamlyacc