Slide 1

Slide 1 text

雰囲気でコンパイラを書いたら 大変だった話 @mizukmb 20, October

Slide 2

Slide 2 text

コンパイラとは ある規則に沿って書かれた文字列を機械可読可能なコードに翻訳すること var foo = 100; ~~~~~~ ソースコード コンパイル オブジェクト コード

Slide 3

Slide 3 text

モチベーション - コンパイラ書いたことがない - コンピュータサイエンスをあまり勉強したことがない - プログラマとして、やっておくべきか… - 枯れた技術の習得 - この辺の記事を読んで影響を受けている - [二週間で簡単なインタープリタ言語を実装してみた (日記) - プログラムモグモ グ](http://itchyny.hatenablog.com/entry/2017/01/23/100000) - [Cコンパイラをスクラッチから開発してみた(日記) - Qiita](https://qiita.com/ruiu/items/4d471216b71ab48d8b74)

Slide 4

Slide 4 text

コンパイラの動き

Slide 5

Slide 5 text

コンパイラの動き 原始プロ グラム 読み込み 字句解析 構文解析 中間語作 成 最適化 コード生成 目的プロ グラム

Slide 6

Slide 6 text

今回実装した部分 原始プロ グラム 読み込み 字句解析 構文解析 中間語作 成 最適化 コード生成 目的プロ グラム

Slide 7

Slide 7 text

今回実装した部分 原始プロ グラム 読み込み 字句解析 構文解析 中間語作 成 最適化 コード生成 目的プロ グラム 中間語から Ruby コードを呼び出して直接実行

Slide 8

Slide 8 text

成果物 mizukmb/my-compiler mizukmb/train-make-ruby-by-ruby - 読み込み〜字句解析〜構文解析〜中間語生成 まで - ↑中間語から Ruby コードを発行し、実行する - 書籍 『RubyでつくるRuby ゼロから学びなおすプログラミ ング言語入門』 の写経

Slide 9

Slide 9 text

各処理の詳細

Slide 10

Slide 10 text

字句解析 規則に沿って並んだ文字や数字の列を意味のある単位に分解していく 空白・インデント・改行などはいい感じに削除する(意味を持たないので) `x = 1 + 10` x … 変数 = … 演算子 1 … 数値リテラル + … 演算子 10 … 数値リテラル

Slide 11

Slide 11 text

字句解析 - How? 決められた規則を定義する → 決定性有限オートマトンで表現 q2 q0 q1 ‘ 英数字 ‘

Slide 12

Slide 12 text

字句解析 - How? 決められた規則を定義する → 決定性有限オートマトンで表現 →正規表現で書ける `/\A\w*\z/` q2 q0 q1 ‘ 英数字 ‘

Slide 13

Slide 13 text

字句解析 - How? 種類が定まったらラベルを付けたペアとしてトークンにする ‘foo’ -> (字句解析) -> [‘lit’, ‘foo’]

Slide 14

Slide 14 text

構文解析 字句解析したトークン列を構文木に変換する [ [ ‘lit’, 1 ], [ ‘op_plus, ‘+’ ], [ ‘lit’, 2 ] ] ‘+’ 2 1

Slide 15

Slide 15 text

構文解析 - How? 解析手法はいっぱいある - 再帰的下向き構文解析 - 上向き構文解析 - LR 構文解析

Slide 16

Slide 16 text

構文解析 - How? 解析手法はいっぱいある - 再帰的下向き構文解析 ←今回はこれを採用 - 上向き構文解析 - LR 構文解析

Slide 17

Slide 17 text

再帰的下向き構文解析 - 解析木を上(根)から下向き(葉に向かって)に作り上げていく - 比較的わかりやすいプログラムが書ける - バックトラック、左再帰性といった問題がある + * 5 [ [ ‘lit’, 5 ], [ ‘op_multi, ‘+’ ], [ ‘lit’, 10 ], [ ‘op_multi’, ‘*’ ], ... ]

Slide 18

Slide 18 text

再帰的下向き構文解析 - 左再帰性 文法を表現する際、即座に自分自身を呼び出して、無限再帰に陥る問題 ``` E -> E + T E() { E(); // <- 無限再帰が発生 ‘+’; T(); } ```

Slide 19

Slide 19 text

再帰的下向き構文解析 - 左再帰性 - 解決策 自分自身を後で読み込むように書き換えれば良い ``` E -> TE’ E’ -> +TE’ | ε T -> i (任意の数列) ```

Slide 20

Slide 20 text

再帰的下向き構文解析 - バックトラック `A -> α | β` のような「または」という規則があるとき、どちらで解析すればよいか 判断できず、解析に失敗して後戻りが発生してしまうこと。 →解析効率が悪い

Slide 21

Slide 21 text

再帰的下向き構文解析 - バックトラック - 解決策 解析するトークンのk個先のトークンを先読みして、文法の曖昧さを除去してい く → LL (k) 文法の変換

Slide 22

Slide 22 text

LL (1) 文法 1個先のトークンを先読みして文法が一意に定まるようになる - First 集合 … 先頭に現れる終端記号の集合 - Follow 集合 … ある非終端記号の直後に現れる終端記号の集合 - Director 集合 … ある非終端記号を特定の終端記号に展開してよいか判 断できる終端記号の集合 - First 集合と Follow 集合から求まる LL (1) かは全ての Director 集合の積集合が空集合であれば良い

Slide 23

Slide 23 text

中間語〜コード実行 構文解析済みの中間語を上から順番に実行するだけ 詳しくは書籍『RubyでつくるRuby ゼロから学びなおすプログラミング言語入門 』

Slide 24

Slide 24 text

製作日記

Slide 25

Slide 25 text

製作日記 期間: 9月24日〜10月19日

Slide 26

Slide 26 text

9月24日〜10月10日 様々な誘惑[^1] に負けつつも、書籍『RubyでつくるRuby ゼロから学びなおす プログラミング言語入門』のコードを写経し続ける mizukmb/train-make-ruby-by-ruby [^1]: Splatoon 2, beatmania IIDX

Slide 27

Slide 27 text

10月11日 コンパイラを作り始める とりあえず数値リテラルを字句解析できるようにした mizukmb/my-compiler

Slide 28

Slide 28 text

10月12日 インデントやスペースを使いたかったので大丈夫なように実装 全角スペースも使える。便利ではないと思う mizukmb/my-compiler

Slide 29

Slide 29 text

10月14日 四則演算ができるように、 + - * / % 演算子を字句解析できるようにした ``` ”1 + 2" => [[“lit", 1], ["operator_plus", "+"], ["lit", 2]] ``` mizukmb/my-compiler

Slide 30

Slide 30 text

10月15日 最低限の計算はできるようになったので、数値リテラルと四則演算の構文解析 を実装。四則演算のできる小さなプログラミング言語の完成 ``` ”1 + 2" (字句解析) => [[“lit", 1], ["operator_plus", "+"], ["lit", 2]] (構文解析) => ["+", ["lit", 1], ["lit", 2]] ``` mizukmb/my-compiler

Slide 31

Slide 31 text

10月16日 数値リテラルと演算子の間にスペースが無くても字句解析できるようにバグ修 正 ``` - 1 + 2 # => [["lit", 1], ["operator_plus", "+"], ["lit", 2]] - 1+2 # => [["lit", 1], ["operator_plus", "+"], ["lit", 2]] ``` mizukmb/my-compiler

Slide 32

Slide 32 text

10月17日 比較演算子 < <= == >= > != の実装 四則演算のときと実装はあまり変わらず。順序だけ気にしていればおk 発表までに FizzBuzz できそう…みたいな叶わぬ夢を見だす mizukmb/my-compiler

Slide 33

Slide 33 text

10月18日 複文を実装する。字句解析で改行(\n)もトークンとして扱うようにして複文を解 析できるようにした。 ``` “1 + 2\n3 - 4” (字句解析) => [[“lit", 1], ["operator_plus", "+"], ["lit", 2], ["newline", "\\n"], ["lit", 3], ["operator_minus", "-"], ["lit", 4]] (構文解析) => [“stmts", ["+", ["lit", 1], ["lit", 2]], ["-", ["lit", 3], ["lit", 4]]] ``` mizukmb/my-compiler

Slide 34

Slide 34 text

10月18日 - 続き 複文の実装を泥臭くやりすぎて、新しい文の追加や、リファクタリングがやりづ らくなった LL (1) ではない気がする… mizukmb/my-compiler

Slide 35

Slide 35 text

10月18日 - 続き if 文を実装する。が、前述の通り、複文が動かなくなったり、トークンの読み込 みがおかしくなってすごい悩んだ ``` if (10 > 9 ) { 5 * 100 } ``` mizukmb/my-compiler

Slide 36

Slide 36 text

10月19日 else を実装しようと試みるが、複文の(ry 実装を断念した。 文字列リテラルを忘れていたので実装 mizukmb/my-compiler

Slide 37

Slide 37 text

最終成果

Slide 38

Slide 38 text

最終成果 実装した - 数値、文字列リテラル - 四則演算、比較演算子 - if 文 - 複数式の評価

Slide 39

Slide 39 text

実演

Slide 40

Slide 40 text

感想 0からコンパイラ作るのは難しい。けど、オリジナルのプログラミング言語が 様々なプロセスを経て実行されると嬉しい。 一気に実装するのではなく、小さなパーツから少しづつ組み立てていくインクリ メンタルな開発は良かった。コンパイラが少しずつ言葉を覚えて育っていく様子 が見られるのは楽しい

Slide 41

Slide 41 text

感想(つらい) 理論を十分理解しないまま実装したので途中からコードの設計に無理が出てき た プログラミング言語として実用的ではない。せめて変数は欲しかった

Slide 42

Slide 42 text

No content