區塊程式碼產生器是一種函式,可產生區塊的程式碼,並以字串形式傳回。區塊產生的程式碼取決於區塊類型:
- 值區塊具有輸出連結。這些區塊的運作方式就像文字語言中的運算式,會產生包含運算式的字串。
- 陳述式區塊是指沒有輸出連結的區塊。這些區塊會像文字語言中的陳述式一樣運作,並產生包含陳述式的字串。
如何編寫區塊程式碼產生器
針對您建立的每個自訂區塊,您必須為每種要支援的語言編寫區塊程式碼產生器。請注意,即使產生的程式碼是其他語言,所有區塊程式碼產生器都是以 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) { /* ... */ };
Unpkg
您必須在加入 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 條件值內部區塊,以及陳述式內部區塊,用於在條件為 true 時執行的程式碼。
與欄位值不同,從內部區塊取得的程式碼已可使用,不需要轉換。
內部值區塊
如要從值輸入內容附加的內部區塊取得程式碼,請呼叫 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 程式碼中適用於內部區塊程式碼的最高強度運算子。這可讓 valueToCode 判斷是否需要在內部區塊的程式碼中加上括號。
舉例來說,勾選 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;
}