def getRegArgNum(macroDict, name): if name == "inc" or name == "dec": return 1 elif name == "halt": return 0 else: if name in macroDict: return macroDict[name].numRegArgs else: error("Macro not found.")
def setZgoto(self, zgoto): #zgoto is an instruction node if self.instr == "D": self.zgoto = zgoto else: error("Only decrement instructions have a zgoto.")
def setGoto(self, goto): #goto is an instruction node if self.instr == "H": error("Halt instructions have no goto.") else: self.goto = goto
def setTarget(self, target): #assume target is valid and is an int if self.instr == "H": error("Halt instructions have no target.") else: self.target = target
def run(): fileName = input(".rmm file path to compile: ") if fileName.split(".")[-1] != "rmm": error("File must be a .rmm (register machine macro) file.") f = open(fileName) lines = f.readlines() lines = lmap(lambda x: x.strip(), lines) #remove newline lines = lfilter(lambda x: x != "", lines) #remove empty lines hasMacros = False if "MACROS" in lines: hasMacros = True if not hasMacros: pass #defer to the original interpreter #TODO: is this dead code? if "MACROS" not in lines: error("\"MACROS\" divider must be in code before macros.") macroDiv = lines.index("MACROS") #line that divides code from macro definitions rmCode = lines[:macroDiv] rmmCode = lines[macroDiv+1:] rmmCode = lmap(lambda x: x.split(" "), rmmCode) #split into tokens #isolate code snippets for converting into macros macros = [] #list of code snippets progress = [] for line in rmmCode: if line[0] == "macro": if progress != []: macros.append(progress) #read until the next "macro" token, then add to list progress = [line] else: progress.append(line) macros.append(progress) #add all the macros to a dictionary for easy lookup macroDict = {} for m in macros: mac = Macro(m) #convert to macro object macroDict[mac.name] = mac #with all macros found, go back and convert line references to labels for m in macroDict: thisMacro = macroDict[m] for i in range(len(thisMacro.code)): line = thisMacro.code[i] labelLine = [[i+1]] instruction = line[0] nR = getRegArgNum(macroDict, instruction) for i in range(len(line)): if i <= nR: #either the instruction or one of the register args labelLine.append(line[i]) #just copy it over else: #a line arg try: labelLine.append([int(line[i])]) #attempt to convert to int and make a label except: labelLine.append(line[i]) #otherwise just copy thisMacro.labCode.append(labelLine) #add line to labeled code #add line labels and convert line refs to labels in the code being compiled rmCode = lmap(lambda x: x.split(),rmCode) for i in range(len(rmCode)): line = rmCode[i] instr = line[0] nR = getRegArgNum(macroDict, instr) nL = getLineArgNum(macroDict, instr) for j in range(len(line)): if j > nR: #line arg try: line[j] = [int(line[j])] except: pass extLabel = [i+1] rmCode[i] = [extLabel] + line[:nR + nL + 1] #expand code by substituting for macros expanded = False while(not expanded): expanded = True for i in range(len(rmCode)): line = rmCode[i] label = line[0] instr = line[1] if instr not in ["inc", "dec", "halt"]: nR = getRegArgNum(macroDict, instr) expanded = False regSub = line[2:2+nR] lineSub = line[2+nR:] mac = macroDict[instr] sub = mac.substitute(macroDict, regSub, lineSub, label) rmCode = rmCode[:i] + sub + rmCode[i+1:] break ########## DEBUG #for line in rmCode: # print(line) #print() #input() ########## maxDepth = max(lmap(lambda x: len(x[0]), rmCode)) #max depth of any label for i in range(len(rmCode)): line = rmCode[i] for j in range(len(line)): if type(line[j]) == list: line[j] += [1] * (maxDepth - len(line[j])) #pad with 1s to max depth #add labels to dictionary labelDict = {} for i in range(len(rmCode)): line = rmCode[i] label = line[0] lineNo = i+1 labelDict[tuple(label)] = lineNo #replace labels with line numbers for i in range(len(rmCode)): line = rmCode[i] for j in range(len(line)): if type(line[j]) == list: line[j] = labelDict[tuple(line[j])] rmCode = lmap(lambda x: x[1:], rmCode) #remove line numbers #create temp register dictionary tempDict = {} registersUsed = [] for line in rmCode: if len(line) > 1: #if not halt reg = line[1] try: #not a temp register line[1] = int(reg) registersUsed.append(int(reg)) except: pass unused = [] usedSet = set(registersUsed) highestUsed = max(registersUsed) internalUnused = set(range(highestUsed + 1)).difference(usedSet) #register numbers that got skipped unused += list(internalUnused) tempsLeft = len(allTemps) - len(unused) unused += list(range(highestUsed+1, highestUsed+1+tempsLeft)) #add additional registers as needed for line in rmCode: print(line) print() #naive replacement for i in range(len(allTemps)): tempDict[allTemps[i]] = unused[i] for line in rmCode: if len(line) > 1: reg = line[1] if type(reg) == str: #temp register that needs to be replaced line[1] = tempDict[reg] f = open(fileName.split(".")[0] + ".rm", "w+") for line in rmCode: line = lmap(lambda x: str(x), line) f.write(" ".join(line) + "\n") #debug for line in rmCode: print(line) print("\n") for m in macroDict: print(str(macroDict[m]) + "\n") print("\n")
def error(self,string): #for some reason, your __init__ might go awry error(string)
def run(path=None, regInput=None): #arguments passed through to naive interpreter setup (code, registers) = setup(path, regInput) diagram = Diagram(code) current = diagram.entry trace = [] #list for keeping track of instructions since last loop # (Instruction object, identifier, target) # I: increment # D: decrement # Z: zero check (register was unable to be decremented) #TODO style = False annPath = '' while True: if current in lmap(lambda x: x[0], trace): print("Loop caught!") instrTrace = lmap(lambda x: x[0], trace) loop = trace[instrTrace.index( current):] #from current instruction forward #print("Loop: %s"%(str(loop))) trace = [] #clear trace list decd = [] #indices of decremented registers for t in loop: if t[1] == "D" or t[1] == "Z": if t[2] not in decd: decd.append(t[2]) #add decremented register to list autoIters = float("inf") #number of safe iterations we can do for i in decd: #for each decremented register thisReg = lfilter(lambda x: x[2] == i, loop) seq = lmap( lambda x: x[1], thisReg) #sequence of things happening to this register #print("%i: %s"%(i,seq)) if "Z" in seq: running = registers[i] for c in seq: if c == "I": running += 1 elif c == "D": if running == 0: autoIters = 0 #already broke behavior else: running -= 1 else: if running != 0: autoIters = 0 else: lowest = 0 running = 0 for c in seq: if c == "I": running += 1 else: running -= 1 lowest = min(lowest, running) safe = lowest * -1 #smallest value of the register without it getting decremented while 0 #running is the net change from the loop if running < 0: safeIters = (registers[i] - safe) // (running * -1) autoIters = min(autoIters, safeIters) if autoIters == 0: #whoops, no point break if autoIters == float("inf"): error("Infinite loop detected") if autoIters > 0: net = [0] * len(registers) #net change to each register for t in loop: tgt = t[2] if t[1] == "I": net[tgt] += 1 elif t[1] == "D": net[tgt] -= 1 for i in range(len(net)): net[i] *= autoIters #net change after all safe iterations for i in range(len(registers)): registers[i] += net[i] display(registers, style, annPath) if autoIters >= 2: print("%i iterations skipped!" % (autoIters)) else: tgt = current.target if current.instr == "I": registers[tgt] += 1 display(registers, style, annPath) trace.append((current, "I", tgt)) current = current.goto elif current.instr == "D": if registers[tgt] == 0: trace.append((current, "Z", tgt)) current = current.zgoto else: registers[tgt] -= 1 display(registers, style, annPath) trace.append((current, "D", tgt)) current = current.goto else: #halt print("~DONE~") return registers