Expand description
The matching engine (#46): anchor-based structural matching of
Patterns against parsed docstrings.
A pattern matches by structural unification of a reading’s fragment
tree (in pattern coordinates) against candidate subtrees of the target
(in target coordinates). Pattern::matches searches the whole
document; Pattern::matches_in scopes the search to an anchor node
and derives the grammar binding from it (the maintainer’s parent-node
design from the #46 discussion). Rewriting is #47.
§Style strictness
Matching is style-strict: a pattern only matches targets of the
exact Style it was parsed with
(Pattern::style==Parsed::style); on a mismatch both entry
points return no matches. Cross-style “smart” matching (a Google-spelled
pattern matching a NumPy document via the unified kinds) is explicitly
deferred, per the #46 design notes.
§Unification rules
Fragment and candidate trees unify element-by-element:
- Nodes unify when their kinds are equal and their child lists unify (below).
- Trivia is skipped on both sides:
WHITESPACEandNEWLINEtokens are invisible to matching. Combined with the per-lineTEXT_LINEdesign (whose ranges exclude leading indentation), this makes matching indentation-relative: a pattern written at one indent matches the same content at any other indent (spaces or tabs). Note that this also means interior extra indentation of continuation lines is layout, not structure — only line content is compared. BLANK_LINEis structure, not trivia, for matching: a blank line inside a text block is a paragraph break, so a multi-paragraph pattern only matches a multi-paragraph target. Blank lines pair up one-to-one (consecutive blank lines produce one token per line), but their text is not compared — the bytes of a blank line are invisible layout.UNDERLINEtext is not compared either (kinds must still pair up): a NumPy header underline’s length is presentation, not content.- Every other token pairs with a target token of the same kind and
byte-identical text. In particular a zero-length (missing
placeholder) pattern token only matches a zero-length target token:
missing matches missing (
x ():matches exactly the targets whoseTYPEis missing too). The same holds for zero-length placeholder nodes (e.g. the emptyDESCRIPTIONof a Googlex:entry): their empty child lists unify only with empty child lists.
§Metavariable binding
- A
$Xsite binds one target element of the sameSyntaxKindas the pattern element it landed on (aNAMEtoken binds aNAMEtoken, a node site binds a node of that kind). The bound element’s subtree is not inspected.$Xnever binds a missing (zero-length) placeholder — to match a missing element, spell the missing form literally. - A
$$$Xsite is a hole in its sibling list: literal siblings before it match forward, literal siblings after it match backwards, and the hole binds the (possibly empty) contiguous middle of the target’s sibling list — whatever the kinds of those siblings. When a$$$Xcovers a whole fragment root (e.g. the entry reading of a lone$$$Xline), it binds that single candidate fragment. - An empty
$$$Xcapture has a zero-length range positioned at the end of the last sibling matched before the hole; if the hole is first in its list, at the start of the first sibling matched after it; if the list is empty, at the start of the parent node. This is the exact offset where sequence content would be inserted (#47). - Repeated metavariables (the same name at several sites,
$Xor$$$X) must all bind byte-identical target text; the reported capture is the first occurrence’s binding, in pattern source order.
§Readings the matcher cannot use (documented v1 limits)
Both limits are per-reading, panic-free, and silent: an affected reading simply contributes no matches, while the pattern’s other readings match normally.
- A reading with an inexact site — a metavariable amid literal prose
inside one
TEXT_LINEtoken, i.e. one whoseMetaVarSite::is_exactisfalse— is not matchable: sub-line text matching is regex territory and is deferred. - A reading with two or more
$$$holes in the same sibling list is not matchable: the split of the middle would be ambiguous. (#45 deliberately inventories such patterns instead of rejecting them, so the matcher — the layer that gives$$$its sequence semantics — enforces the limit at match time.)
§Candidate enumeration and grammar binding
Each reading is tried against the target sites its
FragmentKind and
Reading::section_kinds admit. Section roles are resolved with the
same section-kind resolution the unified views use
(Section::kind):
- Entry readings unify against each
ENTRY(or, for theReferencesreading,CITATION) child of everySECTIONwhose resolved kind is in the reading’ssection_kinds. - Body readings unify against the
DESCRIPTIONbody of every free-text section — anySectionKind::FreeText, includingUnknown-named sections (the free-text body grammar is the same for all of them; the reading’ssection_kindscan only list the known names). - Section readings unify against every
SECTIONnode. Section readings are concrete syntax: the headerNAMEmust match literally (case-sensitively) unless it is a metavariable. - Document readings unify against the
DOCUMENTroot only. Policy:Pattern::matchesskips Document readings unless the pattern has no other reading (a Document reading of a short prose pattern could only ever match a whole document of exactly that shape — noise for a global search);Pattern::matches_inuses them exactly when the anchor is the document root (explicitly anchoring at the root opts in).
§Anchored matching
Pattern::matches_in restricts the search to the subtree of anchor
(the anchor node itself included) and derives the grammar binding from
it: anchored at a SECTION, only the readings admitted by that
section’s role can bind ($TYPE: $DESC anchored at a Raises: section
selects the Raises-shape reading — $TYPE binds a TYPE token — where
the same pattern anchored at an Args: section selects the
parameter-shape reading binding a NAME token). Anchored at the
document root it behaves like Pattern::matches plus Document
readings. An anchor that is not a node of target’s tree yields no
matches.
§Match order and overlap
Candidate sites are visited in document pre-order (a section before its entries), and at each site the readings are tried in the pattern’s documented enumeration order. A candidate whose span overlaps an already-accepted match is skipped — first match wins — so the returned matches are non-overlapping and in document order. Ties at the same site are resolved by reading order.
Structs§
- Capture
- What a metavariable bound in one
Match. - Match
- One non-overlapping match of a
Patternagainst a target document, produced byPattern::matches/Pattern::matches_in.
Enums§
- Captured
Element - A reference to one captured target element — a node or a token of the target tree.