Las aplicaciones de Blockly suelen generar JavaScript como su lenguaje de salida, generalmente para ejecutarse en una página web (posiblemente la misma o una WebView incorporada). Al igual que con cualquier generador, el primer paso es incluir el generador de JavaScript.
import {javascriptGenerator} from 'blockly/javascript';
Para generar JavaScript desde el lugar de trabajo, llama a lo siguiente:
javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);
El código resultante se puede ejecutar directamente en la página web de destino:
try {
eval(code);
} catch (e) {
alert(e);
}
Básicamente, el fragmento anterior solo genera el código y lo evalúa. Sin embargo, hay algunos aspectos que se pueden mejorar. Una mejora es que la evaluación se une en una try
/catch
para que se puedan ver los errores del entorno de ejecución, en lugar de fallar en silencio. Otro perfeccionamiento es que code
se agrega a la lista de palabras reservadas para que, si el código del usuario contiene una variable de ese nombre, se le cambie el nombre automáticamente en lugar de que se produzca una colisión. Todas las variables locales deben reservarse de esta manera.
Bloques de destaque
Destacar el bloque que se está ejecutando mientras se ejecuta el código ayuda a los usuarios a
comprender el comportamiento de su programa. Para destacar a nivel de cada sentencia, configura STATEMENT_PREFIX
antes de generar el código JavaScript:
javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');
Define highlightBlock
para marcar el bloque en el lugar de trabajo.
function highlightBlock(id) {
workspace.highlightBlock(id);
}
Esto da como resultado que la sentencia highlightBlock('123');
se agregue antes de cada sentencia, donde 123
es el número de serie del bloque que se destacará.
Bucles infinitos
Aunque se garantiza que el código resultante sea sintácticamente correcto en todo momento, puede contener bucles infinitos. Dado que resolver el
problema de detención está fuera
del alcance de Blockly (!), el mejor enfoque para abordar estos casos es
mantener un contador y disminuirlo cada vez que se realiza una iteración.
Para lograrlo, simplemente establece javascriptGenerator.INFINITE_LOOP_TRAP
en un fragmento de código que se insertará en cada bucle y cada función. Este es un
ejemplo:
window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);
Ejemplo
Esta es una demostración en vivo de cómo generar y ejecutar JavaScript.
JS-Interpreter
Si quieres ejecutar los bloques del usuario correctamente, el proyecto JS-Interpreter es la mejor opción. Este proyecto es independiente de Blockly, pero se escribió específicamente para Blockly.
- Ejecuta código a cualquier velocidad.
- Pausar, reanudar o ejecutar paso a paso la ejecución
- Destaca los bloques a medida que se ejecutan.
- Está completamente aislado del JavaScript del navegador.
Ejecuta el intérprete
Primero, descarga JS-Interpreter desde GitHub:
Luego, agrégalo a tu página:
<script src="acorn_interpreter.js"></script>
El método más sencillo para llamarlo es generar el código JavaScript, crear el intérprete y ejecutar el código:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Paso a paso del intérprete
Para ejecutar el código más lento o de una manera más controlada, reemplaza la llamada a run
por un bucle que realice pasos (en este caso, un paso cada 10 ms):
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
Ten en cuenta que cada paso no es una línea ni un bloque, es una unidad semántica en JavaScript, que puede ser muy detallada.
Agrega una API
El JS-Interpreter es un entorno de pruebas que está completamente aislado del navegador. Cualquier bloque que realice acciones con el mundo exterior requiere que se agregue una API al intérprete. Para obtener una descripción completa, consulta la documentación de JS-Interpreter. Pero, para empezar, esta es la API necesaria para admitir los bloques de alerta y mensaje:
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));
}
Luego, modifica la inicialización del intérprete para pasar la función initApi:
var myInterpreter = new Interpreter(code, initApi);
Los bloques de alerta y de instrucción son los únicos dos bloques del conjunto predeterminado de bloques que requieren una API personalizada para el intérprete.
Conectando highlightBlock()
Cuando se ejecuta en JS-Interpreter, highlightBlock()
se debe ejecutar de inmediato, fuera de la zona de pruebas, a medida que el usuario avanza en el programa. Para ello, crea una función de wrapper highlightBlock()
para capturar el argumento de la función y regístrala como una función nativa.
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));
}
Es posible que las aplicaciones más sofisticadas deseen ejecutar pasos de forma repetida sin pausa hasta que se alcance un comando de resaltado y, luego, hacer una pausa. Esta estrategia simula la ejecución línea por línea. En el siguiente ejemplo, se usa este enfoque.
Ejemplo de JS-Interpreter
Esta es una demostración en vivo de cómo interpretar JavaScript paso a paso. Además, esta demostración incluye un bloque de espera, un buen ejemplo para usar en otros comportamientos asíncronos (p.ej., voz o audio, entrada del usuario).