Node.js でつくる Node.js ミニコンパイラ - 01 : 仕様の検討と、LLVMの準備

# はじめに

「RubyでつくるRuby ゼロから学びなおすプログラミング言語入門」([ラムダノート](https://www.lambdanote.com/products/ruby-ruby), [Amazon](https://www.amazon.co.jp/dp/4908686017)) という本を手を読んで非常に感銘を受けました。そこでも自分でも[Node.jsでミニNode.jsインタープリタを作って](https://qiita.com/massie_g/items/3ee11c105b4458686bc1)みました。なんとか達成して大きな自己満足を味わうことができました。

実はそのチャレンジにはもう一つのきっかけがあります。それは [Turing Complete FM](https://turingcomplete.fm) のポッドキャストを聴き始めたのことです。
このポッドキャストを聴きなら、そういえば随分昔に「自分で小さなコンパイラを作ること」に憧れていたことを思い出しました。小さなインタープリタを作れた今なら当時挫折したことができそうな気がして、今度はコンパイラ作りを目指します。

* [コンパイラ編 もくじ](https://qiita.com/massie_g/items/3ba1ba5d55499ee84b0b)

# 目指すもの

ミニインタープリタを作って分かったことは、適切に小さくした目標が重要だということです。今回目指すミニNode.jsコンパイラも、思い切って割り切った言語仕様にすることにしました。

* FizzBuzzと、フィボナッチ数列の計算ができるようする
* 仕様はミニNode.js （つまり、ミニRuby）を踏襲する
    * ただし、配列やハッシュは除外する
* 型は32ビット符号付き整数のみサポート
* ローカル変数が使える。グーローバル変数は無し
* if - else, while が使える
* ユーザ定義関数が使える（再帰呼び出しもできるようにしたい）
* 組み込み関数として、整数の出力と固定文字列の出力ができるようにする

コンパイラなので型を導入しますが、1種類に割り切るというJavaScriptらしからぬ仕様にしました。これで型宣言も型推論も不要です（ドヤ顔）。

またコンパイラなので、最終的にはNode.js無しで実行できるバイナリを生成しなければなりません。が、そこは便利なツールを利用させてもらいます。

* 単独で実行できるバイナリを生成することを目指す
    * バイナリ生成は、Node.jsではなくて別のツールを利用する
    * ミニコンパイラでは、中間コードを生成する
* 自分自身をコンパイルすることは諦める（セフルホストはしない）
    * ミニコンパイラ自体は、ミニNode.jsインタープリタで実行できることを目指す

## 前提環境

今回は私の環境である Mac OS X 10.12 Sierra を前提に、取り組みを進めます。

# コード生成に使うツール

最終的にバイナリを生成するところは、他のツールに任せることにしました。これでかなりコンパイラを作るハードルが下がります。ツールの候補としては、次のものを考えました。

* Cコンパイラ (gcc or Clang)
* Goコンパイラ
* Rustコンパイラ
* LLVM

最初の3つの場合は、Node.jsから各言語のソースコードに変換することになります。コンパイラを作ったというと、ちょっと違う感じがします。
そこで以前から気になっていたLLVMを使うことにしました。つまり、 Node.jsでLLVMの中間表現(Internal Representation)を生成することになります。


# LLVMの準備

## LLVMとは

[LLVM](http://llvm.org)によると

> LLVMプロジェクトは、モジュール化された再利用可能なコンパイラおよびツールチェーン技術の集まりです

とあります。もともとは Low Level Virtual Machine の略語として名付けられたらしいのですが、いまはLLVMが正式な名称とのことです。
最近の言語系ではよく利用さているらしく、例えばClang, Swift, Rustが代表例です。ASM.jsやWebAssemblyを生成するEmscriptenもLLVMを利用しているそうです。
ちなみにGoもLLVMを使っているのかと思っていたのですが、実際は違いました。コンパイラもライブラリも自前で作るのがGoの流儀なようです。

## LLVMのインストール

LLVM は Clang と一緒に提供されているようです。([Download page](http://releases.llvm.org/download.html)) その中で、今回は次のツールを使います。

- lli ... LLVM IR（中間表現）やBitcode（バイトコード）を実行するツール
- llc ... 実行モジュールのバイナリを生成するツール

Mac OS X の場合、clangは /usr/bin/clang にインストールされていますが、このツール群は含まれていません。また Xcode をインストールした場合にも clang がインストールされますが、lli/llcといったツールは無いようです。

Clang/LLVMのインストールには次の方法があります。

- (1) ソースコードからビルドする
- (2) homebrewを使ってインストールする（Mac OS Xの場合）
- (3) ビルド済みのバイナリ(Pre-build)をダウンロードする

今回はなるべく簡単にすませたくて、3番目のビルド済みバイナリをダウンロードする方法をとりました。Mac OS Xで作業しているため、[こちらのページ](http://releases.llvm.org/download.html)から Clang for Mac をダウンロードします。2018年7月現在、[v6.0.0のバイナリ](http://releases.llvm.org/6.0.0/clang+llvm-6.0.0-x86_64-apple-darwin.tar.xz)が最新のようです。

圧縮ファイルを解凍すると中に下記のフォルダーが現れます。

- bin
- share
- include
- lib
- libexec

一式をお好きな場所にコピーしてbinにパスを通してください。私の場合はパスを通す代わりに次のようにしました。

-  ~/Application/clang/ 以下に各フォルダーをコピー
-  bashのエイリアスを設定
    -  alias llc='~/Applications/clang/bin/llc'
    -  alias lli='~/Applications/clang/bin/lli'

# 次回は

[つぎはLLVM IRの書き方を調べてみます。](https://qiita.com/massie_g/items/7e2d2d742f1da49b8daf)こちらのページを参考にします。

- [LLVMを始めよう！ 〜 LLVM IRの基礎はclangが教えてくれた・Brainf**kコンパイラを作ってみよう 〜](https://itchyny.hatenablog.com/entry/2017/02/27/100000)
- [LLVM Language Reference Manual](http://llvm.org/docs/LangRef.html)