Forráskód Böngészése

Fix all compilation errors and tests. GraphViz renders 'GraphState' like objects that have fields.

Joeri Exelmans 1 éve
szülő
commit
c9f13e85c2

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 14373
package-lock.json


+ 40 - 18
src/frontend/versioned_model/graph_view.tsx

@@ -4,6 +4,7 @@ import { GraphvizComponent } from '../graphviz';
 
 import {D3Graph, D3GraphData, D3NodeData, defaultGraphForces} from "../d3graph/d3graph";
 import {InfoHoverCardOverlay} from "../info_hover_card";
+import { D3OnionGraphData } from "../d3graph/reducers/onion_graph";
 
 function esc(str) {
   return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
@@ -22,12 +23,39 @@ const graphvizMap = new Map();
 
 export type Renderer = 'd3' | 'graphviz';
 
+export function genericGraphVizLayout<N,L>(graphViewId: string, graphData: D3GraphData<N,L>) {
+  return `digraph {
+    bgcolor="transparent";
+    ${graphData.nodes.map(node => `"${esc(node.id)}" [${node.label===""?`shape=circle, width=0.3, height=0.3`:`shape=box`}, label="${esc(node.label)}", fillcolor="${node.color}", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${graphViewId+node.id}', 2)`)}"]`).join('\n')}
+    ${graphData.links.map(link => `"${esc(link.source.id || link.source)}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')}
+  }`;
+}
+
+function outgoingIsValue(o) {
+  return (o.target.id || o.target).startsWith("V");
+}
+
+export function objectLikeGraphVizLayout(graphViewId: string, graphData: D3OnionGraphData) {
+  return `digraph {
+    bgcolor="transparent";
+    node[shape=record];
+    ${graphData.nodes.filter(node => !node.id.startsWith("V")).map(node => {
+      const outgoing = graphData.links.filter(link => (link.source.id || link.source) === node.id);
+      return `"${esc(node.id)}" [label="{ ${["ID", ...outgoing.filter(o => outgoingIsValue(o)).map(o => `${o.label}`)].join('|')} } | { ${[esc(node.label), ...outgoing.filter(o => outgoingIsValue(o)).map(o => `${esc(o.target.label)}`)].join('|')} }", fillcolor="white", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${graphViewId+node.id}', 2)`)}"]`;
+    }).join('\n')}
+
+    ${graphData.links.filter(link => !link.target.id.startsWith("V")).map(link => `"${esc(link.source.id || link.source)}":"L${link.label}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')}
+  }`;
+}
+
+
 interface GraphViewProps<N,L> {
+  graphvizLayout: (graphViewId: string, graphData: D3GraphData<N,L>) => string;
   graphData: D3GraphData<N,L>;
   help: JSX.Element;
   graphvizHelp?: JSX.Element;
   mouseUpHandler: (event, mouse:{x:number, y:number}, node?: D3NodeData<N>) => void;
-  defaultRenderer?: Renderer;
+  defaultRenderer: Renderer;
   children?: any;
 }
 
@@ -44,26 +72,20 @@ export function GraphView<N,L>(props: GraphViewProps<N,L>) {
     }
   }
 
-  const dot1 = `digraph {
-    bgcolor="transparent";
-    ${graphData.nodes.map(node => `"${esc(node.id)}" [${node.label===""?`shape=circle, width=0.3, height=0.3`:`shape=box`}, label="${esc(node.label)}", fillcolor="${node.color}", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${id+node.id}', 2)`)}"]`).join('\n')}
-    ${graphData.links.map(link => `"${esc(link.source.id || link.source)}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')}
-  }`;
-
-  const dot2 = `digraph {
-    bgcolor="transparent";
-    node[shape=record];
-    ${graphData.nodes.filter(node => !node.id.startsWith("V")).map(node => {
-      const outgoing = graphData.links.filter(link => (link.source.id || link.source) === node.id);
-      return `"${esc(node.id)}" [label="{ ${["", ...outgoing.map(o => `${o.label}`)].join('|')} } | { ${[esc(node.label), ...outgoing.map(o => (o.target.id || o.target).startsWith("V") ? `${esc(o.target.label)}` : `<L${o.label}>`)].join('|')} }", fillcolor="${node.color}", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${id+node.id}', 2)`)}"]`;
-    }).join('\n')}
+  // const dot2 = `digraph {
+  //   bgcolor="transparent";
+  //   node[shape=record];
+  //   ${graphData.nodes.filter(node => !node.id.startsWith("V")).map(node => {
+  //     const outgoing = graphData.links.filter(link => (link.source.id || link.source) === node.id);
+  //     return `"${esc(node.id)}" [label="{ ${["", ...outgoing.map(o => `${o.label}`)].join('|')} } | { ${[esc(node.label), ...outgoing.map(o => (o.target.id || o.target).startsWith("V") ? `${esc(o.target.label)}` : `<L${o.label}>`)].join('|')} }", fillcolor="${node.color}", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${graphViewId: string, +node.id}', 2)`)}"]`;
+  //   }).join('\n')}
 
-    ${graphData.links.filter(link => !link.target.id.startsWith("V")).map(link => `"${esc(link.source.id || link.source)}":"L${link.label}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')}
-  }`;
+  //   ${graphData.links.filter(link => !link.target.id.startsWith("V")).map(link => `"${esc(link.source.id || link.source)}":"L${link.label}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')}
+  // }`;
 
   // @ts-ignore:
   const graphviz = <Mantine.ScrollArea style={{backgroundColor:"#eee"}}>
-      <GraphvizComponent dot={dot2}
+      <GraphvizComponent dot={props.graphvizLayout(id, graphData)}
       />
     </Mantine.ScrollArea>;
 
@@ -80,7 +102,7 @@ export function GraphView<N,L>(props: GraphViewProps<N,L>) {
       />
       </Mantine.Tooltip>
       {props.children}
-      <Mantine.Button onClick={()=>alert(dot2)}>Get Graphviz dot</Mantine.Button>
+      {/* <Mantine.Button onClick={()=>alert(dot2)}>Get Graphviz dot</Mantine.Button> */}
     </Mantine.Group>
     {renderer==="d3"?
       <InfoHoverCardOverlay contents={help}>

+ 2 - 1
src/frontend/versioned_model/merge_view.tsx

@@ -6,7 +6,7 @@ import {Version} from "onion/version";
 import {Delta} from "onion/delta";
 import {DeltaParser} from "onion/delta_parser";
 import {VersionParser} from "onion/version_parser";
-import {GraphView} from "./graph_view";
+import {genericGraphVizLayout, GraphView} from "./graph_view";
 import {fullVersionId, HistoryGraphState, historyGraphReducer} from "../d3graph/reducers/history_graph";
 import {InfoHoverCardOverlay} from "../info_hover_card";
 import {OnionContext, OnionContextType} from "../onion_context";
@@ -138,6 +138,7 @@ export function MergeView({history, forces, versionRegistry, onMerge, onGoto, de
 
   return <>
     <GraphView<Version,Delta> graphData={historyFiltered}
+        graphvizLayout={genericGraphVizLayout}
         help={historyGraphHelpText} 
         graphvizHelp={graphvizHelpText}
         mouseUpHandler={(e, {x, y}, node) => {

+ 6 - 2
src/frontend/versioned_model/single_model.tsx

@@ -20,7 +20,7 @@ import {
 
 import * as helpText from "./help_text";
 import {MergeView} from "./merge_view";
-import {GraphView, Renderer} from "./graph_view";
+import {GraphView, Renderer, genericGraphVizLayout, objectLikeGraphVizLayout} from "./graph_view";
 
 import {D3Graph, emptyGraph, defaultGraphForces} from "../d3graph/d3graph";
 import {RountangleEditor} from "../rountangleEditor/RountangleEditor";
@@ -220,7 +220,10 @@ export function newOnion({readonly, deltaRegistry, versionRegistry}) {
     const callbacks = Object.assign({}, defaultCallbacks, overridenCallbacks(reducer));
 
     const graphStateComponent = readonly ? 
-          <GraphView graphData={graph} help={helpText.graphEditorReadonly} mouseUpHandler={()=>{}} />
+          <GraphView
+            defaultRenderer="graphviz"
+            graphvizLayout={objectLikeGraphVizLayout}
+            graphData={graph} help={helpText.graphEditorReadonly} mouseUpHandler={()=>{}} />
         : <InfoHoverCardOverlay contents={helpText.graphEditor}>
             <D3GraphEditable
               graph={graph}
@@ -232,6 +235,7 @@ export function newOnion({readonly, deltaRegistry, versionRegistry}) {
           </InfoHoverCardOverlay>;
 
     const deltaComponentProps = {
+      graphvizLayout: genericGraphVizLayout,
       help: helpText.deltaGraph,
       defaultRenderer: ('graphviz' as Renderer),
       mouseUpHandler: (e, {x,y}, node) => {

+ 1 - 1
src/onion/delta.ts

@@ -302,7 +302,7 @@ export class EdgeUpdate extends PrimitiveDelta {
   readonly overwritable: ExistingEdge;
 
   constructor(hash: Buffer, overwrites: Edge, target: Target, reads: ExistingEdge[]) {
-    super(hash, `U(${overwrites.label} -> ${target.value})`);
+    super(hash, `U(${overwrites.label}->${target.value instanceof NodeCreation ? target.value.description : target.value})`);
     // Record our own dependencies:
     this.overwrites = overwrites;
     this.target = target;

+ 12 - 15
src/onion/version.test.ts

@@ -145,12 +145,12 @@ describe("Version", () => {
 
       const A = deltaRegistry.newNodeDeletion(X, [], []);
       const B = deltaRegistry.newEdgeUpdate(X.createOutgoingEdge("label"), Y); // conflicts with A
-      assert(_.isEqual(B.conflictsWith, [A]), "Expected B to conflict with A");
+      assert(_.isEqual(B.conflictsWith, [[A, 'U/D']]), "Expected B to conflict with A");
       const C = deltaRegistry.newEdgeUpdate(Y.createOutgoingEdge("label"), Z);
       const BB = deltaRegistry.newEdgeUpdate(B.overwrite(), null); // unset edge B.
       const D = deltaRegistry.newNodeDeletion(Y, [], [BB]); // conflicts with C
 
-      assert(_.isEqual(D.conflictsWith, [C]), "Expected D to conflict with C");
+      assert(_.isEqual(D.conflictsWith, [[C, 'U/D']]), "Expected D to conflict with C");
 
       const nameMap: Map<Delta, string> = new Map<Delta,string>([
         [X, "X"],
@@ -245,7 +245,6 @@ describe("Version", () => {
       const deltaRegistry = new DeltaRegistry();
       const getId = mockUuid();
       const registry = new VersionRegistry();
-      // const comp = new CompositeLevel();
 
       const guestCreate = deltaRegistry.newNodeCreation(getId());
       const guestV1 = registry.createVersion(registry.initialVersion, guestCreate);
@@ -253,26 +252,27 @@ describe("Version", () => {
       const guestDel = deltaRegistry.newNodeDeletion(guestCreate, [], []);
       const guestV2 = registry.createVersion(guestV1, guestDel);
 
-
       const hostCreate = deltaRegistry.newNodeCreation(getId());
       const hostLink = deltaRegistry.newEdgeUpdate(hostCreate.createOutgoingEdge("guest"), guestCreate);
-      const compCreate = deltaRegistry.newTransaction([guestCreate, hostCreate, hostLink], "");
-      const hostV1 = registry.createVersion(registry.initialVersion, compCreate, () => new Map([
+      const allCreate = deltaRegistry.newTransaction([guestCreate, hostCreate, hostLink], "");
+      const hostV1 = registry.createVersion(registry.initialVersion, allCreate, () => new Map([
         ["guest", {version: guestV1, overridings: new Map()}], // no overridings
       ]));
 
-      const hostDel = deltaRegistry.newNodeDeletion(hostCreate, [hostLink], []);
-      const guestDelOver = deltaRegistry.newNodeDeletion(guestCreate, [], [hostDel]);
-      const compDel = deltaRegistry.newTransaction([hostDel, guestDelOver], "");
+      const hostUnlink = deltaRegistry.newEdgeUpdate(hostLink.overwrite(), null);
+      const hostDel = deltaRegistry.newNodeDeletion(hostCreate, [hostUnlink], []);
+
+      const guestDelOverride = deltaRegistry.newNodeDeletion(guestCreate, [], [hostUnlink]);
+      const allDel = deltaRegistry.newTransaction([hostUnlink, hostDel, guestDelOverride], "");
 
       assertThrows(() => {
-        registry.createVersion(hostV1, compDel, () => new Map([
+        registry.createVersion(hostV1, allDel, () => new Map([
           ["guest", {version: guestV2, overridings: new Map()}]
         ]));
       }, "should not be able to create host version without explicitly stating that guestDel was overridden by guestDelOver");
 
-      const hostV2 = registry.createVersion(hostV1, compDel, () => new Map([
-          ["guest", {version: guestV2, overridings: new Map([[guestDel, guestDelOver]])}],
+      const hostV2 = registry.createVersion(hostV1, allDel, () => new Map([
+          ["guest", {version: guestV2, overridings: new Map([[guestDel, guestDelOverride]])}],
         ]));
     });
 
@@ -280,7 +280,6 @@ describe("Version", () => {
       const deltaRegistry = new DeltaRegistry();
       const getId = mockUuid();
       const registry = new VersionRegistry();
-      // const comp = new CompositeLevel();
 
       const createA = deltaRegistry.newNodeCreation(getId());
       const createB = deltaRegistry.newNodeCreation(getId());
@@ -331,8 +330,6 @@ describe("Version", () => {
       const deltaRegistry = new DeltaRegistry();
       const getId = mockUuid();
       const registry = new VersionRegistry();
-      // const L1 = new CompositeLevel();
-      // const L2 = new CompositeLevel();
 
       const createA = deltaRegistry.newNodeCreation(getId());
       const createB = deltaRegistry.newNodeCreation(getId());

+ 41 - 28
src/onion/version.ts

@@ -5,13 +5,13 @@ import {permutations} from "../util/permutations";
 import {bufferXOR} from "./buffer_xor";
 import {PrimitiveDelta, Transaction} from "./delta";
 
-import * as _ from "lodash";
+// import * as _ from "lodash";
 
 import {
   Delta,
-  isConflicting,
-  iterMissingDependencies,
-  iterConflicts,
+  // isConflicting,
+  // iterMissingDependencies,
+  // iterConflicts,
 } from "./delta";
 
 // The difference between two consecutive versions.
@@ -180,7 +180,7 @@ export class Version {
       version.serializeInternal(alreadyHaveVersions, alreadyHaveDeltas, deltas, versions);
       const ovr = {};
       for (const [key,val] of overridings.entries()) {
-        ovr[key.getHash().toString('base64')] = val.getHash().toString('base64');
+        ovr[key.hash.toString('base64')] = val.hash.toString('base64');
       }
       embeddings.push({guestId, v: version.hash.toString('base64'), ovr});
     }
@@ -204,7 +204,7 @@ export class Version {
       versions.push({
         id: this.hash.toString('base64'),
         parent: parentVersion.hash.toString('base64'),
-        delta: delta.getHash().toString('base64'),
+        delta: delta.hash.toString('base64'),
         embeddings,
       })
     }
@@ -213,6 +213,16 @@ export class Version {
 
 const initialHash = Buffer.alloc(32); // all zeros
 
+function isConflicting(deltaA: Delta, deltaB: Delta) {
+  // for performance, iterate over the delta that has the least conflicts:
+  if (deltaA.conflictsWith.length < deltaB.conflictsWith.length) {
+    return deltaA.conflictsWith.some(([d]) => d === deltaB);
+  }
+  else {
+    return deltaB.conflictsWith.some(([d]) => d === deltaA);
+  }
+}
+
 export class VersionRegistry {
   readonly initialVersion: Version = new Version([], initialHash, 0, () => new Map());
 
@@ -245,15 +255,17 @@ export class VersionRegistry {
   //   then all of the guest versions' deltas must exist in the host version, or be explicitly overridden.
   createVersion(parent: Version, delta: DiffType, embeddings: (Version) => Embeddings = () => new Map()): Version {
     // Check pre-condition 1:
-    const missingDependency = iterMissingDependencies(delta, parent).next().value;
-    if (missingDependency !== undefined) {
-      throw new Error("Missing dependency: " + missingDependency.description);
+    for (const [dep] of delta.getDependencies()) {
+      if (!parent.contains(dep)) {
+        throw new Error("createVersion: precondition failed: Missing dependency: " + dep.description);
+      }
     }
 
     // Check pre-condition 2:
-    const conflictsWith = iterConflicts(delta, parent).next().value;
-    if (conflictsWith !== undefined) {
-      throw new Error("Delta " + delta.description + " conflicts with " + conflictsWith.description);
+    for (const [conflictingDelta] of delta.conflictsWith) {
+      if (parent.contains(conflictingDelta)) {
+        throw new Error("createVersion: precondition failed: Delta " + delta.description + " conflicts with " + conflictingDelta.description);
+      }
     }
 
     const newVersion = this.createVersionUnsafe(parent, delta, embeddings);
@@ -266,7 +278,7 @@ export class VersionRegistry {
       for (const [_, guestDelta] of guestDiff) {
         for (const guestPDelta of guestDelta.iterPrimitiveDeltas()) {
           if (!primitiveDeltas.includes(overridings.get(guestPDelta) || guestPDelta)) {
-            throw new Error("Guest's primitive delta " + guestPDelta.description + " does not occur in host, nor is it overridden.");
+            throw new Error("createVersion: precondition failed: Guest's primitive delta " + guestPDelta.description + " does not occur in host, nor is it overridden.");
           }
         }
       }
@@ -278,7 +290,7 @@ export class VersionRegistry {
   // Faster than createVersion, but does not check pre-conditions.
   // Idempotent
   createVersionUnsafe(parent: Version, delta: DiffType, embeddings: (Version) => Embeddings = () => new Map()): Version {
-    const newHash = bufferXOR(parent.hash, delta.getHash());
+    const newHash = bufferXOR(parent.hash, delta.hash);
     // TODO: include embeddings in hash digest.
     const existingVersion = this.lookupOptional(newHash);
     if (existingVersion !== undefined) {
@@ -395,7 +407,6 @@ export class VersionRegistry {
         ancestorsB.push(parent);
       }
     }
-    throw new Error("Unexpected error: Versions have no LCA!")
   }
 
   getLCA(versions: Array<Version>): Version {
@@ -426,7 +437,7 @@ export class VersionRegistry {
     }
 
     const lca = this.getLCA(versions);
-    printDebug("lca:", ...lca);
+    // printDebug("lca:", ...[...lca].map(d => d.description));
 
     // Tuple
     //    Delta: a delta that is at least one of 'versions' but not in 'lca'
@@ -485,18 +496,18 @@ export class VersionRegistry {
     // When nothing can be added anymore without introducing a conflict, we have a 'maximal version' (a result).
     // It's possible that there is more than one 'maximal version'.
     // Precondition: We assume that any single delta of 'deltasToTry' will not be conflicting with 'startVersion'.
-    const depthFirst = (startVersion: Version, deltasToTry: Array<DeltaWithEmbedding>, depth: number) => {
+    const depthFirst = (startVersion: Version, candidates: Array<DeltaWithEmbedding>, depth: number) => {
       function printIndent(...args) {
         printDebug("  ".repeat(depth), ...args);
       }
-      // printIndent("deltasToTry=", ...deltasToTry);
+      printIndent("deltasToTry=", ...candidates.map(([d])=>d));
 
       let couldNotRecurse = true;
 
-      for (const [delta, guestDeltaMap] of deltasToTry) {
-        const missingDependency = iterMissingDependencies(delta, startVersion).next().value; // just get the first element from the iterator
-        if (missingDependency !== undefined) {
-          // printIndent("missing dependency:", delta, "->", missingDependency)
+      for (const [delta, guestDeltaMap] of candidates) {
+        const haveMissingDependency = delta.getDependencies().some(([dependency]) => !startVersion.contains(dependency));
+        if (haveMissingDependency) {
+          printIndent("missing dependency, trying next delta")
           continue; // skip this delta, but keep it in deltasToTry (its missing dependency may be in deltasToTry)
         }
 
@@ -510,7 +521,7 @@ export class VersionRegistry {
           const selfEmbeddingKeys = new Set<string>();
           for (const [guestId, [guestDelta, overridings]] of guestDeltaMap.entries()) {
             const {version: guestStartVersion} = startVersion.getEmbedding(guestId);
-            console.log(guestId, "guestStartVersion: ", guestStartVersion);
+            // console.log(guestId, "guestStartVersion: ", guestStartVersion);
             let nextGuestVersion;
             if (guestDelta === null) {
               nextGuestVersion = guestStartVersion;
@@ -525,6 +536,7 @@ export class VersionRegistry {
             }
             embeddings.set(guestId, {version: nextGuestVersion, overridings});
           }
+          printIndent("Creating version (", delta, ...[...startVersion].map(d => d), ") with embeddings", embeddings);
           const nextVersion = this.createVersion(startVersion, delta, newVersion => {
             // add self-embeddings:
             for (const guestId of selfEmbeddingKeys) {
@@ -532,16 +544,17 @@ export class VersionRegistry {
             }
             return embeddings;
           });
-          console.log("Created version ", nextVersion, " with embeddings", embeddings);
+          printIndent("Created version (", ...[...nextVersion].map(d => d), ") with embeddings", embeddings);
           return nextVersion;
         }
 
         const nextVersion = createMergedVersionRecursive(startVersion, [delta, guestDeltaMap]);
 
         // current delta does not have to be included again in next loop iterations
-        deltasToTry = deltasToTry.filter(([d]) => d !== delta);
+        candidates = candidates.filter(([d]) => d !== delta);
 
-        const [conflicting, nonConflicting] = _.partition(deltasToTry, ([d]) => isConflicting(d, delta));
+        // const [conflicting, nonConflicting] = _.partition(deltasToTry, ([d]) => isConflicting(d, delta));
+        const nonConflicting = candidates.filter(([candidate]) => !isConflicting(candidate, delta));
 
         // if (conflicting.length > 0)
         //   printIndent("will be skipped (conflicts with", delta, "):", ...conflicting);
@@ -549,7 +562,7 @@ export class VersionRegistry {
         depthFirst(nextVersion, nonConflicting, depth+1);
         couldNotRecurse = false;
 
-        if (conflicting.length === 0) {
+        if (nonConflicting.length === candidates.length) {
           // all deltas from deltasToTry were included -> no need to try alternatives
           break;
         }
@@ -558,7 +571,7 @@ export class VersionRegistry {
       if (couldNotRecurse) {
         // possibly have a new maximal version
         if (!result.some(v => startVersion.isSubSetOf(v))) {
-          // printIndent("new result");
+          printIndent("new result");
           result.push(startVersion);
         }
       }

+ 0 - 2
src/parser/rountangle_parser.test.ts

@@ -7,13 +7,11 @@ import {
 import {
   NodeCreation,
   NodeDeletion,
-  EdgeCreation,
   EdgeUpdate,
 } from "../onion/delta";
 
 import {DeltaRegistry} from "../onion/delta_registry";
 
-import {mockUuid} from "../onion/test_helpers";
 import {assert} from "../util/assert";
 
 // describe("Trivial Parser", () => {

+ 19 - 17
src/parser/rountangle_parser.ts

@@ -1,11 +1,10 @@
-import {Delta, PrimitiveDelta} from "../onion/delta";
+import {Delta, ExistingEdge, PrimitiveDelta} from "../onion/delta";
 import {PrimitiveValue, UUID} from "../onion/types";
 import {visitPartialOrdering} from "../util/partial_ordering";
 
 import {
   NodeCreation,
   NodeDeletion,
-  EdgeCreation,
   EdgeUpdate,
 } from "../onion/delta";
 
@@ -95,9 +94,9 @@ export class RountangleParser  {
           // Creations are easy, and never cause conflicts:
           const sourceCreation = sourceDelta; // alias for readability :)
           const corrCreation = this.deltaRegistry.newNodeCreation(this.getUuid());
-          const corr2Source = this.deltaRegistry.newEdgeCreation(corrCreation, corr2SourceLabel, sourceCreation);
+          const corr2Source = this.deltaRegistry.newEdgeUpdate(corrCreation.createOutgoingEdge(corr2SourceLabel), sourceCreation);
           const targetCreation = this.deltaRegistry.newNodeCreation(this.getUuid());
-          const corr2Target = this.deltaRegistry.newEdgeCreation(corrCreation, corr2TargetLabel, targetCreation);
+          const corr2Target = this.deltaRegistry.newEdgeUpdate(corrCreation.createOutgoingEdge(corr2TargetLabel), targetCreation);
 
           // We also update the corrState, in-place, for every change that is the result of parsing/rendering
           applyToState(corrState, sourceCreation);
@@ -119,14 +118,14 @@ export class RountangleParser  {
               ["height", 60],
             ];
             targetDeltas.push(...edges.map(([edgeLabel, value]) =>
-              this.deltaRegistry.newEdgeCreation(targetCreation, edgeLabel, value)));
+              this.deltaRegistry.newEdgeUpdate(targetCreation.createOutgoingEdge(edgeLabel), value)));
 
             complete = false; // actual geometry of new rountangle to be determined by user
           }
         }
         else if (sourceDelta instanceof NodeDeletion) {
           const sourceDeletion = sourceDelta; // alias for readability :)
-          const sourceCreation = sourceDeletion.creation; // the NodeCreation of the deleted cs node
+          const sourceCreation = sourceDeletion.node; // the NodeCreation of the deleted cs node
           const sourceId = sourceCreation.id;
 
           // Follow 'cs'/'as' edges in correspondence model to find the corresponding node:
@@ -158,15 +157,17 @@ export class RountangleParser  {
           targetDeltas.push(...targetDeletion);
           corrDeltas.push(...corrDeletion);
         }
-        else { // sourceDelta is EdgeCreation or EdgeUpdate
+        else if (sourceDelta instanceof EdgeUpdate) { // sourceDelta is EdgeCreation or EdgeUpdate
           if (!parse) {
             // Special case: an edge is being deleted (i.e., its target is set to null) and the previous target of the edge is a node that's being deleted - in this case, we *can* render the result (and we don't even have to do anything).
             if (sourceDelta instanceof EdgeUpdate) {
-              const target = sourceDelta.target.getTarget();
+              const target = sourceDelta.target.value;
               if (target === null) {
-                const prevTarget = sourceDelta.overwrites.target.getTarget();
-                if (sourceDeltas.some(d => d instanceof NodeDeletion && d.creation === prevTarget)) {
-                  continue;
+                if (sourceDelta.overwrites instanceof ExistingEdge) {
+                  const prevTarget = sourceDelta.overwrites.delta.target.value;
+                  if (sourceDeltas.some(d => d instanceof NodeDeletion && d.node === prevTarget)) {
+                    continue;
+                  }
                 }
               }
             }
@@ -176,8 +177,9 @@ export class RountangleParser  {
             continue;
           }
 
-          const edgeCreation = (sourceDelta as (EdgeCreation | EdgeUpdate)).getCreation();
-          const label = edgeCreation.label;
+          // const edgeCreation = (sourceDelta as (EdgeCreation | EdgeUpdate)).getCreation();
+          // const label = edgeCreation.label;
+          const label = sourceDelta.overwrites.label;
 
           function findCorrespondingAsNode(csNode: INodeState, reverse: boolean = false) {
             const pair = csNode.getIncomingEdges().find(([label]) => label===(reverse?"as":"cs"));
@@ -193,7 +195,7 @@ export class RountangleParser  {
           }
 
           if (geometryLabels.includes(label)) {
-            const updatedNodeId = edgeCreation.source.id;
+            const updatedNodeId = sourceDelta.overwrites.source.id;
             const updatedGeometry = getGeometry(sourceState, updatedNodeId);
             if (updatedGeometry !== undefined) {
               const updatedSurface = updatedGeometry.w * updatedGeometry.h;
@@ -209,7 +211,7 @@ export class RountangleParser  {
                   if (otherNodeState.isDeleted) {
                     continue; // skip deleted rountangles
                   }
-                  if (otherNodeState.creation === edgeCreation.source) {
+                  if (otherNodeState.creation === sourceDelta.overwrites.source) {
                     continue; // don't compare with ourselves
                   }
                   const otherGeometry = getGeometry(sourceState, otherNodeId);
@@ -279,7 +281,7 @@ export class RountangleParser  {
                 if (otherNodeState.isDeleted) {
                   continue; // no point comparing ourselves with deleted rountangles
                 }
-                if (otherNodeState.creation === edgeCreation.source) {
+                if (otherNodeState.creation === sourceDelta.overwrites.source) {
                   continue; // don't compare with ourselves
                 }
                 const otherGeometry = getGeometry(sourceState, otherNodeId);
@@ -326,7 +328,7 @@ export class RountangleParser  {
       ...sourceDeltas.map(d => sourceOverrides.get(d) || d),
       ...targetDeltas.map(d => targetOverrides.get(d) || d),
       ...corrDeltas],
-      (d: Delta) => d.getDependencies(),
+      (d: Delta) => d.getDependencies().map(([d]) => d),
       (d: Delta) => corrDeltasOrderedByDependency.push(d)
     );
 

+ 1 - 0
todo.txt

@@ -64,3 +64,4 @@ Cip:
        the optimal implementatoin of functional languages
        (nice rabbit hole)
        garbage collection for free
+   - ian piumarta