Skip to content

← Examples

Python Tutor Memory Diagram ​

A Python Tutor style runtime memory diagram showing a global frame of variables whose pointers fan out with arrows into a heap of linked tuples.

The code below is adapted from the story source; this example relies on local helpers that cannot be inlined into a single standalone snippet.

ts
// adapted from packages/gofish-graphics/stories/bluefish/PythonTutor/PythonTutor.stories.tsx
// (this story uses local helper modules that cannot be inlined as a standalone snippet)

const container = document.getElementById("app");
const data = {
  stack: [
    binding("c", pointer(0)),
    binding("d", pointer(1)),
    binding("x", "5"),
  ],
  heap: [
    tuple(["12", pointer(1), "1", "0", pointer(2), pointer(3)]),
    tuple(["1", "4"]),
    tuple(["3", "10", "7", "8", pointer(4)]),
    tuple(["2", pointer(4)]),
    tuple(["3"]),
  ],
  heapArrangement: [
    [0, 3, null, null],
    [null, 1, 2, 4],
  ],
};

const globalFrameName = createName("globalFrame");
const heapName = createName("heap");

// Address -> (row, col) in the arrangement grid
const addrPos = new Map<number, [number, number]>();
data.heapArrangement.forEach((row, r) =>
  row.forEach((addr, c) => {
    if (addr !== null) addrPos.set(addr, [r, c]);
  })
);

const stackArrows = data.stack.flatMap((slot, i) =>
  isPointer(slot.value)
    ? [
        arrow(
          {
            bow: 0,
            stretch: 0,
            flip: true,
            padStart: 0,
            stroke: "#1A5683",
            start: true,
          },
          [
            ref(globalFrameName).variables[i].value,
            ref(heapName)
              .path(...addrPos.get(slot.value.value)!)
              .elmTuples[0],
          ]
        ),
      ]
    : []
);

const heapArrows = data.heap.flatMap((obj, a) =>
  obj.values.flatMap((v, j) =>
    isPointer(v)
      ? [
          arrow(
            {
              bow: 0,
              padEnd: 25,
              padStart: 0,
              stroke: "#1A5683",
              start: true,
            },
            [
              ref(heapName).path(...addrPos.get(a)!).elmTuples[j].val,
              ref(heapName).path(...addrPos.get(v.value)!).elmTuples[0],
            ]
          ),
        ]
      : []
  )
);

layer([
  // y-down free space: cross-axis "start" tops-aligns the global frame and
  // the heap (issue #143/#16).
  spread({ dir: "x", alignment: "start", spacing: 100 }, [
    globalFrame({ stack: data.stack }).name(globalFrameName),
    heap({
      heap: data.heap,
      heapArrangement: data.heapArrangement,
    }).name(heapName),
  ]),
  ...stackArrows,
  ...heapArrows,
]).render(container, {});