Un generatore di codice di blocco è 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 si comportano come espressioni in un linguaggio basato su testo e generano stringhe contenenti espressioni.
- I blocchi di istruzioni sono blocchi senza una connessione di output. Questi blocchi agiscono come istruzioni in un linguaggio basato su testo e generano stringhe contenenti istruzioni.
Come scrivere un generatore di codice a blocchi
Per ogni blocco personalizzato creato, devi scrivere un generatore di codice blocco per ogni lingua che vuoi supportare. Tieni presente che tutti i generatori di codice di blocco sono scritti in JavaScript, anche se generano codice in un altro linguaggio.
Tutti i generatori di codice blocchi eseguono i seguenti passaggi:
- Importa un generatore di codici di lingua.
- Recupera il valore di ogni campo e trasformalo in una stringa di codice.
- Ottieni le stringhe di codice generate dai blocchi interni, ovvero blocchi collegati agli input di valore e di istruzioni.
- Crea e restituisce 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 con un input di valore denominatoLEFT
, un campo a discesa denominatoOPERATOR
e un campo numerico denominatoRIGHT
. Genera una stringa di espressione del tipo'0 = 0'
.custom_if
è un blocco di istruzioni con un campo di casella di controllo denominatoNOT
, un input di valore denominatoCONDITION
e un input di istruzioni denominatoTHEN
. Genera una stringa di istruzioni del tipo'if (...) {\n...\n};\n'
.
Questo documento illustra la creazione dei generatori passo passo. Puoi trovare i generatori completati alla fine di questo documento.
Tieni presente che questi blocchi hanno lo scopo di illustrare la generazione di codice. In un'applicazione reale, utilizza i blocchi logic_compare
e controls_if
integrati.
Importa un generatore di codici di lingua
Puoi importare un generatore di codici 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) { /* ... */ };
Ottenere i 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 sono diversi da campo a campo. Ad esempio, i campi di testo restituiscono il testo esatto inserito dall'utente, ma i campi di menu a discesa restituiscono una stringa neutra per la 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 usarlo 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 ulteriori informazioni, consulta Trasformare i valori del campo.
Recuperare il codice dai blocchi interni
I blocchi interni sono i blocchi collegati ai valori e agli input delle istruzioni di un blocco.
Ad esempio, il blocco custom_if
ha un blocco interno di valore per la condizione if e blocchi di istruzioni interni per il codice eseguito se la condizione è vera.
A differenza dei valori di campo, il codice che ottieni dai blocchi interni è pronto per l'uso e non deve essere trasformato.
Blocchi di valori interni
Per ottenere il codice da un blocco interno associato 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
codice che verrà applicato al codice del blocco interno. In questo modo, valueToCode
può determinare se è necessario racchiudere il codice del blocco interno tra parentesi.
Ad esempio, la selezione della casella NOT
in custom_if
applica un operatore di negazione logica (!
) 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. Quindi inserisce un a capo nel codice del blocco interno in base alle esigenze:
- Se
CONDITION
è un blocco di variabili,valueToCode
non aggiunge le parentesi perché l'operatore not può essere applicato direttamente a una variabile (!myBoolean
). - Se
CONDITION
è un blocco di confronto,valueToCode
aggiunge le parentesi in modo che l'operatore di negazione si applichi all'intero confronto (!(a < b)
) anziché al valore a sinistra (!a < b
).
Non è necessario sapere se valueToCode
ha aggiunto le parentesi. Devi solo passare la precedenza a valueToCode
e aggiungere il codice restituito alla stringa di codice. Per ulteriori informazioni, consulta la precedenza di valueToCode.
Blocchi di istruzioni interne
Per recuperare il codice da un blocco interno associato all'input di un'istruzione, chiama
statementToCode
. Questo metodo chiama il generatore di codice del blocco interno e gestisce il codice di rientro.
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 all'input di un'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. I valori restituiti dipendono dal tipo di blocco:
Blocchi di valore:restituisce un array contenente la stringa di codice e la precedenza dell'operatore più debole nel codice.
valueToCode
lo utilizza per decidere se il codice deve essere racchiuso tra parentesi quando il blocco viene utilizzato come blocco interno. Per ulteriori informazioni, consulta Precedenza del ritorno.Blocchi di istruzioni: restituisce 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 il codice di un blocco di valori interni più volte nella stringa di codice, devi memorizzare nella cache il codice di quel blocco per evitare bug sottili ed effetti collaterali indesiderati.
Generatori di codice completi
Come riferimento, di seguito sono riportati 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;
}