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.
- Os blocos de instrução são blocos sem uma conexão de saída. Esses blocos funcionam como instruções em uma linguagem baseada em texto e geram strings que contêm instruções.
Como escrever um gerador de código de bloco
Para cada bloco personalizado criado, é necessário escrever um gerador de código de bloco para cada idioma que você quer oferecer suporte. Todos os geradores de código de bloco são programados em JavaScript, mesmo que gerem código em outro idioma.
Todos os geradores de código de bloco executam as seguintes etapas:
- Importe um gerador de código de idioma.
- Extraia 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.
- Cria e retorna 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 de lista suspensa chamadoOPERATOR
e 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 chamadaCONDITION
e uma entrada de instrução chamadaTHEN
. Ela gera uma string de instrução no formato'if (...) {\n...\n};\n'
.
Este documento cria os geradores passo a passo. Os geradores completos estão disponíveis no final deste documento.
Esses blocos servem apenas para 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ódigos de idioma
É possível importar um gerador de códigos 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
É necessário incluir 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
É necessário incluir 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 de menu suspenso retornam uma string neutra em relação ao idioma associada ao item
selecionado pelo usuário. Para mais informações, consulte a documentação de 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 os blocos anexados às entradas de valor e 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 recebido 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
, é necessário informar o operador mais forte no
seu código que será aplicado ao código do bloco interno. Isso permite que valueToCode
determine se é necessário envolver o código do bloco interno entre parênteses.
Por exemplo, marcar a caixa NOT
em custom_if
aplica um operador lógico (!
) à condição. Nesse caso, você transmite a precedência do operador
NOT (Order.LOGICAL_NOT
) para valueToCode
, e valueToCode
compara
isso com a precedência do operador mais fraco no bloco interno. Em seguida, ele envolve
o código do bloco interno conforme necessário:
- Se
CONDITION
for um bloco de variáveis,valueToCode
não vai adicionar parênteses porque o operador "not" pode ser aplicado diretamente a uma variável (!myBoolean
). - Se
CONDITION
for um bloco de comparação,valueToCode
vai adicionar parênteses para que o operador "not" seja aplicado a toda a comparação (!(a < b)
) em vez do valor à esquerda (!a < b
).
Você não precisa saber se valueToCode
adicionou parênteses. Tudo o que você
precisa fazer é transmitir a precedência para valueToCode
e adicionar o código retornado à
sua string de código. Para mais informações, consulte Prioridade de
valueToCode.
Blocos de instruções internas
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 o código de recuo.
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 diretamente conectado
a uma entrada de instrução. statementToCode
processa todos os blocos adicionais anexados
ao primeiro.
Criar e retornar a string de código
Depois de receber o código dos campos e blocos internos, crie e retorne a string de código do 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 código. O
valueToCode
usa isso para decidir se o código precisa ser envolvido em parênteses quando o bloco é usado como um bloco interno. Para mais informações, consulte Prioridade de retorno.Blocos de instrução: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 de 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;
}