Files
mixly3-server/mixly/common/js/python_to_blockly.js
2026-01-24 16:12:04 +08:00

2768 lines
85 KiB
JavaScript

/**
* 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
*/