Generator kodu bloku to funkcja, która generuje kod bloku i zwraca go jako ciąg znaków. Kod generowany przez blok zależy od jego typu:
- Bloki wartości mają połączenie wyjściowe. Działają one jak wyrażenia w języku tekstowym i generują ciągi znaków zawierające wyrażenia.
- Bloki instrukcji to bloki bez połączenia wyjściowego. Działają one jak instrukcje w języku tekstowym i generują ciągi znaków zawierające instrukcje.
Jak napisać generator kodu bloku
W przypadku każdego utworzonego bloku niestandardowego musisz napisać generator kodu bloku dla każdego języka, który chcesz obsługiwać. Pamiętaj, że wszystkie generatory kodu blokowego są napisane w JavaScript, nawet jeśli generują kod w innym języku.
Wszystkie generatory kodu bloku wykonują te czynności:
- Importują generator kodu języka.
- Pobierają wartość każdego pola i przekształcają ją w ciąg kodu.
- Pobierają ciągi kodu wygenerowane przez bloki wewnętrzne, czyli bloki dołączone do danych wejściowych wartości i instrukcji.
- Tworzą i zwracają ciąg kodu bloku.
Przykładowe bloki
Jako przykłady napiszemy generatory kodu JavaScript dla tych bloków.
custom_compareto blok wartości, który ma dane wejściowe wartości o nazwieLEFT, pole listy rozwijanej o nazwieOPERATORi pole numeryczne o nazwieRIGHT. Generuje ciąg wyrażenia w postaci'0 = 0'.
custom_ifto blok instrukcji, który ma pole wyboru o nazwieNOT, dane wejściowe wartości o nazwieCONDITIONi dane wejściowe instrukcji o nazwieTHEN. Generuje ciąg instrukcji w postaci'if (...) {\n...\n};\n'.
W tym dokumencie generatory są tworzone krok po kroku. Gotowe generatory znajdziesz na końcu tego dokumentu.
Pamiętaj, że te bloki służą tylko do ilustrowania generowania kodu. W prawdziwej aplikacji używaj wbudowanych bloków logic_compare i controls_if.
Importowanie generatora kodu języka
Generator kodu języka możesz zaimportować na jeden z tych sposobów. Użyj zaimportowanego generatora, aby przechowywać generatory kodu bloku w obiekcie forBlock.
Moduły
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
Generator musisz dołączyć po dołączeniu 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) { /* ... */ };
Skrypty lokalne
Generator musisz dołączyć po dołączeniu 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) { /* ... */ };
Pobieranie wartości pól
Pola umożliwiają użytkownikom wprowadzanie wartości takich jak ciągi znaków, liczby i kolory. Aby pobrać wartość pola, wywołaj funkcję getFieldValue. Zwracana wartość różni się w zależności od pola. Na przykład pola tekstowe zwracają dokładny tekst wpisany przez użytkownika, ale pola listy rozwijanej zwracają ciąg znaków niezależny od języka powiązany z elementem wybranym przez użytkownika. Więcej informacji znajdziesz w dokumentacji wbudowanych
pól.
W zależności od pola może być konieczne przekształcenie zwróconej wartości przed użyciem jej w kodzie.
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' ? '!' : '';
...
}
Więcej informacji znajdziesz w artykule Przekształcanie wartości pól.
Pobieranie kodu z bloków wewnętrznych
Bloki wewnętrzne to bloki dołączone do danych wejściowych wartości i instrukcji bloku.
Na przykład blok custom_if ma wewnętrzny blok wartości dla warunku if oraz wewnętrzne bloki instrukcji dla kodu, który jest wykonywany, jeśli warunek jest prawdziwy.
W przeciwieństwie do wartości pól kod pobierany z bloków wewnętrznych jest gotowy do użycia i nie wymaga przekształcenia.
Wewnętrzne bloki wartości
Aby pobrać kod z bloku wewnętrznego dołączonego do danych wejściowych wartości, wywołaj funkcję valueToCode.
Ta metoda wywołuje generator kodu bloku wewnętrznego.
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';
...
}
Gdy wywołujesz funkcję valueToCode, musisz poinformować ją o najsilniejszym operatorze w kodzie, który będzie stosowany do kodu bloku wewnętrznego. Dzięki temu funkcja valueToCode może określić, czy kod bloku wewnętrznego należy umieścić w nawiasach.
Na przykład zaznaczenie pola wyboru NOT w bloku custom_if powoduje zastosowanie do warunku operatora logicznego NOT (!). W takim przypadku przekazujesz do funkcji valueToCode pierwszeństwo operatora NOT (Order.LOGICAL_NOT), a funkcja valueToCode porównuje je z pierwszeństwem najsłabszego operatora w bloku wewnętrznym. Następnie w razie potrzeby umieszcza kod bloku wewnętrznego w nawiasach:
- Jeśli
CONDITIONjest blokiem zmiennej, funkcjavalueToCodenie dodaje nawiasów, ponieważ operator NOT można zastosować bezpośrednio do zmiennej (!myBoolean). - Jeśli
CONDITIONjest blokiem porównania,valueToCodedodaje nawiasy, aby operator NOT był stosowany do całego porównania (!(a < b)) zamiast do wartości po lewej stronie (!a < b).
Nie musisz wiedzieć, czy funkcja valueToCode dodała nawiasy. Wystarczy, że przekażesz pierwszeństwo do funkcji valueToCode i dodasz zwrócony kod do ciągu kodu. Więcej informacji znajdziesz w artykule valueToCode
precedence.
Wewnętrzne bloki instrukcji
Aby pobrać kod z bloku wewnętrznego dołączonego do danych wejściowych instrukcji, wywołaj funkcję statementToCode. Ta metoda wywołuje generator kodu bloku wewnętrznego i obsługuje wcięcia w kodzie.
custom_compare
Blok custom_compare nie ma danych wejściowych instrukcji.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
Funkcję statementToCode musisz wywołać tylko w przypadku bloku wewnętrznego bezpośrednio połączonego z danymi wejściowymi instrukcji. Funkcja statementToCode obsługuje wszystkie dodatkowe bloki dołączone do pierwszego bloku.
Tworzenie i zwracanie ciągu kodu
Po pobraniu kodu dla pól i bloków wewnętrznych utwórz i zwróć ciąg kodu dla swojego bloku. To, co zwracasz, zależy od typu bloku:
Bloki wartości: zwracają tablicę zawierającą ciąg kodu i pierwszeństwo najsłabszego operatora w kodzie. Funkcja
valueToCodeużywa tego do określenia, czy kod należy umieścić w nawiasach, gdy blok jest używany jako blok wewnętrzny. Więcej informacji znajdziesz w artykule Return precedence.Bloki instrukcji: zwracają ciąg kodu.
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;
}
Jeśli w ciągu kodu używasz kodu wewnętrznego bloku wartości wiele razy, warto zapisać w pamięci podręcznej kod z tego bloku aby uniknąć subtelnych błędów i niepożądanych efektów ubocznych.
Gotowe generatory kodu
Dla porównania podajemy gotowe generatory kodu dla każdego bloku:
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;
}