Os blocos de valor correspondem a expressões. Quando você usa um bloco de valor como um bloco interno, pode ser necessário usar a expressão gerada mais de uma vez no código do bloco. Por exemplo, um bloco que recebe o último elemento de uma lista usa a expressão que cria a lista duas vezes.
// Incorrect block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
// Get the expression that creates the list.
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
// listCode is used twice.
const code = `${listCode}[${listCode}.length - 1]`;
return [code, Order.MEMBER];
}
Isso causa problemas se o código do bloco interno gera valores diferentes a cada execução ou se o código tem efeitos colaterais. Por exemplo, se o código do bloco interno for uma chamada de função, esse código específico poderá causar uma condição fora de intervalo:
randomList()[randomList().length - 1]
Para evitar esse problema, seu código precisa executar o código de um bloco interno exatamente uma vez. Há duas maneiras de fazer isso:
Variáveis temporárias: armazene em cache o resultado da avaliação do código do bloco interno em uma variável temporária e use a variável temporária. Você só pode usar esse método se o bloco for um bloco de instrução.
Funções utilitárias: crie uma função que execute o trabalho necessário e transmita o resultado da avaliação do código do bloco interno como um argumento para essa função. É possível usar esse método para blocos de valor e de instrução.
Variáveis temporárias
Uma variável temporária armazena o valor da string de código de um bloco interno para que o código seja avaliado apenas uma vez e, em seguida, o valor possa ser referenciado várias vezes.
Variáveis temporárias não podem ser usadas em blocos de valor porque eles precisam retornar uma única linha de código. Use uma função utilitária.
import {javascriptGenerator, Order} from 'blockly/javascript';
// Correct block-code generator for a statement block that prints the last element of a list.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
// Get the expression that creates the list.
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
// Get the name of a temporary variable.
const listVar = generator.nameDB_.getDistinctName(
'temp_list', Blockly.names.NameType.VARIABLE);
// Evaluate listCode once and assign the result to the temporary variable.
const code = `var ${listVar} = ${listCode};\n`;
// Print the last element of the list.
code += `print(${listVar}[${listVar}.length - 1]);\n`;
return code;
}
Por exemplo, se o código do bloco interno for a chamada de função randomList()
, o
código gerado será:
var temp_list = randomList();
print(temp_list[temp_list.length - 1]);
A chamada getDistinctName
recebe o nome da variável que você quer e retorna um
nome que não conflita com nenhuma variável definida pelo usuário.
Reduzir o código redundante
A desvantagem das variáveis temporárias é que, se o código do bloco interno for um valor e não uma função ou expressão, você vai ter um código redundante:
// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);
Para produzir um código mais limpo, verifique se o código do bloco interno é um valor e inclua a variável temporária apenas se ele não for.
if (listCode.match(/^\w+$/)) {
const code = `print(${listCode}[${listCode}.length - 1]);\n`;
} else {
const listVar = generator.nameDB_.getDistinctName(
'temp_list', Blockly.names.NameType.VARIABLE);
const code = `var ${listVar} = ${listCode};\n`;
code += `print(${listVar}[${listVar}.length - 1]);\n`;
}
Funções do utilitário
Uma função utilitária é uma função definida pelo desenvolvedor que é incluída como parte da string de código gerada. Eles podem ser usados para garantir que o código do bloco interno seja avaliado apenas uma vez e que o valor possa ser referenciado várias vezes.
As funções de utilitário podem ser usadas em blocos de valor e de instrução. No entanto, os blocos de instruções geralmente precisam usar variáveis temporárias, que geralmente são mais legíveis.
import {javascriptGenerator, Order} from 'blockly/javascript';
// Correct block-code generator for a value block that gets the last element of a list.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
// Get the expression that creates the list.
const listCode = generator.valueToCode(block, 'LIST', Order.NONE);
// Create a function that accepts a list and returns its last element. The
// language generator adds this function to your code.
const functionName = generator.provideFunction_(
'list_lastElement',
[
`function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) {`,
` return list[list.length - 1];`,
`}`
]
);
// Create an expression that calls the function with listCode as its argument.
// This evaluates listCode once and passes the resulting list to the function.
const code = `${functionName}(${listCode})`;
return [code, Order.FUNCTION_CALL];
}
Por exemplo, se o código do bloco interno for a chamada de função randomList()
, o
código gerado será:
// This code is added to the overall code returned by the code generator.
function list_lastElement(list) {
return list[list.length - 1];
}
// This code is returned by your inner block.
list_lastElement(randomList());
Fornecer a função
É possível definir funções de utilitário em geradores de código de bloco usando
provideFunction_
. Ele recebe o nome que você quer para a função utilitária e
uma matriz de strings de código que contém a definição da função. Ele retorna o
nome resultante da função de utilitário, depois de (possivelmente) modificá-la para que não
entre em conflito com as funções definidas pelo usuário.
O provideFunction_
também elimina definições de função de utilitário, de modo que cada
função de utilitário exista apenas uma vez, mesmo que o tipo de bloco que a define exista
várias vezes.
Atualizar precedências
Ao definir uma função de utilitário, você também precisa atualizar as precedências (que definem como os parênteses são inseridos) incluídos no gerador de código de bloco.
A precedência é sempre baseada na string de código retornada pelo gerador de código
de bloco. Ele não se importa com operadores dentro de funções de utilitário. No
exemplo anterior, a chamada valueToCode
foi alterada para Order.NONE
, e a
tupla de retorno foi alterada para Order.FUNCTION_CALL
.