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 JavaScript.
import {javascriptGenerator} from 'blockly/javascript';
Para gerar JavaScript no 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 o código e o avalia. No entanto,
há alguns refinamentos. Um refinamento é que a avaliação é embrulhada em
um try
/catch
para que todos os erros de execução fiquem visíveis, em vez de falhar
silenciosamente. Outro refinamento é que code
é adicionado à lista de palavras reservadas.
Se o código do usuário contiver uma variável com esse nome, ela será renomeada automaticamente em vez de colidir. Todas as variáveis locais precisam ser
reservadas dessa forma.
Blocos de destaque
Destacar o bloco em execução enquanto o código é executado ajuda os usuários
a entender o comportamento do programa. O destaque pode ser feito em um
nível de instrução por instrução definindo 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
Embora o código resultante tenha garantia de sintaxe correta o tempo
todo, ele pode conter loops infinitos. Como a solução do
problema de interrupção está fora 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. Confira 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 de como gerar e executar JavaScript.
JS-Interpreter
Se você quer executar os blocos do usuário corretamente, o projeto JS-Interpreter é a melhor opção. Esse projeto é separado do Blockly, mas foi criado especificamente para ele.
- Executar código em qualquer velocidade.
- Pausar/retomar/executar passo a passo.
- Destacar blocos conforme eles são executados.
- Totalmente isolado do JavaScript do navegador.
Executar o intérprete
Primeiro, faça o download do JS-Interpreter no GitHub:
Em seguida, adicione à sua página:
<script src="acorn_interpreter.js"></script>
O método mais simples de fazer isso é gerar o JavaScript, criar o intérprete e executar o código:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Ativar o intérprete
Para executar o código mais lentamente ou de maneira mais controlada, substitua a
chamada para run
por um loop que avança (neste caso, uma etapa a cada 10 ms):
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
Cada etapa não é uma linha ou um bloco, é uma unidade semântica em JavaScript, que pode ser extremamente detalhada.
Adicionar uma API
O JS-Interpreter é um sandbox completamente isolado do navegador. Todos os blocos que executam ações com o mundo externo exigem uma API adicionada ao intérprete. Para uma descrição completa, consulte a documentação do JS-Interpreter. Mas, para começar, aqui está a API necessária para oferecer suporte aos blocos de alerta e solicitação:
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 solicitação são os únicos dois blocos 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, conforme o usuário avança pelo programa. Para fazer
isso, crie uma função wrapper highlightBlock()
para capturar o argumento
da função e registre-a 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));
}
Aplicativos mais sofisticados podem querer executar etapas repetidamente sem pausa até que um comando de destaque seja alcançado e, em seguida, pausar. Essa estratégia simula a execução linha por linha. O exemplo abaixo usa essa abordagem.
Exemplo de JS-Interpreter
Confira uma demonstração ao vivo de interpretação de JavaScript. E esta demonstração inclui um bloco de espera, um bom exemplo para usar em outros comportamentos assíncronos (por exemplo, fala ou áudio, entrada do usuário).