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 la même 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 se contente de générer et d'évaluer le code. 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 affinement : code est ajouté à la liste des de sorte que si le code de l'utilisateur contient une variable de ce nom, elle sera automatiquement renommées au lieu de la collision. Toutes les variables locales doivent être réservées de cette manière.

Mettre les blocs en surbrillance

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. La mise en surbrillance peut être effectuée instruction par instruction en définissant STATEMENT_PREFIX avant générer le code JavaScript:

javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');

Définissez highlightBlock pour marquer le volume sur l'espace de travail.

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

Cela entraîne l'ajout de l'instruction highlightBlock('123'); avant chaque instruction, où 123 est le numéro de série du bloc à en surbrillance.

Boucles infinies

Bien que la syntaxe du code obtenu soit garantie, 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 une 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 génération et d'exécution de 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 votre code à n'importe quelle vitesse.
  • Mettre en pause/reprendre/mettre en pause l'exécution
  • Mettez les blocs en surbrillance lors 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 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 code JavaScript, à créer le puis exécutez le code suivant:

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

Interpréteur

Pour exécuter le code plus lentement ou de manière plus contrôlée, remplacez le appelez run avec une boucle qui passe (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 ou un bloc, mais une unité sémantique dans JavaScript, qui peut être extrêmement précis.

Ajouter une API

L'interpréteur JS 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ète. Pour obtenir une description complète, consultez les Documentation JS-Interpreter Mais 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ète.

Association à highlightBlock()

Lors de l'exécution dans JS-Interpreter, highlightBlock() doit être exécuté immédiatement, en dehors du bac à sable, au fur et à mesure que l'utilisateur suit le programme. À faire Créez une fonction wrapper highlightBlock() pour capturer la fonction. un argument et l'enregistrer 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 ligne par ligne. L'exemple ci-dessous utilise cette approche.

Exemple d'interpréteur JS

Voici une démonstration en direct d'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, paroles ou audio, entrée utilisateur).