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 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 apenas gera o código e o avalia. No entanto, há alguns refinamentos. Um refinamento é que a avaliação é encapsulada em
um try
/catch
para que todos os erros de tempo de execução fiquem visíveis, em vez de falharem
silenciosamente. Outro refinamento é que code
é adicionado à lista de palavras reservadas para que, se o código do usuário tiver uma variável com esse nome, ela seja renomeada automaticamente em vez de entrar em conflito. 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 no 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 resulta na adição da instrução highlightBlock('123');
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 seja sintaticamente correto em todos os momentos, ele pode conter loops infinitos. Como resolver o problema de parada 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 todos os loops e funções. 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 geração e execução de JavaScript.
JS-Interpreter
Se você quiser executar os bloqueios do usuário corretamente, o projeto JS-Interpreter é a melhor opção. Ele é separado do Blockly, mas foi escrito especificamente para ele.
- Execute o código em qualquer velocidade.
- Pausar/retomar/executar passo a passo.
- Destacar blocos à medida que são executados.
- Completamente 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 chamar é gerar o JavaScript, criar o interpretador e executar o código:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Executar o intérprete
Para executar o código mais lentamente ou de maneira mais controlada, substitua a
chamada para run
por um loop que execute etapas (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, mas sim 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 realizam ações com o mundo externo exigem uma API adicionada ao interpretador. 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 solicitação são os únicos dois blocos no conjunto padrão que exigem uma API personalizada para o intérprete.
Conectando o provedor highlightBlock()
Ao ser executado no JS-Interpreter, highlightBlock()
precisa ser executado
imediatamente, fora do sandbox, à medida que o usuário avança pelo programa. Para fazer isso, crie uma função de wrapper highlightBlock()
para capturar o argumento da função e registre-o 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 como interpretar JavaScript etapa por etapa. 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).