Skip to content

thevtm/GraMa

Repository files navigation

GraMa

GraMa is a graph programming language. It stands for Graph Machine.

Programs are directed graphs made of edges (source → identifier → target). There is no syntax tree — computation is encoded as relationships between nodes. A query engine interprets the graph, and a compiler emits JavaScript from the same structure.

How it works

Everything is an edge

A GraMa program is a set of edges. Each edge connects a source node to a target node through an identifier (the relationship type).

call --type--> sum
call --arg-->  1
call --arg-->  2

In TypeScript this looks like:

const [$call] = variable();

const edges = [
  { source: $call, identifier: typeRel, target: sumNode },
  { source: $call, identifier: argRel, target: numberNode(1) },
  { source: $call, identifier: argRel, target: numberNode(2) },
];

Primitives define rules

Primitives like sum are also graphs. They declare:

  • A pattern to match (node of type sum with two arg edges)
  • A compute function (how to evaluate it)
  • An emit function (how to render it as JS)
// sum primitive (simplified)
const sumEdges = [
  { source: $x, identifier: typeRel,    target: sumNode },
  { source: $x, identifier: argRel,     target: $a },
  { source: $x, identifier: argRel,     target: $b },
  { source: sumNode, identifier: emitRel,      target: emitFn((args) => `${args[0]} + ${args[1]}`) },
  { source: sumNode, identifier: computeRel,   target: computeFn(/* adds $a + $b */) },
];

Query engine: interpret

The query engine backward-chains over edges to compute results:

const results = query(graph, { source: $call, identifier: resultRel });
// → 1 + 2 = 3

Compiler: emit JavaScript

The compiler detects computation structure in the graph and emits JS:

const js = emitProgram(buildProgram(edges, compGraph));

Examples

See the examples/ directory for the full set. Run any example with:

pnpm ts-node "examples/02 - sum(1, 2, 3).ts"

Arithmetic: sum(1, 2) + 3

Two chained sum calls — the result of the first feeds into the second:

const [$call1] = variable();
const [$call2] = variable();
const [$res1] = variable();
const [$result] = anchor("result");

const edges = [
  // sum(1, 2)
  { source: $call1, identifier: typeRel, target: sumNode },
  { source: $call1, identifier: argRel,  target: numberNode(1) },
  { source: $call1, identifier: argRel,  target: numberNode(2) },
  { source: $call1, identifier: resultRel, target: $res1 },

  // sum(sum(1,2), 3)
  { source: $call2, identifier: typeRel, target: sumNode },
  { source: $call2, identifier: argRel,  target: $res1 },
  { source: $call2, identifier: argRel,  target: numberNode(3) },
  { source: $call2, identifier: resultRel, target: $result },
];

Compiles to:

function sum(arg0, arg1) {
  return arg0 + arg1;
}

const _v0 = sum(1, 2);
const result = sum(_v0, 3);

Conditionals: classify(x)

Multiple rules for the same type create branches. Each rule has an existential constraint — eq(x, 3) must equal true or false — and a different return value:

const classifyNode = id("classify");

// Rule 1: if eq(x, 3) = true → "its 3"
const rule1 = [
  { source: $c1, identifier: typeRel, target: classifyNode },
  { source: $c1, identifier: arg1Rel, target: $x1 },
  { source: $eq1, identifier: typeRel, target: eqNode },
  { source: $eq1, identifier: arg1Rel, target: $x1 },
  { source: $eq1, identifier: arg2Rel, target: numberNode(3) },
  { source: $eq1, identifier: resultRel, target: trueNode },
  { id: c1$, source: $c1, identifier: resultRel, target: stringNode("its 3") },
];

// Rule 2: if eq(x, 3) = false → "not 3"
const rule2 = [
  { source: $c2, identifier: typeRel, target: classifyNode },
  { source: $c2, identifier: arg1Rel, target: $x2 },
  { source: $eq2, identifier: typeRel, target: eqNode },
  { source: $eq2, identifier: arg1Rel, target: $x2 },
  { source: $eq2, identifier: arg2Rel, target: numberNode(3) },
  { source: $eq2, identifier: resultRel, target: falseNode },
  { id: c2$, source: $c2, identifier: resultRel, target: stringNode("not 3") },
];

Compiles to:

function eq(arg0, arg1) {
  return arg0 === arg1;
}

function classify(eq_arg1) {
  const eq_result = eq(eq_arg1, 3);
  if (eq_result === true) {
    return "its 3";
  } else if (eq_result === false) {
    return "not 3";
  }
}

About

GraMa is a graph programming language. It stands for Graph Machine.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors