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 WebView incorporata). Come per qualsiasi generatore, il primo passaggio è includere il generatore JavaScript.

import {javascriptGenerator} from 'blockly/javascript';

Per generare JavaScript dall'area di lavoro, chiama:

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

In sostanza, lo snippet riportato sopra genera il codice e lo valuta. Tuttavia, ci sono un paio di perfezionamenti. Un perfezionamento è che la valutazione è aggregata in try/catch in modo che gli eventuali errori di runtime siano visibili, anziché generare errori in modo discreto. Un altro perfezionamento è l'aggiunta di code all'elenco delle parole riservate, in modo che, se il codice dell'utente contiene una variabile con quel nome, questa verrà automaticamente rinominata anziché essere in conflitto. Tutte le variabili locali devono essere prenotate in questo modo.

Evidenzia blocchi

Evidenziare il blocco attualmente in esecuzione durante l'esecuzione del codice aiuta gli utenti a capire il comportamento del loro programma. L'evidenziazione può essere eseguita a livello di istruzione per istruzione impostando STATEMENT_PREFIX prima di generare 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);
}

In questo modo, l'istruzione highlightBlock('123'); viene aggiunta prima di ogni istruzione, dove 123 è il numero di serie del blocco da evidenziare.

Loop infiniti

Anche se è garantito che il codice risultante sia sempre sintatticamente corretto, potrebbe contenere loop infiniti. Poiché risolvere il problema di Halting va oltre l'ambito di Blockly (!), l'approccio migliore per gestire questi casi è mantenere un contatore e ridurlo ogni volta che viene eseguita un'iterazione. Per farlo, imposta javascriptGenerator.INFINITE_LOOP_TRAP su uno snippet di codice che verrà inserito in ogni loop e 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 sulla generazione e l'esecuzione di JavaScript.

Interprete JS

Se ti interessa eseguire correttamente i blocchi dell'utente, ti consigliamo di utilizzare il progetto JS-Interpreter. Questo progetto è separato da Blockly, ma è stato scritto specificamente per Blockly.

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

Esegui la modalità Interprete

Per prima cosa, 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 chiamarlo consiste nel generare il codice JavaScript, creare l'interprete ed eseguire il codice:

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

Fai il passo con l'interprete

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

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

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

Aggiungi un'API

JS-Interpreter è una sandbox completamente isolata dal browser. Tutti i blocchi che eseguono azioni con il mondo esterno richiedono l'aggiunta di un'API all'interprete. Per una descrizione completa, consulta la documentazione relativa all'interpretazione JS. Per cominciare, ecco 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 per passare la funzione initApi:

var myInterpreter = new Interpreter(code, initApi);

I blocchi per avvisi e prompt sono gli unici due blocchi dell'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, mentre l'utente esegue i passaggi del programma. Per farlo, crea una funzione wrapper highlightBlock() per acquisire l'argomento della funzione e registrala 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 dei passaggi senza mettere in pausa fino a quando non viene raggiunto un comando di evidenziazione. Questa strategia simula l'esecuzione riga per riga. Nell'esempio seguente viene utilizzato questo approccio.

Esempio di interprete JS

Ecco una demo dal vivo che spiega come interpretare JavaScript passo passo. Questa demo include un blocco di attesa, un buon esempio da utilizzare per altri comportamenti asincroni (ad es. voce o audio, input dell'utente).