Exercise 2: Two Separate Inline Runs, Split by a Block Sibling — Possible Solution ==================================================================== THE TEST ------------------------------ div's own style-tree children, in order: 1. text("run one, part A") 2. run one, part B (inline) 3.

...

(block) 4. text("run two, alone") fixed = build_layout_tree(div_node) [c.box_type for c in fixed.children] len(fixed.children[0].children) # run one's anonymous box len(fixed.children[2].children) # run two's anonymous box RESULT ------------------------------ box_types -> ['anonymous', 'block', 'anonymous'] fixed.children[0].children count -> 2 (text + , merged) fixed.children[2].children count -> 1 (just the trailing text) Exactly two separate anonymous boxes, not one combined box spanning the whole container. WHY THE TWO RUNS STAY SEPARATE ------------------------------ build_layout_tree's own grouping loop is: pending_inline = [] def flush(): if pending_inline: anon = LayoutBox('anonymous', None) anon.children = pending_inline[:] box.children.append(anon) pending_inline.clear() for child_styled in styled_node.children: child_type = display_type(child_styled) if child_type == 'block': flush() box.children.append(build_layout_tree(child_styled)) else: pending_inline.append(build_layout_tree(child_styled)) flush() Walking the four children in order: the text node and are both non-block, so both get appended to pending_inline (now holding 2 items). Then

is block-type -- this triggers flush(), which packages the current pending_inline (the 2 accumulated items) into ONE anonymous box, appends it to box.children, and CLEARS pending_inline back to empty.

itself is then appended as a real block box. Finally the trailing text node is non-block, so it goes into the now-EMPTY pending_inline. The loop ends, and the final flush() call packages that single remaining item into a SECOND, separate anonymous box. WHY THIS WORKS AS AN ANSWER ------------------------------ The critical mechanism is that flush() is called and pending_inline is cleared EVERY time a block-type child is encountered -- a block sibling acts as a hard boundary that closes off whatever inline run was accumulating before it and starts a fresh, empty run afterward. Without that clear-on-block-boundary behavior, all inline content across the whole container (both before AND after the block sibling) would merge into one giant anonymous box spanning across a real block element in the middle -- which would be structurally wrong, since that block element genuinely needs to sit between the two runs as an independent sibling, not be swallowed inside a combined inline wrapper.