Sometimes your block-code generator needs to reference the code of its inner block multiple times.
For example, if you have a block that prints the last element of a list, you need to access the list code multiple times:
// Incorrect block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
// listCode gets referenced twice.
return `print(${listCode}[${listCode}.length - 1]);\n`;
}
But this can cause problems if the resulting value of the inner block's code is inconsistent, or it has side effects. For example, if the inner code is actually a function call, this particular code can end up with an out of range condition:
print(randomList()[randomList().length - 1]);
Assigning to temporary variables lets you make sure that inner block's code is only evaluated once.
Temporary variables
A temporary variable stores the value of an inner block's code string so that the code is only evaluated once, and then the value can be referenced multiple times.
import {javascriptGenerator, Order} from 'blockly/javascript';
// Correct block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
const listVar = generator.nameDB_.getDistinctName(
'temp_list', Blockly.names.NameType.VARIABLE);
// listCode only gets evaluated once.
const code = `var ${listVar} = ${listCode};\n`;
return `print(${listVar}[${listVar}.length - 1]);\n`;
}
The getDistinctName
call takes in the variable name you want, and returns a
name that doesn't conflict with any user-defined variables.
Reduce redundant code
The downside of temporary variables is that if the inner block's code is a value and not a function or expression, you get code that's redundant:
// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);
To produce cleaner code you can check if the inner block's code is a value, and only include the temporary variable if it's not.
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`;
}