Como gerar e executar o 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 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:

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