005 - Blocklyを使ったオリジナルVisual Programming Editorを作ってみる : C言語を出力するには


## 今回の目標
前回、JavaScriptのソースコードを出力するところまではできました。今回はJavaScriptでもPythonでもDartでもない、C言語を出力してみたいと思います。

## CのGeneratorを作成する
いままでの作業でだいたいこの辺りを作成すれば動きそうな気配がしています。

 * /blockly/generators に出力したい言語に対応したgeneratorを実装
 * /blockly/generators/c/ に各ブロックに対応したgeneratorを実装

恐らくこれでいけるはず。まずは雑に必要なファイルを用意してみます。

1. "/blockly/generators/c/"というディレクトリを作り、その中に"/blockly/generators/javascript/" 内のファイルをコピーします。

2. "/blockly/generators/javascript.js" のコピーを作り、c.jsという名前にリネームします。

まずは１でコピーしたファイルを開き、"JavaScript"という文字列を"C"にリプレースします。例えば/blockly/generators/c/colour.jsであれば

```js
'use strict';

goog.provide('Blockly.C.colour');

goog.require('Blockly.C');


Blockly.C['colour_picker'] = function(block) {
  // Colour picker.
  var code = '\'' + block.getFieldValue('COLOUR') + '\'';
  return [code, Blockly.C.ORDER_ATOMIC];
};

// 以下略
```

といった具合です。また、"/blockly/generators/c.js"においても同様の事を行います。
本当はこの生成処理をC言語の文法に合わせて書き直す必要がありますが、今はそのままにして先へ進みます。この自作ジェネレーターが実行されているか確かめるために最後の戻り値をいじってみます。（こんなことしなくてもブレーク貼って確かめればいいんですがね…）

```js
// 自分がいじった時は199行目でした
Blockly.C.scrub_ = function(block, code) {
  var commentCode = '';
  // Only collect comments for blocks that aren't inline.
  if (!block.outputConnection || !block.outputConnection.targetConnection) {
    // Collect comment for this block.
    var comment = block.getCommentText();
    if (comment) {
      commentCode += Blockly.C.prefixLines(comment, '// ') + '\n';
    }
    // Collect comments for all value arguments.
    // Don't collect comments for nested statements.
    for (var x = 0; x < block.inputList.length; x++) {
      if (block.inputList[x].type == Blockly.INPUT_VALUE) {
        var childBlock = block.inputList[x].connection.targetBlock();
        if (childBlock) {
          var comment = Blockly.C.allNestedComments(childBlock);
          if (comment) {
            commentCode += Blockly.C.prefixLines(comment, '# ');
          }
        }
      }
    }
  }
  var nextBlock = block.nextConnection && block.nextConnection.targetBlock();
  var nextCode = Blockly.C.blockToCode(nextBlock);
  // ここに適当なコメントを入れてみる
  return '// Cジェネレーターから出力\n' + commentCode + code + nextCode;
};
```

ここまで来たらビルド…なんですが、このままだとビルドターゲットに今回追加されたファイルが含まれていません。なので、"/blockly/build.py"を編集します。155行目あたりにrunという関数があるので、そこに"c"を追加します。ここに登録されているファイルとディレクトリがビルド対象となるようです。

```python
def run(self):
    self.gen_core()
    self.gen_blocks()
    self.gen_generator('javascript')
    self.gen_generator('python')
    self.gen_generator('dart')
    self.gen_generator('c')
```

さあ、ここまで来たらビルドしてみます。

```shell
python /blockly/build.py
```

正常に終了すれば"/blockly/c_compressed.js"というファイルができているはずです。これをindex.htmlで読み込んでCのソースコード（今はJavaScriptが出力されますが…）を出力してみましよう。

```html
<script src="blockly/c_compressed.js"></script>

中略

<script>
    function ShowCodeRealTime(e)
    {
      //var code = Blockly.JavaScript.workspaceToCode(workspace);
      var code = Blockly.C.workspaceToCode(workspace);
      document.getElementById('outputArea').value = code;
    }
</script>
```

こんな感じです。今回は出力されたコードを'outputArea'というidを割り当てたTextAreaに表示させるようにしてみました。では、index.htmlをブラウザ開いてみましよう。これでTextAreaにCのジェネレーターから出力されたコードが表示されます。"// Cジェネレーターから出力"というコメントが出力されていれば成功です。

## まとめ
ひとまずこれでジェネレータ開発の環境が整いました。あとは"/blockly/generators/c.js"を編集し、予約語の登録、演算子の優先順位をC言語向けに修正。"/blockly/generators/c/*.js"もC言語を出力するように修正することで"Blockly for C"が出来上がると思います。みなさんも標準で実装されていない言語を出力できるBlocklyを作ってみてください。現在、C言語用は社内向けに機能を絞って作成しております。