Générer et exécuter du code JavaScript

Les applications Blockly génèrent souvent du code JavaScript comme langage de sortie, généralement pour s'exécuter dans une page Web (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 simplement le code et l'évalue. Toutefois, il existe quelques améliorations. L'évaluation est encapsulée dans un try/catch afin que toutes les erreurs d'exécution soient visibles, au lieu d'échouer de manière silencieuse. Autre amélioration : code est ajouté à la liste des mots réservés. Ainsi, si le code de l'utilisateur contient une variable de ce nom, elle sera automatiquement renommée au lieu de provoquer une collision. Toutes les variables locales doivent être réservées de cette manière.

Blocs de mise en évidence

Mettre en surbrillance le bloc en cours d'exécution pendant l'exécution du code aide les utilisateurs à comprendre le comportement de leur programme. Vous pouvez mettre en surbrillance les instructions une par une 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 donc ajoutée avant chaque instruction, où 123 est le numéro de série du bloc à mettre en surbrillance.

Boucles infinies

Bien que le code obtenu soit toujours correct d'un point de vue syntaxique, il peut contenir des boucles infinies. Étant donné que la résolution du problème d'arrêt dépasse le champ d'application de Blockly, la meilleure approche pour traiter ces cas consiste à gérer un compteur et à le diminuer à chaque itération. Pour ce faire, définissez 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 code JavaScript.

JS-Interpreter

Si vous souhaitez exécuter correctement les blocs de l'utilisateur, le projet JS-Interpreter est la solution. Ce projet est distinct de Blockly, mais a été écrit spécifiquement pour Blockly.

  • Exécutez du code à n'importe quelle vitesse.
  • Mettre en pause/reprendre/exécuter par étapes
  • Mettre en surbrillance les blocs au fur et à mesure de leur exécution
  • Entièrement isolé du code JavaScript du navigateur.

Exécuter l'interpréteur

Commencez par télécharger l'interprète JS sur GitHub:

Télécharger le fichier ZIP Télécharger le fichier TAR Afficher sur GitHub

Ajoutez-la 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ète et à exécuter le code:

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

Exécuter l'interprète

Pour exécuter le code plus lentement ou de manière plus contrôlée, remplacez l'appel de run par une boucle qui effectue des étapes (dans ce cas, une étape tous 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

L'interprète JS est un bac à sable complètement isolé du navigateur. Tous les blocs qui effectuent des actions avec le monde extérieur nécessitent une API ajoutée à l'interprète. Pour obtenir une description complète, consultez la documentation de l'interprète JS. Mais pour commencer, voici l'API requise 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 l'interprète 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ète.

Association à highlightBlock()

Lors de l'exécution en tant qu'interprète JS, highlightBlock() doit être exécuté immédiatement, en dehors du bac à sable, lorsque l'utilisateur suit le programme. Pour ce faire, créez une fonction wrapper highlightBlock() pour capturer l'argument de la fonction et 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 à plusieurs reprises sans pause jusqu'à ce qu'une commande de surbrillance soit atteinte, puis faire une pause. Cette stratégie simule l'exécution ligne par ligne. L'exemple ci-dessous utilise cette approche.

Exemple d'interprète JS

Voici une démonstration en direct de l'interprétation du code JavaScript par étapes. Cette démonstration 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).