块应用程序通常生成JavaScript作为其输出语言,通常在Web页面(可能相同或嵌入式WebView)中运行。像任何生成器,第一步是包括javascript生成器。
对于web Blockly,包括javascript_compressed.js
,紧接 blockly_compressed.js
:
<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>
要从工作区生成JavaScript,请调用:
Blockly.JavaScript.addReservedWords('code');
var code = Blockly.JavaScript.workspaceToCode(workspace);
Blockly for Android生成JavaScript默认情况下,不应该需要配置。有关 详细信息,请参阅 使用自定义JavaScript生成器。
生成的代码可以在目标网页中执行:
try {
eval(code);
} catch (e) {
alert(e);
}
基本上,上面的代码片段只是生成代码和evals它。然而,有几个细化。一个细化是eval被包装在try
/中catch
,以便任何运行时错误是可见的,而不是安静地失败。另一个细化是code
添加到保留字的列表,以便如果用户的代码包含该名称的变量,它将被自动重命名,而不是冲突。任何局部变量都应该以这种方式保留。
突出显示块(仅限Web)
在同一页面中运行代码的Web应用程序通常包括在代码运行时突出显示当前执行的块。这可以STATEMENT_PREFIX
在生成JavaScript代码之前通过设置在语句一语句级别上完成:
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
Blockly.JavaScript.addReservedWords('highlightBlock');
定义highlightBlock
标记工作区上的块。
function highlightBlock(id) {
workspace.highlightBlock(id);
}
这将导致在highlightBlock('123');
每个语句之前添加语句,其中123
是要突出显示的块的序列号。
无限循环
虽然结果代码在任何时候都保证在语法上是正确的,但它可能包含无限循环。由于求解 Halting 问题超出了Blockly的范围(!),处理这些情况的最佳方法是保持计数器,并在每次执行迭代时递减计数器。要实现这一点,只需设置Blockly.JavaScript.INFINITE_LOOP_TRAP
一个代码片段,将插入到每个循环和每个函数。这里是一个例子:
window.LoopTrap = 1000;
Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);
例
JS解释器
如果你认真正确地运行用户的块,那么 JS Interpreter项目是要走的路。这个项目与Blockly分开,但是专门为Blockly写的。
- 以任何速度执行代码。
- 暂停/恢复/逐步执行。
- 突出显示块执行时。
- 完全独立于浏览器的JS。
这里是 使用Blockly和JS Interpreter生成和执行JavaScript 的现场演示。
运行解释器
首先,从GitHub下载JS解释器:
然后将其添加到您的网页:
<script src="acorn_interpreter.js"></script>
调用它的最简单的方法是生成JavaScript,创建解释器,并运行代码:
var code = Blockly.JavaScript.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
步骤翻译
为了更慢地执行代码,或者以更受控制的方式,用run
循环(在这种情况下每10ms一步)替换调用:
function nextStep() {
if (myInterpreter.step()) {
window.setTimeout(nextStep, 10);
}
}
nextStep();
注意,每个步骤不是一行或一个块,它是JavaScript中的语义单元,它可能是非常细粒度的。
添加API
JS解释器是一个与浏览器完全隔离的沙盒。任何对外界执行操作的块都需要向解释器添加API。有关完整说明,请参阅 JS-Interpreter文档。但首先,这里是需要支持警报和提示块的API:
function initApi(interpreter, scope) {
// Add an API function for the alert() block.
var wrapper = function(text) {
text = text ? text.toString() : '';
return interpreter.createPrimitive(alert(text));
};
interpreter.setProperty(scope, 'alert',
interpreter.createNativeFunction(wrapper));
// Add an API function for the prompt() block.
wrapper = function(text) {
text = text ? text.toString() : '';
return interpreter.createPrimitive(prompt(text));
};
interpreter.setProperty(scope, 'prompt',
interpreter.createNativeFunction(wrapper));
}
然后修改您的解释器初始化以传入initApi函数:
var myInterpreter = new Interpreter(code, initApi);
警报和提示块是缺省的块集中只有需要为解释器定制API的两个块。
连接 highlightBlock()
当在JS解释器中运行时,highlightBlock()
应立即执行,在沙箱外,作为用户逐步执行程序。为此,创建一个包装函数highlightBlock()
以捕获函数参数,并将其注册为本机函数。
function initApi(interpreter, scope) {
// Add an API function for highlighting blocks.
var wrapper = function(id) {
id = id ? id.toString() : '';
return interpreter.createPrimitive(workspace.highlightBlock(id));
};
interpreter.setProperty(scope, 'highlightBlock',
interpreter.createNativeFunction(wrapper));
}
更复杂的应用程序可能希望重复执行步骤而不暂停,直到达到突出显示命令,然后暂停。此策略模拟逐行执行。下面的示例使用此方法。