Blockly アプリは多くの場合、出力言語として JavaScript を生成し、通常はウェブページ内(おそらく同じ WebView または埋め込み WebView)内で実行されます。他のジェネレータと同様に、最初のステップは JavaScript ジェネレータを組み込むことです。
import {javascriptGenerator} from 'blockly/javascript';
ワークスペースから JavaScript を生成するには、以下を呼び出します。
javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);
結果のコードは、移動先のウェブページで直接実行できます。
try {
eval(code);
} catch (e) {
alert(e);
}
基本的には、上記のスニペットは、コードを生成して評価するだけです。ただし、いくつかの改良点があります。改良点の一つとして、評価が try
/catch
でラップされているため、静かに失敗するのではなく、ランタイム エラーが表示されます。もう一つの改良点として、予約単語のリストに code
が追加され、ユーザーのコードにその名前の変数が含まれている場合に名前が競合するのではなく、自動的に名前が変更されます。ローカル変数は、この方法で予約する必要があります。
ブロックのハイライト表示
コードの実行時に現在実行中のブロックをハイライト表示すると、ユーザーはプログラムの動作を理解しやすくなります。JavaScript コードを生成する前に STATEMENT_PREFIX
を設定することで、ステートメントごとにハイライト表示できます。
javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');
highlightBlock
を定義して、ワークスペース上のブロックをマークします。
function highlightBlock(id) {
workspace.highlightBlock(id);
}
これにより、ステートメント highlightBlock('123');
がすべてのステートメントの前に追加されます。ここで、123
はハイライト表示されるブロックのシリアル番号です。
無限ループ
結果のコードは常に構文的に正しいことが保証されますが、無限ループが含まれている可能性があります。停止問題の解決は Blockly の範囲外ですので、このようなケースに対処する最善のアプローチは、カウンタを維持して反復処理のたびにカウンタを減少させることです。そのためには、javascriptGenerator.INFINITE_LOOP_TRAP
に、すべてのループとすべての関数に挿入されるコード スニペットを設定します。以下に例を示します。
window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);
例
JavaScript の生成と実行のライブデモをご覧ください。
JS インタープリタ
ユーザーのブロックを適切に実行したい場合は、JS-Interpreter プロジェクトが最適です。このプロジェクトは Blockly とは別のものですが、Blockly 専用に作成されています。
- 任意の速度でコードを実行します。
- 実行の一時停止/再開/ステップスルー。
- 実行中のブロックをハイライト表示します。
- ブラウザの JavaScript から完全に分離されている。
インタープリタを実行する
まず、GitHub から JS-Interpreter をダウンロードします。
その後、次の手順でページに追加します。
<script src="acorn_interpreter.js"></script>
これを呼び出す最も簡単な方法は、JavaScript を生成し、インタープリタを作成して、コードを実行することです。
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
通訳モードを実行する
コードをより遅く、またはより制御された方法で実行するには、run
の呼び出しをステップするループに置き換えます(この場合は 10 ミリ秒ごとに 1 ステップ)。
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
各ステップは行でもブロックでもなく、JavaScript ではセマンティック単位であり、非常にきめ細かい処理が可能です。
API を追加する
JS-Interpreter は、ブラウザから完全に隔離されたサンドボックスです。外部の世界でアクションを実行するブロックでは、インタープリタに API を追加する必要があります。詳細については、JS-Interpreter のドキュメントをご覧ください。ただし、まずは、アラートとプロンプトのブロックをサポートするために必要となる API です。
function initApi(interpreter, globalObject) {
// Add an API function for the alert() block.
var wrapper = function(text) {
return alert(arguments.length ? text : '');
};
interpreter.setProperty(globalObject, 'alert',
interpreter.createNativeFunction(wrapper));
// Add an API function for the prompt() block.
wrapper = function(text) {
return prompt(text);
};
interpreter.setProperty(globalObject, 'prompt',
interpreter.createNativeFunction(wrapper));
}
次に、initApi 関数を渡すようにインタープリタの初期化を変更します。
var myInterpreter = new Interpreter(code, initApi);
インタープリタにカスタム API を必要とするデフォルトのブロック セットに含まれる 2 つのブロックは、アラート ブロックとプロンプト ブロックのみです。
highlightBlock()
を接続中
JS-Interpreter を実行する場合は、ユーザーがプログラムを実行するときに、highlightBlock()
をサンドボックスの外ですぐに実行する必要があります。そのためには、関数の引数を取得するラッパー関数 highlightBlock()
を作成し、ネイティブ関数として登録します。
function initApi(interpreter, globalObject) {
// Add an API function for highlighting blocks.
var wrapper = function(id) {
return workspace.highlightBlock(id);
};
interpreter.setProperty(globalObject, 'highlightBlock',
interpreter.createNativeFunction(wrapper));
}
より高度なアプリでは、Highlight コマンドに到達するまで一時停止なしでステップを繰り返し実行し、その後で一時停止する場合があります。この方法では、行ごとに実行がシミュレートされます。以下の例では、この方法を使用しています。
JS インタープリタの例
JavaScript を理解する詳しい手順については、ライブデモをご覧ください。また、このデモには、他の非同期動作(音声、オーディオ、ユーザー入力など)に使用する適切な例である wait ブロックが含まれています。