Ranger architecture

2 — How a dropped file becomes a picture

Written: 2026-08-15 · Established by: reading src/parser/index.ts, src/parser/tokenizer.ts, src/parser/interpreter.ts, src/checks/index.ts and src/store/file-slice.ts at commit 93a9a67.

This is the path everything else hangs off. A file arrives, parseGCode turns its text into a GCodeFile, the store holds that one object, and the viewer and the sidebar are both projections of it. Nothing is cached between the text and the picture, so re-parsing is how every later change lands too — see diagram 4.

Intake and fan-out. The store holds one GCodeFile, and three surfaces are projections of it.

Drop zone
or file picker

readFileAsText

loadFile

parseGCode

GCodeFile

Per-concern resets

Viewer

CodeMirror

Sidebar panels

Inside parseGCode. tokenize is the only stage that reads the raw text; every other stage reads blocks, and three of them read the interpreted moves as well.

raw text

tokenize

interpretBlocks

detect* and parse*
units, subprograms, cycles
WCS, tool table, G10

splitIntoOperations

runChecks

GCodeFile

The order in parseGCode is the contract

tokenize is the only thing that reads the raw text. Everything downstream reads GCodeBlock[], and three of the six stages read the interpreted Move[] as well. That split is why a check can report a line number without re-lexing, and why src/transforms/apply.ts can splice text back by block index.

interpretBlocks is where the program is actually executed. It carries modal state, expands subprogram calls and canned cycles, and returns the four things the rest of the parse needs: the moves, the block index it halted at if it could not continue, the calls whose expansion hit the cap, and the cycle it declined. Those last three exist so runChecks can report a limit as a finding rather than the parser failing silently.

Halting is normal and partial. haltedAtBlockIndex is a real state a production file reaches — issues #291, #293 and #296 are all programs that halt on a construct Ranger does not yet interpret. The moves found before the halt are still drawn.

Two invariants this path enforces that the diagram cannot show

Position known-ness is per axis. A move that commands an axis never established is kept with fromAssumed: true rather than dropped, and anything turning moves into geometry, bounds or a count of real motion filters on isDrawableADR-0016.

Units are 'mm' | 'inch' | null, and null is not millimetresADR-0021. detectUnits returns the third value for a program that declares neither, which is a program that runs in whatever the control was left in.

Where the second entry point is

safeParseGCode in src/parser/index.ts wraps the same call in a try. The store's loadFile has its own private tryParse doing the same thing, because a caller outside the store — a second file picked for comparison in src/compare/ — has no undo history to fall back on if the parse throws.

Drag to pan · scroll to zoom · Esc to close