Node.js でつくる WASM コンパイラー - 04：比較演算子を実装する

# はじめに

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

※以前の記事[「Node.js でつくる WASM トランスパイラー - 04：比較演算子を実装する」](https://qiita.com/massie_g/items/7086b84755dc82c56852)のアップデート版になります。

- [Node.jsでつくるNode.js-WASMコンパイラ - もくじ]( https://qiita.com/massie_g/items/c663095759a80c53c126)
- 前回の記事 ... [03：ローカル変数を実装する](https://qiita.com/massie_g/private/e337044c3d9ca2064f60)

# 今回実現したいこと

今回は次の比較演算子のサポートが目標です。それぞれ、WATでは次のように表記します。

- === ... eq
- !== ... ne
- < ... lt_s （符号あり）
- > ... gt_s （符号あり）
- <= ... le_s （符号あり） 
- >= ... ge_s （符号あり）

## 比較の変換例

このミニNode.jsのソースコードを対象とします。

```js
putn(1 !== 2);
```

putn()は、前回用意した簡易デバッグ出力用の組み込み関数です。

これを読み込み、単純化したASTはこちらになります。

```
[ 'func_call', 'putn', 
  [ '!==', [ 'lit', 1 ], [ 'lit', 2 ] ]
]
```

この部分を次のWATに変換することが、今回の目的です。

```
    i32.const 1
    i32.const 2
    i32.ne

    call $putn
```

# 比較演算子の生成

早速コンパイラーのgenerate()関数を拡張します。

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

  // --- compare operator ---
  if (tree[0] === '===') {
    return generateCompareOperator(tree, indent, 'eq', lctx);
  }
  if (tree[0] === '==') {
    return generateCompareOperator(tree, indent, 'eq', lctx);
  }
  if (tree[0] === '!==') {
    return generateCompareOperator(tree, indent, 'ne', lctx);
  }
  if (tree[0] === '!=') {
    return generateCompareOperator(tree, indent, 'ne', lctx);
  }
  if (tree[0] === '>') {
    return generateCompareOperator(tree, indent, 'gt_s', lctx);
  }
  if (tree[0] === '>=') {
    return generateCompareOperator(tree, indent, 'ge_s', lctx);
  }
  if (tree[0] === '<') {
    return generateCompareOperator(tree, indent, 'lt_s', lctx);
  }
  if (tree[0] === '<=') {
    return generateCompareOperator(tree, indent, 'le_s', lctx);
  }

  // ... 省略 ...
}

// --- compare operator ---
function generateCompareOperator(tree, indent, operator, lctx) {
  const leftBlock = generate(tree[1], indent, lctx);
  const rightBlock = generate(tree[2], indent, lctx);
  const op = 'i32.' + operator;

  let block = '';
  block = block + leftBlock + LF();
  block = block + rightBlock + LF();
  block = block + TABs(indent) + op + LF();

  return block;
}
```

実際にコードを生成するのは、generateCompareOperator()関数で行なっています。

# 比較演算子の実行

## 対象ソース

こちらのコードをWASM生成対象とします。

```js:sample/neq.js
putn(1 !== 2);

0;
```

## WASMの生成

今回のステップまでのコンパイラーのソースコードを、mininode_wasm_04.js とします。次のようにgenerated.wat を生成します。

```
$ node mininode_wasm_04.js sample/neq.js
```

生成結果はこちら。

```:generated.wat
(module
  (func $putn (import "imports" "imported_putn") (param i32))
  (export "exported_main" (func $main))
  (func $main (result i32)
    i32.const 1
    i32.const 2
    i32.ne

    call $putn

    i32.const 0
    return
  )
)
```

これを WASM に変換、[前回](https://qiita.com/massie_g/private/e337044c3d9ca2064f60)用意した run_wasm_putn.js を使って実行します。

```
$ wat2wasm generated.wat
$ node run_wasm_putn.js generated.wasm
Loading wasm file: generated.wasm
1
ret code=0
```

「1 !==2」は true なので、1 が標準出力に表示されました。

## 色々な比較の実行

もう一つ、色々な比較を使ったサンプルを変換、実行してみます。

```js:comp.js
let a = 1;
putn(a); // expect 1

let b = (a === 2);
putn(b); // expect 0
putn(a == 1); // expect 1

let c = (a !== 2); 
putn(c); // expect 1
putn(a != 1); // expect 0

putn(999);

a = a * 10;
putn(a > 10) ; // expect 0
putn(a >= 10) ; // expect 1
putn(a < 10) ; // expect 0
putn(a <= 10) ; // expect 1

22;
```

実行結果はこちら。色々な比較演算子も想定通りの出力になりました。

```
Loading wasm file: generated.wasm
1
0
1
1
0
999
0
1
0
1
```

# 次回は

目標の1つであるFizzBuzzに必要な、[条件分岐とループを実装する](https://qiita.com/massie_g/private/efef9e1a909eab8ab8e4)予定です。

# ここまでのソース

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

- GitHubのレポジトリ ... https://github.com/mganeko/mini_node_wasm
- [mininode_wasm_04.js](https://github.com/mganeko/mini_node_wasm/blob/master/mininode_wasm_04.js) ... 今回のWASMコンパイラー
- [sample/neq.js](https://github.com/mganeko/mini_node_wasm/blob/master/sample/neq.js) ... シンプルな比較演算子のサンプル
- [sample/comp.js](https://github.com/mganeko/mini_node_wasm/blob/master/sample/comp.js) ... 色々な比較演算子のサンプル