/** * An object for converting Python source code to the * Blockly XML representation. * * @constructor * @this {PythonToBlocks} */ function PythonToBlocks() { } function xmlToString(xml) { return new XMLSerializer().serializeToString(xml); } function hasReturn(block){ var q = [block]; while(q.length != 0) { var curr_block = q.shift(); for (var attr in curr_block) { try { var astname = curr_block[attr]._astname; if (astname == "Return") { return true; } else if (astname != null || curr_block[attr] instanceof Array) { q.push(curr_block[attr]); } }catch(e){ } } } return false; } PythonToBlocks.prototype.funcname_to_type = {}; PythonToBlocks.prototype.funcname_to_type_class = {}; PythonToBlocks.prototype.convertSourceToCodeBlock = function(python_source) { var xml = document.createElement("xml"); xml.appendChild(raw_block(python_source)); return xmlToString(xml); } /** * The main function for converting a string representation of Python * code to the Blockly XML representation. * * @param {string} python_source - The string representation of Python * code (e.g., "a = 0"). * @returns {Object} An object which will either have the converted * source code or an error message and the code as a code-block. */ PythonToBlocks.prototype.convertSource = function(python_source) { var xml = document.createElement("xml"); if (python_source.trim() === "") { return {"xml": xmlToString(xml), "error": null}; } else{ python_source=python_source.replace(/f'/g,"'") python_source=python_source.replace(/f"/g,'"') } this.source = python_source.split("\n"); var filename = 'user_code.py'; // Attempt parsing - might fail! var parse, ast, symbol_table, error; try { parse = Sk.parse(filename, python_source); ast = Sk.astFromParse(parse.cst, filename, parse.flags); //symbol_table = Sk.symboltable(ast, filename, python_source, filename, parse.flags); } catch (e) { error = e; xml.appendChild(raw_block(python_source)) return {"xml": xmlToString(xml), "error": error}; } this.comments = {}; for (var commentLocation in parse.comments) { var lineColumn = commentLocation.split(","); var yLocation = parseInt(lineColumn[0], 10); this.comments[yLocation] = parse.comments[commentLocation]; } this.highestLineSeen = 0; this.levelIndex = 0; this.nextExpectedLine = 0; this.measureNode(ast); var converted = this.convert(ast); if (converted !== null) { for (var block = 0; block < converted.length; block+= 1) { xml.appendChild(converted[block]); } } return {"xml": xmlToString(xml), "error": null, "lineMap": this.lineMap, 'comment': this.comments}; } PythonToBlocks.prototype.identifier = function(node) { return Sk.ffi.remapToJs(node); } PythonToBlocks.prototype.recursiveMeasure = function(node, nextBlockLine) { if (node === undefined) { return; } var myNext = nextBlockLine; if ("orelse" in node && node.orelse.length > 0) { if (node.orelse.length == 1 && node.orelse[0]._astname == "If") { myNext = node.orelse[0].lineno-1; } else { myNext = node.orelse[0].lineno-1-1; } } this.heights.push(nextBlockLine); if ("body" in node) { for (var i = 0; i < node.body.length; i++) { var next; if (i+1 == node.body.length) { next = myNext; } else { next = node.body[i+1].lineno-1; } this.recursiveMeasure(node.body[i], next); } } if ("orelse" in node) { for (var i = 0; i < node.orelse.length; i++) { var next; if (i == node.orelse.length) { next = nextBlockLine; } else { next = 1+(node.orelse[i].lineno-1); } this.recursiveMeasure(node.orelse[i], next); } } } PythonToBlocks.prototype.measureNode = function(node) { this.heights = []; this.recursiveMeasure(node, this.source.length-1); this.heights.shift(); } PythonToBlocks.prototype.getSourceCode = function(frm, to) { var lines = this.source.slice(frm-1, to); // Strip out any starting indentation. if (lines.length > 0) { var indentation = lines[0].search(/\S/); for (var i = 0; i < lines.length; i++) { lines[i] = lines[i].substring(indentation); } } return lines.join("\n"); } PythonToBlocks.prototype.convertBody = function(node, is_top_level) { this.levelIndex += 1; // Empty body, return nothing if (node.length == 0) { return null; } // Final result list var children = [], // The complete set of peers root = null, // The top of the current peer current = null, levelIndex = this.levelIndex; // The bottom of the current peer function addPeer(peer) { if (root == null) { children.push(peer); } else { children.push(root); } root = peer; current = peer; } function finalizePeers() { if (root != null) { children.push(root); } } function nestChild(child) { if (root == null) { root = child; current = child; } else if (current == null) { root = current; } else { var nextElement = document.createElement("next"); nextElement.appendChild(child); current.appendChild(nextElement); current = child; } } var lineNumberInBody = 0, lineNumberInProgram, previousLineInProgram=null, distance, skipped_line, commentCount, previousHeight = null, visitedFirstLine = false; // Iterate through each node for (var i = 0; i < node.length; i++) { lineNumberInBody += 1; lineNumberInProgram = node[i].lineno; distance = 0, wasFirstLine = true; if (previousLineInProgram != null) { distance = lineNumberInProgram - previousLineInProgram-1; wasFirstLine = false; } lineNumberInBody += distance; // Handle earlier comments commentCount = 0; for (var commentLineInProgram in this.comments) { if (commentLineInProgram < lineNumberInProgram) { commentChild = this.Comment(this.comments[commentLineInProgram], commentLineInProgram); if (previousLineInProgram == null) { nestChild(commentChild); } else { skipped_previous_line = Math.abs(previousLineInProgram-commentLineInProgram) > 1; if (is_top_level && skipped_previous_line) { addPeer(commentChild); } else { nestChild(commentChild); } } previousLineInProgram = commentLineInProgram; this.highestLineSeen = Math.max(this.highestLineSeen, parseInt(commentLineInProgram, 10)); distance = lineNumberInProgram - previousLineInProgram; delete this.comments[commentLineInProgram]; commentCount += 1; } } distance = lineNumberInProgram - this.highestLineSeen; this.highestLineSeen = Math.max(lineNumberInProgram, this.highestLineSeen); // Now convert the actual node var height = this.heights.shift(); var originalSourceCode = this.getSourceCode(lineNumberInProgram, height); var newChild = this.convertStatement(node[i], originalSourceCode, is_top_level); // deal with '''XXX''' if (originalSourceCode.indexOf("'''") != -1 || originalSourceCode.indexOf('"""') != -1) { quotes = ["'''", '"""']; for (var i = 0; i < quotes.length; i ++) { var quote = quotes[i]; var idxa = originalSourceCode.indexOf(quote); var idxb = originalSourceCode.indexOf(quote, idxa + 3); if (idxb != -1) { var s = originalSourceCode.substring(idxa, idxb + 3); this.highestLineSeen += s.split('\n').length - 1; } } } // Skip null blocks (e.g., imports) if (newChild == null) { continue; } skipped_line = distance > 1; previousLineInProgram = lineNumberInProgram; previousHeight = height; // Handle top-level expression blocks if (is_top_level && newChild.constructor == Array) { addPeer(newChild[0]); // Handle skipped line } else if (is_top_level && skipped_line && visitedFirstLine) { addPeer(newChild); // Otherwise, always embed it in there. } else { nestChild(newChild); } visitedFirstLine = true; } // Handle comments that are on the very last line var lastLineNumber = lineNumberInProgram+1 if (lastLineNumber in this.comments) { commentChild = this.Comment(this.comments[lastLineNumber], lastLineNumber); nestChild(commentChild); delete this.comments[lastLineNumber]; } // Handle any extra comments that stuck around if (is_top_level) { for (var commentLineInProgram in this.comments) { commentChild = this.Comment(this.comments[commentLineInProgram], commentLineInProgram); distance = commentLineInProgram - previousLineInProgram; if (previousLineInProgram == null) { addPeer(commentChild); } else if (distance > 1) { addPeer(commentChild); } else { nestChild(commentChild); } previousLineInProgram = commentLineInProgram; delete this.comments[lastLineNumber]; } } finalizePeers(); this.levelIndex -= 1; return children; } function block(type, lineNumber, fields, values, settings, mutations, statements) { var newBlock = document.createElement("block"); // Settings newBlock.setAttribute("type", type); newBlock.setAttribute("line_number", lineNumber); for (var setting in settings) { var settingValue = settings[setting]; newBlock.setAttribute(setting, settingValue); } // Mutations if (mutations !== undefined && Object.keys(mutations).length > 0) { var newMutation = document.createElement("mutation"); for (var mutation in mutations) { var mutationValue = mutations[mutation]; if (mutation.charAt(0) == '@') { newMutation.setAttribute(mutation.substr(1), mutationValue); } else if (mutationValue.constructor === Array) { for (var i = 0; i < mutationValue.length; i++) { var mutationNode = document.createElement(mutation); mutationNode.setAttribute("name", mutationValue[i]); newMutation.appendChild(mutationNode); } } else { var mutationNode = document.createElement("arg"); mutationNode.setAttribute("name", mutation); mutationNode.appendChild(mutationValue); newMutation.appendChild(mutationNode); } } newBlock.appendChild(newMutation); } // Fields for (var field in fields) { var fieldValue = fields[field]; var newField = document.createElement("field"); newField.setAttribute("name", field); newField.appendChild(document.createTextNode(fieldValue)); newBlock.appendChild(newField); } // Values for (var value in values) { var valueValue = values[value]; var newValue = document.createElement("value"); if (valueValue !== null) { newValue.setAttribute("name", value); newValue.appendChild(valueValue); newBlock.appendChild(newValue); } } // Statements if (statements !== undefined && Object.keys(statements).length > 0) { for (var statement in statements) { var statementValue = statements[statement]; if (statementValue == null) { continue; } else { for (var i = 0; i < statementValue.length; i += 1) { // In most cases, you really shouldn't ever have more than // one statement in this list. I'm not sure Blockly likes // that. var newStatement = document.createElement("statement"); newStatement.setAttribute("name", statement); newStatement.appendChild(statementValue[i]); newBlock.appendChild(newStatement); } } } } return newBlock; } raw_block = function(txt) { return block("raw_block", 0, { "TEXT": txt }); } raw_expression = function(txt, lineno) { return block("raw_expression", lineno, {"TEXT": txt}); } PythonToBlocks.prototype.convert = function(node, is_top_level) { return this[node._astname](node, is_top_level); } function arrayMax(array) { return array.reduce(function(a, b) { return Math.max(a, b); }); } function arrayMin(array) { return array.reduce(function(a, b) { return Math.min(a, b); }); } PythonToBlocks.prototype.convertStatement = function(node, full_source, is_top_level) { try { return this.convert(node, is_top_level); } catch (e) { heights = this.getChunkHeights(node); extractedSource = this.getSourceCode(arrayMin(heights), arrayMax(heights)); console.error(e); return raw_block(extractedSource); } } PythonToBlocks.prototype.getChunkHeights = function(node) { var lineNumbers = []; if (node.hasOwnProperty("lineno")) { lineNumbers.push(node.lineno); } if (node.hasOwnProperty("body")) { for (var i = 0; i < node.body.length; i += 1) { var subnode = node.body[i]; lineNumbers = lineNumbers.concat(this.getChunkHeights(subnode)); } } if (node.hasOwnProperty("orelse")) { for (var i = 0; i < node.orelse.length; i += 1) { var subnode = node.orelse[i]; lineNumbers = lineNumbers.concat(this.getChunkHeights(subnode)); } } return lineNumbers; } /* ----- Nodes ---- */ /* * NO LINE OR COLUMN NUMBERS * Module * body: asdl_seq */ PythonToBlocks.prototype.Module = function(node) { return this.convertBody(node.body, true); } PythonToBlocks.prototype.Comment = function(txt, lineno) { return block("comment_single", lineno, { "BODY": txt.slice(1) }, {}, {}, {}, {}) } /* * NO LINE OR COLUMN NUMBERS * Interactive * body: asdl_seq */ PythonToBlocks.prototype.Interactive = function(body) { return this.convertBody(node.body); } /* * NO LINE OR COLUMN NUMBERS * TODO * body: expr_ty */ PythonToBlocks.prototype.Expression = function(body) { this.body = body; } /* * NO LINE OR COLUMN NUMBERS * * body: asdl_seq */ PythonToBlocks.prototype.Suite = function(body) { this.asdl_seq(node.body); } /* * * name: identifier * args: arguments__ty * body: asdl_seq * decorator_list: asdl_seq */ PythonToBlocks.prototype.FunctionDef = function(node) { var name = node.name; var args = node.args; var body = node.body; var decorator_list = node.decorator_list; if (decorator_list.length > 0) { throw new Error("Decorators are not implemented."); } if(py2block_config.ignoreS.has(name.v)) { return null; } var body_length = body.length; var return_block = {}; if (node.col_offset == 0) { var blockid = hasReturn(body) ? "procedures_defreturn" : "procedures_defnoreturn"; if(body_length > 0){ var last = body_length - 1; if(body[last]._astname == "Return") { return_block = {"RETURN": this.convert(body[last].value)}; body = body.slice(0, last); } } this.funcname_to_type[this.identifier(name)] = blockid; return block(blockid, node.lineno, { "NAME": this.identifier(name) }, return_block, { "inline": "false" }, { "arg": this.arguments_(args) }, { "STACK": this.convertBody(body) }); } else { var blockid = hasReturn(body) ? "method_procedures_defreturn" : "method_procedures_defnoreturn"; if(body_length > 0){ var last = body_length - 1; if(body[last]._astname == "Return") { return_block = {"RETURN": this.convert(body[last].value)}; body = body.slice(0, last); } } this.funcname_to_type_class[this.identifier(name)] = blockid; return block(blockid, node.lineno, { "NAME": this.identifier(name) }, return_block, { "inline": "false" }, { "arg": this.arguments_(args) }, { "STACK": this.convertBody(body) }); } } /* * name: identifier * args: arguments__ty * bases: asdl_seq * body: asdl_seq * decorator_list: asdl_seq */ PythonToBlocks.prototype.ClassDef = function(node) { var name = node.name; var bases = node.bases; var body = node.body; var decorator_list = node.decorator_list; if (decorator_list.length > 0) { throw new Error("Decorators are not implemented."); } if(py2block_config.ignoreS.has(name.v)) return null; if(bases.length) { if(bases.length == 1) { py2block_config.pinType = "class_get"; var mode = this.convert(node.bases["0"]); py2block_config.pinType=null; return block("class_make_with_base", node.lineno, { "VAR": this.identifier(name) }, { "NAME": mode }, { "inline": "false" }, {}, { "data": this.convertBody(body) }); } } else { return block("class_make", node.lineno, { "VAR": this.identifier(name) }, { }, { "inline": "false" }, {}, { "data": this.convertBody(body) }); } } /* * value: expr_ty * */ PythonToBlocks.prototype.Return = function(node) { var value = node.value; // No field, one title, one setting return block("procedures_return", node.lineno, {}, { "VALUE": this.convert(value) }, { "inline": "false" }); } /* * targets: asdl_seq * */ PythonToBlocks.prototype.Delete = function(/* {asdl_seq *} */ targets) { this.targets = targets; if(targets.targets.length != 1){ throw new Error("not implement del"); } if(targets.targets[0]._astname == "Subscript"){ //del mydict['key'] var valueAstname = targets.targets[0].slice.value._astname; if(valueAstname == "Str" || valueAstname == "Name") { return block("dicts_delete", targets.lineno, {}, { "KEY":this.convert(targets.targets[0].slice.value), "DICT": this.convert(targets.targets[0].value) }, { "inline": "true" }); }else{ return block("lists_remove_at2", targets.lineno, { "OP":'del' }, { "LIST": this.convert(targets.targets[0].value), "DATA": this.convert(targets.targets[0].slice.value), }, { "inline": "true" }); } }else { return block("lists_del_general", targets.lineno, {}, { "TUP": this.convert(targets.targets[0]) }, { "inline": "true" }); } } /* * targets: asdl_seq * value: expr_ty */ PythonToBlocks.prototype.Assign = function(node) { var targets = node.targets; var value = node.value; if (targets.length == 0) { throw new Error("Nothing to assign to!"); } else if (targets.length == 1) { if(targets[0]._astname == "Name") { if (py2block_config.ignoreS.has(this.Name_str(targets[0]))) { return null; } try{ if (value._astname === "Call") { if (value.func._astname == "Name") { py2block_config.objectTypeD[this.Name_str(targets[0])] = value.func.id.v; } else { var line = this.getSourceCode().split('\n')[value.func.lineno - 1]; py2block_config.objectTypeD[this.Name_str(targets[0])] = line.substring(value.func.col_offset).split('(')[0].trim(); } } else { var astname = value._astname; if (value._astname === 'Name' && this.Name_str(value) in py2block_config.objectTypeD) { astname = py2block_config.objectTypeD[this.Name_str(value)]; } py2block_config.objectTypeD[this.Name_str(targets[0])] = astname; } }catch(e){ } for (var key in py2block_config.assignD['dict']) { try { var checkfunc = py2block_config.assignD['dict'][key]['check_assign']; var blockfunc = py2block_config.assignD['dict'][key]['create_block']; if (checkfunc(this, node, targets, value)) return blockfunc(this, node, targets, value); } catch (e) { } } }else if(targets[0]._astname == "Subscript"){ var valueAstname = targets[0].slice.value._astname; if(valueAstname == "Str") { return block("dicts_add_or_change", targets.lineno, {}, { "KEY": this.convert(targets[0].slice.value), "DICT": this.convert(targets[0].value), "VAR": this.convert(value) }, { "inline": "true" }); }else{ return block("lists_set_index", targets.lineno, {}, { "LIST": this.convert(targets[0].value), "AT": this.convert(targets[0].slice.value), "TO": this.convert(value) }, { "inline": "true" }); } }else if(targets[0]._astname == "Tuple"){ var varNameArr = []; for(var i = 0 ; i < targets[0].elts.length; i ++){ varNameArr.push(this.Name_str(targets[0].elts[i])); } var varNameStr = varNameArr.join(','); return block("variables_set", targets.lineno, { "VAR":varNameStr },{ "VALUE":this.convert(value) }); } else if(targets[0]._astname == "Attribute"){ if(py2block_config.board == py2block_config.ESP32){ if(targets[0].value.id.v.indexOf("pin") != -1 && targets[0].attr.v == 'value' && Blockly.Blocks['inout_digital_write']){ py2block_config.pinType = "pins_digital"; var mode = this.convert(node.targets["0"].value); var data = this.convert(node.value); py2block_config.pinType=null; return block("inout_digital_write", targets.lineno, {},{ "PIN": mode, "STAT": data }); } else if(targets[0].value.id.v.indexOf("dac") != -1 && targets[0].attr.v == 'value' && Blockly.Blocks['inout_analog_write']){ py2block_config.pinType = "pins_dac"; var mode = this.convert(node.targets["0"].value); py2block_config.pinType=null; var data = this.convert(node.value); return block("inout_analog_write", targets.lineno, {},{ "PIN": mode, "NUM": data }); } else if(targets[0].attr.v == 'duty_cycle' && Blockly.Blocks['inout_pwm_analog_write']){ py2block_config.pinType = "pins_pwm"; var mode = this.convert(node.targets["0"].value); py2block_config.pinType=null; var data = this.convert(node.value); return block("inout_pwm_analog_write", targets.lineno, {},{ "PIN": mode, "NUM": data }); } else if(targets[0].attr.v == 'frequency' && Blockly.Blocks['inout_pwm_analog_write_set_freq']){ py2block_config.pinType = "pins_pwm"; var mode = this.convert(node.targets["0"].value); py2block_config.pinType=null; var data = this.convert(node.value); return block("inout_pwm_analog_write_set_freq", targets.lineno, {},{ "PIN": mode, "NUM": data }); } } else if(Blockly.Blocks['property_set']){ py2block_config.pinType = "object_get"; var mode = this.convert(node.targets["0"].value); py2block_config.pinType=null; return block("property_set", targets.lineno, { "VAR": node.targets["0"].attr.v },{ "VALUE": mode, "DATA": this.convert(node.value), }); } } return block("variables_set", node.lineno, { "VAR": this.Name_str(targets[0]) //targets }, { "VALUE": this.convert(value) }); } else { //TODO throw new Error("Multiple Assigment Targets Not implemented"); } } /* * target: expr_ty * op: operator_ty * value: expr_ty */ PythonToBlocks.prototype.AugAssign = function(node) { var target = node.target; var op = node.op; var value = node.value; return block("math_selfcalcu", node.lineno, { "OP": this.binaryOperator(op) }, { "A": this.convert(target), "B": this.convert(value) }); } /* * dest: expr_ty * values: asdl_seq * nl: bool * */ PythonToBlocks.prototype.Print = function(node) { var dest = node.dest; var values = node.values; var nl = node.nl; if (values.length == 1) { return block("text_print", node.lineno, {}, { "TEXT": this.convert(values[0]) }); } else { return block("text_print_multiple", node.lineno, {}, this.convertElements("PRINT", values), { "inline": "true" }, { "@items": values.length }); } } /* * target: expr_ty * iter: expr_ty * body: asdl_seq * orelse: asdl_seq * */ PythonToBlocks.prototype.For = function(node) { var target = node.target; var iter = node.iter; var body = node.body; var orelse = node.orelse; for(var key in py2block_config.forStatementD['dict']){ try { var checkfunc = py2block_config.forStatementD['dict'][key]['check_condition']; var blockfunc = py2block_config.forStatementD['dict'][key]['create_block']; if (checkfunc(this, node, target, iter, body, orelse)) { return blockfunc(this, node, target, iter, body, orelse); } }catch (e){ } } if (orelse.length > 0) { // TODO throw new Error("Or-else block of For is not implemented."); } return block("controls_forEach", node.lineno, { }, { "LIST": this.convert(iter), 'VAR':this.convert(target) }, { "inline": "true" }, {}, { "DO": this.convertBody(body) }); } /* * test: expr_ty * body: asdl_seq * orelse: asdl_seq */ PythonToBlocks.prototype.While = function(node, is_top_level) { var test = node.test; var body = node.body; var orelse = node.orelse; for(var key in py2block_config.whileStatementD['dict']){ try { var checkfunc = py2block_config.whileStatementD['dict'][key]['check_condition']; var blockfunc = py2block_config.whileStatementD['dict'][key]['create_block']; if (checkfunc(this, node, test, body, orelse)) return blockfunc(this, node, test, body, orelse); }catch (e){ } } if (orelse.length > 0) { // TODO throw new Error("Or-else block of While is not implemented."); } return block("controls_whileUntil", node.lineno, {}, { "BOOL": this.convert(test) }, {}, {}, { "DO": this.convertBody(body) }); } /* * test: expr_ty * body: asdl_seq * orelse: asdl_seq * */ PythonToBlocks.prototype.If = function(node) { var test = node.test; var body = node.body; var orelse = node.orelse; for(var key in py2block_config.ifStatementD['dict']){ try { var checkfunc = py2block_config.ifStatementD['dict'][key]['check_condition']; var blockfunc = py2block_config.ifStatementD['dict'][key]['create_block']; if (checkfunc(this, node, test, body, orelse)) return blockfunc(this, node, test, body, orelse); }catch (e){ } } var IF_values = {"IF0": this.convert(test)}; var DO_values = {"DO0": this.convertBody(body)}; var elseifCount = 0; var elseCount = 0; var potentialElseBody = null; // Handle weird orelse stuff if (orelse !== undefined) { if (orelse.length == 1 && orelse[0]._astname == "If") { // This is an 'ELIF' while (orelse.length == 1 && orelse[0]._astname == "If") { this.heights.shift(); elseifCount += 1; body = orelse[0].body; test = orelse[0].test; orelse = orelse[0].orelse; DO_values["DO"+elseifCount] = this.convertBody(body, false); if (test !== undefined) { IF_values["IF"+elseifCount] = this.convert(test); } } } if (orelse !== undefined && orelse.length > 0) { // Or just the body of an Else statement elseCount += 1; DO_values["ELSE"] = this.convertBody(orelse); } } return block("controls_if", node.lineno, { }, IF_values, { "inline": "false" }, { "@elseif": elseifCount, "@else": elseCount }, DO_values); } /* * context_expr: expr_ty * optional_vars: expr_ty * body: asdl_seq */ PythonToBlocks.prototype.With = function(node) { var context_expr = node.context_expr; var optional_vars = node.optional_vars; var body = node.body; throw new Error("With_ not implemented"); } /* * type: expr_ty * inst: expr_ty * tback: expr_ty */ PythonToBlocks.prototype.Raise = function(node) { var type = node.type; var inst = node.inst; var tback = node.tback; throw new Error("Raise not implemented"); } /* * body: asdl_seq * handlers: asdl_seq * orelse: asdl_seq * */ PythonToBlocks.prototype.TryExcept = function(node) { var body = node.body; var handlers = node.handlers; var orelse = node.orelse; var IF_values = {}; var DO_values = {"try": this.convertBody(body)}; var elseifCount = 0; var elseCount = 0; var potentialElseBody = null; // Handle weird orelse stuff for(var i = 0 ; i < handlers.length ; i ++){ var h = handlers[i]; var ifkey = "IF" + (i + 1); var dokey = "DO" + (i + 1); if(h.type != null) { if(h.name == null){ IF_values[ifkey] = this.convert(h.type); }else{ var heights = this.getChunkHeights(h.type); var extractedSource = this.getSourceCode(arrayMin(heights), arrayMax(heights)); var text = extractedSource.substring(h.type.col_offset).replace(":", ""); IF_values[ifkey] = block("factory_block_return", node.lineno, { 'VALUE':text }, {}); } }else{ IF_values[ifkey] = null; } DO_values[dokey] = this.convertBody(h.body); elseifCount ++; } return block("controls_try_finally", node.lineno, { }, IF_values, { "inline": "false" }, { "@elseif": elseifCount, "@else": elseCount }, DO_values); } /* * body: asdl_seq * finalbody: asdl_seq * */ PythonToBlocks.prototype.TryFinally = function(node) { var body = node.body; var finalbody = node.finalbody; if(body.length != 1){ throw new Error("TryExcept not implemented"); } var handlers = body[0].handlers; var orelse = body[0].orelse; body = body[0].body; var IF_values = {}; var DO_values = { "try": this.convertBody(body), "ELSE": this.convertBody(finalbody) }; var elseifCount = 0; var elseCount = 1; var potentialElseBody = null; // Handle weird orelse stuff for(var i = 0 ; i < handlers.length ; i ++){ var h = handlers[i]; var ifkey = "IF" + (i + 1); var dokey = "DO" + (i + 1); if(h.type != null) { if(h.name == null){ IF_values[ifkey] = this.convert(h.type); }else{ var heights = this.getChunkHeights(h.type); var extractedSource = this.getSourceCode(arrayMin(heights), arrayMax(heights)); var text = extractedSource.substring(h.type.col_offset).replace(":", ""); IF_values[ifkey] = block("factory_block_return", node.lineno, { 'VALUE':text }, {}); } }else{ IF_values[ifkey] = null; } DO_values[dokey] = this.convertBody(h.body); elseifCount ++; } return block("controls_try_finally", node.lineno, { }, IF_values, { "inline": "false" }, { "@elseif": elseifCount, "@else": elseCount }, DO_values); } /* * test: expr_ty * msg: expr_ty */ PythonToBlocks.prototype.Assert = function(node) { var test = node.test; var msg = node.msg; throw new Error("Assert not implemented"); } /* * names: asdl_seq * */ PythonToBlocks.prototype.Import = function(node) { var names = node.names; // The import statement isn't used in blockly because it happens implicitly return null; } /* * module: identifier * names: asdl_seq * level: int * */ PythonToBlocks.prototype.ImportFrom = function(node) { var module = node.module; var names = node.names; var level = node.level; // The import statement isn't used in blockly because it happens implicitly return null; } /* * body: expr_ty * globals: expr_ty * locals: expr_ty * */ PythonToBlocks.prototype.Exec = function(node) { var body = node.body; var globals = node.globals; var locals = node.locals; throw new Error("Exec not implemented"); } /* * names: asdl_seq * */ PythonToBlocks.prototype.Global = function(node) { var names = node.names; var param = { "id":names[0], "_astname":"Name" }; return block("variables_global", node.lineno, { }, { 'VAR': this.convert(param) }, { "inline": "false" }); } /* * value: expr_ty * */ PythonToBlocks.prototype.Expr = function(node, is_top_level) { var value = node.value; var converted = this.convert(value); if (converted.constructor == Array) { return converted[0]; }else { return block("raw_empty", node.lineno, {}, { "VALUE": converted }); } } /* * * */ PythonToBlocks.prototype.Pass = function(node) { return block("controls_pass", node.lineno, {}); } /* * * */ PythonToBlocks.prototype.Break = function(node) { return block("controls_flow_statements", node.lineno, { "FLOW": "BREAK" }); } /* * * */ PythonToBlocks.prototype.Continue = function(node) { return block("controls_flow_statements", node.lineno, { "FLOW": "CONTINUE" }); } /* * TODO: what does this do? * */ PythonToBlocks.prototype.Debugger = function() { return null; } PythonToBlocks.prototype.booleanOperator = function(op) { switch (op.prototype._astname) { case "And": return "AND"; case "Or": return "OR"; default: throw new Error("Operator not supported:"+op.prototype._astname); } } /* * op: boolop_ty * values: asdl_seq */ PythonToBlocks.prototype.BoolOp = function(node) { var op = node.op; var values = node.values; // TODO: is there ever a case where it's < 1 values? var result_block = this.convert(values[0]); for (var i = 1; i < values.length; i+= 1) { result_block = block("logic_operation", node.lineno, { "OP": this.booleanOperator(op) }, { "A": result_block, "B": this.convert(values[i]) }, { "inline": "true" }); } return result_block; } PythonToBlocks.prototype.binaryOperator = function(op) { switch (op.prototype._astname) { case "Add": return "ADD"; case "Sub": return "MINUS"; case "Div": return "DIVIDE"; case "FloorDiv": return "ZHENGCHU"; case "Mult": return "MULTIPLY"; case "Pow": return "POWER"; case "Mod": return "QUYU"; case "BitAnd": return "&"; case "BitOr": return "|"; case "RShift": return ">>"; case "LShift": return "<<"; default: throw new Error("Operator not supported:"+op.prototype._astname); } } /* * left: expr_ty * op: operator_ty * right: expr_ty */ PythonToBlocks.prototype.BinOp = function(node) { var left = node.left; var op = node.op; var right = node.right; var opName = this.binaryOperator(op); var blockName = "math_arithmetic"; if(opName == "&" || opName == "|" || opName == ">>" || opName == "<<"){ blockName = "math_bit"; } if(opName == "ADD"){ //形如str('XX') + str('XX') 或str('XX') + 'XX' 或'XX' + str('XX') 或'XX' + 'XX' if(((left._astname == "Call" && left.func._astname == "Name" && this.Name_str(left.func) == "str") ||(left._astname == "Str")) && ((right._astname == "Call" && right.func._astname == "Name" && this.Name_str(right.func) == "str") ||(right._astname == "Str"))){ if(left._astname == "Call"){ left = left.args[0]; } if(right._astname == "Call"){ right = right.args[0]; } return block("text_join", node.lineno, { }, { "A": this.convert(left), "B": this.convert(right) }, { "inline": true }); } } return block(blockName, node.lineno, { "OP": this.binaryOperator(op) // TODO }, { "A": this.convert(left), "B": this.convert(right) }, { "inline": true }); } /* * op: unaryop_ty * operand: expr_ty */ PythonToBlocks.prototype.UnaryOp = function(node) { var op = node.op; var operand = node.operand; if (op.prototype._astname == "Not") { return block("logic_negate", node.lineno, {}, { "BOOL": this.convert(operand) }, { "inline": "false" }); } else if(op.prototype._astname == "USub"){ return block("math_trig", node.lineno, { 'OP': '-' }, { 'NUM': this.convert(operand) }, { "inline": "true" }); }else { throw new Error("Other unary operators are not implemented yet."); } } /* * args: arguments__ty * body: expr_ty */ PythonToBlocks.prototype.Lambda = function(node) { var args = node.args; var body = node.body; throw new Error("Lambda functions are not implemented yet."); } /* * test: expr_ty * body: expr_ty * orelse: expr_ty */ PythonToBlocks.prototype.IfExp = function(node) { var test = node.test; var body = node.body; var orelse = node.orelse; return block("logic_true_or_false", node.lineno, { }, { 'B': this.convert(body), 'A': this.convert(test), 'C': this.convert(orelse) }, { "inline": "true" }); } /* * keys: asdl_seq * values: asdl_seq */ PythonToBlocks.prototype.Dict = function(node) { var keys = node.keys; var values = node.values; var keyList = []; var valueList = []; for (var i = 0; i < keys.length; i+= 1) { if (keys[i]._astname != "Str") { throw new Error("Dictionary Keys should be Strings."); } keyList["KEY"+i] = this.Str_value(keys[i]); valueList["VALUE"+i] = this.convert(values[i]); } return block("dicts_create_with", node.lineno, keyList, valueList, { "inline": "false" }, { "@items": keys.length }); } /* * elts: asdl_seq * */ PythonToBlocks.prototype.Set = function(node) { var elts = node.elts; throw new Error("Sets are not implemented"); } /* * elt: expr_ty * generators: asdl_seq */ PythonToBlocks.prototype.ListComp = function(node) { var elt = node.elt; var generators = node.generators; // TODO } /* * elt: expr_ty * generators: asdl_seq */ PythonToBlocks.prototype.SetComp = function(node) { var elt = node.elt; var generators = node.generators; throw new Error("Set Comprehensions are not implemented"); } /* * key: expr_ty * value: expr_ty * generators: asdl_seq */ PythonToBlocks.prototype.DictComp = function(node) { var key = node.key; var value = node.value; var generators = node.generators; throw new Error("Dictionary Comprehensions are not implemented"); } /* * elt: expr_ty * generators: asdl_seq */ PythonToBlocks.prototype.GeneratorExp = function(node) { var elt = node.elt; var generators = node.generators; throw new Error("Generator Expresions are not implemented"); } /* * value: expr_ty * */ PythonToBlocks.prototype.Yield = function(node) { var value = value; throw new Error("Yield expression is not implemented"); } PythonToBlocks.prototype.compareOperator = function(op) { switch (op.prototype._astname) { case "Eq": return "EQ"; case "NotEq": return "NEQ"; case "Lt": return "LT"; case "Gt": return "GT"; case "LtE": return "LTE"; case "GtE": return "GTE"; case "In": return "in"; case "NotIn": return "not in"; case "Is": return "is"; case "IsNot": return "is not"; // Is, IsNot, In, NotIn default: throw new Error("Operator not supported:"+op.prototype._astname); } } /* * left: expr_ty * ops: asdl_int_seq * asdl_seq: comparators */ PythonToBlocks.prototype.Compare = function(node) { var left = node.left; var ops = node.ops; var comparators = node.comparators; if (ops.length != 1) { if(ops.length == 2 && comparators.length == 2){ return block("logic_compare_continous", node.lineno, { "OP1": this.compareOperator(ops[0]), "OP2": this.compareOperator(ops[1]) }, { "A": this.convert(left), "B": this.convert(comparators[0]), "C": this.convert(comparators[1]) }, { "inline": "true" }); } else{ throw new Error("Only one comparison operator is supported"); } } else if (ops[0].name == "In_" || ops[0].name == "NotIn") { if (ops[0].name == "In_"){ var mode="in" } else if (ops[0].name == "NotIn"){ var mode="not in" } return block("logic_is_in", node.lineno, { "BOOL":mode }, { "A": this.convert(left), "B": this.convert(comparators[0]) }, { "inline": "true" }); } else if (ops[0].name == "Is" || ops[0].name == "IsNot") { if (ops[0].name == "Is"){ var mode="is" } else if (ops[0].name == "IsNot"){ var mode="is not" } return block("logic_is", node.lineno, { "BOOL":mode }, { "A": this.convert(left), "B": this.convert(comparators[0]) }, { "inline": "true" }); } else { //处理文本模块的等于 //形如str('XX') == str('XX') 或str('XX') == 'XX' 或'XX' == str('XX') 或'XX' == 'XX' var right = comparators[0]; if(((left._astname == "Call" && left.func._astname == "Name" && this.Name_str(left.func) == "str") ||(left._astname == "Str")) && ((right._astname == "Call" && right.func._astname == "Name" && this.Name_str(right.func) == "str") ||(right._astname == "Str"))){ if(left._astname == "Call"){ left = left.args[0]; } if(right._astname == "Call"){ right = right.args[0]; } return block("text_equals_starts_ends", node.lineno, { "DOWHAT":"===" }, { "STR1": this.convert(left), "STR2": this.convert(right) }, { "inline": true }); } return block("logic_compare", node.lineno, { "OP": this.compareOperator(ops[0]) }, { "A": this.convert(left), "B": this.convert(comparators[0]) }, { "inline": "true" }); } } convertStockSymbols = function(symbol) { switch (symbol) { case 'FB': case "Facebook": return "Facebook"; case "AAPL": case "Apple": return "Apple"; case "MSFT": case "Microsoft": return "Microsoft"; case "GOOG": case "Google": return "Google"; default: throw new Error("Unknown Stock Symbol."); } } toTitleCase = function(str) { return str.replace(/\w\S*/g, function(txt){ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); } PythonToBlocks.KNOWN_MODULES = { "weather": { "get_temperature": ["weather_temperature", "CITY"], "get_report": ["weather_report", "CITY"], "get_forecasts": ["weather_forecasts", "CITY"], "get_highs_lows": ["weather_highs_lows", "CITY"], "get_all_forecasted_temperatures": ["weather_all_forecasts"], "get_forecasted_reports": ["weather_report_forecasts", "CITY"] }, "earthquakes": { "get": ["earthquake_get", "PROPERTY"], "get_both": ["earthquake_both"], "get_all": ["earthquake_all"] }, "stocks": { "get_current": ["stocks_current", ["TICKER", convertStockSymbols]], "get_past": ["stocks_past", ["TICKER", convertStockSymbols]] }, "crime": { // STATE = toTitleCase "get_property_crimes": ["crime_state", ["STATE", toTitleCase], ["TYPE", "property"]], "get_violent_crimes": ["crime_state", ["STATE", toTitleCase], ["TYPE", "violent"]], "get_by_year": ["crime_year", "YEAR"], "get_all": ["crime_all"] }, "books": { "get_all": ["books_get"] }, "plt": { "title": ["*plot_title", "TEXT"], "xlabel": ["*plot_xlabel", "TEXT"], "ylabel": ["*plot_ylabel", "TEXT"], "hist": ["*plot_hist", {"type": "variable", "mode": "value", "name": "values"}], "scatter": ["*plot_scatter", {"type": "variable", "mode": "value", "name": "x_values"}, {"type": "variable", "mode": "value", "name": "y_values"}], "show": ["*plot_show"] } }; PythonToBlocks.prototype.KNOWN_FUNCTIONS = ["append", "strip", "rstrip", "lstrip"]; PythonToBlocks.KNOWN_ATTR_FUNCTIONS = {}; PythonToBlocks.prototype.CallAttribute = function(func, args, keywords, starargs, kwargs, node) { args = args ?? []; var name = this.identifier(func.attr); if (func.value._astname == "Name" || func.value._astname == "Attribute") { var module = null; if (func.value._astname == "Name") { module = this.identifier(func.value.id); } else { try{ module = this.Name_str(func.value.value) + '.' + this.identifier(func.value.attr); }catch(e) { } } if (module == "plt" && name == "plot") { if (args.length == 1) { return [block("plot_line", func.lineno, {}, { "y_values": this.convert(args[0]) }, {"inline": "false"})]; } else if (args.length == 2) { return [block("plot_lineXY", func.lineno, {}, { "x_values": this.convert(args[0]), "y_values": this.convert(args[1]) }, {"inline": "false"})]; } else { throw new Error("Incorrect number of arguments to plt.plot"); } } else if(Object.keys(py2block_config.moduleFunctionD.get(module)).length != 0){ if(name in py2block_config.moduleFunctionD.get(module)) { try { return py2block_config.moduleFunctionD.get(module)[name](this, func, args, keywords, starargs, kwargs, node); } catch (e) { throw new Error("not implement for this module's function"); } }else{ throw new Error("not implement for this module's function"); } } else if (py2block_config.knownModuleS.has(module)){ throw new Error("not implement for this module's function"); } else if (module in PythonToBlocks.KNOWN_MODULES && name in PythonToBlocks.KNOWN_MODULES[module]) { var definition = null; definition = PythonToBlocks.KNOWN_MODULES[module][name]; var blockName = definition[0]; var isExpression = true; if (blockName.charAt(0) == "*") { blockName = blockName.slice(1); isExpression = false; } var fields = {}; var mutations = {}; var values = {}; for (var i = 0; i < args.length; i++) { var argument = definition[1+i]; var destination = fields; if (typeof argument == "string") { fields[argument] = this.Str_value(args[i]); } else if (typeof argument == "object") { if (argument.mode == "value") { destination = values; } if (argument.add_mutation !== undefined) { mutations[argument.add_mutation.name] = argument.add_mutation.value; } if (argument.type == 'mutation') { if (argument.index == undefined) { mutations[argument.name] = this.Str_value(args[i]); } else { mutations[argument.name] = this.Str_value(args[argument.index+1]); } } else if (argument.type == "integer") { destination[argument.name] = this.Num_value(args[i]); } else if (argument.type == 'variable') { if(argument.is_math_angle === true){ destination[argument.name] = this.convert(args[i].left.left); }else{ destination[argument.name] = this.convert(args[i]); } } else if (argument.type == "integer_mapper") { // Okay we jumped the shark here var argumentName = argument.name; var argumentMapper = argument.method; destination[argumentName] = argumentMapper(this.Num_value(args[i])); } else if (argument.type == 'mapper') { var argumentName = argument.name; var argumentMapper = argument.method; destination[argumentName] = argumentMapper(this.Str_value(args[i])); } } else { var argumentName = argument[0]; var argumentMapper = argument[1]; fields[argumentName] = argumentMapper(this.Str_value(args[i])); } } for (var i = 1+args.length; i < definition.length; i++) { var first = definition[i][0]; var second = definition[i][1]; fields[first] = second; } if (isExpression) { var k = block(blockName, func.lineno, fields, values, [], mutations); return k; } else { return [block(blockName, func.lineno, fields, values, [], mutations)]; } } } var keys = Object.keys(py2block_config.objectFunctionD.get(name)); if (this.KNOWN_FUNCTIONS.indexOf(name) > -1 || keys.length != 0) { if(keys.length != 0){ try { var objname = this.identifier(func.value.id); if (objname in py2block_config.objectTypeD) { var objtype = py2block_config.objectTypeD[objname]; if (objtype in py2block_config.objectFunctionD.get(name)) return py2block_config.objectFunctionD.get(name)[objtype](this, func, args, keywords, starargs, kwargs, node); } }catch (e){ } try { if (!py2block_config.objectFunctionD.get(name)['Default']) { var firstKey = keys[0]; py2block_config.objectFunctionD.get(name)['Default'] = py2block_config.objectFunctionD.get(name)[firstKey]; } return py2block_config.objectFunctionD.get(name)['Default'](this, func, args, keywords, starargs, kwargs, node); }catch(e){ } } /*switch (name) { case "append": if (args.length !== 1) { throw new Error("Incorrect number of arguments to .append"); } // Return as statement return [block("lists_append", func.lineno, {}, { "ITEM": this.convert(args[0]), "LIST": this.convert(func.value) }, { "inline": "true" })]; case "strip": return block("text_trim", func.lineno, { "MODE": "BOTH" }, { "TEXT": this.convert(func.value) }); case "lstrip": return block("text_trim", func.lineno, { "MODE": "LEFT" }, { "TEXT": this.convert(func.value) }); case "rstrip": return block("text_trim", func.lineno, { "MODE": "RIGHT" }, { "TEXT": this.convert(func.value) }); default: throw new Error("Unknown function call!"); }*/ } else if (name in PythonToBlocks.KNOWN_ATTR_FUNCTIONS) { return PythonToBlocks.KNOWN_ATTR_FUNCTIONS[name].bind(this)(func, args, keywords, starargs, kwargs, node) } //兜底图形块呈现方式 if(keywords.length != 0){ throw new Error("not implement for kargs"); } //console.log(func, args, keywords, starargs, kwargs); heights = this.getChunkHeights(node); extractedSource = this.getSourceCode(arrayMin(heights), arrayMax(heights)); var col_endoffset = node.col_endoffset; if (args.length > 0) { for (var i = 0; i < args.length; i+= 1) { col_endoffset = args[i].col_endoffset; } } else { col_endoffset += 2; expressionCall += "()"; } var expressionCall = extractedSource.slice(node.col_offset, 1+col_endoffset); //console.log(node, extractedSource, node.col_offset, node.col_endoffset); var lineno = node.lineno; //console.error(e); //return raw_expression(extractedSource.split("=")[1], lineno); var argumentsNormal = {}; var argumentsMutation = {"@name": name}; for (var i = 0; i < args.length; i+= 1) { argumentsNormal["ARG"+i] = this.convert(args[i]); argumentsMutation[i] = this.convert(args[i]); } var methodCall = block("procedures_callreturn", node.lineno, { }, argumentsNormal, { "inline": "true" }, argumentsMutation); return block("attribute_access", node.lineno, {}, { "MODULE": this.convert(func.value), "NAME": methodCall }, { "inline": "true"}, {}); } /* * func: expr_ty * args: asdl_seq * keywords: asdl_seq * starargs: expr_ty * kwargs: expr_ty * */ PythonToBlocks.prototype.Call = function(node) { var func = node.func; var args = node.args; var keywords = node.keywords; var starargs = node.starargs; var kwargs = node.kwargs; switch (func._astname) { case "Name": switch (this.identifier(func.id)) { case "_print": if (args.length == 1) { return [block("text_print", node.lineno, {}, { "TEXT": this.convert(args[0])})]; } else { return [block("text_print_multiple", node.lineno, {}, this.convertElements("PRINT", args), {"inline": "true" }, { "@items": args.length})]; } case "_abs": return block("math_single", node.lineno, {"OP": "ABS"}, {"NUM": this.convert(args[0])}) case "_round": return block("math_round", node.lineno, {"OP": "ROUND"}, {"NUM": this.convert(args[0])}) case "_sum": return block("math_on_list", node.lineno, {"OP": "SUM"}, {"LIST": this.convert(args[0])}) case "_min": return block("math_on_list", node.lineno, {"OP": "MIN"}, {"LIST": this.convert(args[0])}) case "_max": return block("math_on_list", node.lineno, {"OP": "MAX"}, {"LIST": this.convert(args[0])}) case "_len": return block("lists_length", node.lineno, {}, {"VALUE": this.convert(args[0])}) case "_math_modes": return block("list_trig", node.lineno, {'OP':'MODE'}, {"DATA": this.convert(args[0])}) case "_xrange": return block("procedures_callreturn", node.lineno, {}, {"ARG0": this.convert(args[0])}, {"inline": "true"}, {"@name": "xrange", "": this.convert(args[0])}) default: var funcname = this.identifier(func.id); if(funcname in py2block_config.globalFunctionD){ try { var config_block = py2block_config.globalFunctionD[funcname](this, func, args, keywords, starargs, kwargs, node); if (config_block != null) return config_block; }catch(e){ } } if (starargs && starargs.length > 0) { throw new Error("*args (variable arguments) are not implemented yet."); } else if (kwargs && kwargs.length > 0) { throw new Error("**args (keyword arguments) are not implemented yet."); } else if (keywords && keywords.length > 0) { throw new Error("**args (keyword arguments) are not implemented yet."); } var argumentsNormal = {}; var argumentsMutation = {"@name": this.identifier(func.id)}; for (var i = 0; i < args.length; i+= 1) { argumentsNormal["ARG"+i] = this.convert(args[i]); argumentsMutation[i] = this.convert(args[i]); } var functype = this.funcname_to_type[this.identifier(func.id)]; var blockid = "procedures_callreturn"; if(functype == "procedures_defnoreturn"){ blockid = "procedures_callnoreturn"; } var b = block(blockid, node.lineno, {}, argumentsNormal, { "inline": "true" }, argumentsMutation); if(blockid == "procedures_callnoreturn"){ return [b]; }else{ return b; } } // Direct function call case "Attribute": { var functype = this.funcname_to_type_class[func.attr.v]; if(functype == "method_procedures_defnoreturn" || functype == "method_procedures_defreturn") { if (starargs !== null && starargs.length > 0) { throw new Error("*args (variable arguments) are not implemented yet."); } else if (kwargs !== null && kwargs.length > 0) { throw new Error("**args (keyword arguments) are not implemented yet."); } else if (keywords !== null && keywords.length > 0) { throw new Error("**args (keyword arguments) are not implemented yet."); } var funcname = func.attr.v; var argumentsNormal = {}; var argumentsMutation = {"@name": funcname}; if(args.length) { argumentsNormal["ARG0"] = this.convert(args[0]); argumentsMutation[0] = this.convert(args[0]); } for (var i = 1; i <= args.length; i+= 1) { argumentsNormal["ARG"+i] = this.convert(args[i-1]); argumentsMutation[i] = this.convert(args[i-1]); } py2block_config.pinType = "object_get"; argumentsNormal["DATA"] = this.convert(node.func.value); py2block_config.pinType=null; var blockid = "method_procedures_callreturn"; if(functype.indexOf("method_procedures_defnoreturn") != -1){ blockid = "method_procedures_callnoreturn"; } var b = block(blockid, node.lineno, {},argumentsNormal, { "inline": "true" }, argumentsMutation); if(blockid.indexOf("method_procedures_callnoreturn") != -1){ return [b]; }else{ return b; } } else { // Module function call return this.CallAttribute(func, args, keywords, starargs, kwargs, node); } } } } /* * value: expr_ty * */ PythonToBlocks.prototype.Repr = function(node) { var value = node.value; throw new Error("Repr is not yet implemented"); } /* * n: object * */ PythonToBlocks.prototype.Num = function(node) { var n = node.n; var nVal = Sk.ffi.remapToJs(n); if(py2block_config.pinType == "pins_digital") { if(py2block_config.inScope == "i2c_init"){ return block(py2block_config.pinType, node.lineno, { "PIN": nVal }); } if(py2block_config.inScope == "rgb_create_block"){ return block(py2block_config.pinType, node.lineno, { "PIN": nVal }); } if(nVal == 1 || nVal == 0) { return block("inout_highlow", node.lineno, {"BOOL": (nVal == 1 ? "HIGH" : "LOW")}); } }else if(py2block_config.pinType == "math_indexer_number"){ return block(py2block_config.pinType, node.lineno, { "NUM": nVal }); }else if(py2block_config.pinType == "number"){ return block(py2block_config.pinType, node.lineno, { "op": nVal }); }else if(py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": nVal }); } if(py2block_config.inScope == "lcd_init"){ return block("math_number", node.lineno, {"NUM": '0x' + nVal.toString(16)}); }else if(py2block_config.inScope == "ledswitch"){ return block(py2block_config.inScope, node.lineno, { "flag": nVal }); }else if(py2block_config.inScope == "switch"){ return block(py2block_config.inScope, node.lineno, { "flag": nVal }); } return block("math_number", node.lineno, {"NUM": nVal}); } PythonToBlocks.prototype.Num_value = function(node) { var n = node.n; return Sk.ffi.remapToJs(n); } /* * s: string * */ PythonToBlocks.prototype.Str = function(node) { var s = node.s; var strValue = Sk.ffi.remapToJs(s); /*if (strValue.split("\n").length > 1) { return block("string_multiline", node.lineno, {"TEXT": strValue}); } else { return block("text", node.lineno, {"TEXT": strValue}); } */ if (strValue.indexOf('\n')!=-1){ return block("text_textarea", node.lineno, {"VALUE": strValue}) }else{ strValue = strValue.replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\t/g, '\\t') return block("text", node.lineno, {"TEXT": strValue}) } } PythonToBlocks.prototype.Str_value = function(node) { var s = node.s; return Sk.ffi.remapToJs(s); } /* * value: expr_ty * attr: identifier * ctx: expr_context_ty * */ PythonToBlocks.prototype.Attribute = function(node) { var value = node.value; var attr = node.attr; var ctx = node.ctx; var valueName = ""; if(value._astname == "Name"){ valueName = this.identifier(value.id); }else if(value._astname == "Attribute"){ valueName = this.Name_str(value.value) + "." + this.identifier(value.attr); } var attrName = this.identifier(attr); var attrD = py2block_config.moduleAttrD.get(valueName); if (attrName in attrD) { try { return attrD[attrName](node, valueName, attrName); } catch (e) { } } else { var keys = Object.keys(py2block_config.objectAttrD.get(attrName)); if (keys.length != 0) { try { if (!py2block_config.objectAttrD.get(attrName)['Default']) { var firstKey = keys[0]; py2block_config.objectAttrD.get(attrName)['Default'] = py2block_config.objectAttrD.get(attrName)[firstKey]; } return py2block_config.objectAttrD.get(attrName)['Default'](this, node, value, attr); }catch(e){ } } } if(attr.v == 'value' && py2block_config.board == py2block_config.ESP32){ if(value.id.v.indexOf("pin") != -1 && Blockly.Blocks['inout_digital_read']){ pbc.pinType = "pins_digital"; var pinblock = this.convert(value); pbc.pinType = null; return block("inout_digital_read", node.lineno, {}, { "PIN": pinblock }); } else if(value.id.v.indexOf("adc") != -1 && Blockly.Blocks['inout_analog_read']){ pbc.pinType = "pins_analog"; var pinblock = this.convert(value); pbc.pinType = null; return block("inout_analog_read", node.lineno, {}, { "PIN": pinblock }); } else if(value.id.v.indexOf("tc") != -1 && Blockly.Blocks['inout_pin_pressed']){ pbc.pinType = "pins_touch"; var pinblock = this.convert(value); pbc.pinType = null; return block("inout_pin_pressed", node.lineno, {}, { "pin": pinblock }); } } else if(Blockly.Blocks['property_get']){ py2block_config.pinType = "object_get"; var mode = this.convert(value); py2block_config.pinType=null; return block("property_get", node.lineno, { "VAR": node.attr.v }, { "VALUE": mode }, { "inline": "true"}, {}); } return block("attribute_access", node.lineno, { "MODULE": this.convert(value), "NAME": this.convert(attr) }); //throw new Error("Attribute access not implemented"); } /* * value: expr_ty * slice: slice_ty * ctx: expr_context_ty * */ PythonToBlocks.prototype.Subscript = function(node) { var value = node.value; var slice = node.slice; var ctx = node.ctx; if (slice._astname == "Index") { if(slice.value._astname == "Str" ){ return block("dicts_get", node.lineno, {}, { "KEY": this.convert(slice.value), "DICT": this.convert(value) }); } else if(slice.value._astname == "Tuple") { return block("lists_2d_get_data_with_col_row", node.lineno, {}, { "row": this.convert(slice.value.elts[0]), "col": this.convert(slice.value.elts[1]), "LIST": this.convert(value) }); } else { if(slice.value._astname == "Num" && value.func != null && value.func._astname == "Attribute" && this.identifier(value.func.attr) == "ifconfig"){ return block('network_get_connect', node.lineno, { "mode":this.Num_value(slice.value) }, { "VAR":this.convert(value.func.value) }); } if(slice.value._astname == "Num" && value.func != null && value.func._astname == "Attribute" && this.identifier(value.func.attr) == "scan"){ return block('network_scan', node.lineno, { "mode":this.Num_value(slice.value) }, { "VAR":this.convert(value.func.value) }); } if(slice.value._astname == "Num" && value.func != null && value.func._astname == "Attribute" && this.identifier(value.func.attr) == "localtime"){ if (value.func.value.id.v == "time"){ return block('time_localtime', node.lineno, { "op":this.Num_value(slice.value) }, { }); } } return block("lists_get_index", node.lineno, {}, { "AT": this.convert(slice.value), "LIST": this.convert(value) }); } }else if(slice._astname == "Slice"){ var at1block; var at2block; if(slice.lower !== null){ py2block_config.pinType = "math_indexer_number"; at1block = this.convert(slice.lower); py2block_config.pinType = null; }else{ at1block = block("math_indexer_number", node.lineno, {"NUM": ''}); } if(slice.upper !== null){ py2block_config.pinType = "math_indexer_number"; at2block = this.convert(slice.upper); py2block_config.pinType = null; }else{ at2block = block("math_indexer_number", node.lineno, {"NUM": ''}); } return block("lists_get_sublist", node.lineno, {}, { "AT1": at1block, "AT2": at2block, "LIST": this.convert(value), }); } else if(slice._astname == "ExtSlice"){ var at1block; var at2block; var at3block; var at4block; if(slice.dims["0"].lower !== null){ py2block_config.pinType = "math_indexer_number"; at1block = this.convert(slice.dims["0"].lower); py2block_config.pinType = null; }else{ at1block = block("math_indexer_number", node.lineno, {"NUM": ''}); } if(slice.dims["0"].upper !== null){ py2block_config.pinType = "math_indexer_number"; at2block = this.convert(slice.dims["0"].upper); py2block_config.pinType = null; }else{ at2block = block("math_indexer_number", node.lineno, {"NUM": ''}); } if(slice.dims["1"].lower !== null){ py2block_config.pinType = "math_indexer_number"; at3block = this.convert(slice.dims["1"].lower); py2block_config.pinType = null; }else{ at3block = block("math_indexer_number", node.lineno, {"NUM": ''}); } if(slice.dims["1"].upper !== null){ py2block_config.pinType = "math_indexer_number"; at4block = this.convert(slice.dims["1"].upper); py2block_config.pinType = null; }else{ at4block = block("math_indexer_number", node.lineno, {"NUM": ''}); } return block("lists_2d_get_col_row_data", node.lineno, {}, { "row_start": at1block, "row_end": at2block, "col_start": at3block, "col_end": at4block, "LIST": this.convert(value), }); } throw new Error("This kind of subscript is not supported."); } /* * id: identifier * ctx: expr_context_ty */ PythonToBlocks.prototype.Name = function(node) { var id = node.id; var ctx = node.ctx; var nodeName = this.Name_str(node); //处理micropython的管脚(eg:pin10) var pinMatcher = /pin[0-9]*/; var dacMatcher = /dac[0-9]*/; var pwmMatcher = /pwm[0-9]*/; var adcMatcher = /adc[0-9]*/; var tcMatcher = /tc[0-9]*/; var ioMatcher = /IO[0-9]*/; if(py2block_config.board == py2block_config.MICROBITPY && pinMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id).replace("pin", '') }); } if(py2block_config.board == py2block_config.ESP32 && pinMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && dacMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && pwmMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && adcMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && tcMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.MICROBITPY && (nodeName === "button_a" || nodeName === "button_b") && py2block_config.pinType == "pins_button"){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && (nodeName === "button_a" || nodeName === "button_b") && py2block_config.pinType == "pins_button"){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && (nodeName === "button_B1" || nodeName === "button_B2" || nodeName === "button_A1" || nodeName === "button_A2" || nodeName === "button_A3" || nodeName === "button_A4") && py2block_config.pinType == "pins_button" ){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && (nodeName === "touch1" || nodeName === "touch2"|| nodeName === "touch3"|| nodeName === "touch4") && py2block_config.pinType =="number1"){ return block(py2block_config.pinType, node.lineno, { "op": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && (nodeName === "touch1" || nodeName === "touch2"|| nodeName === "touch3"|| nodeName === "touch4"|| nodeName === "touch5"|| nodeName === "touch6") && py2block_config.pinType =="handbit_number1"){ return block(py2block_config.pinType, node.lineno, { "op": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && (nodeName === "touch_T1" || nodeName === "touch_T2"|| nodeName === "touch_T3"|| nodeName === "touch_T4") && py2block_config.pinType =="number1"){ return block(py2block_config.pinType, node.lineno, { "op": this.identifier(id) }); } if(py2block_config.pinType == "pins_callback"){ return block("factory_block_return", node.lineno, { "VALUE": this.identifier(id) }); } if(py2block_config.board == py2block_config.ESP32 && ioMatcher.test(nodeName) && py2block_config.pinType != null){ return block(py2block_config.pinType, node.lineno, { "PIN": this.identifier(id).replace("pin", '') }); } if(py2block_config.reservedNameD[nodeName] != null){ try { return py2block_config.reservedNameD[nodeName](this, node, id, ctx, nodeName); }catch(e){ } } switch (this.Name_str(node)) { case "True": return block("logic_boolean", node.lineno, {"BOOL": "TRUE"}); case "False": return block("logic_boolean", node.lineno, {"BOOL": "FALSE"}); case "None": return block("logic_null", node.lineno); case "___": return null; default: if(py2block_config.pinType == "object_get") { return block('object_get', node.lineno, { "VAR": this.identifier(id) }); } else if(py2block_config.pinType == "class_get") { return block('class_get', node.lineno, { "VAR": this.identifier(id) }); } else { return block('variables_get', node.lineno, { "VAR": this.identifier(id) }); } } } PythonToBlocks.prototype.NameConstant = function(node) { var id = node.id; var ctx = node.ctx; var nodeName = node.value.v; switch (nodeName) { case 1: return block("logic_boolean", node.lineno, {"BOOL": "TRUE"}); case 0: return block("logic_boolean", node.lineno, {"BOOL": "FALSE"}); case null: return block("logic_null", node.lineno); case "___": return null; default: if(py2block_config.pinType == "object_get") { return block('object_get', node.lineno, { "VAR": this.identifier(id) }); } else if(py2block_config.pinType == "class_get") { return block('class_get', node.lineno, { "VAR": this.identifier(id) }); } else { return block('variables_get', node.lineno, { "VAR": this.identifier(id) }); } } } /* * id: identifier * ctx: expr_context_ty */ PythonToBlocks.prototype.Name_str = function(node) { var id = node.id; var ctx = node.ctx; return this.identifier(id); } PythonToBlocks.prototype.convertElements = function(key, values) { var output = {}; for (var i = 0; i < values.length; i++) { output[key+i] = this.convert(values[i]); } return output; } /* * elts: asdl_seq * ctx: expr_context_ty * */ PythonToBlocks.prototype.List = function(node) { var elts = node.elts; if (elts.length > 3){ // return with one block & comma seperated var valueList = []; var s = this.getSourceCode(elts).split('\n')[node.lineno-1]; var lt = s.indexOf('[',elts[0].col_offset) var rt = s.indexOf(']',elts[0].col_offset) while (lt != -1 && lt < rt){ var lt = s.indexOf('[',lt+1) var rt = s.indexOf(']',rt+1) } if (s.length > 0){ //s = s.substring(elts[0].col_offset,rt); s = s.substring(node.col_offset+1,rt); valueList = s.split(","); } else valueList = ""; return block("list_many_input", node.lineno, {"CONTENT": valueList}, {} , { "inline": "true", }, { }); }else{ // return with many blocks return block("lists_create_with_noreturn", node.lineno, {}, this.convertElements("ADD", elts) , { "inline": "true", }, { "@items": elts.length }); } } /* * elts: asdl_seq * ctx: expr_context_ty */ PythonToBlocks.prototype.Tuple = function(node) { var elts = node.elts; if (elts.length > 3){ // return with one block & comma seperated var valueList = []; var s = this.getSourceCode(elts).split('\n')[node.lineno-1]; var lt = s.indexOf('(',elts[0].col_offset) var rt = s.indexOf(')',elts[0].col_offset) while (lt != -1 && lt < rt){ var lt = s.indexOf('(',lt+1) var rt = s.indexOf(')',rt+1) } if (s.length > 0){ //s = s.substring(elts[0].col_offset,rt); s = s.substring(node.col_offset,rt); valueList = s.split(","); } else valueList = ""; return block("tuple_create_with_text_return", node.lineno, {"TEXT": valueList}, {} , { "inline": "true", }, { }); } else{ // return with many blocks return block("tuple_create_with_noreturn", node.lineno, {}, this.convertElements("ADD", elts) , { "inline": "true", }, { "@items": elts.length }); } } /* * elts: asdl_seq * ctx: expr_context_ty */ PythonToBlocks.prototype.Dict = function(node) { var keys = node.keys; var values = node.values; var keyList = []; var valueList = []; for (var i = 0; i < keys.length; i+= 1) { var k = keys[i]; var line = this.getSourceCode().split('\n')[k.lineno - 1]; var colonEnd = line.indexOf(':', keys[i].col_offset); keyList["KEY"+i] = line.substring(keys[i].col_offset, colonEnd).replace(/^\s+|\s+$/g,""); valueList["VALUE"+i] = this.convert(values[i]); } return block("dicts_create_with_noreturn", node.lineno, keyList, this.convertElements("ADD", values), { "inline": "true", }, { "@items": keys.length }); } /* * * */ PythonToBlocks.prototype.Ellipsis = function() { throw new Error("Ellipsis not implemented"); } /* * lower: expr_ty * upper: expr_ty * step: expr_ty * */ PythonToBlocks.prototype.Slice = function(node) { var lower = node.lower; var upper = node.upper; var step = node.step; throw new Error("Slices not implemented"); } /* * dims: asdl_seq * */ PythonToBlocks.prototype.ExtSlice = function(node) { var dims = node.dims; throw new Error("ExtSlice is not implemented."); } /* * value: expr_ty * */ PythonToBlocks.prototype.Index = function(value) { var value = node.value; throw new Error("Index is not implemented"); } /* * target: expr_ty * iter: expr_ty * ifs: asdl_seq * */ PythonToBlocks.prototype.comprehension = function(node) { var target = node.target; var iter = node.iter; var ifs = node.ifs; throw new Error("Comprehensions not implemented."); } /* * type: expr_ty * name: expr_ty * body: asdl_seq * */ PythonToBlocks.prototype.ExceptHandler = function(node) { var type = node.type; var name = node.name; var body = node.boy; throw new Error("Except handlers are not implemented"); } PythonToBlocks.prototype.argument_ = function(node) { var id = node.arg; return this.identifier(id); } /* * args: asdl_seq * vararg: identifier * kwarg: identifier * defaults: asdl_seq * */ PythonToBlocks.prototype.arguments_ = function(node) { var args = node.args; var vararg = node.vararg; var kwarg = node.kwarg; var defaults = node.defaults; var allArgs = []; for (var i = 0; i < args.length; i++) { var arg = args[i]; allArgs.push(this.argument_(arg)); } return allArgs; } /* * arg: identifier * value: expr_ty * */ PythonToBlocks.prototype.keyword = function(node) { var arg = node.arg; var value = node.value; throw new Error("Keywords are not implemented"); } /* * name: identifier * asname: identifier * */ PythonToBlocks.prototype.alias = function(node) { var name = node.name; var asname = node.asname; throw new Error("Aliases are not implemented"); } /* ----- expr_context ----- */ /* Load Store Del AugLoad AugStore Param */ /* ----- operator ----- */ /* Add Sub Mult Div Mod Pow LShift RShift BitOr BitXor BitAnd FloorDiv */ /* ----- unaryop ----- */ /* Invert Not UAdd USub */