Le applicazioni Blockly generano spesso JavaScript come linguaggio di output, generalmente per l'esecuzione all'interno di una pagina web (possibilmente la stessa o un WebView incorporato). Come per qualsiasi generatore, il primo passaggio consiste nell'includere il generatore JavaScript.
import {javascriptGenerator} from 'blockly/javascript';
Per generare JavaScript dallo spazio 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 e valuta il codice. Tuttavia, esistono alcuni perfezionamenti. Un perfezionamento è che l'espressione eval è racchiusa in un try
/catch
in modo che eventuali errori di runtime siano visibili, anziché verificarsi in silenzio. Un altro perfezionamento è che code
viene aggiunto all'elenco di parole riservate in modo che, se il codice dell'utente contiene una variabile con questo nome, verrà rinominata automaticamente anziché verificarsi una collisione. Eventuali variabili locali devono essere
riservate in questo modo.
Blocchi in evidenza
L'evidenziazione del blocco in esecuzione durante l'esecuzione del codice aiuta gli utenti a comprendere il comportamento del programma. L'evidenziazione può essere eseguita su un livello di dichiarazione 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);
}
Di conseguenza, l'istruzione highlightBlock('123');
viene aggiunta prima di ogni istruzione, dove 123
è il numero di serie del blocco da evidenziare.
Loop infiniti
Sebbene il codice risultante sia garantito come sintatticamente corretto in qualsiasi momento, potrebbe contenere loop infiniti. Poiché la risoluzione del
problema di arresto esula
dall'ambito di Blockly (!), l'approccio migliore per gestire questi casi è mantenere un contatore e diminuirlo 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 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.
JS-Interpreter
Se vuoi eseguire correttamente i blocchi dell'utente, il progetto JS-Interpreter è la soluzione che fa per te. Questo progetto è separato da Blockly, ma è stato scritto appositamente per Blockly.
- Esegui il codice a qualsiasi velocità.
- Mettere in pausa/riprendere/eseguire l'esecuzione passo passo.
- Evidenzia i blocchi durante l'esecuzione.
- Completamente isolate dal codice JavaScript del browser.
Esegui l'interprete
Innanzitutto, scarica JS-Interpreter da GitHub:
Quindi aggiungila alla 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 riga o un blocco, ma un'unità semantica in JavaScript, che può essere estremamente granulare.
Aggiungi un'API
L'interprete JS è una sandbox completamente isolata dal browser. Tutti i blocchi che eseguono azioni con il mondo esterno richiedono un'API aggiunta all'interprete. Per una descrizione completa, consulta la documentazione di JS-Interpreter. Per iniziare, ecco l'API necessaria per supportare i blocchi di avviso e richiesta:
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));
}
Poi modifica l'inizializzazione dell'interprete in modo da passare la funzione initApi:
var myInterpreter = new Interpreter(code, initApi);
I blocchi di avviso e prompt sono gli unici due blocchi nell'insieme di blocchi predefinito 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. Per farlo, crea una funzione wrapper highlightBlock()
per acquisire l'argomento della funzione e registrarla 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 interruzione fino al raggiungimento di un comando di evidenziazione, quindi mettere in pausa. Questa strategia simula l'esecuzione riga per riga. L'esempio seguente utilizza questo approccio.
Esempio di JS-Interpreter
Ecco una demo dal vivo dell'interpretazione di JavaScript passo 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).