Gerar e executar JavaScript

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:

Fazer o download do arquivo ZIP Fazer o download do TAR Ball Visualizar 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).