Blockcodegeneratoren

Ein Blockcodegenerator ist eine Funktion, die den Code für einen Block generiert und als String zurückgibt. Welchen Code ein Block generiert, hängt von seinem Typ ab:

  • Wertblöcke haben eine Ausgabeverbindung. Diese Blöcke funktionieren wie Ausdrücke in einer textbasierten Sprache und generieren Strings, die Ausdrücke enthalten.
  • Anweisungsblöcke sind Blöcke ohne Ausgabeverbindung. Diese Blöcke funktionieren wie Anweisungen in einer textbasierten Sprache und generieren Strings, die Anweisungen enthalten.

Blockcode-Generator schreiben

Für jeden benutzerdefinierten Block, den Sie erstellen, müssen Sie einen Blockcode-Generator für jede Sprache schreiben, die Sie unterstützen möchten. Beachten Sie, dass alle Blockcodegeneratoren in JavaScript geschrieben sind, auch wenn sie Code in einer anderen Sprache generieren.

Alle Blockcodegeneratoren führen die folgenden Schritte aus:

  1. Importieren Sie einen Sprach-Code-Generator.
  2. Rufen Sie den Wert jedes Felds ab und wandeln Sie ihn in einen Codestring um.
  3. Ruft die von inneren Blöcken generierten Code-Strings ab. Innere Blöcke sind Blöcke, die an Wert- und Anweisungseingaben angehängt sind.
  4. Erstellt den Codestring des Blocks und gibt ihn zurück.

Beispielblöcke

Als Beispiele schreiben wir JavaScript-Codegeneratoren für die folgenden Blöcke.

  • custom_compare ist ein Wertblock mit einer Werteingabe namens LEFT, einem Drop-down-Feld namens OPERATOR und einem numerischen Feld namens RIGHT. Es wird ein Ausdrucksstring der Form '0 = 0' generiert.

    Benutzerdefinierter Werteblock für Vergleiche.

  • custom_if ist ein Anweisungsblock mit einem Kontrollkästchenfeld namens NOT, einer Werteingabe namens CONDITION und einer Anweisungseingabe namens THEN. Es wird ein Anweisungsstring der Form 'if (...) {\n...\n};\n' generiert.

    Benutzerdefinierter Anweisungsblock für eine „if“-Anweisung. Nutzer können ein Kästchen verwenden, um die „if“-Bedingung zu negieren.

In diesem Dokument werden die Generatoren Schritt für Schritt erstellt. Die fertigen Generatoren finden Sie am Ende dieses Dokuments.

Beachten Sie, dass diese Bausteine nur zur Veranschaulichung der Codeerstellung dienen. Verwenden Sie in einer echten Anwendung die integrierten Blöcke logic_compare und controls_if.

Sprachcode-Generator importieren

Sie können einen Sprachcode-Generator mit einer der folgenden Methoden importieren. Verwenden Sie den importierten Generator, um Blockcodegeneratoren im forBlock-Objekt zu speichern.

Module

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

Sie müssen den Generator nach Blockly einfügen.

<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) { /* ... */ };

Lokale Skripts

Sie müssen den Generator nach Blockly einfügen.

<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) { /* ... */ };

Feldwerte abrufen

Über Felder können Nutzer Werte wie Strings, Zahlen und Farben eingeben. Rufen Sie getFieldValue auf, um den Wert eines Felds abzurufen. Was zurückgegeben wird, unterscheidet sich von Feld zu Feld. Bei Textfeldern wird beispielsweise der vom Nutzer eingegebene Text zurückgegeben, bei Drop-down-Feldern jedoch ein sprachneutraler String, der mit dem vom Nutzer ausgewählten Element verknüpft ist. Weitere Informationen finden Sie in der Dokumentation zu integrierten Feldern.

Je nach Feld müssen Sie den zurückgegebenen Wert möglicherweise transformieren, bevor Sie ihn im Code verwenden.

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' ? '!' : '';
  ...
}

Weitere Informationen finden Sie unter Feldwerte transformieren.

Code aus inneren Blöcken abrufen

Innere Blöcke sind die Blöcke, die an die Wert- und Anweisungseingaben eines Blocks angehängt sind. Der custom_if-Block hat beispielsweise einen inneren Block für die if-Bedingung und innere Anweisungsblöcke für den Code, der ausgeführt wird, wenn die Bedingung wahr ist.

Im Gegensatz zu Feldwerten ist der Code, den Sie von inneren Blöcken erhalten, sofort einsatzbereit und muss nicht transformiert werden.

Blöcke mit inneren Werten

Um Code aus einem inneren Block zu erhalten, der an eine Werteingabe angehängt ist, rufen Sie valueToCode auf. Mit dieser Methode wird der Codegenerator des inneren Blocks aufgerufen.

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';
  ...
}

Wenn Sie valueToCode aufrufen, müssen Sie den stärksten Operator in Ihrem Code angeben, der auf den Code des inneren Blocks angewendet wird. So kann valueToCode feststellen, ob der Code des inneren Blocks in Klammern gesetzt werden muss.

Wenn Sie beispielsweise das Kästchen NOT in custom_if markieren, wird der logische NOT-Operator (!) auf die Bedingung angewendet. In diesem Fall übergeben Sie die Priorität des NOT-Operators (Order.LOGICAL_NOT) an valueToCode. valueToCode vergleicht diese mit der Priorität des schwächsten Operators im inneren Block. Anschließend wird der Code des inneren Blocks nach Bedarf umschlossen:

  • Wenn CONDITION ein Variablenblock ist, werden durch valueToCode keine Klammern hinzugefügt, da der NOT-Operator direkt auf eine Variable angewendet werden kann (!myBoolean).
  • Wenn CONDITION ein Vergleichsblock ist, werden durch valueToCode Klammern hinzugefügt, sodass der NOT-Operator auf den gesamten Vergleich (!(a < b)) angewendet wird und nicht auf den linken Wert (!a < b).

Sie müssen nicht wissen, ob valueToCode Klammern hinzugefügt hat. Sie müssen nur die Priorität an valueToCode übergeben und den zurückgegebenen Code Ihrem Codestring hinzufügen. Weitere Informationen finden Sie unter Vorrang von „valueToCode“.

Innere Anweisungsblöcke

Um Code aus einem inneren Block abzurufen, der an eine Anweisungseingabe angehängt ist, rufen Sie statementToCode auf. Diese Methode ruft den Codegenerator des inneren Blocks auf und kümmert sich um das Einrücken von Code.

custom_compare

Der custom_compare-Block hat keine Anweisungseingaben.

custom_if

javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
  ...
  const statements = generator.statementToCode(block, 'THEN');
  ...
}

Sie müssen statementToCode nur für den inneren Block aufrufen, der direkt mit einer Anweisungseingabe verbunden ist. statementToCode verarbeitet alle zusätzlichen Blöcke, die an den ersten Block angehängt sind.

Code-String erstellen und zurückgeben

Nachdem Sie den Code für die Felder und inneren Blöcke erhalten haben, erstellen Sie den Codestring für Ihren Block und geben ihn zurück. Was genau Sie zurückgeben, hängt von Ihrem Blocktyp ab:

  • Wertblöcke:Gibt ein Array mit dem Codestring und der Priorität des schwächsten Operators in Ihrem Code zurück. valueToCode verwendet diese Informationen, um zu entscheiden, ob Ihr Code in Klammern eingeschlossen werden muss, wenn Ihr Block als innerer Block verwendet wird. Weitere Informationen finden Sie unter Rückgabevorrang.

  • Anweisungsblöcke:Gibt den Codestring zurück.

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;
}

Wenn Sie den Code eines inneren Wertblocks mehrmals in Ihrem Codestring verwenden, sollten Sie den Code aus diesem Block im Cache speichern, um subtile Fehler und unerwünschte Nebeneffekte zu vermeiden.

Vollständige Code-Generatoren

Hier finden Sie die vollständigen Codegeneratoren für die einzelnen Blöcke:

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;
}