fix(parser): recognize "enter(s) the battlefield tapped" long-form external entry#4988
Conversation
…ternal entry
CR 614.1c ETB-tapped replacement effects that key off a controller-scoped
type-phrase subject ("Artifacts, creatures, and lands your opponents
control ...") are templated by WotC in two equivalent surface forms: the
short "enter tapped" (Kismet, Authority of the Consuls, Imposing Sovereign)
and the fully-spelled "enter the battlefield tapped" (Frozen Aether). Only
the short form was recognized, so Frozen Aether's Oracle text fell through
to Unimplemented.
Two gates needed the long form added, mirroring the short-form arm already
present at each:
- `oracle_classifier::is_replacement_pattern` — the plural-subject
`ends_with(" enter tapped")` check has no `" enter the battlefield
tapped"` counterpart, so the line never reaches the replacement parser.
- `oracle_replacement::parse_external_entry_suffix` — the suffix peeler
that recovers the type-phrase subject only strips " enter(s) tapped",
not " enter(s) the battlefield tapped".
The "played by your opponents" and "unless"/"if you control" conditional
variants already accepted both forms; this closes the last unconditional
gap. Added two parser tests exercising the long form for both the
multi-type Or-filter shape (Frozen Aether) and the single-type shape
(Imposing Sovereign), alongside the existing short-form regression tests.
clippy -D warnings clean; full engine test green (14678 lib + 1721
integration, 0 failures).
Model: claude-sonnet-5 (Claude Code)
|
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] Parameterize the external-entry tapped suffix family instead of adding another pair of one-off suffix branches. Evidence: crates/engine/src/parser/oracle_replacement.rs:3457 already owns the external entry suffix peel and has the short-form enter tapped / enters tapped pair; this PR adds separate long-form enter the battlefield tapped / enters the battlefield tapped branches at crates/engine/src/parser/oracle_replacement.rs:3480 and crates/engine/src/parser/oracle_replacement.rs:3498. Why it matters: this is exactly the enter(s) [the battlefield] tapped structural axis, so duplicating branches makes the next templating variant another copy/paste site instead of one grammar. Suggested fix: factor the suffix peel into a small parameterized suffix matcher/table for the plain and played by your opponents external-entry cases, preserving the existing precedence but representing enter vs enters and short vs long form as data/alternatives.
[LOW] The current head fails the rustfmt gate. Evidence: CI Rust lint (fmt, clippy, parser gate) reports a cargo fmt --all -- --check diff in crates/engine/src/parser/oracle_replacement.rs around the new TargetFilter::Typed(TypedFilter::creature().controller(...)) assertion. Why it matters: this blocks the required Rust lint gate and is easy to avoid before review. Suggested fix: run cargo fmt --all and push the formatted result.
Parse changes introduced by this PR · 1 card(s), 2 signature(s) (baseline: main
|
Summary
Fixes
Frozen Aether(and any other card using the fully-spelled entry-event phrasing) falling through toUnimplemented.Root cause: CR 614.1c ETB-tapped replacement effects with a controller-scoped type-phrase subject ("Artifacts, creatures, and lands your opponents control ...") are templated by WotC in two equivalent surface forms:
Only the short form was recognized, so Frozen Aether's Oracle text fell through to
Unimplemented. Two independent gates needed the long form added, each already having a short-form arm to mirror:oracle_classifier::is_replacement_pattern— the plural-subjectends_with(" enter tapped")check had no" enter the battlefield tapped"counterpart, so the line never reached the replacement parser.oracle_replacement::parse_external_entry_suffix— the suffix peeler that recovers the type-phrase subject only stripped" enter(s) tapped", not" enter(s) the battlefield tapped".The "played by your opponents" and "unless"/"if you control" conditional variants already accepted both forms — this closes the last unconditional gap.
Changes
crates/engine/src/parser/oracle_classifier.rs: classify the long-form plural suffix as a replacement pattern.crates/engine/src/parser/oracle_replacement.rs: peel the long-form suffix inparse_external_entry_suffix, mirroring the existing short-form arms. Added two parser tests exercising the long form for both the multi-type Or-filter shape (Frozen Aether) and the single-type shape (Imposing Sovereign), alongside the existing short-form regression tests.Test plan
frozen_aether_enters_the_battlefield_tapped_long_form,single_type_enters_the_battlefield_tapped_long_formauthority_of_the_consuls_enters_tapped,blind_obedience_compound_or_filter,frozen_aether_comma_list,uphill_battle_played_by_opponents_enter_tapped,played_by_opponents_entry_covers_creature_and_landall still passclippy -D warningscleanenginetest suite green (14678 lib + 1721 integration tests, 0 failures)Model: claude-sonnet-5 (Claude Code)