feat(parser): leading except-for exempt-list restriction static — Akron Legionnaire (CR 508.1c) - #5409
Conversation
…n Legionnaire)
Akron Legionnaire's "Except for creatures named Akron Legionnaire and
artifact creatures, creatures you control can't attack." strict-failed:
parse_compound_subject_rule_static hands the FULL prefix before the first
recognized predicate to parse_rule_static_subject_filter, which has no
"except for" arm, so the sentence-initial exception clause was unparseable.
Adds parse_leading_except_for_rule_static, dispatched immediately before
its single-subject sibling. Splits the exempt list on " and " into two
conjuncts, resolves each independently as a bare type-phrase exemption
("artifact creatures") or a named exemption within a type class ("creatures
named Akron Legionnaire", via FilterProp::Named), Or-combines them, and ANDs
Not{Or{..}} onto the affected filter the existing dispatcher produces for
the remainder — the same "resolve conjuncts independently, recombine
generically" shape as phase-rs#5219's compound-subject animation dispatcher.
Scoped to the 2-conjunct form: a Scryfall full-text search (o:/^Except for/)
returns exactly one printed card in this shape, so an unbounded Oxford-comma
exempt list would be speculative with no card to validate the ambiguous
comma-boundary split against.
Fixes phase-rs#5408.
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
matthewevans
left a comment
There was a problem hiding this comment.
[MED] Parser-only tests do not prove the new Akron Legionnaire static works through the production attack-declaration path. Evidence: crates/engine/src/parser/oracle_static/tests.rs:4243. Why it matters: the PR claims card support and builds a supported CantAttack static, but the added tests only assert AST shape; a regression in CantAttack/affected filter evaluation, FilterProp::Named, or validate_attackers would still pass. Suggested fix: add an integration/scenario test that parses Akron Legionnaire’s real Oracle text and declares attackers, proving a normal nonartifact creature you control is rejected while a creature named Akron Legionnaire and an artifact creature are legal attackers.
…er declaration The parser-level test only asserted the parsed AST shape (affected filter structure), which doesn't prove the static actually gates combat -- a regression in CantAttack evaluation, FilterProp::Named matching, or validate_attackers would still pass it. Adds an integration test that attaches the REAL parsed static (via parse_static_line_multi, the entry point that reaches parse_leading_except_for_rule_static) to a battlefield Akron Legionnaire, then declares attackers: a plain nonartifact creature you control is rejected, while Akron Legionnaire itself (exempted by name) and an artifact creature you control (exempted by type) are legal attackers. Addresses review feedback on phase-rs#5409.
|
Addressed: the original tests only asserted the parsed AST shape ( Added
|
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.
Maintainer fixup: current head avoids the helper-name collision, preserves the existing subject-rule parser, and keeps the runtime Akron combat coverage; auto-merge can wait for restarted checks.
matthewevans
left a comment
There was a problem hiding this comment.
Current head has the runtime Akron combat coverage, parse-diff matches the claimed single-card change, and the parser/combat seam is clean.
…20260709 # Conflicts: # crates/engine/src/game/combat.rs
matthewevans
left a comment
There was a problem hiding this comment.
Maintainer review: current head keeps the Akron parser/combat seam clean, has discriminating runtime coverage, and parse-diff matches the claimed single-card restriction change.
Summary
Adds engine support for Akron Legionnaire's leading exempt-list restriction static — and the class of sentence-initial "Except for
<A>and<B>,<rule-static>" exception clauses it represents.Its printed static,
Except for creatures named Akron Legionnaire and artifact creatures, creatures you control can't attack., silently dropped:parse_compound_subject_rule_static(crates/engine/src/parser/oracle_static/evasion.rs) hands the FULL prefix before the first recognized predicate toparse_rule_static_subject_filter, which has no "except for" arm, so the whole line strict-failed.parse_leading_except_for_rule_static(dispatched immediately beforeparse_compound_subject_rule_static) splits the exempt list into two conjuncts on" and ", resolves each independently — a bare type-phrase exemption ("artifact creatures") or a named exemption within a type class ("creatures named Akron Legionnaire", viaFilterProp::Named) — Or-combines them, then ANDsNot{Or{..}}onto theaffectedfilter the existing single-subject dispatcher produces for the remainder. No new runtime:TargetFilter::And/Not/OrandFilterProp::Namedare all existing, already-evaluated primitives.Files changed
crates/engine/src/parser/oracle_static/evasion.rscrates/engine/src/parser/oracle_static/shared.rscrates/engine/src/parser/oracle_static/mod.rscrates/engine/src/parser/oracle_static/tests.rsAnchored on
crates/engine/src/parser/oracle_static/evasion.rsparse_compound_subject_rule_static— the single-subject sibling; the new handler is dispatched immediately before it and delegates the remainder to it unchanged.#5219(compound-subject animation statics, Life and Limb) — the same "resolve conjuncts independently, recombine generically" shape, applied to a different static category (restriction, not type-change).#5384(N-way Oxford-comma compound-subject keyword static, Shalai) — the precedent for evidence-scoping arity: build exactly the conjunct-count the printed card uses, not a speculative unbounded grammar.CR references
CR 508.1c— the active player checks each creature for restrictions ("effects that say a creature can't attack") when declaring attackers.CR 201.2a— two or more objects have the same name if they share at least one name; the basis for the"creatures named <Name>"exemption conjunct.Scope
Scoped to the 2-conjunct
"<A> and <B>"form: a Scryfall full-text search (o:/^Except for/) returns exactly one printed card in this shape (Akron Legionnaire), so an unbounded Oxford-comma exempt list would be speculative — there's no card to validate a 3+-conjunct split against the genuine ambiguity of which comma ends the exempt list vs. starts the next conjunct.Verification
cargo fmt --all— clean.cargo check/test/clippycould not be run in this session — local machine's MSVC C++ Build Tools installation is incomplete (vswherefinds no installation with the VC.Tools component), solink.exeresolves to Git's coreutils shim instead of the real MSVC linker, and every crate (including unrelated third-party dependencies) fails to link before reaching this crate's code. Tilt was also not running in this session. Verified by close manual trace instead: every referenced type, function signature, and visibility (TargetFilter::{And,Not,Or,Typed,Any},FilterProp::Named,TypedFilter,StaticDefinition.{affected,description},nom_primitives::split_once_on,parse_type_phrase,merge_filter_prop[bumpedfn→pub(crate) fn],parse_compound_subject_rule_static) was confirmed against the actual current source, and the dispatch chain (parse_static_line_multi→parse_static_line_multi_ir→parse_static_line_multi_inner→parse_static_line_multi_dispatch) was traced by hand to confirm the new arm is reachable and ordered correctly relative to its sibling. Given the local toolchain is broken independent of this change, deferring final compiler/test confirmation to CI.oracle_static/tests.rs): a positive parse test on Akron Legionnaire's real Oracle text asserting the fullAnd{Typed(creatures you control), Not{Or{Typed(creatures named "akron legionnaire"), Typed(artifact creatures)}}}shape; a guard confirming the untouched sibling ("Creatures you control can't attack.", no exempt clause) still resolves unwrapped; a guard confirming a single-conjunct exempt clause (no" and ") correctly declines rather than mis-parsing.Track
Community
LLM
Model: claude-sonnet-5
Scope Expansion
None.
Validation Failures
None — see Verification note on the local toolchain limitation.
CI Failures
Unknown at PR open time — local compiler verification was unavailable (see Verification).
Closes #5408