مولدهای بلاک کد

مولد کد بلوک تابعی است که کد یک بلوک را تولید می کند و آن را به صورت رشته برمی گرداند. کدی که یک بلوک تولید می کند به نوع آن بستگی دارد:

  • بلوک های ارزش یک اتصال خروجی دارند. این بلوک‌ها مانند عبارات در یک زبان مبتنی بر متن عمل می‌کنند و رشته‌هایی را تولید می‌کنند که حاوی عبارات هستند.
  • بلوک های بیانیه بلوک های بدون اتصال خروجی هستند. این بلوک‌ها مانند عباراتی در یک زبان مبتنی بر متن عمل می‌کنند و رشته‌هایی تولید می‌کنند که حاوی عبارات هستند.

نحوه نوشتن یک مولد کد بلوک

برای هر بلوک سفارشی که ایجاد می‌کنید، باید برای هر زبانی که می‌خواهید پشتیبانی کنید، یک تولیدکننده کد بلوک بنویسید. توجه داشته باشید که همه مولدهای بلوک کد با جاوا اسکریپت نوشته می شوند، حتی اگر کد را به زبان دیگری تولید کنند.

همه مولدهای بلوک کد مراحل زیر را انجام می دهند:

  1. یک تولید کننده کد زبان وارد کنید.
  2. مقدار هر فیلد را بدست آورید و آن را به یک رشته کد تبدیل کنید.
  3. رشته‌های کد تولید شده توسط بلوک‌های داخلی را دریافت کنید، که بلوک‌هایی هستند که به ورودی‌های ارزش و دستورات متصل هستند.
  4. رشته کد بلوک را بسازید و برگردانید.

بلوک های نمونه

به عنوان مثال، تولید کننده کد جاوا اسکریپت را برای بلوک های زیر می نویسیم.

  • custom_compare یک بلوک مقدار است که دارای یک ورودی مقدار به نام LEFT ، یک فیلد کشویی به نام OPERATOR و یک فیلد عددی به نام RIGHT است. یک رشته عبارت به شکل '0 = 0' تولید می کند.

    بلوک ارزش سفارشی برای مقایسه ها

  • custom_if یک بلوک عبارت است که دارای یک فیلد چک با نام NOT ، یک ورودی مقدار به نام CONDITION و یک ورودی عبارت به نام THEN است. یک رشته دستور به شکل 'if (...) {\n...\n};\n' تولید می کند.

    بلوک دستور سفارشی برای دستور if. کاربران می توانند از یک چک باکس استفاده کنند شرط if را نفی کنید.

این سند گام به گام ژنراتورها را می سازد. شما می توانید ژنراتورهای تکمیل شده را در انتهای این سند بیابید.

توجه داشته باشید که این بلوک ها فقط برای نشان دادن تولید کد در نظر گرفته شده اند. در یک برنامه واقعی، از بلوک های 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;
}