Générer et exécuter JavaScript

Les applications Blockly génèrent souvent JavaScript comme langage de sortie, généralement pour s'exécuter sur une page Web (éventuellement identique ou dans 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 dans la page Web de destination:

try {
  eval(code);
} catch (e) {
  alert(e);
}

L'extrait ci-dessus ne fait que générer le code et l'évaluer. Toutefois, il y a quelques améliorations. Pour affiner cette approche, l'évaluation est encapsulée dans un élément try/catch afin que toutes les erreurs d'exécution soient visibles au lieu d'échouer discrètement. Autre affinement : code est ajouté à la liste des mots réservés de sorte que 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.

Mettre les blocs en surbrillance

Mettre en évidence le bloc en cours d'exécution pendant l'exécution du code aide les utilisateurs à comprendre le comportement de leur programme. Vous pouvez effectuer la mise en surbrillance au niveau de chaque instruction 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 sur l'espace de travail.

function highlightBlock(id) {
  workspace.highlightBlock(id);
}

L'instruction highlightBlock('123'); est alors ajoutée avant chaque instruction, où 123 correspond au numéro de série du bloc à mettre en surbrillance.

Boucles infinies

Bien que la syntaxe du code obtenu soit garantie à tout moment, il peut contenir des boucles infinies. Étant donné que la résolution du problème d'arrêt dépasse le cadre de Blockly (!), la meilleure approche pour gérer ces cas est de gérer un compteur et de le décrémenter à chaque itération. Pour ce faire, définissez simplement 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.

Interpréteur JS

Si vous souhaitez vraiment exécuter correctement les blocs de l'utilisateur, nous vous recommandons d'utiliser le projet d'interpréteur JS. Ce projet est distinct de Blockly, mais il a été spécifiquement écrit pour Blockly.

  • Exécutez du code à n'importe quelle vitesse.
  • Interrompre/reprendre/suivre l'exécution.
  • Mettez en surbrillance les blocs lors de leur exécution.
  • Complètement isolé du JavaScript du navigateur.

Exécuter l'interpréteur

Commencez par télécharger l'interpréteur JS depuis GitHub:

Télécharger le fichier ZIP Télécharger TAR Ball Afficher sur 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 JavaScript, à créer l'interpréteur et à exécuter le code:

var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();

Passer à 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 effectue les étapes suivantes (dans ce cas, une étape 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 dans JavaScript, qui peut être extrêmement précise.

Ajouter une API

L'interpréteur JS est un bac à sable entièrement 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 de l'interpréteur JS. Mais pour commencer, voici l'API nécessaire pour gérer les blocs d'alertes et d'invites:

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 par défaut qui nécessitent une API personnalisée pour l'interpréteur.

Association à highlightBlock()

Lors de l'exécution dans JS-Interpréteur, 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 de wrapper highlightBlock() pour capturer l'argument de la fonction, puis enregistrez-la 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 de manière répétée sans interruption 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 d'interpréteur JS

Voici une démonstration en direct de l'interprétation de JavaScript étape par étape. Et cette démonstration inclut un bloc d'attente, un bon exemple à utiliser pour d'autres comportements asynchrones (par exemple, voix ou audio, entrée utilisateur).