Generazione ed esecuzione di JavaScript

Le applicazioni blockly spesso generano JavaScript come linguaggio di output. in genere per l'esecuzione all'interno di una pagina web (possibilmente la stessa o una libreria WebView incorporata). Come per ogni generatore, il primo passaggio consiste nell'includere il generatore JavaScript.

import {javascriptGenerator} from 'blockly/javascript';

Per generare JavaScript dall'area di lavoro, richiama:

javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);

Il codice risultante può essere eseguito direttamente nella pagina web di destinazione:

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

Fondamentalmente, lo snippet riportato sopra genera semplicemente il codice e lo valuta. Tuttavia, devi apportare alcune modifiche. Un perfezionamento è che la valutazione è racchiusa in try/catch, in modo che gli eventuali errori di runtime siano visibili, anziché non avere esito positivo in silenzio. Un altro perfezionamento è che viene aggiunto code all'elenco di elementi prenotati in modo che se il codice dell'utente contiene una variabile con questo nome rinominata automaticamente anziché in conflitto. Eventuali variabili locali devono essere in questo modo.

Evidenzia blocchi

Evidenziare il blocco attualmente in esecuzione durante l'esecuzione del codice aiuta gli utenti il comportamento del loro programma. L'evidenziazione può essere eseguita su a livello di dichiarazione per le dichiarazioni impostando STATEMENT_PREFIX prima di generando il codice JavaScript:

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

Definisci highlightBlock per contrassegnare il blocco nello spazio di lavoro.

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

Ciò determina l'aggiunta dell'istruzione highlightBlock('123'); a prima ogni istruzione, dove 123 è il numero di serie del blocco in evidenza.

Loop infiniti

Sebbene sia garantito che il codice risultante sia sintatticamente corretto, volte, potrebbe contenere loop infiniti. Dopo aver risolto Il problema di blocco non si limita L'ambito di Blockly (!) L'approccio migliore per affrontare questi casi è mantenere un contatore e diminuirlo ogni volta che viene eseguita un'iterazione. A questo scopo, imposta javascriptGenerator.INFINITE_LOOP_TRAP su un codice che verrà inserito in ogni loop e in ogni funzione. Ecco un esempio:

window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);

Esempio

Ecco una demo dal vivo di generazione ed esecuzione di JavaScript.

Interprete di JS

Se vuoi davvero eseguire correttamente i blocchi dell'utente, Il progetto JS-Interpreter è la soluzione. Questo progetto è separato da Blockly, ma è stato scritto specificamente per Blockly.

  • Esegui codice a qualsiasi velocità.
  • Metti in pausa/riprendi/passa l'esecuzione.
  • Evidenzia i blocchi mentre vengono eseguiti.
  • Completamente isolato dal codice JavaScript del browser.

Esegui la modalità Interprete

Innanzitutto, scarica JS-Interpreter da GitHub:

Scarica file ZIP Scarica TAR Ball Visualizza su GitHub

Quindi aggiungilo alla tua pagina:

<script src="acorn_interpreter.js"></script>

Il metodo più semplice per richiamarlo è generare il codice JavaScript, creare l'interprete ed eseguire il codice:

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

Eseguire il passaggio dell'interprete

Per eseguire il codice più lentamente o in modo più controllato, sostituisci la chiamata a run con un ciclo che esegue dei passaggi (in questo caso un passaggio ogni 10 ms):

function nextStep() {
  if (myInterpreter.step()) {
    setTimeout(nextStep, 10);
  }
}
nextStep();

Tieni presente che ogni passaggio non è una linea o un blocco, ma un'unità semantica JavaScript, che può essere molto granulare.

Aggiungi un'API

JS-Interpreter è una sandbox completamente isolata dal browser. È necessaria un'API per tutti i blocchi che eseguono azioni con il mondo esterno l'interprete. Per una descrizione completa, vedi Documentazione di JS-Interpreter. Tuttavia, per iniziare, l'API è necessaria per supportare i blocchi di avvisi e prompt:

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));
}

Quindi modifica l'inizializzazione dell'interprete in modo che passi la funzione initApi:

var myInterpreter = new Interpreter(code, initApi);

I blocchi di avviso e prompt sono gli unici due blocchi nell'insieme predefinito di blocchi che richiedono un'API personalizzata per l'interprete.

Collegamento di highlightBlock()

Quando viene eseguito in JS-Interpreter, highlightBlock() deve essere eseguito immediatamente, al di fuori della sandbox, man mano che l'utente esegue il programma. Da fare Crea quindi una funzione wrapper highlightBlock() per acquisire la funzione e registrarlo come funzione 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));
}

Le applicazioni più sofisticate potrebbero voler eseguire ripetutamente i passaggi senza pausa fino a raggiungere un comando di evidenziazione, quindi metti in pausa. Questa strategia simula riga per riga. Nell'esempio riportato di seguito viene utilizzato questo approccio.

Esempio di JS-Interpreter

Ecco una demo dal vivo di interpretare JavaScript passo dopo passo. Inoltre, questa demo include un blocco di attesa, un buon esempio da utilizzare per altri comportamenti asincroni (ad es. parlato o audio, input utente).