0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-11-16

はじめに

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

※以前の記事「Node.js でつくる WASM トランスパイラー - 05:条件分岐とループを実装する」のアップデート版になります。

これまでの取り組み

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

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

ifの例

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

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

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

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

if 〜 else の例

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

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()関数で生成しています。

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 の実行

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

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の例

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()関数で生成しています。

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のサンプルも用意しました。

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にチャレンジする予定です。

ここまでのソース

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?