var externalLibs = {//外部引入的第三方库 "./blocklygame/__init__.js": "../common/js/skulpt_mixtoy/bg_highlight1.js", "./blocktool/__init__.js": "../common/js/skulpt_mixtoy/inittool.js", "./bg_nonehl/__init__.js":"../common/js/skulpt_mixtoy/bg_nonehl1.js", "./bg_noanimation/__init__.js":"../common/js/skulpt_mixtoy/bg_noanimation1.js" }; // document.write(""); /** * An object for executing Python code and passing the results along to interested components. * * @constructor * @this {BlockPyEditor} * @param {Object} main - The main BlockPy instance * @param {HTMLElement} tag - The HTML object this is attached to. */ var PyEngine = function(programStatus, mixpyProject) { this.loadEngine(); this.programStatus = programStatus; this.mixpyProject = mixpyProject; } /** * Definable function to be run when execution has fully ended, * whether it succeeds or fails. * */ PyEngine.prototype.onExecutionEnd = null; /** * Helper function that will attempt to call the defined onExecutionEnd, * but will do nothing if there is no function defined. */ PyEngine.prototype.executionEnd_ = function() { if (this.onExecutionEnd !== null) { this.onExecutionEnd(); } }; /** * Initializes the Python Execution engine and the Printer (console). */ PyEngine.prototype.loadEngine = function() { var engine = this; // No connected services Sk.connectedServices = {} // TODO //Sk.console = printer.getConfiguration(); // Definitely use a prompt Sk.inputfunTakesPrompt = true; // Keeps track of the tracing while the program is executing; destroyed afterwards. this.executionBuffer = {}; } /** * Used to access Skulpt built-ins. This is pretty generic, taken * almost directly from the Skulpt docs. * * @param {String} filename - The python filename (e.g., "os" or "pprint") that will be loaded. * @returns {String} The JavaScript source code of the file (weird, right?) * @throws Will throw an error if the file isn't found. */ PyEngine.prototype.readFile = function(file) { console.log("Attempting file: " + Sk.ffi.remapToJs(file)); // 加载模块 if (PyGameZero.matchModelName(file)) { return PyGameZero.load(file) } if (externalLibs[file] !== undefined) { return Sk.misceval.promiseToSuspension( fetch(externalLibs[file]).then( function (resp){ return resp.text(); } )); } if (Sk.builtinFiles === undefined || Sk.builtinFiles.files[file] === undefined) { throw "File not found: '" + file + "'"; } return Sk.builtinFiles.files[file]; } PyEngine.prototype.fileread = function(filename, mode) { if(this.mixpyProject.exist(filename)){ return this.mixpyProject.getFileContent(filename); }else{ if(mode.indexOf('w') !== -1){ this.mixpyProject.add(filename, '', 1); return ''; }else return null; } } PyEngine.prototype.filewrite = function(fileItem, str) { var filename = fileItem.name; this.mixpyProject.modify(filename, str); this.mixpyProject.select(filename); } PyEngine.prototype.skInput = function(prompt) { return new Promise((resolve, reject) => { const { mainStatusBarTabs } = Mixly; const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); var currText = statusBarTerminal.getValue(); if (currText.lastIndexOf('\n') !== currText.length - 1) statusBarTerminal.addValue('\n>>>' + prompt); else statusBarTerminal.addValue('>>>' + prompt); const { editor } = statusBarTerminal; const { selection } = editor; editor.setReadOnly(false); const session = editor.getSession(); const initRow = session.getLength(); editor.gotoLine(initRow); selection.moveCursorLineEnd(); const initCursor = selection.getCursor(); const cursorCallback = session.selection.on('changeCursor', function (e) { const cursor = selection.getCursor(); if (cursor.row < initCursor.row) selection.moveCursorTo(initCursor.row, initCursor.column, true); else if (cursor.row === initCursor.row && cursor.column <= initCursor.column) selection.moveCursorTo(initCursor.row, initCursor.column, true); let endRowStr = session.getLine(cursor.row - 1); if (endRowStr.indexOf('>>>') !== -1 && cursor.row === initCursor.row + 1 && cursor.column === 0) { endRowStr = endRowStr.replace('>>>' + prompt, ''); session.selection.removeEventListener('changeCursor', cursorCallback); selection.moveCursorLineEnd(); editor.setReadOnly(true); resolve(endRowStr); } else { const nowRow = session.getLength(); if (nowRow !== initRow) { session.selection.removeEventListener('changeCursor', cursorCallback); editor.setReadOnly(true); resolve(''); } } }); }); } /** * Resets the state of the execution engine, including reinitailizing * the execution buffer (trace, step, etc.), reseting the printer, and * hiding the trace button. * */ PyEngine.prototype.reset = function() { const { mainStatusBarTabs } = Mixly; const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); //点击取消按钮发送数据 Sk.execLimit = 0; Sk.execLimit = Number.POSITIVE_INFINITY; //$("#side_code").text(''); //$("#blocklySVG").empty(); statusBarTerminal.setValue(''); } PyEngine.prototype.kill = function(type) { // event_save('kill'); //点击取消按钮发送数据 Sk.execLimit = 0; $("#loading").css('display', "none"); if (document.getElementById("img01")) { document.getElementById("img01").src=""; } } /** * "Steps" the execution of the code, meant to be used as a callback to the Skulpt * environment. * * @param {Object} variables - Hash that maps the names of variables (Strings) to their Skulpt representation. * @param {Number} lineNumber - The corresponding line number in the source code that is being executed. * @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. * @param {String} filename - The name of the python file being executed (e.g., "__main__.py"). * @param {String} astType - Unused? TODO: What is this? * @param {String} ast - String-encoded JSON representation of the AST node associated with this element. */ PyEngine.prototype.step = function(variables, lineNumber, columnNumber, filename, astType, ast) { if (filename == '.py') { var currentStep = this.executionBuffer.step; var globals = this.parseGlobals(variables); this.executionBuffer.trace.push( {'step': currentStep, 'filename': filename, //'block': highlightMap[lineNumber-1], 'line': lineNumber, 'column': columnNumber, 'properties': globals.properties, 'modules': globals.modules}); this.executionBuffer.step = currentStep+1; this.executionBuffer.last_step = currentStep+1; this.executionBuffer.line_number = lineNumber; } } /** * Runs the AbstractInterpreter to get some static information about the code, * in particular the variables' types. This is needed for type checking. * * @returns {Object} Maps variable names (as Strings) to types as constructed by the AbstractIntepreter. */ PyEngine.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. */ PyEngine.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:
'+report.error.args.v[3][2]+''; } 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]); return false; } if (report["Unconnected blocks"].length >= 1) { var variable = report['Unconnected blocks'][0]; 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) return false; } else if (report['Iteration variable is iteration list'].length >= 1) { var variable = report['Iteration variable is iteration list'][0]; feedback.semanticError("Iteration Problem", "The property "+variable.name+" 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., for dog in dogs:).", variable.position.line) return false; } else if (report["Undefined variables"].length >= 1) { var variable = report["Undefined variables"][0]; feedback.semanticError("Initialization Problem", "The property "+variable.name+" 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) return false; } else if (report["Possibly undefined variables"].length >= 1) { var variable = report["Possibly undefined variables"][0]; feedback.semanticError("Initialization Problem", "The property "+variable.name+" 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); return false; } else if (report["Unread variables"].length >= 1) { var variable = report["Unread variables"][0]; feedback.semanticError("Unused Property", "The property "+variable.name+" was set, but was never used after that.", null) return false; } else if (report["Overwritten variables"].length >= 1) { var variable = report["Overwritten variables"][0]; feedback.semanticError("Overwritten Property", "The property "+variable.name+" 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) return false; } else if (report["Empty iterations"].length >= 1) { var variable = report["Empty iterations"][0]; feedback.semanticError("Iterating over empty list", "The property "+variable.name+" 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) return false; } else if (report["Non-list iterations"].length >= 1) { var variable = report["Non-list iterations"][0]; feedback.semanticError("Iterating over non-list", "The property "+variable.name+" 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) return false; } else if (report["Incompatible types"].length >= 1) { var variable = report["Incompatible types"][0]; 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) return false; } return true; } var GLOBAL_VALUE; /** * Runs the given python code, resetting the console and Trace Table. * 分步调试代码 */ PyEngine.prototype.steprun = function(type) { const { mainStatusBarTabs } = Mixly; const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); statusBarTerminal.setValue(''); mainStatusBarTabs.show(); if (!$('#skulpt-img').length) { $('body').append($( `` )); } var xml = Blockly.Xml.workspaceToDom(Mixly.Editor.blockEditor); var data = Blockly.Xml.domToText(xml); if (document.getElementById("boardSelector").value == ""){ var fn = 'filename.xml' }else{ var fn = document.getElementById("boardSelector").value + '.xml' } window.actionArrayRecord = []; $("#blocklySVG").empty(); $("#loading").css('display', "inline-block"); // Reset everything this.reset(); //SK设置 this.loadEngine(); // Major Skulpt configurations Sk.configure({ // Function to handle the text outputted by Skulpt //设置文本输出 output: function(lineText){ statusBarTerminal.addValue(lineText); }, // Function to handle loading in new files read: this.readFile.bind(this), inputfun:this.skInput.bind(this), inputfunTakesPrompt: true, execLimit:Number.POSITIVE_INFINITY, fileread:this.fileread.bind(this), filewrite:this.filewrite.bind(this), __future__: Sk.python3,//python3已成默认值,要使用python2需要单独设置 }); 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; }); // Get the code var code = Mixly.MFile.getCode(); //如果存在游戏模块,则只能读取模块代码 if ((code.indexOf("import blocklygame")!=-1)||(code.indexOf("from blocklygame import")!=-1)){ code = Blockly.Python.workspaceToCode(Mixly.Editor.blockEditor) || '';//code code+="\nprint(blocklygame.getFinishState())" } var engine = this; if(code === "") { engine.programStatus['running'] = false; $("#loading").css('display', "none"); statusBarTerminal.setValue('==无程序需运行==\n'); return; } var pack_num = 0; //检验是否有数据分析、机器学习相关的模块 var pack_list = ["numpy","pandas","scikit-learn","matplotlib"]; var code_pack_list = []; for (var i = 0; i < pack_list.length; i++) { if ((code.indexOf("import "+pack_list[i])!=-1)||(code.indexOf("from "+pack_list[i]+" import")!=-1)){ pack_num++; code_pack_list.push(pack_list[i]) } } //检验是否存在pygame-zero模块 var pyzrun_pack="pgzrun" var ifpgzrun=false; if ((code.indexOf("import "+pyzrun_pack)!=-1)||(code.indexOf("from "+pyzrun_pack+" import")!=-1)){ ifpgzrun=true; } if (this.layerNum && this.layerSize) { this.layerSize.content = [ $('#skulpt-img').width(), $('#skulpt-img').height(), ]; } if(code_pack_list.includes('matplotlib')){ document.getElementById("mat_div").style.height='100%' document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='0%' $("#matplot_img").css('width', this?.layerSize?.content[0] ?? ($('body').width() / 2 + 'px')); $("#matplot_img").css('height',this?.layerSize?.content[1] ?? ($('body').height() * 0.7 - 30 + 'px')); } //如果存在pygame-zero,则进行相关的配置 if(ifpgzrun){ document.getElementById("mat_div").style.height='0%' document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='100%' const Sk = window.Sk; Sk.TurtleGraphics.reset && Sk.TurtleGraphics.reset() Sk.TurtleGraphics.target = "pggame_stage"; Sk.TurtleGraphics.width = (this?.layerSize?.content[0] ?? ($('body').width() / 2) - 30) - 5; Sk.TurtleGraphics.height = (this?.layerSize?.content[1] ?? ($('body').height() * 0.7 - 40)) - 5; const PyGameZero = window.PyGameZero; const stageSize = {width: 0, height: 0}; const stageEl = document.getElementById("pggame_stage"); // 配置容器 stageSize.width = $('#pggame_stage').width() - 50; stageSize.height = $('#pggame_stage').height() - 50; PyGameZero.setContainer(stageEl) PyGameZero.reset(); } //检验是否存在pygame-zero模块 var bg_nonehl_pack="blocklygame" var ifbg_nonehl=false; if ((code.indexOf("import "+bg_nonehl_pack)!=-1)||(code.indexOf("from "+bg_nonehl_pack+" import")!=-1)){ ifbg_nonehl=true; } //如果存在bg_nonehl,则进行相关的配置 if(ifbg_nonehl){ document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='0%' document.getElementById("blocklySVG").style.height='100%' } // event_save('steprun'); if (ifpgzrun || ifbg_nonehl) { const _this = this; this.layerNum = Mixly.LayerExt.open({ title: [Mixly.Msg.Lang['显示'], '30px'], shade: 0, offset: 'rt', area: _this?.layerSize?.layero ?? ['50%', '70%'], max: [$('body').width() + 'px', $('body').height() + 'px'], fixed: false, content: $('#skulpt-img'), resizing: function(size) { _this.layerSize = size; }, end: function() { _this.layerNum = null; pyengine.kill(); } }); } //已经读取了代码:code // Actually run the python code var executionPromise = Sk.misceval.asyncToPromise(function() { return Sk.importMainWithBody("", false, code, true); }); executionPromise.then( function (module) { engine.programStatus['running'] = false; $("#loading").css('display', "none"); // Run the afterSingleExecution one extra time for final state /*Sk.afterSingleExecution(module.$d, -1, 0, ".py"); engine.check(code, execution.trace(), execution.output(), execution.ast, module.$d); engine.executionEnd_(); */ statusBarTerminal.addValue('==程序运行完成==\n'); console.log('success'); }, function(error) { engine.programStatus['running'] = false; $("#loading").css('display', "none"); var original = prettyPrintError(error); //hack for kill program with time limiterror if(original.indexOf("TimeLimitError") !== -1) return; if($('.inputMsg').length > 0) $('#side_code').text($('#side_code').text() + $('.inputMsg').val()) // var $errorE = $('').addClass('errorMsg').html(original); // $("#side_code").append($errorE) statusBarTerminal.addValue(original); var matchRet = /on line (\d+)/.exec(original); var lineNumber = -1; if(matchRet !== null && matchRet.length == 2){ lineNumber = parseInt(matchRet[1]) - 1; editor.session.addMarker(new EditorRange(lineNumber, 0, lineNumber, 1), "errorMarker", "fullLine"); editor.hasMarker = true; } engine.executionEnd_(); console.log(error.toString()); const nowValue = statusBarTerminal.getValue(); if (nowValue.lastIndexOf('\n') !== nowValue.length - 1) { statusBarTerminal.addValue('\n'); } statusBarTerminal.addValue(error.toString() + '\n==程序运行完成==\n'); } ); } /** * Runs the given python code, resetting the console and Trace Table. * 直接运行,不分步高亮显示 */ PyEngine.prototype.run = function(type) { const { mainStatusBarTabs } = Mixly; const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); statusBarTerminal.setValue(''); mainStatusBarTabs.show(); if (!$('#skulpt-img').length) { $('body').append($( `` )); } var xml = Blockly.Xml.workspaceToDom(Mixly.Editor.blockEditor); var data = Blockly.Xml.domToText(xml); if (document.getElementById("boardSelector").value == ""){ var fn = 'filename.xml' }else{ var fn = document.getElementById("boardSelector").value + '.xml' } window.actionArrayRecord = []; $("#blocklySVG").empty(); $("#loading").css('display', "inline-block"); /*if(sidecodeDisplay === false){ sidecodeClick(); }*/ // Reset everything this.reset(); //SK设置 this.loadEngine(); // Major Skulpt configurations Sk.configure({ // Function to handle the text outputted by Skulpt //设置文本输出 output: function(lineText){ statusBarTerminal.addValue(lineText); }, // Function to handle loading in new files read: this.readFile.bind(this), inputfun:this.skInput.bind(this), inputfunTakesPrompt: true, execLimit:Number.POSITIVE_INFINITY, fileread:this.fileread.bind(this), filewrite:this.filewrite.bind(this), __future__: Sk.python2,//python3已成默认值,要使用python2需要单独设置 }); 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; }); // event_save('run'); // Get the code var code = Mixly.MFile.getCode(); if(code === "") { this.programStatus['running'] = false; $("#loading").css('display', "none"); statusBarTerminal.setValue('==无程序需运行==\n'); return; } //如果存在游戏模块,则只能读取模块代码 if ((code.indexOf("import blocklygame")!=-1)||(code.indexOf("from blocklygame import")!=-1)){ code = Blockly.Python.workspaceToCode(Mixly.Editor.blockEditor) || '';//code code+="\nprint(blocklygame.getFinishState())" } //改为只读区代码区的代码,这样就能够消除高亮执行 if ((code.indexOf("import blocktool")!=-1)||(code.indexOf("import blocklygame")!=-1)||(code.indexOf("from blocklygame import")!=-1)){ //正则匹配替换block id元素 var code_piece=[]; code_piece=code.split("\n"); for(var i=0;i= 0 ){ var target=""; var re=/,?'block_id=[\s\S]*'/.exec(code_piece[i]); if(re!=null){ target=re[0]; code_piece[i]=code_piece[i].replace(target,""); } } //检查是否是高亮辅助块\toll,如果是,则将此行代码移除 if((code_piece[i].indexOf("import blocktool") >= 0) || (code_piece[i].indexOf("blocktool.highlight") >= 0) ){ code_piece[i]="delete"; } //如果使用的是分步调试的blocklygame,要将其换成非分步调试的模块bg_nonehl if((code_piece[i].indexOf("blocklygame") >= 0)){ code_piece[i]=code_piece[i].replace("blocklygame","bg_nonehl"); } } code="" for(var i=0;i= 0){//处理print函数中的end var code_piece=[]; code_piece=code.split("\n"); for(var i=0;i= 0 ){ if(code_piece[i].indexOf(",end") >= 0 ){ var target=""; var re=/,end =([\s\S]*)\)$/.exec(code_piece[i]) if(re!=null){ target=re[0]; target=/['"]([\s\S]*)['"]/.exec(target)[0] code_piece[i]=code_piece[i].replace(/,end =([\s\S]*)\)$/,")"+";print("+target+");"); } //console.log(code_piece[i]) }else{ var target=""; var re=/\)$/.exec(code_piece[i]) if(re!=null){ target=re[0]; // code_piece[i]=code_piece[i].replace(/\)$/,"+'\\n'"+target) code_piece[i]=code_piece[i].replace(/\)$/,target+";"); } //console.log(code_piece[i]) } } } code_new=code_piece.join("\n"); //console.log(code_new) code=code_new } if(code_pack_list.includes('matplotlib')){ document.getElementById("mat_div").style.height='100%' document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='0%' $("#matplot_img").css('width', this?.layerSize?.content[0] ?? ($('body').width() / 2 + 'px')); $("#matplot_img").css('height',this?.layerSize?.content[1] ?? ($('body').height() - 30 + 'px')); } //如果存在pygame-zero,则进行相关的配置 if(ifpgzrun){ document.getElementById("mat_div").style.height='0%' document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='100%' const Sk = window.Sk; Sk.TurtleGraphics.reset && Sk.TurtleGraphics.reset() Sk.TurtleGraphics.target = "pggame_stage"; Sk.TurtleGraphics.width = (this?.layerSize?.content[0] ?? ($('body').width() / 2) - 30) - 5; Sk.TurtleGraphics.height = (this?.layerSize?.content[1] ?? ($('body').height() - 35)) - 5; const PyGameZero = window.PyGameZero; const stageSize = {width: 0, height: 0}; const stageEl = document.getElementById("pggame_stage"); // 配置容器 stageSize.width = $('#pggame_stage').width() - 50; stageSize.height = $('#pggame_stage').height() - 50; PyGameZero.setContainer(stageEl) PyGameZero.reset(); } //如果存在bg_nonehl,则进行相关的配置 if(ifbg_nonehl){ document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='0%' document.getElementById("blocklySVG").style.height='100%' } if (ifpgzrun || ifbg_nonehl) { const _this = this; this.layerNum = Mixly.LayerExt.open({ title: [Mixly.Msg.Lang['显示'], '30px'], shade: 0, offset: 'rt', area: _this?.layerSize?.layero ?? ['50%', '70%'], max: [$('body').width() + 'px', $('body').height() + 'px'], fixed: false, content: $('#skulpt-img'), resizing: function(size) { _this.layerSize = size; }, end: function() { _this.layerNum = null; pyengine.kill(); } }); } //已经读取了代码:code // Actually run the python code var executionPromise = Sk.misceval.asyncToPromise(function() { return Sk.importMainWithBody("", false, code, true); }); executionPromise.then( function (module) { engine.programStatus['running'] = false; $("#loading").css('display', "none"); // Run the afterSingleExecution one extra time for final state /*Sk.afterSingleExecution(module.$d, -1, 0, ".py"); engine.check(code, execution.trace(), execution.output(), execution.ast, module.$d); engine.executionEnd_(); */ const nowValue = statusBarTerminal.getValue(); if (nowValue.lastIndexOf('\n') !== nowValue.length - 1) { statusBarTerminal.addValue('\n'); } window.userOpEvents && window.userOpEvents.addRecord({ operation: 'run-success' }); window.userEvents && window.userEvents.addRecord({ operation: 'run-success' }); statusBarTerminal.addValue(`==${Mixly.Msg.Lang['shell.finish']}==\n`); }, function(error) { engine.programStatus['running'] = false; $("#loading").css('display', "none"); var original = prettyPrintError(error); //hack for kill program with time limiterror if(original.indexOf("TimeLimitError") !== -1) return; var $errorE = $('').addClass('errorMsg').html(original); // $("#side_code").append($errorE) statusBarTerminal.addValue(original); var matchRet = /on line (\d+)/.exec(original); var lineNumber = -1; if(matchRet !== null && matchRet.length == 2){ lineNumber = parseInt(matchRet[1]) - 1; editor.session.addMarker(new EditorRange(lineNumber, 0, lineNumber, 1), "errorMarker", "fullLine"); editor.hasMarker = true; } engine.executionEnd_(); console.log(error.toString()); const nowValue = statusBarTerminal.getValue(); if (nowValue.lastIndexOf('\n') !== nowValue.length - 1) { statusBarTerminal.addValue('\n'); } statusBarTerminal.addValue(error.toString() + '\n'); window.userOpEvents && window.userOpEvents.addRecord({ operation: 'run-error' }); window.userEvents && window.userEvents.addRecord({ operation: 'run-error' }); statusBarTerminal.addValue(`==${Mixly.Msg.Lang['shell.finish']}==\n`); } ); } //提交代码:先运行代码,返回运行结果,然后提交 PyEngine.prototype.run2 = function(type) { const { mainStatusBarTabs } = Mixly; const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); var xml = Blockly.Xml.workspaceToDom(Mixly.Editor.blockEditor); var data = Blockly.Xml.domToText(xml); if (document.getElementById("boardSelector").value == ""){ var fn = 'filename.xml' }else{ var fn = document.getElementById("boardSelector").value + '.xml' } layer.closeAll('page'); window.actionArrayRecord = []; $("#blocklySVG").empty(); $("#loading").css('display', "inline-block"); /*if(sidecodeDisplay === false){ sidecodeClick(); }*/ // Reset everything this.reset(); //SK设置 this.loadEngine(); // Major Skulpt configurations Sk.configure({ // Function to handle the text outputted by Skulpt //设置文本输出 output: function(lineText){ statusBarTerminal.addValue(lineText); }, // Function to handle loading in new files read: this.readFile.bind(this), inputfun:this.skInput.bind(this), inputfunTakesPrompt: true, execLimit:Number.POSITIVE_INFINITY, fileread:this.fileread.bind(this), filewrite:this.filewrite.bind(this), __future__: Sk.python2,//python3已成默认值,要使用python2需要单独设置 }); 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; }); // Get the code var code = Mixly.MFile.getCode(); //如果存在游戏模块,则只能读取模块代码 if ((code.indexOf("import blocklygame")!=-1)||(code.indexOf("from blocklygame import")!=-1)){ code = Blockly.Python.workspaceToCode(Mixly.Editor.blockEditor) || '';//code code+="\nprint(blocklygame.getFinishState())" } //改为只读区代码区的代码,这样就能够消除高亮执行 if ((code.indexOf("import blocktool")!=-1)||(code.indexOf("import blocklygame")!=-1)||(code.indexOf("from blocklygame import")!=-1)){ //正则匹配替换block id元素 var code_piece=[]; code_piece=code.split("\n"); for(var i=0;i= 0 ){ var target=""; var re=/,?'block_id=[\s\S]*'/.exec(code_piece[i]); if(re!=null){ target=re[0]; code_piece[i]=code_piece[i].replace(target,""); } } //检查是否是高亮辅助块\toll,如果是,则将此行代码移除 if((code_piece[i].indexOf("import blocktool") >= 0) || (code_piece[i].indexOf("blocktool.highlight") >= 0) ){ code_piece[i]="delete"; } //如果使用的是分步调试的blocklygame,要将其换成非分步调试的模块bg_nonehl if((code_piece[i].indexOf("blocklygame") >= 0)){ code_piece[i]=code_piece[i].replace("blocklygame","bg_noanimation"); } } code="" for(var i=0;i= 0){//处理print函数中的end var code_piece=[]; code_piece=code.split("\n"); for(var i=0;i= 0 ){ if(code_piece[i].indexOf(",end") >= 0 ){ var target=""; var re=/,end =([\s\S]*)\)$/.exec(code_piece[i]) if(re!=null){ target=re[0]; target=/['"]([\s\S]*)['"]/.exec(target)[0] code_piece[i]=code_piece[i].replace(/,end =([\s\S]*)\)$/,")"+";print("+target+");"); } //console.log(code_piece[i]) }else{ var target=""; var re=/\)$/.exec(code_piece[i]) if(re!=null){ target=re[0]; // code_piece[i]=code_piece[i].replace(/\)$/,"+'\\n'"+target) code_piece[i]=code_piece[i].replace(/\)$/,target+";print('\\n');"); } //console.log(code_piece[i]) } } } code_new=code_piece.join("\n"); //console.log(code_new) code=code_new } if(code_pack_list.includes('matplotlib')){ document.getElementById("mat_div").style.height='100%' document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='0%' $("#matplot_img").css('width','450px'); $("#matplot_img").css('height','450px'); } //如果存在pygame-zero,则进行相关的配置 if(ifpgzrun){ document.getElementById("mat_div").style.height='0%' document.getElementById("output_img").style.height='0%' document.getElementById("pggame_stage").style.height='100%' const Sk = window.Sk; Sk.TurtleGraphics.reset && Sk.TurtleGraphics.reset() Sk.TurtleGraphics.target = "pggame_stage"; Sk.TurtleGraphics.width = $('#pggame_stage').width() - 50 Sk.TurtleGraphics.height = $('#pggame_stage').height() - 50 const PyGameZero = window.PyGameZero; const stageSize = {width: 0, height: 0}; const stageEl = document.getElementById("pggame_stage"); // 配置容器 stageSize.width = $('#pggame_stage').width() - 50; stageSize.height = $('#pggame_stage').height() - 50; PyGameZero.setContainer(stageEl) PyGameZero.reset(); } console.log(code) //已经读取了代码:code // Actually run the python code var executionPromise = Sk.misceval.asyncToPromise(function() { return Sk.importMainWithBody("", false, code, true); }); executionPromise.then( function (module) { engine.programStatus['running'] = false; $("#loading").css('display', "none"); // Run the afterSingleExecution one extra time for final state /*Sk.afterSingleExecution(module.$d, -1, 0, ".py"); engine.check(code, execution.trace(), execution.output(), execution.ast, module.$d); engine.executionEnd_(); */ const nowValue = statusBarTerminal.getValue(); if (nowValue.lastIndexOf('\n') !== nowValue.length - 1) { statusBarTerminal.addValue('\n'); } statusBarTerminal.addValue('==程序运行完成==\n'); console.log('success'); }, function(error) { engine.programStatus['running'] = false; $("#loading").css('display', "none"); var original = prettyPrintError(error); //hack for kill program with time limiterror if(original.indexOf("TimeLimitError") !== -1) return; if($('.inputMsg').length > 0) $('#side_code').text($('#side_code').text() + $('.inputMsg').val()) var $errorE = $('').addClass('errorMsg').html(original); // $("#side_code").append($errorE) statusBarTerminal.addValue(original); var matchRet = /on line (\d+)/.exec(original); var lineNumber = -1; if(matchRet !== null && matchRet.length == 2){ lineNumber = parseInt(matchRet[1]) - 1; editor.session.addMarker(new EditorRange(lineNumber, 0, lineNumber, 1), "errorMarker", "fullLine"); editor.hasMarker = true; } engine.executionEnd_(); console.log(error.toString()); const nowValue = statusBarTerminal.getValue(); if (nowValue.lastIndexOf('\n') !== nowValue.length - 1) { statusBarTerminal.addValue('\n'); } statusBarTerminal.addValue(error.toString() + '\n==程序运行完成==\n'); } ); } /** * Indents the given string by 4 spaces. This correctly handles multi-line strings. * * @param {String} str - The string to be manipulated. * @returns {String} The string with four spaces added at the start of every new line. */ function indent(str) { return str.replace(/^(?=.)/gm, ' '); } function prettyPrintError (error) { if (typeof error === "string") { return error; } else { // A weird skulpt thing? if (error.tp$str !== undefined) { return error.tp$str().v; } else { return ""+error.name + ": " + error.message; } } } /** * Skulpt Module for holding the Instructor API. * * This module is a little hackish. We need to sit down and reevaluate the best way to * organize it and whether this particular structure is ideal. I suspect it should be * it's own proper JS file. * * @param {String} name - The name of the module (should always be 'instructor') * */ var instructor_module = function(name) { // Main module object that gets returned at the end. var mod = {}; /** * Skulpt Exception that represents a Feedback object, to be rendered to the user * when the feedback system finds a problem. * * @param {Array} args - A list of optional arguments to pass to the Exception. * Usually this will include a message for the user. */ Sk.builtin.Feedback = function (args) { var o; if (!(this instanceof Sk.builtin.Feedback)) { o = Object.create(Sk.builtin.Feedback.prototype); o.constructor.apply(o, arguments); return o; } Sk.builtin.Exception.apply(this, arguments); }; Sk.abstr.setUpInheritance("Feedback", Sk.builtin.Feedback, Sk.builtin.Exception); /** * Skulpt Exception that represents a Success object, to be thrown when the user * completes their program successfully. * ** @param {Array} args - A list of optional arguments to pass to the Exception. * Usually this will be empty. */ Sk.builtin.Success = function (args) { var o; if (!(this instanceof Sk.builtin.Success)) { o = Object.create(Sk.builtin.Success.prototype); o.constructor.apply(o, arguments); return o; } Sk.builtin.Exception.apply(this, arguments); }; Sk.abstr.setUpInheritance("Success", Sk.builtin.Success, Sk.builtin.Exception); /** * Skulpt Exception that represents a Finished object, to be thrown when the user * completes their program successfully, but isn't in a problem with a "solution". * This is useful for open-ended canvases where we still want to capture the students' * code in Canvas. * ** @param {Array} args - A list of optional arguments to pass to the Exception. * Usually this will be empty. */ Sk.builtin.Finished = function (args) { var o; if (!(this instanceof Sk.builtin.Finished)) { o = Object.create(Sk.builtin.Finished.prototype); o.constructor.apply(o, arguments); return o; } Sk.builtin.Exception.apply(this, arguments); }; Sk.abstr.setUpInheritance("Finished", Sk.builtin.Finished, Sk.builtin.Exception); /** * A Skulpt function that throws a Feedback exception, allowing us to give feedback * to the user through the Feedback panel. This function call is done for aesthetic * reasons, so that we are calling a function instead of raising an error. Still, * exceptions allow us to break out of the control flow immediately, like a * return, so they are a good mechanism to use under the hood. * * @param {String} message - The message to display to the user. */ mod.set_feedback = new Sk.builtin.func(function(message) { Sk.builtin.pyCheckArgs("set_feedback", arguments, 1, 1); Sk.builtin.pyCheckType("message", "string", Sk.builtin.checkString(message)); throw new Sk.builtin.Feedback(message.v); }); /** * A Skulpt function that throws a Success exception. This will terminate the * feedback analysis, but reports that the students' code was successful. * This function call is done for aesthetic reasons, so that we are calling a * function instead of raising an error. Still, exceptions allow us to break * out of the control flow immediately, like a return would, so they are a * good mechanism to use under the hood. */ mod.set_success = new Sk.builtin.func(function() { Sk.builtin.pyCheckArgs("set_success", arguments, 0, 0); throw new Sk.builtin.Success(); }); /** * A Skulpt function that throws a Finished exception. This will terminate the * feedback analysis, but reports that the students' code was successful. * This function call is done for aesthetic reasons, so that we are calling a * function instead of raising an error. Still, exceptions allow us to break * out of the control flow immediately, like a return would, so they are a * good mechanism to use under the hood. */ mod.set_finished = new Sk.builtin.func(function() { Sk.builtin.pyCheckArgs("set_finished", arguments, 0, 0); throw new Sk.builtin.Finished(); }); // Memoization of previous parses - some mild redundancy to save time // TODO: There's no evidence this is good, and could be a memory hog on big // programs. Someone should investigate this. The assumption is that multiple // helper functions might be using parses. But shouldn't we trim old parses? // Perhaps a timed cache would work better. var parses = {}; /** * Given source code as a string, return a flat list of all of the AST elements * in the parsed source code. * * TODO: There's redundancy here, since the source code was previously parsed * to run the file and to execute it. We should probably be able to do this just * once and shave off time. * * @param {String} source - Python source code. * @returns {Array.} */ function getParseList(source) { if (!(source in parses)) { var parse = Sk.parse("__main__", source); parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags); } var ast = parses[source]; return (new NodeVisitor()).recursive_walk(ast); } /** * 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.} 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); }); return mod; } PyEngine.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.skip_drawing = true; model.settings.mute_printer(true); } PyEngine.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); } PyEngine.prototype.check = function(student_code, traceTable, output, ast, final_values) { var engine = this; 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("", 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); 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); 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); } }); } } PyEngine.prototype.parseGlobals = function(variables) { var result = Array(); var modules = Array(); for (var property in variables) { var value = variables[property]; if (property !== "__name__" && property !== "__doc__") { property = property.replace('_$rw$', '') .replace('_$rn$', ''); var parsed = this.parseValue(property, value); if (parsed !== null) { result.push(parsed); } else if (value.constructor == Sk.builtin.module) { modules.push(value.$d.__name__.v); } } } return {"properties": result, "modules": modules}; } PyEngine.prototype.parseValue = function(property, value) { if (value == undefined) { return {'name': property, 'type': 'Unknown', "value": 'Undefined' }; } switch (value.constructor) { case Sk.builtin.func: return {'name': property, 'type': "Function", "value": (value.func_code.co_varnames !== undefined ? " Arguments: "+value.func_code.co_varnames.join(", ") : ' No arguments') }; case Sk.builtin.module: return null; case Sk.builtin.str: return {'name': property, 'type': "String", "value": value.$r().v }; case Sk.builtin.none: return {'name': property, 'type': "None", "value": "None" }; case Sk.builtin.bool: return {'name': property, 'type': "Boolean", "value": value.$r().v }; case Sk.builtin.nmber: return {'name': property, 'type': "int" == value.skType ? "Integer": "Float", "value": value.$r().v }; case Sk.builtin.int_: return {'name': property, 'type': "Integer", "value": value.$r().v }; case Sk.builtin.float_: return {'name': property, 'type': "Float", "value": value.$r().v }; case Sk.builtin.tuple: return {'name': property, 'type': "Tuple", "value": value.$r().v }; case Sk.builtin.list: if (value.v.length <= 20) { return {'name': property, 'type': "List", "value": value.$r().v, 'exact_value': value }; } else { return {'name': property, 'type': "List", "value": "[... "+value.v.length+" elements ...]", "exact_value": value }; } case Sk.builtin.dict: return {'name': property, 'type': "Dictionary", "value": value.$r().v }; case Number: return {'name': property, 'type': value % 1 === 0 ? "Integer" : "Float", "value": value }; case String: return {'name': property, 'type': "String", "value": value }; case Boolean: return {'name': property, 'type': "Boolean", "value": (value ? "True": "False") }; default: return {'name': property, 'type': value.tp$name == undefined ? value : value.tp$name, "value": value.$r == undefined ? value : value.$r().v }; } }