單元 21 · Blockly 程式碼生成器深入

workspaceToCode 管線、forBlock、value/statement 生成器

21.1 生成程式碼的完整管線

Blockly 不能「直接執行」積木——它把積木轉成文字程式碼字串,再由你的應用決定如何執行(eval、送後端、燒進硬體)。整條管線:

workspace 上的積木blockToCodeblock-code generatorcode 字串執行/輸出
入口 API
// 一次生成整個工作區的程式碼
const code = Blockly.JavaScript.workspaceToCode(workspace);

// 只生成某一塊積木(與其內嵌積木)
const block = workspace.getBlockById('abc123');
const code2 = Blockly.JavaScript.blockToCode(block);
關鍵架構:「語言生成器(language generator)」管語言通用規則(縮排、字串引號、註解);「積木生成器(block-code generator)」管單一積木。Blockly 內建 5 種語言生成器:JavaScript、Python、Lua、Dart、PHP

21.2 兩種積木、兩種回傳格式

積木類型有無 output 連接對應語言概念生成器回傳
Value block有(回傳值)表達式(expression)[code, order] 陣列
Statement block無(執行指令)陳述句(statement)code 字串
最常見的錯:value block 生成器若只回傳字串、不回傳 `[code, order]`,Blockly 無法幫你自動加括號(見單元 22),產出的程式碼可能在算術上錯誤。

21.3 生成器四步驟

  1. import 語言生成器:`import {javascriptGenerator} from 'blockly/javascript'`。
  2. 讀取欄位值:`block.getFieldValue('OPERATOR')`,並轉成程式碼用值。
  3. 取得內嵌積木的程式碼:value 輸入用 generator.valueToCode(block, 'NAME', order);statement 輸入用 generator.statementToCode(block, 'NAME')
  4. 組字串並回傳:value block 回傳 [code, order];statement block 回傳 code

21.4 Worked Example:custom_compare(value block)

完整範例
import {javascriptGenerator, Order} from 'blockly/javascript';

// 定義積木:LEFT (value) OPERATOR (dropdown) RIGHT (number)
Blockly.common.defineBlocksWithJsonArray([{
  type: 'custom_compare',
  message0: '%1 %2 %3',
  args0: [
    {type: 'input_value', name: 'LEFT', check: 'Number'},
    {type: 'field_dropdown', name: 'OPERATOR',
     options: [['=', 'EQUALS'], ['<', 'LESS'], ['>', 'GREATER']]},
    {type: 'input_value', name: 'RIGHT', check: 'Number'}
  ],
  output: 'Boolean',
  colour: 210
}]);

// 生成器:把 dropdown 的語言中立值轉成 JS 運算子
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 = generator.valueToCode(block, 'RIGHT', order);
  const code = left + ' ' + operator + ' ' + right;
  return [code, order];   // ← value block 必須回傳 [code, order]
};
讀懂它:dropdown 回傳的是語言中立的字串(`EQUALS`),生成器負責「翻譯」成目標語言的真實運算子(`==`)。這是「積木 → 你要的語言」的核心動作。

21.5 Worked Example:custom_if(statement block)

完整範例
import {javascriptGenerator, Order} from 'blockly/javascript';

Blockly.common.defineBlocksWithJsonArray([{
  type: 'custom_if',
  message0: 'if %1 (negate: %2) then %3',
  args0: [
    {type: 'input_value', name: 'CONDITION', check: 'Boolean'},
    {type: 'field_checkbox', name: 'NOT', checked: false},
    {type: 'input_statement', name: 'THEN'}
  ],
  previousStatement: null,
  nextStatement: null,
  colour: 120
}]);

javascriptGenerator.forBlock['custom_if'] = function(block, generator) {
  const negate = block.getFieldValue('NOT') === 'TRUE' ? '!' : '';
  const order = negate ? 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;   // ← statement block 只回傳字串
};

21.6 statementToCode 的縮排

generator.statementToCode(block, 'THEN') 不只呼叫內嵌積木的生成器,還會自動縮排每一行、並串起「next」連接的後續積木——你不需要自己處理縮排。

產出
// 使用者拼:custom_if → (custom_compare → 內嵌 show_number)
if (x < 10) {
  console.log(x);
}
小結:生成器 = 「讀欄位 + 取內嵌 + 組字串 + 回傳(含 order)」。學會這四步,你就掌握了 Blockly 二次開發的核心。

21.7 練習

  1. 解釋 `workspaceToCode` 與 `blockToCode` 的差異。
  2. 為 `custom_compare` 寫 Python 生成器(`import {pythonGenerator}`)。
  3. 說出 value block 為何要回傳 `[code, order]`。
看完這單元你應該能說出:
  • 說明 workspaceToCode 的完整管線。
  • 分辨 value block 與 statement block 的生成器回傳格式。
  • 寫出完整 custom_compare 與 custom_if 的 block-code generator。

延伸閱讀