import random from rply.token import BaseBox BUILTIN_ARGS = { "print": "*", "input" : "*", "random": 2 } def rep_join(l): format_string = ',\n'.join( [repr(i) if not isinstance(i, str) else i for i in l] ).replace('\n', '\n ') if format_string != "": format_string = f"\n {format_string}\n" return format_string class Exp(BaseBox): def eval(self, vtable, ftable): return vtable, ftable, None def __repr__(self) -> str: return "exp()" class ExpInt(Exp): def __init__(self, value: int): self.value = value def __repr__(self) -> str: return f"exp_int({self.value})" def eval(self, vtable, ftable): return vtable, ftable, self.value class ExpString(Exp): def __init__(self, value: str): self.value = value def __repr__(self) -> str: return f"exp_string({self.value})" def eval(self, vtable, ftable): return vtable, ftable, self.value class ExpVariable(Exp): def __init__(self, variable_id: str): self.variable_name = variable_id def __repr__(self) -> str: return f"exp_variable({self.variable_name})" def eval(self, vtable, ftable): return vtable, ftable, vtable[self.variable_name] class ExpABinop(Exp): def __init__(self,op: str,exp1: Exp, exp2: Exp): self.op = op self.exp1 = exp1 self.exp2 = exp2 def __repr__(self) -> str: return f"exp_a_binop({self.op}, {self.exp1}, {self.exp2})" def eval(self, vtable, ftable): vtable, ftable, r1 = self.exp1.eval(vtable, ftable) if isinstance(r1,str): if r1.isdigit(): r1 = int(r1) else: r1 = float(r1) vtable, ftable, r2 = self.exp2.eval(vtable, ftable) if isinstance(r2,str): if r2.isdigit(): r2 = int(r2) else: r2 = float(r2) if self.op == "+": return vtable, ftable, r1+r2 elif self.op == "-": return vtable, ftable, r1-r2 elif self.op == "*": return vtable, ftable, r1*r2 elif self.op == "/": return vtable, ftable, r1/r2 elif self.op == "=": return vtable, ftable, r1 == r2 elif self.op == ">": return vtable, ftable, r1 > r2 elif self.op == "<": return vtable, ftable, r1 < r2 else: raise Exception(f"Unknown binop {self.op}") class ExpList(Exp): def __init__(self, expressions: list[Exp]): self.list = expressions def __repr__(self) -> str: return f"exp_list([{rep_join(self.list)}])" def eval(self, vtable, ftable): results = [] for element in self.list: vtable, ftable, result = element.eval(vtable, ftable) results.append(result) return vtable, ftable, results class ExpIndex(Exp): def __init__(self, items: ExpList, index: Exp): self.list = items self.index = index def __repr__(self) -> str: return f"exp_index({self.list}, {self.index})" def eval(self, vtable, ftable): vtable, ftable, elements = self.list.eval(vtable, ftable) vtable, ftable, index = self.index.eval(vtable, ftable) if len(elements) > index: result = elements[index] else: result = None return vtable, ftable, result class ExpArg(Exp): def __init__(self, arg: int): self.arg = arg def __repr__(self) -> str: return f"exp_arg({self.arg})" def eval(self, vtable, ftable): n = vtable["#ARGS"] - self.arg return vtable, ftable, vtable[f"#{n}"] class Command(BaseBox): def eval(self, vtable, ftable): return vtable, ftable, None def __repr__(self) -> str: return f"command()" class Builtin(Command): def __init__(self, builtin, args: list[Exp]): self.builtin = builtin.value self.args = args self.position = builtin.source_pos.lineno expected_args = BUILTIN_ARGS[self.builtin] if not (expected_args == "*" or expected_args == len(self.args)): print(f"E005: Wrong number of arguments for builtin '{self.builtin}' ({len(self.args)}) at line {self.position}") exit() def __repr__(self) -> str: return f"builtin({self.builtin}, {self.args})" def eval(self, vtable, ftable): if self.builtin == "print": prints = [] for arg in self.args: vtable, ftable, result = arg.eval(vtable, ftable) prints.append(str(result)) print(" ".join(prints)) return vtable, ftable, " ".join(prints) elif self.builtin == "input": if not self.args == []: prints = [] for arg in self.args: vtable, ftable, result = arg.eval(vtable, ftable) prints.append(str(result)) print(" ".join(prints), end="") else: print("> ", end="") result = input() return vtable, ftable, result elif self.builtin == "random": vtable, ftable, r1 = self.args[0].eval(vtable, ftable) vtable, ftable, r2 = self.args[1].eval(vtable, ftable) r = random.randint(r1, r2) return vtable, ftable, r else: raise Exception(f"Unknown builtin {self.builtin}") class Do(BaseBox): def __init__(self, command: Command): self.command = command def __repr__(self) -> str: return f"do({self.command})" def eval(self, vtable, ftable): return self.command.eval(vtable, ftable) class Statement(BaseBox): def eval(self, vtable, ftable): return vtable, ftable, None def __repr__(self) -> str: return f"statement()" class StatementSet(Statement): def __init__(self, expression: Exp, variable: str): self.expression = expression self.variable_name = variable def __repr__(self) -> str: return f"set({self.expression}, {self.variable_name})" def eval(self, vtable, ftable): vtable, ftable, result = self.expression.eval( vtable, ftable ) vtable[self.variable_name] = result return vtable, ftable, result class StatementDefine(Statement): def __init__(self, function_name : str, parameters: int, statement: Statement): self.function_name = function_name self.statement = statement self.parameters = parameters def __repr__(self) -> str: return f"define({self.function_name}, {self.statement}, {self.parameters})" def eval(self, vtable, ftable): ftable[self.function_name] = (self.parameters, self.statement) return vtable, ftable, (self.parameters, self.statement) class StatementReturn(Statement): def __init__(self, expression: Exp): self.value = expression def __repr__(self) -> str: return f"return({self.value})" def eval(self, vtable, ftable): vtable, ftable, result = self.value.eval(vtable, ftable) vtable["#return"] = result return vtable, ftable, result class Scope(Statement): def __init__(self, statements: list[Statement]): self.statements = statements def __repr__(self) -> str: return f"scope([{rep_join(self.statements)}])" def eval(self, vtable, ftable): result = None for statement in self.statements: vtable, ftable, _ = statement.eval(vtable, ftable) if "#return" in vtable: result = vtable["#return"] del vtable["#return"] break return vtable, ftable, result class Call(Statement): def __init__(self, function_name: str, arguments: list[Exp]): self.function_name = function_name self.arguments = arguments def __repr__(self) -> str: return f"call({self.function_name}, {self.arguments})" def eval(self, vtable, ftable): assert len(self.arguments) == ftable[self.function_name][0] args = [] for argument in self.arguments: vtable, ftable, result = argument.eval(vtable,ftable) args.append(result) n = vtable["#ARGS"]-1 for i, arg in enumerate(args): vtable[f"#{n+len(args)-i}"] = arg vtable["#ARGS"] += len(args) vtable, ftable, result = ftable[self.function_name][1].eval(vtable, ftable) if "#return" in vtable: del vtable["#return"] vtable["#ARGS"] -= len(args) return vtable, ftable, result class StatementIf(Statement): def __init__(self, statement: Statement, condition): self.statement = statement self.condition = condition def __repr__(self) -> str: return f"if({self.statement}, {self.condition})" def eval(self, vtable, ftable): vtable, ftable, result = self.condition.eval(vtable, ftable) if result: return self.statement.eval(vtable, ftable) else: return vtable, ftable, result class StatementBecause(Statement): def __init__(self, statement: Statement, condition): self.statement = statement self.condition = condition def __repr__(self) -> str: return f"if({self.statement}, {self.condition})" def eval(self, vtable, ftable): vtable, ftable, result = self.condition.eval(vtable, ftable) if result: return self.statement.eval(vtable, ftable) else: print("C001: Because assertion incorrect") exit() class StatementUntil(Statement): def __init__(self, statement: Statement, condition): self.statement = statement self.condition = condition def __repr__(self) -> str: return f"until({self.statement}, {self.condition})" def eval(self, vtable, ftable): while True: vtable, ftable, result = self.condition.eval(vtable, ftable) if not result: vtable, ftable, _ = self.statement.eval(vtable, ftable) if "#return" in vtable: result = vtable["#return"] del vtable["#return"] return vtable, ftable, result else: return vtable, ftable, result class Maybe(BaseBox): def __init__(self, statement : Statement): self.statement = statement def __repr__(self) -> str: return f"maybe({self.statement})" def eval(self, vtable, ftable): if random.randint(0,1) == 1: return self.statement.eval(vtable,ftable) else: return vtable, ftable, False class Program(BaseBox): def __init__(self, statements: list[Statement]) -> None: self.statements = statements random.seed(str(self)) r = random.randint(1,20) if r == 1: print("E004: Random compiler error") exit() random.seed() def __repr__(self) -> str: statements_string = f"program([{rep_join(self.statements)}])" return statements_string def eval(self, *_): vtable = {"#ARGS": 0} ftable = {} for statement in self.statements: vtable, ftable, _ = statement.eval(vtable, ftable) if "#return" in vtable: break