자바스크립트 생성 및 실행

Blockly 애플리케이션은 출력 언어로 JavaScript를 생성하는 경우가 많으며, 일반적으로 웹페이지 (동일한 또는 삽입된 WebView) 내에서 실행됩니다. 다른 생성기와 마찬가지로 첫 번째 단계는 JavaScript 생성기를 포함하는 것입니다.

import {javascriptGenerator} from 'blockly/javascript';

작업공간에서 자바스크립트를 생성하려면 다음을 호출합니다.

javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);

결과 코드는 대상 웹페이지에서 바로 실행할 수 있습니다.

try {
  eval(code);
} catch (e) {
  alert(e);
}

기본적으로 위의 스니펫은 코드를 생성하고 평가합니다. 하지만 몇 가지 개선사항이 있습니다. 한 가지 개선사항은 평가가 try/catch로 래핑되어 모든 런타임 오류가 자동으로 실패하지 않고 표시되도록 하는 것입니다. 또 다른 상세검색은 code이 예약어 목록에 추가되어 사용자의 코드에 해당 이름의 변수가 포함되는 경우 충돌하는 대신 자동으로 이름이 변경되도록 하는 것입니다. 모든 로컬 변수는 이러한 방식으로 예약되어야 합니다.

하이라이트 블록

코드가 실행될 때 현재 실행 중인 블록을 강조 표시하면 사용자가 프로그램의 동작을 이해하는 데 도움이 됩니다. JavaScript 코드를 생성하기 전에 STATEMENT_PREFIX를 설정하여 문별로 강조표시를 수행할 수 있습니다.

javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');

highlightBlock를 정의하여 작업공간의 블록을 표시합니다.

function highlightBlock(id) {
  workspace.highlightBlock(id);
}

그러면 모든 문 앞에 highlightBlock('123'); 문이 추가됩니다. 여기서 123는 강조 표시할 블록의 일련번호입니다.

무한 루프

결과 코드는 항상 문법적으로 정확하다고 보장되지만 무한 루프가 포함될 수 있습니다. 중지 문제를 해결하는 것은 Blockly의 범위를 벗어나므로 (!) 이러한 사례를 처리하는 가장 좋은 방법은 카운터를 유지하고 반복이 실행될 때마다 줄이는 것입니다. 이렇게 하려면 모든 루프와 모든 함수에 삽입될 코드 스니펫으로 javascriptGenerator.INFINITE_LOOP_TRAP를 설정하기만 하면 됩니다. 예를 들면 다음과 같습니다.

window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);

다음은 JavaScript 생성 및 실행을 보여주는 실시간 데모입니다.

JS 인터프리터

사용자의 블록을 올바르게 실행하려는 경우 JS-Translateer 프로젝트를 사용하는 것이 좋습니다. 이 프로젝트는 Blockly와 별개이지만 Blockly용으로 특별히 작성되었습니다.

  • 원하는 속도로 코드 실행
  • 실행 일시중지/재개/단계 스루 실행
  • 실행되는 블록을 강조 표시합니다.
  • 브라우저의 자바스크립트와 완전히 격리됩니다.

인터프리터 실행

먼저 GitHub에서 JS-Translateer를 다운로드합니다.

ZIP 파일 다운로드 TAR Ball 다운로드 GitHub에서 보기

그런 다음 페이지에 추가합니다.

<script src="acorn_interpreter.js"></script>

가장 간단한 호출 방법은 JavaScript를 생성하고, 인터프리터를 만들고, 코드를 실행하는 것입니다.

var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();

인터프리터 단계

코드를 더 느리거나 더 제어된 방식으로 실행하려면 run 호출을 단계별로 실행하는 루프 (이 경우 10ms마다 한 단계)로 바꿉니다.

function nextStep() {
  if (myInterpreter.step()) {
    setTimeout(nextStep, 10);
  }
}
nextStep();

각 단계는 행이나 블록이 아니며 JavaScript의 시맨틱 단위이며 매우 세분화될 수 있습니다.

API 추가

JS-Translateer는 브라우저와 완전히 격리된 샌드박스입니다. 외부 세계에서 작업을 실행하는 모든 블록에는 인터프리터에 API를 추가해야 합니다. 자세한 설명은 JS-Translateer 문서를 참조하세요. 하지만 먼저 알림 및 프롬프트 블록을 지원하는 데 필요한 API는 다음과 같습니다.

function initApi(interpreter, globalObject) {
  // Add an API function for the alert() block.
  var wrapper = function(text) {
    return alert(arguments.length ? text : '');
  };
  interpreter.setProperty(globalObject, 'alert',
      interpreter.createNativeFunction(wrapper));

  // Add an API function for the prompt() block.
  wrapper = function(text) {
    return prompt(text);
  };
  interpreter.setProperty(globalObject, 'prompt',
      interpreter.createNativeFunction(wrapper));
}

그런 다음 인터프리터 초기화를 수정하여 initApi 함수를 전달합니다.

var myInterpreter = new Interpreter(code, initApi);

알림 블록과 프롬프트 블록은 인터프리터용 커스텀 API가 필요한 기본 블록 집합에서 유일한 두 블록입니다.

highlightBlock() 연결 중

JS-Translateer에서 실행하는 경우 highlightBlock() 사용자가 프로그램을 진행할 때 샌드박스 외부에서 즉시 실행해야 합니다. 이렇게 하려면 래퍼 함수 highlightBlock()를 만들어 함수 인수를 캡처하고 네이티브 함수로 등록합니다.

function initApi(interpreter, globalObject) {
  // Add an API function for highlighting blocks.
  var wrapper = function(id) {
    return workspace.highlightBlock(id);
  };
  interpreter.setProperty(globalObject, 'highlightBlock',
      interpreter.createNativeFunction(wrapper));
}

보다 정교한 애플리케이션에서는 강조표시 명령어에 도달할 때까지 일시중지 없이 단계를 반복적으로 실행한 다음 일시중지할 수 있습니다. 이 전략은 한 줄씩 실행을 시뮬레이션합니다. 아래 예에서는 이 방식을 사용합니다.

JS-Translateer 예

다음은 자바스크립트를 단계별로 해석하는 실시간 데모입니다. 이 데모에는 대기 블록이 포함되어 있습니다. 대기 블록은 다른 비동기 동작(예: 음성 또는 오디오, 사용자 입력)에 사용하기에 좋은 예입니다.