Skip to content

osrforge.statblocks

Shared structural parsing of printed stat-block lines — AC, Hit Dice, and class-level notations.

These parsers are the pipeline's one reading of a printed block's structure, shared by two consumers below and above the stage boundary: assembly's deterministic template mapping (map_stat_block and its refusal-ladder predicate usable_stat_block) and the monsters stage's stat-block veto (stat_block_veto). They live in their own module because assemble.py imports monsters.py — the veto could never import them from assembly without a cycle, and the veto must change the cached resolution, so it cannot run in assembly.

Parsing is structural transcription of a printed notation, never a rules judgment: every judgment (AC complement direction hazards, HD-to-MonsterHitDice anchors, class-table derivations) stays with the consumer that owns it.

ParsedHd dataclass

ParsedHd(count: int, die: int | None, modifier: int, asterisks: int, fractional: bool)

A printed Hit Dice line, structurally parsed: count, printed die (if any), modifier, asterisks.

count instance-attribute

count: int

die instance-attribute

die: int | None

modifier instance-attribute

modifier: int

asterisks instance-attribute

asterisks: int

fractional instance-attribute

fractional: bool

parse_ac

parse_ac(block: RawStatBlock) -> tuple[int, int, bool] | None

Parse the printed AC into (descending, ascending, complement_derived), or None.

Dual notation carries both values as printed; a single value converts by the 19-complement (the B/X identity OSE prints directly: AC 5 [14]) in the direction the block's notation states, defaulting to descending — the B/X reading — when the notation is unclassified.

parse_class_level

parse_class_level(text: str | None) -> tuple[str, int] | None

Parse a printed class-level notation (F 3, MU4, "3rd-level cleric") into (class_id, level).

A level below 1 refuses in both forms — a 0-level notation carries no combat math to derive, so it must fall to the refusal ladder, never into mapping (which is total only over parses this function accepts).

parse_hd_text

parse_hd_text(text: str | None) -> ParsedHd | None

Structurally parse a printed HD line (3+1, 1-1, 3*, ½, 2d8), or None.