From 5ce2c5a281888f1d378ff2ed7c381085cebc6c18 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Thu, 11 Dec 2025 01:09:28 +0100 Subject: [PATCH] Finish compiler refactoring --- Project-02-03/compiler.py | 185 ++++++++++++------------ Project-02-03/tramcodes/geq.tram | 18 +++ Project-02-03/tramcodes/homework.tram | 46 +++--- Project-02-03/triplaprograms/geq.tripla | 1 + 4 files changed, 137 insertions(+), 113 deletions(-) create mode 100644 Project-02-03/tramcodes/geq.tram create mode 100644 Project-02-03/triplaprograms/geq.tripla diff --git a/Project-02-03/compiler.py b/Project-02-03/compiler.py index f06f2d8..2302e3c 100644 --- a/Project-02-03/compiler.py +++ b/Project-02-03/compiler.py @@ -8,163 +8,168 @@ def new_label(): label_counter += 1 return Label(text=f"L{label_counter}") +# Rules E1, E2 def elab_def(decls, rho, nl): r = dict(rho) for d in decls: r = d.elab(r, nl) return r +# Rule K6 class CONST(syntax.CONST): def code(self, rho, nl): return [const(self.value)] +# Rule K3 class ID(syntax.ID): def code(self, rho, nl): - k, nlp = rho[self.name] - return [load(k, nl - nlp)] + k, nl_def = rho[self.name] + return [load(k, nl - nl_def)] +# Rule K7 class AOP(syntax.AOP): def code(self, rho, nl): c1 = self.arg1.code(rho, nl) c2 = self.arg2.code(rho, nl) - op = self.operator - if op == "+": - return c1 + c2 + [add()] - if op == "-": - return c1 + c2 + [sub()] - if op == "*": - return c1 + c2 + [mul()] - return c1 + c2 + [div()] + match self.operator: + case "+": + return c1 + c2 + [add()] + case "-": + return c1 + c2 + [sub()] + case "*": + return c1 + c2 + [mul()] + case "/": + return c1 + c2 + [div()] + case _: + raise Exception(f"Unknown AOP operator.") + +# Rule K5 class COMP(syntax.COMP): def code(self, rho, nl): c1 = self.arg1.code(rho, nl) c2 = self.arg2.code(rho, nl) - if self.operator == "<": - return c1 + c2 + [lt()] - if self.operator == ">": - return c1 + c2 + [gt()] - if self.operator == "<=": - return c1 + c2 + [gt(), nop()] # NOT IMPLEMENTED IN TRAM - if self.operator == ">=": - return c1 + c2 + [lt(), nop()] # NOT IMPLEMENTED IN TRAM - return c1 + c2 + match self.operator: + case "<": + return c1 + c2 + [lt()] + case ">": + return c1 + c2 + [gt()] + case "<=": + # leq is not implemented in vistra.tram + return LOP("||", COMP("<", self.arg1, self.arg2), EQOP("==", self.arg1, self.arg2)).code(rho, nl) + case ">=": + # geq is not implemented in vistra.tram + return LOP("||", COMP(">", self.arg1, self.arg2), EQOP("==", self.arg1, self.arg2)).code(rho, nl) + case _: + raise ValueError(f"Unknown COMP operator.") + +# Rule K5 class EQOP(syntax.EQOP): def code(self, rho, nl): c1 = self.arg1.code(rho, nl) c2 = self.arg2.code(rho, nl) - if self.operator == "==": - return c1 + c2 + [eq()] - return c1 + c2 + [neq()] + match self.operator: + case "==": + return c1 + c2 + [eq()] + case "!=": + return c1 + c2 + [neq()] + case _: + raise ValueError(f"Unknown EQOP operator.") + +# see lecture code generation AND/OR class LOP(syntax.LOP): def code(self, rho, nl): - # TRIPLA: logical operators must be done by short-circuiting (not strict) - # but your TRAM has no boolean ops, so fallback to == and pop c1 = self.arg1.code(rho, nl) c2 = self.arg2.code(rho, nl) - if self.operator == "&&": - l = new_label() - return ( - c1 + - [ifzero(l)] + - c2 + - [ifzero(l)] + - [const(1)] + - [nop(assigned_label=l)] - ) - if self.operator == "||": - l = new_label() - return ( - c1 + - [ifzero(l)] + - [const(1)] + - [goto(l)] + - [nop(assigned_label=l)] + - c2 - ) - return c1 + c2 + l1 = new_label() + l2 = new_label() + match self.operator: + case "&&": + return c1 + [ifzero(l1)] + [const(1)] + c2 + [mul()] + [goto(l2)] + [nop(l1)] + [const(0)] + [nop(l2)] + case "||": + return c1 + [ifzero(l1)] + [const(1)] + [goto(l2)] + [nop(l1)] + c2 + [nop(l2)] + case _: + raise ValueError("Unknown LOP operator.") + +# Rule K4 class ASSIGN(syntax.ASSIGN): def code(self, rho, nl): c = self.expr.code(rho, nl) - k, nlp = rho[self.var.name] - d = nl - nlp - return c + [store(k, d), load(k, d)] + k, nl_def = rho[self.var.name] + dif = nl - nl_def + return c + [store(k, dif), load(k, dif)] +# Rule K9 class SEQ(syntax.SEQ): def code(self, rho, nl): c1 = self.exp1.code(rho, nl) c2 = self.exp2.code(rho, nl) return c1 + [pop()] + c2 +# Rule K10 class IF(syntax.IF): def code(self, rho, nl): l1 = new_label() l2 = new_label() + c1 = self.cond.code(rho, nl) c2 = self.exp1.code(rho, nl) c3 = self.exp2.code(rho, nl) - return ( - c1 + - [ifzero(l1)] + - c2 + - [goto(l2)] + - [nop(assigned_label=l1)] + - c3 + - [nop(assigned_label=l2)] - ) + return c1 + [ifzero(l1)] + c2 + [goto(l2)] + [nop(assigned_label=l1)] + c3 + [nop(assigned_label=l2)] + +# Rule K11 class WHILE(syntax.WHILE): def code(self, rho, nl): - l1 = new_label() # loop header - l2 = new_label() # exit - l3 = new_label() # repeat - l4 = new_label() # evaluate body - return ( - self.cond.code(rho, nl) - + [ifzero(l2), goto(l4)] - + [nop(assigned_label=l1)] - + self.cond.code(rho, nl) - + [ifzero(l3), pop()] - + [nop(assigned_label=l4)] - + self.body.code(rho, nl) - + [goto(l1)] - + [nop(assigned_label=l2), const(None), nop(assigned_label=l3)] - ) + l1 = new_label() + l2 = new_label() + l3 = new_label() + l4 = new_label() + c1 = self.cond.code(rho, nl) + c2 = self.body.code(rho, nl) + + return c1 + [ifzero(l3), goto(l4)] + [nop(l1)] + c1 + [ifzero(l2), pop()] + [nop(l4)] + c2 + [goto(l1)] + [nop(l3), const(None), nop(l2)] + +# Rule K2 class LET(syntax.LET): def code(self, rho, nl): l = new_label() - rho2 = elab_def(self.decl, rho, nl) - cd = [] - for d in self.decl: - cd += d.code(rho2, nl) - ce = self.body.code(rho2, nl) - return [goto(l)] + cd + [nop(assigned_label=l)] + ce + # Normalize to list + decls = self.decl if isinstance(self.decl, list) else [self.decl] + + shared_rho = elab_def(decls, rho, nl) + ds = [] + for d in decls: + ds += d.code(shared_rho, nl) + + es = self.body.code(shared_rho, nl) + return [goto(l)] + ds + [nop(assigned_label=l)] + es + +# Rule K13 class DECL(syntax.DECL): def elab(self, rho, nl): - r = dict(rho) - r[self.f_name] = (new_label(), nl) - return r + nested_rho = dict(rho) + nested_rho[self.f_name] = (new_label(), nl) + return nested_rho def code(self, rho, nl): l, _ = rho[self.f_name] - rho2 = dict(rho) + nested_rho = dict(rho) for i, p in enumerate(self.params): - rho2[p] = (i, nl + 1) - return ( - [nop(assigned_label=l)] - + self.body.code(rho2, nl + 1) - + [ireturn()] - ) + nested_rho[p] = (i, nl + 1) + c = self.body.code(nested_rho, nl + 1) + return [nop(assigned_label=l)] + c + [ireturn()] +# Rule K12 class CALL(syntax.CALL): def code(self, rho, nl): c = [] for a in self.arg: c += a.code(rho, nl) - l, nlp = rho[self.f_name] - return c + [invoke(len(self.arg), l, nl - nlp)] + l, nl_def = rho[self.f_name] + return c + [invoke(len(self.arg), l, nl - nl_def)] diff --git a/Project-02-03/tramcodes/geq.tram b/Project-02-03/tramcodes/geq.tram new file mode 100644 index 0000000..837bc61 --- /dev/null +++ b/Project-02-03/tramcodes/geq.tram @@ -0,0 +1,18 @@ + CONST 1 + CONST 0 + GT + IFZERO L3 + CONST 1 + GOTO L4 +L3: NOP + CONST 1 + CONST 0 + EQ +L4: NOP + IFZERO L1 + CONST 1 + GOTO L2 +L1: NOP + CONST 0 +L2: NOP + HALT diff --git a/Project-02-03/tramcodes/homework.tram b/Project-02-03/tramcodes/homework.tram index 4cfdd5e..101989a 100644 --- a/Project-02-03/tramcodes/homework.tram +++ b/Project-02-03/tramcodes/homework.tram @@ -1,47 +1,47 @@ - GOTO L1 -L2: NOP + GOTO L13 +L14: NOP LOAD 0 0 CONST 0 EQ - IFZERO L4 - GOTO L6 -L7: NOP + IFZERO L16 + GOTO L18 +L19: NOP LOAD 0 0 LOAD 1 1 MUL LOAD 1 0 ADD RETURN -L6: NOP +L18: NOP CONST 2 LOAD 1 0 MUL LOAD 0 0 - INVOKE 2 L7 0 - GOTO L5 -L4: NOP + INVOKE 2 L19 0 + GOTO L17 +L16: NOP LOAD 0 0 CONST 1 ADD -L5: NOP +L17: NOP RETURN -L3: NOP +L15: NOP LOAD 1 0 CONST 0 GT - IFZERO L9 - GOTO L11 -L8: NOP + IFZERO L22 + GOTO L23 +L20: NOP LOAD 1 0 CONST 0 GT - IFZERO L10 + IFZERO L21 POP -L11: NOP +L23: NOP LOAD 0 0 LOAD 0 0 LOAD 1 0 - INVOKE 2 L2 1 + INVOKE 2 L14 1 MUL STORE 0 0 LOAD 0 0 @@ -51,21 +51,21 @@ L11: NOP SUB STORE 1 0 LOAD 1 0 - GOTO L8 -L9: NOP + GOTO L20 +L22: NOP CONST None -L10: NOP +L21: NOP POP LOAD 0 0 CONST 42 ADD RETURN -L1: NOP +L13: NOP CONST 1 CONST 2 - INVOKE 2 L2 0 + INVOKE 2 L14 0 POP CONST 3 CONST 3 - INVOKE 2 L3 0 + INVOKE 2 L15 0 HALT diff --git a/Project-02-03/triplaprograms/geq.tripla b/Project-02-03/triplaprograms/geq.tripla new file mode 100644 index 0000000..ea0b903 --- /dev/null +++ b/Project-02-03/triplaprograms/geq.tripla @@ -0,0 +1 @@ +if 1 >= 0 then 1 else 0 \ No newline at end of file