feat(parser): source-anchored combat-relationship subject static (Alms Beast)#5520
Conversation
…s Beast)
A self-relative combat-relationship subject — "Creatures blocking or
blocked by ~ have lifelink" (Alms Beast) — failed to parse: no
subject-filter arm recognized the "blocking or blocked by <self>"
qualifier, so the whole static line dropped.
Strip the trailing "blocking or blocked by ~" / "blocking or blocked by
this creature" qualifier in parse_continuous_subject_filter, parse the
base subject recursively, and attach a source-anchored
FilterProp::CombatRelation { BlockingOrBlockedBy, Source }. That prop is
already fully evaluated at runtime by game::filter (and the target-
relative form is already parsed in oracle_target), so this is a grammar-
only seam. The self-reference is normalized to "~"; "this creature" is
accepted for the literal phrasing.
There was a problem hiding this comment.
Code Review
This pull request adds support for parsing combat-relationship qualifiers (specifically "blocking or blocked by ~" and "blocking or blocked by this creature") in static abilities, mapping them to a source-anchored FilterProp::CombatRelation. It also includes corresponding unit tests to verify the new parsing behavior. As there are no review comments provided, I have no further feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Parse changes introduced by this PR · 1 card(s), 2 signature(s) (baseline: main
|
matthewevans
left a comment
There was a problem hiding this comment.
Approving — clean, well-scoped grammar work.
Verified:
- Right seam. This adds the
subject: CombatRelationSubject::Source(self-anchored) sibling of the already-parsed target-relative form inoracle_target.rs(which usessubject: ParentTarget) — sameFilterProp::CombatRelation, different anchor. It's a grammar-only change. - Runtime authority pre-exists.
game::filteralready evaluatesCombatRelationSubject::Source(filter.rs:3302) andCombatRelation::BlockingOrBlockedBy(filter.rs:3318), so the parsed filter is live at runtime — no new engine logic, no inert AST. - Combinators + suffix discipline.
all_consuming(terminated(take_until(needle), tag(needle)))correctly requires the qualifier to be a true trailing suffix, and the recursion intoparse_continuous_subject_filter(base)composes the base subject with the added prop. - Discriminating test asserts the typed structure (creature subject + source-anchored
BlockingOrBlockedByprop + lifelink grant) for both the normalized~and literal "this creature" phrasings — not a Debug-string dump.
Thanks — nicely mirrors the existing pattern for the static-subject case.
Problem
A self-relative combat-relationship subject failed to parse — Alms Beast:
Creatures blocking or blocked by ~ have lifelink.No subject-filter arm recognized theblocking or blocked by <self>qualifier, so the whole static line dropped (static_structureUnimplemented).The target-relative form (
… blocking or blocked by target creature) is already parsed inoracle_target; only the self/source-relative static form was missing.Fix
Strip the trailing
blocking or blocked by ~/blocking or blocked by this creaturequalifier inparse_continuous_subject_filter, parse the base subject recursively, and attach a source-anchoredFilterProp::CombatRelation { relation: BlockingOrBlockedBy, subject: Source }.That
FilterProp(andCombatRelationSubject::Source) is already fully evaluated at runtime bygame::filter— so this is a grammar-only seam. The self-reference is normalized to~;this creatureis accepted for the literal phrasing. Mirrors the existing merged qualifier-strip pattern in the same function.Tests
combat_relation_subject_static_binds_source_anchored_filterasserts (typed AST, both~andthis creatureforms) that the line produces a single static whose affected filter is a creature subject carryingFilterProp::CombatRelation { BlockingOrBlockedBy, Source }, with anAddKeyword { Lifelink }grant.Local:
parser::oracle_staticpasses;cargo fmt, clippy (-D warnings), parser-combinator + engine-authorities gates all clean.