summaryrefslogtreecommitdiff
path: root/docs/chapter07
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2023-12-03 23:23:04 +0000
committerCliff Click <cliffc@acm.org>2026-10-08 16:52:27 -0700
commitf4d0e1583b9ae0237422640d8ee7eea5ce8aad6f (patch)
tree446229350a4406bccccf8eb222f96043a7467b31 /docs/chapter07
parentb6d5e06a2099749e867d3068aec9d555ab2fe693 (diff)
downloadSimple-f4d0e1583b9ae0237422640d8ee7eea5ce8aad6f.tar.gz
Simple-f4d0e1583b9ae0237422640d8ee7eea5ce8aad6f.zip
Chapter 7: While Statement
Co-authored-by: Dibyendu Majumdar <mobile@majumdar.org.uk> Co-authored-by: Thalia Archibald <thalia@archibald.dev> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: tannal <tannal2409@gmail.com> Co-authored-by: hadha <helsgithub15@gmail.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: RobertObkircher <robert.obkircher@gmail.com> Co-authored-by: Robert Obkircher <robert.obkircher@gmail.com> Co-authored-by: Jun Kawasaki <jun784@gmail.com>
Diffstat (limited to 'docs/chapter07')
-rw-r--r--docs/chapter07/07-grammar.ja.md91
-rw-r--r--docs/chapter07/07-grammar.md86
-rw-r--r--docs/chapter07/07-graph1.gv107
-rw-r--r--docs/chapter07/07-graph1.json27
-rw-r--r--docs/chapter07/07-graph1.svg122
-rw-r--r--docs/chapter07/07-graph2.gv62
-rw-r--r--docs/chapter07/07-graph2.json26
-rw-r--r--docs/chapter07/07-graph2.svg119
-rw-r--r--docs/chapter07/07-graph3.gv108
-rw-r--r--docs/chapter07/07-graph3.json40
-rw-r--r--docs/chapter07/07-graph3.svg175
-rw-r--r--docs/chapter07/07-graph4.gv96
-rw-r--r--docs/chapter07/07-graph4.json37
-rw-r--r--docs/chapter07/07-graph4.svg159
-rw-r--r--docs/chapter07/Makefile1
-rw-r--r--docs/chapter07/README.ja.md229
-rw-r--r--docs/chapter07/README.md250
17 files changed, 1735 insertions, 0 deletions
diff --git a/docs/chapter07/07-grammar.ja.md b/docs/chapter07/07-grammar.ja.md
new file mode 100644
index 00000000..a49e31e5
--- /dev/null
+++ b/docs/chapter07/07-grammar.ja.md
@@ -0,0 +1,91 @@
+# 第7章の文法
+
+[English](07-grammar.md) | 日本語
+
+この文書は [原文](07-grammar.md) の日本語訳です。
+
+
+[第7章に戻る](README.ja.md)
+
+```antlrv4
+grammar SimpleLanguage;
+
+program
+ : statement+ EOF
+ ;
+
+statement
+ : returnStatement
+ | declStatement
+ | blockStatment
+ | expressionStatement
+ | ifStatement
+ | whileStatement
+ ;
+
+whileStatement
+ : 'while' '(' expression ')' statement
+ ;
+
+ifStatement
+ : 'if' '(' expression ')' statement ('else' statement)?
+ ;
+
+
+expressionStatement
+ : IDENTIFIER '=' expression ';'
+ ;
+
+blockStatement
+ : '{' statement+ '}'
+ ;
+
+declStatement
+ : 'int' IDENTIFIER '=' expression ';'
+ ;
+
+returnStatement
+ : 'return' expression ';'
+ ;
+
+expression
+ : comparisonExpression
+ ;
+
+comparisonExpression
+ : additiveExpression (('==' | '!='| '>'| '<'| '>='| '<=') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-') unaryExpression
+ | primaryExpression
+ ;
+
+primaryExpression
+ : IDENTIFIER
+ | INTEGER_LITERAL
+ | 'true'
+ | 'false'
+ | '(' expression ')'
+ ;
+
+INTEGER_LITERAL
+ : [1-9][0-9]*
+ | [0]
+ ;
+
+IDENTIFIER
+ : NON_DIGIT (NON_DIGIT | DIGIT)*
+ ;
+
+NON_DIGIT: [a-zA-Z_];
+DEC_DIGIT: [0-9];
+```
diff --git a/docs/chapter07/07-grammar.md b/docs/chapter07/07-grammar.md
new file mode 100644
index 00000000..c09f499c
--- /dev/null
+++ b/docs/chapter07/07-grammar.md
@@ -0,0 +1,86 @@
+# Grammar for Chapter 7
+
+English | [日本語](07-grammar.ja.md)
+
+```antlrv4
+grammar SimpleLanguage;
+
+program
+ : statement+ EOF
+ ;
+
+statement
+ : returnStatement
+ | declStatement
+ | blockStatment
+ | expressionStatement
+ | ifStatement
+ | whileStatement
+ ;
+
+whileStatement
+ : 'while' '(' expression ')' statement
+ ;
+
+ifStatement
+ : 'if' '(' expression ')' statement ('else' statement)?
+ ;
+
+
+expressionStatement
+ : IDENTIFIER '=' expression ';'
+ ;
+
+blockStatement
+ : '{' statement+ '}'
+ ;
+
+declStatement
+ : 'int' IDENTIFIER '=' expression ';'
+ ;
+
+returnStatement
+ : 'return' expression ';'
+ ;
+
+expression
+ : comparisonExpression
+ ;
+
+comparisonExpression
+ : additiveExpression (('==' | '!='| '>'| '<'| '>='| '<=') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-') unaryExpression
+ | primaryExpression
+ ;
+
+primaryExpression
+ : IDENTIFIER
+ | INTEGER_LITERAL
+ | 'true'
+ | 'false'
+ | '(' expression ')'
+ ;
+
+INTEGER_LITERAL
+ : [1-9][0-9]*
+ | [0]
+ ;
+
+IDENTIFIER
+ : NON_DIGIT (NON_DIGIT | DIGIT)*
+ ;
+
+NON_DIGIT: [a-zA-Z_];
+DEC_DIGIT: [0-9];
+```
diff --git a/docs/chapter07/07-graph1.gv b/docs/chapter07/07-graph1.gv
new file mode 100644
index 00000000..5957a931
--- /dev/null
+++ b/docs/chapter07/07-graph1.gv
@@ -0,0 +1,107 @@
+digraph chapter07 {
+/*
+while(arg < 10) {
+ arg = arg + 1;
+
+}
+return arg;
+
+*/
+ rankdir=BT;
+ ordering="in";
+ concentrate="true";
+ compound="true";
+ subgraph cluster_Nodes {
+ Start2 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">Start</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">$ctrl</TD><TD PORT="p1">arg</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Loop6 [ shape=box style=filled fillcolor=yellow label="Loop" ];
+ Phi_arg8 [ style=filled fillcolor=lightyellow label="&phi;_arg" ];
+ Con_9 [ label="10" ];
+ LT10 [ label="<" ];
+ If11 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">If</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">True</TD><TD PORT="p1" BGCOLOR="yellow">False</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Con_15 [ label="1" ];
+ Add16 [ label="+" ];
+ { rank=same; Loop6;Phi_arg8;}
+ }
+ node [shape=plaintext];
+ subgraph cluster_Scope7_1 {
+ Scope7_1 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">2</TD></TR>
+ </TABLE>>];
+ subgraph cluster_Scope7_2 {
+ Scope7_2 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">1</TD></TR>
+ </TABLE>>];
+ subgraph cluster_Scope7_3 {
+ Scope7_3 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">0</TD><TD PORT="Scope7_3_$ctrl">$ctrl</TD><TD PORT="Scope7_3_arg">arg</TD></TR>
+ </TABLE>>];
+ }
+ }
+ }
+ node [shape=plaintext];
+ subgraph cluster_Scope1_1 {
+ Scope1_1 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">1</TD></TR>
+ </TABLE>>];
+ subgraph cluster_Scope1_2 {
+ Scope1_2 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">0</TD><TD PORT="Scope1_2_$ctrl">$ctrl</TD><TD PORT="Scope1_2_arg">arg</TD></TR>
+ </TABLE>>];
+ }
+ }
+ node [shape=plaintext];
+ subgraph cluster_Scope14_1 {
+ Scope14_1 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">1</TD></TR>
+ </TABLE>>];
+ subgraph cluster_Scope14_2 {
+ Scope14_2 [label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="cyan">0</TD><TD PORT="Scope14_2_$ctrl">$ctrl</TD><TD PORT="Scope14_2_arg">arg</TD></TR>
+ </TABLE>>];
+ }
+ }
+ edge [ fontname=Helvetica, fontsize=8 ];
+ Loop6 -> Start2:p0[taillabel=1 color=red];
+ Phi_arg8 -> Loop6 [style=dotted taillabel=0];
+ Phi_arg8 -> Start2:p1[taillabel=1];
+ LT10 -> Phi_arg8[taillabel=1];
+ LT10 -> Con_9[taillabel=2];
+ If11 -> Loop6[taillabel=0 color=red];
+ If11 -> LT10[taillabel=1];
+ Add16 -> Phi_arg8[taillabel=1];
+ Add16 -> Con_15[taillabel=2];
+ edge [style=dashed color=cornflowerblue];
+ Scope7_3:"Scope7_3_$ctrl" -> If11:p0;
+ Scope7_3:"Scope7_3_arg" -> Add16;
+ edge [style=dashed color=cornflowerblue];
+ Scope1_2:"Scope1_2_$ctrl" -> Loop6;
+ Scope1_2:"Scope1_2_arg" -> Phi_arg8;
+ edge [style=dashed color=cornflowerblue];
+ Scope14_2:"Scope14_2_$ctrl" -> If11:p1;
+ Scope14_2:"Scope14_2_arg" -> Phi_arg8;
+}
diff --git a/docs/chapter07/07-graph1.json b/docs/chapter07/07-graph1.json
new file mode 100644
index 00000000..0f2c6c4d
--- /dev/null
+++ b/docs/chapter07/07-graph1.json
@@ -0,0 +1,27 @@
+{
+ "ver": 1,
+ "comp": "chapter07/docs/07-graph1.svg",
+ "step": 1,
+ "roots": [],
+ "scope": 0,
+ "nodes": [
+ {"id": 1, "label": "Scope", "type": null, "kind": "SCOPE", "edges": [{"idx": 0, "def": 18, "role": "ASSOC", "label": "3: $ctrl"}, {"idx": 1, "def": 16, "role": "ASSOC", "label": "3: arg"}], "proj": null},
+ {"id": 2, "label": "Start", "type": null, "kind": "CTRL", "edges": [], "proj": null},
+ {"id": 5, "label": "Scope", "type": null, "kind": "SCOPE", "edges": [{"idx": 0, "def": 6, "role": "ASSOC", "label": "2: $ctrl"}, {"idx": 1, "def": 8, "role": "ASSOC", "label": "2: arg"}], "proj": null},
+ {"id": 6, "label": "Loop", "type": null, "kind": "LOOP", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 14, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 8, "label": "φ_arg", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 6, "role": "ASSOC", "label": null}, {"idx": 1, "def": 17, "role": "DATA", "label": null}], "proj": null},
+ {"id": 9, "label": "10", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 10, "label": "<", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 8, "role": "DATA", "label": null}, {"idx": 2, "def": 9, "role": "DATA", "label": null}], "proj": null},
+ {"id": 11, "label": "If", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 6, "role": "CTRL", "label": null}, {"idx": 1, "def": 10, "role": "DATA", "label": null}], "proj": null},
+ {"id": 12, "label": "Scope", "type": null, "kind": "SCOPE", "edges": [{"idx": 0, "def": 19, "role": "ASSOC", "label": "2: $ctrl"}, {"idx": 1, "def": 8, "role": "ASSOC", "label": "2: arg"}], "proj": null},
+ {"id": 14, "label": "$ctrl", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 2, "role": "CTRL", "label": null}], "proj": {"par": 2, "idx": 0}},
+ {"id": 15, "label": "1", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 16, "label": "+", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 8, "role": "DATA", "label": null}, {"idx": 2, "def": 15, "role": "DATA", "label": null}], "proj": null},
+ {"id": 17, "label": "arg", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 2, "role": "DATA", "label": null}], "proj": {"par": 2, "idx": 1}},
+ {"id": 18, "label": "True", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 11, "role": "CTRL", "label": null}], "proj": {"par": 11, "idx": 0}},
+ {"id": 19, "label": "False", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 11, "role": "CTRL", "label": null}], "proj": {"par": 11, "idx": 1}}
+ ],
+ "groups": [
+ {"id": 6, "par": 0, "nodes": [6, 8]}
+ ]
+}
diff --git a/docs/chapter07/07-graph1.svg b/docs/chapter07/07-graph1.svg
new file mode 100644
index 00000000..d5de1351
--- /dev/null
+++ b/docs/chapter07/07-graph1.svg
@@ -0,0 +1,122 @@
+<svg aria-label="Compiler graph" width="848" height="708" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 848 708" style="font-family: sans-serif;"><style>
+ html, body { height:100%; margin:0; font-family:sans-serif; }
+ .viewer { height:100%; display:grid; grid-template-rows:auto minmax(0,1fr) auto; }
+ .source { padding:8px; border-bottom:1px solid #ccc; }
+ #program { width:min(600px,80vw); vertical-align:middle; }
+ #status { margin-top:4px; }
+ #graph { min-height:0; overflow:hidden; position:relative; background:#fafbfc; }
+ #elk { width:100%; height:100%; }
+ #graph svg { display:block; }
+ .tools { display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:8px; font-size:12px; }
+ .hint { color:#616c78; }
+ .ctrl { color:#b63838; } .mem { color:#2867b2; } .data { color:#536477; }
+ .edge { fill:none; stroke-width:1.5; }
+ .edge.CTRL { stroke-width:2; }
+ .edge.ASSOC { stroke-dasharray:4 4; }
+ .edge.PARSER { stroke-dasharray:5 4; }
+ .parser rect { fill:#f6f0ff; stroke:#7956a5; stroke-width:1.5; stroke-dasharray:5 4; }
+ .edge.selected { stroke-width:3.5; }
+ .edge.bundle { stroke-width:4; }
+ .edge.bundle.selected { stroke-width:6; }
+ .edge-hit { fill:none; stroke:transparent; stroke-width:12; vector-effect:non-scaling-stroke; pointer-events:stroke; cursor:pointer; }
+ .edge-jump { fill:transparent; pointer-events:all; cursor:pointer; }
+ .node { cursor:pointer; }
+ .box { stroke:#758191; stroke-width:1; }
+ .node.held .box { stroke-dasharray:5 3; }
+ .node.near .box { stroke:#c18221; stroke-width:2; }
+ .node.focus .box { stroke:#9c4900; stroke-width:3; }
+ .node.selected .box { stroke:#1569b9; stroke-width:3; }
+ .node.folded .box { stroke-width:4; }
+ .node.folding .box { fill:#f4ccd7; }
+ .fold-tag { font-size:9px; font-weight:700; fill:#963751; }
+ .peep-area { fill:#fff3d9; fill-opacity:.8; stroke:#c18221; stroke-width:2; pointer-events:none; }
+ .node.peep-edit .box { stroke:#9c4900; stroke-width:3; }
+ .node.error .box { stroke:#b42318; stroke-width:4; }
+ #event.error { color:#b42318; font-weight:600; white-space:pre-wrap; overflow-wrap:anywhere; }
+ .peep-ghosts { pointer-events:none; }
+ .region-box { stroke:#9da9b8; stroke-width:1.5; }
+ .fold-btn { cursor:pointer; }
+ .fold-btn rect { fill:white; stroke:#596674; }
+ .fold-btn text { font-size:14px; fill:#152330; }
+ .fold-btn:focus rect { stroke:#1569b9; stroke-width:2; }
+ .shortcut { cursor:pointer; }
+ .shortcut path { fill:none; stroke-width:1.5; }
+ .shortcut rect { fill:#fff8ec; stroke:#b63838; stroke-dasharray:3 2; }
+ .shortcut text { font-size:11px; fill:#783030; }
+ .shortcut.selected rect, .shortcut:focus rect { stroke-width:2.5; }
+ .label { font-size:12px; fill:#152330; font-weight:600; }
+ .node-id { font-size:10px; fill:#596674; font-weight:400; }
+ .type { font-size:11px; fill:#455768; }
+ .port { stroke:#536477; stroke-width:1; }
+ .row { stroke:#b6afcf; stroke-width:1; }
+ .bind { font-size:12px; fill:#152330; }
+ .slot { font-size:10px; fill:#364352; paint-order:stroke; stroke:#fafbfc; stroke-width:3; }
+ #detail { position:absolute; top:8px; right:8px; margin:0; padding:12px; max-width:320px;
+ max-height:45%; overflow:auto; white-space:pre-wrap; overflow-wrap:anywhere;
+ background:#fffffff2; border:1px solid #bac5d0; border-radius:4px; font-size:12px; }
+ .controls { display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:6px; padding:6px; }
+ .button { color:white; background:black; font-size:20px; min-width:30px; }
+ .button:disabled { background:grey; }
+ .count { font-size:20px; min-width:35px; text-align:center; }
+ #N { width:6ch; }
+ #scrub { width:clamp(120px,35vw,600px); }
+ </style><defs><marker id="arrow-CTRL" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#b63838"/></marker><marker id="arrow-DATA" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#536477"/></marker><marker id="arrow-MEM" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#2867b2"/></marker><marker id="arrow-ASSOC" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#9396a3"/></marker><marker id="arrow-PARSER" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#7956a5"/></marker></defs><rect width="100%" height="100%" fill="rgb(250, 251, 252)"/><g><g class="regions" style="opacity: 1;"><g class="region" transform="translate(32,171)"><rect class="region-box" rx="8" width="296" height="102" fill="#f1f6fc"/></g></g><rect class="peep-area" rx="36" style="opacity: 0;"/><g class="edges"><path class="edge ASSOC" id="e1i0" d="M84,527 C84,551 735.5,524 735.5,548" stroke="#9396a3" marker-start="url(#arrow-ASSOC)" style="opacity: 1;"><title>#1[0] → #18 · ASSOC · 3: $ctrl</title></path><path class="edge ASSOC" id="e1i1" d="M256,379 C256,403 795.5,524 795.5,548" stroke="#9396a3" marker-start="url(#arrow-ASSOC)" style="opacity: 1;"><title>#1[1] → #16 · ASSOC · 3: arg</title></path><path class="edge ASSOC" id="e5i0" d="M104,258 C104,282 735.5,255 735.5,279" stroke="#9396a3" marker-start="url(#arrow-ASSOC)" style="opacity: 1;"><title>#5[0] → #6 · ASSOC · 2: $ctrl</title></path><path class="edge ASSOC" id="e5i1" d="M256,258 C256,282 795.5,255 795.5,279" stroke="#9396a3" marker-start="url(#arrow-ASSOC)" style="opacity: 1;"><title>#5[1] → #8 · ASSOC · 2: arg</title></path><path class="edge CTRL" id="e6i1" d="M124,118 L124,205" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#6[1] → #14 · CTRL</title></path><path class="edge DATA" id="e8i1" d="M244,118 L244,123 Q244,128 249,128 L250,128 Q256,128 256,134 L256,205" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#8[1] → #17 · DATA</title></path><path class="edge DATA" id="e10i1" d="M256,261 L256,290 Q256,298 264,298 L400,298 Q408,298 408,306 L408,326" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#10[1] → #8 · DATA</title></path><path class="edge DATA" id="e10i2" d="M572,278 L572,283 Q572,288 567,288 L446,288 Q438,288 438,296 L438,326" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#10[2] → #9 · DATA</title></path><path class="edge CTRL" id="e11i0" d="M104,261 L104,430" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#11[0] → #6 · CTRL</title></path><path class="edge DATA" id="e11i1" d="M408,382 L408,387 Q408,392 403,392 L192,392 Q184,392 184,400 L184,430" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#11[1] → #10 · DATA</title></path><path class="edge ASSOC" id="e12i0" d="M204,527 C204,551 735.5,604 735.5,628" stroke="#9396a3" marker-start="url(#arrow-ASSOC)" style="opacity: 1;"><title>#12[0] → #19 · ASSOC · 2: $ctrl</title></path><path class="edge ASSOC" id="e12i1" d="M256,258 C256,282 795.5,604 795.5,628" stroke="#9396a3" marker-start="url(#arrow-ASSOC)" style="opacity: 1;"><title>#12[1] → #8 · ASSOC · 2: arg</title></path><path class="edge DATA" id="e16i1" d="M256,261 L256,326" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#16[1] → #8 · DATA</title></path><path class="edge DATA" id="e16i2" d="M420,278 L420,283 Q420,288 415,288 L294,288 Q286,288 286,296 L286,326" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#16[2] → #15 · DATA</title></path></g><g class="edge-hits"/><g class="parser" style="opacity: 1;"/><g class="nodes"><g class="node" id="n1" data-id="1" transform="translate(704,548)" style="opacity: 1;"><path class="box" d="M4,0H116Q120,0 120,4V52Q120,56 116,56H4Q0,56 0,52V4Q0,0 4,0Z" fill="#e5e0ff"/><text class="label" text-anchor="middle" x="60" y="48"><tspan>Scope</tspan><tspan class="node-id">  #1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#1 Scope
+Type not computed
+
+[0] → #18 ASSOC 3: $ctrl
+[1] → #16 ASSOC 3: arg</title><line class="row" x1="0" x2="120" y1="30" y2="30"/><line class="row" x1="63" x2="63" y1="0" y2="30"/><text class="bind" x="31.5" y="20" text-anchor="middle">3: $ctrl</text><text class="bind" x="91.5" y="20" text-anchor="middle">3: arg</text><circle class="port" cx="31.5" cy="0" r="3" fill="#536477"/><circle class="port" cx="91.5" cy="0" r="3" fill="#536477"/><text class="slot" x="31.5" y="-7" text-anchor="middle">0</text><text class="slot" x="91.5" y="-7" text-anchor="middle">1</text></g><g class="node" id="n2" data-id="2" transform="translate(64,24)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>Start</tspan><tspan class="node-id">  #2</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#2 Start
+Type not computed
+
+</title></g><g class="node" id="n5" data-id="5" transform="translate(704,279)" style="opacity: 1;"><path class="box" d="M4,0H116Q120,0 120,4V52Q120,56 116,56H4Q0,56 0,52V4Q0,0 4,0Z" fill="#e5e0ff"/><text class="label" text-anchor="middle" x="60" y="48"><tspan>Scope</tspan><tspan class="node-id">  #5</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#5 Scope
+Type not computed
+
+[0] → #6 ASSOC 2: $ctrl
+[1] → #8 ASSOC 2: arg</title><line class="row" x1="0" x2="120" y1="30" y2="30"/><line class="row" x1="63" x2="63" y1="0" y2="30"/><text class="bind" x="31.5" y="20" text-anchor="middle">2: $ctrl</text><text class="bind" x="91.5" y="20" text-anchor="middle">2: arg</text><circle class="port" cx="31.5" cy="0" r="3" fill="#536477"/><circle class="port" cx="91.5" cy="0" r="3" fill="#536477"/><text class="slot" x="31.5" y="-7" text-anchor="middle">0</text><text class="slot" x="91.5" y="-7" text-anchor="middle">1</text></g><g class="node" id="n6" data-id="6" transform="translate(44,211)" style="opacity: 1;"><path class="box" d="M20,0H100Q120,0 120,20V24Q120,44 100,44H20Q0,44 0,24V20Q0,0 20,0Z" fill="#ffe0ab"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Loop</tspan><tspan class="node-id">  #6</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#6 Loop
+Type not computed
+
+[0] → — DATA
+[1] → #14 CTRL</title><circle class="port" cx="40" cy="-3" r="3" fill="white"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><text class="slot" x="40" y="-10" text-anchor="middle">0</text><text class="slot" x="80" y="-10" text-anchor="middle">1</text></g><g class="node" id="n8" data-id="8" transform="translate(196,211)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_arg</tspan><tspan class="node-id">  #8</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#8 φ_arg
+Type not computed
+
+[0] → #6 ASSOC
+[1] → #17 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #6</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text></g><g class="node" id="n9" data-id="9" transform="translate(512,228)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>10</tspan><tspan class="node-id">  #9</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#9 10
+Type not computed
+
+</title></g><g class="node" id="n10" data-id="10" transform="translate(348,332)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>&lt;</tspan><tspan class="node-id">  #10</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#10 &lt;
+Type not computed
+
+[0] → — DATA
+[1] → #8 DATA
+[2] → #9 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n11" data-id="11" transform="translate(24,436)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>If</tspan><tspan class="node-id">  #11</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#11 If
+Type not computed
+
+[0] → #6 CTRL
+[1] → #10 DATA</title><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><circle class="port" cx="160" cy="-3" r="3" fill="#536477"/><text class="slot" x="80" y="-10" text-anchor="middle">0</text><text class="slot" x="160" y="-10" text-anchor="middle">1</text></g><g class="node" id="n12" data-id="12" transform="translate(704,628)" style="opacity: 1;"><path class="box" d="M4,0H116Q120,0 120,4V52Q120,56 116,56H4Q0,56 0,52V4Q0,0 4,0Z" fill="#e5e0ff"/><text class="label" text-anchor="middle" x="60" y="48"><tspan>Scope</tspan><tspan class="node-id">  #12</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#12 Scope
+Type not computed
+
+[0] → #19 ASSOC 2: $ctrl
+[1] → #8 ASSOC 2: arg</title><line class="row" x1="0" x2="120" y1="30" y2="30"/><line class="row" x1="63" x2="63" y1="0" y2="30"/><text class="bind" x="31.5" y="20" text-anchor="middle">2: $ctrl</text><text class="bind" x="91.5" y="20" text-anchor="middle">2: arg</text><circle class="port" cx="31.5" cy="0" r="3" fill="#536477"/><circle class="port" cx="91.5" cy="0" r="3" fill="#536477"/><text class="slot" x="31.5" y="-7" text-anchor="middle">0</text><text class="slot" x="91.5" y="-7" text-anchor="middle">1</text></g><g class="node" id="n14" data-id="14" transform="translate(64,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>$ctrl</tspan><tspan class="node-id">  #14/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#14 $ctrl
+Projection 0 of #2
+Type not computed
+
+[0] → #2 CTRL</title></g><g class="node" id="n15" data-id="15" transform="translate(360,228)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>1</tspan><tspan class="node-id">  #15</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#15 1
+Type not computed
+
+</title></g><g class="node" id="n16" data-id="16" transform="translate(196,332)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>+</tspan><tspan class="node-id">  #16</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#16 +
+Type not computed
+
+[0] → — DATA
+[1] → #8 DATA
+[2] → #15 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n17" data-id="17" transform="translate(184,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>arg</tspan><tspan class="node-id">  #17/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#17 arg
+Projection 1 of #2
+Type not computed
+
+[0] → #2 DATA</title></g><g class="node" id="n18" data-id="18" transform="translate(24,480)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>True</tspan><tspan class="node-id">  #18/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#18 True
+Projection 0 of #11
+Type not computed
+
+[0] → #11 CTRL</title></g><g class="node" id="n19" data-id="19" transform="translate(144,480)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>False</tspan><tspan class="node-id">  #19/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#19 False
+Projection 1 of #11
+Type not computed
+
+[0] → #11 CTRL</title></g></g><g class="region-heads" style="opacity: 1;"><g class="region-head" transform="translate(32,171)"><text class="label" x="26" y="17"><tspan>Loop</tspan><tspan class="node-id">  #6</tspan></text><title>Loop · 2 nodes</title><g class="fold-btn" role="button" tabindex="0" transform="translate(4,4)" aria-label="Fold region #6"><rect width="16" height="16" rx="2"/><text x="8" y="12" text-anchor="middle">−</text><title>Fold region</title></g></g></g><g class="shortcuts" style="opacity: 1;"/><g class="peep-ghosts" pointer-events="none"/></g></svg>
diff --git a/docs/chapter07/07-graph2.gv b/docs/chapter07/07-graph2.gv
new file mode 100644
index 00000000..4ff5ff35
--- /dev/null
+++ b/docs/chapter07/07-graph2.gv
@@ -0,0 +1,62 @@
+digraph chapter07 {
+/*
+while(arg < 10) {
+ arg = arg + 1;
+
+}
+return arg;
+
+*/
+ rankdir=BT;
+ ordering="in";
+ concentrate="true";
+ compound="true";
+ subgraph cluster_Nodes {
+ Start2 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">Start</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">$ctrl</TD><TD PORT="p1">arg</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Stop3 [ shape=box style=filled fillcolor=yellow label="Stop" ];
+ Loop6 [ shape=box style=filled fillcolor=yellow label="Loop" ];
+ Phi_arg8 [ style=filled fillcolor=lightyellow label="&phi;_arg" ];
+ Con_9 [ label="10" ];
+ LT10 [ label="<" ];
+ If11 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">If</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">True</TD><TD PORT="p1" BGCOLOR="yellow">False</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Con_15 [ label="1" ];
+ Add16 [ label="+" ];
+ Return17 [ shape=box style=filled fillcolor=yellow label="Return" ];
+ { rank=same; Loop6;Phi_arg8;}
+ }
+ node [shape=plaintext];
+ edge [ fontname=Helvetica, fontsize=8 ];
+ Stop3 -> Return17[taillabel=0 color=red];
+ Loop6 -> Start2:p0[taillabel=1 color=red];
+ Loop6 -> If11:p0[taillabel=2 color=red constraint=false];
+ Phi_arg8 -> Loop6 [style=dotted taillabel=0];
+ Phi_arg8 -> Start2:p1[taillabel=1];
+ Phi_arg8 -> Add16[taillabel=2 constraint=false];
+ LT10 -> Phi_arg8[taillabel=1];
+ LT10 -> Con_9[taillabel=2];
+ If11 -> Loop6[taillabel=0 color=red];
+ If11 -> LT10[taillabel=1];
+ Add16 -> Phi_arg8[taillabel=1];
+ Add16 -> Con_15[taillabel=2];
+ Return17 -> If11:p1[taillabel=0 color=red];
+ Return17 -> Phi_arg8[taillabel=1];
+ edge [style=dashed color=cornflowerblue];
+} \ No newline at end of file
diff --git a/docs/chapter07/07-graph2.json b/docs/chapter07/07-graph2.json
new file mode 100644
index 00000000..cc0e6d6c
--- /dev/null
+++ b/docs/chapter07/07-graph2.json
@@ -0,0 +1,26 @@
+{
+ "ver": 1,
+ "comp": "chapter07/docs/07-graph2.svg",
+ "step": 1,
+ "roots": [],
+ "scope": 0,
+ "nodes": [
+ {"id": 1, "label": "$ctrl", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 2, "role": "CTRL", "label": null}], "proj": {"par": 2, "idx": 0}},
+ {"id": 2, "label": "Start", "type": null, "kind": "CTRL", "edges": [], "proj": null},
+ {"id": 3, "label": "Stop", "type": null, "kind": "STOP", "edges": [{"idx": 0, "def": 17, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 4, "label": "arg", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 2, "role": "DATA", "label": null}], "proj": {"par": 2, "idx": 1}},
+ {"id": 5, "label": "True", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 11, "role": "CTRL", "label": null}], "proj": {"par": 11, "idx": 0}},
+ {"id": 6, "label": "Loop", "type": null, "kind": "LOOP", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 1, "role": "CTRL", "label": null}, {"idx": 2, "def": 5, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 7, "label": "False", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 11, "role": "CTRL", "label": null}], "proj": {"par": 11, "idx": 1}},
+ {"id": 8, "label": "φ_arg", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 6, "role": "ASSOC", "label": null}, {"idx": 1, "def": 4, "role": "DATA", "label": null}, {"idx": 2, "def": 16, "role": "DATA", "label": null}], "proj": null},
+ {"id": 9, "label": "10", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 10, "label": "<", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 8, "role": "DATA", "label": null}, {"idx": 2, "def": 9, "role": "DATA", "label": null}], "proj": null},
+ {"id": 11, "label": "If", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 6, "role": "CTRL", "label": null}, {"idx": 1, "def": 10, "role": "DATA", "label": null}], "proj": null},
+ {"id": 15, "label": "1", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 16, "label": "+", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 8, "role": "DATA", "label": null}, {"idx": 2, "def": 15, "role": "DATA", "label": null}], "proj": null},
+ {"id": 17, "label": "Return", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 7, "role": "CTRL", "label": null}, {"idx": 1, "def": 8, "role": "DATA", "label": null}], "proj": null}
+ ],
+ "groups": [
+ {"id": 6, "par": 0, "nodes": [5, 6, 7, 8, 10, 11, 16]}
+ ]
+}
diff --git a/docs/chapter07/07-graph2.svg b/docs/chapter07/07-graph2.svg
new file mode 100644
index 00000000..d2488859
--- /dev/null
+++ b/docs/chapter07/07-graph2.svg
@@ -0,0 +1,119 @@
+<svg aria-label="Compiler graph" width="841" height="788" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841 788" style="font-family: sans-serif;"><style>
+ html, body { height:100%; margin:0; font-family:sans-serif; }
+ .viewer { height:100%; display:grid; grid-template-rows:auto minmax(0,1fr) auto; }
+ .source { padding:8px; border-bottom:1px solid #ccc; }
+ #program { width:min(600px,80vw); vertical-align:middle; }
+ #status { margin-top:4px; }
+ #graph { min-height:0; overflow:hidden; position:relative; background:#fafbfc; }
+ #elk { width:100%; height:100%; }
+ #graph svg { display:block; }
+ .tools { display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:8px; font-size:12px; }
+ .hint { color:#616c78; }
+ .ctrl { color:#b63838; } .mem { color:#2867b2; } .data { color:#536477; }
+ .edge { fill:none; stroke-width:1.5; }
+ .edge.CTRL { stroke-width:2; }
+ .edge.ASSOC { stroke-dasharray:4 4; }
+ .edge.PARSER { stroke-dasharray:5 4; }
+ .parser rect { fill:#f6f0ff; stroke:#7956a5; stroke-width:1.5; stroke-dasharray:5 4; }
+ .edge.selected { stroke-width:3.5; }
+ .edge.bundle { stroke-width:4; }
+ .edge.bundle.selected { stroke-width:6; }
+ .edge-hit { fill:none; stroke:transparent; stroke-width:12; vector-effect:non-scaling-stroke; pointer-events:stroke; cursor:pointer; }
+ .edge-jump { fill:transparent; pointer-events:all; cursor:pointer; }
+ .node { cursor:pointer; }
+ .box { stroke:#758191; stroke-width:1; }
+ .node.held .box { stroke-dasharray:5 3; }
+ .node.near .box { stroke:#c18221; stroke-width:2; }
+ .node.focus .box { stroke:#9c4900; stroke-width:3; }
+ .node.selected .box { stroke:#1569b9; stroke-width:3; }
+ .node.folded .box { stroke-width:4; }
+ .node.folding .box { fill:#f4ccd7; }
+ .fold-tag { font-size:9px; font-weight:700; fill:#963751; }
+ .peep-area { fill:#fff3d9; fill-opacity:.8; stroke:#c18221; stroke-width:2; pointer-events:none; }
+ .node.peep-edit .box { stroke:#9c4900; stroke-width:3; }
+ .node.error .box { stroke:#b42318; stroke-width:4; }
+ #event.error { color:#b42318; font-weight:600; white-space:pre-wrap; overflow-wrap:anywhere; }
+ .peep-ghosts { pointer-events:none; }
+ .region-box { stroke:#9da9b8; stroke-width:1.5; }
+ .fold-btn { cursor:pointer; }
+ .fold-btn rect { fill:white; stroke:#596674; }
+ .fold-btn text { font-size:14px; fill:#152330; }
+ .fold-btn:focus rect { stroke:#1569b9; stroke-width:2; }
+ .shortcut { cursor:pointer; }
+ .shortcut path { fill:none; stroke-width:1.5; }
+ .shortcut rect { fill:#fff8ec; stroke:#b63838; stroke-dasharray:3 2; }
+ .shortcut text { font-size:11px; fill:#783030; }
+ .shortcut.selected rect, .shortcut:focus rect { stroke-width:2.5; }
+ .label { font-size:12px; fill:#152330; font-weight:600; }
+ .node-id { font-size:10px; fill:#596674; font-weight:400; }
+ .type { font-size:11px; fill:#455768; }
+ .port { stroke:#536477; stroke-width:1; }
+ .row { stroke:#b6afcf; stroke-width:1; }
+ .bind { font-size:12px; fill:#152330; }
+ .slot { font-size:10px; fill:#364352; paint-order:stroke; stroke:#fafbfc; stroke-width:3; }
+ #detail { position:absolute; top:8px; right:8px; margin:0; padding:12px; max-width:320px;
+ max-height:45%; overflow:auto; white-space:pre-wrap; overflow-wrap:anywhere;
+ background:#fffffff2; border:1px solid #bac5d0; border-radius:4px; font-size:12px; }
+ .controls { display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:6px; padding:6px; }
+ .button { color:white; background:black; font-size:20px; min-width:30px; }
+ .button:disabled { background:grey; }
+ .count { font-size:20px; min-width:35px; text-align:center; }
+ #N { width:6ch; }
+ #scrub { width:clamp(120px,35vw,600px); }
+ </style><defs><marker id="arrow-CTRL" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#b63838"/></marker><marker id="arrow-DATA" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#536477"/></marker><marker id="arrow-MEM" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#2867b2"/></marker><marker id="arrow-ASSOC" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#9396a3"/></marker><marker id="arrow-PARSER" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#7956a5"/></marker></defs><rect width="100%" height="100%" fill="rgb(250, 251, 252)"/><g><g class="regions" style="opacity: 1;"><g class="region" transform="translate(24,171)"><rect class="region-box" rx="8" width="637" height="410" fill="#f1f6fc"/></g></g><rect class="peep-area" rx="36" style="opacity: 0;"/><g class="edges"><path class="edge CTRL" id="e3i0" d="M246,687 C246,711 420.5,714 420.5,738" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#3[0] → #17 · CTRL</title></path><path class="edge CTRL" id="e6i1" d="M126,118 L126,241" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#6[1] → #1 · CTRL</title></path><path class="edge CTRL" id="e6i2" d="M106,546 L106,551 Q106,556 111,556 L641,556 Q649,556 649,548 L649,232 Q649,224 641,224 L164,224 Q156,224 156,232 L156,244" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#6[2] → #5 · CTRL</title></path><path class="edge DATA" id="e8i1" d="M246,118 L246,123 Q246,128 251,128 L252,128 Q258,128 258,134 L258,241" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#8[1] → #4 · DATA</title></path><path class="edge DATA" id="e8i2" d="M525,398 L525,403 Q525,408 530,408 L631,408 Q639,408 639,400 L639,240 Q639,232 631,232 L304,232 Q298,232 298,238 L298,244" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#8[2] → #16 · DATA</title></path><path class="edge DATA" id="e10i1" d="M278,297 L278,309 Q278,317 286,317 L365,317 Q373,317 373,325 L373,345" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#10[1] → #8 · DATA</title></path><path class="edge DATA" id="e10i2" d="M605,118 L605,299 Q605,307 597,307 L411,307 Q403,307 403,315 L403,345" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#10[2] → #9 · DATA</title></path><path class="edge CTRL" id="e11i0" d="M126,297 L126,449" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#11[0] → #6 · CTRL</title></path><path class="edge DATA" id="e11i1" d="M373,401 L373,406 Q373,411 368,411 L214,411 Q206,411 206,419 L206,449" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#11[1] → #10 · DATA</title></path><path class="edge DATA" id="e16i1" d="M278,297 L278,309 Q278,317 286,317 L517,317 Q525,317 525,325 L525,345" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#16[1] → #8 · DATA</title></path><path class="edge DATA" id="e16i2" d="M757,118 L757,123 Q757,128 752,128 L624,128 Q616,128 616,136 L616,309 Q616,317 608,317 L563,317 Q555,317 555,325 L555,345" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#16[2] → #15 · DATA</title></path><path class="edge CTRL" id="e17i0" d="M226,549 L226,634" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#17[0] → #7 · CTRL</title></path><path class="edge DATA" id="e17i1" d="M278,297 L278,309 Q278,317 286,317 L294,317 Q302,317 302,325 L302,588 Q302,596 294,596 L274,596 Q266,596 266,604 L266,634" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#17[1] → #8 · DATA</title></path></g><g class="edge-hits"/><g class="parser" style="opacity: 1;"/><g class="nodes"><g class="node" id="n1" data-id="1" transform="translate(66,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>$ctrl</tspan><tspan class="node-id">  #1/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#1 $ctrl
+Projection 0 of #2
+Type not computed
+
+[0] → #2 CTRL</title></g><g class="node" id="n2" data-id="2" transform="translate(66,24)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>Start</tspan><tspan class="node-id">  #2</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#2 Start
+Type not computed
+
+</title></g><g class="node" id="n3" data-id="3" transform="translate(384.5,738)" style="opacity: 1;"><path class="box" d="M0,0H72V26H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="36" y="17"><tspan>Stop</tspan><tspan class="node-id">  #3</tspan></text><text class="type" text-anchor="middle" y="35" x="36"/><title>#3 Stop
+Type not computed
+
+[0] → #17 CTRL</title><circle class="port" cx="36" cy="0" r="3" fill="#536477"/><text class="slot" x="36" y="-7" text-anchor="middle">0</text></g><g class="node" id="n4" data-id="4" transform="translate(186,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>arg</tspan><tspan class="node-id">  #4/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#4 arg
+Projection 1 of #2
+Type not computed
+
+[0] → #2 DATA</title></g><g class="node" id="n5" data-id="5" transform="translate(46,499)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>True</tspan><tspan class="node-id">  #5/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#5 True
+Projection 0 of #11
+Type not computed
+
+[0] → #11 CTRL</title></g><g class="node" id="n6" data-id="6" transform="translate(66,247)" style="opacity: 1;"><path class="box" d="M20,0H100Q120,0 120,20V24Q120,44 100,44H20Q0,44 0,24V20Q0,0 20,0Z" fill="#ffe0ab"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Loop</tspan><tspan class="node-id">  #6</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#6 Loop
+Type not computed
+
+[0] → — DATA
+[1] → #1 CTRL
+[2] → #5 CTRL</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n7" data-id="7" transform="translate(166,499)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>False</tspan><tspan class="node-id">  #7/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#7 False
+Projection 1 of #11
+Type not computed
+
+[0] → #11 CTRL</title></g><g class="node" id="n8" data-id="8" transform="translate(218,247)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_arg</tspan><tspan class="node-id">  #8</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#8 φ_arg
+Type not computed
+
+[0] → #6 ASSOC
+[1] → #4 DATA
+[2] → #16 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #6</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n9" data-id="9" transform="translate(545,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>10</tspan><tspan class="node-id">  #9</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#9 10
+Type not computed
+
+</title></g><g class="node" id="n10" data-id="10" transform="translate(313,351)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>&lt;</tspan><tspan class="node-id">  #10</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#10 &lt;
+Type not computed
+
+[0] → — DATA
+[1] → #8 DATA
+[2] → #9 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n11" data-id="11" transform="translate(46,455)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>If</tspan><tspan class="node-id">  #11</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#11 If
+Type not computed
+
+[0] → #6 CTRL
+[1] → #10 DATA</title><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><circle class="port" cx="160" cy="-3" r="3" fill="#536477"/><text class="slot" x="80" y="-10" text-anchor="middle">0</text><text class="slot" x="160" y="-10" text-anchor="middle">1</text></g><g class="node" id="n15" data-id="15" transform="translate(697,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>1</tspan><tspan class="node-id">  #15</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#15 1
+Type not computed
+
+</title></g><g class="node" id="n16" data-id="16" transform="translate(465,351)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>+</tspan><tspan class="node-id">  #16</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#16 +
+Type not computed
+
+[0] → — DATA
+[1] → #8 DATA
+[2] → #15 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n17" data-id="17" transform="translate(186,640)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Return</tspan><tspan class="node-id">  #17</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#17 Return
+Type not computed
+
+[0] → #7 CTRL
+[1] → #8 DATA</title><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><text class="slot" x="40" y="-10" text-anchor="middle">0</text><text class="slot" x="80" y="-10" text-anchor="middle">1</text></g></g><g class="region-heads" style="opacity: 1;"><g class="region-head" transform="translate(24,171)"><text class="label" x="26" y="17"><tspan>Loop</tspan><tspan class="node-id">  #6</tspan></text><title>Loop · 7 nodes</title><g class="fold-btn" role="button" tabindex="0" transform="translate(4,4)" aria-label="Fold region #6"><rect width="16" height="16" rx="2"/><text x="8" y="12" text-anchor="middle">−</text><title>Fold region</title></g></g></g><g class="shortcuts" style="opacity: 1;"/><g class="peep-ghosts" pointer-events="none"/></g></svg>
diff --git a/docs/chapter07/07-graph3.gv b/docs/chapter07/07-graph3.gv
new file mode 100644
index 00000000..4482100b
--- /dev/null
+++ b/docs/chapter07/07-graph3.gv
@@ -0,0 +1,108 @@
+digraph chapter07 {
+/*
+int sum = 0;
+int i = 0;
+while(i < arg) {
+ i = i + 1;
+ int j = 0;
+ while( j < arg ) {
+ sum = sum + j;
+ j = j + 1;
+ }
+}
+return sum;
+
+*/
+ rankdir=BT;
+ ordering="in";
+ concentrate="true";
+ compound="true";
+ subgraph cluster_Nodes {
+ Start2 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">Start</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">$ctrl</TD><TD PORT="p1">arg</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Stop3 [ shape=box style=filled fillcolor=yellow label="Stop" ];
+ Con_6 [ label="0" ];
+ Con_7 [ label="0" ];
+ Loop8 [ shape=box style=filled fillcolor=yellow label="Loop" ];
+ Phi_sum11 [ style=filled fillcolor=lightyellow label="&phi;_sum" ];
+ Phi_i12 [ style=filled fillcolor=lightyellow label="&phi;_i" ];
+ LT13 [ label="<" ];
+ If14 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">If</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">True</TD><TD PORT="p1" BGCOLOR="yellow">False</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Con_18 [ label="1" ];
+ Add19 [ label="+" ];
+ Con_20 [ label="0" ];
+ Loop21 [ shape=box style=filled fillcolor=yellow label="Loop" ];
+ Phi_sum24 [ style=filled fillcolor=lightyellow label="&phi;_sum" ];
+ Phi_j26 [ style=filled fillcolor=lightyellow label="&phi;_j" ];
+ LT27 [ label="<" ];
+ If28 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">If</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">True</TD><TD PORT="p1" BGCOLOR="yellow">False</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Add32 [ label="+" ];
+ Con_33 [ label="1" ];
+ Add34 [ label="+" ];
+ Return35 [ shape=box style=filled fillcolor=yellow label="Return" ];
+ { rank=same; Loop8;Phi_sum11;Phi_i12;}
+ { rank=same; Loop21;Phi_sum24;Phi_j26;}
+ }
+ node [shape=plaintext];
+ edge [ fontname=Helvetica, fontsize=8 ];
+ Stop3 -> Return35[taillabel=0 color=red];
+ Loop8 -> Start2:p0[taillabel=1 color=red];
+ Loop8 -> If28:p1[taillabel=2 color=red constraint=false];
+ Phi_sum11 -> Loop8 [style=dotted taillabel=0];
+ Phi_sum11 -> Con_6[taillabel=1];
+ Phi_sum11 -> Phi_sum24[taillabel=2 constraint=false];
+ Phi_i12 -> Loop8 [style=dotted taillabel=0];
+ Phi_i12 -> Con_7[taillabel=1];
+ Phi_i12 -> Add19[taillabel=2 constraint=false];
+ LT13 -> Phi_i12[taillabel=1];
+ LT13 -> Start2:p1[taillabel=2];
+ If14 -> Loop8[taillabel=0 color=red];
+ If14 -> LT13[taillabel=1];
+ Add19 -> Phi_i12[taillabel=1];
+ Add19 -> Con_18[taillabel=2];
+ Loop21 -> If14:p0[taillabel=1 color=red];
+ Loop21 -> If28:p0[taillabel=2 color=red constraint=false];
+ Phi_sum24 -> Loop21 [style=dotted taillabel=0];
+ Phi_sum24 -> Phi_sum11[taillabel=1];
+ Phi_sum24 -> Add32[taillabel=2 constraint=false];
+ Phi_j26 -> Loop21 [style=dotted taillabel=0];
+ Phi_j26 -> Con_20[taillabel=1];
+ Phi_j26 -> Add34[taillabel=2 constraint=false];
+ LT27 -> Phi_j26[taillabel=1];
+ LT27 -> Start2:p1[taillabel=2];
+ If28 -> Loop21[taillabel=0 color=red];
+ If28 -> LT27[taillabel=1];
+ Add32 -> Phi_j26[taillabel=1];
+ Add32 -> Phi_sum24[taillabel=2];
+ Add34 -> Phi_j26[taillabel=1];
+ Add34 -> Con_33[taillabel=2];
+ Return35 -> If14:p1[taillabel=0 color=red];
+ Return35 -> Phi_sum11[taillabel=1];
+ edge [style=dashed color=cornflowerblue];
+} \ No newline at end of file
diff --git a/docs/chapter07/07-graph3.json b/docs/chapter07/07-graph3.json
new file mode 100644
index 00000000..26dbc457
--- /dev/null
+++ b/docs/chapter07/07-graph3.json
@@ -0,0 +1,40 @@
+{
+ "ver": 1,
+ "comp": "chapter07/docs/07-graph3.svg",
+ "step": 1,
+ "roots": [],
+ "scope": 0,
+ "nodes": [
+ {"id": 1, "label": "$ctrl", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 2, "role": "CTRL", "label": null}], "proj": {"par": 2, "idx": 0}},
+ {"id": 2, "label": "Start", "type": null, "kind": "CTRL", "edges": [], "proj": null},
+ {"id": 3, "label": "Stop", "type": null, "kind": "STOP", "edges": [{"idx": 0, "def": 35, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 4, "label": "arg", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 2, "role": "DATA", "label": null}], "proj": {"par": 2, "idx": 1}},
+ {"id": 5, "label": "True", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 14, "role": "CTRL", "label": null}], "proj": {"par": 14, "idx": 0}},
+ {"id": 6, "label": "0", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 7, "label": "0", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 8, "label": "Loop", "type": null, "kind": "LOOP", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 1, "role": "CTRL", "label": null}, {"idx": 2, "def": 15, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 9, "label": "False", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 14, "role": "CTRL", "label": null}], "proj": {"par": 14, "idx": 1}},
+ {"id": 10, "label": "True", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 28, "role": "CTRL", "label": null}], "proj": {"par": 28, "idx": 0}},
+ {"id": 11, "label": "φ_sum", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 8, "role": "ASSOC", "label": null}, {"idx": 1, "def": 6, "role": "DATA", "label": null}, {"idx": 2, "def": 24, "role": "DATA", "label": null}], "proj": null},
+ {"id": 12, "label": "φ_i", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 8, "role": "ASSOC", "label": null}, {"idx": 1, "def": 7, "role": "DATA", "label": null}, {"idx": 2, "def": 19, "role": "DATA", "label": null}], "proj": null},
+ {"id": 13, "label": "<", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 12, "role": "DATA", "label": null}, {"idx": 2, "def": 4, "role": "DATA", "label": null}], "proj": null},
+ {"id": 14, "label": "If", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 8, "role": "CTRL", "label": null}, {"idx": 1, "def": 13, "role": "DATA", "label": null}], "proj": null},
+ {"id": 15, "label": "False", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 28, "role": "CTRL", "label": null}], "proj": {"par": 28, "idx": 1}},
+ {"id": 18, "label": "1", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 19, "label": "+", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 12, "role": "DATA", "label": null}, {"idx": 2, "def": 18, "role": "DATA", "label": null}], "proj": null},
+ {"id": 20, "label": "0", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 21, "label": "Loop", "type": null, "kind": "LOOP", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 5, "role": "CTRL", "label": null}, {"idx": 2, "def": 10, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 24, "label": "φ_sum", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 21, "role": "ASSOC", "label": null}, {"idx": 1, "def": 11, "role": "DATA", "label": null}, {"idx": 2, "def": 32, "role": "DATA", "label": null}], "proj": null},
+ {"id": 26, "label": "φ_j", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 21, "role": "ASSOC", "label": null}, {"idx": 1, "def": 20, "role": "DATA", "label": null}, {"idx": 2, "def": 34, "role": "DATA", "label": null}], "proj": null},
+ {"id": 27, "label": "<", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 26, "role": "DATA", "label": null}, {"idx": 2, "def": 4, "role": "DATA", "label": null}], "proj": null},
+ {"id": 28, "label": "If", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 21, "role": "CTRL", "label": null}, {"idx": 1, "def": 27, "role": "DATA", "label": null}], "proj": null},
+ {"id": 32, "label": "+", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 26, "role": "DATA", "label": null}, {"idx": 2, "def": 24, "role": "DATA", "label": null}], "proj": null},
+ {"id": 33, "label": "1", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 34, "label": "+", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 26, "role": "DATA", "label": null}, {"idx": 2, "def": 33, "role": "DATA", "label": null}], "proj": null},
+ {"id": 35, "label": "Return", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 9, "role": "CTRL", "label": null}, {"idx": 1, "def": 11, "role": "DATA", "label": null}], "proj": null}
+ ],
+ "groups": [
+ {"id": 8, "par": 0, "nodes": [5, 8, 9, 11, 12, 13, 14, 19]},
+ {"id": 21, "par": 8, "nodes": [10, 15, 21, 24, 26, 27, 28, 32, 34]}
+ ]
+}
diff --git a/docs/chapter07/07-graph3.svg b/docs/chapter07/07-graph3.svg
new file mode 100644
index 00000000..e8ca3ba5
--- /dev/null
+++ b/docs/chapter07/07-graph3.svg
@@ -0,0 +1,175 @@
+<svg aria-label="Compiler graph" width="1334" height="1332" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1334 1332" style="font-family: sans-serif;"><style>
+ html, body { height:100%; margin:0; font-family:sans-serif; }
+ .viewer { height:100%; display:grid; grid-template-rows:auto minmax(0,1fr) auto; }
+ .source { padding:8px; border-bottom:1px solid #ccc; }
+ #program { width:min(600px,80vw); vertical-align:middle; }
+ #status { margin-top:4px; }
+ #graph { min-height:0; overflow:hidden; position:relative; background:#fafbfc; }
+ #elk { width:100%; height:100%; }
+ #graph svg { display:block; }
+ .tools { display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:8px; font-size:12px; }
+ .hint { color:#616c78; }
+ .ctrl { color:#b63838; } .mem { color:#2867b2; } .data { color:#536477; }
+ .edge { fill:none; stroke-width:1.5; }
+ .edge.CTRL { stroke-width:2; }
+ .edge.ASSOC { stroke-dasharray:4 4; }
+ .edge.PARSER { stroke-dasharray:5 4; }
+ .parser rect { fill:#f6f0ff; stroke:#7956a5; stroke-width:1.5; stroke-dasharray:5 4; }
+ .edge.selected { stroke-width:3.5; }
+ .edge.bundle { stroke-width:4; }
+ .edge.bundle.selected { stroke-width:6; }
+ .edge-hit { fill:none; stroke:transparent; stroke-width:12; vector-effect:non-scaling-stroke; pointer-events:stroke; cursor:pointer; }
+ .edge-jump { fill:transparent; pointer-events:all; cursor:pointer; }
+ .node { cursor:pointer; }
+ .box { stroke:#758191; stroke-width:1; }
+ .node.held .box { stroke-dasharray:5 3; }
+ .node.near .box { stroke:#c18221; stroke-width:2; }
+ .node.focus .box { stroke:#9c4900; stroke-width:3; }
+ .node.selected .box { stroke:#1569b9; stroke-width:3; }
+ .node.folded .box { stroke-width:4; }
+ .node.folding .box { fill:#f4ccd7; }
+ .fold-tag { font-size:9px; font-weight:700; fill:#963751; }
+ .peep-area { fill:#fff3d9; fill-opacity:.8; stroke:#c18221; stroke-width:2; pointer-events:none; }
+ .node.peep-edit .box { stroke:#9c4900; stroke-width:3; }
+ .node.error .box { stroke:#b42318; stroke-width:4; }
+ #event.error { color:#b42318; font-weight:600; white-space:pre-wrap; overflow-wrap:anywhere; }
+ .peep-ghosts { pointer-events:none; }
+ .region-box { stroke:#9da9b8; stroke-width:1.5; }
+ .fold-btn { cursor:pointer; }
+ .fold-btn rect { fill:white; stroke:#596674; }
+ .fold-btn text { font-size:14px; fill:#152330; }
+ .fold-btn:focus rect { stroke:#1569b9; stroke-width:2; }
+ .shortcut { cursor:pointer; }
+ .shortcut path { fill:none; stroke-width:1.5; }
+ .shortcut rect { fill:#fff8ec; stroke:#b63838; stroke-dasharray:3 2; }
+ .shortcut text { font-size:11px; fill:#783030; }
+ .shortcut.selected rect, .shortcut:focus rect { stroke-width:2.5; }
+ .label { font-size:12px; fill:#152330; font-weight:600; }
+ .node-id { font-size:10px; fill:#596674; font-weight:400; }
+ .type { font-size:11px; fill:#455768; }
+ .port { stroke:#536477; stroke-width:1; }
+ .row { stroke:#b6afcf; stroke-width:1; }
+ .bind { font-size:12px; fill:#152330; }
+ .slot { font-size:10px; fill:#364352; paint-order:stroke; stroke:#fafbfc; stroke-width:3; }
+ #detail { position:absolute; top:8px; right:8px; margin:0; padding:12px; max-width:320px;
+ max-height:45%; overflow:auto; white-space:pre-wrap; overflow-wrap:anywhere;
+ background:#fffffff2; border:1px solid #bac5d0; border-radius:4px; font-size:12px; }
+ .controls { display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:6px; padding:6px; }
+ .button { color:white; background:black; font-size:20px; min-width:30px; }
+ .button:disabled { background:grey; }
+ .count { font-size:20px; min-width:35px; text-align:center; }
+ #N { width:6ch; }
+ #scrub { width:clamp(120px,35vw,600px); }
+ </style><defs><marker id="arrow-CTRL" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#b63838"/></marker><marker id="arrow-DATA" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#536477"/></marker><marker id="arrow-MEM" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#2867b2"/></marker><marker id="arrow-ASSOC" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#9396a3"/></marker><marker id="arrow-PARSER" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#7956a5"/></marker></defs><rect width="100%" height="100%" fill="rgb(250, 251, 252)"/><g><g class="regions" style="opacity: 1;"><g class="region" transform="translate(72,171)"><rect class="region-box" rx="8" width="1092" height="954" fill="#f1f6fc"/></g><g class="region" transform="translate(116,640)"><rect class="region-box" rx="8" width="666" height="438" fill="#f1f6fc"/></g></g><rect class="peep-area" rx="36" style="opacity: 0;"/><g class="edges"><path class="edge CTRL" id="e3i0" d="M812,1231 C812,1255 667,1258 667,1282" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#3[0] → #35 · CTRL</title></path><path class="edge CTRL" id="e8i1" d="M692,118 L692,279" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#8[1] → #1 · CTRL</title></path><path class="edge CTRL" id="e8i2" d="M328,1043 L328,1048 Q328,1053 333,1053 L1144,1053 Q1152,1053 1152,1045 L1152,262 Q1152,254 1144,254 L730,254 Q722,254 722,262 L722,282" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#8[2] → #15 · CTRL</title></path><path class="edge DATA" id="e11i1" d="M388,118 L388,130 Q388,138 396,138 L664,138 Q672,138 672,146 L672,251 Q672,259 680,259 L816,259 Q824,259 824,267 L824,279" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#11[1] → #6 · DATA</title></path><path class="edge DATA" id="e11i2" d="M380,791 L380,796 Q380,801 385,801 L1134,801 Q1142,801 1142,793 L1142,270 Q1142,262 1134,262 L872,262 Q864,262 864,270 L864,282" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#11[2] → #24 · DATA</title></path><path class="edge DATA" id="e12i1" d="M540,118 L540,123 Q540,128 545,128 L674,128 Q682,128 682,136 L682,231 Q682,239 690,239 L968,239 Q976,239 976,247 L976,279" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#12[1] → #7 · DATA</title></path><path class="edge DATA" id="e12i2" d="M844,436 L844,441 Q844,446 849,446 L1124,446 Q1132,446 1132,438 L1132,278 Q1132,270 1124,270 L1022,270 Q1016,270 1016,276 L1016,282" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#12[2] → #19 · DATA</title></path><path class="edge DATA" id="e13i1" d="M996,335 L996,383" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#13[1] → #12 · DATA</title></path><path class="edge DATA" id="e13i2" d="M812,118 L812,123 Q812,128 817,128 L1068,128 Q1076,128 1076,136 L1076,337 Q1076,345 1068,345 L1034,345 Q1026,345 1026,353 L1026,383" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#13[2] → #4 · DATA</title></path><path class="edge CTRL" id="e14i0" d="M692,335 L692,487" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#14[0] → #8 · CTRL</title></path><path class="edge DATA" id="e14i1" d="M996,439 L996,461 Q996,469 988,469 L780,469 Q772,469 772,477 L772,487" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#14[1] → #13 · DATA</title></path><path class="edge DATA" id="e19i1" d="M996,335 L996,347 Q996,355 988,355 L852,355 Q844,355 844,363 L844,383" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#19[1] → #12 · DATA</title></path><path class="edge DATA" id="e19i2" d="M84,118 L84,357 Q84,365 92,365 L866,365 Q874,365 874,373 L874,383" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#19[2] → #18 · DATA</title></path><path class="edge CTRL" id="e21i1" d="M672,587 L672,592 Q672,597 667,597 L236,597 Q228,597 228,605 L228,738" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#21[1] → #5 · CTRL</title></path><path class="edge CTRL" id="e21i2" d="M208,1043 L208,1048 Q208,1053 213,1053 L762,1053 Q770,1053 770,1045 L770,721 Q770,713 762,713 L266,713 Q258,713 258,721 L258,741" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#21[2] → #10 · CTRL</title></path><path class="edge DATA" id="e24i1" d="M844,335 L844,340 Q844,345 839,345 L781,345 Q773,345 773,353 L773,451 Q773,459 765,459 L226,459 Q218,459 218,467 L218,700 Q218,708 226,708 L352,708 Q360,708 360,716 L360,738" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#24[1] → #11 · DATA</title></path><path class="edge DATA" id="e24i2" d="M334,895 L334,900 Q334,905 339,905 L752,905 Q760,905 760,897 L760,729 Q760,721 752,721 L408,721 Q400,721 400,729 L400,741" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#24[2] → #32 · DATA</title></path><path class="edge DATA" id="e26i1" d="M1098,118 L1098,599 Q1098,607 1090,607 L725,607 Q717,607 717,615 L717,700 Q717,708 709,708 L520,708 Q512,708 512,716 L512,738" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#26[1] → #20 · DATA</title></path><path class="edge DATA" id="e26i2" d="M627,895 L627,900 Q627,905 632,905 L742,905 Q750,905 750,897 L750,737 Q750,729 742,729 L558,729 Q552,729 552,735 L552,741" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#26[2] → #34 · DATA</title></path><path class="edge DATA" id="e27i1" d="M532,794 L532,799 Q532,804 527,804 L483,804 Q475,804 475,812 L475,842" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#27[1] → #26 · DATA</title></path><path class="edge DATA" id="e27i2" d="M812,118 L812,123 Q812,128 817,128 L1079,128 Q1087,128 1087,136 L1087,589 Q1087,597 1079,597 L715,597 Q707,597 707,605 L707,806 Q707,814 699,814 L513,814 Q505,814 505,822 L505,842" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#27[2] → #4 · DATA</title></path><path class="edge CTRL" id="e28i0" d="M228,794 L228,946" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#28[0] → #21 · CTRL</title></path><path class="edge DATA" id="e28i1" d="M475,898 L475,910 Q475,918 467,918 L316,918 Q308,918 308,926 L308,946" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#28[1] → #27 · DATA</title></path><path class="edge DATA" id="e32i1" d="M532,794 L532,799 Q532,804 527,804 L342,804 Q334,804 334,812 L334,842" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#32[1] → #26 · DATA</title></path><path class="edge DATA" id="e32i2" d="M380,794 L380,806 Q380,814 372,814 L372,814 Q364,814 364,822 L364,842" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#32[2] → #24 · DATA</title></path><path class="edge DATA" id="e34i1" d="M532,794 L532,799 Q532,804 537,804 L619,804 Q627,804 627,812 L627,842" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#34[1] → #26 · DATA</title></path><path class="edge DATA" id="e34i2" d="M1250,118 L1250,123 Q1250,128 1245,128 L1117,128 Q1109,128 1109,136 L1109,609 Q1109,617 1101,617 L735,617 Q727,617 727,625 L727,816 Q727,824 719,824 L665,824 Q657,824 657,832 L657,842" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#34[2] → #33 · DATA</title></path><path class="edge CTRL" id="e35i0" d="M792,587 L792,1178" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#35[0] → #9 · CTRL</title></path><path class="edge DATA" id="e35i1" d="M844,335 L844,340 Q844,345 839,345 L781,345 Q773,345 773,353 L773,451 Q773,459 781,459 L860,459 Q868,459 868,467 L868,1132 Q868,1140 860,1140 L840,1140 Q832,1140 832,1148 L832,1178" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#35[1] → #11 · DATA</title></path></g><g class="edge-hits"/><g class="parser" style="opacity: 1;"/><g class="nodes"><g class="node" id="n1" data-id="1" transform="translate(632,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>$ctrl</tspan><tspan class="node-id">  #1/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#1 $ctrl
+Projection 0 of #2
+Type not computed
+
+[0] → #2 CTRL</title></g><g class="node" id="n2" data-id="2" transform="translate(632,24)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>Start</tspan><tspan class="node-id">  #2</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#2 Start
+Type not computed
+
+</title></g><g class="node" id="n3" data-id="3" transform="translate(631,1282)" style="opacity: 1;"><path class="box" d="M0,0H72V26H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="36" y="17"><tspan>Stop</tspan><tspan class="node-id">  #3</tspan></text><text class="type" text-anchor="middle" y="35" x="36"/><title>#3 Stop
+Type not computed
+
+[0] → #35 CTRL</title><circle class="port" cx="36" cy="0" r="3" fill="#536477"/><text class="slot" x="36" y="-7" text-anchor="middle">0</text></g><g class="node" id="n4" data-id="4" transform="translate(752,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>arg</tspan><tspan class="node-id">  #4/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#4 arg
+Projection 1 of #2
+Type not computed
+
+[0] → #2 DATA</title></g><g class="node" id="n5" data-id="5" transform="translate(612,537)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>True</tspan><tspan class="node-id">  #5/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#5 True
+Projection 0 of #14
+Type not computed
+
+[0] → #14 CTRL</title></g><g class="node" id="n6" data-id="6" transform="translate(328,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>0</tspan><tspan class="node-id">  #6</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#6 0
+Type not computed
+
+</title></g><g class="node" id="n7" data-id="7" transform="translate(480,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>0</tspan><tspan class="node-id">  #7</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#7 0
+Type not computed
+
+</title></g><g class="node" id="n8" data-id="8" transform="translate(632,285)" style="opacity: 1;"><path class="box" d="M20,0H100Q120,0 120,20V24Q120,44 100,44H20Q0,44 0,24V20Q0,0 20,0Z" fill="#ffe0ab"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Loop</tspan><tspan class="node-id">  #8</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#8 Loop
+Type not computed
+
+[0] → — DATA
+[1] → #1 CTRL
+[2] → #15 CTRL</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n9" data-id="9" transform="translate(732,537)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>False</tspan><tspan class="node-id">  #9/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#9 False
+Projection 1 of #14
+Type not computed
+
+[0] → #14 CTRL</title></g><g class="node" id="n10" data-id="10" transform="translate(148,996)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>True</tspan><tspan class="node-id">  #10/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#10 True
+Projection 0 of #28
+Type not computed
+
+[0] → #28 CTRL</title></g><g class="node" id="n11" data-id="11" transform="translate(784,285)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_sum</tspan><tspan class="node-id">  #11</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#11 φ_sum
+Type not computed
+
+[0] → #8 ASSOC
+[1] → #6 DATA
+[2] → #24 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #8</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n12" data-id="12" transform="translate(936,285)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_i</tspan><tspan class="node-id">  #12</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#12 φ_i
+Type not computed
+
+[0] → #8 ASSOC
+[1] → #7 DATA
+[2] → #19 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #8</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n13" data-id="13" transform="translate(936,389)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>&lt;</tspan><tspan class="node-id">  #13</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#13 &lt;
+Type not computed
+
+[0] → — DATA
+[1] → #12 DATA
+[2] → #4 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n14" data-id="14" transform="translate(612,493)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>If</tspan><tspan class="node-id">  #14</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#14 If
+Type not computed
+
+[0] → #8 CTRL
+[1] → #13 DATA</title><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><circle class="port" cx="160" cy="-3" r="3" fill="#536477"/><text class="slot" x="80" y="-10" text-anchor="middle">0</text><text class="slot" x="160" y="-10" text-anchor="middle">1</text></g><g class="node" id="n15" data-id="15" transform="translate(268,996)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>False</tspan><tspan class="node-id">  #15/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#15 False
+Projection 1 of #28
+Type not computed
+
+[0] → #28 CTRL</title></g><g class="node" id="n18" data-id="18" transform="translate(24,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>1</tspan><tspan class="node-id">  #18</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#18 1
+Type not computed
+
+</title></g><g class="node" id="n19" data-id="19" transform="translate(784,389)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>+</tspan><tspan class="node-id">  #19</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#19 +
+Type not computed
+
+[0] → — DATA
+[1] → #12 DATA
+[2] → #18 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n20" data-id="20" transform="translate(1038,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>0</tspan><tspan class="node-id">  #20</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#20 0
+Type not computed
+
+</title></g><g class="node" id="n21" data-id="21" transform="translate(168,744)" style="opacity: 1;"><path class="box" d="M20,0H100Q120,0 120,20V24Q120,44 100,44H20Q0,44 0,24V20Q0,0 20,0Z" fill="#ffe0ab"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Loop</tspan><tspan class="node-id">  #21</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#21 Loop
+Type not computed
+
+[0] → — DATA
+[1] → #5 CTRL
+[2] → #10 CTRL</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n24" data-id="24" transform="translate(320,744)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_sum</tspan><tspan class="node-id">  #24</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#24 φ_sum
+Type not computed
+
+[0] → #21 ASSOC
+[1] → #11 DATA
+[2] → #32 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #21</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n26" data-id="26" transform="translate(472,744)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_j</tspan><tspan class="node-id">  #26</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#26 φ_j
+Type not computed
+
+[0] → #21 ASSOC
+[1] → #20 DATA
+[2] → #34 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #21</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n27" data-id="27" transform="translate(415,848)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>&lt;</tspan><tspan class="node-id">  #27</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#27 &lt;
+Type not computed
+
+[0] → — DATA
+[1] → #26 DATA
+[2] → #4 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n28" data-id="28" transform="translate(148,952)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>If</tspan><tspan class="node-id">  #28</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#28 If
+Type not computed
+
+[0] → #21 CTRL
+[1] → #27 DATA</title><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><circle class="port" cx="160" cy="-3" r="3" fill="#536477"/><text class="slot" x="80" y="-10" text-anchor="middle">0</text><text class="slot" x="160" y="-10" text-anchor="middle">1</text></g><g class="node" id="n32" data-id="32" transform="translate(274,848)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>+</tspan><tspan class="node-id">  #32</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#32 +
+Type not computed
+
+[0] → — DATA
+[1] → #26 DATA
+[2] → #24 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n33" data-id="33" transform="translate(1190,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>1</tspan><tspan class="node-id">  #33</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#33 1
+Type not computed
+
+</title></g><g class="node" id="n34" data-id="34" transform="translate(567,848)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>+</tspan><tspan class="node-id">  #34</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#34 +
+Type not computed
+
+[0] → — DATA
+[1] → #26 DATA
+[2] → #33 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n35" data-id="35" transform="translate(752,1184)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Return</tspan><tspan class="node-id">  #35</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#35 Return
+Type not computed
+
+[0] → #9 CTRL
+[1] → #11 DATA</title><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><text class="slot" x="40" y="-10" text-anchor="middle">0</text><text class="slot" x="80" y="-10" text-anchor="middle">1</text></g></g><g class="region-heads" style="opacity: 1;"><g class="region-head" transform="translate(72,171)"><text class="label" x="26" y="17"><tspan>Loop</tspan><tspan class="node-id">  #8</tspan></text><title>Loop · 17 nodes</title><g class="fold-btn" role="button" tabindex="0" transform="translate(4,4)" aria-label="Fold region #8"><rect width="16" height="16" rx="2"/><text x="8" y="12" text-anchor="middle">−</text><title>Fold region</title></g></g><g class="region-head" transform="translate(116,640)"><text class="label" x="26" y="17"><tspan>Loop</tspan><tspan class="node-id">  #21</tspan></text><title>Loop · 9 nodes</title><g class="fold-btn" role="button" tabindex="0" transform="translate(4,4)" aria-label="Fold region #21"><rect width="16" height="16" rx="2"/><text x="8" y="12" text-anchor="middle">−</text><title>Fold region</title></g></g></g><g class="shortcuts" style="opacity: 1;"/><g class="peep-ghosts" pointer-events="none"/></g></svg>
diff --git a/docs/chapter07/07-graph4.gv b/docs/chapter07/07-graph4.gv
new file mode 100644
index 00000000..24ace3da
--- /dev/null
+++ b/docs/chapter07/07-graph4.gv
@@ -0,0 +1,96 @@
+digraph chapter07 {
+/*
+int a = 1;
+int b = 2;
+while(a < 10) {
+ if (a == 2) a = 3;
+ else b = 4;
+}
+return b;
+
+*/
+ rankdir=BT;
+ ordering="in";
+ concentrate="true";
+ compound="true";
+ subgraph cluster_Nodes {
+ Start2 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">Start</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">$ctrl</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Stop3 [ shape=box style=filled fillcolor=yellow label="Stop" ];
+ Con_6 [ label="1" ];
+ Con_7 [ label="2" ];
+ Loop8 [ shape=box style=filled fillcolor=yellow label="Loop" ];
+ Phi_a11 [ style=filled fillcolor=lightyellow label="&phi;_a" ];
+ Phi_b12 [ style=filled fillcolor=lightyellow label="&phi;_b" ];
+ Con_13 [ label="10" ];
+ LT14 [ label="<" ];
+ If15 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">If</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">True</TD><TD PORT="p1" BGCOLOR="yellow">False</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Con_19 [ label="2" ];
+ EQ20 [ label="==" ];
+ If21 [ shape=plaintext label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR><TD BGCOLOR="yellow">If</TD></TR>
+ <TR><TD>
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="p0" BGCOLOR="yellow">True</TD><TD PORT="p1" BGCOLOR="yellow">False</TD></TR>
+ </TABLE>
+ </TD></TR>
+ </TABLE>>
+ ];
+ Con_25 [ label="3" ];
+ Con_26 [ label="4" ];
+ Region27 [ shape=box style=filled fillcolor=yellow label="Region" ];
+ Phi_a28 [ style=filled fillcolor=lightyellow label="&phi;_a" ];
+ Phi_b29 [ style=filled fillcolor=lightyellow label="&phi;_b" ];
+ Return30 [ shape=box style=filled fillcolor=yellow label="Return" ];
+ { rank=same; Loop8;Phi_a11;Phi_b12;}
+ { rank=same; Region27;Phi_b29;Phi_a28;}
+ }
+ node [shape=plaintext];
+ edge [ fontname=Helvetica, fontsize=8 ];
+ Stop3 -> Return30[taillabel=0 color=red];
+ Loop8 -> Start2:p0[taillabel=1 color=red];
+ Loop8 -> Region27[taillabel=2 color=red constraint=false];
+ Phi_a11 -> Loop8 [style=dotted taillabel=0];
+ Phi_a11 -> Con_6[taillabel=1];
+ Phi_a11 -> Phi_a28[taillabel=2 constraint=false];
+ Phi_b12 -> Loop8 [style=dotted taillabel=0];
+ Phi_b12 -> Con_7[taillabel=1];
+ Phi_b12 -> Phi_b29[taillabel=2 constraint=false];
+ LT14 -> Phi_a11[taillabel=1];
+ LT14 -> Con_13[taillabel=2];
+ If15 -> Loop8[taillabel=0 color=red];
+ If15 -> LT14[taillabel=1];
+ EQ20 -> Phi_a11[taillabel=1];
+ EQ20 -> Con_19[taillabel=2];
+ If21 -> If15:p0[taillabel=0 color=red];
+ If21 -> EQ20[taillabel=1];
+ Region27 -> If21:p0[taillabel=1 color=red];
+ Region27 -> If21:p1[taillabel=2 color=red];
+ Phi_a28 -> Region27 [style=dotted taillabel=0];
+ Phi_a28 -> Con_25[taillabel=1];
+ Phi_a28 -> Phi_a11[taillabel=2 constraint=false];
+ Phi_b29 -> Region27 [style=dotted taillabel=0];
+ Phi_b29 -> Phi_b12[taillabel=1];
+ Phi_b29 -> Con_26[taillabel=2 constraint=false];
+ Return30 -> If15:p1[taillabel=0 color=red];
+ Return30 -> Phi_b12[taillabel=1];
+ edge [style=dashed color=cornflowerblue];
+}
diff --git a/docs/chapter07/07-graph4.json b/docs/chapter07/07-graph4.json
new file mode 100644
index 00000000..a9848505
--- /dev/null
+++ b/docs/chapter07/07-graph4.json
@@ -0,0 +1,37 @@
+{
+ "ver": 1,
+ "comp": "chapter07/docs/07-graph4.svg",
+ "step": 1,
+ "roots": [],
+ "scope": 0,
+ "nodes": [
+ {"id": 1, "label": "$ctrl", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 2, "role": "CTRL", "label": null}], "proj": {"par": 2, "idx": 0}},
+ {"id": 2, "label": "Start", "type": null, "kind": "CTRL", "edges": [], "proj": null},
+ {"id": 3, "label": "Stop", "type": null, "kind": "STOP", "edges": [{"idx": 0, "def": 30, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 4, "label": "True", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 15, "role": "CTRL", "label": null}], "proj": {"par": 15, "idx": 0}},
+ {"id": 5, "label": "False", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 15, "role": "CTRL", "label": null}], "proj": {"par": 15, "idx": 1}},
+ {"id": 6, "label": "1", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 7, "label": "2", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 8, "label": "Loop", "type": null, "kind": "LOOP", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 1, "role": "CTRL", "label": null}, {"idx": 2, "def": 27, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 9, "label": "True", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 21, "role": "CTRL", "label": null}], "proj": {"par": 21, "idx": 0}},
+ {"id": 10, "label": "False", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 21, "role": "CTRL", "label": null}], "proj": {"par": 21, "idx": 1}},
+ {"id": 11, "label": "φ_a", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 8, "role": "ASSOC", "label": null}, {"idx": 1, "def": 6, "role": "DATA", "label": null}, {"idx": 2, "def": 28, "role": "DATA", "label": null}], "proj": null},
+ {"id": 12, "label": "φ_b", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 8, "role": "ASSOC", "label": null}, {"idx": 1, "def": 7, "role": "DATA", "label": null}, {"idx": 2, "def": 29, "role": "DATA", "label": null}], "proj": null},
+ {"id": 13, "label": "10", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 14, "label": "<", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 11, "role": "DATA", "label": null}, {"idx": 2, "def": 13, "role": "DATA", "label": null}], "proj": null},
+ {"id": 15, "label": "If", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 8, "role": "CTRL", "label": null}, {"idx": 1, "def": 14, "role": "DATA", "label": null}], "proj": null},
+ {"id": 19, "label": "2", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 20, "label": "==", "type": null, "kind": "DATA", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 11, "role": "DATA", "label": null}, {"idx": 2, "def": 19, "role": "DATA", "label": null}], "proj": null},
+ {"id": 21, "label": "If", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 4, "role": "CTRL", "label": null}, {"idx": 1, "def": 20, "role": "DATA", "label": null}], "proj": null},
+ {"id": 25, "label": "3", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 26, "label": "4", "type": null, "kind": "DATA", "edges": [], "proj": null},
+ {"id": 27, "label": "Region", "type": null, "kind": "REGION", "edges": [{"idx": 0, "def": 0, "role": "DATA", "label": null}, {"idx": 1, "def": 9, "role": "CTRL", "label": null}, {"idx": 2, "def": 10, "role": "CTRL", "label": null}], "proj": null},
+ {"id": 28, "label": "φ_a", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 27, "role": "ASSOC", "label": null}, {"idx": 1, "def": 25, "role": "DATA", "label": null}, {"idx": 2, "def": 11, "role": "DATA", "label": null}], "proj": null},
+ {"id": 29, "label": "φ_b", "type": null, "kind": "PHI", "edges": [{"idx": 0, "def": 27, "role": "ASSOC", "label": null}, {"idx": 1, "def": 12, "role": "DATA", "label": null}, {"idx": 2, "def": 26, "role": "DATA", "label": null}], "proj": null},
+ {"id": 30, "label": "Return", "type": null, "kind": "CTRL", "edges": [{"idx": 0, "def": 5, "role": "CTRL", "label": null}, {"idx": 1, "def": 12, "role": "DATA", "label": null}], "proj": null}
+ ],
+ "groups": [
+ {"id": 8, "par": 0, "nodes": [4, 5, 8, 11, 12, 14, 15]},
+ {"id": 21, "par": 8, "nodes": [9, 10, 20, 21, 27, 28, 29]}
+ ]
+}
diff --git a/docs/chapter07/07-graph4.svg b/docs/chapter07/07-graph4.svg
new file mode 100644
index 00000000..09cea0b4
--- /dev/null
+++ b/docs/chapter07/07-graph4.svg
@@ -0,0 +1,159 @@
+<svg aria-label="Compiler graph" width="1151" height="1270" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1151 1270" style="font-family: sans-serif;"><style>
+ html, body { height:100%; margin:0; font-family:sans-serif; }
+ .viewer { height:100%; display:grid; grid-template-rows:auto minmax(0,1fr) auto; }
+ .source { padding:8px; border-bottom:1px solid #ccc; }
+ #program { width:min(600px,80vw); vertical-align:middle; }
+ #status { margin-top:4px; }
+ #graph { min-height:0; overflow:hidden; position:relative; background:#fafbfc; }
+ #elk { width:100%; height:100%; }
+ #graph svg { display:block; }
+ .tools { display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:8px; font-size:12px; }
+ .hint { color:#616c78; }
+ .ctrl { color:#b63838; } .mem { color:#2867b2; } .data { color:#536477; }
+ .edge { fill:none; stroke-width:1.5; }
+ .edge.CTRL { stroke-width:2; }
+ .edge.ASSOC { stroke-dasharray:4 4; }
+ .edge.PARSER { stroke-dasharray:5 4; }
+ .parser rect { fill:#f6f0ff; stroke:#7956a5; stroke-width:1.5; stroke-dasharray:5 4; }
+ .edge.selected { stroke-width:3.5; }
+ .edge.bundle { stroke-width:4; }
+ .edge.bundle.selected { stroke-width:6; }
+ .edge-hit { fill:none; stroke:transparent; stroke-width:12; vector-effect:non-scaling-stroke; pointer-events:stroke; cursor:pointer; }
+ .edge-jump { fill:transparent; pointer-events:all; cursor:pointer; }
+ .node { cursor:pointer; }
+ .box { stroke:#758191; stroke-width:1; }
+ .node.held .box { stroke-dasharray:5 3; }
+ .node.near .box { stroke:#c18221; stroke-width:2; }
+ .node.focus .box { stroke:#9c4900; stroke-width:3; }
+ .node.selected .box { stroke:#1569b9; stroke-width:3; }
+ .node.folded .box { stroke-width:4; }
+ .node.folding .box { fill:#f4ccd7; }
+ .fold-tag { font-size:9px; font-weight:700; fill:#963751; }
+ .peep-area { fill:#fff3d9; fill-opacity:.8; stroke:#c18221; stroke-width:2; pointer-events:none; }
+ .node.peep-edit .box { stroke:#9c4900; stroke-width:3; }
+ .node.error .box { stroke:#b42318; stroke-width:4; }
+ #event.error { color:#b42318; font-weight:600; white-space:pre-wrap; overflow-wrap:anywhere; }
+ .peep-ghosts { pointer-events:none; }
+ .region-box { stroke:#9da9b8; stroke-width:1.5; }
+ .fold-btn { cursor:pointer; }
+ .fold-btn rect { fill:white; stroke:#596674; }
+ .fold-btn text { font-size:14px; fill:#152330; }
+ .fold-btn:focus rect { stroke:#1569b9; stroke-width:2; }
+ .shortcut { cursor:pointer; }
+ .shortcut path { fill:none; stroke-width:1.5; }
+ .shortcut rect { fill:#fff8ec; stroke:#b63838; stroke-dasharray:3 2; }
+ .shortcut text { font-size:11px; fill:#783030; }
+ .shortcut.selected rect, .shortcut:focus rect { stroke-width:2.5; }
+ .label { font-size:12px; fill:#152330; font-weight:600; }
+ .node-id { font-size:10px; fill:#596674; font-weight:400; }
+ .type { font-size:11px; fill:#455768; }
+ .port { stroke:#536477; stroke-width:1; }
+ .row { stroke:#b6afcf; stroke-width:1; }
+ .bind { font-size:12px; fill:#152330; }
+ .slot { font-size:10px; fill:#364352; paint-order:stroke; stroke:#fafbfc; stroke-width:3; }
+ #detail { position:absolute; top:8px; right:8px; margin:0; padding:12px; max-width:320px;
+ max-height:45%; overflow:auto; white-space:pre-wrap; overflow-wrap:anywhere;
+ background:#fffffff2; border:1px solid #bac5d0; border-radius:4px; font-size:12px; }
+ .controls { display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:6px; padding:6px; }
+ .button { color:white; background:black; font-size:20px; min-width:30px; }
+ .button:disabled { background:grey; }
+ .count { font-size:20px; min-width:35px; text-align:center; }
+ #N { width:6ch; }
+ #scrub { width:clamp(120px,35vw,600px); }
+ </style><defs><marker id="arrow-CTRL" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#b63838"/></marker><marker id="arrow-DATA" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#536477"/></marker><marker id="arrow-MEM" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#2867b2"/></marker><marker id="arrow-ASSOC" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#9396a3"/></marker><marker id="arrow-PARSER" viewBox="0 -4 8 8" refX="8" refY="0" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,-4L8,0L0,4Z" fill="#7956a5"/></marker></defs><rect width="100%" height="100%" fill="rgb(250, 251, 252)"/><g><g class="regions" style="opacity: 1;"><g class="region" transform="translate(24,173)"><rect class="region-box" rx="8" width="813" height="890" fill="#f1f6fc"/></g><g class="region" transform="translate(271,632)"><rect class="region-box" rx="8" width="512" height="374" fill="#f2f7ef"/></g></g><rect class="peep-area" rx="36" style="opacity: 0;"/><g class="edges"><path class="edge CTRL" id="e3i0" d="M269,1169 C269,1193 575.5,1196 575.5,1220" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#3[0] → #30 · CTRL</title></path><path class="edge CTRL" id="e8i1" d="M149,118 L149,271" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#8[1] → #1 · CTRL</title></path><path class="edge CTRL" id="e8i2" d="M407,971 L407,976 Q407,981 412,981 L817,981 Q825,981 825,973 L825,254 Q825,246 817,246 L187,246 Q179,246 179,254 L179,274" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#8[2] → #27 · CTRL</title></path><path class="edge DATA" id="e11i1" d="M307,118 L307,123 Q307,128 302,128 L289,128 Q281,128 281,136 L281,271" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#11[1] → #6 · DATA</title></path><path class="edge DATA" id="e11i2" d="M559,971 L559,976 Q559,981 564,981 L807,981 Q815,981 815,973 L815,262 Q815,254 807,254 L329,254 Q321,254 321,262 L321,274" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#11[2] → #28 · DATA</title></path><path class="edge DATA" id="e12i1" d="M459,118 L459,123 Q459,128 454,128 L441,128 Q433,128 433,136 L433,271" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#12[1] → #7 · DATA</title></path><path class="edge DATA" id="e12i2" d="M711,971 L711,976 Q711,981 716,981 L797,981 Q805,981 805,973 L805,270 Q805,262 797,262 L479,262 Q473,262 473,268 L473,274" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#12[2] → #29 · DATA</title></path><path class="edge DATA" id="e14i1" d="M301,327 L301,349 Q301,357 293,357 L263,357 Q255,357 255,365 L255,375" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#14[1] → #11 · DATA</title></path><path class="edge DATA" id="e14i2" d="M611,118 L611,123 Q611,128 606,128 L531,128 Q523,128 523,136 L523,329 Q523,337 515,337 L293,337 Q285,337 285,345 L285,375" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#14[2] → #13 · DATA</title></path><path class="edge CTRL" id="e15i0" d="M149,327 L149,479" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#15[0] → #8 · CTRL</title></path><path class="edge DATA" id="e15i1" d="M255,431 L255,436 Q255,441 250,441 L237,441 Q229,441 229,449 L229,479" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#15[1] → #14 · DATA</title></path><path class="edge DATA" id="e20i1" d="M301,327 L301,349 Q301,357 309,357 L435,357 Q443,357 443,365 L443,666" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#20[1] → #11 · DATA</title></path><path class="edge DATA" id="e20i2" d="M763,118 L763,130 Q763,138 755,138 L542,138 Q534,138 534,146 L534,581 Q534,589 526,589 L481,589 Q473,589 473,597 L473,666" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#20[2] → #19 · DATA</title></path><path class="edge CTRL" id="e21i0" d="M129,579 L129,591 Q129,599 137,599 L355,599 Q363,599 363,607 L363,770" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#21[0] → #4 · CTRL</title></path><path class="edge DATA" id="e21i1" d="M443,722 L443,770" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#21[1] → #20 · DATA</title></path><path class="edge CTRL" id="e27i1" d="M343,870 L343,875 Q343,880 348,880 L399,880 Q407,880 407,888 L407,918" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#27[1] → #9 · CTRL</title></path><path class="edge CTRL" id="e27i2" d="M463,870 L463,875 Q463,880 458,880 L445,880 Q437,880 437,888 L437,918" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#27[2] → #10 · CTRL</title></path><path class="edge DATA" id="e28i1" d="M915,118 L915,140 Q915,148 907,148 L553,148 Q545,148 545,156 L545,586 Q545,589 542,589 L542,589 Q539,589 539,592 L539,918" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#28[1] → #25 · DATA</title></path><path class="edge DATA" id="e28i2" d="M301,327 L301,349 Q301,357 309,357 L571,357 Q579,357 579,365 L579,918" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#28[2] → #11 · DATA</title></path><path class="edge DATA" id="e29i1" d="M453,327 L453,339 Q453,347 461,347 L582,347 Q590,347 590,355 L590,581 Q590,589 598,589 L683,589 Q691,589 691,597 L691,918" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#29[1] → #12 · DATA</title></path><path class="edge DATA" id="e29i2" d="M1067,118 L1067,150 Q1067,158 1059,158 L739,158 Q731,158 731,166 L731,918" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#29[2] → #26 · DATA</title></path><path class="edge CTRL" id="e30i0" d="M249,579 L249,1116" stroke="#b63838" marker-start="url(#arrow-CTRL)" style="opacity: 1;"><title>#30[0] → #5 · CTRL</title></path><path class="edge DATA" id="e30i1" d="M453,327 L453,339 Q453,347 445,347 L333,347 Q325,347 325,355 L325,581 Q325,589 317,589 L268,589 Q260,589 260,597 L260,1070 Q260,1078 268,1078 L281,1078 Q289,1078 289,1086 L289,1116" stroke="#536477" marker-start="url(#arrow-DATA)" style="opacity: 1;"><title>#30[1] → #12 · DATA</title></path></g><g class="edge-hits"/><g class="parser" style="opacity: 1;"/><g class="nodes"><g class="node" id="n1" data-id="1" transform="translate(89,68)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>$ctrl</tspan><tspan class="node-id">  #1/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#1 $ctrl
+Projection 0 of #2
+Type not computed
+
+[0] → #2 CTRL</title></g><g class="node" id="n2" data-id="2" transform="translate(89,24)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Start</tspan><tspan class="node-id">  #2</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#2 Start
+Type not computed
+
+</title></g><g class="node" id="n3" data-id="3" transform="translate(539.5,1220)" style="opacity: 1;"><path class="box" d="M0,0H72V26H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="36" y="17"><tspan>Stop</tspan><tspan class="node-id">  #3</tspan></text><text class="type" text-anchor="middle" y="35" x="36"/><title>#3 Stop
+Type not computed
+
+[0] → #30 CTRL</title><circle class="port" cx="36" cy="0" r="3" fill="#536477"/><text class="slot" x="36" y="-7" text-anchor="middle">0</text></g><g class="node" id="n4" data-id="4" transform="translate(69,529)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>True</tspan><tspan class="node-id">  #4/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#4 True
+Projection 0 of #15
+Type not computed
+
+[0] → #15 CTRL</title></g><g class="node" id="n5" data-id="5" transform="translate(189,529)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>False</tspan><tspan class="node-id">  #5/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#5 False
+Projection 1 of #15
+Type not computed
+
+[0] → #15 CTRL</title></g><g class="node" id="n6" data-id="6" transform="translate(247,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>1</tspan><tspan class="node-id">  #6</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#6 1
+Type not computed
+
+</title></g><g class="node" id="n7" data-id="7" transform="translate(399,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>2</tspan><tspan class="node-id">  #7</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#7 2
+Type not computed
+
+</title></g><g class="node" id="n8" data-id="8" transform="translate(89,277)" style="opacity: 1;"><path class="box" d="M20,0H100Q120,0 120,20V24Q120,44 100,44H20Q0,44 0,24V20Q0,0 20,0Z" fill="#ffe0ab"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Loop</tspan><tspan class="node-id">  #8</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#8 Loop
+Type not computed
+
+[0] → — DATA
+[1] → #1 CTRL
+[2] → #27 CTRL</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n9" data-id="9" transform="translate(283,820)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>True</tspan><tspan class="node-id">  #9/0</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#9 True
+Projection 0 of #21
+Type not computed
+
+[0] → #21 CTRL</title></g><g class="node" id="n10" data-id="10" transform="translate(403,820)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>False</tspan><tspan class="node-id">  #10/1</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#10 False
+Projection 1 of #21
+Type not computed
+
+[0] → #21 CTRL</title></g><g class="node" id="n11" data-id="11" transform="translate(241,277)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_a</tspan><tspan class="node-id">  #11</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#11 φ_a
+Type not computed
+
+[0] → #8 ASSOC
+[1] → #6 DATA
+[2] → #28 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #8</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n12" data-id="12" transform="translate(393,277)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_b</tspan><tspan class="node-id">  #12</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#12 φ_b
+Type not computed
+
+[0] → #8 ASSOC
+[1] → #7 DATA
+[2] → #29 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #8</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n13" data-id="13" transform="translate(551,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>10</tspan><tspan class="node-id">  #13</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#13 10
+Type not computed
+
+</title></g><g class="node" id="n14" data-id="14" transform="translate(195,381)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>&lt;</tspan><tspan class="node-id">  #14</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#14 &lt;
+Type not computed
+
+[0] → — DATA
+[1] → #11 DATA
+[2] → #13 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n15" data-id="15" transform="translate(69,485)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>If</tspan><tspan class="node-id">  #15</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#15 If
+Type not computed
+
+[0] → #8 CTRL
+[1] → #14 DATA</title><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><circle class="port" cx="160" cy="-3" r="3" fill="#536477"/><text class="slot" x="80" y="-10" text-anchor="middle">0</text><text class="slot" x="160" y="-10" text-anchor="middle">1</text></g><g class="node" id="n19" data-id="19" transform="translate(703,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>2</tspan><tspan class="node-id">  #19</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#19 2
+Type not computed
+
+</title></g><g class="node" id="n20" data-id="20" transform="translate(383,672)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>==</tspan><tspan class="node-id">  #20</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#20 ==
+Type not computed
+
+[0] → — DATA
+[1] → #11 DATA
+[2] → #19 DATA</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n21" data-id="21" transform="translate(283,776)" style="opacity: 1;"><path class="box" d="M0,0H240V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="120" y="18"><tspan>If</tspan><tspan class="node-id">  #21</tspan></text><text class="type" text-anchor="middle" y="35" x="120"/><title>#21 If
+Type not computed
+
+[0] → #4 CTRL
+[1] → #20 DATA</title><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><circle class="port" cx="160" cy="-3" r="3" fill="#536477"/><text class="slot" x="80" y="-10" text-anchor="middle">0</text><text class="slot" x="160" y="-10" text-anchor="middle">1</text></g><g class="node" id="n25" data-id="25" transform="translate(855,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>3</tspan><tspan class="node-id">  #25</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#25 3
+Type not computed
+
+</title></g><g class="node" id="n26" data-id="26" transform="translate(1007,68)" style="opacity: 1;"><path class="box" d="M14,0H106Q120,0 120,14V30Q120,44 106,44H14Q0,44 0,30V14Q0,0 14,0Z" fill="#edf3f7"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>4</tspan><tspan class="node-id">  #26</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#26 4
+Type not computed
+
+</title></g><g class="node" id="n27" data-id="27" transform="translate(347,924)" style="opacity: 1;"><path class="box" d="M12,0H108L120,44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Region</tspan><tspan class="node-id">  #27</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#27 Region
+Type not computed
+
+[0] → — DATA
+[1] → #9 CTRL
+[2] → #10 CTRL</title><circle class="port" cx="30" cy="-3" r="3" fill="white"/><circle class="port" cx="60" cy="-3" r="3" fill="#536477"/><circle class="port" cx="90" cy="-3" r="3" fill="#536477"/><text class="slot" x="30" y="-10" text-anchor="middle">0</text><text class="slot" x="60" y="-10" text-anchor="middle">1</text><text class="slot" x="90" y="-10" text-anchor="middle">2</text></g><g class="node" id="n28" data-id="28" transform="translate(499,924)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_a</tspan><tspan class="node-id">  #28</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#28 φ_a
+Type not computed
+
+[0] → #27 ASSOC
+[1] → #25 DATA
+[2] → #11 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #27</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n29" data-id="29" transform="translate(651,924)" style="opacity: 1;"><path class="box" d="M12,0H108L120,22L108,44H12L0,22Z" fill="#f5e4fa"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>φ_b</tspan><tspan class="node-id">  #29</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#29 φ_b
+Type not computed
+
+[0] → #27 ASSOC
+[1] → #12 DATA
+[2] → #26 DATA</title><circle class="port" cx="-3" cy="22" r="3" fill="#536477"/><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><path class="reg" fill="none" stroke="#b63838" d="M-3,22H-18m4,-3l-4,3l4,3"><title>[0] → #27</title></path><text class="slot" x="-10" y="15" text-anchor="middle">0</text><text class="slot" x="40" y="-10" text-anchor="middle">1</text><text class="slot" x="80" y="-10" text-anchor="middle">2</text></g><g class="node" id="n30" data-id="30" transform="translate(209,1122)" style="opacity: 1;"><path class="box" d="M0,0H120V44H0Z" fill="#fff1c4"/><text class="label" text-anchor="middle" x="60" y="18"><tspan>Return</tspan><tspan class="node-id">  #30</tspan></text><text class="type" text-anchor="middle" y="35" x="60"/><title>#30 Return
+Type not computed
+
+[0] → #5 CTRL
+[1] → #12 DATA</title><circle class="port" cx="40" cy="-3" r="3" fill="#536477"/><circle class="port" cx="80" cy="-3" r="3" fill="#536477"/><text class="slot" x="40" y="-10" text-anchor="middle">0</text><text class="slot" x="80" y="-10" text-anchor="middle">1</text></g></g><g class="region-heads" style="opacity: 1;"><g class="region-head" transform="translate(24,173)"><text class="label" x="26" y="17"><tspan>Loop</tspan><tspan class="node-id">  #8</tspan></text><title>Loop · 14 nodes</title><g class="fold-btn" role="button" tabindex="0" transform="translate(4,4)" aria-label="Fold region #8"><rect width="16" height="16" rx="2"/><text x="8" y="12" text-anchor="middle">−</text><title>Fold region</title></g></g><g class="region-head" transform="translate(271,632)"><text class="label" x="26" y="17"><tspan>If</tspan><tspan class="node-id">  #21</tspan></text><title>If · 7 nodes</title><g class="fold-btn" role="button" tabindex="0" transform="translate(4,4)" aria-label="Fold region #21"><rect width="16" height="16" rx="2"/><text x="8" y="12" text-anchor="middle">−</text><title>Fold region</title></g></g></g><g class="shortcuts" style="opacity: 1;"/><g class="peep-ghosts" pointer-events="none"/></g></svg>
diff --git a/docs/chapter07/Makefile b/docs/chapter07/Makefile
new file mode 100644
index 00000000..6fa0f057
--- /dev/null
+++ b/docs/chapter07/Makefile
@@ -0,0 +1 @@
+include ../../graph/docs.mk
diff --git a/docs/chapter07/README.ja.md b/docs/chapter07/README.ja.md
new file mode 100644
index 00000000..67d59c1b
--- /dev/null
+++ b/docs/chapter07/README.ja.md
@@ -0,0 +1,229 @@
+# 第7章: While 文
+
+[English](README.md) | 日本語
+
+この文書は [原文](README.md) の日本語訳です。
+
+[前の章: 第6章](../chapter06/README.ja.md) |
+[次の章: 第8章](../chapter08/README.ja.md)
+
+# 目次
+
+1. [バックエッジの扱い](#バックエッジの扱い)
+2. [新しいノード型](#新しいノード型)
+3. [詳細な手順](#詳細な手順)
+4. [可視化](#可視化)
+5. [入れ子のループ](#入れ子のループ)
+6. [If を内包するループ](#if-を内包するループ)
+
+[linear](https://github.com/SeaOfNodes/Simple/tree/linear) ブランチの直線的な Git のリビジョン履歴で [この章](https://github.com/SeaOfNodes/Simple/tree/linear-chapter07) を読み、前の章と [比較](https://github.com/SeaOfNodes/Simple/compare/linear-chapter06...linear-chapter07) することもできます。
+
+この章では `while` 文を導入します。
+
+この章の [完全な言語文法](07-grammar.ja.md) はこちらです。
+
+## バックエッジの扱い
+
+ループ構造によって生じる難しさは、反復時に変数がループ本体へ戻ってくることです。たとえば、次のコードです。
+
+```java
+while(arg < 10) {
+ arg = arg + 1;
+}
+return arg;
+```
+
+変数 `arg` にはループ本体内で新しい値が代入され、その値がループ本体へ戻ります。
+
+一般に、ループ構造を次のように書き換えます。
+
+```java
+loop_head:
+if( !(arg < 10) )
+ goto loop_exit;
+arg = arg + 1;
+goto loop_head;
+
+loop_exit:
+```
+
+これは説明のためのもので、この言語にはラベルや `goto` 文はありません。
+
+SSA[^1] の観点では、`arg` が戻ってくるため、先頭に `phi` ノードが必要です。概念的には、次のような結果にしたいのです。
+
+```java
+// arg1 represents incoming arg
+//
+loop_head:
+arg2 = phi(arg1, arg3);
+if( !(arg2 < 10) )
+ goto loop_exit;
+arg3 = arg2 + 1;
+goto loop_head;
+
+loop_exit:
+```
+
+`arg2` の phi が `arg3` を参照することに注目してください。`while` ループの条件を解析する時点では、`arg3` はまだわかりません。常に SSA 形式である Sea of Nodes グラフを正しく構築するために解決すべき問題の核心は、ここにあります。
+
+[第5章](../chapter05/README.ja.md) を思い出してください。`if` 文の解析では、`if` の条件を過ぎるときにシンボルテーブルを複製します。後で2組のシンボルテーブルを `Region` でマージし、`if` 文の2つの分岐内で定義が変わったすべての名前に phi を作ります。`if` 文の場合、マージする定義がすでにわかっているマージ地点で phi を作ります。
+
+ループ構造に対する基本的な考えは、ループ本体内でどの名前が再定義されるかわからないため、ループの `if` 条件に入る *前に*、シンボルテーブル内のすべての名前に対して積極的に phi を作ることです。ループが終わると、さかのぼって不要な phi を削除します。これを「eager phi(先行生成する phi)」方式と呼びます。[^2]
+
+[第8章](../chapter08/README.ja.md) では、名前の再定義に遭遇したときだけ phi を作る「lazy phi(遅延生成する phi)」方式を実装します。
+
+## 新しいノード型
+
+ノードの一覧は [第5章](../chapter05/README.ja.md) と同じですが、必要な追加ロジックの一部をより適切にカプセル化するため、`Region` のサブタイプ `Loop` を作ります。Loop と Phi のピープホール最適化では、バックエッジが必要な書き換えを、ループ本体の解析が完了するまで延期します。ほかのノードは通常どおり最適化を続けます。
+
+## 詳細な手順
+
+1. まず、`Region` の新しいサブクラス `Loop` を作ります。`Loop` は2つの制御入力を取ります。最初は入口、すなわち `$ctrl` の現在の束縛です。2つ目(`null`)は、ループ解析後に設定するバックエッジのためのプレースホルダーです。バックエッジの欠如は、Loop と対応する Phi が未完成であることを示します。そのため、それらのピープホール最適化は、各書き換えに必要な入力がそろうまで待ちます。
+
+ ```java
+ ctrl(new LoopNode(ctrl(),null).peephole());
+ ```
+
+ 新しく作った Region が現在の制御になります。
+
+2. 現在の Scope ノードを複製します。スコープ内のすべての階層のシンボルを複製し、`$ctrl` の束縛を除くすべてのシンボルに phi を作ります。
+
+ ```java
+ // Make a new Scope for the body.
+ _scope = _scope.dup(true);
+ ```
+
+ `dup` の呼び出しに引数 `true` を渡すことに注目してください。これが phi の作成を引き起こします。`dup()` メソッドで phi を作るコードを次に示します。
+
+ ```java
+ // boolean loop=true if this is a loop region
+
+ String[] names = reverseNames(); // Get the variable names
+ dup.add_def(ctrl()); // Control input is just copied
+ for( int i=1; i<nIns(); i++ ) {
+ if ( !loop ) { dup.add_def(in(i)); }
+ else {
+ // Loop region
+ // Create a phi node with second input as null - to be filled in
+ // by endLoop() below
+ dup.add_def(new PhiNode(names[i], ctrl(), in(i), null).peephole());
+ // Ensure our node has the same phi in case we created one
+ set_def(i, dup.in(i));
+ }
+ }
+ ```
+
+ `else` ブロック内では、複製したスコープと元のスコープの両方に同じ phi ノードを設定します。
+
+3. 次に、通常の if とほぼ同じように `if` 条件を設定します。
+
+ ```java
+ // Parse predicate
+ var pred = require(parseExpression(), ")");
+ // IfNode takes current control and predicate
+ IfNode ifNode = (IfNode)new IfNode(ctrl(), pred).<IfNode>keep().peephole();
+ // Setup projection nodes
+ Node ifT = new ProjNode(ifNode, 0, "True" ).peephole();
+ ifNode.unkeep();
+ Node ifF = new ProjNode(ifNode, 1, "False").peephole();
+ ```
+
+4. 現在のスコープをもう一度複製します。これはループ終了後に生き続ける出口スコープなので、出口スコープの `$ctrl` を `False` 射影に設定します。出口スコープは、ループの条件式の副作用も取り込みます。
+
+ ```java
+ // The exit scope, accounting for any side effects in the predicate
+ var exit = _scope.dup();
+ exit.ctrl(ifF);
+ ```
+
+5. 制御を `True` 射影に設定し、ループ本体を解析します。
+
+ ```java
+ // Parse the true side, which corresponds to loop body
+ ctrl(ifT); // set ctrl token to ifTrue projection
+ parseStatement(); // Parse loop body
+ ```
+
+6. ループ本体の解析後、先ほど作ったすべての phi に戻って処理します。
+
+ ```java
+ // The true branch loops back, so whatever is current control gets
+ // added to head loop as input
+ head.endLoop(_scope, exit);
+ ```
+
+ `endLoop` メソッドは、ループ Region の2つ目の制御をバックエッジの制御に設定します。続いてすべての phi をたどり、それぞれの2つ目のデータ入力をループ本体の対応する項目に設定します。使われなかった phi はピープホール最適化され、元の入力に置き換わります。
+
+ ```java
+ Node ctrl = ctrl();
+ ctrl.set_def(2,back.ctrl());
+ for( int i=1; i<nIns(); i++ ) {
+ PhiNode phi = (PhiNode)in(i);
+ assert phi.region()==ctrl && phi.in(2)==null;
+ phi.set_def(2,back.in(i));
+ // Do an eager useless-phi removal
+ Node in = phi.peephole();
+ if( in != phi )
+ phi.subsume(in);
+ }
+ ```
+
+7. 最後に、最初の元のスコープ(head)と、本体用に作った複製の両方を削除します。出口では false の制御が現在の制御(手順4)であり、出口スコープを現在のスコープに設定します。
+
+ ```java
+ // At exit the false control is the current control, and
+ // the scope is the exit scope after the exit test.
+ return (_scope = exit);
+ ```
+
+### 可視化
+
+上の例の途中の状態を次に示します。
+
+![グラフ1](./07-graph1.svg)
+
+* ループ先頭、出口、本体の3つの Scope を示しています。それぞれの `$ctrl` の束縛は Loop、False、True を指します。
+
+最終的なグラフは次のようになります。
+
+![グラフ2](./07-graph2.svg)
+
+## より複雑な例
+
+### 入れ子のループ
+
+```java
+int sum = 0;
+int i = 0;
+while(i < arg) {
+ i = i + 1;
+ int j = 0;
+ while( j < arg ) {
+ sum = sum + j;
+ j = j + 1;
+ }
+}
+return sum;
+```
+
+![グラフ2](./07-graph3.svg)
+
+### If を内包するループ
+
+```java
+int a = 1;
+int b = 2;
+while(a < 10) {
+ if (a == 2) a = 3;
+ else b = 4;
+}
+return b;
+```
+
+![グラフ2](./07-graph4.svg)
+
+[^1]: Cytron, R. et al (1991).
+ Efficiently computing static single assignment form and the control dependence graph, in ACM Transactions on Programming Languages and Systems, 13(4):451-490, 1991.
+
+[^2]: Click, C. (1995).
+ Combining Analyses, Combining Optimizations, 103.
diff --git a/docs/chapter07/README.md b/docs/chapter07/README.md
new file mode 100644
index 00000000..188b629c
--- /dev/null
+++ b/docs/chapter07/README.md
@@ -0,0 +1,250 @@
+# Chapter 7: While Statement
+
+English | [日本語](README.ja.md)
+
+[Previous: Chapter 6](../chapter06/README.md) |
+[Next: Chapter 8](../chapter08/README.md)
+
+# Table of Contents
+
+1. [Dealing With Back Edges](#dealing-with-back-edges)
+2. [New Node Types](#new-node-types)
+3. [Detailed Steps](#detailed-steps)
+4. [Visualization](#visualization)
+5. [Nested Loops](#nested-loops)
+6. [Loop With Nested If](#loop-with-nested-if)
+
+You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter07) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter06...linear-chapter07) it to the previous chapter.
+
+
+In this chapter we introduce the `while` statement.
+
+Here is the [complete language grammar](07-grammar.md) for this chapter.
+
+
+## Dealing With Back Edges
+
+The complication introduced by looping constructs is that variables flow back into the body of the loop on iteration.
+For example:
+
+```java
+while(arg < 10) {
+ arg = arg + 1;
+}
+return arg;
+```
+
+The variable `arg` is assigned a new value inside the body of the loop, and this then flows back into the body of the loop.
+
+In general, we will rewrite the looping construct as follows:
+
+```java
+loop_head:
+if( !(arg < 10) )
+ goto loop_exit;
+arg = arg + 1;
+goto loop_head;
+
+loop_exit:
+```
+
+Above is for illustration only, we do not have labels and `goto` statements in the language.
+
+From an SSA[^1] point of view, since `arg` flows back, it requires a `phi` node at the head. Conceptually we would like the outcome to be:
+
+```java
+// arg1 represents incoming arg
+//
+loop_head:
+arg2 = phi(arg1, arg3);
+if( !(arg2 < 10) )
+ goto loop_exit;
+arg3 = arg2 + 1;
+goto loop_head;
+
+loop_exit:
+```
+
+Notice that the phi for `arg2` refers to `arg3`, which is not known at the time we parse the `while` loop predicate. This is the crux of the problem that we need
+to solve in order to successfully construct the Sea of Nodes graph, which is always in SSA form.
+
+Recall from [Chapter 5](../chapter05/README.md) that when parsing `if` statements, we clone the symbol tables as we go past the `if` predicate.
+Later we merge the two sets of symbol tables at a `Region` - creating phis for all names that encountered a change in definition within the two
+branches of the `if` statement. In the case of the `if` statement, the phis are created at the merge point when we already know the definitions
+that are being merged.
+
+The essential idea for loop constructs is to eagerly create phis for all names in the symbol tables *before* we enter the loop's `if` condition,
+since we do not know which names will be redefined inside the body of the loop. When the loop terminates, we go back and remove unnecessary
+phis. We call this approach the "eager phi" approach.[^2]
+
+In [Chapter 8](../chapter08) we will implement a "lazy phi" approach that creates phis only when we encounter redefinition of a name.
+
+## New Node Types
+
+Our list of nodes remains the same as in [Chapter 5](../chapter05/README.md), however, we create a subtype of `Region` named `Loop` to better
+encapsulate some of the additional logic required. Loop and Phi peepholes defer rewrites that need the back edge
+until we complete parsing the loop body. Other nodes continue to optimize normally.
+
+## Detailed Steps
+
+1. We start by creating a new subclass of `Region`, the `Loop`. The `Loop` gets two control inputs,
+ the first is the entry point, i.e. the current binding to `$ctrl`, and second (`null`) is a placeholder for the back edge that is
+ set after the loop is parsed. A missing back edge marks the Loop and associated Phis as incomplete,
+ so their peepholes wait for the inputs needed by each rewrite.
+
+ ```java
+ ctrl(new LoopNode(ctrl(),null).peephole());
+ ```
+
+ The newly created region becomes the current control.
+
+2. We duplicate the current Scope node. This involves duplicating all the symbols at
+ every level within the scope, and creating phis for every symbol except the `$ctrl` binding.
+
+ ```java
+ // Make a new Scope for the body.
+ _scope = _scope.dup(true);
+ ```
+
+ Note that the `dup` call is given an argument `true`. This triggers creating phis. The code
+ that creates the phis in the `dup()` method is shown below.
+
+ ```java
+ // boolean loop=true if this is a loop region
+
+ String[] names = reverseNames(); // Get the variable names
+ dup.add_def(ctrl()); // Control input is just copied
+ for( int i=1; i<nIns(); i++ ) {
+ if ( !loop ) { dup.add_def(in(i)); }
+ else {
+ // Loop region
+ // Create a phi node with second input as null - to be filled in
+ // by endLoop() below
+ dup.add_def(new PhiNode(names[i], ctrl(), in(i), null).peephole());
+ // Ensure our node has the same phi in case we created one
+ set_def(i, dup.in(i));
+ }
+ }
+ ```
+
+ Inside the `else` block both the duplicated scope and the original scope get the same phi node.
+
+3. Next we set up the `if` condition, very much like we do with regular ifs.
+
+ ```java
+ // Parse predicate
+ var pred = require(parseExpression(), ")");
+ // IfNode takes current control and predicate
+ IfNode ifNode = (IfNode)new IfNode(ctrl(), pred).<IfNode>keep().peephole();
+ // Setup projection nodes
+ Node ifT = new ProjNode(ifNode, 0, "True" ).peephole();
+ ifNode.unkeep();
+ Node ifF = new ProjNode(ifNode, 1, "False").peephole();
+ ```
+
+4. We make another clone of the current scope. This will be the exit scope that will live after the loop ends,
+ therefore `$ctrl` in the exit scope is set to the `False` projection.
+ The exit scope captures any side effects of the loop's predicate.
+
+ ```java
+ // The exit scope, accounting for any side effects in the predicate
+ var exit = _scope.dup();
+ exit.ctrl(ifF);
+ ```
+
+5. We now set the control to the `True` projection and parse the loop body.
+
+ ```java
+ // Parse the true side, which corresponds to loop body
+ ctrl(ifT); // set ctrl token to ifTrue projection
+ parseStatement(); // Parse loop body
+ ```
+
+6. After the loop body is parsed, we go back and process all the phis we created earlier.
+
+ ```java
+ // The true branch loops back, so whatever is current control gets
+ // added to head loop as input
+ head.endLoop(_scope, exit);
+ ```
+
+ The `endLoop` method sets the second control of the loop region to the control from the back edge.
+ It then goes through all the phis and sets the second data input on the phi to the corresponding entry
+ from the loop body; phis that were not used are peepholed and get replaced by the original input.
+
+ ```java
+ Node ctrl = ctrl();
+ ctrl.set_def(2,back.ctrl());
+ for( int i=1; i<nIns(); i++ ) {
+ PhiNode phi = (PhiNode)in(i);
+ assert phi.region()==ctrl && phi.in(2)==null;
+ phi.set_def(2,back.in(i));
+ // Do an eager useless-phi removal
+ Node in = phi.peephole();
+ if( in != phi )
+ phi.subsume(in);
+ }
+ ```
+
+7. Finally, both the original scope (head) we started with, and the duplicate created for the body are killed.
+ At exit the false control is the current control (step 4), and exit scope is set as the current scope.
+
+ ```java
+ // At exit the false control is the current control, and
+ // the scope is the exit scope after the exit test.
+ return (_scope = exit);
+ ```
+
+### Visualization
+
+The example quoted above is shown below at an intermediate state:
+
+![Graph1](./07-graph1.svg)
+
+* Three Scopes are shown: the loop head, exit, and body. Their `$ctrl` bindings
+ point to Loop, False, and True, respectively.
+
+The final graph looks like this:
+
+![Graph2](./07-graph2.svg)
+
+## More Complex Examples
+
+### Nested Loops
+
+```java
+int sum = 0;
+int i = 0;
+while(i < arg) {
+ i = i + 1;
+ int j = 0;
+ while( j < arg ) {
+ sum = sum + j;
+ j = j + 1;
+ }
+}
+return sum;
+```
+
+![Graph2](./07-graph3.svg)
+
+### Loop With Nested If
+
+```java
+int a = 1;
+int b = 2;
+while(a < 10) {
+ if (a == 2) a = 3;
+ else b = 4;
+}
+return b;
+```
+
+![Graph2](./07-graph4.svg)
+
+
+[^1]: Cytron, R. et al (1991).
+ Efficiently computing static single assignment form and the control dependence graph, in ACM Transactions on Programming Languages and Systems, 13(4):451-490, 1991.
+
+[^2]: Click, C. (1995).
+ Combining Analyses, Combining Optimizations, 103.