summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter06/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter06/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter07/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter07/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter08/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter08/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter09/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter09/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter10/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter10/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter11/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter11/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter14/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter14/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter16/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter16/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java1
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java3
-rw-r--r--graph/README.md9
-rw-r--r--graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java2
-rw-r--r--graph/web/layout.js63
-rw-r--r--graph/web/render.js13
-rw-r--r--graph/web/viewer.js8
47 files changed, 135 insertions, 42 deletions
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java
index dbaa093d..359d0175 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java
@@ -166,6 +166,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -175,7 +176,6 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF); // Ctrl token is now set to ifFalse projection
if (matchx("else")) {
parseStatement();
fScope = _scope;
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter05/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 2ec62125..0c2ac4b0 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -34,6 +34,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof RegionNode ) return Kind.REGION;
diff --git a/chapter06/src/main/java/com/seaofnodes/simple/Parser.java b/chapter06/src/main/java/com/seaofnodes/simple/Parser.java
index 9b79c195..b9fbf26f 100644
--- a/chapter06/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter06/src/main/java/com/seaofnodes/simple/Parser.java
@@ -167,6 +167,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -176,7 +177,6 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF); // Ctrl token is now set to ifFalse projection
if (matchx("else")) {
parseStatement();
fScope = _scope;
diff --git a/chapter06/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter06/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index cee75f6a..84ef99f2 100644
--- a/chapter06/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter06/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -34,6 +34,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof RegionNode ) return Kind.REGION;
diff --git a/chapter07/src/main/java/com/seaofnodes/simple/Parser.java b/chapter07/src/main/java/com/seaofnodes/simple/Parser.java
index f40eb3a3..2e043538 100644
--- a/chapter07/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter07/src/main/java/com/seaofnodes/simple/Parser.java
@@ -237,6 +237,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -246,7 +247,6 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF); // Ctrl token is now set to ifFalse projection
if (matchx("else")) {
parseStatement();
fScope = _scope;
diff --git a/chapter07/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter07/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index e907e744..9f33520e 100644
--- a/chapter07/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter07/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -34,6 +34,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter08/src/main/java/com/seaofnodes/simple/Parser.java b/chapter08/src/main/java/com/seaofnodes/simple/Parser.java
index 5c699a54..d7055f48 100644
--- a/chapter08/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter08/src/main/java/com/seaofnodes/simple/Parser.java
@@ -284,6 +284,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -293,7 +294,6 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF); // Ctrl token is now set to ifFalse projection
if (matchx("else")) {
parseStatement();
fScope = _scope;
diff --git a/chapter08/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter08/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 585592d4..3c4dd7dd 100644
--- a/chapter08/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter08/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -34,6 +34,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter09/src/main/java/com/seaofnodes/simple/Parser.java b/chapter09/src/main/java/com/seaofnodes/simple/Parser.java
index 393b5358..3c1eab96 100644
--- a/chapter09/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter09/src/main/java/com/seaofnodes/simple/Parser.java
@@ -282,6 +282,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -291,7 +292,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
if (matchx("else")) {
parseStatement();
fScope = _scope;
diff --git a/chapter09/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter09/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index a3372f08..df46cc6b 100644
--- a/chapter09/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter09/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -36,6 +36,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter10/src/main/java/com/seaofnodes/simple/Parser.java b/chapter10/src/main/java/com/seaofnodes/simple/Parser.java
index 01c7da29..ddbbdf82 100644
--- a/chapter10/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter10/src/main/java/com/seaofnodes/simple/Parser.java
@@ -331,6 +331,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -341,7 +342,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
if (matchx("else")) {
_scope.upcast(ifF,pred,true); // Up-cast predicate
parseStatement();
diff --git a/chapter10/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter10/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 545897d8..600e9c73 100644
--- a/chapter10/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter10/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -37,6 +37,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter11/src/main/java/com/seaofnodes/simple/Parser.java b/chapter11/src/main/java/com/seaofnodes/simple/Parser.java
index 7008512f..5f4922f4 100644
--- a/chapter11/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter11/src/main/java/com/seaofnodes/simple/Parser.java
@@ -342,6 +342,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -352,7 +353,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
if (matchx("else")) {
_scope.upcast(ifF,pred,true); // Up-cast predicate
parseStatement();
diff --git a/chapter11/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter11/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 38acb8cd..9ddf0de6 100644
--- a/chapter11/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter11/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/Parser.java b/chapter12/src/main/java/com/seaofnodes/simple/Parser.java
index 2bcd8da7..f5c63cc7 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/Parser.java
@@ -345,6 +345,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -355,7 +356,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
if (matchx("else")) {
_scope.upcast(ifF,pred,true); // Up-cast predicate
parseStatement();
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 8e909e1c..8af87a2b 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/Parser.java b/chapter13/src/main/java/com/seaofnodes/simple/Parser.java
index e41293bd..b3bd1ada 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/Parser.java
@@ -346,6 +346,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -356,7 +357,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
_scope.upcast(ifF,pred,true); // Up-cast predicate
if (matchx("else")) {
parseStatement();
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter13/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 60eae682..3778a6d9 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter14/src/main/java/com/seaofnodes/simple/Parser.java b/chapter14/src/main/java/com/seaofnodes/simple/Parser.java
index 5d7d67da..34fac183 100644
--- a/chapter14/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter14/src/main/java/com/seaofnodes/simple/Parser.java
@@ -370,6 +370,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -380,7 +381,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
_scope.upcast(ifF,pred,true); // Up-cast predicate
if (matchx("else")) {
parseStatement();
diff --git a/chapter14/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter14/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 39f79e69..4b1cc065 100644
--- a/chapter14/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter14/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
index 71bf1b07..965bd146 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
@@ -381,6 +381,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -391,7 +392,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
_scope.upcast(ifF,pred,true); // Up-cast predicate
if (matchx("else")) {
parseStatement();
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter15/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 639108ae..f5efd3e8 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java
index b52767cc..1f20a913 100644
--- a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java
@@ -355,6 +355,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -365,7 +366,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
_scope.upcast(ifF,pred,true); // Up-cast predicate
if (matchx("else")) {
parseStatement();
diff --git a/chapter16/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter16/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 6d281535..da54e8c8 100644
--- a/chapter16/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter16/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/Parser.java b/chapter17/src/main/java/com/seaofnodes/simple/Parser.java
index a8157b41..07312396 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/Parser.java
@@ -446,6 +446,7 @@ public class Parser {
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -458,7 +459,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter17/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 5a45aa9d..a88987d5 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof StartNode ) return Kind.CTRL;
if( n instanceof LoopNode ) return Kind.LOOP;
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
index 1ed0c02a..3e914bbd 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
@@ -519,6 +519,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -536,7 +537,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 23085c9c..442bfd50 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/Parser.java b/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
index 29738a31..1a775050 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
@@ -502,6 +502,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -519,7 +520,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 170dcb7d..d61f4771 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/Parser.java b/chapter20/src/main/java/com/seaofnodes/simple/Parser.java
index 0809a6f3..af047850 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/Parser.java
@@ -503,6 +503,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -520,7 +521,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 15afe5d1..5c277abc 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/Parser.java b/chapter21/src/main/java/com/seaofnodes/simple/Parser.java
index adddbaa6..fb108447 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/Parser.java
@@ -521,6 +521,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -538,7 +539,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 73967e05..69403674 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java
index 60a36574..72fe6a71 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java
@@ -534,6 +534,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -551,7 +552,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 8dcee566..2bcda3ef 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java
index 41d8fcb5..be5c8932 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java
@@ -548,6 +548,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -573,7 +574,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 38a4778f..f9b940a9 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/Parser.java b/chapter24/src/main/java/com/seaofnodes/simple/Parser.java
index c9e53b4b..cd4d5afb 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/Parser.java
@@ -555,6 +555,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -580,7 +581,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index bf8f4181..374197c0 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,6 +38,7 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
if( n instanceof FunNode ) return Kind.FUN;
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
index ffd8bf64..05685418 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
@@ -778,6 +778,7 @@ public class Parser {
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ fScope.ctrl(ifF); // The pending arm already starts at False
_xScopes.push(fScope); // For graph visualization we need all scopes
// Parse the true side
@@ -803,7 +804,7 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
- ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ ifF.unkeep(); // fScope already owns the false control
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index e9b1dc6d..506ac3e2 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -38,8 +38,9 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
private Kind kind(Node n) {
+ if( n instanceof StopNode ) return Kind.STOP;
if( n instanceof ScopeNode ) return Kind.SCOPE;
- if( n instanceof StartCUNode || n instanceof StopCUNode ) return Kind.UNIT;
+ if( n instanceof StartCUNode ) return Kind.UNIT;
if( n instanceof FunNode ) return Kind.FUN;
// Start inherits Loop for whole-program analysis, but is not a source loop.
if( n instanceof StartNode ) return Kind.CTRL;
diff --git a/graph/README.md b/graph/README.md
index a2145220..b98ca9d3 100644
--- a/graph/README.md
+++ b/graph/README.md
@@ -147,6 +147,15 @@ the internal parent/projection edge is represented by the shared box. A
projection whose parent is absent is drawn on its own. Node kinds use color
and shape rather than a KIND label.
+Phi slot 0 uses a short left-facing arrow at the middle of the box's left side,
+instead of a full edge to its Region. The tooltip and node details retain the
+definition ID. Value inputs stay along the top.
+Each Region (including a loop header) and its Phis form one layout row, with
+the Region on the left. Their boxes, ports, selections and node IDs stay separate.
+This also keeps a newly created Phi on its Region's row while the parser holds it.
+Stops occupy the bottom row, beside the Parser box when it is present, even
+before return edges attach. Chapter adapters identify Stops with `Kind.STOP`.
+
Scopes have named slots across the top, including control, memory and variable
bindings. Edges leave these slots upward toward their definitions. They stay visible
even when **Associations** is unchecked, and do not constrain CFG layout.
diff --git a/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java b/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java
index 6836693e..53d74706 100644
--- a/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java
+++ b/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java
@@ -14,7 +14,7 @@ public record GraphSnapshot(int ver, String comp, long step,
@Override public int[] roots() { return roots.clone(); }
- public enum Kind { DATA, CTRL, MEM, SCOPE, PHI, REGION, LOOP, FUN, UNIT }
+ public enum Kind { DATA, CTRL, MEM, SCOPE, PHI, REGION, LOOP, FUN, UNIT, STOP }
public enum Role { DATA, CTRL, MEM, ASSOC }
/** IDs are stable within one compilation. Labels and types are plain text. */
diff --git a/graph/web/layout.js b/graph/web/layout.js
index a277cb53..b192de87 100644
--- a/graph/web/layout.js
+++ b/graph/web/layout.js
@@ -22,7 +22,7 @@ class GraphLayout {
if (scope) w = Math.ceil(Math.max(120, this.ctx.measureText(label).width + 24,
cols.reduce((sum, w) => sum + w, 0)));
return {id: "n" + n.id, width: w, height: 62,
- ports: [], n, label, type, scope, cols, ox: 0, oy: 0};
+ ports: [], n, label, type, scope, cols, stop: n.kind === "STOP", ox: 0, oy: 0};
});
const byId = new Map(nodes.map(n => [n.n.id, n]));
const parsing = evt?.phase === "Parse" && evt.kind !== "PHASE";
@@ -46,7 +46,16 @@ class GraphLayout {
projs.get(par).push(n);
n.par = par;
}
- const groups = nodes.filter(n => !n.par).map(n => {
+ const phis = new Map();
+ for (const n of nodes) {
+ if (n.n.kind !== "PHI") continue;
+ const reg = byId.get(n.n.edges.find(e => e.idx === 0)?.def);
+ if (!reg || !["REGION", "LOOP", "FUN"].includes(reg.n.kind)) continue;
+ if (!phis.has(reg)) phis.set(reg, []);
+ phis.get(reg).push(n);
+ n.reg = reg;
+ }
+ const groups = nodes.filter(n => !n.par && !n.reg).map(n => {
const slots = (projs.get(n) || []).sort((a, b) => a.n.proj.idx - b.n.proj.idx || a.n.id - b.n.id);
const parts = [n, ...slots];
if (slots.length) {
@@ -61,15 +70,24 @@ class GraphLayout {
x += p.width;
}
}
+ // ELK sees one row; the renderer keeps each Region/Phi as a separate box.
+ let width = n.width;
+ for (const p of phis.get(n) || []) {
+ p.ox = width + 32;
+ width = p.ox + p.width;
+ parts.push(p);
+ }
const ports = [];
for (const p of parts) {
- p.cell = slots.length > 0;
+ p.cell = slots.length > 0 && !p.reg;
const extra = p.scope && p.cols.length ?
(p.width - p.cols.reduce((sum, w) => sum + w, 0)) / p.cols.length : 0;
let x = 0;
for (const [idx, e] of p.n.edges.entries()) {
// The parent/projection link is represented by the shared box.
if (p.par && e.idx === 0 && e.def === p.par.n.id) continue;
+ // Phi's Region input is a local left-facing marker, not a routed edge.
+ if (p.n.kind === "PHI" && e.idx === 0) continue;
if (p.scope) {
const span = p.cols[idx] + extra;
ports.push({id: p.id + "i" + e.idx, x: x + span / 2 - 3, y: -3, span,
@@ -77,7 +95,8 @@ class GraphLayout {
x += span;
continue;
}
- ports.push({id: p.id + "i" + e.idx, x: p.ox + p.width * (e.idx + 1) / (p.n.edges.length + 1) - 3,
+ const off = p.n.kind === "PHI" ? 0 : 1;
+ ports.push({id: p.id + "i" + e.idx, x: p.ox + p.width * (e.idx + off) / (p.n.edges.length + off) - 3,
y: p.oy - 3, width: 6, height: 6, layoutOptions: {"elk.port.side": "NORTH"}});
}
// Direct uses of the MultiNode attach to its header, projections below.
@@ -86,12 +105,13 @@ class GraphLayout {
y: p.oy + (side === "EAST" ? p.height / 2 : p.height) - 3,
width: 6, height: 6, layoutOptions: {"elk.port.side": side}});
}
- return {id: n.id, width: n.width, height: n.height * (slots.length ? 2 : 1), ports, parts};
+ return {id: n.id, width, height: n.height * (slots.length ? 2 : 1), ports, parts};
});
const edges = [];
for (const use of snap.nodes) {
for (const e of use.edges) {
if (!e.def) continue;
+ if (use.kind === "PHI" && e.idx === 0) continue;
if (byId.get(use.id).par && e.idx === 0 && e.def === use.proj.par) continue;
const id = "e" + use.id + "i" + e.idx;
const reg = byId.get(use.edges[0]?.def)?.n;
@@ -106,13 +126,14 @@ class GraphLayout {
}
}
const graph = await this.elk.layout({
- id: "root", children: groups.filter(g => !g.parts[0].scope).map(({parts, ...box}) => ({...box,
+ id: "root", children: groups.filter(g => !g.parts[0].scope && !g.parts[0].stop).map(({parts, ...box}) => ({...box,
layoutOptions: {"elk.portConstraints": "FIXED_POS",
// Keep a projection with its parent even when that parent has real uses.
"elk.layered.layering.layerConstraint": parts.length === 1 && parts[0].held ? "LAST_SEPARATE" : "NONE"}
})),
// Scope bindings and lifetime associations must not impose CFG ranks.
- edges: edges.filter(e => e.role !== "ASSOC" && !e.bind && !byId.get(e.def).scope).map(e => ({
+ edges: edges.filter(e => e.role !== "ASSOC" && !e.bind && !byId.get(e.def).scope &&
+ !byId.get(e.use).stop && !byId.get(e.def).stop).map(e => ({
id: e.id, sources: e.sources, targets: e.targets, layoutOptions: e.layoutOptions
})),
layoutOptions: {
@@ -124,8 +145,9 @@ class GraphLayout {
"elk.randomSeed": 1
}
});
- // Scope ports use the same local geometry; place their boxes after CFG nodes.
- for (const g of groups.filter(g => g.parts[0].scope)) graph.children.push({...g, x: 0, y: 0});
+ // Place scopes and Stops after the flow graph; their ports stay local.
+ for (const g of groups.filter(g => g.parts[0].scope || g.parts[0].stop))
+ graph.children.push({...g, x: 0, y: 0});
const boxes = new Map(graph.children.map(n => [n.id, n]));
const routes = new Map((graph.edges || []).map(e => [e.id, e]));
for (const group of groups) {
@@ -135,6 +157,8 @@ class GraphLayout {
n.x = box.x + n.ox; n.y = box.y + n.oy;
// Keep semantic nodes/slots intact for selection and neighborhood marks.
n.ports = n.n.edges.map(e => {
+ if (n.n.kind === "PHI" && e.idx === 0)
+ return {id: n.id + "i0", x: -6, y: n.height / 2 - 3, width: 6, height: 6, reg: true, edge: e};
const p = ports.get(n.id + "i" + e.idx);
return p && {...p, x: p.x - n.ox, y: p.y - n.oy, edge: e};
}).filter(Boolean);
@@ -169,11 +193,21 @@ class GraphLayout {
}
// Parser ownership is an overlay, never an IR node or an ELK flow edge.
const parser = owned.length ? {width: Math.max(180, (owned.length + 1) * 18), height: 36} : null;
+ // Stop belongs at the bottom even before any Return has attached to it.
+ const foot = [...nodes.filter(n => n.stop), ...(parser ? [parser] : [])];
+ if (foot.length) {
+ const width = foot.reduce((sum, n) => sum + n.width, 0) + (foot.length - 1) * 32;
+ const height = Math.max(...foot.map(n => n.height));
+ graph.width = Math.max(graph.width, width + 48);
+ let x = (graph.width - width) / 2;
+ for (const n of foot) {
+ n.x = x;
+ n.y = graph.height + 24 + height - n.height;
+ x += n.width + 32;
+ }
+ graph.height += height + 48;
+ }
if (parser) {
- graph.width = Math.max(graph.width, parser.width + 48);
- parser.x = (graph.width - parser.width) / 2;
- parser.y = graph.height + 24;
- graph.height = parser.y + parser.height + 24;
owned.sort((a, b) => a.x - b.x || a.n.id - b.n.id);
parser.ports = owned.map((n, i) => ({id: "parser-i" + n.n.id,
x: parser.width * (i + 1) / (owned.length + 1) - 3, y: -3}));
@@ -181,7 +215,8 @@ class GraphLayout {
use: 0, def: n.n.id, role: "PARSER", sources: [n.id + "o"], targets: ["parser-i" + n.n.id]});
}
for (const e of edges) {
- if (e.role === "ASSOC" || e.role === "PARSER" || e.bind || byId.get(e.def).scope) {
+ if (e.role === "ASSOC" || e.role === "PARSER" || e.bind || byId.get(e.def).scope ||
+ byId.get(e.use)?.stop || byId.get(e.def).stop) {
const def = byId.get(e.def), use = e.role === "PARSER" ? parser : byId.get(e.use);
const a = def.ports.find(p => p.id === e.sources[0]);
const b = use.ports.find(p => p.id === e.targets[0]);
diff --git a/graph/web/render.js b/graph/web/render.js
index 34c66a02..883475f5 100644
--- a/graph/web/render.js
+++ b/graph/web/render.js
@@ -77,7 +77,7 @@ class GraphView {
.on("click", (event, n) => { event.stopPropagation(); this.select(n.n.id); });
boxes.select("path.box").attr("d", n => this.shape(n))
.attr("fill", n => ({CTRL: "#fff1c4", REGION: "#fff1c4", LOOP: "#ffe0ab", FUN: "#ffe0ab",
- UNIT: "#ffe0ab", MEM: "#dcecff", PHI: "#f5e4fa", SCOPE: "#e5e0ff", DATA: "#edf3f7"})[n.n.kind]);
+ UNIT: "#ffe0ab", STOP: "#fff1c4", MEM: "#dcecff", PHI: "#f5e4fa", SCOPE: "#e5e0ff", DATA: "#edf3f7"})[n.n.kind]);
boxes.select("text.head").attr("y", n => n.scope ? 47 : 15)
.text(n => `#${n.n.id}${n.n.proj ? " · p" + n.n.proj.idx : ""}`);
boxes.select("text.label").attr("x", n => n.width / 2).attr("y", n => n.scope ? 51 : 33)
@@ -97,8 +97,15 @@ class GraphView {
group.selectAll("circle.port").data(ports, p => p.id).join("circle")
.attr("class", "port").attr("cx", p => p.x + 3).attr("cy", p => p.y + 3)
.attr("r", 3).attr("fill", p => p.edge.def ? "#536477" : "white");
+ group.selectAll("path.reg").data(ports.filter(p => p.reg && p.edge.def), p => p.id).join("path")
+ .attr("class", "reg").attr("fill", "none").attr("stroke", this.colors.CTRL)
+ .attr("d", p => `M-3,${p.y + 3}H-18m4,-3l-4,3l4,3`)
+ .each(function(p) {
+ d3.select(this).selectAll("title").data([p]).join("title")
+ .text(`[0] → #${p.edge.def}`);
+ });
group.selectAll("text.slot").data(ports, p => p.id).join("text")
- .attr("class", "slot").attr("x", p => p.x + 3).attr("y", p => p.y - 4)
+ .attr("class", "slot").attr("x", p => p.reg ? -10 : p.x + 3).attr("y", p => p.y - 4)
.attr("text-anchor", "middle").text(p => p.edge.idx);
});
this.assocs(document.getElementById("assocs").checked);
@@ -110,7 +117,7 @@ class GraphView {
shape(n) {
const w = n.width, h = n.height;
// A MultiNode and its projection cells tile one rectangular box.
- if (n.cell || n.n.kind === "CTRL" || n.n.kind === "UNIT") return `M0,0H${w}V${h}H0Z`;
+ if (n.cell || ["CTRL", "UNIT", "STOP"].includes(n.n.kind)) return `M0,0H${w}V${h}H0Z`;
if (n.n.kind === "REGION") return `M12,0H${w - 12}L${w},${h}H0Z`;
if (n.n.kind === "PHI") return `M12,0H${w - 12}L${w},${h / 2}L${w - 12},${h}H12L0,${h / 2}Z`;
if (n.n.kind === "MEM" || n.n.kind === "FUN")
diff --git a/graph/web/viewer.js b/graph/web/viewer.js
index 63ee195e..4a88245b 100644
--- a/graph/web/viewer.js
+++ b/graph/web/viewer.js
@@ -87,9 +87,13 @@ async function render(index) {
if (generation === frameGeneration) {
renderer.show(frame.snap, frame.layout, frame.evt);
if (frame.pos >= 0) {
- // Stepping focuses a button; focus the source so its selection is visible.
+ // A newline or EOF has no visible character to highlight. Show the last
+ // parsed character there, rather than losing the position indicator.
+ let pos = Math.min(frame.pos, program.value.length);
+ while (pos > 0 && (!program.value[pos] || /\s/.test(program.value[pos]))) pos--;
+ program.setSelectionRange(pos, pos + 1);
+ // Focus after selecting, so the browser brings the new position into view.
program.focus({preventScroll: true});
- program.setSelectionRange(frame.pos, frame.pos + 1);
}
}
rendering = false;