יצירה והפעלה של JavaScript

אפליקציות חסימה מייצרות לעיתים קרובות JavaScript כשפת הפלט שלהן, בדרך כלל פועלות בדף אינטרנט (יכול להיות באותו אופן או ב-WebView מוטמע). כמו כל מחולל, תחילה יש לכלול את מחולל ה-JavaScript.

import {javascriptGenerator} from 'blockly/javascript';

כדי ליצור JavaScript מסביבת העבודה, קוראים ל-:

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

את הקוד שמתקבל ניתן להריץ ישירות בדף האינטרנט של היעד:

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

בעיקרון, קטע הקוד שלמעלה רק יוצר את הקוד ומציג אותו. אבל, לפעמים יש כמה שיפורים. אחד החידודים הוא שההערכה כוללת try/catch כדי שכל השגיאות בסביבת זמן הריצה יהיו גלויות, במקום להיכשל בשקט. חידוד נוסף הוא שהתוסף code מתווסף לרשימת הפריטים השמורים כך שאם הקוד של המשתמש מכיל משתנה בשם הזה, הוא יהיה לשנות את השם באופן אוטומטי במקום להתנגש. כל משתנה מקומי צריך להיות שמורים בדרך הזו.

קטעי הדגשה

הדגשת הבלוק שפועל כרגע בזמן הרצת הקוד עוזרת למשתמשים להבין את ההתנהגות של התוכנית. ניתן להדגיש רמת פירוט על ידי הגדרת הערך STATEMENT_PREFIX לפני יצירת קוד ה-JavaScript:

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-Translate. הפרויקט הזה נפרד מ-Blockly, אבל נכתב במיוחד עבור Blockly.

  • להריץ קוד במהירות
  • להשהות/להמשיך/לפעול לפי ביצוע.
  • הדגשת בלוקים בזמן ההפעלה שלהם.
  • מבודד לגמרי מ-JavaScript של הדפדפן.

הרצת המתורגמן

תחילה, מורידים את הכלי לתרגום JS מ-GitHub:

הורדת קובץ ZIP הורדת TAR Ball הצגה ב-GitHub

לאחר מכן אפשר להוסיף אותו לדף:

<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, שעשוי להיות מדויק מאוד.

הוספת API

כלי התרגום של JS הוא ארגז חול מבודד לחלוטין מהדפדפן. לכל בלוק שמבצע פעולות עם העולם החיצוני צריך להוסיף API לבין המתורגמן. לתיאור מלא אפשר לעיין מסמכי תיעוד של JS-Translate. אבל קודם כל, זה ה-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()

כשמריצים את הפקודה highlightBlock() ב-JS, צריך להריץ אותה מיד, מחוץ לארגז החול, בזמן שהמשתמש מתקדם בתוכנית. לבצע צריך ליצור פונקציית wrapper 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

הנה הדגמה (דמו) בזמן אמת של פירוש JavaScript שלב אחר שלב. וגם ההדגמה הזו כולל בלוק המתנה, דוגמה טובה להתנהגות אסינכרונית אחרת. (למשל: דיבור או אודיו, קלט של משתמשים).