ScratchAnalyzer 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ScratchAnalyzer/Cast.py +12 -0
- ScratchAnalyzer/Project.py +277 -0
- ScratchAnalyzer/Project.pyi +235 -0
- ScratchAnalyzer/Scratch.py +416 -0
- ScratchAnalyzer/Scratch.pyi +95 -0
- ScratchAnalyzer/__init__.py +6 -0
- ScratchAnalyzer/__main__.py +5 -0
- ScratchAnalyzer/assets/codeMain.python.tpl +8 -0
- ScratchAnalyzer/assets/progMain.python.tpl +29 -0
- ScratchAnalyzer/assets/translator.python.json +126 -0
- ScratchAnalyzer/errors.py +2 -0
- ScratchAnalyzer/iostream.py +103 -0
- ScratchAnalyzer/iostream.pyi +116 -0
- ScratchAnalyzer/main.py +78 -0
- ScratchAnalyzer/public.py +41 -0
- ScratchAnalyzer/translator.py +20 -0
- scratchanalyzer-0.1.0.dist-info/METADATA +90 -0
- scratchanalyzer-0.1.0.dist-info/RECORD +22 -0
- scratchanalyzer-0.1.0.dist-info/WHEEL +5 -0
- scratchanalyzer-0.1.0.dist-info/entry_points.txt +2 -0
- scratchanalyzer-0.1.0.dist-info/licenses/LICENSE +21 -0
- scratchanalyzer-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"motion_gotoxy": "await Scratch.goto_by_pos(instance, %[X]%, %[Y]%)",
|
|
3
|
+
"motion_glideto": "await Scratch.glide_to(instance, %[SECS]%, %[TO]%)",
|
|
4
|
+
"motion_turnright": "await Scratch.turn_right(instance, %[DEGREES]%)",
|
|
5
|
+
"motion_turnleft": "await Scratch.turn_left(instance, %[DEGREES]%)",
|
|
6
|
+
"motion_movesteps": "await Scratch.move_steps(instance, %[STEPS]%)",
|
|
7
|
+
"motion_goto": "await Scratch.goto(instance, %[TO]%)",
|
|
8
|
+
"motion_glidesecstoxy": "await Scratch.glide_to_xy(instance, %[SECS]%, %[X]%, %[Y]%)",
|
|
9
|
+
"motion_pointtowards": "await Scratch.set_direction_to(instance, %[TOWARDS]%)",
|
|
10
|
+
"motion_pointindirection": "await Scratch.set_direction(instance, %[DIRECTION]%)",
|
|
11
|
+
"motion_changexby": "await Scratch.change_x_by(instance, %[DX]%)",
|
|
12
|
+
"motion_setx": "await Scratch.set_x(instance, %[X]%)",
|
|
13
|
+
"motion_changeyby": "await Scratch.change_y_by(instance, %[DY]%)",
|
|
14
|
+
"motion_sety": "await Scratch.set_y(instance, %[Y]%)",
|
|
15
|
+
"motion_ifonedgebounce": "await Scratch.set_RWTE(instance)",
|
|
16
|
+
"motion_setrotationstyle": "await Scratch.set_rotation_style(instance, %[STYLE]%)",
|
|
17
|
+
"motion_xposition": "instance.x",
|
|
18
|
+
"motion_yposition": "instance.y",
|
|
19
|
+
"motion_direction": "instance.direction",
|
|
20
|
+
"looks_say": "await Scratch.say(instance, %[MESSAGE]%)",
|
|
21
|
+
"looks_sayforsecs": "await Scratch.say_for(instance, %[MESSAGE]%, %[SECS]%)",
|
|
22
|
+
"looks_switchcostumeto": "await Scratch.switch_costume_to(instance, %[COSTUME]%)",
|
|
23
|
+
"looks_nextcostume": "await Scratch.next_costume(instance)",
|
|
24
|
+
"looks_switchbackdropto": "await Scratch.switch_backdrop_to(instance, %[BACKDROP]%)",
|
|
25
|
+
"looks_nextbackdrop": "await Scratch.next_backdrop(instance)",
|
|
26
|
+
"looks_changesizeby": "await Scratch.change_size_by(instance, %[CHANGE]%)",
|
|
27
|
+
"looks_setsizeto": "await Scratch.set_size_to(instance, %[SIZE]%)",
|
|
28
|
+
"looks_seteffectto": "await Scratch.set_effect_to(instance, %[EFFECT]%, %[VALUE]%)",
|
|
29
|
+
"looks_changeeffectby": "await Scratch.change_effect_by(instance, %[EFFECT]%, %[CHANGE]%)",
|
|
30
|
+
"looks_cleargraphiceffects": "await Scratch.clear_effects(instance)",
|
|
31
|
+
"looks_show": "await Scratch.show(instance)",
|
|
32
|
+
"looks_hide": "await Scratch.hide(instance)",
|
|
33
|
+
"looks_gotofrontback": "await Scratch.move_to_by_layer_order(instance, %[FRONT_BACK]%)",
|
|
34
|
+
"looks_goforwardbackwardlayers": "await Scratch.move_by_by_layer_order(instance, %[NUM]%, %[FORWARD_BACKWARD]%)",
|
|
35
|
+
"looks_size": "instance.size",
|
|
36
|
+
"looks_costumenumbername": "Scratch.getCostume(instance, %[NUMBER_NAME]%)",
|
|
37
|
+
"looks_backdropnumbername": "Scratch.getBackdrop(instance, %[NUMBER_NAME]%)",
|
|
38
|
+
"sound_play": "await Scratch.play(instance, %[SOUND_MENU]%)",
|
|
39
|
+
"sound_playuntildone": "await Scratch.play_until_done(instance, %[SOUND_MENU]%)",
|
|
40
|
+
"sound_stopallsounds": "await Scratch.stop_all_from(instance)",
|
|
41
|
+
"sound_seteffectto": "await Scratch.set_sound_effect_to(instance, %[EFFECT]%, %[VALUE]%)",
|
|
42
|
+
"sound_changeeffectby": "await Scratch.change_sound_effect_by(instance, %[EFFECT]%, %[VALUE]%)",
|
|
43
|
+
"sound_cleareffects": "await Scratch.clear_sound_effects(instance)",
|
|
44
|
+
"sound_changevolumeby": "await Scratch.change_volume_by(instance, %[VOLUME]%)",
|
|
45
|
+
"sound_setvolumeto": "await Scratch.set_volume_to(instance, %[VOLUME]%)",
|
|
46
|
+
"sound_volume": "instance.sprite.volume",
|
|
47
|
+
"event_broadcast": "await Scratch.broadcast(instance, %[BROADCAST_INPUT]%)",
|
|
48
|
+
"event_broadcastandwait": "await Scratch.broadcast_and_wait(instance, %[BROADCAST_INPUT]%)",
|
|
49
|
+
"control_wait": "await Scratch.wait(instance, %[DURATION]%)",
|
|
50
|
+
"control_repeat": "for _ in range(Scratch.Cast.toNumber(%[TIMES]%)):",
|
|
51
|
+
"control_forever": "while True:",
|
|
52
|
+
"control_if": "if %[CONDITION]%:",
|
|
53
|
+
"control_if_else": "if %[CONDITION]%:",
|
|
54
|
+
"control_if_else_before_2": "else:",
|
|
55
|
+
"control_wait_until": "await Scratch.wait_until(instance, lambda instance: %[CONDITION]%)",
|
|
56
|
+
"control_repeat_until": "while not (%[CONDITION]%):",
|
|
57
|
+
"control_while": "while (%[CONDITION]%):",
|
|
58
|
+
"control_stop": "await Scratch.c_stop(instance, %[STOP_OPTION]%, task_id)",
|
|
59
|
+
"control_create_clone_of": "await Scratch.clone(instance, %[CLONE_OPTION]%)",
|
|
60
|
+
"control_delete_this_clone": "await Scratch.deleteThisClone(instance, task_id)",
|
|
61
|
+
"sensing_touchingobject": "Scratch.is_touching(instance, %[TOUCHINGOBJECTMENU]%)",
|
|
62
|
+
"sensing_touchingcolor": "Scratch.is_touching_color(instance, %[COLOR]%)",
|
|
63
|
+
"sensing_coloristouchingcolor": "Scratch.is_color_touching_color(%[COLOR]%, %[COLOR2]%)",
|
|
64
|
+
"sensing_distanceto": "Scratch.get_distance_to(instance, %[DISTANCETOMENU]%)",
|
|
65
|
+
"sensing_askandwait": "await Scratch.ask(instance, %[QUESTION]%)",
|
|
66
|
+
"sensing_answer": "Scratch.get_last_answer()",
|
|
67
|
+
"sensing_keypressed": "Scratch.is_key_pressed(%[KEY_OPTION]%)",
|
|
68
|
+
"sensing_mousedown": "Scratch.is_mouse_down()",
|
|
69
|
+
"sensing_mousex": "Scratch.get_mouse_x()",
|
|
70
|
+
"sensing_mousey": "Scratch.get_mouse_y()",
|
|
71
|
+
"sensing_setdragmode": "Scratch.set_drag_mode(instance, %[DRAG_MODE]%)",
|
|
72
|
+
"sensing_loudness": "(-1)",
|
|
73
|
+
"sensing_timer": "Scratch.getTimer()",
|
|
74
|
+
"sensing_resettimer": "Scratch.resetTimer()",
|
|
75
|
+
"sensing_of": "Scratch.obj_of_sprite(%[OBJECT]%, %[PROPERTY]%)",
|
|
76
|
+
"sensing_current": "Scratch.get_part_of_current_time(%[CURRENTMENU]%)",
|
|
77
|
+
"sensing_dayssince2000": "Scratch.get_days_since_2000()",
|
|
78
|
+
"sensing_username": "Scratch.get_user_name()",
|
|
79
|
+
"operator_add": "(Scratch.Cast.toNumber(%[NUM1]%, error=False) + Scratch.Cast.toNumber(%[NUM2]%, error=False))",
|
|
80
|
+
"operator_subtract": "(Scratch.Cast.toNumber(%[NUM1]%, error=False) - Scratch.Cast.toNumber(%[NUM2]%, error=False))",
|
|
81
|
+
"operator_multiply": "(Scratch.Cast.toNumber(%[NUM1]%, error=False) * Scratch.Cast.toNumber(%[NUM2]%, error=False))",
|
|
82
|
+
"operator_divide": "Scratch.safe_divide(Scratch.Cast.toNumber(%[NUM1]%, error=False), Scratch.Cast.toNumber(%[NUM2]%, error=False))",
|
|
83
|
+
"operator_random": "Scratch.randnum(Scratch.Cast.toNumber(%[FROM]%, error=False), Scratch.Cast.toNumber(%[TO]%, error=False))",
|
|
84
|
+
"operator_lt": "(Scratch.Cast.toNumber(%[OPERAND1]%, error=False) < Scratch.Cast.toNumber(%[OPERAND2]%, error=False))",
|
|
85
|
+
"operator_gt": "(Scratch.Cast.toNumber(%[OPERAND1]%, error=False) > Scratch.Cast.toNumber(%[OPERAND2]%, error=False))",
|
|
86
|
+
"operator_equals": "(Scratch.Cast.toVar(%[OPERAND1]%) == Scratch.Cast.toVar(%[OPERAND2]%))",
|
|
87
|
+
"operator_and": "(Scratch.Cast.toBool(%[OPERAND1]%) and Scratch.Cast.toBool(%[OPERAND2]%))",
|
|
88
|
+
"operator_or": "(Scratch.Cast.toBool(%[OPERAND1]%) or Scratch.Cast.toBool(%[OPERAND2]%))",
|
|
89
|
+
"operator_not": "(not Scratch.Cast.toBool(%[OPERAND]%))",
|
|
90
|
+
"operator_join": "(Scratch.Cast.toStr(%[STRING1]%) + Scratch.Cast.toStr(%[STRING2]%))",
|
|
91
|
+
"operator_letter_of": "Scratch.letter_of(Scratch.Cast.toStr(%[STRING]%), Scratch.Cast.toNumber(%[LETTER]%, error=False))",
|
|
92
|
+
"operator_length": "len(Scratch.Cast.toStr(%[STRING]%))",
|
|
93
|
+
"operator_contains": "(Scratch.Cast.toStr(%[STRING2]%).lower() in Scratch.Cast.toStr(%[STRING1]%).lower())",
|
|
94
|
+
"operator_mod": "Scratch.mod(Scratch.Cast.toNumber(%[NUM1]%, error=False), Scratch.Cast.toNumber(%[NUM2]%, error=False))",
|
|
95
|
+
"operator_round": "round(Scratch.Cast.toNumber(%[NUM]%, error=False))",
|
|
96
|
+
"operator_mathop": "Scratch.math_operation(%[OPERATOR]%, Scratch.Cast.toNumber(%[NUM]%, error=False))",
|
|
97
|
+
"data_variable": "Scratch.getVariable(instance, %[VARIABLE]%)",
|
|
98
|
+
"data_setvariableto": "Scratch.setVariable(instance, %[VARIABLE]%, %[VALUE]%)",
|
|
99
|
+
"data_changevariableby": "Scratch.changeVariableBy(instance, %[VARIABLE]%, %[VALUE]%)",
|
|
100
|
+
"data_showvariable": "# BLANK. OPCODE: data_showvariable",
|
|
101
|
+
"data_hidevariable": "# BLANK. OPCODE: data_hidevariable",
|
|
102
|
+
"data_listcontents": "Scratch.getList(instance, %[LIST]%)",
|
|
103
|
+
"data_addtolist": "Scratch.addToList(instance, %[LIST]%, %[ITEM]%)",
|
|
104
|
+
"data_deleteoflist": "Scratch.deleteIndexOfList(instance, %[LIST]%, %[INDEX]%)",
|
|
105
|
+
"data_deletealloflist": "Scratch.clearList(instance, %[LIST]%)",
|
|
106
|
+
"data_insertatlist": "Scratch.insetAtList(instance, %[LIST]%, Scratch.Cast.toNumber(%[INDEX]%, error=False), %[ITEM]%)",
|
|
107
|
+
"data_replaceitemoflist": "Scratch.replaceItemOfList(instance, %[LIST]%, Scratch.Cast.toNumber(%[INDEX]%, error=False), %[ITEM]%)",
|
|
108
|
+
"data_itemoflist": "Scratch.getItemOfList(instance, %[LIST]%, Scratch.Cast.toNumber(%[INDEX]%, error=False))",
|
|
109
|
+
"data_itemnumoflist": "Scratch.findIndexOfList(instance, %[LIST]%, %[ITEM]%)",
|
|
110
|
+
"data_lengthoflist": "Scratch.getLengthOfList(instance, %[LIST]%)",
|
|
111
|
+
"data_listcontainsitem": "Scratch.containItemOfList(instance, %[LIST]%, %[ITEM]%)",
|
|
112
|
+
"data_showlist": "# BLANK. OPCODE: data_showlist",
|
|
113
|
+
"data_hidelist": "# BLANK. OPCODE: data_hidelist",
|
|
114
|
+
"procedures_call": "await Scratch.call(%[FUNC_NAME]%, instance, task_id, %[ARGS]%)",
|
|
115
|
+
"argument_reporter_string_number": "Scratch.get_param(task_id, Scratch.Cast.toStr(%[VALUE]%))",
|
|
116
|
+
"argument_reporter_boolean": "Scratch.get_param(task_id, Scratch.Cast.toStr(%[VALUE]%))",
|
|
117
|
+
"pen_clear": "Scratch.pen_clear()",
|
|
118
|
+
"pen_penDown": "Scratch.pen_pen_down(instance)",
|
|
119
|
+
"pen_penUp": "Scratch.pen_pen_up(instance)",
|
|
120
|
+
"pen_setPenColorToColor": "Scratch.pen_set_color(instance, %[COLOR]%)",
|
|
121
|
+
"changePenColorParamBy": "Scratch.pen_change_color_param(instance, %[COLOR_PARAM]%, %[VALUE]%)",
|
|
122
|
+
"pen_setPenColorParamTo": "Scratch.pen_set_color_param(instance, %[COLOR_PARAM]%, %[VALUE]%)",
|
|
123
|
+
"pen_changePenSizeBy": "Scratch.pen_change_width(instance, %[SIZE]%)",
|
|
124
|
+
"pen_setPenSizeTo": "Scratch.pen_set_width(instance, %[SIZE]%)",
|
|
125
|
+
"pen_menu_colorParam": "%[colorParam]%"
|
|
126
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from colorama import init, Fore
|
|
2
|
+
import tqdm
|
|
3
|
+
|
|
4
|
+
init()
|
|
5
|
+
|
|
6
|
+
def Reset():
|
|
7
|
+
print(Fore.RESET, end="")
|
|
8
|
+
|
|
9
|
+
def _PrinterGenerator(color):
|
|
10
|
+
def Printer(*args, sep=" "):
|
|
11
|
+
string = sep.join([str(item) for item in args])
|
|
12
|
+
return getattr(Fore, color)+string+Fore.RESET
|
|
13
|
+
return Printer
|
|
14
|
+
|
|
15
|
+
def _StarterGenerator(color):
|
|
16
|
+
def Starter():
|
|
17
|
+
print(getattr(Fore, color), end="")
|
|
18
|
+
return Starter
|
|
19
|
+
|
|
20
|
+
class ColoredTqdm(tqdm.tqdm):
|
|
21
|
+
def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None,
|
|
22
|
+
ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None,
|
|
23
|
+
ascii=None, disable=False, unit='it', unit_scale=False,
|
|
24
|
+
dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0,
|
|
25
|
+
position=None, postfix=None, unit_divisor=1000, write_bytes=False,
|
|
26
|
+
lock_args=None, nrows=None, colour=None, delay=0.0, gui=False,
|
|
27
|
+
color1=Fore.LIGHTYELLOW_EX, color2=Fore.LIGHTGREEN_EX,
|
|
28
|
+
**kwargs):
|
|
29
|
+
print(color1, end="", flush=True)
|
|
30
|
+
self.color2 = color2
|
|
31
|
+
super().__init__(iterable, desc, total, leave, file,
|
|
32
|
+
ncols, mininterval, maxinterval, miniters,
|
|
33
|
+
ascii, disable, unit, unit_scale,
|
|
34
|
+
dynamic_ncols, smoothing, bar_format, initial,
|
|
35
|
+
position, postfix, unit_divisor, write_bytes,
|
|
36
|
+
lock_args, nrows, colour, delay, gui,
|
|
37
|
+
**kwargs)
|
|
38
|
+
def close(self):
|
|
39
|
+
print(self.color2, end="", flush=True)
|
|
40
|
+
super().close()
|
|
41
|
+
print(Fore.RESET, end="", flush=True)
|
|
42
|
+
|
|
43
|
+
def _makeAll():
|
|
44
|
+
colors = [
|
|
45
|
+
"Black",
|
|
46
|
+
"Red",
|
|
47
|
+
"Green",
|
|
48
|
+
"Yellow",
|
|
49
|
+
"Blue",
|
|
50
|
+
"Magenta",
|
|
51
|
+
"Cyan",
|
|
52
|
+
"White",
|
|
53
|
+
"LightBlack_EX",
|
|
54
|
+
"LightRed_EX",
|
|
55
|
+
"LightGreen_EX",
|
|
56
|
+
"LightYellow_EX",
|
|
57
|
+
"LightBlue_EX",
|
|
58
|
+
"LightMagenta_EX",
|
|
59
|
+
"LightCyan_EX",
|
|
60
|
+
"LightWhite_EX",
|
|
61
|
+
]
|
|
62
|
+
for color in colors:
|
|
63
|
+
globals()[f"Fore{color.replace('_EX', '')}"] = _PrinterGenerator(color.upper())
|
|
64
|
+
globals()[f"Start{color.replace('_EX', '')}"] = _StarterGenerator(color.upper())
|
|
65
|
+
|
|
66
|
+
_makeAll()
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"Reset",
|
|
70
|
+
"ColoredTqdm",
|
|
71
|
+
"ForeBlack",
|
|
72
|
+
"ForeRed",
|
|
73
|
+
"ForeGreen",
|
|
74
|
+
"ForeYellow",
|
|
75
|
+
"ForeBlue",
|
|
76
|
+
"ForeMagenta",
|
|
77
|
+
"ForeCyan",
|
|
78
|
+
"ForeWhite",
|
|
79
|
+
"ForeLightBlack",
|
|
80
|
+
"ForeLightRed",
|
|
81
|
+
"ForeLightGreen",
|
|
82
|
+
"ForeLightYellow",
|
|
83
|
+
"ForeLightBlue",
|
|
84
|
+
"ForeLightMagenta",
|
|
85
|
+
"ForeLightCyan",
|
|
86
|
+
"ForeLightWhite",
|
|
87
|
+
"StartBlack",
|
|
88
|
+
"StartRed",
|
|
89
|
+
"StartGreen",
|
|
90
|
+
"StartYellow",
|
|
91
|
+
"StartBlue",
|
|
92
|
+
"StartMagenta",
|
|
93
|
+
"StartCyan",
|
|
94
|
+
"StartWhite",
|
|
95
|
+
"StartLightBlack",
|
|
96
|
+
"StartLightRed",
|
|
97
|
+
"StartLightGreen",
|
|
98
|
+
"StartLightYellow",
|
|
99
|
+
"StartLightBlue",
|
|
100
|
+
"StartLightMagenta",
|
|
101
|
+
"StartLightCyan",
|
|
102
|
+
"StartLightWhite",
|
|
103
|
+
]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import tqdm
|
|
2
|
+
from colorama import Fore
|
|
3
|
+
|
|
4
|
+
def Reset() -> None:
|
|
5
|
+
...
|
|
6
|
+
|
|
7
|
+
class ColoredTqdm(tqdm.tqdm):
|
|
8
|
+
color2: str
|
|
9
|
+
|
|
10
|
+
def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None,
|
|
11
|
+
ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None,
|
|
12
|
+
ascii=None, disable=False, unit='it', unit_scale=False,
|
|
13
|
+
dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0,
|
|
14
|
+
position=None, postfix=None, unit_divisor=1000, write_bytes=False,
|
|
15
|
+
lock_args=None, nrows=None, colour=None, delay=0.0, gui=False,
|
|
16
|
+
color1: str=Fore.LIGHTYELLOW_EX, color2: str=Fore.LIGHTGREEN_EX,
|
|
17
|
+
**kwargs):
|
|
18
|
+
...
|
|
19
|
+
def close(self):
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def ForeBlack(*args, sep:str=" ") -> str:
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def ForeRed(*args, sep:str=" ") -> str:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def ForeGreen(*args, sep:str=" ") -> str:
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
def ForeYellow(*args, sep:str=" ") -> str:
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def ForeBlue(*args, sep:str=" ") -> str:
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def ForeMagenta(*args, sep:str=" ") -> str:
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def ForeCyan(*args, sep:str=" ") -> str:
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
def ForeWhite(*args, sep:str=" ") -> str:
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
def ForeLightBlack(*args, sep:str=" ") -> str:
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
def ForeLightRed(*args, sep:str=" ") -> str:
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def ForeLightGreen(*args, sep:str=" ") -> str:
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
def ForeLightYellow(*args, sep:str=" ") -> str:
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def ForeLightBlue(*args, sep:str=" ") -> str:
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
def ForeLightMagenta(*args, sep:str=" ") -> str:
|
|
62
|
+
...
|
|
63
|
+
|
|
64
|
+
def ForeLightCyan(*args, sep:str=" ") -> str:
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
def ForeLightWhite(*args, sep:str=" ") -> str:
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
def StartBlack() -> None:
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
def StartRed() -> None:
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
def StartGreen() -> None:
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def StartYellow() -> None:
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
def StartBlue() -> None:
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
def StartMagenta() -> None:
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
def StartCyan() -> None:
|
|
89
|
+
...
|
|
90
|
+
|
|
91
|
+
def StartWhite() -> None:
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
def StartLightBlack() -> None:
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
def StartLightRed() -> None:
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
def StartLightGreen() -> None:
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
def StartLightYellow() -> None:
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
def StartLightBlue() -> None:
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
def StartLightMagenta() -> None:
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
def StartLightCyan() -> None:
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
def StartLightWhite() -> None:
|
|
116
|
+
...
|
ScratchAnalyzer/main.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import zipfile
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
import traceback
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from .iostream import ForeLightRed, ForeLightGreen, StartLightRed, Reset, ColoredTqdm
|
|
11
|
+
from .Project import Project
|
|
12
|
+
from .Scratch import Scratch
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> int:
|
|
16
|
+
parser = argparse.ArgumentParser()
|
|
17
|
+
parser.add_argument("--input", "-i", type=str, required=True, help="输入文件(必须是Scratch3.0文件)")
|
|
18
|
+
parser.add_argument("--output", "-o", type=str, required=True, help="输出目录")
|
|
19
|
+
parser.add_argument("--language", "-l", default="python", choices=["python", "java"], type=str, required=False, help="转换成的语言")
|
|
20
|
+
args = parser.parse_args()
|
|
21
|
+
|
|
22
|
+
if os.path.isfile(args.output):
|
|
23
|
+
print(ForeLightRed("输出路径指向了文件!"))
|
|
24
|
+
return -1
|
|
25
|
+
|
|
26
|
+
if not os.path.exists(args.input) or not os.path.isfile(args.input):
|
|
27
|
+
print(ForeLightRed("输入文件不存在或不是文件!"))
|
|
28
|
+
return -1
|
|
29
|
+
|
|
30
|
+
if not args.input.endswith(".sb3"):
|
|
31
|
+
if args.input.endswith(".sb2") or args.input.endswith(".sb1"):
|
|
32
|
+
print(ForeLightRed("仅支持 Scratch 3.0 项目文件!"))
|
|
33
|
+
return -1
|
|
34
|
+
print(ForeLightRed("输入文件不是 Scratch 项目文件!"))
|
|
35
|
+
return -1
|
|
36
|
+
|
|
37
|
+
print(ForeLightGreen("正在初始化输出目录..."))
|
|
38
|
+
if os.path.exists(args.output) and os.path.isdir(args.output):
|
|
39
|
+
shutil.rmtree(args.output)
|
|
40
|
+
os.makedirs(args.output)
|
|
41
|
+
|
|
42
|
+
# 步骤1:解压数据
|
|
43
|
+
out = os.path.join(args.output, "assets") # 全部解压至输出目录
|
|
44
|
+
with zipfile.ZipFile(args.input, "r") as zip_ref:
|
|
45
|
+
try:
|
|
46
|
+
for member in ColoredTqdm(zip_ref.infolist(), desc="正在解压中", unit="文件"):
|
|
47
|
+
zip_ref.extract(member, out)
|
|
48
|
+
except:
|
|
49
|
+
StartLightRed()
|
|
50
|
+
print("解压时出错:")
|
|
51
|
+
traceback.print_exc()
|
|
52
|
+
Reset()
|
|
53
|
+
return -1
|
|
54
|
+
|
|
55
|
+
# 步骤2:解析project.json
|
|
56
|
+
print(f"正在解析项目数据...")
|
|
57
|
+
project_json = os.path.join(out, "project.json")
|
|
58
|
+
with open(project_json, "r", encoding="utf-8") as f:
|
|
59
|
+
data = json.load(f)
|
|
60
|
+
|
|
61
|
+
# 步骤3:解析数据
|
|
62
|
+
project = Project(data)
|
|
63
|
+
|
|
64
|
+
# 步骤4:生成数据
|
|
65
|
+
scratch = Scratch(project)
|
|
66
|
+
scratch.generate(Path(args.output))
|
|
67
|
+
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
try:
|
|
72
|
+
sys.exit(main())
|
|
73
|
+
except Exception as e:
|
|
74
|
+
StartLightRed()
|
|
75
|
+
print("主程序遇到未捕获错误:")
|
|
76
|
+
traceback.print_exc()
|
|
77
|
+
Reset()
|
|
78
|
+
sys.exit(-1)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
supported_languages = [
|
|
5
|
+
"python"
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
head_block_opcodes = [
|
|
9
|
+
"event_whenflagclicked",
|
|
10
|
+
"event_whenkeypressed",
|
|
11
|
+
"event_whenthisspriteclicked",
|
|
12
|
+
"event_whenbackdropswitchesto",
|
|
13
|
+
"event_whengreaterthan",
|
|
14
|
+
"event_whenbroadcastreceived",
|
|
15
|
+
"control_start_as_clone",
|
|
16
|
+
"procedures_definition"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
entries_block_opcodes = [
|
|
20
|
+
"event_whenflagclicked"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
substack_opcodes = [
|
|
24
|
+
"control_repeat",
|
|
25
|
+
"control_forever",
|
|
26
|
+
"control_if",
|
|
27
|
+
"control_if_else",
|
|
28
|
+
"control_repeat_until",
|
|
29
|
+
"control_while"
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
substack_opcodes_need_flush = {
|
|
33
|
+
"control_repeat": True,
|
|
34
|
+
"control_forever": True,
|
|
35
|
+
"control_if": False,
|
|
36
|
+
"control_if_else": False,
|
|
37
|
+
"control_repeat_until": True,
|
|
38
|
+
"control_while": True
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
assets_root_path = Path(__file__).parent / "assets"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from .public import assets_root_path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WrappedTranslator(object):
|
|
6
|
+
def __init__(self, translator):
|
|
7
|
+
self.translator = translator
|
|
8
|
+
def __getattr__(self, item):
|
|
9
|
+
translator = super().__getattribute__('translator')
|
|
10
|
+
# 先在字典属性translator中查找
|
|
11
|
+
if item in translator:
|
|
12
|
+
return translator[item]
|
|
13
|
+
else:
|
|
14
|
+
# 如果找不到,调用父类的__getattribute__方法
|
|
15
|
+
return super().__getattribute__(item)
|
|
16
|
+
|
|
17
|
+
def load_translator(language):
|
|
18
|
+
with open(assets_root_path / f"translator.{language.lower()}.json", "r", encoding="utf-8") as file:
|
|
19
|
+
data = json.load(file)
|
|
20
|
+
return WrappedTranslator(data)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ScratchAnalyzer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Scratch parser that can generate Python code, ensuring that the logic remains as it is, only unable to run.
|
|
5
|
+
Author-email: PerfectMYGHY <916881890@qq.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/PerfectMYGHY/scratch-analyzer
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: colorama==0.4.6
|
|
12
|
+
Requires-Dist: tqdm==4.67.3
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# scratch-analyzer
|
|
16
|
+
|
|
17
|
+
English version README.md: [README.english.md](README.english.md)
|
|
18
|
+
|
|
19
|
+
Scratch 解析库。一个使用Python制作的能够分析Python代码的软件包。
|
|
20
|
+
|
|
21
|
+
## 介绍
|
|
22
|
+
|
|
23
|
+
### 历史
|
|
24
|
+
|
|
25
|
+
基础代码再2025年(发布仓库的1年前)编写,当时我有一个梦想,将Scratch翻译成一个Python上能够运行的程序,于是我写下了这个程序,原名`Scratch2Python`。
|
|
26
|
+
|
|
27
|
+
我~~凭借着我惊人的智慧~~写出了`Scratch2Python`的Scratch转换Python代码功能,虽然代码有一丢烂,但是转换相当成功。
|
|
28
|
+
|
|
29
|
+
然而,接下来的问题是,Scratch运行时阻挠了我。从代码历史你能看到,本项目最终使用`Scratch4Python`作为运行时库,但是很可惜,我跟他耗了几个月,最终失败。
|
|
30
|
+
|
|
31
|
+
### 那么此库为何而在
|
|
32
|
+
|
|
33
|
+
因为我的这个翻译器还是太好了,理论上还可以做到翻译成各种语言,虽然目前仅保证转换为Python是正常的。
|
|
34
|
+
|
|
35
|
+
我认为这个功能很不错,于是建立存储库并单独提取其代码转换功能。
|
|
36
|
+
|
|
37
|
+
### 那Scratch4Python呢
|
|
38
|
+
|
|
39
|
+
我不打算完全放弃他,但是请等我深造几年,吃透OpenGL后,再管它吧。我想到时候将他作为独立库发布,同时更名`python-scratch-vm`。到时候可以两个库结合使用。
|
|
40
|
+
|
|
41
|
+
### 目录结构
|
|
42
|
+
|
|
43
|
+
```folder
|
|
44
|
+
├── docs # 文档注释文件夹
|
|
45
|
+
│ ├── README.chinese.md # 中文文档
|
|
46
|
+
│ └── README.english.md # 英文文档
|
|
47
|
+
├── LICENSE # MIT
|
|
48
|
+
├── pyproject.toml # 项目配置
|
|
49
|
+
├── README.md -> docs/README.chinese.md # 自述文件符号链接
|
|
50
|
+
├── requirements.txt # 需求文件
|
|
51
|
+
└── src # 源代码
|
|
52
|
+
└── ScratchAnalyzer # 软件包目录
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 使用
|
|
56
|
+
|
|
57
|
+
首先下载软件包。注意使用时软件包名为`ScratchAnalyzer`。
|
|
58
|
+
|
|
59
|
+
### 准备
|
|
60
|
+
|
|
61
|
+
首先,请准备好Scratch项目的`project.json`,将Scratch文件的后缀名更改为`.zip`,然后解压,即可得到素材文件和`project.json`。分析文件只需要`project.json`,但是如果需要运行等,请加上素材文件。
|
|
62
|
+
|
|
63
|
+
### 分析
|
|
64
|
+
|
|
65
|
+
假设已经准备好环境,使用方法如下:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from ScratchAnalyzer import Project, Scratch
|
|
69
|
+
import json
|
|
70
|
+
from pathlib import Path
|
|
71
|
+
|
|
72
|
+
# 1.读取文件,需要解析为字典
|
|
73
|
+
with open("project.json", "r", encoding="utf-8") as file: # 使用UTF-8确保不会编码错误
|
|
74
|
+
data = json.load(file)
|
|
75
|
+
|
|
76
|
+
# 2.创建Project对象,自动建立代码分析树,并存储项目元数据
|
|
77
|
+
project = Project(data)
|
|
78
|
+
|
|
79
|
+
# 3.创建Scratch对象,自动分析素材列表等元数据
|
|
80
|
+
scratch = Scratch(project) # 要传入project,这样就能得到所有数据
|
|
81
|
+
|
|
82
|
+
# 4.生产,由于生产的是多文件,所以要指定输出目录
|
|
83
|
+
scratch.generate(Path("output"), language="python") # 第一个参数为输出目录,language为可选参数,默认为"python",暂不支持其他语言,在此留个TODO
|
|
84
|
+
|
|
85
|
+
# 5.output下即生产结果,对象析构安全,不需要手动管理
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 注意
|
|
89
|
+
|
|
90
|
+
目前我还没有修改软件包接口,因此用起来有些困难。请等待我重构接口,以让这个软件包更好使用。
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
ScratchAnalyzer/Cast.py,sha256=3bGZgoQvSfFiJEpVInZLPIoZ3rEjbBGDv8M2hgKoSIM,377
|
|
2
|
+
ScratchAnalyzer/Project.py,sha256=DnVk5HdlAiTY3YMB2-a0k1DR654Pqm0PqVjs6O_uaok,12548
|
|
3
|
+
ScratchAnalyzer/Project.pyi,sha256=zamenmUewwHV6IVgBIlNIXhDvLE7TIk4nwEGOSTvPQ8,5334
|
|
4
|
+
ScratchAnalyzer/Scratch.py,sha256=UdIJiy4mA82a3-1X9HZm5F8V642LxkSgBXdDLczUAkc,19301
|
|
5
|
+
ScratchAnalyzer/Scratch.pyi,sha256=f2_kOtOMkbaHF0oBHZSYN-4_qGHdEQIjm912WEDktFA,2161
|
|
6
|
+
ScratchAnalyzer/__init__.py,sha256=mOu3Ny8NlRIVf2a0BlMLyL82h4gSmpjcgp9TYR9xbXw,101
|
|
7
|
+
ScratchAnalyzer/__main__.py,sha256=4rngtsfRe8uuplAOzG_zfDYMiiKpb_egxRMTFXQbCKY,83
|
|
8
|
+
ScratchAnalyzer/errors.py,sha256=P-GzGJglPeTITkotHX5TnRSv5qri_c7gtY9oqVMAvlM,62
|
|
9
|
+
ScratchAnalyzer/iostream.py,sha256=rXRPiF5V0M267Hvdb4UJdI_eifxEjKZt5HZtRe92JbY,2875
|
|
10
|
+
ScratchAnalyzer/iostream.pyi,sha256=eiPtjKPbLgjuwQ0dqgm-IAfdSp3pXI9MHbutZ95dWOU,2204
|
|
11
|
+
ScratchAnalyzer/main.py,sha256=zDTu4hv1mP9iZKFKi2gbFNMO6XbADRnWsCtg-E11rWk,2572
|
|
12
|
+
ScratchAnalyzer/public.py,sha256=CBtaYiSpJHVu-tLh-1b6fjeYL3z9VZmfqN3nQpHGDSc,817
|
|
13
|
+
ScratchAnalyzer/translator.py,sha256=W5urML2dmCrKGEEvAv-4oW-bJB6GZCmSJefsg4WYpm0,700
|
|
14
|
+
ScratchAnalyzer/assets/codeMain.python.tpl,sha256=SDOVf7GQE4FzNLa50TvRoIdt3jK4yrZAEyYw1ZOE9Fk,538
|
|
15
|
+
ScratchAnalyzer/assets/progMain.python.tpl,sha256=EdtfeEFP8qgNobmpY3lhZo_cYHfiUS1Yy2Ec9jztpuw,761
|
|
16
|
+
ScratchAnalyzer/assets/translator.python.json,sha256=caHGFBa4N0KQkPWXkwJS9S7LejosDo28CPo499nj8_k,9580
|
|
17
|
+
scratchanalyzer-0.1.0.dist-info/licenses/LICENSE,sha256=xR2fRS8P5gOUdAyfoPox79F1AxbCHWiLZf5QR02QN1M,1069
|
|
18
|
+
scratchanalyzer-0.1.0.dist-info/METADATA,sha256=Yf1JV04otOr3KGYHolqD5Q1AL_8eoOJeSxJ7NQ_NYpI,3672
|
|
19
|
+
scratchanalyzer-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
20
|
+
scratchanalyzer-0.1.0.dist-info/entry_points.txt,sha256=5BrROCFmY8F9t16Aj20PQj-k1Qa782GHg_3B5d05MQE,66
|
|
21
|
+
scratchanalyzer-0.1.0.dist-info/top_level.txt,sha256=1ztG_Nyee1TdYh94S0hIDn3Vjl8z2w7XwjQydPiHzlk,16
|
|
22
|
+
scratchanalyzer-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PerfectMYGHY
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ScratchAnalyzer
|