Un generatore di codice a blocchi è una funzione che genera il codice per un blocco e lo restituisce come stringa. Il codice generato da un blocco dipende dal suo tipo:
- I blocchi di valori hanno una connessione di output. Questi blocchi fungono da espressioni in un linguaggio basato su testo e generano stringhe che contengono espressioni.
- I blocchi di istruzioni sono blocchi senza una connessione di output. Questi blocchi si comportano come istruzioni in un linguaggio basato sul testo e generano stringhe che contengono istruzioni.
Come scrivere un generatore di codice a blocchi
Per ogni blocco personalizzato che crei, devi scrivere un generatore di codice blocco per ogni lingua che vuoi supportare. Tieni presente che tutti i generatori di codice a blocchi sono scritti in JavaScript, anche se generano codice in un'altra lingua.
Tutti i generatori di blocchi di codice eseguono i seguenti passaggi:
- Importa un generatore di codici di lingua.
- Ottieni il valore di ogni campo e trasformalo in una stringa di codice.
- Ottieni le stringhe di codice generate dai blocchi interni, ovvero i blocchi collegati agli input di valore e istruzione.
- Crea e restituisci la stringa di codice del blocco.
Blocchi di esempio
Come esempi, scriveremo generatori di codice JavaScript per i seguenti blocchi.
custom_compareè un blocco di valori che ha un input di valore denominatoLEFT, un campo a discesa denominatoOPERATORe un campo numerico denominatoRIGHT. Genera una stringa di espressione nel formato'0 = 0'.
custom_ifè un blocco di istruzioni che include un campo casella di controllo denominatoNOT, un input valore denominatoCONDITIONe un input istruzione denominatoTHEN. Genera una stringa di istruzione nel formato'if (...) {\n...\n};\n'.
Questo documento crea i generatori passo dopo passo. Puoi trovare i generatori completati alla fine di questo documento.
Tieni presente che questi blocchi hanno solo lo scopo di illustrare la generazione del codice. In un'applicazione
reale, utilizza i blocchi logic_compare e controls_if integrati.
Importare un generatore di codici di lingua
Puoi importare un generatore di codici di lingua utilizzando uno dei seguenti metodi. Utilizza
il generatore importato per archiviare i generatori di codice a blocchi nell'oggetto forBlock.
Moduli
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
Devi includere il generatore dopo aver incluso 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) { /* ... */ };
Script locali
Devi includere il generatore dopo aver incluso 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) { /* ... */ };
Ottieni valori dei campi
I campi consentono agli utenti di inserire valori come stringhe, numeri e colori. Per ottenere il valore di un campo, chiama getFieldValue. I valori restituiti variano da campo a campo. Ad esempio, i campi di testo restituiscono il testo esatto inserito dall'utente, mentre
i campi a discesa restituiscono una stringa indipendente dalla lingua associata all'elemento selezionato
dall'utente. Per saperne di più, consulta la documentazione relativa ai campi
integrati.
A seconda del campo, potrebbe essere necessario trasformare il valore restituito prima di utilizzarlo nel codice.
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' ? '!' : '';
...
}
Per saperne di più, consulta Trasformare i valori dei campi.
Ottieni il codice dai blocchi interni
I blocchi interni sono i blocchi collegati ai valori e alle istruzioni di un blocco.
Ad esempio, il blocco custom_if ha un blocco interno di valore per la condizione if e blocchi interni di istruzione per il codice eseguito se la condizione è vera.
A differenza dei valori dei campi, il codice ottenuto dai blocchi interni è pronto per l'uso e non deve essere trasformato.
Blocchi di valori interni
Per ottenere il codice da un blocco interno collegato a un input di valore, chiama valueToCode.
Questo metodo chiama il generatore di codice del blocco interno.
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';
...
}
Quando chiami valueToCode, devi indicare l'operatore più forte nel
tuo codice che verrà applicato al codice del blocco interno. Ciò consente a valueToCode
di determinare se è necessario racchiudere il codice del blocco interno tra parentesi.
Ad esempio, selezionando la casella NOT in custom_if viene applicato un operatore logico NOT (!) alla condizione. In questo caso, passi la precedenza dell'operatore NOT (Order.LOGICAL_NOT) a valueToCode e valueToCode la confronta con la precedenza dell'operatore più debole nel blocco interno. Poi esegue il wrapping
del codice del blocco interno in base alle esigenze:
- Se
CONDITIONè un blocco variabile,valueToCodenon aggiunge parentesi perché l'operatore NOT può essere applicato direttamente a una variabile (!myBoolean). - Se
CONDITIONè un blocco di confronto,valueToCodeaggiunge le parentesi in modo che l'operatore NOT si applichi all'intero confronto (!(a < b)) anziché al valore a sinistra (!a < b).
Non è necessario sapere se valueToCode ha aggiunto le parentesi. Tutto ciò che devi fare è passare la precedenza a valueToCode e aggiungere il codice restituito alla stringa di codice. Per ulteriori informazioni, vedi precedenza di valueToCode.
Blocchi di istruzioni interni
Per ottenere il codice da un blocco interno collegato a un input di istruzione, chiama
statementToCode. Questo metodo chiama il generatore di codice del blocco interno e
gestisce l'indentazione del codice.
custom_compare
Il blocco custom_compare non ha input di istruzioni.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
Devi chiamare statementToCode solo per il blocco interno collegato direttamente
a un input di istruzione. statementToCode gestisce eventuali blocchi aggiuntivi collegati
al primo blocco.
Crea e restituisci la stringa di codice
Dopo aver ottenuto il codice per i campi e i blocchi interni, crea e restituisci la stringa di codice per il blocco. Ciò che restituisci dipende dal tipo di blocco:
Blocchi di valori:restituisce un array contenente la stringa di codice e la precedenza dell'operatore più debole nel codice.
valueToCodelo utilizza per decidere se il codice deve essere racchiuso tra parentesi quando il blocco viene utilizzato come blocco interno. Per saperne di più, consulta Precedenza del reso.Blocchi di istruzioni:restituiscono la stringa di codice.
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;
}
Se utilizzi più volte il codice di un blocco di valori interni nella stringa di codice, devi memorizzare nella cache il codice di quel blocco per evitare bug sottili ed effetti collaterali indesiderati.
Generatori di codici completi
Per riferimento, ecco i generatori di codice completi per ogni blocco:
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;
}