Генератор блочного кода — это функция, которая генерирует код для блока и возвращает его в виде строки. Какой код генерирует блок, зависит от его типа:
- Блоки значений имеют выходное соединение. Эти блоки действуют как выражения на текстовом языке и генерируют строки, содержащие выражения.
- Блоки операторов — это блоки без выходного соединения. Эти блоки действуют как операторы на текстовом языке и генерируют строки, содержащие операторы.
Как написать генератор блочного кода
Для каждого создаваемого вами пользовательского блока вам необходимо написать генератор блочного кода для каждого языка, который вы хотите поддерживать. Обратите внимание, что все генераторы блочного кода написаны на JavaScript, даже если они генерируют код на другом языке.
Все генераторы блочного кода выполняют следующие шаги:
- Импортируйте генератор языкового кода.
- Получите значение каждого поля и преобразуйте его в строку кода.
- Получите строки кода, сгенерированные внутренними блоками, которые являются блоками, прикрепленными к входным значениям и операторам.
- Создайте и верните строку кода блока.
Примеры блоков
В качестве примеров мы напишем генераторы кода JavaScript для следующих блоков.
custom_compare
— это блок значений, который имеет входное значение с именемLEFT
, раскрывающееся поле с именемOPERATOR
и числовое поле с именемRIGHT
. Он генерирует строку выражения вида'0 = 0'
.custom_if
— это блок операторов, который имеет поле флажка с именемNOT
, входное значение с именемCONDITION
и входное поле оператора с именемTHEN
. Он генерирует строку оператора вида'if (...) {\n...\n};\n'
.
В этом документе генераторы строятся шаг за шагом. Готовые генераторы вы можете найти в конце этого документа .
Обратите внимание, что эти блоки предназначены только для иллюстрации генерации кода. В реальном приложении используйте встроенные блоки logic_compare
и controls_if
.
Импортировать генератор языковых кодов
Вы можете импортировать генератор языкового кода, используя один из следующих методов. Используйте импортированный генератор для хранения генераторов блочного кода в объекте forBlock
.
Модули
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) { /* ... */ };
Распаковать
Вы должны включить генератор после включения 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) { /* ... */ };
Локальные скрипты
Вы должны включить генератор после включения 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) { /* ... */ };
Получить значения полей
Поля позволяют пользователям вводить такие значения, как строки, числа и цвета. Чтобы получить значение поля, вызовите getFieldValue
. То, что возвращается, отличается от поля к полю. Например, текстовые поля возвращают точный текст, введенный пользователем, а поля раскрывающихся списков возвращают независимую от языка строку, связанную с элементом, выбранным пользователем. Дополнительную информацию см. в документации по встроенным полям .
В зависимости от поля вам может потребоваться преобразовать возвращаемое значение перед его использованием в коде.
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' ? '!' : '';
...
}
Дополнительные сведения см. в разделе Преобразование значений полей .
Получить код из внутренних блоков
Внутренние блоки — это блоки, прикрепленные к входным значениям блока и операторам. Например, блок custom_if
имеет внутренний блок значений для условия if и внутренние блоки операторов для кода, который выполняется, если условие истинно.
В отличие от значений полей, код, который вы получаете из внутренних блоков, готов к использованию и не требует преобразования.
Внутренние блоки значений
Чтобы получить код из внутреннего блока, прикрепленного к входному значению, вызовите valueToCode
. Этот метод вызывает генератор кода внутреннего блока.
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';
...
}
Когда вы вызываете valueToCode
, вам нужно сообщить ему о самом сильном операторе в вашем коде, который будет применяться к коду внутреннего блока. Это позволяет valueToCode
определить, нужно ли заключать код внутреннего блока в круглые скобки.
Например, установка флажка NOT
в custom_if
применяет к условию логический оператор not ( !
). В этом случае вы передаете приоритет оператора not ( Order.LOGICAL_NOT
) в valueToCode
, и valueToCode
сравнивает его с приоритетом самого слабого оператора во внутреннем блоке. Затем он оборачивает код внутреннего блока по мере необходимости:
- Если
CONDITION
является блоком переменных,valueToCode
не добавляет круглые скобки, поскольку оператор not можно применять непосредственно к переменной (!myBoolean
). - Если
CONDITION
является блоком сравнения,valueToCode
добавляет круглые скобки, поэтому оператор not применяется ко всему сравнению (!(a < b)
) вместо левого значения (!a < b
).
На самом деле вам не нужно знать, добавлены ли в valueToCode
круглые скобки. Все, что вам нужно сделать, это передать приоритет valueToCode
и добавить возвращенный код в строку кода. Дополнительные сведения см. в разделе приоритет valueToCode .
Внутренние блоки операторов
Чтобы получить код из внутреннего блока, прикрепленного к вводу оператора, вызовите оператор statementToCode
. Этот метод вызывает генератор кода внутреннего блока и обрабатывает код отступов.
custom_compare
Блок custom_compare
не имеет входных операторов.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
Вам нужно вызвать statementToCode
только для внутреннего блока, напрямую подключенного к входу оператора. statementToCode
обрабатывает любые дополнительные блоки, прикрепленные к первому блоку.
Создайте и верните строку кода
После того как вы получили код для полей и внутренних блоков, создайте и верните строку кода для вашего блока. То, что вы возвращаете, зависит от типа вашего блока:
Блоки значений: возвращают массив, содержащий строку кода и приоритет самого слабого оператора в вашем коде.
valueToCode
использует это, чтобы решить, нужно ли заключать ваш код в круглые скобки, когда ваш блок используется в качестве внутреннего блока. Дополнительные сведения см. в разделе Приоритет возврата .Блоки операторов: возвращают строку кода.
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;
}
Если вы используете код внутреннего блока значений несколько раз в своей строке кода, вам следует кэшировать код из этого блока, чтобы избежать мелких ошибок и нежелательных побочных эффектов.
Полные генераторы кода
Для справки, вот полные генераторы кода для каждого блока:
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;
}