update ig
commit
abd6c06cfa
|
@ -0,0 +1,48 @@
|
|||
import importlib.util
|
||||
import sys
|
||||
import os
|
||||
|
||||
class loader():
|
||||
def __init__(self, bar):
|
||||
self.instructions = {}
|
||||
self.loadedpaths = []
|
||||
self.bar = bar
|
||||
|
||||
def RISloading(self, path, IR):
|
||||
count = 0
|
||||
for i in range(len(os.listdir(path))):
|
||||
i = os.listdir(path)[i]
|
||||
if os.path.isdir(path+"/"+i):
|
||||
for f in os.listdir(path+"/"+i):
|
||||
if os.path.isfile(path+"/"+i+"/"+f):
|
||||
count += 1
|
||||
else:
|
||||
count += 1
|
||||
stat = self.bar(count)
|
||||
stat.__iter__()
|
||||
for i in range(len(os.listdir(path))):
|
||||
i = os.listdir(path)[i]
|
||||
if os.path.isdir(path+"/"+i):
|
||||
for f in os.listdir(path+"/"+i):
|
||||
if os.path.isfile(path+"/"+i+"/"+f):
|
||||
self.loadIS(path+"/"+i+"/"+f, IR)
|
||||
stat.__next__()
|
||||
else:
|
||||
self.loadIS(path+"/"+i, IR)
|
||||
stat.__next__()
|
||||
try:
|
||||
stat.__next__()
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
|
||||
def loadIS(self, path, IR):
|
||||
spec = importlib.util.spec_from_file_location(os.path.basename(path), path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[os.path.basename(path)] = module
|
||||
spec.loader.exec_module(module)
|
||||
try:
|
||||
self.instructions[os.path.basename(path)] = module.main(IR)
|
||||
except:
|
||||
self.instructions[os.path.basename(path)] = module.main()
|
||||
self.loadedpaths.append(path)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,20 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
text = ""
|
||||
if len(params) > 0:
|
||||
text = params[0]
|
||||
if len(params) > 1:
|
||||
return (1)
|
||||
return (0, input(text))
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
print("Operations are not supported with this instruction")
|
||||
return (1)
|
|
@ -0,0 +1,27 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
end = "\n"
|
||||
flush = False
|
||||
text = ""
|
||||
if len(params) > 0:
|
||||
text = params[0]
|
||||
if len(params) > 1:
|
||||
end = params[1]
|
||||
if len(params) > 2:
|
||||
flush = bool(params[2])
|
||||
if len(params) > 3:
|
||||
return (1)
|
||||
print(text, end=end, flush=flush)
|
||||
return (0, None)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
print("Operations are not supported with this instruction")
|
||||
return (1)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
return (0, int(arg1)+int(arg2))
|
||||
else:
|
||||
return (0, arg1+arg2)
|
|
@ -0,0 +1,18 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
return (0, int(arg1)/int(arg2))
|
||||
else:
|
||||
return (1)
|
|
@ -0,0 +1,18 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if str(arg1) == str(arg2):
|
||||
return (0, True)
|
||||
else:
|
||||
return (0, False)
|
|
@ -0,0 +1,21 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
if arg1 > arg2:
|
||||
return (0, True)
|
||||
else:
|
||||
return (0, False)
|
||||
else:
|
||||
return (1)
|
|
@ -0,0 +1,21 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
if arg1 < arg2:
|
||||
return (0, True)
|
||||
else:
|
||||
return (0, False)
|
||||
else:
|
||||
return (1)
|
|
@ -0,0 +1,18 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
return (0, int(arg1)*int(arg2))
|
||||
else:
|
||||
return (0, arg1*arg2)
|
|
@ -0,0 +1,18 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
return (0, int(arg1)%int(arg2))
|
||||
else:
|
||||
return (1)
|
|
@ -0,0 +1,16 @@
|
|||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
if arg1.isnumeric() and arg2.isnumeric():
|
||||
return (0, int(arg1)-int(arg2))
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,41 @@
|
|||
class main():
|
||||
def __init__(self, interpreter):
|
||||
self.interpreter = interpreter
|
||||
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
class temp():
|
||||
def __init__(self, interpreter):
|
||||
self.interpreter = interpreter
|
||||
self.CS = ""
|
||||
self.STRtypes = ['"', "<", "{", "[", "(", ":"]
|
||||
self.ENDtypes = ['"', ">", "}", "]", ")", ";"]
|
||||
def IND(self, index):
|
||||
return (0, self.CS[index])
|
||||
def EXEC(self, params):
|
||||
PREPEDAST = self.interpreter.lexer.tokenGEN(self.CS, self.STRtypes, self.ENDtypes)
|
||||
PREPEDAST = self.interpreter.PREPAST(PREPEDAST)
|
||||
PREPEDAST = self.interpreter.EXECINS(PREPEDAST)
|
||||
return (0, PREPEDAST)
|
||||
def ATR(self, target):
|
||||
return (0, self.CS)
|
||||
def STAT(self, params):
|
||||
self.CS = params[0]
|
||||
return (0, None)
|
||||
def OP(self, arg1, arg2):
|
||||
print("Operations are not supported with this CI")
|
||||
return (1)
|
||||
tempC = temp(self.interpreter)
|
||||
self.interpreter.vars[params[0]] = tempC
|
||||
return (0, None)
|
||||
def OP(self, arg1, arg2):
|
||||
print("Operations are not supported with this instruction")
|
||||
return (1)
|
|
@ -0,0 +1,23 @@
|
|||
class main():
|
||||
def __init__(self, interpreter):
|
||||
self.interpreter = interpreter
|
||||
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
if str(params[1]) == "True":
|
||||
if params[0] in self.interpreter.instructions:
|
||||
self.interpreter.instructions[params[0]].EXEC([])
|
||||
elif params[0] in self.interpreter.vars:
|
||||
self.interpreter.vars[params[0]].EXEC([])
|
||||
return (0, None)
|
||||
def OP(self, arg1, arg2):
|
||||
print("Operations are not supported with this instruction")
|
||||
return (1)
|
|
@ -0,0 +1,105 @@
|
|||
version = 0.5
|
||||
modules = ["interpreter", "ISL", "lexer", "compiler"]
|
||||
initmodules = {}
|
||||
def INITMODULES():
|
||||
global interpreter
|
||||
global lexer
|
||||
global loader
|
||||
global compiler
|
||||
global statusbar
|
||||
if __name__ == "__main__":
|
||||
import interpreter
|
||||
import lexer
|
||||
import ISL as loader
|
||||
import compiler
|
||||
import statusbar
|
||||
else:
|
||||
from PCPL import interpreter
|
||||
from PCPL import lexer
|
||||
from PCPL import ISL as loader
|
||||
from PCPL import compiler
|
||||
from PCPL import statusbar
|
||||
statusbar = statusbar.statusbar
|
||||
lexer = lexer.lexer()
|
||||
interpreter = interpreter.interpreter(lexer)
|
||||
loader = loader.loader(statusbar)
|
||||
compiler = compiler.compiler(statusbar)
|
||||
INITMODULES()
|
||||
STRtypes = ['"', "<", "{", "[", "(", ":"]
|
||||
ENDtypes = ['"', ">", "}", "]", ")", ";"]
|
||||
def compile(target, onefile):
|
||||
code = compiler.compilecode(target, loader.loadedpaths, onefile)
|
||||
return code
|
||||
def run(target):
|
||||
for i in target.split("\n"):
|
||||
#try:
|
||||
if True:
|
||||
AST = lexer.tokenGEN(i, STRtypes, ENDtypes)
|
||||
AST = interpreter.PREPAST(AST)
|
||||
interpreter.EXECINS(AST)[1]
|
||||
#except Exception as e:
|
||||
#print(f"Critical Error, HALT! {e}")
|
||||
#break
|
||||
def LIS(target):
|
||||
try:
|
||||
loader.RISloading(target, interpreter)
|
||||
for i in loader.instructions:
|
||||
interpreter.addinstruction(i.split(".")[0], loader.instructions[i])
|
||||
except Exception as e:
|
||||
print(f"Critical Error while loading instruction set {e}")
|
||||
def resetvar():
|
||||
interpreter.resetvars()
|
||||
if __name__ == "__main__":
|
||||
print("Types initialised")
|
||||
print(f"PCPL Command Line Version {version}, Made by Justus Wolff.")
|
||||
print(f"Type 'help' to get a list of commands.")
|
||||
commands = ["compile <path> <target> compiles the file at the specified path and writes output to target", "load <path> executes the file at the specified path", "Reload-M Reloads core modules", "LIS <target instruction set name> loads the specific instruction set under the file path", "exit exits the PCPL Command Line", "help Prints this message"]
|
||||
while True:
|
||||
command = input(">")
|
||||
if command.startswith("compile"):
|
||||
if len(command.split(" ")) != 4:
|
||||
print("The compile command only accepts three parameters! <src> <dst> <onefile 1/0>")
|
||||
else:
|
||||
print(f"compiling {command.split(' ')[1]} as {command.split(' ')[3]} and saving at {command.split(' ')[2]}. y/n?")
|
||||
while True:
|
||||
temp = input("compile? >")
|
||||
if temp == "y":
|
||||
compile()
|
||||
targetfile = open(command.split(" ")[1], 'r')
|
||||
code = targetfile.read()
|
||||
targetfile.close()
|
||||
code = compile(code, loader.loadedpaths, command.split(" ")[3])
|
||||
targetfile = open(command.split(" ")[2], 'w')
|
||||
targetfile.write(code)
|
||||
targetfile.close()
|
||||
break
|
||||
elif temp == "n":
|
||||
break
|
||||
elif command.startswith("load"):
|
||||
if len(command.split(" ")) != 2:
|
||||
print("The load command only accepts one parameter!")
|
||||
else:
|
||||
targetfile = open(command.split(" ")[1], 'r')
|
||||
code = targetfile.read()
|
||||
targetfile.close()
|
||||
run(code)
|
||||
elif command.startswith("Reload-M"):
|
||||
INITMODULES()
|
||||
elif command.startswith("help"):
|
||||
for i in commands:
|
||||
print(i)
|
||||
elif command.startswith("LIS"):
|
||||
if len(command.split(" ")) != 2:
|
||||
print("The LIS command only accepts one parameter!")
|
||||
else:
|
||||
LIS(command.split(" ")[1])
|
||||
elif command.startswith("exit"):
|
||||
exit()
|
||||
else:
|
||||
try:
|
||||
AST = lexer.tokenGEN(command, STRtypes, ENDtypes)
|
||||
AST = interpreter.PREPAST(AST)
|
||||
print(interpreter.EXECINS(AST)[1])
|
||||
except Exception as e:
|
||||
print(f"Critical Error, HALT! {e}")
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,281 @@
|
|||
import os
|
||||
|
||||
class compiler():
|
||||
def __init__(self, bar):
|
||||
self.bar = bar
|
||||
|
||||
def compilecode(self, code, modules, onefile="1"):
|
||||
print("Initialising compiler...")
|
||||
out = ""
|
||||
print("Adding interpreter...")
|
||||
out = out + """
|
||||
class interpreter():
|
||||
def __init__(self, lexer):
|
||||
self.instructions = {}
|
||||
self.lexer = lexer
|
||||
self.vars = {}
|
||||
self.types = {"<": self.INDEXING, "{": self.EXECUTE, "[": self.ATTRIBUTING, "(": self.STATEMENT, ":": self.OPERATION}
|
||||
|
||||
def addinstruction(self, name, instruction):
|
||||
self.instructions[name] = instruction
|
||||
|
||||
def INDEXING(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].IND(target[1])
|
||||
else:
|
||||
return self.vars[target[0]].IND(target[1])
|
||||
|
||||
def EXECUTE(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].EXEC(target[1:])
|
||||
else:
|
||||
return self.vars[target[0]].EXEC(target[1:])
|
||||
|
||||
def ATTRIBUTING(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].ATR(target[1])
|
||||
else:
|
||||
return self.vars[target[0]].ATR(target[1])
|
||||
|
||||
def STATEMENT(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].STAT(target[1:])
|
||||
else:
|
||||
return self.vars[target[0]].STAT(target[1:])
|
||||
|
||||
def OPERATION(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].OP(target[1], target[2])
|
||||
else:
|
||||
return self.vars[target[0]].OP(target[1], target[2])
|
||||
|
||||
def EXECINS(self, target):
|
||||
back = self.types[target[0]](target)
|
||||
if back[0] == 1:
|
||||
raise TypeError(f"Error in instruction, HALT! {target}")
|
||||
return back
|
||||
|
||||
def PREPAST(self, target):
|
||||
# return codes 0: Normal 1: Error
|
||||
# print(f"Error in instruction, HALT! {target}")
|
||||
ACM = ""
|
||||
for i in range(len(target)):
|
||||
index = i
|
||||
i = target[i]
|
||||
if type(i) == list:
|
||||
i = self.PREPAST(i)
|
||||
#i = i.replace('"', "")
|
||||
if index != len(target)-1:
|
||||
if i[0] in self.types:
|
||||
back = self.EXECINS(i)
|
||||
ACM = ACM + "," + str(back[1])
|
||||
else:
|
||||
ACM = ACM + i
|
||||
else:
|
||||
i = i[:-1]+ ACM + i[-1]
|
||||
return i
|
||||
"""
|
||||
print("Adding lexer to output...")
|
||||
out = out + """
|
||||
class lexer():
|
||||
def scantill(self, target, goin, ends, RS=False):
|
||||
temp = []
|
||||
tempt = target[0]
|
||||
breaki = None
|
||||
f = 1
|
||||
ignore = False
|
||||
while True:
|
||||
index = f
|
||||
i = target[f]
|
||||
if i == '"' and ignore == False:
|
||||
ignore = True
|
||||
elif i == '"' and ignore == True:
|
||||
ignore = False
|
||||
else:
|
||||
if ignore == False:
|
||||
if i == ends[goin.index(target[0])]:
|
||||
breaki = index
|
||||
tempt = tempt + i
|
||||
break
|
||||
elif i in goin:
|
||||
TI,tempo = self.scantill(target[index:], goin, ends, True)
|
||||
f += TI
|
||||
temp.append(tempo)
|
||||
else:
|
||||
tempt = tempt + i
|
||||
else:
|
||||
tempt = tempt + i
|
||||
f += 1
|
||||
if breaki == None:
|
||||
breaki = len(target)-1
|
||||
temp.append(tempt)
|
||||
if RS == True:
|
||||
return breaki,temp
|
||||
else:
|
||||
return temp
|
||||
|
||||
def tokenGEN(self, target, starts, ends):
|
||||
#AST = {}
|
||||
#types = {'"': 0, "<": 1, "{": 2, "[": 3, "(": 4, "-":5}
|
||||
temp = self.scantill(target, starts, ends)
|
||||
#for i in temp:
|
||||
# if type(i) == str:
|
||||
# if i[0] != "-":
|
||||
# AST[i] = types[i[0]]
|
||||
# else:
|
||||
# AST[i] = types[i[0]] + i[1]
|
||||
# else:
|
||||
|
||||
return temp
|
||||
#return AST
|
||||
"""
|
||||
print("Adding init to output...")
|
||||
out = out + """
|
||||
lexerC = lexer()
|
||||
interpreterC = interpreter(lexerC)
|
||||
"""
|
||||
print("Reading modules...")
|
||||
if onefile == "1":
|
||||
instructions = []
|
||||
IDs = []
|
||||
for i in self.bar(len(modules)):
|
||||
if not os.path.exists(modules[i]):
|
||||
print(f"{modules[i]} not found.")
|
||||
continue
|
||||
file = open(modules[i], 'r')
|
||||
file = file.read()
|
||||
# os.path.basename(modules[i]).split(".")[0]
|
||||
file = file[:6]+os.path.basename(modules[i]).split(".")[0]+file[6:]
|
||||
out = out + file + "\n"
|
||||
instructions.append(os.path.basename(modules[i]).split(".")[0])
|
||||
IDs.append(os.path.basename(modules[i]).split(".")[0]+"main")
|
||||
print("Adding INS to output...")
|
||||
out = out + "INS = "+str(instructions)+"\n"
|
||||
out = out + "IDs = "+str(IDs).replace("'", "")
|
||||
print("Adding INS adder to output...")
|
||||
out = out + """
|
||||
for i in range(len(INS)):
|
||||
try:
|
||||
interpreterC.addinstruction(INS[i], IDs[i](interpreterC))
|
||||
except:
|
||||
interpreterC.addinstruction(INS[i], IDs[i]())
|
||||
"""
|
||||
else:
|
||||
os.mkdir("IS")
|
||||
for i in self.bar(len(modules)):
|
||||
if not os.path.exists(modules[i]):
|
||||
print(f"{modules[i]} not found.")
|
||||
continue
|
||||
file = open(modules[i], 'r')
|
||||
tcode = file.read()
|
||||
file.close()
|
||||
# os.path.basename(modules[i]).split(".")[0]
|
||||
file = open(f"IS/{os.path.basename(modules[i])}", 'w')
|
||||
file.write(tcode)
|
||||
file.close()
|
||||
print("Adding ISL because onefile is 0...")
|
||||
out = out + "RCHAR = " + repr("\r") + "\n"
|
||||
out = out + """
|
||||
import importlib.util
|
||||
import sys
|
||||
import os
|
||||
|
||||
class statusbar:
|
||||
def __init__(self, repeat, length=30, step=1):
|
||||
self.length = length
|
||||
self.repeat = repeat
|
||||
self.steps = repeat/length
|
||||
self.step = step
|
||||
|
||||
def __iter__(self):
|
||||
self.current = 0
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
print("["+("#"*int(self.current/self.steps))+(" "*(self.length-int(self.current/self.steps)))+"]"+f"{self.current}/{self.repeat}", end=RCHAR, flush=True)
|
||||
if self.current >= self.repeat:
|
||||
print()
|
||||
raise StopIteration
|
||||
self.current += self.step
|
||||
return self.current-1
|
||||
|
||||
class loader():
|
||||
def __init__(self):
|
||||
self.instructions = {}
|
||||
self.loadedpaths = []
|
||||
|
||||
def RISloading(self, path, IR):
|
||||
temp = os.listdir(path)
|
||||
for i in statusbar(len(temp)):
|
||||
i = temp[i]
|
||||
if os.path.isdir(path+"/"+i):
|
||||
for f in os.listdir(path+"/"+i):
|
||||
if os.path.isfile(path+"/"+i+"/"+f):
|
||||
self.loadIS(path+"/"+i+"/"+f, IR)
|
||||
else:
|
||||
self.loadIS(path+"/"+i, IR)
|
||||
print("finished RIS instruction loading")
|
||||
|
||||
|
||||
def loadIS(self, path, IR):
|
||||
spec = importlib.util.spec_from_file_location(os.path.basename(path), path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[os.path.basename(path)] = module
|
||||
spec.loader.exec_module(module)
|
||||
try:
|
||||
self.instructions[os.path.basename(path)] = module.main(IR)
|
||||
except:
|
||||
self.instructions[os.path.basename(path)] = module.main()
|
||||
self.loadedpaths.append(path)
|
||||
loaderC = loader()
|
||||
"""
|
||||
print("Adding INS adder to output...")
|
||||
out = out + """
|
||||
loaderC.RISloading("IS", interpreterC)
|
||||
for i in loaderC.instructions:
|
||||
interpreterC.addinstruction(i.split(".")[0], loaderC.instructions[i])
|
||||
"""
|
||||
print("Adding STR and END types to output...")
|
||||
out = out + """
|
||||
STRtypes = ['"', "<", "{", "[", "(", ":"]
|
||||
ENDtypes = ['"', ">", "}", "]", ")", ";"]
|
||||
"""
|
||||
print("Adding target code...")
|
||||
code = code.replace("\n", "<PCPL-COM-NEWLINE>")
|
||||
out = out + 'code = '+ ('"'*3) + code + ('"'*3)
|
||||
print("Adding execution code...")
|
||||
out = out + """
|
||||
for i in code.split("<PCPL-COM-NEWLINE>"):
|
||||
AST = lexerC.tokenGEN(i, STRtypes, ENDtypes)
|
||||
AST = interpreterC.PREPAST(AST)
|
||||
interpreterC.EXECINS(AST)
|
||||
"""
|
||||
print("Done.")
|
||||
if onefile == "0":
|
||||
print("The folder 'IS' needs to always be in the same directory as the .py file or else the compiled program will crash.")
|
||||
return out
|
|
@ -0,0 +1,77 @@
|
|||
PCPL (Python Compilable Programming Language)
|
||||
|
||||
PCPL style
|
||||
|
||||
Strings:
|
||||
start: "
|
||||
end: "
|
||||
type: 0
|
||||
Indexing:
|
||||
start: <
|
||||
end: >
|
||||
type: 1
|
||||
Execute:
|
||||
start: {
|
||||
end: }
|
||||
type: 2
|
||||
Attributes:
|
||||
start: [
|
||||
end: ]
|
||||
type: 3
|
||||
Statements:
|
||||
start: (
|
||||
end: )
|
||||
type: 4
|
||||
|
||||
Operations:
|
||||
Operators (JUFS-BASE-PACK):
|
||||
equal:
|
||||
type: E
|
||||
higher:
|
||||
type: H
|
||||
lower:
|
||||
type: L
|
||||
add:
|
||||
type: A
|
||||
subtract:
|
||||
type: S
|
||||
multiply:
|
||||
type: M
|
||||
divide:
|
||||
type: D
|
||||
remainder:
|
||||
type: R
|
||||
start: :
|
||||
end: ;
|
||||
BASE-type: 5
|
||||
Example-type: 5-H This would mean an operation with the operator "higher" which checks if argument 1 is higher than argument 2
|
||||
|
||||
Example instruction:
|
||||
|
||||
<indexobject{function,5}>
|
||||
|
||||
This would return the indexed thing from "indexobject" with the index from the result of the function named "function" that is given the argument "5"
|
||||
|
||||
Example instruction class:
|
||||
|
||||
class main():
|
||||
def IND(self, index):
|
||||
print("Indexing is not supported with this instruction")
|
||||
return (1)
|
||||
def EXEC(self, params):
|
||||
print("Execution is not supported with this instruction")
|
||||
return (1)
|
||||
def ATR(self, target):
|
||||
print("Getting an attribute is not supported with this instruction")
|
||||
return (1)
|
||||
def STAT(self, params):
|
||||
print("Statements are not supported with this instruction")
|
||||
return (1)
|
||||
def OP(self, arg1, arg2):
|
||||
print("Operations are not supported with this instruction")
|
||||
return (1)
|
||||
|
||||
!WARNING! in strings (between ") instead of , use . the dots will be replaced by , automatically
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
class interpreter():
|
||||
def __init__(self, lexer):
|
||||
self.instructions = {}
|
||||
self.lexer = lexer
|
||||
self.vars = {}
|
||||
self.types = {"<": self.INDEXING, "{": self.EXECUTE, "[": self.ATTRIBUTING, "(": self.STATEMENT, ":": self.OPERATION}
|
||||
|
||||
def resetvars(self):
|
||||
self.vars = {}
|
||||
|
||||
def addinstruction(self, name, instruction):
|
||||
self.instructions[name] = instruction
|
||||
|
||||
def INDEXING(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].IND(target[1])
|
||||
elif target[0] in self.vars:
|
||||
return self.vars[target[0]].IND(target[1])
|
||||
else:
|
||||
raise TypeError("Referenced Operation doesnt exist.")
|
||||
|
||||
def EXECUTE(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].EXEC(target[1:])
|
||||
elif target[0] in self.vars:
|
||||
return self.vars[target[0]].EXEC(target[1:])
|
||||
else:
|
||||
raise TypeError("Referenced Operation doesnt exist.")
|
||||
|
||||
def ATTRIBUTING(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].ATR(target[1])
|
||||
elif target[0] in self.vars:
|
||||
return self.vars[target[0]].ATR(target[1])
|
||||
else:
|
||||
raise TypeError("Referenced Operation doesnt exist.")
|
||||
|
||||
def STATEMENT(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].STAT(target[1:])
|
||||
elif target[0] in self.vars:
|
||||
return self.vars[target[0]].STAT(target[1:])
|
||||
else:
|
||||
raise TypeError("Referenced Operation doesnt exist.")
|
||||
|
||||
def OPERATION(self, target):
|
||||
target = target[1:]
|
||||
target = target[:-1]
|
||||
target = target.split(",")
|
||||
for i in range(len(target)):
|
||||
target[i] = target[i].replace(".", ",")
|
||||
if target[0] in self.instructions:
|
||||
return self.instructions[target[0]].OP(target[1], target[2])
|
||||
elif target[0] in self.vars:
|
||||
return self.vars[target[0]].OP(target[1], target[2])
|
||||
else:
|
||||
raise TypeError("Referenced Operation doesnt exist.")
|
||||
|
||||
def EXECINS(self, target):
|
||||
back = self.types[target[0]](target)
|
||||
if back[0] == 1:
|
||||
raise TypeError(f"Error in instruction, HALT! {target}")
|
||||
return back
|
||||
|
||||
def PREPAST(self, target):
|
||||
# return codes 0: Normal 1: Error
|
||||
# print(f"Error in instruction, HALT! {target}")
|
||||
ACM = ""
|
||||
for i in range(len(target)):
|
||||
index = i
|
||||
i = target[i]
|
||||
if type(i) == list:
|
||||
i = self.PREPAST(i)
|
||||
#i = i.replace('"', "")
|
||||
if index != len(target)-1:
|
||||
if i[0] in self.types:
|
||||
back = self.EXECINS(i)
|
||||
ACM = ACM + "," + str(back[1])
|
||||
else:
|
||||
ACM = ACM + i
|
||||
else:
|
||||
i = i[:-1]+ ACM + i[-1]
|
||||
return i
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
class lexer():
|
||||
def scantill(self, target, goin, ends, RS=False):
|
||||
temp = []
|
||||
tempt = target[0]
|
||||
breaki = None
|
||||
f = 1
|
||||
ignore = False
|
||||
while True:
|
||||
if f > len(target)-1:
|
||||
raise TypeError("No end detected!")
|
||||
index = f
|
||||
i = target[f]
|
||||
if i == '"' and ignore == False:
|
||||
ignore = True
|
||||
elif i == '"' and ignore == True:
|
||||
ignore = False
|
||||
else:
|
||||
if ignore == False:
|
||||
if i == ends[goin.index(target[0])]:
|
||||
breaki = index
|
||||
tempt = tempt + i
|
||||
break
|
||||
elif i in goin:
|
||||
TI,tempo = self.scantill(target[index:], goin, ends, True)
|
||||
f += TI
|
||||
temp.append(tempo)
|
||||
else:
|
||||
tempt = tempt + i
|
||||
else:
|
||||
tempt = tempt + i
|
||||
f += 1
|
||||
if breaki == None:
|
||||
breaki = len(target)-1
|
||||
temp.append(tempt)
|
||||
if RS == True:
|
||||
return breaki,temp
|
||||
else:
|
||||
return temp
|
||||
|
||||
def tokenGEN(self, target, starts, ends):
|
||||
#AST = {}
|
||||
#types = {'"': 0, "<": 1, "{": 2, "[": 3, "(": 4, "-":5}
|
||||
temp = self.scantill(target, starts, ends)
|
||||
#for i in temp:
|
||||
# if type(i) == str:
|
||||
# if i[0] != "-":
|
||||
# AST[i] = types[i[0]]
|
||||
# else:
|
||||
# AST[i] = types[i[0]] + i[1]
|
||||
# else:
|
||||
|
||||
return temp
|
||||
#return AST
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
class statusbar:
|
||||
def __init__(self, repeat, length=30, step=1):
|
||||
self.length = length
|
||||
self.repeat = repeat
|
||||
self.steps = repeat/length
|
||||
self.step = step
|
||||
|
||||
def __iter__(self):
|
||||
self.current = 0
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
print("["+("#"*int(self.current/self.steps))+(" "*(self.length-int(self.current/self.steps)))+"]"+f"{self.current}/{self.repeat}", end="\r", flush=True)
|
||||
if self.current >= self.repeat:
|
||||
print()
|
||||
raise StopIteration
|
||||
self.current += self.step
|
||||
return self.current-1
|
Binary file not shown.
|
@ -0,0 +1,170 @@
|
|||
import tkinter as tk
|
||||
import string
|
||||
import random
|
||||
from colorama import Fore, Back, Style
|
||||
from colorama import just_fix_windows_console
|
||||
just_fix_windows_console()
|
||||
|
||||
class stdrend:
|
||||
def __init__(self, size):
|
||||
self._size = size
|
||||
self._grid = {}
|
||||
self._win = tk.Tk()
|
||||
for y in range(size[1]):
|
||||
for x in range(size[0]):
|
||||
temp = tk.Label(text=" ")
|
||||
temp.grid(row=y, column=x)
|
||||
self._win.update()
|
||||
self._grid[f"{x}:{y}"] = temp
|
||||
|
||||
def pix(self, x, y, text):
|
||||
if f"{x}:{y}" in self._grid:
|
||||
self._grid[f"{x}:{y}"].config(text=text)
|
||||
self._win.update()
|
||||
|
||||
class vector2:
|
||||
def __init__(self, x=0, y=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def __add__(self, v):
|
||||
#if not type(v) != vector2: raise ValueError(f"Must be vector2! got {type(v)}")
|
||||
return vector2(self.x+v.x, self.y+v.y)
|
||||
|
||||
def __sub__(self, v):
|
||||
if not type(v) != vector2: raise ValueError("Must be vector2!")
|
||||
return vector2(self.x-v.x, self.y-v.y)
|
||||
|
||||
def __mul__(self, v):
|
||||
if not type(v) != vector2: raise ValueError("Must be vector2!")
|
||||
return vector2(self.x*v.x, self.y*v.y)
|
||||
|
||||
def __iadd__(self, v):
|
||||
if not type(v) != vector2: raise ValueError("Must be vector2!")
|
||||
return vector2(self.x+v.x, self.y+v.y)
|
||||
|
||||
def __isub__(self, v):
|
||||
if not type(v) != vector2: raise ValueError("Must be vector2!")
|
||||
return vector2(self.x-v.x, self.y-v.y)
|
||||
|
||||
def __imul__(self, v):
|
||||
if not type(v) != vector2: raise ValueError("Must be vector2!")
|
||||
return vector2(self.x*v.x, self.y*v.y)
|
||||
|
||||
class NULL:
|
||||
def __init__(self):
|
||||
return None
|
||||
|
||||
class enum:
|
||||
def __init__(self, sel):
|
||||
self._sel = dict(sel)
|
||||
for i in self._sel:
|
||||
setattr(self, i, self._sel[i])
|
||||
|
||||
def getposssel(self):
|
||||
return list(self._sel.keys())
|
||||
|
||||
_fcolors = {"norm": Fore.RESET, "red": Fore.RED, "green": Fore.GREEN, "blue": Fore.BLUE, "white": Fore.WHITE, "cyan": Fore.CYAN, "black": Fore.BLACK, "magenta": Fore.MAGENTA, "yellow": Fore.YELLOW}
|
||||
_bcolors = {"norm": Back.RESET, "red": Back.RED, "green": Back.GREEN, "blue": Back.BLUE, "white": Back.WHITE, "cyan": Back.CYAN, "black": Back.BLACK, "magenta": Back.MAGENTA, "yellow": Back.YELLOW}
|
||||
|
||||
backcolors = enum(_bcolors)
|
||||
forecolors = enum(_fcolors)
|
||||
cammode = enum({"editable": 0, "follow": 1})
|
||||
|
||||
class obj:
|
||||
def __init__(self):
|
||||
self.position = vector2()
|
||||
self.char = " "
|
||||
self.ID = 0
|
||||
self.parent = None
|
||||
self.gravity = 0
|
||||
self.acceleration = vector2()
|
||||
self.velocity = vector2()
|
||||
self.friction = 0
|
||||
self.collide = True
|
||||
self.touch = True
|
||||
self.anchored = False
|
||||
self.bcolor = backcolors.norm
|
||||
self.fcolor = forecolors.norm
|
||||
|
||||
class camera(obj):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.mode = cammode.editable
|
||||
self.subject = None
|
||||
self.collide = False
|
||||
self.touch = False
|
||||
self.char = " "
|
||||
|
||||
def update(self):
|
||||
if self.mode == cammode.follow and self.subject:
|
||||
self.position = self.subject.position
|
||||
|
||||
class event:
|
||||
def __init__(self):
|
||||
self._receivers = []
|
||||
|
||||
def connect(self, code):
|
||||
self._receivers.append(code)
|
||||
|
||||
def fire(self):
|
||||
for i in self._receivers:
|
||||
exec(i, globals())
|
||||
|
||||
class game:
|
||||
def __init__(self, size=[20, 20], renderer=stdrend):
|
||||
if renderer == None: raise TypeError("Renderer class needed!")
|
||||
self._size = size
|
||||
self._renderer = renderer(size)
|
||||
self._objects = {}
|
||||
self.camera = camera()
|
||||
|
||||
def addobj(self, obj):
|
||||
id = ""
|
||||
for i in range(256):
|
||||
id = id + random.choice(list(string.ascii_letters))
|
||||
obj.ID = id
|
||||
self._objects[id] = obj
|
||||
|
||||
def getobjbyid(self, id):
|
||||
return self._objects[id]
|
||||
|
||||
def removeobj(self, obj):
|
||||
self._objects.pop(obj.ID)
|
||||
obj.ID = NULL()
|
||||
|
||||
def removeobjbyid(self, id):
|
||||
self._objects.pop(id).ID = NULL()
|
||||
|
||||
def between(self, target, minb, maxb):
|
||||
if minb < target < maxb:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def colliding(self, obj):
|
||||
# return list of colliding objects
|
||||
pass
|
||||
|
||||
def render(self):
|
||||
self.camera.update()
|
||||
for i in self._objects.values():
|
||||
pos = i.position + self.camera.position
|
||||
char = i.char
|
||||
bc = i.bcolor
|
||||
fc = i.fcolor
|
||||
if self.between(pos.y, -1, self._size[1]) and self.between(pos.x, -1, self._size[0]):
|
||||
self._renderer.pix(pos.x, pos.y, bc+fc+char+Style.RESET_ALL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
testgame = game()
|
||||
object = obj()
|
||||
testgame.addobj(object)
|
||||
object.char = "#"
|
||||
object.fc = forecolors.black
|
||||
testgame.render()
|
||||
print(object.ID)
|
||||
input()
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import os
|
||||
import ast
|
||||
class langhandler:
|
||||
def __init__(self, LANGNOTFOUND="LANGNULL", TEXTNOTFOUND="NULL"):
|
||||
self._langnf = LANGNOTFOUND
|
||||
self._textnf = TEXTNOTFOUND
|
||||
self._langs = {}
|
||||
for i in os.listdir(os.path.dirname(__file__)+"/lang"):
|
||||
if i.split(".")[1] == "LAN":
|
||||
file = open(os.path.dirname(__file__)+"/lang/"+i, 'r')
|
||||
file = file.read()
|
||||
file = ast.literal_eval(file)
|
||||
self._langs[i.split(".")[0]] = file
|
||||
if len(self._langs) <= 0:
|
||||
print("NO languages found!")
|
||||
self._langs["NULL"] = {
|
||||
"NULL": "NULL",
|
||||
}
|
||||
self._lang = list(self._langs.keys())[0]
|
||||
|
||||
def setlang(self, new):
|
||||
self._lang = new
|
||||
|
||||
def string(self, x):
|
||||
if not self._lang in self._langs: return self._langnf
|
||||
if not x in self._langs[self._lang]: return self._textnf
|
||||
return self._langs[self._lang][x]
|
||||
|
||||
def getlangs(self):
|
||||
return list(self._langs.keys())
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"file": "Datei",
|
||||
"new": "Neu",
|
||||
"open": "Oeffnen",
|
||||
"exit": "Beenden",
|
||||
"langs": "Sprachen",
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"file": "File",
|
||||
"new": "New",
|
||||
"open": "Open",
|
||||
"exit": "Quit",
|
||||
"langs": "Languages",
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import mtTkinter as tk
|
||||
import PCPL
|
||||
import langsys
|
||||
import time
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
global LH
|
||||
LH = langsys.langhandler()
|
||||
lang = open("clang", 'r')
|
||||
lang = lang.read()
|
||||
LH.setlang(lang)
|
||||
# LH.string("")
|
||||
|
||||
def selectlang(new):
|
||||
lang = open("clang", 'w')
|
||||
lang.write(new)
|
||||
lang.close()
|
||||
container.quit()
|
||||
subprocess.Popen([sys.executable, __file__])
|
||||
|
||||
def GUIinit():
|
||||
global container
|
||||
container = tk.Tk()
|
||||
menu = tk.Menu(container)
|
||||
container.config(menu=menu)
|
||||
filemenu = tk.Menu(menu)
|
||||
menu.add_cascade(label=LH.string("file"), menu=filemenu)
|
||||
filemenu.add_command(label=LH.string("new"))
|
||||
filemenu.add_command(label=LH.string("open"))
|
||||
filemenu.add_separator()
|
||||
filemenu.add_command(label=LH.string("exit"), command=container.quit)
|
||||
|
||||
langmenu = tk.Menu(menu)
|
||||
menu.add_cascade(label=LH.string("langs"), menu=langmenu)
|
||||
for i in LH.getlangs():
|
||||
langmenu.add_command(label=i, command=lambda i=i: selectlang(i))
|
||||
|
||||
container.mainloop()
|
||||
|
||||
GUIinit()
|
|
@ -0,0 +1,175 @@
|
|||
|
||||
import sys
|
||||
import threading
|
||||
if sys.version_info[0] == 2:
|
||||
# Python 2
|
||||
from Tkinter import *
|
||||
import Queue as queue
|
||||
else:
|
||||
# Python 3
|
||||
from tkinter import *
|
||||
import queue
|
||||
|
||||
|
||||
class _Tk(object):
|
||||
"""Wrapper for underlying attribute tk of class Tk"""
|
||||
|
||||
def __init__(self, tk, mt_debug=0, mt_check_period=10):
|
||||
"""
|
||||
:param tk: Tkinter.Tk.tk Tk interpreter object
|
||||
:param mt_debug: Determines amount of debug output.
|
||||
0 = No debug output (default)
|
||||
1 = Minimal debug output
|
||||
...
|
||||
9 = Full debug output
|
||||
:param mt_check_period: Amount of time in milliseconds (default
|
||||
10) between checks for out-of-thread events when things are
|
||||
otherwise idle. Decreasing this value can improve GUI
|
||||
responsiveness, but at the expense of consuming more CPU
|
||||
cycles.
|
||||
|
||||
# TODO: Replace custom logging functionality with standard
|
||||
# TODO: logging.Logger for easier access and standardization
|
||||
"""
|
||||
self._tk = tk
|
||||
|
||||
# Create the incoming event queue
|
||||
self._event_queue = queue.Queue(1)
|
||||
|
||||
# Identify the thread from which this object is being created
|
||||
# so we can tell later whether an event is coming from another
|
||||
# thread.
|
||||
self._creation_thread = threading.current_thread()
|
||||
|
||||
# Create attributes for kwargs
|
||||
self._debug = mt_debug
|
||||
self._check_period = mt_check_period
|
||||
# Destroying flag to be set by the .destroy() hook
|
||||
self._destroying = False
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Diverts attribute accesses to a wrapper around the underlying tk
|
||||
object.
|
||||
"""
|
||||
return _TkAttr(self, getattr(self._tk, name))
|
||||
|
||||
|
||||
class _TkAttr(object):
|
||||
"""Thread-safe callable attribute wrapper"""
|
||||
|
||||
def __init__(self, tk, attr):
|
||||
self._tk = tk
|
||||
self._attr = attr
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""
|
||||
Thread-safe method invocation. Diverts out-of-thread calls
|
||||
through the event queue. Forwards all other method calls to the
|
||||
underlying tk object directly.
|
||||
"""
|
||||
|
||||
# Check if we're in the creation thread
|
||||
if threading.current_thread() == self._tk._creation_thread:
|
||||
# We're in the creation thread; just call the event directly
|
||||
if self._tk._debug >= 8 or \
|
||||
self._tk._debug >= 3 and self._attr.__name__ == 'call' and \
|
||||
len(args) >= 1 and args[0] == 'after':
|
||||
print('Calling event directly:', self._attr.__name__, args, kwargs)
|
||||
return self._attr(*args, **kwargs)
|
||||
else:
|
||||
if not self._tk._destroying:
|
||||
# We're in a different thread than the creation thread;
|
||||
# enqueue the event, and then wait for the response.
|
||||
response_queue = queue.Queue(1)
|
||||
if self._tk._debug >= 1:
|
||||
print('Marshalling event:', self._attr.__name__, args, kwargs)
|
||||
self._tk._event_queue.put((self._attr, args, kwargs, response_queue), True, 1)
|
||||
is_exception, response = response_queue.get(True, None)
|
||||
|
||||
# Handle the response, whether it's a normal return value or
|
||||
# an exception.
|
||||
if is_exception:
|
||||
ex_type, ex_value, ex_tb = response
|
||||
raise ex_type(ex_value, ex_tb)
|
||||
return response
|
||||
|
||||
|
||||
def _Tk__init__(self, *args, **kwargs):
|
||||
"""
|
||||
Hook for Tkinter.Tk.__init__ method
|
||||
:param self: Tk instance
|
||||
:param args, kwargs: Arguments for Tk initializer
|
||||
"""
|
||||
# We support some new keyword arguments that the original __init__ method
|
||||
# doesn't expect, so separate those out before doing anything else.
|
||||
new_kwnames = ('mt_check_period', 'mt_debug')
|
||||
new_kwargs = {
|
||||
kw_name: kwargs.pop(kw_name) for kw_name in new_kwnames
|
||||
if kwargs.get(kw_name, None) is not None
|
||||
}
|
||||
|
||||
# Call the original __init__ method, creating the internal tk member.
|
||||
self.__original__init__mtTkinter(*args, **kwargs)
|
||||
|
||||
# Replace the internal tk member with a wrapper that handles calls from
|
||||
# other threads.
|
||||
self.tk = _Tk(self.tk, **new_kwargs)
|
||||
|
||||
# Set up the first event to check for out-of-thread events.
|
||||
self.after_idle(_check_events, self)
|
||||
|
||||
|
||||
# Define a hook for class Tk's destroy method.
|
||||
def _Tk_destroy(self):
|
||||
self.tk._destroying = True
|
||||
self.__original__destroy()
|
||||
|
||||
|
||||
def _check_events(tk):
|
||||
"""Checks events in the queue on a given Tk instance"""
|
||||
|
||||
used = False
|
||||
try:
|
||||
# Process all enqueued events, then exit.
|
||||
while True:
|
||||
try:
|
||||
# Get an event request from the queue.
|
||||
method, args, kwargs, response_queue = tk.tk._event_queue.get_nowait()
|
||||
except queue.Empty:
|
||||
# No more events to process.
|
||||
break
|
||||
else:
|
||||
# Call the event with the given arguments, and then return
|
||||
# the result back to the caller via the response queue.
|
||||
used = True
|
||||
if tk.tk._debug >= 2:
|
||||
print('Calling event from main thread:', method.__name__, args, kwargs)
|
||||
try:
|
||||
response_queue.put((False, method(*args, **kwargs)))
|
||||
except SystemExit:
|
||||
raise # Raises original SystemExit
|
||||
except Exception:
|
||||
# Calling the event caused an exception; return the
|
||||
# exception back to the caller so that it can be raised
|
||||
# in the caller's thread.
|
||||
from sys import exc_info # Python 2 requirement
|
||||
ex_type, ex_value, ex_tb = exc_info()
|
||||
response_queue.put((True, (ex_type, ex_value, ex_tb)))
|
||||
finally:
|
||||
# Schedule to check again. If we just processed an event, check
|
||||
# immediately; if we didn't, check later.
|
||||
if used:
|
||||
tk.after_idle(_check_events, tk)
|
||||
else:
|
||||
tk.after(tk.tk._check_period, _check_events, tk)
|
||||
|
||||
|
||||
"""Perform in-memory modification of Tkinter module"""
|
||||
# Replace Tk's original __init__ with the hook.
|
||||
Tk.__original__init__mtTkinter = Tk.__init__
|
||||
Tk.__init__ = _Tk__init__
|
||||
|
||||
# Replace Tk's original destroy with the hook.
|
||||
Tk.__original__destroy = Tk.destroy
|
||||
Tk.destroy = _Tk_destroy
|
Loading…
Reference in New Issue