JavaScript जनरेट और चलाना

Blockly ऐप्लिकेशन, अक्सर JavaScript को आउटपुट लैंग्वेज के तौर पर जनरेट करते हैं. आम तौर पर, ऐसा किसी वेब पेज (ऐसा हो सकता है कि वह एक ही हो या एम्बेड किया गया WebView हो) में चलाने के लिए किया जाता है. किसी भी जनरेटर की तरह, पहला चरण JavaScript जनरेटर को शामिल करना है.

import {javascriptGenerator} from 'blockly/javascript';

वर्कस्पेस से JavaScript जनरेट करने के लिए, इस फ़ंक्शन को कॉल करें:

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

इससे जनरेट हुए कोड को डेस्टिनेशन वेब पेज पर ही एक्ज़ीक्यूट किया जा सकता है:

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

असल में, ऊपर दिया गया स्निपेट सिर्फ़ कोड जनरेट करता है और उसका आकलन करता है. हालांकि, इसमें कुछ बदलाव किए गए हैं. इसमें एक सुधार यह किया गया है कि eval को 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-Interpreter

अगर आपको उपयोगकर्ता के ब्लॉक को सही तरीके से चलाना है, तो JS-Interpreter प्रोजेक्ट का इस्तेमाल करें. यह प्रोजेक्ट Blockly से अलग है, लेकिन इसे खास तौर पर Blockly के लिए लिखा गया है.

  • कोड को किसी भी स्पीड पर एक्ज़ीक्यूट करें.
  • स्क्रिप्ट को रोकना/फिर से शुरू करना/एक-एक करके चलाना.
  • ब्लॉक के एक्ज़ीक्यूट होने पर उन्हें हाइलाइट करें.
  • यह ब्राउज़र की JavaScript से पूरी तरह अलग होता है.

अनुवादक मोड चालू करना

सबसे पहले, GitHub से JS-Interpreter डाउनलोड करें:

इसके बाद, इसे अपने पेज पर जोड़ें:

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

इसे कॉल करने का सबसे आसान तरीका यह है कि JavaScript जनरेट करें, इंटरप्रेटर बनाएं, और कोड चलाएं:

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

अनुवादक मोड

कोड को धीरे-धीरे या ज़्यादा कंट्रोल के साथ चलाने के लिए, run को ऐसे लूप से बदलें जो इस तरह से काम करता हो (इस मामले में, हर 10 मि॰से॰ में एक स्टेप):

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

ध्यान दें कि हर चरण, लाइन या ब्लॉक नहीं होता. यह JavaScript में एक सिमैंटिक यूनिट होती है, जो बहुत ज़्यादा बारीक हो सकती है.

कोई एपीआई जोड़ना

JS-Interpreter एक सैंडबॉक्स है, जो ब्राउज़र से पूरी तरह अलग होता है. बाहरी दुनिया से इंटरैक्ट करने वाले किसी भी ब्लॉक के लिए, इंटरप्रेटर में एक एपीआई जोड़ा जाना चाहिए. पूरी जानकारी के लिए, JS-Interpreter का दस्तावेज़ देखें. हालांकि, सूचना और प्रॉम्प्ट ब्लॉक के लिए ज़रूरी एपीआई यहां दिया गया है:

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);

डिफ़ॉल्ट रूप से उपलब्ध ब्लॉक के सेट में, सिर्फ़ सूचना और प्रॉम्प्ट ब्लॉक ऐसे ब्लॉक होते हैं जिनके लिए इंटरप्रेटर को कस्टम एपीआई की ज़रूरत होती है.

highlightBlock() को कनेक्ट कर रहा है

JS-Interpreter में चलने के दौरान, 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-Interpreter का उदाहरण

यहां JavaScript को सिलसिलेवार तरीके से समझने का लाइव डेमो दिया गया है. साथ ही, इस डेमो में वेट ब्लॉक शामिल है. यह एसिंक्रोनस व्यवहार (जैसे, स्पीच या ऑडियो, उपयोगकर्ता का इनपुट) के लिए इस्तेमाल करने का एक अच्छा उदाहरण है.