try to improve diagrams

This commit is contained in:
Jan-Niclas Loosen
2026-01-21 21:58:56 +01:00
parent dfd4b4f305
commit 36ef2fd23d
4 changed files with 143 additions and 92 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

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

View File

@@ -0,0 +1,64 @@
digraph CFG {
node [fontname="Helvetica"];
n31 [label="START", shape=circle];
n31 -> n59;
n59 [label="f(2,3)", shape=box];
n59 -> n33;
n33 [label="f(['x', 'y', 'z']) { y = 2; z = 3; let g(['x']) { x = 7; if ((y > 0)) then { g(y) } else { x = 8 }; x } in (g(x) + x) }", shape=box];
n33 -> n35;
n35 [label="2", shape=box];
n35 -> n36;
n36 [label="y = 2", shape=box];
n36 -> n37;
n37 [label="3", shape=box];
n37 -> n38;
n38 [label="z = 3", shape=box];
n38 -> n55;
n55 [label="g(x)", shape=box];
n55 -> n39;
n39 [label="g(['x']) { x = 7; if ((y > 0)) then { g(y) } else { x = 8 }; x }", shape=box];
n39 -> n41;
n41 [label="7", shape=box];
n41 -> n42;
n42 [label="x = 7", shape=box];
n42 -> n43;
n43 [label="y", shape=box];
n43 -> n44;
n44 [label="0", shape=box];
n44 -> n45;
n45 [label="(y > 0)", shape=box];
n45 -> n46;
n46 [label="(y > 0)", shape=diamond];
n46 -> n47;
n47 [label="", shape=box];
n47 -> n50;
n50 [label="g(y)", shape=box];
n50 -> n39;
n46 -> n48;
n48 [label="", shape=box];
n48 -> n52;
n52 [label="8", shape=box];
n52 -> n53;
n53 [label="x = 8", shape=box];
n53 -> n49;
n49 [label="", shape=box];
n49 -> n54;
n54 [label="x", shape=box];
n54 -> n40;
n40 [label="g(['x']) { x = 7; if ((y > 0)) then { g(y) } else { x = 8 }; x }", shape=box];
n40 -> n51;
n51 [label="", shape=box];
n51 -> n49;
n40 -> n56;
n56 [label="", shape=box];
n56 -> n57;
n57 [label="x", shape=box];
n57 -> n58;
n58 [label="(g(x) + x)", shape=box];
n58 -> n34;
n34 [label="f(['x', 'y', 'z']) { y = 2; z = 3; let g(['x']) { x = 7; if ((y > 0)) then { g(y) } else { x = 8 }; x } in (g(x) + x) }", shape=box];
n34 -> n60;
n60 [label="", shape=box];
n60 -> n32;
n32 [label="END", shape=doublecircle];
}

View File

@@ -0,0 +1,11 @@
let f(x,y,z) {
y=2;
z=3;
let g(x) {
x=7;
if (y>0)
then g(y)
else x=8;
x
} in g(x)+x
} in f(2,3)