Order 列舉、valueToCode 傳遞規則、最小括號原則
積木本身「隱含括號」:下面的積木代表 -(5 + 2) 而不是 -5 + 2:
如果每個內嵌積木都包括號,會得到 ((5) * (2)) + (3)——正確但難以閱讀。Blockly 用運算子優先序(precedence)只在「必要」時加括號。
每個語言生成器都定義自己的 Order 列舉(對應該語言的真實優先序),例如 JavaScript 的 Order 含 ADDITION、MULTIPLICATION、RELATIONAL、LOGICAL_NOT…
| Order 值 | 意義 | 使用時機 |
|---|---|---|
| Order.ATOMIC | 最強(原子值) | 想「永遠加括號」時傳給 valueToCode;積木無運算子時回傳 |
| Order.NONE | 最弱 | 想「永遠加括號」時回傳;無運算子作用於內嵌積木時傳入 |
| 其他 | 各語言真實優先序 | 依你的積木使用了哪種運算子選擇 |
呼叫 generator.valueToCode(block, 'NAME', order) 時,傳入的 order 是你的積木中作用於該內嵌值的最強運算子——也就是內嵌碼需要「被保護不被拆散」的那個運算子。
// 積木:-(內嵌值) + 2
// 作用於內嵌值的運算子:負號(UNARY_NEGATION)與加法(ADDITION)
// 負號最強 → 傳給 valueToCode
const innerCode = generator.valueToCode(block, 'INNER', Order.UNARY_NEGATION);
const code = `-${innerCode} + 2`;
// 本積木內最弱的運算是加法 → 回傳 ADDITION
return [code, Order.ADDITION];
回傳的 order 是積木內部最弱的運算子——也就是最需要被保護的那個。這樣外層積木在包這塊積木時,才知道要不要加括號。
// 積木內容:-x + 2(有負號與加法兩個運算子) // 加法比負號弱 → 回傳 ADDITION return [code, Order.ADDITION]; // 若被「乘法積木」當內嵌:2 * (-x + 2) // MULTIPLICATION 比 ADDITION 強 → 自動加括號 → 正確! // 若被「負號積木」當內嵌:-(-x + 2) // 也是 UNARY_NEGATION 較強 → 括號保護 → 正確
valueToCode 都傳 Order.ATOMIC,所有生成器都回傳 Order.NONE → 每個內嵌都被括號包住(可讀性差但絕對正確)。最小括號:5 * 2 + 3 永遠括號:((5) * (2)) + (3) // 兩者都 = 13,但前者可讀性高得多
問題:下面的生成器回傳 [code] 而非 [code, order]:
javascriptGenerator.forBlock['negate_plus_two'] = function(block, generator) {
const inner = generator.valueToCode(block, 'INNER', Order.ATOMIC);
const code = '-' + inner + ' + 2';
return code; // ✗ 沒回傳 order
};
// 當它被乘法積木內嵌 → 生成 "2 * -x + 2"(錯!應是 2 * (-x + 2))
修正:
return [code, Order.ADDITION]; // ✓ 告知外界「我內部最弱是加法」
驗證:
乘積木內嵌時 → valueToCode 收到 ADDITION 弱於 MULTIPLICATION
→ 自動包括號 → "2 * (-x + 2)" ✓