יצירה והפעלה של 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.

תרגום JavaScript

אם חשוב לכם להפעיל את הבלוקים של המשתמש בצורה נכונה, מומלץ להשתמש בפרויקט JS-Translation. הפרויקט הזה נפרד מ-Blockly, אבל הוא נכתב במיוחד עבורBlockly.

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

הפעלת התרגום

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

הורדת קובץ ZIP הורדת כדור TAR הצגה ב-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-Translation. קודם כול, זהו ממשק ה-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-interpreer, צריך להפעיל את highlightBlock() באופן מיידי, מחוץ לארגז החול, כשהמשתמש מבצע את שלבי התוכנה. לשם כך, יוצרים פונקציית 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 שלב אחר שלב. בנוסף, ההדגמה הזו כוללת בלוק המתנה – דוגמה טובה לשימוש אסינכרוני אחר (למשל, דיבור או אודיו, קלט משתמש).