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

# はじめに

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

※この記事のアップデート版はこちら ... [Node.js でつくる WASM コンパイラー - 05：条件分岐とループを実装する](https://qiita.com/massie_g/items/efef9e1a909eab8ab8e4)

- 前回の記事 ... [04：比較演算子を実装する](https://qiita.com/massie_g/items/7086b84755dc82c56852)

## これまでの取り組み

- [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);
}
```

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

```
(if
  (i32.const 1)
  (then
    (call $putn
      (i32.const 111)
    )
  )
)
```

## if 〜 else の例

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

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

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

```
(if
 (i32.const 0)
  (then
    (call $putn
      (i32.const 111)
    )
  )
  (else
    (call $putn
      (i32.const 0)
    )
  )
)
```

## 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 + 1, lctx);

  const positiveBlock = generate(tree[2], indent + 2, lctx);

  let block = TABs(indent) + '(if' + LF();
  block = block + conditionBlock + LF();

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

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

  block = block + TABs(indent) + ')';
  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
$ wasm-as generated.wast
$ 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;
}
```

生成したいWASTはこちら。

```
(local $a i32)

(set_local $a
  (i32.const 0)
)

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

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

      (br 1) ;; --jump to head of while loop--
    ) ;; end of then
  ) ;; end of if
) ;; --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 + 2, lctx);

  const innerBlock = generate(tree[2], indent + 3, lctx);

  let block = TABs(indent) + '(loop ;; --begin of while loop--' + LF();
  block = block + TABs(indent + 1) + '(if' + LF();
  block = block + conditionBlock + LF();
  block = block + TABs(indent + 2) + '(then' + LF();
  block = block + innerBlock;
  block = block + TABs(indent + 3) + '(br 1) ;; --jump to head of while loop--' + LF();
  block = block + TABs(indent + 2) + ') ;; end of then' + LF();
  block = block + TABs(indent + 1) + ') ;; end of if' + LF();
  block = block + TABs(indent) + ') ;; --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
$ wasm-as generated.wast
$ 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/items/2bbd6a914f1abdac195a)する予定です。

# ここまでのソース

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

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