Files
Construction-of-Compilers/Project-02-03-04/cfg_build.py
Jan-Niclas Loosen dfd4b4f305 Wire offline message
2026-01-17 00:14:44 +01:00

182 lines
4.0 KiB
Python

from cfg.CFG_Node import (
CFG_Node,
CFG_CALL,
CFG_RETURN,
CFG_DIAMOND,
)
import compiler
import syntax
# -------------------------------------------------
# Global function environment
# -------------------------------------------------
FUNCTIONS = {} # name -> (func_start, func_end)
# -------------------------------------------------
# Expressions — NO CFG NODES
# -------------------------------------------------
class CONST(compiler.CONST):
def cfa(self, pred, end):
return pred
class ID(compiler.ID):
def cfa(self, pred, end):
return pred
class AOP(compiler.AOP):
def cfa(self, pred, end):
return pred
class COMP(compiler.COMP):
def cfa(self, pred, end):
return pred
class EQOP(compiler.EQOP):
def cfa(self, pred, end):
return pred
class LOP(compiler.LOP):
def cfa(self, pred, end):
return pred
# -------------------------------------------------
# Statements
# -------------------------------------------------
class ASSIGN(compiler.ASSIGN):
def cfa(self, pred, end):
n = CFG_Node(self)
pred.add_child(n)
return n
class SEQ(compiler.SEQ):
def cfa(self, pred, end):
mid = self.exp1.cfa(pred, end)
if mid is None:
return None
return self.exp2.cfa(mid, end)
class IF(compiler.IF):
def cfa(self, pred, end):
# decision node (condition only)
cond = CFG_DIAMOND(self.cond)
pred.add_child(cond)
# explicit branch entries
then_entry = CFG_Node()
else_entry = CFG_Node()
cond.add_child(then_entry)
cond.add_child(else_entry)
# join node
join = CFG_Node()
then_end = self.exp1.cfa(then_entry, end)
else_end = self.exp2.cfa(else_entry, end)
if then_end is not None:
then_end.add_child(join)
if else_end is not None:
else_end.add_child(join)
return join
class WHILE(compiler.WHILE):
def cfa(self, pred, end):
# loop condition
cond = CFG_DIAMOND(self.cond)
pred.add_child(cond)
# body entry
body_entry = CFG_Node()
cond.add_child(body_entry)
body_end = self.body.cfa(body_entry, end)
if body_end is not None:
body_end.add_child(cond) # back-edge
# loop exit
after = CFG_Node()
cond.add_child(after)
return after
# -------------------------------------------------
# Functions / calls (interprocedural CFG)
# -------------------------------------------------
class CALL(compiler.CALL):
def cfa(self, pred, end):
call = CFG_CALL(self)
pred.add_child(call)
# continuation after return
cont = CFG_Node()
if self.f_name not in FUNCTIONS:
raise RuntimeError(f"Call to undefined function '{self.f_name}'")
f_start, f_end = FUNCTIONS[self.f_name]
# call → function entry
call.add_child(f_start)
# function exit → continuation
f_end.add_child(cont)
return cont
class DECL(compiler.DECL):
def cfa(self, pred, end):
# function entry / exit
f_start = CFG_Node(self)
f_end = CFG_Node(self)
# register function
FUNCTIONS[self.f_name] = (f_start, f_end)
# build function body
body_end = self.body.cfa(f_start, f_end)
if body_end is not None:
body_end.add_child(f_end)
# function declaration does not alter current control flow
return pred
class LET(compiler.LET):
def cfa(self, pred, end):
current = pred
decls = self.decl if isinstance(self.decl, list) else [self.decl]
for d in decls:
current = d.cfa(current, end)
if current is None:
return None
return self.body.cfa(current, end)
class RETURN(syntax.EXPRESSION):
def cfa(self, pred, end):
n = CFG_RETURN(self)
pred.add_child(n)
n.add_child(end) # return to function exit
return None # no fallthrough