블록 코드 생성기

블록 코드 생성기는 블록의 코드를 생성하고 문자열로 반환하는 함수입니다. 블록에서 생성되는 코드는 유형에 따라 다릅니다.

  • 값 블록에는 출력 연결이 있습니다. 이러한 블록은 텍스트 기반 언어의 표현식처럼 작동하며 표현식이 포함된 문자열을 생성합니다.
  • 문 블록은 출력 연결이 없는 블록입니다. 이러한 블록은 텍스트 기반 언어의 문과 같이 작동하며 문과 함께 문자열을 생성합니다.

블록 코드 생성기 작성 방법

만드는 맞춤 블록마다 지원하려는 각 언어의 블록 코드 생성기를 작성해야 합니다. 모든 블록 코드 생성기는 다른 언어로 코드를 생성하더라도 JavaScript로 작성됩니다.

모든 블록 코드 생성기는 다음 단계를 실행합니다.

  1. 언어 코드 생성기를 가져옵니다.
  2. 각 필드의 값을 가져와 코드 문자열로 변환합니다.
  3. 값 및 문 입력에 연결된 블록인 내부 블록에서 생성된 코드 문자열을 가져옵니다.
  4. 블록의 코드 문자열을 빌드하고 반환합니다.

블록 예시

예를 들어 다음 블록의 JavaScript 코드 생성기를 작성해 보겠습니다.

  • custom_compareLEFT라는 값 입력란, OPERATOR라는 드롭다운 필드, RIGHT라는 숫자 필드가 있는 값 블록입니다. '0 = 0' 형식의 표현식 문자열을 생성합니다.

    비교를 위한 맞춤 값 블록입니다.

  • custom_ifNOT라는 체크박스 필드, CONDITION라는 값 입력, THEN라는 문 입력이 있는 문 블록입니다. 'if (...) {\n...\n};\n' 형식의 문자열을 생성합니다.

    if 문에 대한 맞춤 문 블록입니다. 사용자는 체크박스를 사용하여 if 조건을 부정할 수 있습니다.

이 문서에서는 생성기를 단계별로 빌드합니다. 완성된 생성기는 이 문서의 끝에서 확인할 수 있습니다.

이러한 블록은 코드 생성을 보여주기 위한 것입니다. 실제 애플리케이션에서는 기본 제공 logic_comparecontrols_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가 내부 블록의 코드를 괄호로 래핑해야 하는지 결정할 수 있습니다.

예를 들어 custom_if에서 NOT 체크박스를 선택하면 조건에 논리적 불리언 연산자 (!)가 적용됩니다. 이 경우 not 연산자의 우선순위 (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;
}