Les applications Blockly génèrent souvent du JavaScript comme langage de sortie, généralement pour s'exécuter dans une page Web (éventuellement la même ou une WebView intégrée). Comme pour tout générateur, la première étape consiste à inclure le générateur JavaScript.
import {javascriptGenerator} from 'blockly/javascript';
Pour générer du code JavaScript à partir de l'espace de travail, appelez :
javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);
Le code obtenu peut être exécuté directement sur la page Web de destination :
try {
eval(code);
} catch (e) {
alert(e);
}
En gros, l'extrait de code ci-dessus génère le code et l'évalue. Toutefois, quelques ajustements sont nécessaires. Une amélioration consiste à envelopper l'évaluation dans un try
/catch
afin que les erreurs d'exécution soient visibles au lieu d'échouer silencieusement. Une autre amélioration consiste à ajouter code
à la liste des mots réservés. Ainsi, si le code de l'utilisateur contient une variable portant ce nom, elle sera automatiquement renommée au lieu d'entrer en conflit. Toutes les variables locales doivent être réservées de cette manière.
Blocs de mise en avant
La mise en surbrillance du bloc en cours d'exécution pendant l'exécution du code aide les utilisateurs à comprendre le comportement de leur programme. La mise en surbrillance peut être effectuée au niveau des instructions en définissant STATEMENT_PREFIX
avant de générer le code JavaScript :
javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');
Définissez highlightBlock
pour marquer le bloc dans l'espace de travail.
function highlightBlock(id) {
workspace.highlightBlock(id);
}
L'instruction highlightBlock('123');
est ainsi ajoutée avant chaque instruction, où 123
correspond au numéro de série du bloc à mettre en surbrillance.
Boucles infinies
Bien que le code obtenu soit garanti d'être syntaxiquement correct à tout moment, il peut contenir des boucles infinies. Comme la résolution du problème de l'arrêt dépasse le champ d'application de Blockly (!), la meilleure approche pour gérer ces cas consiste à tenir un compteur et à le décrémenter à chaque itération.
Pour ce faire, il vous suffit de définir javascriptGenerator.INFINITE_LOOP_TRAP
sur un extrait de code qui sera inséré dans chaque boucle et chaque fonction. Voici un exemple :
window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);
Exemple
Voici une démonstration en direct de la génération et de l'exécution de JavaScript.
JS-Interpreter
Si vous souhaitez exécuter correctement les blocs de l'utilisateur, le projet JS-Interpreter est la solution idéale. Ce projet est distinct de Blockly, mais a été spécifiquement écrit pour Blockly.
- Exécutez du code à n'importe quelle vitesse.
- Mettre en pause/reprendre/parcourir l'exécution.
- Mettez en surbrillance les blocs à mesure qu'ils s'exécutent.
- Complètement isolé du JavaScript du navigateur.
Exécuter l'interpréteur
Commencez par télécharger JS-Interpreter depuis GitHub :
Ajoutez-le ensuite à votre page :
<script src="acorn_interpreter.js"></script>
La méthode la plus simple pour l'appeler consiste à générer le code JavaScript, à créer l'interpréteur et à exécuter le code :
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Étape de l'interpréteur
Pour exécuter le code plus lentement ou de manière plus contrôlée, remplacez l'appel à run
par une boucle qui avance (dans ce cas, un pas toutes les 10 ms) :
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
Notez que chaque étape n'est pas une ligne ni un bloc, mais une unité sémantique en JavaScript, qui peut être extrêmement précise.
Ajouter une API
JS-Interpreter est un bac à sable complètement isolé du navigateur. Tous les blocs qui effectuent des actions avec le monde extérieur nécessitent l'ajout d'une API à l'interpréteur. Pour obtenir une description complète, consultez la documentation JS-Interpreter. Pour commencer, voici l'API nécessaire pour prendre en charge les blocs d'alerte et d'invite :
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));
}
Modifiez ensuite l'initialisation de votre interpréteur pour transmettre la fonction initApi :
var myInterpreter = new Interpreter(code, initApi);
Les blocs d'alerte et d'invite sont les deux seuls blocs de l'ensemble de blocs par défaut qui nécessitent une API personnalisée pour l'interpréteur.
Association à highlightBlock()
Lors de l'exécution dans JS-Interpreter, highlightBlock()
doit être exécuté immédiatement, en dehors du bac à sable, à mesure que l'utilisateur parcourt le programme. Pour ce faire, créez une fonction wrapper highlightBlock()
pour capturer l'argument de la fonction et enregistrez-le en tant que fonction native.
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));
}
Les applications plus sophistiquées peuvent souhaiter exécuter des étapes à plusieurs reprises sans pause jusqu'à ce qu'une commande de mise en surbrillance soit atteinte, puis mettre en pause. Cette stratégie simule l'exécution ligne par ligne. L'exemple ci-dessous utilise cette approche.
Exemple JS-Interpreter
Voici une démonstration en direct de l'interprétation de JavaScript étape par étape. Cette démo inclut un bloc d'attente, un bon exemple à utiliser pour d'autres comportements asynchrones (par exemple, la parole ou l'audio, l'entrée utilisateur).