diff --git a/src/RAM.py b/src/RAM.py new file mode 100644 index 0000000..cea12fa --- /dev/null +++ b/src/RAM.py @@ -0,0 +1,147 @@ +from pathlib import Path +import re + + +class Expression: + def __init__(self): + self.register = r"R\[(?:\d+|R\[\d+])\]" + self.un_exp = f"(?:{self.register}|\\d+)" + self.bi_exp = f"{self.un_exp}(\\+|-|div|\\*|!=|==|<|>|<=|>=){self.un_exp}" + self.named_bi_exp = f"(?P{self.un_exp})(?P\\+|-|div|\\*|!=|==|<|>|<=|>=)(?P{self.un_exp})" + + def _eval_unary(self, exp: str, mem: dict) -> int: + try: + return int(exp) + except Exception: + pass + p = r"R\[(?:(?P\d+)|R\[(?P\d+)])\]" + m = re.match(p, exp) + + if not m: raise Exception("no valid expression") + rd, rrd = m.group("rd", "rrd") + + if rd: return mem[int(rd)] + elif rrd: return mem[mem[int(rrd)]] + else: raise Exception("no valid expression") + + def eval(self, exp: str, mem: dict) -> int: + m = re.match(self.named_bi_exp, exp) + if m: + lexp, rexp, op = m.group("lexp", "rexp", "op") + vl = self._eval_unary(lexp, mem) + vr = self._eval_unary(rexp, mem) + + if op == "==": return vl == vr + if op == "!=": return vl != vr + if op == "<=": return vl <= vr + if op == ">=": return vl >= vr + if op == "<": return vl < vr + if op == ">": return vl > vr + if op == "-": return vl - vr + if op == "+": return vl + vr + if op == "*": return vl * vr + if op == "div": return vl // vr + raise Exception("no valid expression") + else: + return self._eval_unary(exp, mem) + + def pattern(self) -> str: + # Order in regex is important! + return f"({self.bi_exp}|{self.un_exp})" + + +class Assignment: + def __init__(self): + self.exp = Expression() + self.pattern = f"^R\\[(?P\\d+|R\\[\\d+]|out)\\]:=(?P{self.exp.pattern()})$" + + def exec(self, exp: str, mem: dict) -> dict: + m = re.match(self.pattern, exp) + if not m: raise Exception("not a expression") + e, write_addr = m.group("e", "write_addr") + + # Look up Register + if write_addr[0] == "R": write_addr = mem[int(write_addr[2:-1])] + + # Evaluate the expression in the if statement + result = self.exp.eval(e, mem) + + # Write to memory + if write_addr == "out": mem["out"] = result + else: mem[int(write_addr)] = result + + # Set the programm counter + mem["pc"] += 1 + + return mem + + +class Condition: + def __init__(self): + self.exp = Expression() + self.pattern = f"if(?P{self.exp.pattern()})goto(?P\\d+)" + pass + + def exec(self, exp: str, mem: dict) -> dict: + m = re.match(self.pattern, exp) + if not m: raise Exception("not a expression") + e, line = m.group("e", "line") + + # Evaluate the expression in the if statement + result = self.exp.eval(e, mem) + + # Set the programm counter + if result != 0: mem["pc"] = int(line) - 1 + else: mem["pc"] += 1 + + return mem + + +class RAM: + def __init__(self, path: str): + self.load_file(Path(path)) + + print(self.instructions) + + self.mem = {} + self.mem["pc"] = 0 + print(self.run()) + + def load_file(self, path: Path) -> dict: + with open(path, "r") as f: + raw_src_text = "".join(f.readlines()) + + # Sanitize input + raw_src_text = re.sub(r"\\", "", raw_src_text, flags=re.MULTILINE) + + # Remove whitespace, mark empty lines + raw_src_text = re.sub("(#[^\n]*)?\n", r"\\", raw_src_text, flags=re.MULTILINE) + raw_src_text = re.sub(r"\s", "", raw_src_text, flags=re.MULTILINE) + + self.instructions = { l:i for l, i in enumerate(raw_src_text.split("\\")) } + + def run(self) -> int: + while "out" not in self.mem.keys(): self.eval() + return self.mem["out"] + + def eval(self): + # Get current instruction + current = self.instructions[self.mem["pc"]] + + # Check for empty line + if current == "": + self.mem["pc"] += 1 + return + + asg = Assignment() + con = Condition() + if current[0] == "R": + asg.exec(current, self.mem) + elif current[0] == "i": + con.exec(current, self.mem) + else: + raise Exception(f"Syntax error in line {self.mem['pc']}") + + +if __name__ == "__main__": + RAM("../examples/test.ram")