Generatory kodu blokowego

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:

  1. Importowanie generatora kodów językowych
  2. Pobierz wartość każdego pola i przekształć ją w ciąg kodu.
  3. Pobieraj ciągi znaków kodu wygenerowane przez bloki wewnętrzne, czyli bloki dołączone do wartości i wejść instrukcji.
  4. 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 nazwie LEFT, polem menu o nazwie OPERATOR i polem liczbowym o nazwie RIGHT. Generuje ciąg tekstowy wyrażenia w postaci '0 = 0'.

    Blok wartości niestandardowych do porównań.

  • custom_if to blok instrukcji zawierający pole wyboru o nazwie NOT, pole wartości o nazwie CONDITION i pole instrukcji o nazwie THEN. Generuje ciąg instrukcji w formie 'if (...) {\n...\n};\n'.

    Blok instrukcji niestandardowej dla instrukcji if. Użytkownicy mogą użyć pola wyboru, aby anulować warunek if.

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 valueToCodemoż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, funkcja valueToCode 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;
}