123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /**
- Parse student code
- Analyze student code
- Run student code
- Run instructor code
- Output feedback
- # Within the instructor code you can surpress certain kinds of feedback
- surpress_syntax()
- surpress_empty()
- surpress_algorithm()
- surpress_runtime()
- surpress_runtime('NameError')
- # Or explicitly run certain kinds of analyses
- run_student()
- run_abstract()
- */
-
- /**
- * Given source code as a string, return a list of all of the AST elements
- * that are Num (aka numeric literals) but that are not inside List elements.
- *
- * @param {String} source - Python source code.
- * @returns {Array.number} The list of JavaScript numeric literals that were found.
- */
- function getNonListNums(source) {
- if (!(source in parses)) {
- var parse = Sk.parse("__main__", source);
- parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
- }
- var ast = parses[source];
- var visitor = new NodeVisitor();
- var insideList = false;
- var nums = [];
- visitor.visit_List = function(node) {
- insideList = true;
- this.generic_visit(node);
- insideList = false;
- }
- visitor.visit_Num = function(node) {
- if (!insideList) {
- nums.push(node.n);
- }
- this.generic_visit(node);
- }
- visitor.visit(ast);
- return nums;
- }
-
- /**
- * Given source code as a string, return a list of all of the AST elements
- * that are being printed (using the print function) but are not variables.
- *
- * @param {String} source - Python source code.
- * @returns {Array.<Object>} The list of AST elements that were found.
- */
- function getPrintedNonProperties(source) {
- if (!(source in parses)) {
- var parse = Sk.parse("__main__", source);
- parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
- }
- var ast = parses[source];
- var visitor = new NodeVisitor();
- var nonVariables = [];
- visitor.visit_Call = function(node) {
- var func = node.func;
- var args = node.args;
- if (func._astname == 'Name' && func.id.v == 'print') {
- for (var i =0; i < args.length; i+= 1) {
- if (args[i]._astname != "Name") {
- nonVariables.push(args[i]);
- }
- }
- }
- this.generic_visit(node);
- }
- visitor.visit(ast);
- return nonVariables;
- }
-
- /**
- * Skulpt function to iterate through the final state of
- * all the variables in the program, and check to see if they have
- * a given value.
- */
- mod.get_value_by_name = new Sk.builtin.func(function(name) {
- Sk.builtin.pyCheckArgs("get_value_by_name", arguments, 1, 1);
- Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
- name = name.v;
- var final_values = Sk.builtins._final_values;
- if (name in final_values) {
- return final_values[name];
- } else {
- return Sk.builtin.none.none$;
- }
- });
- mod.get_value_by_type = new Sk.builtin.func(function(type) {
- Sk.builtin.pyCheckArgs("get_value_by_type", arguments, 1, 1);
-
- var final_values = Sk.builtins._final_values;
- var result = [];
- for (var property in final_values) {
- if (final_values[property].tp$name == type.tp$name) {
- result.push(final_values[property]);
- }
- }
- return Sk.builtin.list(result);
- });
-
- mod.parse_json = new Sk.builtin.func(function(blob) {
- Sk.builtin.pyCheckArgs("parse_json", arguments, 1, 1);
- Sk.builtin.pyCheckType("blob", "string", Sk.builtin.checkString(blob));
- blob = blob.v;
- return Sk.ffi.remapToPy(JSON.parse(blob));
- });
- mod.get_property = new Sk.builtin.func(function(name) {
- Sk.builtin.pyCheckArgs("get_property", arguments, 1, 1);
- Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
- name = name.v;
- var trace = Sk.builtins._trace;
- if (trace.length <= 0) {
- return Sk.builtin.none.none$;
- }
- var properties = trace[trace.length-1]["properties"];
- for (var i = 0, len = properties.length; i < len; i += 1) {
- if (properties[i]['name'] == name) {
- return Sk.ffi.remapToPy(properties[i])
- }
- }
- return Sk.builtin.none.none$;
- });
-
- mod.calls_function = new Sk.builtin.func(function(source, name) {
- Sk.builtin.pyCheckArgs("calls_function", arguments, 2, 2);
- Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
- Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
-
- source = source.v;
- name = name.v;
-
- var ast_list = getParseList(source);
-
- var count = 0;
- for (var i = 0, len = ast_list.length; i < len; i = i+1) {
- if (ast_list[i]._astname == 'Call') {
- if (ast_list[i].func._astname == 'Attribute') {
- count += Sk.ffi.remapToJs(ast_list[i].func.attr) == name | 0;
- } else if (ast_list[i].func._astname == 'Name') {
- count += Sk.ffi.remapToJs(ast_list[i].func.id) == name | 0;
- }
- }
- }
-
- return Sk.ffi.remapToPy(count > 0);
- });
-
- mod.count_components = new Sk.builtin.func(function(source, component) {
- Sk.builtin.pyCheckArgs("count_components", arguments, 2, 2);
- Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
- Sk.builtin.pyCheckType("component", "string", Sk.builtin.checkString(component));
-
- source = source.v;
- component = component.v;
-
- var ast_list = getParseList(source);
-
- var count = 0;
- for (var i = 0, len = ast_list.length; i < len; i = i+1) {
- if (ast_list[i]._astname == component) {
- count = count+1;
- }
- }
-
- return Sk.ffi.remapToPy(count);
- });
-
- mod.no_nonlist_nums = new Sk.builtin.func(function(source) {
- Sk.builtin.pyCheckArgs("no_nonlist_nums", arguments, 1, 1);
- Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
-
- source = source.v;
-
- var num_list = getNonListNums(source);
-
- var count = 0;
- for (var i = 0, len = num_list.length; i < len; i = i+1) {
- if (num_list[i].v != 0 && num_list[i].v != 1) {
- return Sk.ffi.remapToPy(true);
- }
- }
- return Sk.ffi.remapToPy(false);
- });
- mod.only_printing_properties = new Sk.builtin.func(function(source) {
- Sk.builtin.pyCheckArgs("only_printing_properties", arguments, 1, 1);
- Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
-
- source = source.v;
-
- var non_var_list = getPrintedNonProperties(source);
- return Sk.ffi.remapToPy(non_var_list.length == 0);
- });
-
- /**
- * Given a feedback string, records the corrective feedback string for later printing
- * @param {string} feedback - the piece of feedback to save
- **/
- mod.add_interrupt_feedback = new Sk.builtin.func(function(feedback) {
- Sk.builtin.pyCheckArgs("add_interupt_feedback", arguments, 1, 1);
- Sk.builtin.pyCheckType("feedback", "string", Sk.builtin.checkString(feedback));
- accInterruptFeedback.push(feedback);
- });
- /**
- * Given a feedback string, records the complementary feedback string for later printing
- * @param {string} feedback - the piece of feedback to save
- **/
- mod.add_comp_feedback = new Sk.builtin.func(function(feedback) {
- Sk.builtin.pyCheckArgs("add_comp_feedback", arguments, 1, 1);
- Sk.builtin.pyCheckType("feedback", "string", Sk.builtin.checkString(feedback));
- accCompFeedback.push(feedback);
- });
- /**
- * This resolves all of the feedback and posts it to the appropriate places
- * @TODO: actually implement this functionality
- **/
- mod.post_feedback = new Sk.builtin.func(function() {
- var allFeedback = accInterruptFeedback.concat(accCompFeedback);
- completePythonAll = Sk.builtin.list(allFeedback);
- jsPureFeedback = Sk.ffi.remapToJs(completePythonAll);
- console.log("" + jsPureFeedback);
- });
- Sk.builtins.value = new Sk.builtin.func(function() {
- return Sk.ffi.remapToPy(GLOBAL_VALUE === undefined ? 5 : GLOBAL_VALUE);
- });
- Sk.builtins.set_value = new Sk.builtin.func(function(v) {
- GLOBAL_VALUE = v.v;
- });
- /**
- * Runs the given python code, resetting the console and Trace Table.
- */
- BlockPyEngine.prototype.run = function() {
- // Reset everything
- this.resetExecution();
-
- if (!this.main.model.settings.disable_semantic_errors() &&
- !this.main.model.assignment.disable_algorithm_errors()) {
- var success = this.analyze();
- if (success === false) {
- this.executionEnd_();
- this.main.components.server.markSuccess(0.0);
- return;
- }
- }
-
- this.main.model.execution.status("running");
-
- var feedback = this.main.components.feedback;
-
- // Get the code
- var code = this.main.model.programs['__main__']();
- if (code.trim() == "") {
- feedback.emptyProgram();
- this.main.model.execution.status("error");
- this.executionEnd_();
- this.main.components.server.markSuccess(0.0);
- return;
- }
- // Actually run the python code
- var executionPromise = Sk.misceval.asyncToPromise(function() {
- return Sk.importMainWithBody("<stdin>", false, code, true);
- });
-
- var engine = this;
- var server = this.server;
- var execution = this.main.model.execution;
- executionPromise.then(
- function (module) {
- // Run the afterSingleExecution one extra time for final state
- Sk.afterSingleExecution(module.$d, -1, 0, "<stdin>.py");
- engine.lastStep();
- // Handle checks
- feedback.noErrors()
- engine.check(code, execution.trace(), execution.output(), execution.ast, module.$d);
- // Reenable "Run"
- engine.main.model.execution.status("waiting");
- engine.executionEnd_();
- },
- function(error) {
- feedback.printError(error);
- engine.main.model.execution.status("error");
- engine.executionEnd_();
- server.markSuccess(0.0);
- //server.logEvent('blockly_error', error);
- }
- );
- }
- BlockPyEngine.prototype.setupEnvironment = function(student_code, traceTable, output, ast, final_values) {
- var model = this.main.model;
- this._backup_execution = Sk.afterSingleExecution;
- Sk.afterSingleExecution = undefined;
- Sk.builtins.get_output = new Sk.builtin.func(function() {
- Sk.builtin.pyCheckArgs("get_output", arguments, 0, 0);
- return Sk.ffi.remapToPy(model.execution.output());
- });
- Sk.builtins.reset_output = new Sk.builtin.func(function() {
- Sk.builtin.pyCheckArgs("reset_output", arguments, 0, 0);
- model.execution.output.removeAll();
- });
- Sk.builtins.log = new Sk.builtin.func(function(data) {
- Sk.builtin.pyCheckArgs("log", arguments, 1, 1);
- console.log(data)
- });
- //Sk.builtins.trace = Sk.ffi.remapToPy(traceTable);
- Sk.builtins._trace = traceTable;
- Sk.builtins._final_values = final_values;
- Sk.builtins.code = Sk.ffi.remapToPy(student_code);
- Sk.builtins.set_success = this.instructor_module.set_success;
- Sk.builtins.set_feedback = this.instructor_module.set_feedback;
- Sk.builtins.set_finished = this.instructor_module.set_finished;
- Sk.builtins.count_components = this.instructor_module.count_components;
- Sk.builtins.no_nonlist_nums = this.instructor_module.no_nonlist_nums;
- Sk.builtins.only_printing_properties = this.instructor_module.only_printing_properties;
- Sk.builtins.calls_function = this.instructor_module.calls_function;
- Sk.builtins.get_property = this.instructor_module.get_property;
- Sk.builtins.get_value_by_name = this.instructor_module.get_value_by_name;
- Sk.builtins.get_value_by_type = this.instructor_module.get_value_by_type;
- Sk.builtins.parse_json = this.instructor_module.parse_json;
- Sk.builtins.Stack = this.instructor_module.Stack;
- Sk.skip_drawing = true;
- model.settings.mute_printer(true);
- }
- BlockPyEngine.prototype.disposeEnvironment = function() {
- Sk.afterSingleExecution = this._backup_execution;
- Sk.builtins.get_output = undefined;
- Sk.builtins.reset_output = undefined;
- Sk.builtins.log = undefined;
- Sk.builtins._trace = undefined;
- Sk.builtins.trace = undefined;
- Sk.builtins.code = undefined;
- Sk.builtins.set_success = undefined;
- Sk.builtins.set_feedback = undefined;
- Sk.builtins.set_finished = undefined;
- Sk.builtins.count_components = undefined;
- Sk.builtins.calls_function = undefined;
- Sk.builtins.get_property = undefined;
- Sk.builtins.get_value_by_name = undefined;
- Sk.builtins.get_value_by_type = undefined;
- Sk.builtins.no_nonlist_nums = undefined;
- Sk.builtins.only_printing_properties = undefined;
- Sk.builtins.parse_json = undefined;
- Sk.skip_drawing = false;
- GLOBAL_VALUE = undefined;
- this.main.model.settings.mute_printer(false);
- }
- BlockPyEngine.prototype.check = function(student_code, traceTable, output, ast, final_values) {
- var engine = this;
- var server = this.main.components.server;
- var model = this.main.model;
- var on_run = model.programs['give_feedback']();
- if (on_run !== undefined && on_run.trim() !== "") {
- on_run = 'def run_code():\n'+indent(student_code)+'\n'+on_run;
- this.setupEnvironment(student_code, traceTable, output, ast, final_values);
-
- var executionPromise = Sk.misceval.asyncToPromise(function() {
- return Sk.importMainWithBody("<stdin>", false, on_run, true);
- });
- executionPromise.then(
- function (module) {
- engine.main.components.feedback.noErrors();
- engine.disposeEnvironment();
- }, function (error) {
- engine.disposeEnvironment();
- console.log(error.tp$name, error.tp$name == "Success");
- if (error.tp$name == "Success") {
- server.markSuccess(1.0, model.settings.completedCallback);
- engine.main.components.feedback.complete();
- } else if (error.tp$name == "Feedback") {
- server.markSuccess(0.0);
- engine.main.components.feedback.instructorFeedback("Incorrect Answer", error.args.v[0].v);
- } else if (error.tp$name == "Finished") {
- server.markSuccess(1.0, model.settings.completedCallback);
- engine.main.components.feedback.finished();
- } else {
- console.error(error);
- engine.main.components.feedback.internalError(error, "Feedback Error", "Error in instructor's feedback. Please show the above message to an instructor!");
- server.logEvent('blockly_instructor_error', ''+error);
- server.markSuccess(0.0);
- }
- });
- }
- }
- /**
- * Runs the AbstractInterpreter to get some static information about the code,
- * in particular the variables' types. This is needed for type checking.
- *
- * @returns {Object<String, AIType>} Maps variable names (as Strings) to types as constructed by the AbstractIntepreter.
- */
- BlockPyEngine.prototype.analyzeVariables = function() {
- // Get the code
- var code = this.main.model.programs['__main__']();
- if (code.trim() == "") {
- return {};
- }
-
- var analyzer = new AbstractInterpreter(code);
- report = analyzer.report;
- return analyzer.variableTypes;
- }
- /**
- * Runs the AbstractInterpreter to get some static information about the code,
- * including potential semantic errors. It then parses that information to give
- * feedback.
- *
- * @returns {Boolean} Whether the code was successfully analyzed.
- */
- BlockPyEngine.prototype.analyze = function() {
- this.main.model.execution.status("analyzing");
-
- var feedback = this.main.components.feedback;
-
- // Get the code
- var code = this.main.model.programs['__main__']();
- if (code.trim() == "") {
- this.main.components.feedback.emptyProgram("You haven't written any code yet!");
- //this.main.model.feedback.status("semantic");
- return false;
- }
-
- var analyzer = new AbstractInterpreter(code);
- this.main.model.execution.ast = analyzer.ast;
-
- report = analyzer.report;
- // Syntax error
- if (report.error !== false) {
- console.log(report.error.args.v)
- var codeLine = '.';
- if (report.error.args.v.length > 3) {
- codeLine = ', where it says:<br><code>'+report.error.args.v[3][2]+'</code>';
- }
- feedback.editorError(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]);
- return false;
- }
-
-
-
- return true;
- }
- var GLOBAL_VALUE;
|