Le applicazioni Blockly spesso generano JavaScript come linguaggio di output, in genere per essere eseguite all'interno di una pagina web (possibilmente la stessa o una WebView incorporata). 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 precedente genera il codice e lo valuta. Tuttavia,
ci sono un paio di perfezionamenti. Un perfezionamento è che l'eval è racchiuso in
un try/catch in modo che eventuali errori di runtime siano visibili, anziché non riuscire
in modo silenzioso. Un altro perfezionamento è l'aggiunta di code all'elenco delle parole riservate, in modo che se il codice dell'utente contiene una variabile con questo nome, questa verrà rinominata automaticamente anziché entrare in conflitto. Tutte le variabili locali devono essere
riservate in questo modo.
Blocchi di momenti salienti
L'evidenziazione del blocco attualmente in esecuzione durante l'esecuzione del codice aiuta gli utenti
a comprendere il comportamento del programma. L'evidenziazione può essere eseguita a livello di 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.
Infinite Loops
Sebbene il codice risultante sia sempre sintatticamente corretto, potrebbe contenere loop infiniti. Poiché la risoluzione del
problema di arresto non rientra
nell'ambito di Blockly (!), l'approccio migliore per gestire questi casi è
mantenere un contatore e decrementarlo ogni volta che viene eseguita un'iterazione.
Per farlo, imposta javascriptGenerator.INFINITE_LOOP_TRAP su uno snippet di codice
che verrà inserito in ogni ciclo 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 live della generazione ed esecuzione di JavaScript.
JS-Interpreter
Se vuoi eseguire correttamente i blocchi dell'utente, il progetto JS-Interpreter è la soluzione ideale. 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 isolato da JavaScript del browser.
Esegui l'interprete
Innanzitutto, scarica JS-Interpreter da GitHub:
Quindi, aggiungilo alla tua pagina:
<script src="acorn_interpreter.js"></script>
Il metodo più semplice per chiamarlo è generare il codice JavaScript, creare l'interprete ed eseguire il codice:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Passare all'interprete
Per eseguire il codice più lentamente o in modo più controllato, sostituisci la
chiamata a run con un ciclo che esegue un passo (in questo caso un passo 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. Qualsiasi blocco che esegue azioni con il mondo esterno richiede l'aggiunta di un'API 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 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 di avviso e prompt sono gli unici due blocchi nel set 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. Per farlo, crea una funzione wrapper highlightBlock() per acquisire l'argomento della 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 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 passo passo di JavaScript. Questa demo include un blocco di attesa, un buon esempio da utilizzare per altri comportamenti asincroni (ad es. voce o audio, input dell'utente).