Trình tạo mã khối là một hàm tạo mã cho một khối và trả về mã đó dưới dạng một chuỗi. Mã mà một khối tạo ra phụ thuộc vào loại khối đó:
- Khối giá trị có kết nối đầu ra. Các khối này hoạt động như các biểu thức trong một ngôn ngữ dựa trên văn bản và tạo ra các chuỗi chứa biểu thức.
- Khối câu lệnh là các khối không có kết nối đầu ra. Các khối này hoạt động như các câu lệnh trong ngôn ngữ dựa trên văn bản và tạo các chuỗi chứa các câu lệnh.
Cách viết trình tạo mã khối
Đối với mỗi khối tuỳ chỉnh mà bạn tạo, bạn cần viết một trình tạo mã khối cho mỗi ngôn ngữ mà bạn muốn hỗ trợ. Xin lưu ý rằng tất cả trình tạo mã khối đều được viết bằng JavaScript, ngay cả khi chúng tạo mã bằng ngôn ngữ khác.
Tất cả trình tạo mã khối đều thực hiện các bước sau:
- Nhập trình tạo mã ngôn ngữ.
- Lấy giá trị của từng trường và chuyển đổi giá trị đó thành một chuỗi mã.
- Nhận các chuỗi mã do các khối bên trong tạo ra, đó là các khối được đính kèm vào giá trị và dữ liệu đầu vào của câu lệnh.
- Tạo và trả về chuỗi mã của khối.
Khối mẫu
Ví dụ: chúng ta sẽ viết trình tạo mã JavaScript cho các khối sau.
custom_compare
là một khối giá trị có dữ liệu đầu vào giá trị tên làLEFT
, một trường thả xuống tên làOPERATOR
và một trường số tên làRIGHT
. Hàm này tạo một chuỗi biểu thức ở dạng'0 = 0'
.custom_if
là một khối câu lệnh có trường hộp đánh dấu tên làNOT
, một giá trị đầu vào tên làCONDITION
và một câu lệnh đầu vào tên làTHEN
. Hàm này tạo một chuỗi câu lệnh ở dạng'if (...) {\n...\n};\n'
.
Tài liệu này xây dựng trình tạo theo từng bước. Bạn có thể tìm thấy các trình tạo đã hoàn tất ở phần cuối tài liệu này.
Xin lưu ý rằng các khối này chỉ nhằm minh hoạ việc tạo mã. Trong một ứng dụng thực tế, hãy sử dụng các khối logic_compare
và controls_if
tích hợp sẵn.
Nhập trình tạo mã ngôn ngữ
Bạn có thể nhập trình tạo mã ngôn ngữ bằng một trong các phương thức sau. Sử dụng trình tạo đã nhập để lưu trữ trình tạo mã khối trong đối tượng forBlock
.
Mô-đun
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
Bạn phải thêm trình tạo sau khi thêm 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) { /* ... */ };
Tập lệnh cục bộ
Bạn phải thêm trình tạo sau khi thêm 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) { /* ... */ };
Lấy giá trị trường
Các trường cho phép người dùng nhập các giá trị như chuỗi, số và màu sắc. Để lấy giá trị của một trường, hãy gọi getFieldValue
. Kết quả trả về sẽ khác nhau tuỳ theo trường. Ví dụ: các trường văn bản trả về văn bản chính xác do người dùng nhập, nhưng các trường thả xuống trả về một chuỗi trung lập về ngôn ngữ liên kết với mục mà người dùng đã chọn. Để biết thêm thông tin, hãy xem tài liệu về các trường tích hợp sẵn.
Tuỳ thuộc vào trường, bạn có thể cần chuyển đổi giá trị được trả về trước khi sử dụng giá trị đó trong mã.
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' ? '!' : '';
...
}
Để biết thêm thông tin, hãy xem phần Biến đổi giá trị trường.
Lấy mã từ các khối bên trong
Khối bên trong là các khối được đính kèm vào giá trị của khối và dữ liệu đầu vào của câu lệnh.
Ví dụ: khối custom_if
có một khối bên trong giá trị cho điều kiện if và các khối bên trong câu lệnh cho mã được thực thi nếu điều kiện là đúng.
Không giống như giá trị trường, mã bạn nhận được từ các khối bên trong đã sẵn sàng và không cần chuyển đổi.
Khối giá trị bên trong
Để lấy mã từ một khối bên trong được đính kèm vào một giá trị đầu vào, hãy gọi valueToCode
.
Phương thức này gọi trình tạo mã của khối bên trong.
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';
...
}
Khi gọi valueToCode
, bạn cần cho biết toán tử mạnh nhất trong mã sẽ áp dụng cho mã của khối bên trong. Điều này cho phép valueToCode
xác định xem có cần gói mã của khối bên trong trong dấu ngoặc đơn hay không.
Ví dụ: việc đánh dấu vào hộp NOT
trong custom_if
sẽ áp dụng toán tử phủ định logic (!
) cho điều kiện. Trong trường hợp này, bạn truyền thứ tự ưu tiên của toán tử not (Order.LOGICAL_NOT
) đến valueToCode
và valueToCode
so sánh thứ tự ưu tiên này với thứ tự ưu tiên của toán tử yếu nhất trong khối bên trong. Sau đó, lớp này sẽ gói mã của khối bên trong nếu cần:
- Nếu
CONDITION
là một khối biến,valueToCode
sẽ không thêm dấu ngoặc đơn vì toán tử not có thể được áp dụng trực tiếp cho một biến (!myBoolean
). - Nếu
CONDITION
là một khối so sánh,valueToCode
sẽ thêm dấu ngoặc đơn để toán tử phủ định áp dụng cho toàn bộ phép so sánh (!(a < b)
) thay vì giá trị bên trái (!a < b
).
Bạn thực sự không cần biết liệu valueToCode
có thêm dấu ngoặc đơn hay không. Tất cả những gì bạn cần làm là truyền mức độ ưu tiên đến valueToCode
và thêm mã được trả về vào chuỗi mã. Để biết thêm thông tin, hãy xem phần tính năng ưu tiên valueToCode.
Khối câu lệnh bên trong
Để lấy mã từ một khối bên trong được đính kèm vào dữ liệu đầu vào của câu lệnh, hãy gọi statementToCode
. Phương thức này gọi trình tạo mã của khối bên trong và xử lý mã thụt lề.
custom_compare
Khối custom_compare
không có bất kỳ dữ liệu đầu vào nào của câu lệnh.
custom_if
javascriptGenerator.forBlock['custom_if'] = function (block, generator) {
...
const statements = generator.statementToCode(block, 'THEN');
...
}
Bạn chỉ cần gọi statementToCode
cho khối bên trong được kết nối trực tiếp với đầu vào câu lệnh. statementToCode
xử lý mọi khối bổ sung được đính kèm vào khối đầu tiên.
Tạo và trả về chuỗi mã
Sau khi bạn nhận được mã cho các trường và khối bên trong, hãy tạo và trả về chuỗi mã cho khối của bạn. Nội dung bạn trả về chính xác sẽ phụ thuộc vào loại khối:
Khối giá trị: Trả về một mảng chứa chuỗi mã và thứ tự ưu tiên của toán tử yếu nhất trong mã.
valueToCode
sử dụng thuộc tính này để quyết định xem mã của bạn có cần được gói trong dấu ngoặc đơn hay không khi khối của bạn được dùng làm khối bên trong. Để biết thêm thông tin, hãy xem phần Thứ tự ưu tiên trả về.Khối câu lệnh: Trả về chuỗi mã.
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;
}
Nếu sử dụng mã của một khối giá trị bên trong nhiều lần trong chuỗi mã, bạn nên lưu mã vào bộ nhớ đệm từ khối đó để tránh các lỗi nhỏ và hiệu ứng phụ không mong muốn.
Trình tạo mã hoàn chỉnh
Để tham khảo, sau đây là các trình tạo mã hoàn chỉnh cho từng khối:
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;
}