מחולל קוד בלוק הוא פונקציה שיוצרת את הקוד של בלוק ומחזירה אותו כמחרוזת. הקוד שבלוק יוצר תלוי בסוג שלו:
- לבלוקים של ערכים יש חיבור פלט. הבלוקים האלה פועלים כמו ביטויים בשפה מבוססת-טקסט ומפיקים מחרוזות שמכילות ביטויים.
- בלוקים של הצהרות הם בלוקים ללא חיבור פלט. הבלוקים האלה פועלים כמו הצהרות בשפה מבוססת-טקסט, ומפיקים מחרוזות שמכילות הצהרות.
איך כותבים מחולל קוד בלוקים
לכל בלוק מותאם אישית שיוצרים, צריך לכתוב מחולל קוד בלוק לכל שפה שרוצים לתמוך בה. שימו לב שכל הגנרטורים של קוד בלוקים כתובים ב-JavaScript, גם אם הם יוצרים קוד בשפה אחרת.
כל הכלים ליצירת קוד בלוקים מבצעים את השלבים הבאים:
- מייבאים כלי ליצירת קודי שפה.
- מקבלים את הערך של כל שדה ומשנים אותו למחרוזת קוד.
- קבלת מחרוזות הקוד שנוצרו על ידי בלוקים פנימיים, שהם בלוקים שמצורפים לקלט של ערכים והצהרות.
- יוצרת ומחזירה את מחרוזת הקוד של הבלוק.
בלוקים לדוגמה
כדוגמה, נכתוב מחוללי קוד JavaScript לבלוקים הבאים.
custom_compareהוא בלוק ערכים עם שדה להזנת ערך בשםLEFT, שדה נפתח בשםOPERATORושדה מספרי בשםRIGHT. הפונקציה יוצרת מחרוזת של ביטוי מהצורה'0 = 0'.
custom_ifהוא בלוק הצהרה עם שדה לתיבת סימון בשםNOT, שדה להזנת ערך בשםCONDITIONושדה להזנת הצהרה בשםTHEN. הוא יוצר מחרוזת של הצהרה מהצורה'if (...) {\n...\n};\n'.
במאמר הזה נסביר איך ליצור את הגנרטורים שלב אחר שלב. בסוף המסמך הזה מופיעים גנרטורים לדוגמה.
חשוב לזכור שהבלוקים האלה נועדו רק להמחשת יצירת הקוד. באפליקציה אמיתית, משתמשים בבלוקים המובנים logic_compare ו-controls_if.
ייבוא כלי ליצירת קוד שפה
אפשר לייבא כלי ליצירת קוד שפה באחת מהשיטות הבאות. משתמשים בגנרטור המיובא כדי לאחסן גנרטורים של קוד בלוקים באובייקט forBlock.
מודולים
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
חובה לכלול את הגנרטור אחרי שכוללים את 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) { /* ... */ };
סקריפטים מקומיים
חובה לכלול את הגנרטור אחרי שכוללים את 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) { /* ... */ };
קבלת ערכי שדות
שדות מאפשרים למשתמשים להזין ערכים כמו מחרוזות, מספרים וצבעים. כדי לקבל את הערך של שדה, קוראים לפונקציה getFieldValue. הערך שמוחזר שונה משדה לשדה. לדוגמה, שדות טקסט מחזירים את הטקסט המדויק שהמשתמש הזין, אבל שדות של תפריט נפתח מחזירים מחרוזת ניטרלית מבחינת שפה שמשויכת לפריט שהמשתמש בחר. מידע נוסף זמין במאמרי העזרה בנושא שדות מובנים.
יכול להיות שתצטרכו לבצע טרנספורמציה של הערך שמוחזר לפני שתשתמשו בו בקוד, בהתאם לשדה.
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' ? '!' : '';
...
}
מידע נוסף זמין במאמר בנושא שינוי ערכי שדות.
קבלת קוד מבלוקים פנימיים
בלוקים פנימיים הם הבלוקים שמצורפים לערך של בלוק ולערכי הקלט של ההצהרה.
לדוגמה, לבלוק custom_if יש בלוק פנימי של ערך לתנאי if, ובלוקים פנימיים של הצהרה לקוד שמופעל אם התנאי הוא true.
בניגוד לערכי שדות, הקוד שמתקבל מבלוקים פנימיים מוכן לשימוש ואין צורך לבצע בו שינויים.
בלוקים של ערכים פנימיים
כדי לקבל קוד מבלוק פנימי שמצורף לקלט ערך, קוראים ל-valueToCode.
השיטה הזו מפעילה את כלי ליצירת הקוד של הבלוק הפנימי.
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';
...
}
כשמתקשרים אל valueToCode, צריך לציין את האופרטור החזק ביותר בקוד שיחול על הקוד של הבלוק הפנימי. כך valueToCode יכול לקבוע אם צריך להוסיף סוגריים לקוד של הבלוק הפנימי.
לדוגמה, סימון התיבה NOT ב-custom_if מחיל על התנאי אופרטור לוגי של NOT (!). במקרה כזה, מעבירים את העדיפות של האופרטור not (Order.LOGICAL_NOT) אל valueToCode, ו-valueToCode משווה את העדיפות הזו לעדיפות של האופרטור הכי חלש בבלוק הפנימי. הוא עוטף את הקוד של הבלוק הפנימי לפי הצורך:
- אם
CONDITIONהוא בלוק משתנה,valueToCodeלא מוסיף סוגריים כי אפשר להחיל את אופרטור השלילה ישירות על משתנה (!myBoolean). - אם
CONDITIONהוא בלוק השוואה,valueToCodeמוסיף סוגריים כדי שאופרטור השלילה יחול על ההשוואה כולה (!(a < b)) במקום על הערך בצד ימין (!a < b).
לא צריך לדעת אם valueToCode הוסיף סוגריים. כל מה שצריך לעשות הוא להעביר את העדיפות אל valueToCode ולהוסיף את הקוד שמוחזר למחרוזת הקוד. מידע נוסף זמין במאמר בנושא הקדימות של valueToCode.
בלוקים פנימיים של הצהרות
כדי לקבל קוד מבלוק פנימי שמצורף לקלט של הצהרה, מתקשרים אל
statementToCode. שיטה זו קוראת לכלי ליצירת קוד של הבלוק הפנימי ומטפלת בהזחת קוד.
custom_compare
בלוק custom_compare לא מכיל נתוני קלט של דפי חשבון.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
צריך להתקשר רק אל statementToCode עבור הבלוק הפנימי שמחובר ישירות לקלט של הצהרה. statementToCode מטפל בכל הבלוקים הנוספים שמצורפים לבלוק הראשון.
יצירה והחזרה של מחרוזת הקוד
אחרי שמקבלים את הקוד של השדות והבלוקים הפנימיים, יוצרים את מחרוזת הקוד של הבלוק ומחזירים אותה. הפריטים שתחזירו תלויים בסוג החסימה:
בלוקים של ערכים: מחזירים מערך שמכיל את מחרוזת הקוד ואת רמת העדיפות של האופרטור החלש ביותר בקוד.
valueToCodeמשתמש בזה כדי להחליט אם צריך להוסיף סוגריים לקוד כשמשתמשים בבלוק כבלוק פנימי. מידע נוסף זמין במאמר בנושא סדר עדיפות להחזרת מוצרים.בלוקים של הצהרות: מחזירים את מחרוזת הקוד.
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;
}
אם משתמשים בקוד של בלוק ערכים פנימי מספר פעמים במחרוזת הקוד, כדאי לשמור במטמון את הקוד מהבלוק הזה כדי למנוע באגים לא ברורים ותופעות לוואי לא רצויות.
מחוללי קוד מלאים
לעיון, אלה מחוללי הקוד המלאים לכל בלוק:
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;
}