feat: sync all remaining python source board configurations
This commit is contained in:
1
mixly/boards/default_src/python_skulpt_car/.eslintignore
Normal file
1
mixly/boards/default_src/python_skulpt_car/.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
others/skulpt
|
||||
3
mixly/boards/default_src/python_skulpt_car/.npmignore
Normal file
3
mixly/boards/default_src/python_skulpt_car/.npmignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
build
|
||||
origin
|
||||
551
mixly/boards/default_src/python_skulpt_car/blocks/game.js
Normal file
551
mixly/boards/default_src/python_skulpt_car/blocks/game.js
Normal file
@@ -0,0 +1,551 @@
|
||||
import * as Blockly from 'blockly/core';
|
||||
|
||||
//初始化地图为第1关
|
||||
export const initSettedMap_1 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第一关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
//初始化地图为第2关
|
||||
export const initSettedMap_2 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第二关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
//初始化地图为第3关
|
||||
export const initSettedMap_3 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第三关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
//初始化地图为第4关
|
||||
export const initSettedMap_4 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第四关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
//初始化地图为第5关
|
||||
export const initSettedMap_5 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第五关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
//初始化地图为第6关
|
||||
export const initSettedMap_6 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第六关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
//初始化地图为第7关
|
||||
export const initSettedMap_7 = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("初始化地图为第七关")
|
||||
this.setInputsInline(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const game_init = {
|
||||
init: function () {
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.appendStatementInput('DO0')
|
||||
.appendField(Blockly.Msg.MIXLY_GAME_INIT);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const move_direction_steps = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("向")
|
||||
.appendField(new Blockly.FieldDropdown([["东", '1'], ["南", '2'], ["西", '3'], ["北", '0']]), "direction");
|
||||
this.appendDummyInput()
|
||||
.appendField("移动");
|
||||
this.appendValueInput("times")
|
||||
.setCheck(Number)
|
||||
this.appendDummyInput()
|
||||
.appendField("步");
|
||||
this.setInputsInline(true);
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const Turn = {
|
||||
init: function () {
|
||||
var Directions = [
|
||||
[Blockly.Msg.MIXLY_GAME_Turn_LEFT, 'left'],
|
||||
[Blockly.Msg.MIXLY_GAME_Turn_RIGHT, 'right']
|
||||
];
|
||||
this.appendDummyInput()
|
||||
.appendField(Blockly.Msg.MIXLY_GAME_TURN1)
|
||||
.appendField(new Blockly.FieldDropdown(Directions), 'Direction')
|
||||
.appendField(Blockly.Msg.MIXLY_GAME_TURN2);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isDone = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField(Blockly.Msg.MIXLY_GAME_ISDONE);
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isPath = {
|
||||
init: function () {
|
||||
var Directions = [
|
||||
[Blockly.Msg.MIXLY_GAME_ISPATH_LEFT, 'left'],
|
||||
[Blockly.Msg.MIXLY_GAME_ISPATH_RIGHT, 'right']
|
||||
];
|
||||
this.appendDummyInput()
|
||||
.appendField(Blockly.Msg.MIXLY_GAME_ISPATH1)
|
||||
.appendField(new Blockly.FieldDropdown(Directions), 'Direction')
|
||||
.appendField(Blockly.Msg.MIXLY_GAME_ISPATH2);
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
//最新块
|
||||
export const set_map = {
|
||||
init: function () {
|
||||
// this.appendDummyInput()
|
||||
// .appendField("设置地图:长");
|
||||
this.appendValueInput("x")
|
||||
.setCheck(Number)
|
||||
.appendField("设置地图,长为:");
|
||||
this.appendValueInput("y")
|
||||
.setCheck(Number)
|
||||
.appendField("宽为:");
|
||||
this.appendValueInput("startPos")
|
||||
.setCheck(null)
|
||||
.appendField(",起点坐标");
|
||||
this.appendValueInput("endPos")
|
||||
.setCheck(null)
|
||||
.appendField("终点坐标");
|
||||
this.appendValueInput("background")
|
||||
.setCheck(null)
|
||||
.appendField("背景");
|
||||
// this.appendDummyInput()
|
||||
// .appendField(",背景")
|
||||
// .appendField(new Blockly.FieldDropdown([["背景1","3"],["背景2","4"]]), "bg");
|
||||
this.setInputsInline(true);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const game_get_local_img = {
|
||||
init: function () {
|
||||
this.imgArr = this.getLocalImg();
|
||||
this.appendDummyInput()
|
||||
.appendField(new Blockly.FieldDropdown(this.getLocalImg()), "type");
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
},
|
||||
onchange: function () {
|
||||
let typeValue = this.getFieldValue("type");
|
||||
//let newImgArr = this.getLocalImg();
|
||||
let newImgArr = this.imgArr;
|
||||
if (this.haveNewSrc(this.imgArr, newImgArr)) {
|
||||
this.imgArr = newImgArr;
|
||||
var typeField = this.getField("type");
|
||||
typeField.menuGenerator_ = this.imgArr;
|
||||
if (this.checkSrc(typeValue, this.imgArr)) {
|
||||
this.setFieldValue(typeValue, "type");
|
||||
} else {
|
||||
this.setFieldValue(this.imgArr[0][1], "type");
|
||||
}
|
||||
}
|
||||
},
|
||||
haveNewSrc: function (oldArr, newArr) {
|
||||
if (oldArr.length !== newArr.length) return true;
|
||||
for (var i = 0; i < oldArr.length; i++) {
|
||||
if (oldArr[i][0].src !== newArr[i][0].src) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
checkSrc: function (newSrc, srcArr) {
|
||||
for (var i = 0; i < srcArr.length; i++) {
|
||||
if (srcArr[i][0].src == newSrc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getLocalImg: function () {
|
||||
let imgArr = [];
|
||||
try {
|
||||
var imgDirArr = ["bg_default.png", "bg_astro.png", "bg_panda.jpg"]
|
||||
for (var i = 0; i < imgDirArr.length; i++) {
|
||||
var dropdownItem = {};
|
||||
dropdownItem.src = "./media/mixpyBuild/maps/" + imgDirArr[i];
|
||||
dropdownItem.width = 40;
|
||||
dropdownItem.height = 45;
|
||||
if (imgDirArr[i] == "") {
|
||||
dropdownItem.alt = "无";
|
||||
} else {
|
||||
dropdownItem.alt = "*";
|
||||
}
|
||||
var dropdownArr = [];
|
||||
dropdownArr.push(dropdownItem);
|
||||
var dropdownData = imgDirArr[i].substring(0, imgDirArr[i].lastIndexOf("."));
|
||||
dropdownData = '\'' + dropdownData + '\'';
|
||||
dropdownArr.push(dropdownData);
|
||||
imgArr.push(dropdownArr);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
imgArr = [["'无可用地图'", "'无可用地图'"]];
|
||||
}
|
||||
if (imgArr.length > 0) {
|
||||
return imgArr;
|
||||
}
|
||||
return [["'无可用地图'", "'无可用地图'"]];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const set_pathtype = {
|
||||
init: function () {
|
||||
this.appendValueInput("pathtype")
|
||||
.setCheck(null)
|
||||
.appendField("设置路径样式为");
|
||||
this.setInputsInline(true);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const game_get_path_img = {
|
||||
init: function () {
|
||||
this.imgArr = this.getLocalImg();
|
||||
this.appendDummyInput()
|
||||
.appendField(new Blockly.FieldDropdown(this.getLocalImg()), "type");
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
},
|
||||
onchange: function () {
|
||||
let typeValue = this.getFieldValue("type");
|
||||
//let newImgArr = this.getLocalImg();
|
||||
let newImgArr = this.imgArr;
|
||||
if (this.haveNewSrc(this.imgArr, newImgArr)) {
|
||||
this.imgArr = newImgArr;
|
||||
var typeField = this.getField("type");
|
||||
typeField.menuGenerator_ = this.imgArr;
|
||||
if (this.checkSrc(typeValue, this.imgArr)) {
|
||||
this.setFieldValue(typeValue, "type");
|
||||
} else {
|
||||
this.setFieldValue(this.imgArr[0][1], "type");
|
||||
}
|
||||
}
|
||||
},
|
||||
haveNewSrc: function (oldArr, newArr) {
|
||||
if (oldArr.length !== newArr.length) return true;
|
||||
for (var i = 0; i < oldArr.length; i++) {
|
||||
if (oldArr[i][0].src !== newArr[i][0].src) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
checkSrc: function (newSrc, srcArr) {
|
||||
for (var i = 0; i < srcArr.length; i++) {
|
||||
if (srcArr[i][0].src == newSrc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getLocalImg: function () {
|
||||
let imgArr = [];
|
||||
try {
|
||||
var imgDirArr = ["default.png", "bamboo.png", "pipeline.png"]
|
||||
for (var i = 0; i < imgDirArr.length; i++) {
|
||||
var dropdownItem = {};
|
||||
dropdownItem.src = "./media/mixpyBuild/path/" + imgDirArr[i];
|
||||
dropdownItem.width = 40;
|
||||
dropdownItem.height = 45;
|
||||
dropdownItem.alt = "*";
|
||||
var dropdownArr = [];
|
||||
dropdownArr.push(dropdownItem);
|
||||
var dropdownData = imgDirArr[i].substring(0, imgDirArr[i].lastIndexOf("."));
|
||||
dropdownData = '\'' + dropdownData + '\'';
|
||||
dropdownArr.push(dropdownData);
|
||||
imgArr.push(dropdownArr);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
imgArr = [["'无可用路径'", "'无可用路径'"]];
|
||||
}
|
||||
if (imgArr.length > 0) {
|
||||
return imgArr;
|
||||
}
|
||||
return [["'无可用路径'", "'无可用路径'"]];
|
||||
}
|
||||
};
|
||||
|
||||
export const place_item = {
|
||||
init: function () {
|
||||
this.setColour(290);
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.appendValueInput("posx")
|
||||
.setCheck(null)
|
||||
.appendField("在(");
|
||||
this.appendDummyInput()
|
||||
.appendField(',');
|
||||
this.appendValueInput("posy")
|
||||
.setCheck(null)
|
||||
.appendField("");
|
||||
this.appendDummyInput()
|
||||
.appendField(')放置')
|
||||
.appendField(new Blockly.FieldDropdown([["障碍", "'wall'"], ["金币", "'coin'"]]), "item");
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const game_get_character_img = {
|
||||
init: function () {
|
||||
this.imgArr = this.getLocalImg();
|
||||
this.appendDummyInput()
|
||||
.appendField(new Blockly.FieldDropdown(this.getLocalImg()), "type");
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
},
|
||||
onchange: function () {
|
||||
let typeValue = this.getFieldValue("type");
|
||||
//let newImgArr = this.getLocalImg();
|
||||
let newImgArr = this.imgArr;
|
||||
if (this.haveNewSrc(this.imgArr, newImgArr)) {
|
||||
this.imgArr = newImgArr;
|
||||
var typeField = this.getField("type");
|
||||
typeField.menuGenerator_ = this.imgArr;
|
||||
if (this.checkSrc(typeValue, this.imgArr)) {
|
||||
this.setFieldValue(typeValue, "type");
|
||||
} else {
|
||||
this.setFieldValue(this.imgArr[0][1], "type");
|
||||
}
|
||||
}
|
||||
},
|
||||
haveNewSrc: function (oldArr, newArr) {
|
||||
if (oldArr.length !== newArr.length) return true;
|
||||
for (var i = 0; i < oldArr.length; i++) {
|
||||
if (oldArr[i][0].src !== newArr[i][0].src) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
checkSrc: function (newSrc, srcArr) {
|
||||
for (var i = 0; i < srcArr.length; i++) {
|
||||
if (srcArr[i][0].src == newSrc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getLocalImg: function () {
|
||||
let imgArr = [];
|
||||
try {
|
||||
var imgDirArr = ["pegman.png", "astro.png", "panda.png", "robot.png"]
|
||||
for (var i = 0; i < imgDirArr.length; i++) {
|
||||
var dropdownItem = {};
|
||||
dropdownItem.src = "./media/mixpyBuild/characters/" + imgDirArr[i];
|
||||
dropdownItem.width = 40;
|
||||
dropdownItem.height = 45;
|
||||
dropdownItem.alt = "*";
|
||||
var dropdownArr = [];
|
||||
dropdownArr.push(dropdownItem);
|
||||
var dropdownData = imgDirArr[i].substring(0, imgDirArr[i].lastIndexOf("."));
|
||||
dropdownData = '\'' + dropdownData + '\'';
|
||||
dropdownArr.push(dropdownData);
|
||||
imgArr.push(dropdownArr);
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
imgArr = [["'无可用角色'", "'无可用角色'"]];
|
||||
}
|
||||
if (imgArr.length > 0) {
|
||||
return imgArr;
|
||||
}
|
||||
return [["'无可用角色'", "'无可用角色'"]];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const initialize = {
|
||||
init: function () {
|
||||
this.setColour(290);
|
||||
this.appendValueInput("character")
|
||||
.setCheck(null)
|
||||
.appendField("初始化角色为");
|
||||
this.appendDummyInput()
|
||||
// .appendField('初始化角色为')
|
||||
// .appendField(new Blockly.FieldDropdown([["默认⼩⼈","0"],["熊猫","1"],["宇航员","2"],["机器⼈","3"]]), "character")
|
||||
.appendField('面朝')
|
||||
.appendField(new Blockly.FieldDropdown([["北", "0"], ["南", "2"], ["西", "3"], ["东", "1"]]), "direction");
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const get_actor_point = {
|
||||
init: function () {
|
||||
this.setColour(290);
|
||||
this.appendDummyInput()
|
||||
.appendField('获取⻆⾊所获分数');
|
||||
this.setOutput(true, Number);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isBarrier = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField(new Blockly.FieldDropdown([["北", "0"], ["南", "2"], ["西", "3"], ["东", "1"]]), "direction");
|
||||
this.appendDummyInput()
|
||||
.appendField('侧有障碍');
|
||||
this.setInputsInline(true);
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const randomOil = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("随机生成小车油量");
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isOilFull = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField('需要加油');
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isLightGreen = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField('信号灯为绿灯');
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isLightRed = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField('信号灯为红灯');
|
||||
this.setOutput(true, null);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
this.setHelpUrl('');
|
||||
}
|
||||
};
|
||||
|
||||
export const addOil = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("进加油站加油");
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
|
||||
export const isCirculationRight = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("检查程序循环数目是否正确");
|
||||
this.setPreviousStatement(true);
|
||||
this.setColour(290);
|
||||
this.setTooltip('');
|
||||
}
|
||||
};
|
||||
1512
mixly/boards/default_src/python_skulpt_car/converters/data.js
Normal file
1512
mixly/boards/default_src/python_skulpt_car/converters/data.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
pbc.globalFunctionD['input'] = function(py2block, func, args, keywords, starargs, kwargs, node){
|
||||
if (args.length !== 1 && args.length !== 0) {
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
if (args.length == 1){
|
||||
var argblock = py2block.convert(args[0]);
|
||||
return block("inout_type_input", func.lineno, {
|
||||
"DIR":"str"
|
||||
}, {
|
||||
'VAR':argblock
|
||||
}, {
|
||||
"inline": "true"
|
||||
});}
|
||||
if (args.length == 0){
|
||||
|
||||
return block("inout_type_input", func.lineno, {
|
||||
"DIR":"str"
|
||||
}, {
|
||||
//'VAR':argblock
|
||||
}, {
|
||||
"inline": "true"
|
||||
});}
|
||||
}
|
||||
|
||||
|
||||
//int(input('prompt'))在math.js中实现
|
||||
//float(input('prompt'))在lists.js中实现
|
||||
|
||||
pbc.globalFunctionD['print'] = function(py2block, func, args, keywords, starargs, kwargs, node){
|
||||
if (args.length === 1 && keywords.length === 1
|
||||
&& py2block.identifier(keywords[0].arg) === "end"
|
||||
&& keywords[0].value._astname === "Str"
|
||||
//&& py2block.Str_value(keywords[0].value) === ""
|
||||
) { if(py2block.Str_value(keywords[0].value) === ""){//print('Hello',end ="")
|
||||
var argblock = py2block.convert(args[0]);
|
||||
return [block("inout_print_inline", func.lineno, {}, {
|
||||
'VAR':argblock
|
||||
}, {
|
||||
"inline": "false"
|
||||
})];
|
||||
}
|
||||
else{
|
||||
var argblock = py2block.convert(args[0]);
|
||||
return [block("inout_print_end", func.lineno, {
|
||||
}, {
|
||||
'VAR':argblock,
|
||||
'END':py2block.convert(keywords[0].value)
|
||||
}, {
|
||||
"inline": "true"
|
||||
})];
|
||||
}
|
||||
}else if (args.length === 1 && keywords.length === 0) { //print('Hello')
|
||||
var argblock = py2block.convert(args[0]);
|
||||
return [block("inout_print", func.lineno, {}, {
|
||||
'VAR':argblock
|
||||
}, {
|
||||
"inline": "false"
|
||||
})];
|
||||
}else if (args.length != 1 && keywords.length === 0) { //print()
|
||||
var d = py2block.convertElements("ADD", args);
|
||||
|
||||
return [block("inout_print_many", node.lineno, {
|
||||
}, d, {
|
||||
"inline": "true",
|
||||
}, {
|
||||
"@items":args.length
|
||||
})];
|
||||
|
||||
}else{
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
function defDict(type) {
|
||||
var dict = {};
|
||||
return {
|
||||
get: function (key) {
|
||||
if (!dict[key]) {
|
||||
dict[key] = type.constructor();
|
||||
}
|
||||
return dict[key];
|
||||
},
|
||||
dict: dict
|
||||
};
|
||||
}
|
||||
|
||||
function Py2blockConfig (){
|
||||
this.initIgnoreS();
|
||||
this.initModuleAttrD();
|
||||
this.initKnownModuleS();
|
||||
this.initObjectTypeD();
|
||||
}
|
||||
|
||||
var pbc = Py2blockConfig.prototype;
|
||||
pbc.MIXPY = "MIXPY";
|
||||
pbc.board = pbc.MIXPY;
|
||||
pbc.objectFunctionD = defDict({});
|
||||
pbc.moduleFunctionD = defDict({});
|
||||
pbc.moduleAttrD = defDict({});
|
||||
pbc.objectAttrD = defDict({});
|
||||
pbc.globalFunctionD = {};
|
||||
pbc.assignD = defDict({});
|
||||
pbc.ifStatementD= defDict({});
|
||||
pbc.whileStatementD= defDict({});
|
||||
pbc.forStatementD= defDict({});
|
||||
pbc.reservedNameD= {};
|
||||
pbc.knownModuleS = new Set();
|
||||
pbc.objectTypeD = {}; //key:变量名,value:变量类型,如{'a':'List'}
|
||||
pbc.ignoreS = new Set();
|
||||
pbc.pinType = null;
|
||||
pbc.inScope = null;
|
||||
pbc.formatModuleKeyL = [];
|
||||
pbc.formatModuleL = [];
|
||||
|
||||
//忽略某些方法、类、赋值
|
||||
pbc.initIgnoreS = function(){
|
||||
var pythonIgnoreL = [
|
||||
];
|
||||
var boardIgnoreL = [];
|
||||
|
||||
var ignoreL = pythonIgnoreL.concat(boardIgnoreL);
|
||||
for (var i = 0; i < ignoreL.length; i++) {
|
||||
this.ignoreS.add(ignoreL[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pbc.initModuleAttrD = function(){
|
||||
}
|
||||
|
||||
pbc.initKnownModuleS = function(){
|
||||
var pythonModuleL = [
|
||||
'math', 'random'
|
||||
];
|
||||
var boardModuleL = [];
|
||||
|
||||
var moduleL = pythonModuleL.concat(boardModuleL);
|
||||
for (var i = 0; i < moduleL.length; i++) {
|
||||
this.knownModuleS.add(moduleL[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pbc.initObjectTypeD = function () {
|
||||
this.objectTypeD = {
|
||||
'tina': 'turtle.Turtle',
|
||||
'f': 'open'
|
||||
}
|
||||
}
|
||||
|
||||
pbc.reset = function(){
|
||||
this.initObjectTypeD();
|
||||
}
|
||||
|
||||
var py2block_config = new Py2blockConfig();
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
pbc.globalFunctionD['exit'] = function (py2block, func, args, keywords, starargs, kwargs, node) {
|
||||
if (args.length != 0) {
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
return [block("controls_end_program", func.lineno, {}, {}, {
|
||||
"inline": "true"
|
||||
})];
|
||||
}
|
||||
|
||||
|
||||
|
||||
pbc.moduleFunctionD.get('time')['time'] = function(py2block, func, args, keywords, starargs, kwargs, node) {
|
||||
if (args.length != 0) {
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
return block("controls_millis", func.lineno, {}, {}, {
|
||||
"inline": "true"
|
||||
});
|
||||
}
|
||||
|
||||
pbc.moduleFunctionD.get('time')['localtime'] = function(py2block, func, args, keywords, starargs, kwargs, node) {
|
||||
if (args.length != 0) {
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
return block("time_localtime", func.lineno, {
|
||||
"op":'all'
|
||||
}, {}, {
|
||||
"inline": "true"
|
||||
});
|
||||
}
|
||||
|
||||
pbc.moduleFunctionD.get('time')['sleep'] = function(py2block, func, args, keywords, starargs, kwargs, node) {
|
||||
if (args.length != 1) {
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
var argblock = py2block.convert(args[0]);
|
||||
return [block("time_sleep", func.lineno, {}, {
|
||||
|
||||
"DELAY_TIME":argblock
|
||||
}, {
|
||||
"inline": "true"
|
||||
})];
|
||||
}
|
||||
1127
mixly/boards/default_src/python_skulpt_car/converters/turtle.js
Normal file
1127
mixly/boards/default_src/python_skulpt_car/converters/turtle.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,141 @@
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(1)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/inout.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(1)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/inout2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(2)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/ctrl.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(2)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/ctrl2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(3)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/math.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(3)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/math2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(4)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/logic.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(4)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/logic2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(5)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/text.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(5)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/text2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(6)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/list3.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(6)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/list4.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(7)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/tuple.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(7)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/tuple2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(8)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/dict.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(8)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/dict2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(9)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/set.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(9)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/set2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(10)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/var.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(10)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/var2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(11)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/func.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(11)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/func2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(12)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/game5.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(12)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/game6.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(13)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/data.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(13)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/data2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(14)>div.blocklyTreeRow>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/machine_learning.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
div.blocklyToolboxDiv>div.blocklyToolboxContents>div:nth-child(14)>div.blocklyTreeRow.blocklyTreeSelected>div.blocklyTreeRowContentContainer>span.blocklyTreeIcon {
|
||||
background: url('../../../../common/media/mark/machine_learning2.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
9
mixly/boards/default_src/python_skulpt_car/export.js
Normal file
9
mixly/boards/default_src/python_skulpt_car/export.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import * as MicropythonESP32Pins from './blocks/esp32_profile';
|
||||
import * as MicropythonESP32PinsBlocks from './blocks/pins';
|
||||
import * as MicropythonESP32PinsGenerators from './generators/pins';
|
||||
|
||||
export {
|
||||
MicropythonESP32Pins,
|
||||
MicropythonESP32PinsBlocks,
|
||||
MicropythonESP32PinsGenerators
|
||||
};
|
||||
202
mixly/boards/default_src/python_skulpt_car/generators/game.js
Normal file
202
mixly/boards/default_src/python_skulpt_car/generators/game.js
Normal file
@@ -0,0 +1,202 @@
|
||||
export const game_init = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = generator.statementToCode(block, "DO0") + 'blocklygame.initMap(\'block_id=' + block.id + '\');\n'
|
||||
|
||||
var code_piece = [];
|
||||
code_piece = code.split("\n");
|
||||
for (var i = 0; i < code_piece.length; i++) {
|
||||
if ((code_piece[i].indexOf(" ") >= 0)) {
|
||||
code_piece[i] = code_piece[i].replace(" ", "");
|
||||
}
|
||||
}
|
||||
code = ""
|
||||
for (var i = 0; i < code_piece.length; i++) {
|
||||
code += code_piece[i] + '\n'
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
// export const move_direction = function(block) {
|
||||
// generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
// var Direction = this.getFieldValue('direction');
|
||||
// return 'actor.moveDirection('+Direction+',\'block_id=' + block.id + '\');\n';
|
||||
// }
|
||||
|
||||
export const move_direction_steps = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var times = generator.valueToCode(this, 'times', generator.ORDER_ATOMIC);
|
||||
var Direction = this.getFieldValue('direction');
|
||||
var d = 'actor.moveDirection(' + Direction + ',\'block_id=' + block.id + '\');\n',
|
||||
d = generator.addLoopTrap(d, block.id) || generator.PASS;
|
||||
return 'for _my_variable in range(' + times + '):\n\t' + d;
|
||||
}
|
||||
|
||||
export const initSettedMap_1 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(0,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n';
|
||||
}
|
||||
|
||||
export const initSettedMap_2 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(1,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n';
|
||||
}
|
||||
|
||||
export const initSettedMap_3 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(2,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n';
|
||||
}
|
||||
|
||||
export const initSettedMap_4 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(3,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n' + 'actor.randomOil(\'block_id=' + block.id + '\');\n';
|
||||
}
|
||||
|
||||
export const initSettedMap_5 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(4,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n';
|
||||
}
|
||||
|
||||
export const initSettedMap_6 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(5,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n';
|
||||
}
|
||||
|
||||
export const initSettedMap_7 = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'blocklygame.settedMap(6,\'block_id=' + block.id + '\');\n' + 'actor=blocklygame.Actor(\'car\',2);\n';
|
||||
}
|
||||
|
||||
// export const move_forward = function(block) {
|
||||
// generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
// return 'actor.moveForward(\'block_id=' + block.id + '\');\n';
|
||||
// }
|
||||
|
||||
// export const move_backward = function(block) {
|
||||
// generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
// var code = 'actor.moveBackward(\'block_id=' + block.id + '\');\n';
|
||||
// return code;
|
||||
// }
|
||||
|
||||
export const Turn = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var dropdown_Direction = this.getFieldValue('Direction');
|
||||
var code = 'actor.turn(\'' + dropdown_Direction + "','block_id=" + block.id + '\');\n';
|
||||
return code;
|
||||
}
|
||||
|
||||
export const isDone = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'actor.isDone(\'block_id=' + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const isPath = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var dropdown_Direction = this.getFieldValue('Direction');
|
||||
var code = 'actor.isPath(\'' + dropdown_Direction + "','block_id=" + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
// 从这里开始是新的块
|
||||
export const get_actor_point = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'actor.getPoint(\'block_id=' + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const game_get_local_img = function (_, generator) {
|
||||
var dropdown_type = this.getFieldValue('type');
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = dropdown_type;
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const set_map = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var value_x = generator.valueToCode(this, 'x', generator.ORDER_ATOMIC);
|
||||
var value_y = generator.valueToCode(this, 'y', generator.ORDER_ATOMIC);
|
||||
var startPos = generator.valueToCode(this, 'startPos', generator.ORDER_ATOMIC);
|
||||
var endPos = generator.valueToCode(this, 'endPos', generator.ORDER_ATOMIC);
|
||||
var bg_pic = generator.valueToCode(this, 'background', generator.ORDER_ATOMIC);
|
||||
|
||||
return 'blocklygame.setMap(' + value_x + ',' + value_y + ',' + startPos + ',' + endPos + ',' + bg_pic + ",'block_id=" + block.id + '\');\n';
|
||||
}
|
||||
|
||||
export const game_get_character_img = function (_, generator) {
|
||||
var dropdown_type = this.getFieldValue('type');
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = dropdown_type;
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const initialize = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
// var value_character = this.getFieldValue('character');
|
||||
var value_direction = this.getFieldValue('direction');
|
||||
var value_character = generator.valueToCode(this, 'character', generator.ORDER_ATOMIC);
|
||||
return 'actor=blocklygame.Actor(' + value_character + ',' + value_direction + ",'block_id=" + block.id + '\');\n';
|
||||
}
|
||||
|
||||
export const place_item = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var value_posx = generator.valueToCode(this, 'posx', generator.ORDER_ATOMIC);
|
||||
var value_posy = generator.valueToCode(this, 'posy', generator.ORDER_ATOMIC);
|
||||
var value_item = this.getFieldValue('item');
|
||||
return 'blocklygame.placeItem(' + value_posx + ',' + value_posy + ',' + value_item + ",'block_id=" + block.id + '\');\n';
|
||||
}
|
||||
|
||||
export const game_get_path_img = function (_, generator) {
|
||||
var dropdown_type = this.getFieldValue('type');
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = dropdown_type;
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const set_pathtype = function (block, generator) {
|
||||
generator.definitions_['import_blocklygame'] = 'import blocklygame';
|
||||
var path_type = generator.valueToCode(this, 'pathtype', generator.ORDER_ATOMIC);
|
||||
return 'blocklygame.setPathType(' + path_type + ",'block_id=" + block.id + '\');\n';
|
||||
// return 'actor.getPoint();\n';
|
||||
}
|
||||
|
||||
export const isBarrier = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var dropdown_Direction = this.getFieldValue('direction');
|
||||
var code = 'actor.isBarrier(' + dropdown_Direction + ",'block_id=" + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const randomOil = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
return 'actor.randomOil(\'block_id=' + block.id + '\');\n';
|
||||
}
|
||||
|
||||
export const isOilFull = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'actor.isOilFull(\'block_id=' + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const isLightGreen = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'actor.isLightGreen(\'block_id=' + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const isLightRed = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'not actor.isLightGreen(\'block_id=' + block.id + '\')';
|
||||
return [code, generator.ORDER_ATOMIC];
|
||||
}
|
||||
|
||||
export const addOil = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'actor.addOil(\'block_id=' + block.id + '\');\n';
|
||||
return code;
|
||||
}
|
||||
|
||||
export const isCirculationRight = function (block, generator) {
|
||||
generator.definitions_.import_blocklygame = "import blocklygame";
|
||||
var code = 'actor.isCirculationRight(\'block_id=' + block.id + '\');\n';
|
||||
return code;
|
||||
}
|
||||
103
mixly/boards/default_src/python_skulpt_car/index.js
Normal file
103
mixly/boards/default_src/python_skulpt_car/index.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import * as Blockly from 'blockly/core';
|
||||
import { Profile } from 'mixly';
|
||||
|
||||
import Variables from '@mixly/python/others/variables';
|
||||
import Procedures from '@mixly/python/others/procedures';
|
||||
import { Python } from '@mixly/python/python_generator';
|
||||
|
||||
import * as PythonVariablesBlocks from '@mixly/python/blocks/variables';
|
||||
import * as PythonControlBlocks from '@mixly/python/blocks/control';
|
||||
import * as PythonMathBlocks from '@mixly/python/blocks/math';
|
||||
import * as PythonTextBlocks from '@mixly/python/blocks/text';
|
||||
import * as PythonListsBlocks from '@mixly/python/blocks/lists';
|
||||
import * as PythonDictsBlocks from '@mixly/python/blocks/dicts';
|
||||
import * as PythonLogicBlocks from '@mixly/python/blocks/logic';
|
||||
import * as PythonStorageBlocks from '@mixly/python/blocks/storage';
|
||||
import * as PythonProceduresBlocks from '@mixly/python/blocks/procedures';
|
||||
import * as PythonTupleBlocks from '@mixly/python/blocks/tuple';
|
||||
import * as PythonSetBlocks from '@mixly/python/blocks/set';
|
||||
import * as PythonHtmlBlocks from '@mixly/python/blocks/html';
|
||||
import * as PythonUtilityBlocks from '@mixly/python/blocks/utility';
|
||||
|
||||
import * as SkulptPyDataBlocks from '@mixly/python-skulpt/blocks/data';
|
||||
import * as SkulptPyInoutBlocks from '@mixly/python-skulpt/blocks/inout';
|
||||
import * as SkulptPySystemBlocks from '@mixly/python-skulpt/blocks/system';
|
||||
import * as SkulptPyTurtleBlocks from '@mixly/python-skulpt/blocks/turtle';
|
||||
|
||||
import * as GameBlocks from './blocks/game';
|
||||
|
||||
import * as PythonVariablesGenerators from '@mixly/python/generators/variables';
|
||||
import * as PythonControlGenerators from '@mixly/python/generators/control';
|
||||
import * as PythonMathGenerators from '@mixly/python/generators/math';
|
||||
import * as PythonTextGenerators from '@mixly/python/generators/text';
|
||||
import * as PythonListsGenerators from '@mixly/python/generators/lists';
|
||||
import * as PythonDictsGenerators from '@mixly/python/generators/dicts';
|
||||
import * as PythonLogicGenerators from '@mixly/python/generators/logic';
|
||||
import * as PythonStorageGenerators from '@mixly/python/generators/storage';
|
||||
import * as PythonProceduresGenerators from '@mixly/python/generators/procedures';
|
||||
import * as PythonTupleGenerators from '@mixly/python/generators/tuple';
|
||||
import * as PythonSetGenerators from '@mixly/python/generators/set';
|
||||
import * as PythonHtmlGenerators from '@mixly/python/generators/html';
|
||||
import * as PythonUtilityGenerators from '@mixly/python/generators/utility';
|
||||
|
||||
import * as SkulptPyDataGenerators from '@mixly/python-skulpt/generators/data';
|
||||
import * as SkulptPyInoutGenerators from '@mixly/python-skulpt/generators/inout';
|
||||
import * as SkulptPySystemGenerators from '@mixly/python-skulpt/generators/system';
|
||||
import * as SkulptPyTurtleGenerators from '@mixly/python-skulpt/generators/turtle';
|
||||
|
||||
import * as GameGenerators from './generators/game';
|
||||
|
||||
import './others/loader';
|
||||
|
||||
import './css/color_mixpy_python_skulpt.css';
|
||||
|
||||
Object.assign(Blockly.Variables, Variables);
|
||||
Object.assign(Blockly.Procedures, Procedures);
|
||||
Blockly.Python = Python;
|
||||
Blockly.generator = Python;
|
||||
|
||||
Profile.default = {};
|
||||
|
||||
Object.assign(
|
||||
Blockly.Blocks,
|
||||
PythonVariablesBlocks,
|
||||
PythonControlBlocks,
|
||||
PythonMathBlocks,
|
||||
PythonTextBlocks,
|
||||
PythonListsBlocks,
|
||||
PythonDictsBlocks,
|
||||
PythonLogicBlocks,
|
||||
PythonStorageBlocks,
|
||||
PythonProceduresBlocks,
|
||||
PythonTupleBlocks,
|
||||
PythonSetBlocks,
|
||||
PythonHtmlBlocks,
|
||||
PythonUtilityBlocks,
|
||||
SkulptPyDataBlocks,
|
||||
SkulptPyInoutBlocks,
|
||||
SkulptPySystemBlocks,
|
||||
SkulptPyTurtleBlocks,
|
||||
GameBlocks,
|
||||
);
|
||||
|
||||
Object.assign(
|
||||
Blockly.Python.forBlock,
|
||||
PythonVariablesGenerators,
|
||||
PythonControlGenerators,
|
||||
PythonMathGenerators,
|
||||
PythonTextGenerators,
|
||||
PythonListsGenerators,
|
||||
PythonDictsGenerators,
|
||||
PythonLogicGenerators,
|
||||
PythonStorageGenerators,
|
||||
PythonProceduresGenerators,
|
||||
PythonTupleGenerators,
|
||||
PythonSetGenerators,
|
||||
PythonHtmlGenerators,
|
||||
PythonUtilityGenerators,
|
||||
SkulptPyDataGenerators,
|
||||
SkulptPyInoutGenerators,
|
||||
SkulptPySystemGenerators,
|
||||
SkulptPyTurtleGenerators,
|
||||
GameGenerators
|
||||
);
|
||||
18
mixly/boards/default_src/python_skulpt_car/jsconfig.json
Normal file
18
mixly/boards/default_src/python_skulpt_car/jsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@mixly/python": [
|
||||
"../python"
|
||||
],
|
||||
"@mixly/python-skulpt": [
|
||||
"../python_skulpt"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"language": "Python",
|
||||
"nav": {
|
||||
"webrun": true,
|
||||
"websteprun": true,
|
||||
"webcancel": true,
|
||||
"save": {
|
||||
"py": true
|
||||
},
|
||||
"levelSelector": true
|
||||
},
|
||||
"saveMixWithCode": false
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,3 @@
|
||||
var mixpyProject = new MixpyProject();
|
||||
var pyengine = new PyEngine({}, mixpyProject);
|
||||
Sk.__future__ = Sk.python3;
|
||||
@@ -0,0 +1,3 @@
|
||||
import NavExt from './nav-ext';
|
||||
|
||||
NavExt.init();
|
||||
@@ -0,0 +1,97 @@
|
||||
export default class MixpyProject {
|
||||
constructor() {
|
||||
this.initProject();
|
||||
}
|
||||
|
||||
initProject() {
|
||||
this.fileD = {};
|
||||
this.MAINF = 'main.py';
|
||||
this.fileD[this.MAINF] = ["", true, 1];
|
||||
this.selectFile = this.MAINF;
|
||||
}
|
||||
|
||||
add(file, filecontent, filetype) {
|
||||
if (this.exist(file)) {
|
||||
console.log("Warning:file already in project");
|
||||
return;
|
||||
}
|
||||
this.fileD[file] = [filecontent, false, filetype];
|
||||
}
|
||||
|
||||
delete(file) {
|
||||
delete this.fileD[file];
|
||||
this.selectFile = undefined;
|
||||
}
|
||||
|
||||
getProject() {
|
||||
return Object.keys(this.fileD);
|
||||
}
|
||||
|
||||
getUploadFileList() {
|
||||
var fileNameList = Object.keys(this.fileD);
|
||||
var ret = [];
|
||||
for (var i in fileNameList) {
|
||||
if (this.fileD[fileNameList[i]][2] === 2)
|
||||
ret.push(fileNameList[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getNewFileList() {
|
||||
var fileNameList = Object.keys(this.fileD);
|
||||
var ret = [];
|
||||
for (var i in fileNameList) {
|
||||
if (this.fileD[fileNameList[i]][2] === 1)
|
||||
ret.push(fileNameList[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
isSelect(f) {
|
||||
return this.fileD[f][1];
|
||||
}
|
||||
|
||||
select(f) {
|
||||
// if (this.selectFile !== undefined) {
|
||||
// this.modify(this.selectFile, mixlyjs.getCodeContent());
|
||||
// this.fileD[this.selectFile][1] = false;
|
||||
// }
|
||||
this.fileD[f][1] = true;
|
||||
// this.selectFile = f;
|
||||
// var suffix = mixlyjs.getFileSuffix(f);
|
||||
// var textFileSuffix = ["py", "txt", "csv", "xml"];
|
||||
// if (textFileSuffix.indexOf(suffix) !== -1) {
|
||||
// tabClick('arduino');
|
||||
// mixlyjs.renderIno(this.fileD[f][0]);
|
||||
// } else {
|
||||
// var base64str = 'data:image/' + suffix + ';base64,' + this.fileD[f][0];
|
||||
// $('#mixpy_show_image').attr('src', base64str);
|
||||
// mixlyjs.renderIno(this.fileD[f][0]);
|
||||
// tabClick('image');
|
||||
// var $imageA = $('#mixpy_link_image');
|
||||
// $imageA.attr('href', base64str);
|
||||
// $imageA.attr('download', f);
|
||||
// }
|
||||
}
|
||||
|
||||
getFileNum() {
|
||||
var files = Object.keys(this.fileD);
|
||||
return files.length;
|
||||
}
|
||||
|
||||
getFileContent(f) {
|
||||
return this.fileD[f][0];
|
||||
}
|
||||
|
||||
getFileType(f) {
|
||||
return this.fileD[f][2];
|
||||
}
|
||||
|
||||
modify(f, content) {
|
||||
this.fileD[f][0] = content;
|
||||
}
|
||||
|
||||
exist(f) {
|
||||
return f in this.fileD;
|
||||
}
|
||||
}
|
||||
126
mixly/boards/default_src/python_skulpt_car/others/nav-ext.js
Normal file
126
mixly/boards/default_src/python_skulpt_car/others/nav-ext.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import $ from 'jquery';
|
||||
import * as Blockly from 'blockly/core';
|
||||
import {
|
||||
app,
|
||||
Nav,
|
||||
Debug,
|
||||
HTMLTemplate,
|
||||
Msg,
|
||||
Workspace,
|
||||
Storage
|
||||
} from 'mixly';
|
||||
import PythonShell from './python-shell';
|
||||
import LEVEL_SELECTOR_TEMPLATE from '../templates/html/level-selector.html';
|
||||
|
||||
|
||||
const NavExt = {};
|
||||
const LEVELS = [
|
||||
`<xml>
|
||||
<block type="initSettedMap_1"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_2"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_3"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_4"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_5"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_6"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_7"></block>
|
||||
</xml>`
|
||||
];
|
||||
NavExt.$shadow = $('<div style="position:absolute;z-index:1000;width:100%;background:transparent;bottom:0px;top:var(--nav-height);"></div>');
|
||||
NavExt.count = 0;
|
||||
|
||||
NavExt.init = function () {
|
||||
PythonShell.init();
|
||||
const nav = app.getNav();
|
||||
|
||||
nav.register({
|
||||
icon: 'icon-play-circled',
|
||||
title: '',
|
||||
id: 'python-steprun-btn',
|
||||
displayText: Blockly.Msg.MSG['step_run'],
|
||||
preconditionFn: () => {
|
||||
return true;
|
||||
},
|
||||
callback: () => {
|
||||
PythonShell.steprun().catch(Debug.error);
|
||||
},
|
||||
scopeType: Nav.Scope.LEFT,
|
||||
weight: 4
|
||||
});
|
||||
|
||||
nav.register({
|
||||
icon: 'icon-play-circled',
|
||||
title: '',
|
||||
id: 'python-run-btn',
|
||||
displayText: Blockly.Msg.MSG['run'],
|
||||
preconditionFn: () => {
|
||||
return true;
|
||||
},
|
||||
callback: () => {
|
||||
PythonShell.run().catch(Debug.error);
|
||||
},
|
||||
scopeType: Nav.Scope.LEFT,
|
||||
weight: 5
|
||||
});
|
||||
|
||||
nav.register({
|
||||
icon: 'icon-cancel',
|
||||
title: '',
|
||||
id: 'python-stop-btn',
|
||||
displayText: Blockly.Msg.MSG['stop'],
|
||||
preconditionFn: () => {
|
||||
return true;
|
||||
},
|
||||
callback: () => {
|
||||
PythonShell.stop().catch(Debug.error);
|
||||
},
|
||||
scopeType: Nav.Scope.LEFT,
|
||||
weight: 6
|
||||
});
|
||||
|
||||
const template = new HTMLTemplate(LEVEL_SELECTOR_TEMPLATE);
|
||||
const $selector = $(template.render());
|
||||
nav.getBoardSelector().before($selector);
|
||||
$selector.select2({
|
||||
width: '90px',
|
||||
minimumResultsForSearch: Infinity,
|
||||
dropdownCssClass: `mixly-scrollbar mixly-${template.getId()}`,
|
||||
dropdownAutoWidth: true,
|
||||
placeholder: '',
|
||||
language: Msg.nowLang
|
||||
});
|
||||
for (let i = 0; i < LEVELS.length; i++) {
|
||||
const option = new Option(`关卡 ${i + 1}`, i);
|
||||
$selector.append(option);
|
||||
}
|
||||
$selector.on('select2:select', (event) => {
|
||||
const { data } = event.params;
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
editor.setValue(LEVELS[parseInt(data.id)], '.mix');
|
||||
});
|
||||
$selector.on('select2:opening', () => {
|
||||
NavExt.count += 1;
|
||||
$(document.body).append(NavExt.$shadow);
|
||||
});
|
||||
$selector.on('select2:closing', () => {
|
||||
NavExt.count -= 1;
|
||||
!NavExt.count && NavExt.$shadow.detach();
|
||||
});
|
||||
$selector.trigger('change');
|
||||
Storage.board('mix', LEVELS[0]);
|
||||
Storage.board('path', '');
|
||||
}
|
||||
|
||||
export default NavExt;
|
||||
904
mixly/boards/default_src/python_skulpt_car/others/py-engine.js
Normal file
904
mixly/boards/default_src/python_skulpt_car/others/py-engine.js
Normal file
@@ -0,0 +1,904 @@
|
||||
/* eslint-disable new-cap */
|
||||
import Sk from './skulpt/skulpt';
|
||||
import $ from 'jquery';
|
||||
import PyGameZero from './skulpt/pygame-zero';
|
||||
import { Events, Debug } from 'mixly';
|
||||
import MIXPY_TEMPLATE from '../templates/python/mixpy.py';
|
||||
import BLOCKLY_GAME_URL from '../others/skulpt/libs/bg_highlight.js?url';
|
||||
import INIT_TOOL_URL from '../others/skulpt/libs/inittool.js?url';
|
||||
import BG_NONEHL_URL from '../others/skulpt/libs/bg_nonehl.js?url';
|
||||
|
||||
|
||||
// 外部引入的第三方库
|
||||
const externalLibs = {
|
||||
'./blocklygame/__init__.js': BLOCKLY_GAME_URL,
|
||||
'./blocktool/__init__.js': INIT_TOOL_URL,
|
||||
'./bg_nonehl/__init__.js': BG_NONEHL_URL
|
||||
};
|
||||
|
||||
var GLOBAL_VALUE;
|
||||
|
||||
function prettyPrintError(error) {
|
||||
if (typeof error === "string") {
|
||||
return error;
|
||||
}
|
||||
// A weird skulpt thing?
|
||||
if (error.tp$str !== undefined) {
|
||||
return error.tp$str().v;
|
||||
}
|
||||
return "" + error.name + ": " + error.message;
|
||||
}
|
||||
|
||||
export default class PyEngine {
|
||||
#events_ = new Events(['input', 'output', 'display', 'finished', 'error']);
|
||||
|
||||
constructor(programStatus, mixpyProject) {
|
||||
this.programStatus = programStatus;
|
||||
this.mixpyProject = mixpyProject;
|
||||
/**
|
||||
* Definable function to be run when execution has fully ended,
|
||||
* whether it succeeds or fails.
|
||||
*
|
||||
*/
|
||||
this.onExecutionEnd = null;
|
||||
}
|
||||
|
||||
getEvents() {
|
||||
return this.#events_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that will attempt to call the defined onExecutionEnd,
|
||||
* but will do nothing if there is no function defined.
|
||||
*/
|
||||
executionEnd_() {
|
||||
if (this.onExecutionEnd !== null) {
|
||||
this.onExecutionEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Python Execution engine and the Printer (console).
|
||||
*/
|
||||
loadEngine(container) {
|
||||
Sk.__future__ = Sk.python3;
|
||||
// No connected services
|
||||
Sk.connectedServices = {};
|
||||
// No time limit
|
||||
//Sk.execLimit = null;
|
||||
// Ensure version 3, so we get proper print handling
|
||||
// Sk.python3 = true;
|
||||
//输出海龟画图图片
|
||||
//如果需要输出pygame_zero场景,需要重新设置
|
||||
Sk.TurtleGraphics = {
|
||||
target: container,
|
||||
width: 500,
|
||||
height: 500
|
||||
};
|
||||
|
||||
PyGameZero.setContainer(container);
|
||||
|
||||
//数据分析显示图片
|
||||
Sk.MatPlotLibGraphics = {
|
||||
target: container,
|
||||
width: 500,
|
||||
height: 500
|
||||
};
|
||||
// TODO
|
||||
//Sk.console = printer.getConfiguration();
|
||||
// Definitely use a prompt
|
||||
Sk.inputfunTakesPrompt = true;
|
||||
|
||||
// Keeps track of the tracing while the program is executing; destroyed afterwards.
|
||||
this.executionBuffer = {};
|
||||
|
||||
Sk.domOutput = (html) => {
|
||||
const dom = this.#events_.on('display', html)[0];
|
||||
return dom;
|
||||
};
|
||||
|
||||
Sk.configure({
|
||||
//设置文本输出
|
||||
output: (lineText) => {
|
||||
this.#events_.run('output', {
|
||||
content: lineText
|
||||
});
|
||||
},
|
||||
// Function to handle loading in new files
|
||||
read: this.readFile.bind(this),
|
||||
inputfun: this.skInput.bind(this),
|
||||
inputfunTakesPrompt: true,
|
||||
execLimit: Number.POSITIVE_INFINITY,
|
||||
fileread: this.fileread.bind(this),
|
||||
filewrite: this.filewrite.bind(this),
|
||||
__future__: Sk.python3, //python3已成默认值,要使用python2需要单独设置
|
||||
});
|
||||
|
||||
Sk.builtins.value = new Sk.builtin.func(function () {
|
||||
return Sk.ffi.remapToPy(GLOBAL_VALUE === undefined ? 5 : GLOBAL_VALUE);
|
||||
});
|
||||
Sk.builtins.set_value = new Sk.builtin.func(function (v) {
|
||||
GLOBAL_VALUE = v.v;
|
||||
});
|
||||
|
||||
Sk.builtinFiles.files['./mixpy.py'] = MIXPY_TEMPLATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to access Skulpt built-ins. This is pretty generic, taken
|
||||
* almost directly from the Skulpt docs.
|
||||
*
|
||||
* @param {String} filename - The python filename (e.g., "os" or "pprint") that will be loaded.
|
||||
* @returns {String} The JavaScript source code of the file (weird, right?)
|
||||
* @throws Will throw an error if the file isn't found.
|
||||
*/
|
||||
readFile(file) {
|
||||
// console.log("Attempting file: " + Sk.ffi.remapToJs(file));
|
||||
// 加载模块
|
||||
if (PyGameZero.matchModelName(file)) {
|
||||
return PyGameZero.load(file);
|
||||
}
|
||||
if (externalLibs[file] !== undefined) {
|
||||
return Sk.misceval.promiseToSuspension(fetch(externalLibs[file]).then((resp) => resp.text()));
|
||||
}
|
||||
if (Sk.builtinFiles === undefined || Sk.builtinFiles.files[file] === undefined) {
|
||||
throw "File not found: '" + file + "'";
|
||||
}
|
||||
return Sk.builtinFiles.files[file];
|
||||
}
|
||||
|
||||
fileread(filename, mode) {
|
||||
if (this.mixpyProject.exist(filename)) {
|
||||
return this.mixpyProject.getFileContent(filename);
|
||||
}
|
||||
if (mode.indexOf('w') !== -1) {
|
||||
this.mixpyProject.add(filename, '', 1);
|
||||
return '';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filewrite(fileItem, str) {
|
||||
var filename = fileItem.name;
|
||||
this.mixpyProject.modify(filename, str);
|
||||
this.mixpyProject.select(filename);
|
||||
}
|
||||
|
||||
skInput(prompt) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#events_.run('input', {
|
||||
content: {
|
||||
prompt
|
||||
},
|
||||
resolve, reject
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of the execution engine, including reinitailizing
|
||||
* the execution buffer (trace, step, etc.), reseting the printer, and
|
||||
* hiding the trace button.
|
||||
*
|
||||
*/
|
||||
reset() {
|
||||
Sk.execLimit = Number.POSITIVE_INFINITY;
|
||||
Sk.TurtleGraphics.reset && Sk.TurtleGraphics.reset();
|
||||
}
|
||||
|
||||
kill() {
|
||||
// 新增了sprite相关内容
|
||||
// window.SPRITE.kill();
|
||||
//点击取消按钮发送数据
|
||||
Sk.execLimit = 0;
|
||||
this.executionEnd_();
|
||||
}
|
||||
|
||||
/**
|
||||
* "Steps" the execution of the code, meant to be used as a callback to the Skulpt
|
||||
* environment.
|
||||
*
|
||||
* @param {Object} variables - Hash that maps the names of variables (Strings) to their Skulpt representation.
|
||||
* @param {Number} lineNumber - The corresponding line number in the source code that is being executed.
|
||||
* @param {Number} columnNumber - The corresponding column number in the source code that is being executed. Think of it as the "X" position to the lineNumber's "Y" position.
|
||||
* @param {String} filename - The name of the python file being executed (e.g., "__main__.py").
|
||||
* @param {String} astType - Unused? TODO: What is this?
|
||||
* @param {String} ast - String-encoded JSON representation of the AST node associated with this element.
|
||||
*/
|
||||
step(variables, lineNumber,
|
||||
columnNumber, filename) {
|
||||
if (filename == '<stdin>.py') {
|
||||
var currentStep = this.executionBuffer.step;
|
||||
var globals = this.parseGlobals(variables);
|
||||
this.executionBuffer.trace.push(
|
||||
{
|
||||
'step': currentStep,
|
||||
'filename': filename,
|
||||
//'block': highlightMap[lineNumber-1],
|
||||
'line': lineNumber,
|
||||
'column': columnNumber,
|
||||
'properties': globals.properties,
|
||||
'modules': globals.modules
|
||||
}
|
||||
);
|
||||
this.executionBuffer.step = currentStep + 1;
|
||||
this.executionBuffer.last_step = currentStep + 1;
|
||||
this.executionBuffer.line_number = lineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the AbstractInterpreter to get some static information about the code,
|
||||
* in particular the variables' types. This is needed for type checking.
|
||||
*
|
||||
* @returns {Object<String, AIType>} Maps variable names (as Strings) to types as constructed by the AbstractIntepreter.
|
||||
*/
|
||||
analyzeVariables() {
|
||||
// Get the code
|
||||
var code = this.main.model.programs['__main__']();
|
||||
if (code.trim() == "") {
|
||||
return {};
|
||||
}
|
||||
|
||||
// var analyzer = new AbstractInterpreter(code);
|
||||
// report = analyzer.report;
|
||||
// return analyzer.variableTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the AbstractInterpreter to get some static information about the code,
|
||||
* including potential semantic errors. It then parses that information to give
|
||||
* feedback.
|
||||
*
|
||||
* @returns {Boolean} Whether the code was successfully analyzed.
|
||||
*/
|
||||
analyze() {
|
||||
this.main.model.execution.status("analyzing");
|
||||
|
||||
// var feedback = this.main.components.feedback;
|
||||
|
||||
// Get the code
|
||||
var code = this.main.model.programs['__main__']();
|
||||
if (code.trim() == "") {
|
||||
this.main.components.feedback.emptyProgram("You haven't written any code yet!");
|
||||
//this.main.model.feedback.status("semantic");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given python code, resetting the console and Trace Table.
|
||||
* 分步调试代码
|
||||
*/
|
||||
steprun(code) {
|
||||
// Reset everything
|
||||
this.reset();
|
||||
if (code.indexOf('import blocklygame') !== -1
|
||||
|| code.indexOf('from blocklygame import') !== -1) {
|
||||
PyGameZero.reset();
|
||||
$(Sk.TurtleGraphics.target).empty();
|
||||
}
|
||||
//如果是第五关、第七关,则需要把检查循环次数的代码加进去
|
||||
if (code.indexOf("settedMap(4") != -1 | code.indexOf("settedMap(6") != -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isCirculationRight()\n"
|
||||
}
|
||||
}
|
||||
|
||||
//除了第六关,其他把检查是否成功代码加进去
|
||||
if (code.indexOf("settedMap(5)") == -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isSuccess()\n"
|
||||
}
|
||||
}
|
||||
this.programStatus['running'] = true;
|
||||
Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("<stdin>", false, code, true))
|
||||
.then(() => {
|
||||
// window.SPRITE.running = false;
|
||||
this.programStatus['running'] = false;
|
||||
this.#events_.run('finished');
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
// window.SPRITE.running = false;
|
||||
this.programStatus['running'] = false;
|
||||
this.#events_.run('error', error);
|
||||
var original = prettyPrintError(error);
|
||||
this.#events_.run('finished');
|
||||
//hack for kill program with time limiterror
|
||||
if (original.indexOf("TimeLimitError") !== -1) {
|
||||
return;
|
||||
}
|
||||
this.executionEnd_();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given python code, resetting the console and Trace Table.
|
||||
*/
|
||||
run(code) {
|
||||
// Reset everything
|
||||
this.reset();
|
||||
if (code.indexOf('import blocklygame') !== -1
|
||||
|| code.indexOf('from blocklygame import') !== -1) {
|
||||
PyGameZero.reset();
|
||||
$(Sk.TurtleGraphics.target).empty();
|
||||
}
|
||||
if ((code.indexOf("import blocktool") !== -1) || (code.indexOf("import blocklygame") !== -1) || (code.indexOf("from blocklygame import") !== -1)) {
|
||||
//正则匹配替换block id元素
|
||||
var code_piece = [];
|
||||
code_piece = code.split("\n");
|
||||
for (var i = 0; i < code_piece.length; i++) {
|
||||
if (code_piece[i].indexOf("block_id") >= 0) {
|
||||
var target = "";
|
||||
var re = /,?'block_id=[\s\S]*'/.exec(code_piece[i]);
|
||||
if (re != null) {
|
||||
target = re[0];
|
||||
code_piece[i] = code_piece[i].replace(target, "");
|
||||
}
|
||||
}
|
||||
//检查是否是高亮辅助块\toll,如果是,则将此行代码移除
|
||||
if ((code_piece[i].indexOf("import blocktool") >= 0) || (code_piece[i].indexOf("blocktool.highlight") >= 0)) {
|
||||
code_piece[i] = "delete";
|
||||
}
|
||||
//如果使用的是分步调试的blocklygame,要将其换成非分步调试的模块bg_nonehl
|
||||
if ((code_piece[i].indexOf("blocklygame") >= 0)) {
|
||||
code_piece[i] = code_piece[i].replace("blocklygame", "bg_nonehl");
|
||||
}
|
||||
}
|
||||
code = ""
|
||||
for (var i = 0; i < code_piece.length; i++) {
|
||||
if (code_piece[i] != "delete") {
|
||||
code += code_piece[i] + '\n'
|
||||
}
|
||||
}
|
||||
}
|
||||
//如果是第五关、第七关,则需要把检查循环次数的代码加进去
|
||||
if (code.indexOf("settedMap(4") != -1 | code.indexOf("settedMap(6") != -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isCirculationRight();\n"
|
||||
}
|
||||
}
|
||||
|
||||
//除了第六关,其他把检查是否成功代码加进去
|
||||
if (code.indexOf("settedMap(5)") == -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isSuccess();\n"
|
||||
}
|
||||
}
|
||||
this.programStatus['running'] = true;
|
||||
Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("<stdin>", false, code, true))
|
||||
.then(() => {
|
||||
// window.SPRITE.running = false;
|
||||
this.programStatus['running'] = false;
|
||||
this.#events_.run('finished');
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
// window.SPRITE.running = false;
|
||||
this.programStatus['running'] = false;
|
||||
this.#events_.run('error', error);
|
||||
var original = prettyPrintError(error);
|
||||
this.#events_.run('finished');
|
||||
//hack for kill program with time limiterror
|
||||
if (original.indexOf("TimeLimitError") !== -1) {
|
||||
return;
|
||||
}
|
||||
this.executionEnd_();
|
||||
});
|
||||
}
|
||||
|
||||
setupEnvironment(student_code, traceTable, output, ast, final_values) {
|
||||
var model = this.main.model;
|
||||
this._backup_execution = Sk.afterSingleExecution;
|
||||
Sk.afterSingleExecution = undefined;
|
||||
Sk.builtins.get_output = new Sk.builtin.func(function () {
|
||||
Sk.builtin.pyCheckArgs("get_output", arguments, 0, 0);
|
||||
return Sk.ffi.remapToPy(model.execution.output());
|
||||
});
|
||||
Sk.builtins.reset_output = new Sk.builtin.func(function () {
|
||||
Sk.builtin.pyCheckArgs("reset_output", arguments, 0, 0);
|
||||
model.execution.output.removeAll();
|
||||
});
|
||||
Sk.builtins.log = new Sk.builtin.func(function (data) {
|
||||
Sk.builtin.pyCheckArgs("log", arguments, 1, 1);
|
||||
console.log(data);
|
||||
});
|
||||
//Sk.builtins.trace = Sk.ffi.remapToPy(traceTable);
|
||||
Sk.builtins._trace = traceTable;
|
||||
Sk.builtins._final_values = final_values;
|
||||
Sk.builtins.code = Sk.ffi.remapToPy(student_code);
|
||||
Sk.builtins.set_success = this.instructor_module.set_success;
|
||||
Sk.builtins.set_feedback = this.instructor_module.set_feedback;
|
||||
Sk.builtins.set_finished = this.instructor_module.set_finished;
|
||||
Sk.builtins.count_components = this.instructor_module.count_components;
|
||||
Sk.builtins.no_nonlist_nums = this.instructor_module.no_nonlist_nums;
|
||||
Sk.builtins.only_printing_properties = this.instructor_module.only_printing_properties;
|
||||
Sk.builtins.calls_function = this.instructor_module.calls_function;
|
||||
Sk.builtins.get_property = this.instructor_module.get_property;
|
||||
Sk.builtins.get_value_by_name = this.instructor_module.get_value_by_name;
|
||||
Sk.builtins.get_value_by_type = this.instructor_module.get_value_by_type;
|
||||
Sk.builtins.parse_json = this.instructor_module.parse_json;
|
||||
Sk.skip_drawing = true;
|
||||
model.settings.mute_printer(true);
|
||||
}
|
||||
|
||||
disposeEnvironment() {
|
||||
Sk.afterSingleExecution = this._backup_execution;
|
||||
Sk.builtins.get_output = undefined;
|
||||
Sk.builtins.reset_output = undefined;
|
||||
Sk.builtins.log = undefined;
|
||||
Sk.builtins._trace = undefined;
|
||||
Sk.builtins.trace = undefined;
|
||||
Sk.builtins.code = undefined;
|
||||
Sk.builtins.set_success = undefined;
|
||||
Sk.builtins.set_feedback = undefined;
|
||||
Sk.builtins.set_finished = undefined;
|
||||
Sk.builtins.count_components = undefined;
|
||||
Sk.builtins.calls_function = undefined;
|
||||
Sk.builtins.get_property = undefined;
|
||||
Sk.builtins.get_value_by_name = undefined;
|
||||
Sk.builtins.get_value_by_type = undefined;
|
||||
Sk.builtins.no_nonlist_nums = undefined;
|
||||
Sk.builtins.only_printing_properties = undefined;
|
||||
Sk.builtins.parse_json = undefined;
|
||||
Sk.skip_drawing = false;
|
||||
GLOBAL_VALUE = undefined;
|
||||
this.main.model.settings.mute_printer(false);
|
||||
}
|
||||
|
||||
parseGlobals(variables) {
|
||||
var result = Array();
|
||||
var modules = Array();
|
||||
for (var property in variables) {
|
||||
var value = variables[property];
|
||||
if (property !== "__name__" && property !== "__doc__") {
|
||||
property = property.replace('_$rw$', '')
|
||||
.replace('_$rn$', '');
|
||||
var parsed = this.parseValue(property, value);
|
||||
if (parsed !== null) {
|
||||
result.push(parsed);
|
||||
} else if (value.constructor == Sk.builtin.module) {
|
||||
modules.push(value.$d.__name__.v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { "properties": result, "modules": modules };
|
||||
}
|
||||
|
||||
parseValue(property, value) {
|
||||
if (value == undefined) {
|
||||
return {
|
||||
'name': property,
|
||||
'type': 'Unknown',
|
||||
"value": 'Undefined'
|
||||
};
|
||||
}
|
||||
switch (value.constructor) {
|
||||
case Sk.builtin.func:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Function",
|
||||
"value": (value.func_code.co_varnames !== undefined ?
|
||||
" Arguments: " + value.func_code.co_varnames.join(", ") :
|
||||
' No arguments')
|
||||
};
|
||||
case Sk.builtin.module: return null;
|
||||
case Sk.builtin.str:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "String",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Sk.builtin.none:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "None",
|
||||
"value": "None"
|
||||
};
|
||||
case Sk.builtin.bool:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Boolean",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Sk.builtin.nmber:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "int" == value.skType ? "Integer" : "Float",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Sk.builtin.int_:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Integer",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Sk.builtin.float_:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Float",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Sk.builtin.tuple:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Tuple",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Sk.builtin.list:
|
||||
if (value.v.length <= 20) {
|
||||
return {
|
||||
'name': property,
|
||||
'type': "List",
|
||||
"value": value.$r().v,
|
||||
'exact_value': value
|
||||
};
|
||||
}
|
||||
return {
|
||||
'name': property,
|
||||
'type': "List",
|
||||
"value": "[... " + value.v.length + " elements ...]",
|
||||
"exact_value": value
|
||||
};
|
||||
|
||||
case Sk.builtin.dict:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Dictionary",
|
||||
"value": value.$r().v
|
||||
};
|
||||
case Number:
|
||||
return {
|
||||
'name': property,
|
||||
'type': value % 1 === 0 ? "Integer" : "Float",
|
||||
"value": value
|
||||
};
|
||||
case String:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "String",
|
||||
"value": value
|
||||
};
|
||||
case Boolean:
|
||||
return {
|
||||
'name': property,
|
||||
'type': "Boolean",
|
||||
"value": (value ? "True" : "False")
|
||||
};
|
||||
default:
|
||||
return {
|
||||
'name': property,
|
||||
'type': value.tp$name == undefined ? value : value.tp$name,
|
||||
"value": value.$r == undefined ? value : value.$r().v
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skulpt Module for holding the Instructor API.
|
||||
*
|
||||
* This module is a little hackish. We need to sit down and reevaluate the best way to
|
||||
* organize it and whether this particular structure is ideal. I suspect it should be
|
||||
* it's own proper JS file.
|
||||
*
|
||||
* @param {String} name - The name of the module (should always be 'instructor')
|
||||
*
|
||||
*/
|
||||
// var instructor_module = function (name) {
|
||||
// // Main module object that gets returned at the end.
|
||||
// var mod = {};
|
||||
|
||||
// /**
|
||||
// * Skulpt Exception that represents a Feedback object, to be rendered to the user
|
||||
// * when the feedback system finds a problem.
|
||||
// *
|
||||
// * @param {Array} args - A list of optional arguments to pass to the Exception.
|
||||
// * Usually this will include a message for the user.
|
||||
// */
|
||||
// Sk.builtin.Feedback = function (args) {
|
||||
// var o;
|
||||
// if (!(this instanceof Sk.builtin.Feedback)) {
|
||||
// o = Object.create(Sk.builtin.Feedback.prototype);
|
||||
// o.constructor.apply(o, arguments);
|
||||
// return o;
|
||||
// }
|
||||
// Sk.builtin.Exception.apply(this, arguments);
|
||||
// };
|
||||
// Sk.abstr.setUpInheritance("Feedback", Sk.builtin.Feedback, Sk.builtin.Exception);
|
||||
|
||||
// /**
|
||||
// * Skulpt Exception that represents a Success object, to be thrown when the user
|
||||
// * completes their program successfully.
|
||||
// *
|
||||
// ** @param {Array} args - A list of optional arguments to pass to the Exception.
|
||||
// * Usually this will be empty.
|
||||
// */
|
||||
// Sk.builtin.Success = function (args) {
|
||||
// var o;
|
||||
// if (!(this instanceof Sk.builtin.Success)) {
|
||||
// o = Object.create(Sk.builtin.Success.prototype);
|
||||
// o.constructor.apply(o, arguments);
|
||||
// return o;
|
||||
// }
|
||||
// Sk.builtin.Exception.apply(this, arguments);
|
||||
// };
|
||||
// Sk.abstr.setUpInheritance("Success", Sk.builtin.Success, Sk.builtin.Exception);
|
||||
|
||||
// /**
|
||||
// * Skulpt Exception that represents a Finished object, to be thrown when the user
|
||||
// * completes their program successfully, but isn't in a problem with a "solution".
|
||||
// * This is useful for open-ended canvases where we still want to capture the students'
|
||||
// * code in Canvas.
|
||||
// *
|
||||
// ** @param {Array} args - A list of optional arguments to pass to the Exception.
|
||||
// * Usually this will be empty.
|
||||
// */
|
||||
// Sk.builtin.Finished = function (args) {
|
||||
// var o;
|
||||
// if (!(this instanceof Sk.builtin.Finished)) {
|
||||
// o = Object.create(Sk.builtin.Finished.prototype);
|
||||
// o.constructor.apply(o, arguments);
|
||||
// return o;
|
||||
// }
|
||||
// Sk.builtin.Exception.apply(this, arguments);
|
||||
// };
|
||||
// Sk.abstr.setUpInheritance("Finished", Sk.builtin.Finished, Sk.builtin.Exception);
|
||||
|
||||
// /**
|
||||
// * A Skulpt function that throws a Feedback exception, allowing us to give feedback
|
||||
// * to the user through the Feedback panel. This function call is done for aesthetic
|
||||
// * reasons, so that we are calling a function instead of raising an error. Still,
|
||||
// * exceptions allow us to break out of the control flow immediately, like a
|
||||
// * return, so they are a good mechanism to use under the hood.
|
||||
// *
|
||||
// * @param {String} message - The message to display to the user.
|
||||
// */
|
||||
// mod.set_feedback = new Sk.builtin.func(function (message) {
|
||||
// Sk.builtin.pyCheckArgs("set_feedback", arguments, 1, 1);
|
||||
// Sk.builtin.pyCheckType("message", "string", Sk.builtin.checkString(message));
|
||||
// throw new Sk.builtin.Feedback(message.v);
|
||||
// });
|
||||
|
||||
// /**
|
||||
// * A Skulpt function that throws a Success exception. This will terminate the
|
||||
// * feedback analysis, but reports that the students' code was successful.
|
||||
// * This function call is done for aesthetic reasons, so that we are calling a
|
||||
// * function instead of raising an error. Still, exceptions allow us to break
|
||||
// * out of the control flow immediately, like a return would, so they are a
|
||||
// * good mechanism to use under the hood.
|
||||
// */
|
||||
// mod.set_success = new Sk.builtin.func(function () {
|
||||
// Sk.builtin.pyCheckArgs("set_success", arguments, 0, 0);
|
||||
// throw new Sk.builtin.Success();
|
||||
// });
|
||||
|
||||
// /**
|
||||
// * A Skulpt function that throws a Finished exception. This will terminate the
|
||||
// * feedback analysis, but reports that the students' code was successful.
|
||||
// * This function call is done for aesthetic reasons, so that we are calling a
|
||||
// * function instead of raising an error. Still, exceptions allow us to break
|
||||
// * out of the control flow immediately, like a return would, so they are a
|
||||
// * good mechanism to use under the hood.
|
||||
// */
|
||||
// mod.set_finished = new Sk.builtin.func(function () {
|
||||
// Sk.builtin.pyCheckArgs("set_finished", arguments, 0, 0);
|
||||
// throw new Sk.builtin.Finished();
|
||||
// });
|
||||
|
||||
// // Memoization of previous parses - some mild redundancy to save time
|
||||
// // TODO: There's no evidence this is good, and could be a memory hog on big
|
||||
// // programs. Someone should investigate this. The assumption is that multiple
|
||||
// // helper functions might be using parses. But shouldn't we trim old parses?
|
||||
// // Perhaps a timed cache would work better.
|
||||
// var parses = {};
|
||||
|
||||
// /**
|
||||
// * Given source code as a string, return a flat list of all of the AST elements
|
||||
// * in the parsed source code.
|
||||
// *
|
||||
// * TODO: There's redundancy here, since the source code was previously parsed
|
||||
// * to run the file and to execute it. We should probably be able to do this just
|
||||
// * once and shave off time.
|
||||
// *
|
||||
// * @param {String} source - Python source code.
|
||||
// * @returns {Array.<Object>}
|
||||
// */
|
||||
// function getParseList(source) {
|
||||
// if (!(source in parses)) {
|
||||
// var parse = Sk.parse("__main__", source);
|
||||
// parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
|
||||
// }
|
||||
// var ast = parses[source];
|
||||
// return (new NodeVisitor()).recursive_walk(ast);
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Given source code as a string, return a list of all of the AST elements
|
||||
// * that are Num (aka numeric literals) but that are not inside List elements.
|
||||
// *
|
||||
// * @param {String} source - Python source code.
|
||||
// * @returns {Array.number} The list of JavaScript numeric literals that were found.
|
||||
// */
|
||||
// function getNonListNums(source) {
|
||||
// if (!(source in parses)) {
|
||||
// var parse = Sk.parse("__main__", source);
|
||||
// parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
|
||||
// }
|
||||
// var ast = parses[source];
|
||||
// var visitor = new NodeVisitor();
|
||||
// var insideList = false;
|
||||
// var nums = [];
|
||||
// visitor.visit_List = function (node) {
|
||||
// insideList = true;
|
||||
// this.generic_visit(node);
|
||||
// insideList = false;
|
||||
// }
|
||||
// visitor.visit_Num = function (node) {
|
||||
// if (!insideList) {
|
||||
// nums.push(node.n);
|
||||
// }
|
||||
// this.generic_visit(node);
|
||||
// }
|
||||
// visitor.visit(ast);
|
||||
// return nums;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Given source code as a string, return a list of all of the AST elements
|
||||
// * that are being printed (using the print function) but are not variables.
|
||||
// *
|
||||
// * @param {String} source - Python source code.
|
||||
// * @returns {Array.<Object>} The list of AST elements that were found.
|
||||
// */
|
||||
// function getPrintedNonProperties(source) {
|
||||
// if (!(source in parses)) {
|
||||
// var parse = Sk.parse("__main__", source);
|
||||
// parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
|
||||
// }
|
||||
// var ast = parses[source];
|
||||
// var visitor = new NodeVisitor();
|
||||
// var nonVariables = [];
|
||||
// visitor.visit_Call = function (node) {
|
||||
// var func = node.func;
|
||||
// var args = node.args;
|
||||
// if (func._astname == 'Name' && func.id.v == 'print') {
|
||||
// for (var i = 0; i < args.length; i += 1) {
|
||||
// if (args[i]._astname != "Name") {
|
||||
// nonVariables.push(args[i]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// this.generic_visit(node);
|
||||
// }
|
||||
// visitor.visit(ast);
|
||||
// return nonVariables;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Skulpt function to iterate through the final state of
|
||||
// * all the variables in the program, and check to see if they have
|
||||
// * a given value.
|
||||
// */
|
||||
// mod.get_value_by_name = new Sk.builtin.func(function (name) {
|
||||
// Sk.builtin.pyCheckArgs("get_value_by_name", arguments, 1, 1);
|
||||
// Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
|
||||
// name = name.v;
|
||||
// var final_values = Sk.builtins._final_values;
|
||||
// if (name in final_values) {
|
||||
// return final_values[name];
|
||||
// }
|
||||
// return Sk.builtin.none.none$;
|
||||
|
||||
// });
|
||||
// mod.get_value_by_type = new Sk.builtin.func(function (type) {
|
||||
// Sk.builtin.pyCheckArgs("get_value_by_type", arguments, 1, 1);
|
||||
|
||||
// var final_values = Sk.builtins._final_values;
|
||||
// var result = [];
|
||||
// for (var property in final_values) {
|
||||
// if (final_values[property].tp$name == type.tp$name) {
|
||||
// result.push(final_values[property]);
|
||||
// }
|
||||
// }
|
||||
// return Sk.builtin.list(result);
|
||||
// });
|
||||
|
||||
// mod.parse_json = new Sk.builtin.func(function (blob) {
|
||||
// Sk.builtin.pyCheckArgs("parse_json", arguments, 1, 1);
|
||||
// Sk.builtin.pyCheckType("blob", "string", Sk.builtin.checkString(blob));
|
||||
// blob = blob.v;
|
||||
// return Sk.ffi.remapToPy(JSON.parse(blob));
|
||||
// });
|
||||
// mod.get_property = new Sk.builtin.func(function (name) {
|
||||
// Sk.builtin.pyCheckArgs("get_property", arguments, 1, 1);
|
||||
// Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
|
||||
// name = name.v;
|
||||
// var trace = Sk.builtins._trace;
|
||||
// if (trace.length <= 0) {
|
||||
// return Sk.builtin.none.none$;
|
||||
// }
|
||||
// var properties = trace[trace.length - 1]["properties"];
|
||||
// for (var i = 0, len = properties.length; i < len; i += 1) {
|
||||
// if (properties[i]['name'] == name) {
|
||||
// return Sk.ffi.remapToPy(properties[i])
|
||||
// }
|
||||
// }
|
||||
// return Sk.builtin.none.none$;
|
||||
// });
|
||||
|
||||
// mod.calls_function = new Sk.builtin.func(function (source, name) {
|
||||
// Sk.builtin.pyCheckArgs("calls_function", arguments, 2, 2);
|
||||
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||
// Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
|
||||
|
||||
// source = source.v;
|
||||
// name = name.v;
|
||||
|
||||
// var ast_list = getParseList(source);
|
||||
|
||||
// var count = 0;
|
||||
// for (var i = 0, len = ast_list.length; i < len; i = i + 1) {
|
||||
// if (ast_list[i]._astname == 'Call') {
|
||||
// if (ast_list[i].func._astname == 'Attribute') {
|
||||
// count += Sk.ffi.remapToJs(ast_list[i].func.attr) == name | 0;
|
||||
// } else if (ast_list[i].func._astname == 'Name') {
|
||||
// count += Sk.ffi.remapToJs(ast_list[i].func.id) == name | 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return Sk.ffi.remapToPy(count > 0);
|
||||
// });
|
||||
|
||||
// mod.count_components = new Sk.builtin.func(function (source, component) {
|
||||
// Sk.builtin.pyCheckArgs("count_components", arguments, 2, 2);
|
||||
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||
// Sk.builtin.pyCheckType("component", "string", Sk.builtin.checkString(component));
|
||||
|
||||
// source = source.v;
|
||||
// component = component.v;
|
||||
|
||||
// var ast_list = getParseList(source);
|
||||
|
||||
// var count = 0;
|
||||
// for (var i = 0, len = ast_list.length; i < len; i = i + 1) {
|
||||
// if (ast_list[i]._astname == component) {
|
||||
// count = count + 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return Sk.ffi.remapToPy(count);
|
||||
// });
|
||||
|
||||
// mod.no_nonlist_nums = new Sk.builtin.func(function (source) {
|
||||
// Sk.builtin.pyCheckArgs("no_nonlist_nums", arguments, 1, 1);
|
||||
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||
|
||||
// source = source.v;
|
||||
|
||||
// var num_list = getNonListNums(source);
|
||||
|
||||
// var count = 0;
|
||||
// for (var i = 0, len = num_list.length; i < len; i = i + 1) {
|
||||
// if (num_list[i].v != 0 && num_list[i].v != 1) {
|
||||
// return Sk.ffi.remapToPy(true);
|
||||
// }
|
||||
// }
|
||||
// return Sk.ffi.remapToPy(false);
|
||||
// });
|
||||
// mod.only_printing_properties = new Sk.builtin.func(function (source) {
|
||||
// Sk.builtin.pyCheckArgs("only_printing_properties", arguments, 1, 1);
|
||||
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||
|
||||
// source = source.v;
|
||||
|
||||
// var non_var_list = getPrintedNonProperties(source);
|
||||
// return Sk.ffi.remapToPy(non_var_list.length == 0);
|
||||
// });
|
||||
|
||||
// return mod;
|
||||
// }
|
||||
@@ -0,0 +1,208 @@
|
||||
import MixpyProject from './mixpy-project';
|
||||
import PyEngine from './py-engine';
|
||||
import { Workspace, Msg } from 'mixly';
|
||||
import StatusBarImage from './statusbar-image';
|
||||
|
||||
class PythonShell {
|
||||
static {
|
||||
this.pythonShell = null;
|
||||
|
||||
this.init = async function () {
|
||||
StatusBarImage.init();
|
||||
this.pythonShell = new PythonShell();
|
||||
}
|
||||
|
||||
this.steprun = function () {
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
return this.pythonShell.steprun(code);
|
||||
}
|
||||
|
||||
this.run = function () {
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
return this.pythonShell.run(code);
|
||||
}
|
||||
|
||||
this.stop = function () {
|
||||
return this.pythonShell.stop();
|
||||
}
|
||||
}
|
||||
|
||||
#statusBarTerminal_ = null;
|
||||
#statusBarImage_ = null;
|
||||
#statusBarsManager_ = null;
|
||||
#cursor_ = {
|
||||
row: 0,
|
||||
column: 0
|
||||
};
|
||||
#prompt_ = '';
|
||||
#inputResolve_ = null;
|
||||
#inputReject_ = null;
|
||||
#waittingForInput_ = false;
|
||||
#running_ = false;
|
||||
#pyEngine_ = null;
|
||||
#onCursorChangeEvent_ = () => this.#onCursorChange_();
|
||||
#commands_ = [
|
||||
{
|
||||
name: 'REPL-Enter',
|
||||
bindKey: 'Enter',
|
||||
exec: (editor) => {
|
||||
const session = editor.getSession();
|
||||
const cursor = session.selection.getCursor();
|
||||
if (cursor.row === this.#cursor_.row) {
|
||||
const newPos = this.#statusBarTerminal_.getEndPos();
|
||||
let str = this.#statusBarTerminal_.getValueRange(this.#cursor_, newPos);
|
||||
str = str.replace(this.#prompt_, '');
|
||||
this.#inputResolve_?.(str);
|
||||
this.#inputResolve_ = null;
|
||||
this.#inputReject_ = null;
|
||||
this.#statusBarTerminal_.addValue('\n');
|
||||
this.#exitInput_();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, {
|
||||
name: 'REPL-ChangeEditor',
|
||||
bindKey: 'Delete|Ctrl-X|Backspace',
|
||||
exec: (editor) => {
|
||||
const session = editor.getSession();
|
||||
const cursor = session.selection.getCursor();
|
||||
if (cursor.row < this.#cursor_.row || cursor.column <= this.#cursor_.column) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
];
|
||||
constructor() {
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
this.#statusBarsManager_ = mainWorkspace.getStatusBarsManager();
|
||||
this.#statusBarTerminal_ = this.#statusBarsManager_.getStatusBarById('output');
|
||||
this.#statusBarImage_ = this.#statusBarsManager_.getStatusBarById('images');
|
||||
this.#pyEngine_ = new PyEngine({}, new MixpyProject());
|
||||
this.#pyEngine_.loadEngine(this.#statusBarImage_.getContent().children()[0]);
|
||||
this.#addEventsListener_();
|
||||
}
|
||||
|
||||
#addEventsListener_() {
|
||||
const events = this.#pyEngine_.getEvents();
|
||||
events.bind('finished', () => {
|
||||
this.#running_ = false;
|
||||
this.#statusBarTerminal_.addValue(`\n==${Msg.Lang['shell.finish']}==`);
|
||||
});
|
||||
|
||||
events.bind('output', (data) => {
|
||||
this.#statusBarTerminal_.addValue(data.content);
|
||||
});
|
||||
|
||||
events.bind('error', (data) => {
|
||||
this.#running_ = false;
|
||||
this.#statusBarTerminal_.addValue(`\n${data.toString()}\n`);
|
||||
});
|
||||
|
||||
events.bind('input', (data) => {
|
||||
const prompt = String(data?.content?.prompt);
|
||||
this.#statusBarTerminal_.addValue(`>>> ${prompt}`);
|
||||
this.#prompt_ = prompt;
|
||||
this.#inputResolve_ = data.resolve;
|
||||
this.#inputReject_ = data.reject;
|
||||
this.#enterInput_();
|
||||
});
|
||||
|
||||
events.bind('display', (data) => {
|
||||
this.#statusBarsManager_.changeTo('images');
|
||||
this.#statusBarImage_.display(data);
|
||||
});
|
||||
}
|
||||
|
||||
#onCursorChange_() {
|
||||
const editor = this.#statusBarTerminal_.getEditor();
|
||||
const session = editor.getSession();
|
||||
const cursor = session.selection.getCursor();
|
||||
editor.setReadOnly(
|
||||
cursor.row < this.#cursor_.row || cursor.column < this.#cursor_.column
|
||||
);
|
||||
}
|
||||
|
||||
#enterInput_() {
|
||||
if (!this.#running_) {
|
||||
return;
|
||||
}
|
||||
this.#waittingForInput_ = true;
|
||||
this.#cursor_ = this.#statusBarTerminal_.getEndPos();
|
||||
const editor = this.#statusBarTerminal_.getEditor();
|
||||
editor.setReadOnly(false);
|
||||
editor.focus();
|
||||
const session = editor.getSession();
|
||||
session.selection.on('changeCursor', this.#onCursorChangeEvent_);
|
||||
editor.commands.addCommands(this.#commands_);
|
||||
}
|
||||
|
||||
#exitInput_() {
|
||||
this.#waittingForInput_ = false;
|
||||
const editor = this.#statusBarTerminal_.getEditor();
|
||||
const session = editor.getSession();
|
||||
session.selection.off('changeCursor', this.#onCursorChangeEvent_);
|
||||
editor.commands.removeCommands(this.#commands_);
|
||||
this.#prompt_ = '';
|
||||
this.#inputResolve_?.('');
|
||||
// this.#inputReject_?.({});
|
||||
this.cursor_ = { row: 0, column: 0 };
|
||||
editor.setReadOnly(true);
|
||||
}
|
||||
|
||||
addPrompt(prompt, resolve, reject) {
|
||||
this.#statusBarTerminal_.addValue(prompt);
|
||||
this.#prompt_ = prompt;
|
||||
this.#inputResolve_ = resolve;
|
||||
this.#inputReject_ = reject;
|
||||
this.#enterInput_();
|
||||
}
|
||||
|
||||
async steprun(code) {
|
||||
await this.stop();
|
||||
this.#statusBarsManager_.changeTo('output');
|
||||
this.#statusBarsManager_.show();
|
||||
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
|
||||
this.#running_ = true;
|
||||
if (code.indexOf('import blocklygame') !== -1
|
||||
|| code.indexOf('from blocklygame import') !== -1) {
|
||||
this.#statusBarsManager_.changeTo('images');
|
||||
}
|
||||
this.#pyEngine_.steprun(code);
|
||||
}
|
||||
|
||||
async run(code) {
|
||||
await this.stop();
|
||||
this.#statusBarsManager_.changeTo('output');
|
||||
this.#statusBarsManager_.show();
|
||||
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
|
||||
this.#running_ = true;
|
||||
if (code.indexOf('import blocklygame') !== -1
|
||||
|| code.indexOf('from blocklygame import') !== -1) {
|
||||
this.#statusBarsManager_.changeTo('images');
|
||||
}
|
||||
this.#pyEngine_.run(code);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
if (this.#waittingForInput_) {
|
||||
this.#exitInput_();
|
||||
}
|
||||
if (this.#running_) {
|
||||
this.#pyEngine_.kill();
|
||||
await this.sleep(500);
|
||||
this.#running_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
||||
export default PythonShell;
|
||||
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Debugger support for skulpt module
|
||||
*/
|
||||
import './skulpt.min.js';
|
||||
|
||||
function hasOwnProperty(obj, prop) {
|
||||
var proto = obj.constructor.prototype;
|
||||
return (prop in obj) &&
|
||||
(!(prop in proto) || proto[prop] !== obj[prop]);
|
||||
}
|
||||
|
||||
class Breakpoint {
|
||||
constructor(filename, lineno, colno) {
|
||||
this.filename = filename;
|
||||
this.lineno = lineno;
|
||||
this.colno = colno;
|
||||
this.enabled = true;
|
||||
this.ignore_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Debugger {
|
||||
constructor(filename, output_callback) {
|
||||
this.dbg_breakpoints = {};
|
||||
this.tmp_breakpoints = {};
|
||||
this.suspension_stack = [];
|
||||
this.current_suspension = -1;
|
||||
this.eval_callback = null;
|
||||
this.suspension = null;
|
||||
this.output_callback = output_callback;
|
||||
this.step_mode = false;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
print(txt) {
|
||||
if (this.output_callback != null) {
|
||||
this.output_callback.print(txt);
|
||||
}
|
||||
}
|
||||
|
||||
get_source_line(lineno) {
|
||||
if (this.output_callback != null) {
|
||||
return this.output_callback.get_source_line(lineno);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
move_up_the_stack() {
|
||||
this.current_suspension = Math.min(this.current_suspension + 1, this.suspension_stack.length - 1);
|
||||
}
|
||||
|
||||
move_down_the_stack() {
|
||||
this.current_suspension = Math.max(this.current_suspension - 1, 0);
|
||||
}
|
||||
|
||||
enable_step_mode() {
|
||||
this.step_mode = true;
|
||||
}
|
||||
|
||||
disable_step_mode() {
|
||||
this.step_mode = false;
|
||||
}
|
||||
|
||||
get_suspension_stack() {
|
||||
return this.suspension_stack;
|
||||
}
|
||||
|
||||
get_active_suspension() {
|
||||
if (this.suspension_stack.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.suspension_stack[this.current_suspension];
|
||||
}
|
||||
|
||||
generate_breakpoint_key(filename, lineno) {
|
||||
var key = filename + "-" + lineno;
|
||||
return key;
|
||||
}
|
||||
|
||||
check_breakpoints(filename, lineno, colno) {
|
||||
// If Step mode is enabled then ignore breakpoints since we will just break
|
||||
// at every line.
|
||||
if (this.step_mode === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
if (hasOwnProperty(this.dbg_breakpoints, key) &&
|
||||
this.dbg_breakpoints[key].enabled === true) {
|
||||
var bp = null;
|
||||
if (hasOwnProperty(this.tmp_breakpoints, key)) {
|
||||
delete this.dbg_breakpoints[key];
|
||||
delete this.tmp_breakpoints[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
this.dbg_breakpoints[key].ignore_count -= 1;
|
||||
this.dbg_breakpoints[key].ignore_count = Math.max(0, this.dbg_breakpoints[key].ignore_count);
|
||||
|
||||
bp = this.dbg_breakpoints[key];
|
||||
if (bp.ignore_count === 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get_breakpoints_list() {
|
||||
return this.dbg_breakpoints;
|
||||
}
|
||||
|
||||
disable_breakpoint(filename, lineno, colno) {
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
|
||||
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||
this.dbg_breakpoints[key].enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
enable_breakpoint(filename, lineno, colno) {
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
|
||||
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||
this.dbg_breakpoints[key].enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
clear_breakpoint(filename, lineno, colno) {
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||
delete this.dbg_breakpoints[key];
|
||||
return null;
|
||||
}
|
||||
return "Invalid breakpoint specified: " + filename + " line: " + lineno;
|
||||
}
|
||||
|
||||
clear_all_breakpoints() {
|
||||
this.dbg_breakpoints = {};
|
||||
this.tmp_breakpoints = {};
|
||||
}
|
||||
|
||||
set_ignore_count(filename, lineno, colno, count) {
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||
var bp = this.dbg_breakpoints[key];
|
||||
bp.ignore_count = count;
|
||||
}
|
||||
}
|
||||
|
||||
set_condition(filename, lineno, colno, lhs, cond, rhs) {
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
var bp;
|
||||
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||
// Set a new condition
|
||||
bp = this.dbg_breakpoints[key];
|
||||
} else {
|
||||
bp = new Breakpoint(filename, lineno, colno);
|
||||
}
|
||||
|
||||
bp.condition = new window.Sk.Condition(lhs, cond, rhs);
|
||||
this.dbg_breakpoints[key] = bp;
|
||||
}
|
||||
|
||||
print_suspension_info(suspension) {
|
||||
var filename = suspension.filename;
|
||||
var lineno = suspension.lineno;
|
||||
var colno = suspension.colno;
|
||||
this.print("Hit Breakpoint at <" + filename + "> at line: " + lineno + " column: " + colno + "\n");
|
||||
this.print("----------------------------------------------------------------------------------\n");
|
||||
this.print(" ==> " + this.get_source_line(lineno - 1) + "\n");
|
||||
this.print("----------------------------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
set_suspension(suspension) {
|
||||
var parent = null;
|
||||
if (!hasOwnProperty(suspension, "filename") && suspension.child instanceof window.Sk.misceval.Suspension) {
|
||||
suspension = suspension.child;
|
||||
}
|
||||
|
||||
// Pop the last suspension of the stack if there is more than 0
|
||||
if (this.suspension_stack.length > 0) {
|
||||
this.suspension_stack.pop();
|
||||
this.current_suspension -= 1;
|
||||
}
|
||||
|
||||
// Unroll the stack to get each suspension.
|
||||
while (suspension instanceof window.Sk.misceval.Suspension) {
|
||||
parent = suspension;
|
||||
this.suspension_stack.push(parent);
|
||||
this.current_suspension += 1;
|
||||
suspension = suspension.child;
|
||||
}
|
||||
|
||||
suspension = parent;
|
||||
|
||||
this.print_suspension_info(suspension);
|
||||
}
|
||||
|
||||
add_breakpoint(filename, lineno, colno, temporary) {
|
||||
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||
this.dbg_breakpoints[key] = new Breakpoint(filename, lineno, colno);
|
||||
if (temporary) {
|
||||
this.tmp_breakpoints[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
suspension_handler(susp) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
resolve(susp.resume());
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resume() {
|
||||
// Reset the suspension stack to the topmost
|
||||
this.current_suspension = this.suspension_stack.length - 1;
|
||||
|
||||
if (this.suspension_stack.length === 0) {
|
||||
this.print("No running program");
|
||||
} else {
|
||||
var promise = this.suspension_handler(this.get_active_suspension());
|
||||
promise.then(this.success.bind(this), this.error.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
pop_suspension_stack() {
|
||||
this.suspension_stack.pop();
|
||||
this.current_suspension -= 1;
|
||||
}
|
||||
|
||||
success(r) {
|
||||
if (r instanceof window.Sk.misceval.Suspension) {
|
||||
this.set_suspension(r);
|
||||
} else {
|
||||
if (this.suspension_stack.length > 0) {
|
||||
// Current suspension needs to be popped of the stack
|
||||
this.pop_suspension_stack();
|
||||
|
||||
if (this.suspension_stack.length === 0) {
|
||||
this.print("Program execution complete");
|
||||
return;
|
||||
}
|
||||
|
||||
var parent_suspension = this.get_active_suspension();
|
||||
// The child has completed the execution. So override the child's resume
|
||||
// so we can continue the execution.
|
||||
parent_suspension.child.resume = function () {
|
||||
return r;
|
||||
};
|
||||
this.resume();
|
||||
} else {
|
||||
this.print("Program execution complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error(e) {
|
||||
this.print("Traceback (most recent call last):");
|
||||
for (var idx = 0; idx < e.traceback.length; ++idx) {
|
||||
this.print(" File \"" + e.traceback[idx].filename + "\", line " + e.traceback[idx].lineno + ", in <module>");
|
||||
var code = this.get_source_line(e.traceback[idx].lineno - 1);
|
||||
code = code.trim();
|
||||
code = " " + code;
|
||||
this.print(code);
|
||||
}
|
||||
|
||||
var err_ty = e.constructor.tp$name;
|
||||
for (idx = 0; idx < e.args.v.length; ++idx) {
|
||||
this.print(err_ty + ": " + e.args.v[idx].v);
|
||||
}
|
||||
}
|
||||
|
||||
asyncToPromise(suspendablefn, suspHandlers, debugger_obj) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
var r = suspendablefn();
|
||||
|
||||
(function handleResponse(r) {
|
||||
try {
|
||||
while (r instanceof window.Sk.misceval.Suspension) {
|
||||
debugger_obj.set_suspension(r);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(r);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
})(r);
|
||||
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
execute(suspendablefn) {
|
||||
var r = suspendablefn();
|
||||
|
||||
if (r instanceof window.Sk.misceval.Suspension) {
|
||||
this.suspensions.concat(r);
|
||||
this.eval_callback(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
var $builtinmodule = function (name) {
|
||||
let mod= {__name__: new Sk.builtin.str("blocktool")};
|
||||
|
||||
var highlight = function(id) {
|
||||
id=Sk.ffi.remapToJs(id)
|
||||
Mixly.Editor.blockEditor.highlightBlock(id);
|
||||
};
|
||||
|
||||
var highlight_f=function(block_id) {
|
||||
return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) {
|
||||
setTimeout( () => {
|
||||
highlight(block_id)
|
||||
resolve(Sk.builtin.none.none$);
|
||||
}, 800);
|
||||
}));
|
||||
}
|
||||
mod.highlight = new Sk.builtin.func(highlight_f);
|
||||
|
||||
return mod;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import './pygame-zero.min.js';
|
||||
|
||||
export default window.PyGameZero;
|
||||
15269
mixly/boards/default_src/python_skulpt_car/others/skulpt/pygame-zero.min.js
vendored
Normal file
15269
mixly/boards/default_src/python_skulpt_car/others/skulpt/pygame-zero.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
import './skulpt.min.js';
|
||||
import './skulpt-stdlib.js';
|
||||
|
||||
export default window.Sk;
|
||||
1058
mixly/boards/default_src/python_skulpt_car/others/skulpt/skulpt.min.js
vendored
Normal file
1058
mixly/boards/default_src/python_skulpt_car/others/skulpt/skulpt.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,173 @@
|
||||
import $ from 'jquery';
|
||||
import { Msg } from 'blockly/core';
|
||||
import {
|
||||
PageBase,
|
||||
HTMLTemplate,
|
||||
StatusBarsManager,
|
||||
Workspace
|
||||
} from 'mixly';
|
||||
import STATUS_BAR_IMAGE_TEMPLATE from '../templates/html/statusbar-image.html';
|
||||
|
||||
|
||||
class StatusBarImage extends PageBase {
|
||||
static {
|
||||
HTMLTemplate.add(
|
||||
'html/statusbar/statusbar-image.html',
|
||||
new HTMLTemplate(STATUS_BAR_IMAGE_TEMPLATE)
|
||||
);
|
||||
|
||||
this.init = function () {
|
||||
StatusBarsManager.typesRegistry.register(['images'], StatusBarImage);
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const statusBarsManager = mainWorkspace.getStatusBarsManager();
|
||||
statusBarsManager.add({
|
||||
type: 'images',
|
||||
id: 'images',
|
||||
name: Msg.MIXLY_MICROBIT_IMAGE,
|
||||
title: Msg.MIXLY_MICROBIT_IMAGE
|
||||
});
|
||||
statusBarsManager.changeTo('output');
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const $content = $(HTMLTemplate.get('html/statusbar/statusbar-image.html').render());
|
||||
this.setContent($content);
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init();
|
||||
this.hideCloseBtn();
|
||||
}
|
||||
|
||||
clean() {
|
||||
this.getContent().empty();
|
||||
}
|
||||
|
||||
display(data) {
|
||||
const $content = this.getContent();
|
||||
const autoFit = function (node) {
|
||||
node.style.width = 'auto';
|
||||
node.style.height = 'auto';
|
||||
node.style.maxWidth = '100%';
|
||||
node.style.maxHeight = '100%';
|
||||
};
|
||||
this.clean();
|
||||
let root = data.content;
|
||||
let canvas = null;
|
||||
let iframe = null;
|
||||
switch (data.display_type) {
|
||||
case 'p5':
|
||||
root.style.width = '100%';
|
||||
root.style.height = '100%';
|
||||
root.style.display = 'flex';
|
||||
root.style.justifyContent = 'center';
|
||||
root.style.alignItems = 'center';
|
||||
|
||||
// some canvas nodes can be added later so we observe...
|
||||
new MutationObserver(function (mutationsList) {
|
||||
mutationsList.forEach((mutation) =>
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
const elem = node;
|
||||
if (
|
||||
elem.tagName != null &&
|
||||
['canvas', 'video'].includes(elem.tagName.toLowerCase())
|
||||
)
|
||||
autoFit(elem);
|
||||
})
|
||||
);
|
||||
}).observe(root, { childList: true });
|
||||
|
||||
root.querySelectorAll('canvas,video').forEach(autoFit);
|
||||
$content.append(root);
|
||||
break;
|
||||
case 'matplotlib':
|
||||
canvas = root.querySelector('canvas');
|
||||
if (canvas) root = canvas;
|
||||
root.style.width = '';
|
||||
root.style.height = '';
|
||||
root.style.maxWidth = '100%';
|
||||
root.style.maxHeight = '100%';
|
||||
$content.append(root);
|
||||
break;
|
||||
case 'ocaml-canvas':
|
||||
root.style.width = '';
|
||||
root.style.height = '';
|
||||
root.style.maxWidth = '100%';
|
||||
root.style.maxHeight = '100%';
|
||||
$content.append(root);
|
||||
break;
|
||||
case 'turtle':
|
||||
// Turtle result
|
||||
root.setAttribute('width', '100%');
|
||||
root.setAttribute('height', '100%');
|
||||
$content.append(root.outerHTML);
|
||||
break;
|
||||
case 'sympy':
|
||||
$content.append(data.content);
|
||||
if (typeof window.MathJax === 'undefined') {
|
||||
// dynamically loading MathJax
|
||||
console.log('Loading MathJax (Sympy expression needs it).');
|
||||
(function () {
|
||||
let script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src =
|
||||
'https://cdn.jsdelivr.net/npm/mathjax@3.0.5/es5/tex-mml-chtml.js';
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
})();
|
||||
} else {
|
||||
// otherwise, render it
|
||||
window.MathJax.typeset();
|
||||
}
|
||||
break;
|
||||
case 'multiple':
|
||||
/* typically dispached by display() */
|
||||
for (let mime of [
|
||||
'image/svg+xml',
|
||||
'image/png',
|
||||
'text/html',
|
||||
'text/plain',
|
||||
]) {
|
||||
if (mime in data.content) {
|
||||
let content = data.content[mime];
|
||||
if (mime === 'image/png') {
|
||||
content =
|
||||
'<img src="data:image/png;base64,' +
|
||||
content +
|
||||
'" style="max-width: 100%; max-height: 100%;">';
|
||||
}
|
||||
$content.append(content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'tutor':
|
||||
// hacky but iframe.document.body.style require to wait for
|
||||
// iframe loading
|
||||
$content.append($(data.content.replace('overflow-y%3A%20hidden%3B', '')));
|
||||
iframe = this.getContent()[0].getElementsByTagName('iframe')[0];
|
||||
if (iframe == null) return;
|
||||
// trick to avoid taking height update into account
|
||||
iframe.style.maxHeight = iframe.style.minHeight = '100%';
|
||||
|
||||
// force rendering when visible,
|
||||
// otherwise, strange things happends
|
||||
// since PythonTutor check for visibility at some point
|
||||
new IntersectionObserver((entries, observer) => {
|
||||
const entry = entries[0];
|
||||
if (entry && !entry.isIntersecting) return;
|
||||
iframe.contentWindow?.postMessage({ type: 'redraw' }, '*');
|
||||
observer.disconnect();
|
||||
}).observe(iframe);
|
||||
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
`Not supported node type '${data.display_type}' in eval.display result processing.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default StatusBarImage;
|
||||
36
mixly/boards/default_src/python_skulpt_car/package.json
Normal file
36
mixly/boards/default_src/python_skulpt_car/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@mixly/python-skulpt-car",
|
||||
"version": "1.2.0",
|
||||
"description": "适用于mixly的python skulpt car模块",
|
||||
"scripts": {
|
||||
"serve": "webpack-dev-server --config=webpack.dev.js",
|
||||
"build:dev": "webpack --config=webpack.dev.js",
|
||||
"build:prod": "npm run build:examples & webpack --config=webpack.prod.js",
|
||||
"build:examples": "node ../../../scripts/build-examples.js -t special",
|
||||
"build:examples:ob": "node ../../../scripts/build-examples.js -t special --obfuscate",
|
||||
"publish:board": "npm publish --registry https://registry.npmjs.org/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mixly/python": "1.0.0"
|
||||
},
|
||||
"main": "./export.js",
|
||||
"author": "Mixly Team",
|
||||
"keywords": [
|
||||
"mixly",
|
||||
"mixly-plugin",
|
||||
"python-skulpt-car"
|
||||
],
|
||||
"homepage": "https://gitee.com/bnu_mixly/mixly3/tree/master/boards/default_src/python_skulpt_car",
|
||||
"bugs": {
|
||||
"url": "https://gitee.com/bnu_mixly/mixly3/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/bnu_mixly/mixly3.git",
|
||||
"directory": "default_src/python_skulpt_car"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"license": "Apache 2.0"
|
||||
}
|
||||
1209
mixly/boards/default_src/python_skulpt_car/template.xml
Normal file
1209
mixly/boards/default_src/python_skulpt_car/template.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
<style>
|
||||
.select2-dropdown.mixly-{{d.mId}} {
|
||||
width: 100% !important;
|
||||
transform: translate(calc(-50% + 45px), calc((var(--nav-height) - var(--nav-select-height)) / 2));
|
||||
}
|
||||
|
||||
body > .select2-container:has(.mixly-{{d.mId}}) {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: layui-upbit;
|
||||
}
|
||||
</style>
|
||||
<select m-id={{d.mId}}></select>
|
||||
@@ -0,0 +1,26 @@
|
||||
<style>
|
||||
div[m-id="{{d.mId}}"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
html[data-bs-theme=light] div[m-id="{{d.mId}}"] {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
html[data-bs-theme=dark] div[m-id="{{d.mId}}"] {
|
||||
background-color: #1e1e1e;
|
||||
}
|
||||
|
||||
div[m-id="{{d.mId}}"] > #output-img {
|
||||
width: 400px;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<div m-id="{{d.mId}}" class="page-item mixly-scrollbar">
|
||||
<div id="output-img"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,59 @@
|
||||
import math
|
||||
|
||||
def math_map(v, al, ah, bl, bh):
|
||||
return bl + (bh - bl) * (v - al) / (ah - al)
|
||||
|
||||
def math_mean(myList):
|
||||
localList = [e for e in myList if type(e) == int or type(e) == float]
|
||||
if not localList: return
|
||||
return float(sum(localList)) / len(localList)
|
||||
|
||||
def math_median(myList):
|
||||
localList = sorted([e for e in myList if type(e) == int or type(e) == float])
|
||||
if not localList: return
|
||||
if len(localList) % 2 == 0:
|
||||
return (localList[len(localList) // 2 - 1] + localList[len(localList) // 2]) / 2.0
|
||||
else:
|
||||
return localList[(len(localList) - 1) // 2]
|
||||
|
||||
def math_modes(some_list):
|
||||
modes = []
|
||||
# Using a lists of [item, count] to keep count rather than dict
|
||||
# to avoid "unhashable" errors when the counted item is itself a list or dict.
|
||||
counts = []
|
||||
maxCount = 1
|
||||
for item in some_list:
|
||||
found = False
|
||||
for count in counts:
|
||||
if count[0] == item:
|
||||
count[1] += 1
|
||||
maxCount = max(maxCount, count[1])
|
||||
found = True
|
||||
if not found:
|
||||
counts.append([item, 1])
|
||||
for counted_item, item_count in counts:
|
||||
if item_count == maxCount:
|
||||
modes.append(counted_item)
|
||||
return modes
|
||||
|
||||
def math_standard_deviation(numbers):
|
||||
n = len(numbers)
|
||||
if n == 0: return
|
||||
mean = float(sum(numbers)) / n
|
||||
variance = sum((x - mean) ** 2 for x in numbers) / n
|
||||
return math.sqrt(variance)
|
||||
|
||||
def lists_sort(my_list, type, reverse):
|
||||
def try_float(s):
|
||||
try:
|
||||
return float(s)
|
||||
except:
|
||||
return 0
|
||||
key_funcs = {
|
||||
"NUMERIC": try_float,
|
||||
"TEXT": str,
|
||||
"IGNORE_CASE": lambda s: str(s).lower()
|
||||
}
|
||||
key_func = key_funcs[type]
|
||||
list_cpy = list(my_list)
|
||||
return sorted(list_cpy, key=key_func, reverse=reverse)
|
||||
30
mixly/boards/default_src/python_skulpt_car/webpack.common.js
Normal file
30
mixly/boards/default_src/python_skulpt_car/webpack.common.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const path = require('path');
|
||||
const common = require('../../../webpack.common');
|
||||
const { merge } = require('webpack-merge');
|
||||
|
||||
module.exports = merge(common, {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@mixly/python': path.resolve(__dirname, '../python'),
|
||||
'@mixly/python-skulpt': path.resolve(__dirname, '../python_skulpt')
|
||||
}
|
||||
},
|
||||
externals: {
|
||||
'sk': 'Sk'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.py$/,
|
||||
type: 'asset/source',
|
||||
}, {
|
||||
test: /\.js$/,
|
||||
resourceQuery: /url/,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: "[name]_[contenthash:8][ext]",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
36
mixly/boards/default_src/python_skulpt_car/webpack.dev.js
Normal file
36
mixly/boards/default_src/python_skulpt_car/webpack.dev.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const path = require("path");
|
||||
const common = require("./webpack.common");
|
||||
const { merge } = require("webpack-merge");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: "development",
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new ESLintPlugin({
|
||||
context: process.cwd(),
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
template: path.resolve(process.cwd(), 'template.xml'),
|
||||
filename: 'index.xml',
|
||||
minify: false
|
||||
})
|
||||
],
|
||||
devServer: {
|
||||
https: true,
|
||||
port: 8080,
|
||||
host: '0.0.0.0',
|
||||
hot: false,
|
||||
static: {
|
||||
directory: path.join(process.cwd(), '../../../'),
|
||||
watch: false
|
||||
},
|
||||
devMiddleware: {
|
||||
index: false,
|
||||
publicPath: `/boards/default/${path.basename(process.cwd())}`,
|
||||
writeToDisk: false
|
||||
}
|
||||
}
|
||||
});
|
||||
27
mixly/boards/default_src/python_skulpt_car/webpack.prod.js
Normal file
27
mixly/boards/default_src/python_skulpt_car/webpack.prod.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const path = require("path");
|
||||
const common = require("./webpack.common");
|
||||
const { merge } = require("webpack-merge");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: "production",
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
extractComments: false,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
template: path.resolve(process.cwd(), 'template.xml'),
|
||||
filename: 'index.xml',
|
||||
minify: {
|
||||
removeAttributeQuotes: true,
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user