IGraph and planning
The JSON form of a program, and how planGraph checks it once for both backends.
An IGraph is a program as data: a list of nodes and a list of connections. @hlang-org/ir defines its types and planGraph, and has no dependencies.
// The same program as data: an IGraph. A connection's Output is the upstream
// port and its Input is the downstream port.
export const graph = {
nodeList: [
{ nodeId: 'a', code: 'greeting', name: 'Greeting', type: 'R' },
{ nodeId: 'b', code: 'uppercase', name: 'Uppercase', type: 'T' },
{ nodeId: 'c', code: 'print', name: 'Print', type: 'W' },
],
connections: [
{ Output: { nodeId: 'a', portConf: { id: 'out' } }, Input: { nodeId: 'b', portConf: { id: 'in' } } },
{ Output: { nodeId: 'b', portConf: { id: 'out' } }, Input: { nodeId: 'c', portConf: { id: 'in' } } },
],
};Each node has a nodeId (unique in the graph), a code (the node type a backend resolves), a name and a type: 'R', 'T' or 'W'. Parameters go in bizData.params. A connection's Output is the upstream port and its Input is the downstream port.
planGraph
planGraph(graph) checks the graph and returns an ExecutionPlan: its nodes, its connections in graph order, and its Readable roots. The interpreter and the compiler both start from this plan, so they apply the same rules.
import { planGraph, GraphError } from '@hlang-org/ir';
import { graph } from './lib/graph.mjs';
// planGraph checks a graph once and returns the plan both backends use.
const plan = planGraph(graph);
console.log('roots:', plan.roots.join(', '));
for (const { source, target } of plan.connections) {
console.log(`${source.nodeId}.${source.portId} -> ${target.nodeId}.${target.portId}`);
}
// A Writable has no output port, so a connection from it is refused.
const reversed = {
...graph,
connections: [{ Output: { nodeId: 'c', portConf: { id: 'in' } }, Input: { nodeId: 'a', portConf: { id: 'out' } } }],
};
try {
planGraph(reversed);
} catch (error) {
if (error instanceof GraphError) {
console.log(`${error.code}: ${error.message}`);
} else {
throw error;
}
}roots: a
a.out -> b.in
b.out -> c.in
invalid-connection: Writable node "c" has no output ports and cannot be a connection source.Errors
A graph that breaks a rule throws a GraphError with one of these codes:
| Code | Meaning |
|---|---|
duplicate-node-id | Two nodes share a nodeId. |
invalid-node-kind | A node's type is not R, T or W. |
unknown-node | A connection names a node that is not in nodeList. |
invalid-connection | A connection leaves a Writable node, enters a Readable node, or targets the @node/error port, which is an output. |
no-readable-root | The graph has no Readable node to run from. |
The full API is in the ir reference.
The examples on this page run in CI. Edit this page on GitHub