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

# はじめに

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

※この記事のアップデート版はこちら ... [Node.js でつくる WASM コンパイラー - 04：比較演算子を実装する](https://qiita.com/massie_g/items/90c4055c661c42a5d1a3)

- 前回の記事 ... [03：ローカル変数を実装する](https://qiita.com/massie_g/items/4aea9e95579a30b16bc2)

# 今回実現したいこと

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

- === ... 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 ] ]
]
```

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

```
(call $putn
  (i32.ne
    (i32.const 1)
    (i32.const 2)
  )
)
```

# 比較演算子の生成

早速トランスパイラーの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+1, lctx);
  const rightBlock = generate(tree[2], indent+1, lctx);

  let block = TABs(indent) + '(i32.' + operator + LF();
  block = block + leftBlock + LF();
  block = block + rightBlock + LF();
  block = block + TABs(indent) + ')';
  return block;
}
```

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

# 比較演算子の実行

## 対象ソース

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

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

0;
```

## WASMの生成

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

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

生成結果はこちら。

```:generated.wast
(module
  (func $putn (import "imports" "imported_putn") (param i32))
  (export "exported_main" (func $main))
  (func $main (result i32)

    (call $putn
      (i32.ne
        (i32.const 1)
        (i32.const 2)
      )
    )

    (i32.const 0)

  )
)
```

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

```
$ wasm-as generated.wast
$ 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/items/fd18df7ddc732f6fab2f)予定です。

# ここまでのソース

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

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