Upgrade to Pro — share decks privately, control downloads, hide ads and more …

雰囲気でコンパイラを書いたら大変だった話

A622327832207d7a36b23b07f16c9c85?s=47 MiZUP
October 20, 2017

 雰囲気でコンパイラを書いたら大変だった話

2017年10月20日に発表した社内勉強会の資料です

A622327832207d7a36b23b07f16c9c85?s=128

MiZUP

October 20, 2017
Tweet

Transcript

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

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

    コード
  3. モチベーション - コンパイラ書いたことがない - コンピュータサイエンスをあまり勉強したことがない - プログラマとして、やっておくべきか… - 枯れた技術の習得 -

    この辺の記事を読んで影響を受けている - [二週間で簡単なインタープリタ言語を実装してみた (日記) - プログラムモグモ グ](http://itchyny.hatenablog.com/entry/2017/01/23/100000) - [Cコンパイラをスクラッチから開発してみた(日記) - Qiita](https://qiita.com/ruiu/items/4d471216b71ab48d8b74)
  4. コンパイラの動き

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

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

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

    目的プロ グラム 中間語から Ruby コードを呼び出して直接実行
  8. 成果物 mizukmb/my-compiler mizukmb/train-make-ruby-by-ruby - 読み込み〜字句解析〜構文解析〜中間語生成 まで - ↑中間語から Ruby コードを発行し、実行する

    - 書籍 『RubyでつくるRuby ゼロから学びなおすプログラミ ング言語入門』 の写経
  9. 各処理の詳細

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

    変数 = … 演算子 1 … 数値リテラル + … 演算子 10 … 数値リテラル
  11. 字句解析 - How? 決められた規則を定義する → 決定性有限オートマトンで表現 q2 q0 q1 ‘

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

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

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

    ], [ ‘lit’, 2 ] ] ‘+’ 2 1
  15. 構文解析 - How? 解析手法はいっぱいある - 再帰的下向き構文解析 - 上向き構文解析 - LR

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

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

    [ [ ‘lit’, 5 ], [ ‘op_multi, ‘+’ ], [ ‘lit’, 10 ], [ ‘op_multi’, ‘*’ ], ... ]
  18. 再帰的下向き構文解析 - 左再帰性 文法を表現する際、即座に自分自身を呼び出して、無限再帰に陥る問題 ``` E -> E + T

    E() { E(); // <- 無限再帰が発生 ‘+’; T(); } ```
  19. 再帰的下向き構文解析 - 左再帰性 - 解決策 自分自身を後で読み込むように書き換えれば良い ``` E -> TE’

    E’ -> +TE’ | ε T -> i (任意の数列) ```
  20. 再帰的下向き構文解析 - バックトラック `A -> α | β` のような「または」という規則があるとき、どちらで解析すればよいか 判断できず、解析に失敗して後戻りが発生してしまうこと。

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

    文法の変換
  22. LL (1) 文法 1個先のトークンを先読みして文法が一意に定まるようになる - First 集合 … 先頭に現れる終端記号の集合 -

    Follow 集合 … ある非終端記号の直後に現れる終端記号の集合 - Director 集合 … ある非終端記号を特定の終端記号に展開してよいか判 断できる終端記号の集合 - First 集合と Follow 集合から求まる LL (1) かは全ての Director 集合の積集合が空集合であれば良い
  23. 中間語〜コード実行 構文解析済みの中間語を上から順番に実行するだけ 詳しくは書籍『RubyでつくるRuby ゼロから学びなおすプログラミング言語入門 』

  24. 製作日記

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

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

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

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

  29. 10月14日 四則演算ができるように、 + - * / % 演算子を字句解析できるようにした ``` ”1

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

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

    [["lit", 1], ["operator_plus", "+"], ["lit", 2]] - 1+2 # => [["lit", 1], ["operator_plus", "+"], ["lit", 2]] ``` mizukmb/my-compiler
  32. 10月17日 比較演算子 < <= == >= > != の実装 四則演算のときと実装はあまり変わらず。順序だけ気にしていればおk

    発表までに FizzBuzz できそう…みたいな叶わぬ夢を見だす mizukmb/my-compiler
  33. 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
  34. 10月18日 - 続き 複文の実装を泥臭くやりすぎて、新しい文の追加や、リファクタリングがやりづ らくなった LL (1) ではない気がする… mizukmb/my-compiler

  35. 10月18日 - 続き if 文を実装する。が、前述の通り、複文が動かなくなったり、トークンの読み込 みがおかしくなってすごい悩んだ ``` if (10 >

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

  37. 最終成果

  38. 最終成果 実装した - 数値、文字列リテラル - 四則演算、比較演算子 - if 文 -

    複数式の評価
  39. 実演

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

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

  42. None