مولد کد بلوک تابعی است که کد یک بلوک را تولید می کند و آن را به صورت رشته برمی گرداند. کدی که یک بلوک تولید می کند به نوع آن بستگی دارد:
- بلوک های ارزش یک اتصال خروجی دارند. این بلوکها مانند عبارات در یک زبان مبتنی بر متن عمل میکنند و رشتههایی را تولید میکنند که حاوی عبارات هستند.
- بلوک های بیانیه بلوک های بدون اتصال خروجی هستند. این بلوکها مانند عباراتی در یک زبان مبتنی بر متن عمل میکنند و رشتههایی تولید میکنند که حاوی عبارات هستند.
نحوه نوشتن یک مولد کد بلوک
برای هر بلوک سفارشی که ایجاد میکنید، باید برای هر زبانی که میخواهید پشتیبانی کنید، یک تولیدکننده کد بلوک بنویسید. توجه داشته باشید که همه مولدهای بلوک کد با جاوا اسکریپت نوشته می شوند، حتی اگر کد را به زبان دیگری تولید کنند.
همه مولدهای بلوک کد مراحل زیر را انجام می دهند:
- یک تولید کننده کد زبان وارد کنید.
- مقدار هر فیلد را بدست آورید و آن را به یک رشته کد تبدیل کنید.
- رشتههای کد تولید شده توسط بلوکهای داخلی را دریافت کنید، که بلوکهایی هستند که به ورودیهای ارزش و دستورات متصل هستند.
- رشته کد بلوک را بسازید و برگردانید.
بلوک های نمونه
به عنوان مثال، تولید کننده کد جاوا اسکریپت را برای بلوک های زیر می نویسیم.
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، و بلوک های داخلی عبارت برای کد است که در صورت درست بودن شرط اجرا می شود.
برخلاف مقادیر فیلد، کدی که از بلوکهای داخلی دریافت میکنید آماده است و نیازی به تبدیل شدن ندارد.
بلوک های ارزش داخلی
برای دریافت کد از یک بلوک داخلی متصل به ورودی مقدار، 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 operator را ( Order.LOGICAL_NOT
) به valueToCode
می دهید و valueToCode
این را با اولویت ضعیف ترین عملگر در بلوک داخلی مقایسه می کند. سپس در صورت نیاز کد بلوک داخلی را میپیچد:
- اگر
CONDITION
یک بلوک متغیر است،valueToCode
پرانتز اضافه نمی کند زیرا عملگر not را می توان مستقیماً به یک متغیر (!myBoolean
) اعمال کرد. - اگر
CONDITION
یک بلوک مقایسه باشد،valueToCode
پرانتزها را اضافه می کند تا عملگر not به جای مقدار سمت چپ (!(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;
}