Node.js でつくる WASM コンパイラー - 05：条件分岐とループを実装する

# はじめに

Node.jsで小さなプログラミング言語を作ってみるシリーズを、「ミニインタープリター」「ミニコンパイラー」とやってきました。そして三部作(?)の最後として、 ミニNode.jsからWASMを生成する小さなコンパイラーに取り組んでいます。

※以前の記事[「Node.js でつくる WASM トランスパイラー - 05：条件分岐とループを実装する」](https://qiita.com/massie_g/items/fd18df7ddc732f6fab2f)のアップデート版になります。

- [Node.jsでつくるNode.js-WASMコンパイラ - もくじ]( https://qiita.com/massie_g/items/c663095759a80c53c126)
- 前回の記事 ... [04：比較演算子を実装する](https://qiita.com/massie_g/private/90c4055c661c42a5d1a3)

## これまでの取り組み

- [Node.jsでつくるNode.js（ミニインタープリター） - もくじ](https://qiita.com/massie_g/items/3ee11c105b4458686bc1)
- [Node.jsでつくるNode.jsミニコンパイラ - もくじ](https://qiita.com/massie_g/items/3ba1ba5d55499ee84b0b)

# 今回実現したいこと(1) if

今回の目標の1つ目は条件分岐（if 〜 else）のサポートです。

## ifの例

対象となるミニNode.jsのコードはこちら。

```js:if1.js
if (1) {
  putn(111);
}
```

生成したいWATはこちらです。

```
    i32.const 1
    if
      i32.const 111
      call $putn
    end
```

## if 〜 else の例

もう1つ、elseがある場合はこちら。

```js:if0.js
if (0) {
  putn(111);
}
else {
  putn(0);
}
```

同様に、このようにWATを生成していきます。

```
    i32.const 0
    if
      i32.const 111
      call $putn
    else
      i32.const 0
      call $putn
    end
```

## if 〜 else の生成

いつものようにコンパイラーのgenerate()関数を拡張します。実際にif〜elseに対応する部分は、genereateIf()関数で生成しています。

```js
function generate(tree, indent, lctx) {
  // ... 省略 ...

  // --- if ---
  if (tree[0] === 'if') {
    const block = genereateIf(tree, indent, lctx);
    return block;
  }

  // ... 省略 ...
}

// --- if ---
// tree = ['if', condition, then, else]
function genereateIf(tree, indent, lctx) {
  const conditionBlock = generate(tree[1], indent, lctx);
  const positiveBlock = generate(tree[2], indent + 1, lctx);

  // -- condition --
  let block = conditionBlock + LF();

  // -- if-then --
  block = block + TABs(indent) + 'if' + LF();
  block = block + positiveBlock;

  // -- else ---
  if (tree[3]) {
    const negativeBlock = generate(tree[3], indent + 1, lctx);
    block = block + TABs(indent) + 'else' + LF();
    block = block + negativeBlock;
  }

  // -- end --
  block = block + TABs(indent) + 'end' + LF();

  return block;
}
```

## if 〜 else の実行

複数の条件分岐を使ったサンプルを用意します。

```js:if.js
let a = 1;
if (a === 1) {
  putn(111);
}

if (a !== 1) {
  putn(222);
}
else {
  putn(333);
}


putn(999);

33;
```

これをWASMに変換、実行した結果がこちらです。

```
$ node mininode_wasm_05.js sample/if.js
$ wat2wasm generated.wat
$ node run_wasm_putn.js generated.wasm
Loading wasm file: generated.wasm
111
333
999
ret code=33
```

想定通りの出力になりました。



# 今回実現したいこと(2) while ループ

今回の目標の2つ目は while ループのサポートです。

## whileの例

```js
let a = 0;
while (a < 10) {
  putn(a);
  a = a + 1;
}
```

生成したいWATはこちら。

```
    (local $a i32)
    i32.const 0
    set_local $a

    loop ;; --begin of while loop--
      get_local $a
      i32.const 10
      i32.lt_s
      if
        get_local $a
        call $putn

        get_local $a
        i32.const 1
        i32.add
        set_local $a

        br 1 ;; --jump to head of while loop--
      end ;; end of if-then
    end ;; --end of while loop--
```

色々な実現の仕方があると思いますが、今回は while ループの中身を if で記述し、br 命令でループの先頭に戻るようにしました。

## while の生成

再度generate()関数を拡張します。実際にwhileに対応する部分は、genereateWhile()関数で生成しています。

```js
function generate(tree, indent, lctx) {
  // ... 省略 ...

  // --- while ---
  if (tree[0] === 'while') {
    const block = genereateWhile(tree, indent, lctx);
    return block;
  }

  // ... 省略 ...
}

// --- while ---
// tree = ['while', condition, then, else]
function genereateWhile(tree, indent, lctx) {
  const conditionBlock = generate(tree[1], indent + 1, lctx);
  const innerBlock = generate(tree[2], indent + 2, lctx);

  let block = TABs(indent) + 'loop ;; --begin of while loop--' + LF();
  block = block + conditionBlock;
  block = block + TABs(indent + 1) + 'if' + LF();
  block = block + innerBlock;
  block = block + TABs(indent + 2) + 'br 1 ;; --jump to head of while loop--' + LF();
  block = block + TABs(indent + 1) + 'end ;; end of if-then' + LF();
  block = block + TABs(indent) + 'end ;; --end of while loop--';

  return block;
}
```

## while の実行

whileのサンプルも用意しました。

```js:while.js
let a = 0;
while (a < 10) {
  putn(a);
  a = a + 1;
}

0;
```

これをWASMに変換、実行した結果がこちらです。

```
$ node mininode_wasm_05.js sample/while.js
$ wat2wasm generated.wat
$ node run_wasm_putn.js generated.wasm
Loading wasm file: generated.wasm
0
1
2
3
4
5
6
7
8
9
ret code=0
```

無事ループが実行されました。


# 次回は

いよいよ目標の1つである[FizzBuzzにチャレンジ](https://qiita.com/massie_g/private/2bbd6a914f1abdac195a)する予定です。

# ここまでのソース

GitHubにソースを上げておきます。

- GitHubのレポジトリ ... https://github.com/mganeko/mini_node_wasm
- [mininode_wasm_05.js](https://github.com/mganeko/mini_node_wasm/blob/master/mininode_wasm_05.js) ... 今回のWASMコンパイラー
- [sample/if.js](https://github.com/mganeko/mini_node_wasm/blob/master/sample/if.js) ... if〜elseのサンプル
- [sample/while.js](https://github.com/mganeko/mini_node_wasm/blob/master/sample/while.js) ... whileのサンプル