engine.js.html 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: engine.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: engine.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>/**
  20. * An object for executing Python code and passing the results along to interested components.
  21. *
  22. * @constructor
  23. * @this {BlockPyEditor}
  24. * @param {Object} main - The main BlockPy instance
  25. * @param {HTMLElement} tag - The HTML object this is attached to.
  26. */
  27. function BlockPyEngine(main) {
  28. this.main = main;
  29. this.loadEngine();
  30. this.instructor_module = instructor_module('instructor');
  31. //this.main.model.program.subscribe(this.analyze.bind(this))
  32. }
  33. /**
  34. * Definable function to be run when execution has fully ended,
  35. * whether it succeeds or fails.
  36. *
  37. */
  38. BlockPyEngine.prototype.onExecutionEnd = null;
  39. /**
  40. * Helper function that will attempt to call the defined onExecutionEnd,
  41. * but will do nothing if there is no function defined.
  42. */
  43. BlockPyEngine.prototype.executionEnd_ = function() {
  44. if (this.onExecutionEnd !== null) {
  45. this.onExecutionEnd();
  46. }
  47. };
  48. /**
  49. * Initializes the Python Execution engine and the Printer (console).
  50. */
  51. BlockPyEngine.prototype.loadEngine = function() {
  52. var engine = this;
  53. var printer = this.main.components.printer;
  54. // Skulpt settings
  55. // No connected services
  56. Sk.connectedServices = {}
  57. // Limit execution to 5 seconds
  58. Sk.execLimit = this.main.model.settings.disable_timeout() ? null : 5000;
  59. this.main.model.settings.disable_timeout.subscribe(function(newValue) {
  60. Sk.execLimit = newValue ? null : 5000;
  61. });
  62. // Ensure version 3, so we get proper print handling
  63. Sk.python3 = true;
  64. // Major Skulpt configurations
  65. Sk.configure({
  66. // Function to handle the text outputted by Skulpt
  67. output: printer.print.bind(printer),
  68. // Function to handle loading in new files
  69. read: this.readFile.bind(this)
  70. });
  71. // Identify the location to put new charts
  72. Sk.console = printer.getConfiguration();
  73. // Stepper! Executed after every statement.
  74. Sk.afterSingleExecution = this.step.bind(this);
  75. // Definitely use a prompt
  76. Sk.inputfunTakesPrompt = true;
  77. // Keeps track of the tracing while the program is executing; destroyed afterwards.
  78. this.executionBuffer = {};
  79. }
  80. /**
  81. * Used to access Skulpt built-ins. This is pretty generic, taken
  82. * almost directly from the Skulpt docs.
  83. *
  84. * @param {String} filename - The python filename (e.g., "os" or "pprint") that will be loaded.
  85. * @returns {String} The JavaScript source code of the file (weird, right?)
  86. * @throws Will throw an error if the file isn't found.
  87. */
  88. BlockPyEngine.prototype.readFile = function(filename) {
  89. if (Sk.builtinFiles === undefined ||
  90. Sk.builtinFiles["files"][filename] === undefined) {
  91. throw "File not found: '" + filename + "'";
  92. }
  93. return Sk.builtinFiles["files"][filename];
  94. }
  95. /**
  96. * Resets the state of the execution engine, including reinitailizing
  97. * the execution buffer (trace, step, etc.), reseting the printer, and
  98. * hiding the trace button.
  99. *
  100. */
  101. BlockPyEngine.prototype.reset = function() {
  102. this.executionBuffer = {
  103. 'trace': [],
  104. 'step': 0,
  105. 'last_step': 0,
  106. 'line_number': 0,
  107. };
  108. this.main.model.execution.trace.removeAll();
  109. this.main.model.execution.step(0);
  110. this.main.model.execution.last_step(0);
  111. this.main.model.execution.line_number(0)
  112. this.main.components.printer.resetPrinter();
  113. this.main.model.execution.show_trace(false);
  114. }
  115. /**
  116. * "Steps" the execution of the code, meant to be used as a callback to the Skulpt
  117. * environment.
  118. *
  119. * @param {Object} variables - Hash that maps the names of variables (Strings) to their Skulpt representation.
  120. * @param {Number} lineNumber - The corresponding line number in the source code that is being executed.
  121. * @param {Number} columnNumber - The corresponding column number in the source code that is being executed. Think of it as the "X" position to the lineNumber's "Y" position.
  122. * @param {String} filename - The name of the python file being executed (e.g., "__main__.py").
  123. * @param {String} astType - Unused? TODO: What is this?
  124. * @param {String} ast - String-encoded JSON representation of the AST node associated with this element.
  125. */
  126. BlockPyEngine.prototype.step = function(variables, lineNumber,
  127. columnNumber, filename, astType, ast) {
  128. if (filename == '&lt;stdin>.py') {
  129. var currentStep = this.executionBuffer.step;
  130. var globals = this.parseGlobals(variables);
  131. this.executionBuffer.trace.push(
  132. {'step': currentStep,
  133. 'filename': filename,
  134. //'block': highlightMap[lineNumber-1],
  135. 'line': lineNumber,
  136. 'column': columnNumber,
  137. 'properties': globals.properties,
  138. 'modules': globals.modules});
  139. this.executionBuffer.step = currentStep+1;
  140. this.executionBuffer.last_step = currentStep+1;
  141. this.executionBuffer.line_number = lineNumber;
  142. }
  143. }
  144. /**
  145. * Called at the end of the Skulpt execution to terminate the executionBuffer
  146. * and hand it off to the execution trace in the model.
  147. */
  148. BlockPyEngine.prototype.lastStep = function() {
  149. var execution = this.main.model.execution;
  150. execution.trace(this.executionBuffer.trace);
  151. this.main.model.execution.step(this.executionBuffer.step)
  152. this.main.model.execution.last_step(this.executionBuffer.last_step)
  153. this.main.model.execution.line_number(this.executionBuffer.line_number)
  154. //this.executionBuffer = undefined;
  155. }
  156. /**
  157. * Runs the AbstractInterpreter to get some static information about the code,
  158. * in particular the variables' types. This is needed for type checking.
  159. *
  160. * @returns {Object&lt;String, AIType>} Maps variable names (as Strings) to types as constructed by the AbstractIntepreter.
  161. */
  162. BlockPyEngine.prototype.analyzeVariables = function() {
  163. // Get the code
  164. var code = this.main.model.programs['__main__']();
  165. if (code.trim() == "") {
  166. return {};
  167. }
  168. var analyzer = new AbstractInterpreter(code);
  169. report = analyzer.report;
  170. return analyzer.variableTypes;
  171. }
  172. /**
  173. * Runs the AbstractInterpreter to get some static information about the code,
  174. * including potential semantic errors. It then parses that information to give
  175. * feedback.
  176. *
  177. * @returns {Boolean} Whether the code was successfully analyzed.
  178. */
  179. BlockPyEngine.prototype.analyze = function() {
  180. this.main.model.execution.status("analyzing");
  181. var feedback = this.main.components.feedback;
  182. // Get the code
  183. var code = this.main.model.programs['__main__']();
  184. if (code.trim() == "") {
  185. this.main.components.feedback.emptyProgram("You haven't written any code yet!");
  186. //this.main.model.feedback.status("semantic");
  187. return false;
  188. }
  189. var analyzer = new AbstractInterpreter(code);
  190. this.main.model.execution.ast = analyzer.ast;
  191. report = analyzer.report;
  192. // Syntax error
  193. if (report.error !== false) {
  194. console.log(report.error.args.v)
  195. var codeLine = '.';
  196. if (report.error.args.v.length > 3) {
  197. codeLine = ', where it says:&lt;br>&lt;code>'+report.error.args.v[3][2]+'&lt;/code>';
  198. }
  199. this.main.reportError('editor', report.error, "While attempting to process your Python code, I found a syntax error. In other words, your Python code has a mistake in it (e.g., mispelled a keyword, bad indentation, unnecessary symbol). You should check to make sure that you have written all of your code correctly. To me, it looks like the problem is on line "+ report.error.args.v[2]+codeLine, report.error.args.v[2]);
  200. return false;
  201. }
  202. if (report["Unconnected blocks"].length >= 1) {
  203. var variable = report['Unconnected blocks'][0];
  204. feedback.semanticError("Unconnected blocks", "It looks like you have unconnected blocks on line "+variable.position.line+". Before you run your program, you must make sure that all of your blocks are connected and that there are no unfilled holes.", variable.position.line)
  205. return false;
  206. } else if (report['Iteration variable is iteration list'].length >= 1) {
  207. var variable = report['Iteration variable is iteration list'][0];
  208. feedback.semanticError("Iteration Problem", "The property &lt;code>"+variable.name+"&lt;/code> was iterated on line "+variable.position.line+", but you used the same variable as the iteration variable. You should choose a different variable name for the iteration variable. Usually, the iteration variable is the singular form of the iteration list (e.g., &lt;code>for dog in dogs:&lt;/code>).", variable.position.line)
  209. return false;
  210. } else if (report["Undefined variables"].length >= 1) {
  211. var variable = report["Undefined variables"][0];
  212. feedback.semanticError("Initialization Problem", "The property &lt;code>"+variable.name+"&lt;/code> was read on line "+variable.position.line+", but it was not given a value on a previous line. You cannot use a property until it has been initialized.", variable.position.line)
  213. return false;
  214. } else if (report["Possibly undefined variables"].length >= 1) {
  215. var variable = report["Possibly undefined variables"][0];
  216. feedback.semanticError("Initialization Problem", "The property &lt;code>"+variable.name+"&lt;/code> was read on line "+variable.position.line+", but it was possibly not given a value on a previous line. You cannot use a property until it has been initialized. Check to make sure that this variable was declared in all of the branches of your decision.", variable.position.line);
  217. return false;
  218. } else if (report["Unread variables"].length >= 1) {
  219. var variable = report["Unread variables"][0];
  220. feedback.semanticError("Unused Property", "The property &lt;code>"+variable.name+"&lt;/code> was set, but was never used after that.", null)
  221. return false;
  222. } else if (report["Overwritten variables"].length >= 1) {
  223. var variable = report["Overwritten variables"][0];
  224. feedback.semanticError("Overwritten Property", "The property &lt;code>"+variable.name+"&lt;/code> was set, but before it could be read it was changed on line "+variable.position.line+". It is unnecessary to change an existing variable's value without reading it first.", variable.position.line)
  225. return false;
  226. } else if (report["Empty iterations"].length >= 1) {
  227. var variable = report["Empty iterations"][0];
  228. feedback.semanticError("Iterating over empty list", "The property &lt;code>"+variable.name+"&lt;/code> was set as an empty list, and then you attempted to iterate over it on "+variable.position.line+". You should only iterate over non-empty lists.", variable.position.line)
  229. return false;
  230. } else if (report["Non-list iterations"].length >= 1) {
  231. var variable = report["Non-list iterations"][0];
  232. feedback.semanticError("Iterating over non-list", "The property &lt;code>"+variable.name+"&lt;/code> is not a list, but you attempted to iterate over it on "+variable.position.line+". You should only iterate over non-empty lists.", variable.position.line)
  233. return false;
  234. } else if (report["Incompatible types"].length >= 1) {
  235. var variable = report["Incompatible types"][0];
  236. feedback.semanticError("Incompatible types", "You attempted to "+variable.operation+" a "+variable.left.type+" and a "+variable.right.type+" on line "+variable.position.line+". But you can't do that with that operator. Make sure both sides of the operator are the right type.", variable.position.line)
  237. return false;
  238. }
  239. return true;
  240. }
  241. var GLOBAL_VALUE;
  242. /**
  243. * Runs the given python code, resetting the console and Trace Table.
  244. */
  245. BlockPyEngine.prototype.run = function() {
  246. // Reset everything
  247. this.reset();
  248. if (!this.main.model.settings.disable_semantic_errors() &amp;&amp;
  249. !this.main.model.assignment.disable_algorithm_errors()) {
  250. var success = this.analyze();
  251. if (success === false) {
  252. this.executionEnd_();
  253. return;
  254. }
  255. }
  256. Sk.builtins.value = new Sk.builtin.func(function() {
  257. return Sk.ffi.remapToPy(GLOBAL_VALUE === undefined ? 5 : GLOBAL_VALUE);
  258. });
  259. Sk.builtins.set_value = new Sk.builtin.func(function(v) {
  260. GLOBAL_VALUE = v.v;
  261. });
  262. this.main.model.execution.status("running");
  263. var feedback = this.main.components.feedback;
  264. // Get the code
  265. var code = this.main.model.programs['__main__']();
  266. if (code.trim() == "") {
  267. feedback.emptyProgram();
  268. this.main.model.execution.status("error");
  269. this.executionEnd_();
  270. return;
  271. }
  272. // Actually run the python code
  273. var executionPromise = Sk.misceval.asyncToPromise(function() {
  274. return Sk.importMainWithBody("&lt;stdin>", false, code, true);
  275. });
  276. var engine = this;
  277. var server = this.server;
  278. var execution = this.main.model.execution;
  279. executionPromise.then(
  280. function (module) {
  281. // Run the afterSingleExecution one extra time for final state
  282. Sk.afterSingleExecution(module.$d, -1, 0, "&lt;stdin>.py");
  283. engine.lastStep();
  284. // Handle checks
  285. feedback.noErrors()
  286. engine.check(code, execution.trace(), execution.output(), execution.ast, module.$d);
  287. // Reenable "Run"
  288. engine.main.model.execution.status("waiting");
  289. engine.executionEnd_();
  290. },
  291. function(error) {
  292. feedback.printError(error);
  293. engine.main.model.execution.status("error");
  294. engine.executionEnd_();
  295. //server.logEvent('blockly_error', error);
  296. }
  297. );
  298. }
  299. /**
  300. * Indents the given string by 4 spaces. This correctly handles multi-line strings.
  301. *
  302. * @param {String} str - The string to be manipulated.
  303. * @returns {String} The string with four spaces added at the start of every new line.
  304. */
  305. function indent(str) {
  306. return str.replace(/^(?=.)/gm, ' ');
  307. }
  308. /**
  309. * Skulpt Module for holding the Instructor API.
  310. *
  311. * This module is a little hackish. We need to sit down and reevaluate the best way to
  312. * organize it and whether this particular structure is ideal. I suspect it should be
  313. * it's own proper JS file.
  314. *
  315. * @param {String} name - The name of the module (should always be 'instructor')
  316. *
  317. */
  318. var instructor_module = function(name) {
  319. // Main module object that gets returned at the end.
  320. var mod = {};
  321. /**
  322. * Skulpt Exception that represents a Feedback object, to be rendered to the user
  323. * when the feedback system finds a problem.
  324. *
  325. * @param {Array} args - A list of optional arguments to pass to the Exception.
  326. * Usually this will include a message for the user.
  327. */
  328. Sk.builtin.Feedback = function (args) {
  329. var o;
  330. if (!(this instanceof Sk.builtin.Feedback)) {
  331. o = Object.create(Sk.builtin.Feedback.prototype);
  332. o.constructor.apply(o, arguments);
  333. return o;
  334. }
  335. Sk.builtin.Exception.apply(this, arguments);
  336. };
  337. Sk.abstr.setUpInheritance("Feedback", Sk.builtin.Feedback, Sk.builtin.Exception);
  338. /**
  339. * Skulpt Exception that represents a Success object, to be thrown when the user
  340. * completes their program successfully.
  341. *
  342. ** @param {Array} args - A list of optional arguments to pass to the Exception.
  343. * Usually this will be empty.
  344. */
  345. Sk.builtin.Success = function (args) {
  346. var o;
  347. if (!(this instanceof Sk.builtin.Success)) {
  348. o = Object.create(Sk.builtin.Success.prototype);
  349. o.constructor.apply(o, arguments);
  350. return o;
  351. }
  352. Sk.builtin.Exception.apply(this, arguments);
  353. };
  354. Sk.abstr.setUpInheritance("Success", Sk.builtin.Success, Sk.builtin.Exception);
  355. /**
  356. * Skulpt Exception that represents a Finished object, to be thrown when the user
  357. * completes their program successfully, but isn't in a problem with a "solution".
  358. * This is useful for open-ended canvases where we still want to capture the students'
  359. * code in Canvas.
  360. *
  361. ** @param {Array} args - A list of optional arguments to pass to the Exception.
  362. * Usually this will be empty.
  363. */
  364. Sk.builtin.Finished = function (args) {
  365. var o;
  366. if (!(this instanceof Sk.builtin.Finished)) {
  367. o = Object.create(Sk.builtin.Finished.prototype);
  368. o.constructor.apply(o, arguments);
  369. return o;
  370. }
  371. Sk.builtin.Exception.apply(this, arguments);
  372. };
  373. Sk.abstr.setUpInheritance("Finished", Sk.builtin.Finished, Sk.builtin.Exception);
  374. /**
  375. * A Skulpt function that throws a Feedback exception, allowing us to give feedback
  376. * to the user through the Feedback panel. This function call is done for aesthetic
  377. * reasons, so that we are calling a function instead of raising an error. Still,
  378. * exceptions allow us to break out of the control flow immediately, like a
  379. * return, so they are a good mechanism to use under the hood.
  380. *
  381. * @param {String} message - The message to display to the user.
  382. */
  383. mod.set_feedback = new Sk.builtin.func(function(message) {
  384. Sk.builtin.pyCheckArgs("set_feedback", arguments, 1, 1);
  385. Sk.builtin.pyCheckType("message", "string", Sk.builtin.checkString(message));
  386. throw new Sk.builtin.Feedback(message.v);
  387. });
  388. /**
  389. * A Skulpt function that throws a Success exception. This will terminate the
  390. * feedback analysis, but reports that the students' code was successful.
  391. * This function call is done for aesthetic reasons, so that we are calling a
  392. * function instead of raising an error. Still, exceptions allow us to break
  393. * out of the control flow immediately, like a return would, so they are a
  394. * good mechanism to use under the hood.
  395. */
  396. mod.set_success = new Sk.builtin.func(function() {
  397. Sk.builtin.pyCheckArgs("set_success", arguments, 0, 0);
  398. throw new Sk.builtin.Success();
  399. });
  400. /**
  401. * A Skulpt function that throws a Finished exception. This will terminate the
  402. * feedback analysis, but reports that the students' code was successful.
  403. * This function call is done for aesthetic reasons, so that we are calling a
  404. * function instead of raising an error. Still, exceptions allow us to break
  405. * out of the control flow immediately, like a return would, so they are a
  406. * good mechanism to use under the hood.
  407. */
  408. mod.set_finished = new Sk.builtin.func(function() {
  409. Sk.builtin.pyCheckArgs("set_finished", arguments, 0, 0);
  410. throw new Sk.builtin.Finished();
  411. });
  412. // Memoization of previous parses - some mild redundancy to save time
  413. // TODO: There's no evidence this is good, and could be a memory hog on big
  414. // programs. Someone should investigate this. The assumption is that multiple
  415. // helper functions might be using parses. But shouldn't we trim old parses?
  416. // Perhaps a timed cache would work better.
  417. var parses = {};
  418. /**
  419. * Given source code as a string, return a flat list of all of the AST elements
  420. * in the parsed source code.
  421. *
  422. * TODO: There's redundancy here, since the source code was previously parsed
  423. * to run the file and to execute it. We should probably be able to do this just
  424. * once and shave off time.
  425. *
  426. * @param {String} source - Python source code.
  427. * @returns {Array.&lt;Object>}
  428. */
  429. function getParseList(source) {
  430. if (!(source in parses)) {
  431. var parse = Sk.parse("__main__", source);
  432. parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
  433. }
  434. var ast = parses[source];
  435. return (new NodeVisitor()).recursive_walk(ast);
  436. }
  437. /**
  438. * Given source code as a string, return a list of all of the AST elements
  439. * that are Num (aka numeric literals) but that are not inside List elements.
  440. *
  441. * @param {String} source - Python source code.
  442. * @returns {Array.number} The list of JavaScript numeric literals that were found.
  443. */
  444. function getNonListNums(source) {
  445. if (!(source in parses)) {
  446. var parse = Sk.parse("__main__", source);
  447. parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
  448. }
  449. var ast = parses[source];
  450. var visitor = new NodeVisitor();
  451. var insideList = false;
  452. var nums = [];
  453. visitor.visit_List = function(node) {
  454. insideList = true;
  455. this.generic_visit(node);
  456. insideList = false;
  457. }
  458. visitor.visit_Num = function(node) {
  459. if (!insideList) {
  460. nums.push(node.n);
  461. }
  462. this.generic_visit(node);
  463. }
  464. visitor.visit(ast);
  465. return nums;
  466. }
  467. /**
  468. * Given source code as a string, return a list of all of the AST elements
  469. * that are being printed (using the print function) but are not variables.
  470. *
  471. * @param {String} source - Python source code.
  472. * @returns {Array.&lt;Object>} The list of AST elements that were found.
  473. */
  474. function getPrintedNonProperties(source) {
  475. if (!(source in parses)) {
  476. var parse = Sk.parse("__main__", source);
  477. parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
  478. }
  479. var ast = parses[source];
  480. var visitor = new NodeVisitor();
  481. var nonVariables = [];
  482. visitor.visit_Call = function(node) {
  483. var func = node.func;
  484. var args = node.args;
  485. if (func._astname == 'Name' &amp;&amp; func.id.v == 'print') {
  486. for (var i =0; i &lt; args.length; i+= 1) {
  487. if (args[i]._astname != "Name") {
  488. nonVariables.push(args[i]);
  489. }
  490. }
  491. }
  492. this.generic_visit(node);
  493. }
  494. visitor.visit(ast);
  495. return nonVariables;
  496. }
  497. /**
  498. * Skulpt function to iterate through the final state of
  499. * all the variables in the program, and check to see if they have
  500. * a given value.
  501. */
  502. mod.get_value_by_name = new Sk.builtin.func(function(name) {
  503. Sk.builtin.pyCheckArgs("get_value_by_name", arguments, 1, 1);
  504. Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
  505. name = name.v;
  506. var final_values = Sk.builtins._final_values;
  507. if (name in final_values) {
  508. return final_values[name];
  509. } else {
  510. return Sk.builtin.none.none$;
  511. }
  512. });
  513. mod.get_value_by_type = new Sk.builtin.func(function(type) {
  514. Sk.builtin.pyCheckArgs("get_value_by_type", arguments, 1, 1);
  515. var final_values = Sk.builtins._final_values;
  516. var result = [];
  517. for (var property in final_values) {
  518. if (final_values[property].tp$name == type.tp$name) {
  519. result.push(final_values[property]);
  520. }
  521. }
  522. return Sk.builtin.list(result);
  523. });
  524. mod.parse_json = new Sk.builtin.func(function(blob) {
  525. Sk.builtin.pyCheckArgs("parse_json", arguments, 1, 1);
  526. Sk.builtin.pyCheckType("blob", "string", Sk.builtin.checkString(blob));
  527. blob = blob.v;
  528. return Sk.ffi.remapToPy(JSON.parse(blob));
  529. });
  530. mod.get_property = new Sk.builtin.func(function(name) {
  531. Sk.builtin.pyCheckArgs("get_property", arguments, 1, 1);
  532. Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
  533. name = name.v;
  534. var trace = Sk.builtins._trace;
  535. if (trace.length &lt;= 0) {
  536. return Sk.builtin.none.none$;
  537. }
  538. var properties = trace[trace.length-1]["properties"];
  539. for (var i = 0, len = properties.length; i &lt; len; i += 1) {
  540. if (properties[i]['name'] == name) {
  541. return Sk.ffi.remapToPy(properties[i])
  542. }
  543. }
  544. return Sk.builtin.none.none$;
  545. });
  546. mod.calls_function = new Sk.builtin.func(function(source, name) {
  547. Sk.builtin.pyCheckArgs("calls_function", arguments, 2, 2);
  548. Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
  549. Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
  550. source = source.v;
  551. name = name.v;
  552. var ast_list = getParseList(source);
  553. var count = 0;
  554. for (var i = 0, len = ast_list.length; i &lt; len; i = i+1) {
  555. if (ast_list[i]._astname == 'Call') {
  556. if (ast_list[i].func._astname == 'Attribute') {
  557. count += Sk.ffi.remapToJs(ast_list[i].func.attr) == name | 0;
  558. } else if (ast_list[i].func._astname == 'Name') {
  559. count += Sk.ffi.remapToJs(ast_list[i].func.id) == name | 0;
  560. }
  561. }
  562. }
  563. return Sk.ffi.remapToPy(count > 0);
  564. });
  565. mod.count_components = new Sk.builtin.func(function(source, component) {
  566. Sk.builtin.pyCheckArgs("count_components", arguments, 2, 2);
  567. Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
  568. Sk.builtin.pyCheckType("component", "string", Sk.builtin.checkString(component));
  569. source = source.v;
  570. component = component.v;
  571. var ast_list = getParseList(source);
  572. var count = 0;
  573. for (var i = 0, len = ast_list.length; i &lt; len; i = i+1) {
  574. if (ast_list[i]._astname == component) {
  575. count = count+1;
  576. }
  577. }
  578. return Sk.ffi.remapToPy(count);
  579. });
  580. mod.no_nonlist_nums = new Sk.builtin.func(function(source) {
  581. Sk.builtin.pyCheckArgs("no_nonlist_nums", arguments, 1, 1);
  582. Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
  583. source = source.v;
  584. var num_list = getNonListNums(source);
  585. var count = 0;
  586. for (var i = 0, len = num_list.length; i &lt; len; i = i+1) {
  587. if (num_list[i].v != 0 &amp;&amp; num_list[i].v != 1) {
  588. return Sk.ffi.remapToPy(true);
  589. }
  590. }
  591. return Sk.ffi.remapToPy(false);
  592. });
  593. mod.only_printing_properties = new Sk.builtin.func(function(source) {
  594. Sk.builtin.pyCheckArgs("only_printing_properties", arguments, 1, 1);
  595. Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
  596. source = source.v;
  597. var non_var_list = getPrintedNonProperties(source);
  598. return Sk.ffi.remapToPy(non_var_list.length == 0);
  599. });
  600. return mod;
  601. }
  602. BlockPyEngine.prototype.setupEnvironment = function(student_code, traceTable, output, ast, final_values) {
  603. var model = this.main.model;
  604. this._backup_execution = Sk.afterSingleExecution;
  605. Sk.afterSingleExecution = undefined;
  606. Sk.builtins.get_output = new Sk.builtin.func(function() {
  607. Sk.builtin.pyCheckArgs("get_output", arguments, 0, 0);
  608. return Sk.ffi.remapToPy(model.execution.output());
  609. });
  610. Sk.builtins.reset_output = new Sk.builtin.func(function() {
  611. Sk.builtin.pyCheckArgs("reset_output", arguments, 0, 0);
  612. model.execution.output.removeAll();
  613. });
  614. Sk.builtins.log = new Sk.builtin.func(function(data) {
  615. Sk.builtin.pyCheckArgs("log", arguments, 1, 1);
  616. console.log(data)
  617. });
  618. //Sk.builtins.trace = Sk.ffi.remapToPy(traceTable);
  619. Sk.builtins._trace = traceTable;
  620. Sk.builtins._final_values = final_values;
  621. Sk.builtins.code = Sk.ffi.remapToPy(student_code);
  622. Sk.builtins.set_success = this.instructor_module.set_success;
  623. Sk.builtins.set_feedback = this.instructor_module.set_feedback;
  624. Sk.builtins.set_finished = this.instructor_module.set_finished;
  625. Sk.builtins.count_components = this.instructor_module.count_components;
  626. Sk.builtins.no_nonlist_nums = this.instructor_module.no_nonlist_nums;
  627. Sk.builtins.only_printing_properties = this.instructor_module.only_printing_properties;
  628. Sk.builtins.calls_function = this.instructor_module.calls_function;
  629. Sk.builtins.get_property = this.instructor_module.get_property;
  630. Sk.builtins.get_value_by_name = this.instructor_module.get_value_by_name;
  631. Sk.builtins.get_value_by_type = this.instructor_module.get_value_by_type;
  632. Sk.builtins.parse_json = this.instructor_module.parse_json;
  633. Sk.skip_drawing = true;
  634. model.settings.mute_printer(true);
  635. }
  636. BlockPyEngine.prototype.disposeEnvironment = function() {
  637. Sk.afterSingleExecution = this._backup_execution;
  638. Sk.builtins.get_output = undefined;
  639. Sk.builtins.reset_output = undefined;
  640. Sk.builtins.log = undefined;
  641. Sk.builtins._trace = undefined;
  642. Sk.builtins.trace = undefined;
  643. Sk.builtins.code = undefined;
  644. Sk.builtins.set_success = undefined;
  645. Sk.builtins.set_feedback = undefined;
  646. Sk.builtins.set_finished = undefined;
  647. Sk.builtins.count_components = undefined;
  648. Sk.builtins.calls_function = undefined;
  649. Sk.builtins.get_property = undefined;
  650. Sk.builtins.get_value_by_name = undefined;
  651. Sk.builtins.get_value_by_type = undefined;
  652. Sk.builtins.no_nonlist_nums = undefined;
  653. Sk.builtins.only_printing_properties = undefined;
  654. Sk.builtins.parse_json = undefined;
  655. Sk.skip_drawing = false;
  656. GLOBAL_VALUE = undefined;
  657. this.main.model.settings.mute_printer(false);
  658. }
  659. BlockPyEngine.prototype.check = function(student_code, traceTable, output, ast, final_values) {
  660. var engine = this;
  661. var server = this.main.components.server;
  662. var model = this.main.model;
  663. var on_run = model.programs['give_feedback']();
  664. if (on_run !== undefined &amp;&amp; on_run.trim() !== "") {
  665. on_run = 'def run_code():\n'+indent(student_code)+'\n'+on_run;
  666. this.setupEnvironment(student_code, traceTable, output, ast, final_values);
  667. var executionPromise = Sk.misceval.asyncToPromise(function() {
  668. return Sk.importMainWithBody("&lt;stdin>", false, on_run, true);
  669. });
  670. executionPromise.then(
  671. function (module) {
  672. engine.main.components.feedback.noErrors();
  673. engine.disposeEnvironment();
  674. }, function (error) {
  675. engine.disposeEnvironment();
  676. console.log(error.tp$name, error.tp$name == "Success");
  677. if (error.tp$name == "Success") {
  678. server.markSuccess(1.0);
  679. engine.main.components.feedback.complete();
  680. } else if (error.tp$name == "Feedback") {
  681. server.markSuccess(0.0);
  682. engine.main.components.feedback.instructorFeedback("Incorrect Answer", error.args.v[0].v);
  683. } else if (error.tp$name == "Finished") {
  684. server.markSuccess(1.0);
  685. engine.main.components.feedback.finished();
  686. } else {
  687. console.error(error);
  688. engine.main.components.feedback.internalError(error, "Feedback Error", "Error in instructor's feedback. Please show the above message to an instructor!");
  689. server.logEvent('blockly_instructor_error', ''+error);
  690. }
  691. });
  692. }
  693. }
  694. BlockPyEngine.prototype.parseGlobals = function(variables) {
  695. var result = Array();
  696. var modules = Array();
  697. for (var property in variables) {
  698. var value = variables[property];
  699. if (property !== "__name__" &amp;&amp; property !== "__doc__") {
  700. property = property.replace('_$rw$', '')
  701. .replace('_$rn$', '');
  702. var parsed = this.parseValue(property, value);
  703. if (parsed !== null) {
  704. result.push(parsed);
  705. } else if (value.constructor == Sk.builtin.module) {
  706. modules.push(value.$d.__name__.v);
  707. }
  708. }
  709. }
  710. return {"properties": result, "modules": modules};
  711. }
  712. BlockPyEngine.prototype.parseValue = function(property, value) {
  713. if (value == undefined) {
  714. return {'name': property,
  715. 'type': 'Unknown',
  716. "value": 'Undefined'
  717. };
  718. }
  719. switch (value.constructor) {
  720. case Sk.builtin.func:
  721. return {'name': property,
  722. 'type': "Function",
  723. "value":
  724. (value.func_code.co_varnames !== undefined ?
  725. " Arguments: "+value.func_code.co_varnames.join(", ") :
  726. ' No arguments')
  727. };
  728. case Sk.builtin.module: return null;
  729. case Sk.builtin.str:
  730. return {'name': property,
  731. 'type': "String",
  732. "value": value.$r().v
  733. };
  734. case Sk.builtin.none:
  735. return {'name': property,
  736. 'type': "None",
  737. "value": "None"
  738. };
  739. case Sk.builtin.bool:
  740. return {'name': property,
  741. 'type': "Boolean",
  742. "value": value.$r().v
  743. };
  744. case Sk.builtin.nmber:
  745. return {'name': property,
  746. 'type': "int" == value.skType ? "Integer": "Float",
  747. "value": value.$r().v
  748. };
  749. case Sk.builtin.int_:
  750. return {'name': property,
  751. 'type': "Integer",
  752. "value": value.$r().v
  753. };
  754. case Sk.builtin.float_:
  755. return {'name': property,
  756. 'type': "Float",
  757. "value": value.$r().v
  758. };
  759. case Sk.builtin.tuple:
  760. return {'name': property,
  761. 'type': "Tuple",
  762. "value": value.$r().v
  763. };
  764. case Sk.builtin.list:
  765. if (value.v.length &lt;= 20) {
  766. return {'name': property,
  767. 'type': "List",
  768. "value": value.$r().v,
  769. 'exact_value': value
  770. };
  771. } else {
  772. return {'name': property,
  773. 'type': "List",
  774. "value": "[... "+value.v.length+" elements ...]",
  775. "exact_value": value
  776. };
  777. }
  778. case Sk.builtin.dict:
  779. return {'name': property,
  780. 'type': "Dictionary",
  781. "value": value.$r().v
  782. };
  783. case Number:
  784. return {'name': property,
  785. 'type': value % 1 === 0 ? "Integer" : "Float",
  786. "value": value
  787. };
  788. case String:
  789. return {'name': property,
  790. 'type': "String",
  791. "value": value
  792. };
  793. case Boolean:
  794. return {'name': property,
  795. 'type': "Boolean",
  796. "value": (value ? "True": "False")
  797. };
  798. default:
  799. return {'name': property,
  800. 'type': value.tp$name == undefined ? value : value.tp$name,
  801. "value": value.$r == undefined ? value : value.$r().v
  802. };
  803. }
  804. }
  805. </code></pre>
  806. </article>
  807. </section>
  808. </div>
  809. <nav>
  810. <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="BlockPy.html">BlockPy</a></li><li><a href="BlockPyCorgis.html">BlockPyCorgis</a></li><li><a href="BlockPyDialog.html">BlockPyDialog</a></li><li><a href="BlockPyEditor.html">BlockPyEditor</a></li><li><a href="BlockPyEngine.html">BlockPyEngine</a></li><li><a href="BlockPyEnglish.html">BlockPyEnglish</a></li><li><a href="BlockPyFeedback.html">BlockPyFeedback</a></li><li><a href="BlockPyHistory.html">BlockPyHistory</a></li><li><a href="BlockPyPresentation.html">BlockPyPresentation</a></li><li><a href="BlockPyPrinter.html">BlockPyPrinter</a></li><li><a href="BlockPyServer.html">BlockPyServer</a></li><li><a href="BlockPyToolbar.html">BlockPyToolbar</a></li><li><a href="LocalStorageWrapper.html">LocalStorageWrapper</a></li><li><a href="PythonToBlocks.html">PythonToBlocks</a></li></ul><h3>Global</h3><ul><li><a href="global.html#BlockPyInterface">BlockPyInterface</a></li><li><a href="global.html#cloneNode">cloneNode</a></li><li><a href="global.html#encodeHTML">encodeHTML</a></li><li><a href="global.html#expandArray">expandArray</a></li><li><a href="global.html#EXTENDED_ERROR_EXPLANATION">EXTENDED_ERROR_EXPLANATION</a></li><li><a href="global.html#indent">indent</a></li><li><a href="global.html#instructor_module">instructor_module</a></li><li><a href="global.html#prettyPrintDateTime">prettyPrintDateTime</a></li><li><a href="global.html#randomInteger">randomInteger</a></li><li><a href="global.html#set_button_loaded">set_button_loaded</a></li><li><a href="global.html#timerGuard">timerGuard</a></li></ul>
  811. </nav>
  812. <br class="clear">
  813. <footer>
  814. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.3</a> on Sun Mar 26 2017 09:45:03 GMT-0400 (Eastern Daylight Time)
  815. </footer>
  816. <script> prettyPrint(); </script>
  817. <script src="scripts/linenumber.js"> </script>
  818. </body>
  819. </html>