Summary
The copy-token parser's count helper, parse_copy_token_entry_modifiers (crates/engine/src/parser/oracle_effect/token.rs), accepts only "a" / "one" / a literal digit:
let (rest, count) = opt(alt((
value(QuantityExpr::Fixed { value: 1 }, alt((tag("a "), tag("one ")))),
map(nom_primitives::parse_number, |value| QuantityExpr::Fixed { value: value as i32 }),
)))
.parse(rest)?;
So "Create X tokens that are copies of …" fails to parse and the whole effect is dropped — the spell does nothing. The regular (non-copy) token path does not have this limitation: parse_token_count_prefix delegates to the shared parse_count_expr building block, which already handles "X", "twice X", "that many", "half X rounded up", etc. The copy-token path simply doesn't reuse it.
Steps to reproduce
use engine::parser::oracle_effect::token::try_parse_token;
// Variable count — BROKEN:
try_parse_token(
"create x tokens that are copies of target creature you control",
"Create X tokens that are copies of target creature you control",
&mut ParseContext::default(),
);
// => None (whole effect dropped)
// Literal count — works:
try_parse_token(
"create two tokens that are copies of target creature you control",
"Create two tokens that are copies of target creature you control",
&mut ParseContext::default(),
);
// => Some(CopyTokenOf { target: Typed(Creature, You), count: Fixed(2), … })
The only difference is the count token (X vs two); the target creature you control parsing is already correct.
Real card examples (all currently dropped)
| Card |
Oracle text |
| Rionya, Fire Dancer |
"{1}{R}, {T}: Create X tokens that are copies of another target creature you control, where X is …" |
| Devastating Onslaught |
"… then create X tokens that are copies of target artifact or creature you control, where X is the number of artifacts destroyed this way." |
| Nacatl War-Pride |
"… create X tokens that are copies of it and that are tapped and attacking, where X is the number of creatures defending player controls." |
| For the Common Good |
"… create X tokens that are copies of target token you control, where X is the number of Clues you control." |
| Aggressive Biomancy |
"Create X tokens that are copies of target creature you control, except …" |
| The Good Gamers |
"… create X tokens that are copies of it …" |
(Cards using the "that many" phrasing — e.g. The Scarab God — go through parse_token_count_prefix and are unaffected; this gap is specific to the literal X/variable count in the copy-token path.)
Expected behavior
"Create X tokens that are copies of <target>" should parse to
Effect::CopyTokenOf { target, count: QuantityExpr::Ref { qty: QuantityRef::Variable { name: "X" } }, … },
with the X bound to the trailing "where X is <quantity>" clause (as the regular token path already does), so the spell creates the correct number of copies.
Actual behavior
parse_copy_token_entry_modifiers returns no count for X, the subsequent "tokens that are copies of" tag does not align, and try_parse_token returns None → the effect is Unimplemented and the spell resolves with no effect.
Comprehensive Rules
- CR 707.2 / 707.9 — copying a permanent / token copies.
- CR 107.3 — the value of
X in a spell/ability (here defined by the "where X is …" clause).
Suggested fix (compose from building blocks)
In token.rs, make the copy-token count reuse the shared count grammar instead of the bespoke a/one/digit parser:
- Replace the inline count in
parse_copy_token_entry_modifiers with parse_token_count_prefix / parse_count_expr (the same building block the non-copy path uses), so "X", "twice X", "that many", and multiplied/fractional counts all parse uniformly.
- In
try_parse_token's copy branch, parse a trailing ", where X is <expr>" clause and bind the Variable("X") count to it via the existing bind_where_x_in_quantity_expr helper (mirroring how the regular Effect::Token path binds X for token P/T and counts).
- Preserve the existing
tapped/attacking flag parsing and the except <body> clause handling.
Tests
try_parse_token("create x tokens that are copies of target creature you control", …) → CopyTokenOf with a Variable("X") count.
- A
"where X is the number of …" card binds X to the correct QuantityRef.
- Regression: literal-count (
"two") and "a" copy-token forms unchanged.
Non-duplicate note
Rionya, Devastating Onslaught, Nacatl War-Pride, and Aggressive Biomancy return 0 open or closed issues/PRs. The existing "create X tokens that are copies" issues (#2169, #1735, #1424) concern runtime token-count doubling and X-resolution, not the parser's inability to recognize the X count — a distinct, parser-only gap.
Summary
The copy-token parser's count helper,
parse_copy_token_entry_modifiers(crates/engine/src/parser/oracle_effect/token.rs), accepts only"a"/"one"/ a literal digit:So "Create X tokens that are copies of …" fails to parse and the whole effect is dropped — the spell does nothing. The regular (non-copy) token path does not have this limitation:
parse_token_count_prefixdelegates to the sharedparse_count_exprbuilding block, which already handles"X","twice X","that many","half X rounded up", etc. The copy-token path simply doesn't reuse it.Steps to reproduce
The only difference is the count token (
Xvstwo); thetarget creature you controlparsing is already correct.Real card examples (all currently dropped)
(Cards using the
"that many"phrasing — e.g. The Scarab God — go throughparse_token_count_prefixand are unaffected; this gap is specific to the literalX/variable count in the copy-token path.)Expected behavior
"Create X tokens that are copies of
<target>" should parse toEffect::CopyTokenOf { target, count: QuantityExpr::Ref { qty: QuantityRef::Variable { name: "X" } }, … },with the
Xbound to the trailing"where X is <quantity>"clause (as the regular token path already does), so the spell creates the correct number of copies.Actual behavior
parse_copy_token_entry_modifiersreturns no count forX, the subsequent"tokens that are copies of"tag does not align, andtry_parse_tokenreturnsNone→ the effect isUnimplementedand the spell resolves with no effect.Comprehensive Rules
Xin a spell/ability (here defined by the"where X is …"clause).Suggested fix (compose from building blocks)
In
token.rs, make the copy-token count reuse the shared count grammar instead of the bespokea/one/digit parser:parse_copy_token_entry_modifierswithparse_token_count_prefix/parse_count_expr(the same building block the non-copy path uses), so"X","twice X","that many", and multiplied/fractional counts all parse uniformly.try_parse_token's copy branch, parse a trailing", where X is <expr>"clause and bind theVariable("X")count to it via the existingbind_where_x_in_quantity_exprhelper (mirroring how the regularEffect::Tokenpath bindsXfor token P/T and counts).tapped/attackingflag parsing and theexcept <body>clause handling.Tests
try_parse_token("create x tokens that are copies of target creature you control", …)→CopyTokenOfwith aVariable("X")count."where X is the number of …"card bindsXto the correctQuantityRef."two") and"a"copy-token forms unchanged.Non-duplicate note
Rionya,Devastating Onslaught,Nacatl War-Pride, andAggressive Biomancyreturn 0 open or closed issues/PRs. The existing "create X tokens that are copies" issues (#2169, #1735, #1424) concern runtime token-count doubling and X-resolution, not the parser's inability to recognize theXcount — a distinct, parser-only gap.