Skip to content

Instantly share code, notes, and snippets.

@JasonKleban
Last active October 22, 2019 16:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JasonKleban/e0b8ab1ef0a6baf5148b6d0ba33bbd7c to your computer and use it in GitHub Desktop.
Save JasonKleban/e0b8ab1ef0a6baf5148b6d0ba33bbd7c to your computer and use it in GitHub Desktop.
raph.ts - DSL for specifying immutable graphs of nodes and edges.
/**
* DSL for specifying graphs of nodes and edges.
* Graphs are immutable and create a new graph object for each operation.
* Graphs are stateful about a subject node and a subject edge for purposes of
* creating new edges and adding attributes to the subject node or subject edge.
* From the perspective of a user DSL code listing, the last _mentioned_ node is the
* subject node to which new edges or attributes are added and the last mentioned
* edge is the subject edge to which attributes are added.
*/
namespace Raph {
interface G<NA, EA> {
/** Focus on a node by key. If the node key wasn't known, it is added. */
n(key: string): Gn<NA, EA>
readonly nodes: N<NA>[]
readonly edges: E<EA>[]
dot(): string;
}
interface Gn<NA, EA> extends G<NA, EA> {
/** Add an edge between the subject node and the node by key. If the node key wasn't known, it is added. */
en(key: string): Gn<NA, EA> & Ge<NA, EA>
/** Set the attributes of the subject node */
na(attributes : NA) : Gn<NA, EA>
}
interface Ge<NA, EA> extends Gn<NA, EA> {
/** Set the attributes of the subject edge */
ea(attributes : EA) : Gn<NA, EA> & Ge<NA, EA>
}
interface N<NA> {
key: string
attributes?: NA
}
interface E<EA> {
l: string
r: string
attributes?: EA
}
class Graph<NA, EA> implements G<NA, EA> {
constructor (
public readonly nodes: N<NA>[] = [],
public readonly edges: E<EA>[] = []) { }
n(key: string): Gn<NA, EA> {
return new GraphWithSubjNode<NA, EA>(
!!(this.nodes || []).filter(n => n.key === key)[0]
? this.nodes
: [ ... this.nodes, { key } ],
this.edges,
key
);
}
dot = () => `digraph G {\n${ "".concat(... this.edges.map(e => ` "${e.l}" -> "${e.r}"\n`)) }}`;
}
class GraphWithSubjNode<NA, EA> extends Graph<NA, EA> implements Gn<NA, EA> {
constructor (
nodes: N<NA>[],
edges: E<EA>[],
public readonly subjectNodeKey: string) {
super(nodes, edges);
}
en(key: string): Gn<NA, EA> & Ge<NA, EA> {
const newEdge : E<EA> = {
l: this.subjectNodeKey,
r: key
}
return new GraphWithSubjEdge<NA, EA>(
!!(this.nodes || []).filter(n => n.key === key)[0]
? this.nodes
: [ ... this.nodes, { key } ],
[
... this.edges,
newEdge
],
key,
newEdge
);
}
na(attributes: NA): Gn<NA, EA> {
const newNodes = this.nodes.slice();
newNodes.splice(
this.nodes.findIndex(n => n.key === this.subjectNodeKey),
1,
{ key: this.subjectNodeKey, attributes });
return new GraphWithSubjNode<NA, EA>(
newNodes,
this.edges,
this.subjectNodeKey
);
}
}
class GraphWithSubjEdge<NA, EA> extends GraphWithSubjNode<NA, EA> implements Ge<NA, EA> {
constructor (
nodes: N<NA>[],
edges: E<EA>[],
subjectNodeKey: string,
public readonly subjectEdge: E<EA>) {
super(nodes, edges, subjectNodeKey);
}
ea(attributes: EA): Gn<NA, EA> & Ge<NA, EA> {
const newEdgesWithSubjectEdge = this.edges.map(e =>
e !== this.subjectEdge
? { e, isSubjectEdge: false }
: {
e: {
l: e.l,
r: e.r,
attributes
},
isSubjectEdge: true
});
const newEdges = newEdgesWithSubjectEdge.map(({ e }) => e);
const newSubjectEdge = newEdgesWithSubjectEdge.map(({ e }) => e)[0];
return new GraphWithSubjEdge<NA, EA>(
this.nodes,
newEdges,
this.subjectNodeKey,
newSubjectEdge
);
}
}
export const g = <NA = void, EA = void>() : G<NA, EA> => new Graph<NA, EA>();
export const from = <NA = void, EA = void>(obj: unknown): G<NA, EA> => {
const obj_ = obj as any;
return new Graph<NA, EA>(
obj_.nodes.map((n : any) => ({ key: n.key, attributes: n.attributes })),
obj_.edges.map((e : any) => ({ l: e.l, r: e.r, attributes: e.attributes }))
)
}
}
const x = Raph.g<{ label: string }, { label: string }>()
.n("A").en("B").en("C").ea({ label: "bc" })
.n("D").na({ label: "d" })
.n("A").en("D");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment