Os aplicativos do Blockly geralmente geram JavaScript como linguagem de saída, geralmente para serem executados em uma página da Web (possivelmente a mesma ou uma WebView incorporada). Como qualquer gerador, a primeira etapa é incluir o gerador de JavaScript.
import {javascriptGenerator} from 'blockly/javascript';
Para gerar JavaScript a partir do espaço de trabalho, chame:
javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);
O código resultante pode ser executado diretamente na página da Web de destino:
try {
eval(code);
} catch (e) {
alert(e);
}
Basicamente, o snippet acima gera e faz a avaliação do código. No entanto,
há alguns ajustes. Um refinamento é que a avaliação é encapsulada em um try
/catch
para que os erros de execução fiquem visíveis, em vez de falhar silenciosamente. Outro refinamento é que code
é adicionado à lista de palavras
reservadas para que, se o código do usuário contiver uma variável com esse nome, ele será
automaticamente renomeado em vez de entrar em conflito. Todas as variáveis locais precisam ser
reservadas dessa maneira.
Destacar blocos
Destacar o bloco em execução no momento durante a execução do código ajuda os usuários
a entender o comportamento do programa. A destaque pode ser feita em um
nível de instrução por instrução. Para isso, defina STATEMENT_PREFIX
antes
de gerar o código JavaScript:
javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');
Defina highlightBlock
para marcar o bloco no espaço de trabalho.
function highlightBlock(id) {
workspace.highlightBlock(id);
}
Isso faz com que a instrução highlightBlock('123');
seja adicionada antes
de cada instrução, em que 123
é o número de série do bloco a ser
destacado.
Loops infinitos
É garantido que o código resultante estará sintaticamente correto o tempo todo, mas ele pode conter loops infinitos. Como a solução do
problema de pausa está além
do escopo do Blockly (!), a melhor abordagem para lidar com esses casos é
manter um contador e diminuí-lo sempre que uma iteração for realizada.
Para fazer isso, basta definir javascriptGenerator.INFINITE_LOOP_TRAP
como um snippet de código que será inserido em cada loop e função. Veja um
exemplo:
window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);
Exemplo
Confira uma demonstração ao vivo (em inglês) de como gerar e executar JavaScript.
Intérprete de JS
Se você leva a sério a execução dos blocos do usuário corretamente, o projeto JS-Interpreter é ideal. Esse projeto é separado do Blockly, mas foi especificamente escrito para o Blockly.
- Execute o código em qualquer velocidade.
- Pausar/retomar/passar a execução.
- Destaque os blocos à medida que eles forem executados.
- Completamente isolado do JavaScript do navegador.
Executar o intérprete
Primeiro, faça o download do JS-Interpreter no GitHub:
Depois, adicione-o à sua página:
<script src="acorn_interpreter.js"></script>
O método mais simples de chamá-lo é gerar o JavaScript, criar o intérprete e executar o código:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Etapa para o intérprete
Para executar o código mais lentamente ou de maneira mais controlada, substitua a
chamada para run
por um loop que segue as etapas (neste caso, uma etapa a cada 10ms):
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
Observe que cada etapa não é uma linha ou um bloco. Ela é uma unidade semântica em JavaScript, que pode ser extremamente refinada.
Adicionar uma API
O JS-Interpreter é um sandbox totalmente isolado do navegador. Todos os blocos que executam ações externas exigem que uma API seja adicionada ao intérprete. Para uma descrição completa, consulte a documentação do JS-Interpreter (em inglês). Mas, para começar, esta é a API necessária para oferecer suporte aos blocos de alerta 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));
}
Em seguida, modifique a inicialização do interpretador para transmitir a função initApi:
var myInterpreter = new Interpreter(code, initApi);
Os blocos de alerta e de prompt são os únicos no conjunto padrão de blocos que exigem uma API personalizada para o intérprete.
Conectando o provedor highlightBlock()
Ao ser executado no JS-Interpreter, o highlightBlock()
precisa ser executado
imediatamente, fora do sandbox, à medida que o usuário avança no programa. Para fazer
isso, crie uma função wrapper highlightBlock()
para capturar o argumento
da função e o registre como uma função 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));
}
É possível que aplicativos mais sofisticados executem etapas repetidamente sem pausar até que um comando de destaque seja alcançado e depois pausem. Essa estratégia simula a execução linha por linha. O exemplo abaixo usa essa abordagem.
Exemplo do JS-Interpreter
Confira uma demonstração ao vivo (em inglês) de como interpretar o JavaScript passo a passo. E esta demonstração inclui um bloco de espera, um bom exemplo para usar para outro comportamento assíncrono (por exemplo, fala ou áudio, entrada do usuário).