Um gerador de código de bloco é uma função que gera o código de um bloco e o retorna como uma string. O código gerado por um bloco depende do tipo dele:
- Os blocos de valor têm uma conexão de saída. Esses blocos funcionam como expressões em uma linguagem baseada em texto e geram strings que contêm expressões.
- Blocos de instrução são blocos sem uma conexão de saída. Esses blocos agem como instruções em uma linguagem baseada em texto e geram strings que contêm instruções.
Como escrever um gerador de código em blocos
Para cada bloco personalizado criado, é necessário escrever um gerador de código de bloco para cada idioma que você quer oferecer suporte. Observação: todos os geradores de código em blocos são escritos em JavaScript, mesmo que gerem código em outra linguagem.
Todos os geradores de código em blocos executam as seguintes etapas:
- Importe um gerador de código de idioma.
- Receba o valor de cada campo e transforme-o em uma string de código.
- Receba as strings de código geradas por blocos internos, que são blocos anexados a entradas de valor e instrução.
- Crie e retorne a string de código do bloco.
Exemplos de blocos
Como exemplos, vamos escrever geradores de código JavaScript para os seguintes blocos.
custom_compareé um bloco de valor que tem uma entrada de valor chamadaLEFT, um campo suspenso chamadoOPERATORe um campo numérico chamadoRIGHT. Ela gera uma string de expressão no formato'0 = 0'.
custom_ifé um bloco de instrução que tem um campo de caixa de seleção chamadoNOT, uma entrada de valor chamadaCONDITIONe uma entrada de instrução chamadaTHEN. Ele gera uma string de instrução no formato'if (...) {\n...\n};\n'.
Este documento cria os geradores etapa por etapa. Os geradores completos estão no final deste documento.
Observe que esses blocos se destinam apenas a ilustrar a geração de código. Em um
aplicativo real, use os blocos logic_compare e controls_if integrados.
Importar um gerador de código de idioma
É possível importar um gerador de código de idioma usando um dos seguintes métodos. Use o gerador importado para armazenar geradores de código de bloco no objeto forBlock.
Módulos
import {javascriptGenerator} from 'blockly/javascript';
import {pythonGenerator} from 'blockly/python';
import {phpGenerator} from 'blockly/php';
import {luaGenerator} from 'blockly/lua';
import {dartGenerator} from 'blockly/dart';
// Add block-code generators for the custom_if block.
javascriptGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
pythonGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
phpGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
luaGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
dartGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
Unpkg
Inclua o gerador depois de incluir o Blockly.
<script src="https://unpkg.com/blockly"></script>
<script src="https://unpkg.com/blockly/javascript_compressed"></script>
<script src="https://unpkg.com/blockly/python_compressed"></script>
<script src="https://unpkg.com/blockly/php_compressed"></script>
<script src="https://unpkg.com/blockly/lua_compressed"></script>
<script src="https://unpkg.com/blockly/dart_compressed"></script>
// Add block-code generators for the custom_if block.
javascript.javascriptGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
python.pythonGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
php.phpGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
lua.luaGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
dart.dartGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
Scripts locais
Inclua o gerador depois de incluir o Blockly.
<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>
<script src="python_compressed.js"></script>
<script src="php_compressed.js"></script>
<script src="lua_compressed.js"></script>
<script src="dart_compressed.js"></script>
// Add block-code generators for the custom_if block.
javascript.javascriptGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
python.pythonGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
php.phpGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
lua.luaGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
dart.dartGenerator.forBlock['custom_if'] = function (block, generator) { /* ... */ };
Receber valores de campo
Os campos permitem que os usuários insiram valores como strings, números e cores. Para receber o valor de um
campo, chame getFieldValue. O que é retornado varia de campo para campo. Por exemplo, os campos de texto retornam o texto exato inserido pelo usuário, mas os campos suspensos retornam uma string neutra em relação ao idioma associada ao item selecionado pelo usuário. Para mais informações, consulte a documentação sobre campos
integrados.
Dependendo do campo, talvez seja necessário transformar o valor retornado antes de usá-lo no código.
custom_compare
javascriptGenerator.forBlock['custom_compare'] = function (block, generator) {
// Use the value of the OPERATOR dropdown to look up the actual operator.
const OPERATORS = {
EQUALS: '==',
LESS: '<',
GREATER: '>',
};
const operator = OPERATORS[block.getFieldValue('OPERATOR')];
// The value of the RIGHT field is a number and can be used directly when
// building the block's code string.
const right = block.getFieldValue('RIGHT');
...
}
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
// Use the value of the NOT field to get the negation operator (if any).
const checkbox = block.getFieldValue('NOT');
const negate = checkbox === 'TRUE' ? '!' : '';
...
}
Para mais informações, consulte Transformar valores de campo.
Receber código de blocos internos
Os blocos internos são aqueles anexados ao valor e às entradas de instrução de um bloco.
Por exemplo, o bloco custom_if tem um bloco interno de valor para a condição "if" e blocos internos de instrução para o código que é executado se a condição for verdadeira.
Ao contrário dos valores de campo, o código que você recebe dos blocos internos está pronto para uso e não precisa ser transformado.
Blocos de valor interno
Para receber o código de um bloco interno anexado a uma entrada de valor, chame valueToCode.
Esse método chama o gerador de código do bloco interno.
custom_compare
import {javascriptGenerator, Order} from 'blockly/javascript';
javascriptGenerator.forBlock['custom_compare'] = function (block, generator) {
...
const order = operator === '==' ? Order.EQUALITY : Order.RELATIONAL;
const left = generator.valueToCode(block, 'LEFT', order);
...
}
custom_if
import {javascriptGenerator, Order} from 'blockly/javascript';
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const order = checkbox === 'TRUE' ? Order.LOGICAL_NOT : Order.NONE;
const condition = generator.valueToCode(block, 'CONDITION', order) || 'false';
...
}
Ao chamar valueToCode, você precisa informar o operador mais forte no
código que será aplicado ao código do bloco interno. Isso permite que valueToCode
determine se precisa incluir o código do bloco interno entre parênteses.
Por exemplo, marcar a caixa NOT em custom_if aplica um operador lógico NOT (!) à condição. Nesse caso, você transmite a precedência do operador NOT (Order.LOGICAL_NOT) para valueToCode, e valueToCode compara isso à precedência do operador mais fraco no bloco interno. Em seguida, ele encapsula o código do bloco interno conforme necessário:
- Se
CONDITIONfor um bloco de variáveis,valueToCodenão vai adicionar parênteses porque o operador "not" pode ser aplicado diretamente a uma variável (!myBoolean). - Se
CONDITIONfor um bloco de comparação,valueToCodevai adicionar parênteses para que o operador not seja aplicado à comparação inteira (!(a < b)) em vez do valor à esquerda (!a < b).
Não é necessário saber se valueToCode adicionou parênteses. Basta transmitir a precedência para valueToCode e adicionar o código retornado à sua string de código. Para mais informações, consulte precedência de valueToCode.
Blocos de instruções internos
Para receber o código de um bloco interno anexado a uma entrada de instrução, chame
statementToCode. Esse método chama o gerador de código do bloco interno e
processa a indentação do código.
custom_compare
O bloco custom_compare não tem entradas de instrução.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
Você só precisa chamar statementToCode para o bloco interno conectado diretamente
a uma entrada de instrução. O statementToCode processa todos os blocos extras anexados
ao primeiro bloco.
Criar e retornar sua string de código
Depois de receber o código dos campos e blocos internos, crie e retorne a string de código do seu bloco. O que você retorna depende do tipo de bloco:
Blocos de valor:retornam uma matriz que contém a string de código e a precedência do operador mais fraco no seu código. O
valueToCodeusa isso para decidir se o código precisa ser colocado entre parênteses quando o bloco é usado como um bloco interno. Para mais informações, consulte Precedência de retorno.Blocos de instruções:retornam a string de código.
custom_compare
import {javascriptGenerator, Order} from 'blockly/javascript';
javascriptGenerator.forBlock['custom_compare'] = function (block, generator) {
...
const order = operator === '==' ? Order.EQUALITY : Order.RELATIONAL;
...
const code = left + ' ' + operator + ' ' + right;
return [code, order];
}
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const code = 'if (' + negate + condition + ') {\n' + statements + '}\n';
return code;
}
Se você usar o código de um bloco de valor interno várias vezes na string de código, armazene em cache o código desse bloco para evitar bugs sutis e efeitos colaterais indesejados.
Geradores de código completos
Para referência, confira os geradores de código completos para cada bloco:
custom_compare
import {javascriptGenerator, Order} from 'blockly/javascript';
javascriptGenerator.forBlock['custom_compare'] = function (block, generator) {
const OPERATORS = {
EQUALS: '==',
LESS: '<',
GREATER: '>',
};
const operator = OPERATORS[block.getFieldValue('OPERATOR')];
const order = operator === '==' ? Order.EQUALITY : Order.RELATIONAL;
const left = generator.valueToCode(block, 'LEFT', order);
const right = block.getFieldValue('RIGHT');
const code = left + ' ' + operator + ' ' + right;
return [code, order];
}
custom_if
import {javascriptGenerator, Order} from 'blockly/javascript';
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
const checkbox = block.getFieldValue('NOT');
const negate = checkbox === 'TRUE' ? '!' : '';
const order = checkbox === 'TRUE' ? Order.LOGICAL_NOT : Order.NONE;
const condition = generator.valueToCode(block, 'CONDITION', order) || 'false';
const statements = generator.statementToCode(block, 'THEN');
const code = 'if (' + negate + condition + ') {\n' + statements + '}\n';
return code;
}