Ein Block-Codegenerator ist eine Funktion, die den Code für einen Block generiert und als String zurückgibt. Welcher Code von einem Block generiert wird, hängt von seinem Typ ab:
- Werteblöcke haben eine Ausgabeverbindung. Diese Blöcke verhalten sich wie Ausdrücke in einer textbasierten Sprache und generieren Strings, die Ausdrücke enthalten.
- Anweisungsböcke sind Blöcke ohne Ausgabeverbindung. Diese Blöcke verhalten sich wie Anweisungen in einer textbasierten Sprache und generieren Strings, die Anweisungen enthalten.
Block-Code-Generator schreiben
Für jeden benutzerdefinierten Block, den Sie erstellen, müssen Sie einen Block-Codegenerator für jede Sprache schreiben, die Sie unterstützen möchten. Alle Block-Code-Generatoren sind in JavaScript geschrieben, auch wenn sie Code in einer anderen Sprache generieren.
Alle Block-Code-Generator führen die folgenden Schritte aus:
- Importieren Sie einen Sprachcodegenerator.
- Rufen Sie den Wert jedes Felds ab und wandeln Sie ihn in einen Codestring um.
- Hier finden Sie die Codestrings, die von inneren Blöcken generiert werden. Das sind Blöcke, die an Werte- und Anweisungseingaben angehängt sind.
- Erstelle den Codestring des Blocks und gib 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 Kästchenfeld namensNOT, einer Werteingabe namensCONDITIONund einer Anweisungs-Eingabe namensTHEN. Es wird ein Anweisungsstring der Form'if (...) {\n...\n};\n'generiert.
In diesem Dokument werden die Generatoren Schritt für Schritt erstellt. Die ausgefüllten Generatoren finden Sie am Ende dieses Dokuments.
Diese Blöcke dienen nur der Veranschaulichung der Codegenerierung. Verwenden Sie in einer echten Anwendung die integrierten Blöcke logic_compare und controls_if.
Sprachcodegenerator importieren
Sie können einen Sprachcodegenerator mit einer der folgenden Methoden importieren. Verwenden Sie den importierten Generator, um Block-Codegeneratoren 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 dem Einfügen von 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 Scripts
Sie müssen den Generator nach dem Einfügen von 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. Textfelder geben beispielsweise genau den vom Nutzer eingegebenen Text zurück, Drop-down-Felder jedoch einen sprachneutralen String, der dem vom Nutzer ausgewählten Element zugeordnet ist. Weitere Informationen finden Sie in der Dokumentation zu vordefinierten Feldern.
Je nach Feld müssen Sie den zurückgegebenen Wert möglicherweise transformieren, bevor Sie ihn in 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 enthält beispielsweise einen inneren Wertblock 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 aus inneren Blöcken erhalten, sofort einsatzbereit und muss nicht umgewandelt werden.
Innere Wertblöcke
Wenn Sie Code aus einem inneren Block abrufen möchten, der an eine Werteingabe angehängt ist, rufen Sie valueToCode auf.
Diese Methode ruft den Codegenerator des inneren Blocks auf.
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 werden soll. So kann valueToCode bestimmen, ob der Code des inneren Blocks in Klammern gesetzt werden muss.
Wenn Sie beispielsweise das Kästchen NOT in custom_if anklicken, wird der logische Operator „Nicht“ (!) auf die Bedingung angewendet. In diesem Fall übergeben Sie die Priorität des Not-Operators (Order.LOGICAL_NOT) an valueToCode und valueToCode vergleicht dies mit der Priorität des schwächsten Operators im inneren Block. Der Code des inneren Blocks wird dann nach Bedarf umgebrochen:
- 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, fügtvalueToCodeKlammern hinzu, damit der Operator „nicht“ auf den gesamten Vergleich (!(a < b)) anstelle des linken Werts (!a < b) angewendet wird.
Sie müssen nicht wissen, ob valueToCode Klammern hinzugefügt hat. Sie müssen lediglich die Priorität an valueToCode übergeben und den zurückgegebenen Code in Ihren Codestring einfügen. Weitere Informationen finden Sie unter Vorrang von „valueToCode“.
Innere Anweisungsböcke
Wenn Sie Code aus einem inneren Block abrufen möchten, der an eine Anweisung angehängt ist, rufen Sie statementToCode auf. Diese Methode ruft den Codegenerator des inneren Blocks auf und kümmert sich um das Einrücken des Codes.
custom_compare
Der custom_compare-Block enthält 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 Anweisungs-Eingabe verbunden ist. statementToCode verarbeitet alle zusätzlichen Blöcke, die mit dem ersten Block verbunden sind.
Codestring erstellen und zurückgeben
Nachdem du den Code für die Felder und inneren Blöcke erhalten hast, musst du den Codestring für deinen Block erstellen und zurückgeben. Was genau Sie zurückgeben, hängt vom Blocktyp ab:
Wertblöcke:Gibt ein Array mit dem Codestring und der Priorität des schwächsten Operators in Ihrem Code zurück. Anhand dieser Information entscheidet
valueToCode, ob Ihr Code in Klammern gesetzt werden muss, wenn er als innerer Block verwendet wird. Weitere Informationen finden Sie unter Rückgabevorrang.Statement-Blöcke:Geben 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 Nebenwirkungen zu vermeiden.
Komplette Codegeneratoren
Hier finden Sie die vollständigen Codegeneratoren für jeden Block:
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;
}