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:
- Importieren Sie einen Sprach-Code-Generator.
- Rufen Sie den Wert jedes Felds ab und wandeln Sie ihn in einen Codestring um.
- Ruft die von inneren Blöcken generierten Code-Strings ab. Innere Blöcke sind Blöcke, die an Wert- und Anweisungseingaben angehängt sind.
- 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_compareist ein Wertblock mit einer Werteingabe namensLEFT, einem Drop-down-Feld namensOPERATORund einem numerischen Feld namensRIGHT. Es wird ein Ausdrucksstring der Form'0 = 0'generiert.
custom_ifist ein Anweisungsblock mit einem Kontrollkästchenfeld namensNOT, einer Werteingabe namensCONDITIONund einer Anweisungseingabe namensTHEN. Es wird ein Anweisungsstring der Form'if (...) {\n...\n};\n'generiert.
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
CONDITIONein Variablenblock ist, werden durchvalueToCodekeine Klammern hinzugefügt, da der NOT-Operator direkt auf eine Variable angewendet werden kann (!myBoolean). - Wenn
CONDITIONein Vergleichsblock ist, werden durchvalueToCodeKlammern 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.
valueToCodeverwendet 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;
}