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. Te bloki działają 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. Te bloki działają jak instrukcje w języku tekstowym i generują ciągi znaków zawierające instrukcje.
Jak napisać generator kodu blokowego
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 języku JavaScript, nawet jeśli generują kod w innym języku.
Wszystkie generatory kodu blokowego wykonują te czynności:
- Importowanie generatora kodów językowych
- Pobierz wartość każdego pola i przekształć ją w ciąg kodu.
- Pobieraj ciągi znaków kodu wygenerowane przez bloki wewnętrzne, czyli bloki dołączone do wartości i wejść instrukcji.
- Tworzy i zwraca ciąg znaków kodu bloku.
Przykładowe bloki
W ramach przykładów napiszemy generatory kodu JavaScript dla tych bloków.
custom_compare
to blok wartości z polem wejściowym wartości o nazwieLEFT
, polem menu o nazwieOPERATOR
i polem liczbowym o nazwieRIGHT
. Generuje ciąg tekstowy wyrażenia w postaci'0 = 0'
.custom_if
to blok instrukcji zawierający pole wyboru o nazwieNOT
, pole wartości o nazwieCONDITION
i pole instrukcji o nazwieTHEN
. Generuje ciąg instrukcji w formie'if (...) {\n...\n};\n'
.
W tym dokumencie znajdziesz instrukcje tworzenia generatorów krok po kroku. Wygenerowane generatory znajdziesz na końcu tego dokumentu.
Pamiętaj, że te bloki mają tylko na celu zilustrowanie generowania kodu. W prawdziwej aplikacji użyj wbudowanych bloków logic_compare
i controls_if
.
Importowanie generatora kodów językowych
Generator kodu językowego możesz zaimportować na jeden z tych sposobów: Użyj zaimportowanego generatora do przechowywania generatorów kodu blokowego 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
Musisz uwzględnić generator po uwzględnieniu 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
Musisz uwzględnić generator po uwzględnieniu 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 wpisywanie wartości, takich jak ciągi znaków, liczby i kolory. Aby uzyskać wartość pola, wywołaj funkcję getFieldValue
. Zwracane wartości różnią się w zależności od pola. Na przykład pola tekstowe zwracają dokładnie tekst wpisany przez użytkownika, ale pola menu zwracają ciąg znaków neutralnych pod względem języka powiązanych z wybranym przez użytkownika elementem. Więcej informacji znajdziesz w dokumentacji wbudowanych pól.
W zależności od pola przed użyciem wartości zwróconej w kodzie może być konieczne jej przekształcenie.
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 połączone z wartością bloku i danymi wejściowymi instrukcji.
Na przykład blok custom_if
zawiera blok wewnętrzny z wartością dla warunku if oraz bloki wewnętrzne z oświadczeniem dla kodu, który jest wykonywany, gdy warunek jest spełniony.
W przeciwieństwie do wartości pól kod uzyskiwany z bloków wewnętrznych jest gotowy do użycia i nie wymaga przekształcania.
Bloki wartości wewnętrznych
Aby uzyskać kod z bloku wewnętrznego dołączonego do wartości wejściowej, 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łasz funkcję valueToCode
, musisz podać jej najsilniejszy operator w Twoim kodzie, który ma być stosowany w kodzie bloku wewnętrznego. Dzięki temu valueToCode
może określić, czy musi objąć kod bloku wewnętrznego nawiasami.
Na przykład zaznaczenie pola NOT
w pozycji custom_if
powoduje zastosowanie do warunku operatora logicznego „nie” (!
). W tym przypadku przekazujesz priorytet operatora „nie” (Order.LOGICAL_NOT
) do valueToCode
, a valueToCode
porównuje to z priorytetem najsłabszego operatora w bloku wewnętrznym. Następnie otacza kod bloku wewnętrznego w razie potrzeby:
- Jeśli
CONDITION
jest blokiem zmiennej, funkcjavalueToCode
nie dodaje nawiasów, ponieważ operator not może być stosowany bezpośrednio do zmiennej (!myBoolean
). - Jeśli
CONDITION
to blok porównania,valueToCode
dodaje nawiasy, aby operator not miał zastosowanie do całego porównania (!(a < b)
), a nie do wartości po lewej stronie (!a < b
).
Nie musisz wiedzieć, czy valueToCode
dodała nawiasy. Wystarczy, że przekażesz priorytet do valueToCode
i dodasz zwrócony kod do ciągu kodu. Więcej informacji znajdziesz w sekcji Kolejność stosowania funkcji valueToCode.
Bloki instrukcji wewnętrznych
Aby uzyskać kod z bloku wewnętrznego dołączonego do wejścia instrukcji, wywołaj funkcję statementToCode
. Ta metoda wywołuje generator kodu bloku wewnętrznego i obsługuje wcięcie kodu.
custom_compare
Blok custom_compare
nie ma żadnych danych wejściowych instrukcji.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
Funkcję statementToCode
należy wywoływać tylko w przypadku bloku wewnętrznego bezpośrednio połączonego z wejściem instrukcji. statementToCode
obsługuje wszystkie dodatkowe bloki dołączone do pierwszego bloku.
Tworzenie i zwracanie ciągu znaków kodu
Po uzyskaniu kodu dla pól i bloków wewnętrznych utwórz i zwróć ciąg kodu dla bloku. Dokładny typ bloku zależy od typu bloku:
Bloki wartości: zwraca tablicę zawierającą ciąg kodu i pierwszeństwo najsłabszego operatora w kodzie.
valueToCode
używa tego parametru, aby określić, czy kod musi być otoczony nawiasami, gdy blok jest używany jako blok wewnętrzny. Więcej informacji znajdziesz w artykule Kolejność zwracania wartości.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 swojej łańcuchu kodu używasz kodu bloku wartości wewnętrznej kilka razy, zapisz w pamięci podręcznej kod z tego bloku, aby uniknąć drobnych błędów i niepożądanych efektów ubocznych.
Generatory kodu
Poniżej znajdziesz linki do generatorów kodu dla poszczególnych bloków:
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;
}