From ed13d1ca215ea094b4d6beab89b90ab4c05ed396 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Tue, 22 Sep 2026 14:59:11 -0700 Subject: Tidy up scope display --- .../main/java/com/seaofnodes/graph/GraphAdapter.java | 7 ++++++- .../main/java/com/seaofnodes/graph/GraphCapture.java | 18 ++++++++++++------ .../src/main/java/com/seaofnodes/graph/GraphJson.java | 2 +- .../main/java/com/seaofnodes/graph/GraphSnapshot.java | 4 ++-- 4 files changed, 21 insertions(+), 10 deletions(-) (limited to 'graph/src') diff --git a/graph/src/main/java/com/seaofnodes/graph/GraphAdapter.java b/graph/src/main/java/com/seaofnodes/graph/GraphAdapter.java index 3e840b54..47bf3444 100644 --- a/graph/src/main/java/com/seaofnodes/graph/GraphAdapter.java +++ b/graph/src/main/java/com/seaofnodes/graph/GraphAdapter.java @@ -37,6 +37,11 @@ public abstract class GraphAdapter { } public final GraphSnapshot snap(String comp, long step, ArrayList roots) { + return snap(comp, step, roots, 0); + } + + /** scope names the active parser scope, which the caller includes in roots. */ + public final GraphSnapshot snap(String comp, long step, ArrayList roots, int scope) { // Node IDs are dense; index directly without boxed keys or map entries. var seen = new ArrayList(); var todo = new ArrayDeque(); @@ -55,7 +60,7 @@ public abstract class GraphAdapter { // Determinism without reordering the compiler's use lists. nodes.sort(Comparator.comparingInt(GraphSnapshot.Node::id)); return new GraphSnapshot(GraphSnapshot.VER, comp, step, - Arrays.copyOf(rids, len), nodes); + Arrays.copyOf(rids, len), scope, nodes); } private boolean enq(N node, ArrayList seen, ArrayDeque todo) { diff --git a/graph/src/main/java/com/seaofnodes/graph/GraphCapture.java b/graph/src/main/java/com/seaofnodes/graph/GraphCapture.java index 6fc40b5f..4be65392 100644 --- a/graph/src/main/java/com/seaofnodes/graph/GraphCapture.java +++ b/graph/src/main/java/com/seaofnodes/graph/GraphCapture.java @@ -23,6 +23,8 @@ public abstract class GraphCapture extends GraphObserver { protected abstract String phase(); protected abstract int pos(); protected abstract void roots(ArrayList roots); + // Called only during parsing; the shared capture adds this root too. + protected N scope() { return null; } protected abstract void detach(); final void run(String src, GraphSocket server) { @@ -65,7 +67,7 @@ public abstract class GraphCapture extends GraphObserver { near(p, n); // Buffer until we know that this attempt (or a nested one) made progress. // Include n explicitly: parse-time nodes need not have any uses yet. - p.pre = snap(n); + p.pre = snap(n, true); } @Override public void after(N n, N repl, boolean applied) { @@ -85,7 +87,7 @@ public abstract class GraphCapture extends GraphObserver { up.sent = true; } N rez = repl == null ? n : repl; - emit(snap(_graph.dead(rez) ? null : rez), pos(), + emit(snap(_graph.dead(rez) ? null : rez, true), pos(), evt(p, applied ? GraphEvent.Kind.APPLY : GraphEvent.Kind.RETURN, _graph.dead(rez) ? 0 : _graph.id(rez))); } @@ -101,7 +103,7 @@ public abstract class GraphCapture extends GraphObserver { @Override public void phase(String phase) { if( _off ) return; - emit(snap(null), pos(), new GraphEvent(GraphEvent.Kind.PHASE, 0, 0, phase, 0, 0, new int[0])); + emit(snap(null, false), pos(), new GraphEvent(GraphEvent.Kind.PHASE, 0, 0, phase, 0, 0, new int[0])); } private GraphEvent evt(Peep p, GraphEvent.Kind kind, int repl) { @@ -121,18 +123,22 @@ public abstract class GraphCapture extends GraphObserver { } - private GraphSnapshot snap(N extra) { + private GraphSnapshot snap(N extra, boolean parsing) { var roots = new ArrayList(); roots(roots); + // Early parsers retain their emptied scope after returning. A completed + // phase no longer owns that scope, even while phase() still says Parse. + N scope = parsing && "Parse".equals(phase()) ? scope() : null; + roots.add(scope); for( Peep p : _peeps ) if( !_graph.dead(p.node) ) roots.add(p.node); roots.add(extra); - return _graph.snap(_comp, 0, roots); + return _graph.snap(_comp, 0, roots, _graph.ref(scope)); } private void emit(GraphSnapshot snap, int pos, GraphEvent evt) { if( _off ) return; // Assign steps on emission; attempts without progress do not consume frames. - snap = new GraphSnapshot(snap.ver(), _comp, _step++, snap.roots(), snap.nodes()); + snap = new GraphSnapshot(snap.ver(), _comp, _step++, snap.roots(), snap.scope(), snap.nodes()); try { _server.put(GraphJson.frame(snap, pos, evt)); } catch( IOException e ) { diff --git a/graph/src/main/java/com/seaofnodes/graph/GraphJson.java b/graph/src/main/java/com/seaofnodes/graph/GraphJson.java index 8726d81b..c920763a 100644 --- a/graph/src/main/java/com/seaofnodes/graph/GraphJson.java +++ b/graph/src/main/java/com/seaofnodes/graph/GraphJson.java @@ -42,7 +42,7 @@ public class GraphJson { if( i > 0 ) sb.append(','); sb.append(roots[i]); } - sb.append("],\"nodes\":["); + sb.append("],\"scope\":").append(snap.scope()).append(",\"nodes\":["); var nodes = snap.nodes(); for( int i = 0; i < nodes.size(); i++ ) { if( i > 0 ) sb.append(','); diff --git a/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java b/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java index 18926726..6836693e 100644 --- a/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java +++ b/graph/src/main/java/com/seaofnodes/graph/GraphSnapshot.java @@ -2,9 +2,9 @@ package com.seaofnodes.graph; import java.util.ArrayList; -/** A detached graph. Treat its lists as read-only after capture. */ +/** A detached graph. Lists are read-only; scope is the active parser scope ID, or zero. */ public record GraphSnapshot(int ver, String comp, long step, - int[] roots, ArrayList nodes) { + int[] roots, int scope, ArrayList nodes) { public static final int VER = 1; public GraphSnapshot { -- cgit v1.2.3