ppparse_instructions - load and print a reconstructed netlist from build instructions
ppparse_instructions
[--debug] [--quiet] INSTRUCTIONS-FILE
ppparse_instructions
{--help|--version}
ppparse_instructions reads a real build-instruction text file, as produced by ppbuild_instructions(1), and prints the netlist and placement parse_instructions.py ’s own reconstruct() recovers from it: one line per component (reference designator and value), followed by one line per net (net name and every member pin, written as REF.PIN).
This is the mirror image of ppnetlist(1): where that program loads a real, already-exported KiCad netlist and prints it, ppparse_instructions loads a real, plain-English instruction file and prints the netlist recovered by reading it back. It performs no resolution against real component data at all — a component’s own pin, where the instructions never had reason to state a real, specific identity for it, is printed as a real, synthetic placeholder (_pin@N, N being the real column it was found at). See ppresolve_components(1) for the program that DOES resolve every component and produce a working KiCad schematic from the same input.
Any of this project’s own three real, plain-text instruction formats (plain, markdown, orgmode, all producible by ppbuild_instructions(1)’s own --format option) is accepted. html is NOT supported — see parse_instructions.py ’s own docstring for why.
-h, --help
Show a usage summary and exit.
--version
Show this project’s own current version (read from VERSION) and exit.
--debug
Also annotate each component’s own line with its real, reconstructed board position: an anchor’s own column, or a passive’s own column, half, and — where the instructions stated it — which rail net it attaches to.
--quiet
Suppress the leading "N components, N nets" summary line; print only the component and net listings themselves.
A wire sentence may name the colour of its wire ("Connect a green jumper wire from hole b5 to hole d9 (net X)"). The colour is read and ignored; a sentence without one, as written by an earlier version, is read as before.
|
0 |
The instructions were parsed and printed successfully. | ||
|
1 |
The given file could not be opened, or its content did not match any of this program’s own recognized step grammars (a genuine, real parse failure — see parse_instructions.py ’s own ParseError for the exact line and reason). | ||
|
2 |
Invalid command-line usage (e.g. no INSTRUCTIONS-FILE given). |
Generate instructions from a real schematic, then read them back and print the reconstructed netlist:
ppbuild_instructions
--format orgmode -o steps.org power.kicad_sch
ppparse_instructions --debug steps.org
A design that needs a bigger board than the default gets one computed for it, and the hole names in the instructions ("f3") mean different strips on different boards. When the board is not the default five rows per half, the build instructions therefore state it, once, right after the title:
Board: 7 rows
per half (rows a-g above the trench, h-n below).
ppparse_instructions reads this line, so a design on a
larger board reads back correctly with no further option. A
hole in a sector after the first, written "a5 (sector
2)", is read as the same hole of that sector.
The role a passive step names for a lead (the anode, the cathode, the positive or the negative lead) is read and recorded, in the rebuilt netlist, as that lead’s pin name: the near lead is pin 1 and the far lead pin 2. A step that names no role is read as before.
The sentence an anchor step may end with about its footprint (surface-mount, pins that would share a column, off the 0.1 inch grid, no footprint on file) is read and recorded as the step’s fit (smd, shorts, offgrid or unknown), on a passive step as on an anchor’s; the header line that names those parts is dropped, like the Board line.
The pin list of an anchor step and the sentences naming its pins on each rail are read, and the pins are recorded under their real identifiers, at the holes the step gives them, instead of an opaque identifier made from a column; a text without them is read as before. The header line that explains the pin holes is dropped, like the Board line. The pins that the step of a rail-direct source names on each rail, and the pin that each lead of a passive step names ("(pin S)"), are read the same way: they are the real pins, so that no pin_map is needed for those parts; a text without them is read as before (the source’s pins are synthesized, the two leads are pins 1 and 2).
The text says which board it is for, on a line after the title, whenever it is not the default: "Board: 15 rows per half." The reader takes it from there: the steps it returns carry the number, the circuit is rebuilt with that geometry without being told, a board configuration that contradicts it is an error, and a hole letter that cannot exist on the default board (k14, in a text that states no board) is an error naming the hole, since reading it anyway would put it on the wrong strip.
A step "Connect a jumper wire from hole H to the nearby power (+) rail." (or ground (-)) is read as a rail_wire step: it adds no connection of its own, since the part’s step already says what is on the rail, and it is checked against the circuit the other steps rebuild: the strip of H must hold a pin or a lead of that rail’s net, or the problem is listed (p.rail_wire_problems). A strip that holds no registered pin, the representative rail column of a part laid out as a diagram, cannot be checked and is not a problem. The step of a chip laid out as a diagram also names its pins on that rail and the hole of the column ("U1’s ground (-) pins 21, 3 go in this column: hole f14."): they are put in that hole, so the strip holds them and the check has something to check; p.rail_wire_unchecked counts the wires with nothing to check against.
A link step that ends with "Its first end is where R19’s far lead connects." is read as starting at the far lead of that passive, which the passive’s own step gave no hole: the hole is registered as that lead’s pin before the link is resolved. Without the sentence (an older text) the hole is given to the nearest part on its left, as before. The step of a wire between boards for that end carries the same information ("This end is where R24’s far lead connects."), read when the two steps of the wire are paired.
A net that a link step names GND or VBUS without being the ground or power rail (a USB 5 V net called VBUS beside a 3.3 V power rail) is read as GND#net or VBUS#net, since the rails are named GND and VBUS in the rebuilt circuit and the two would otherwise merge.
A text that spans several boards starts with a line "Boards: 2 breadboards (board 1: 63 columns, 5 rows per half, 1 sector; board 2: ...)" and every step that sits on one of them starts with [board N] (a wire between two boards, and a rail bridge, name their boards inside the sentence). The parser reads the line and the prefix and gives each board its own range of columns, so one board’s hole a5 is not another’s. The two steps of a wire between boards ("from hole e22 (board 1) to the matching connection point on board 2" and its twin from the other end) are paired into one link; a step without its twin is an error that names the net and the boards. Until 0.72.81 every multi-board step was refused, so no multi-board output had ever gone round the loop.
A step "Connect a jumper wire from the north ground (-) rail to the south ground (-) rail." (or power (+); the rails are north, middle and south; on several boards "... rail of board 1 to the north ... rail of board 2") is read as a rail_bridge step. It adds no connection to the rebuilt circuit, since a rail is one net by its name, but it is CHECKED: the physical rails the rail wires use must be joined by the bridges the text gives, otherwise the circuit as built would have separate grounds or supplies. The result is in the placement (rail_bridge_problems), and the loop checks of the test suite require it to be empty.
ppnetlist(1), ppbuild_instructions(1), ppresolve_components(1), ResearchJournal.org in packpay’s own documentation set (see Index.org).
Péter Felecan <pfelecan@gmail.com>, with development assistance from Claude (Anthropic).
Copyright © 2026 Péter Felecan. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.