The "Iterator:" macro generates two variables - one to store the main iterator data (the array) and the other to store the number of items in the iterator (the count):
new
Iterator:MyIter<7>;
Becomes:
new
// Number of items currently in the iterator.
Iter_Single@MyIter,
// The data in the iterator, plus internal data (all slots invalidated).
Iterator@MyIter[7 + 1] = {14, 13, ...};
The names are designed to give vaguely meaningful errors - Undefined symbol "Iterator@MyIter"
might give you some hint that an iterator is missing. Iter_Single
denotes a standard iterator, Iter_Multi
denotes a multi-iterator both are the number of items currently
added to the iterator, and not the total size of the iterator. "Iterator@MyIter"
is the values currently stored in the iterator.
Iterators are "linked lists", which means that each element points to the next valid element. Before adding anything to the array it is declared using:
Iterator@MyIter[7 + 1] = {7 * 2, 7 * 2 - 1, ...};
This declares the array as 7 cells big to store all the valid data, with an extra cell to store the start point of the list. The initialisation data is carefully crafted to be invalid. The use of "- 1" for the second value means that the compiler attempts to infer a pattern to the array declaration using the first two values (and determines that the pattern is that each slot is one less than the previous slot). This means that the array starts off looking like:
14, 13, 12, 11, 10, 9, 8, 7
Most of those values are greater than 7, so they are invalid. Our extra slot (the last one) has a value of "7" which is ALMOST valid. The code is designed so that the last slot ALWAYS contains its own index, which equals the declared size of the iterator. This is important for the linked list as will be shown later.
After adding the values "0", "4", and "6" the array will look like:
4, 13, 12, 11, 6, 9, 7, 0
FOUR slots have changed value - 0, 4, and 6 (the values we added to the iterator), and slot 7 (our extra slot added by the "Iterator:" macro). Slots 1, 2, 3, and 5 have not been touched so are all still invalid (as shown by the fact that they are all greater than the maximum valid slot value).
That's interesting, but why is it like that; and why is slot 0 "4", slot 4 "6" etc? Each valid slot has a value LESS THAN OR EQUAL TO the iterator size. So slot 0 is valid and has a value "<= 7" to prove it, as do slots 4, 6, and 7. But wait, slot 7 CAN'T be valid because it is too big - only slots 0 to 6 are within the declared range of the iterator, so why does it have a valid value stored in it? More to the point, why is that last slot initially declared as "7", when it shouldn't be valid?
Each valid slot's value is the index of the next valid slot. If we start at slot 0 we get the value "4", jumping to slot 4 gives us the value "6", and slot 6 is the last item we initially added to the iterator:
Iter_Add(MyIter, 0);
Iter_Add(MyIter, 4);
Iter_Add(MyIter, 6);
Already it should be clear how "foreach" can loop over only values that exist, unlike "for" which must loop over every possible value. From the information above we can write:
for (new i = Iterator@MyIter[0]; ; i = Iterator@MyIter[i])
{
printf("%d", i);
}
That will loop forever and output:
0
4
6
7
0
4
6
7
...
We need an end condition for the loop - which is exactly what we have the extra slot at the end for. Slot 6 has the value "7", which is a value we have already determined should be invalid, and indeed it is. So we can modify our loop to:
for (new i = Iterator@MyIter[0]; i != 7; i = Iterator@MyIter[i])
{
printf("%d", i);
}
New output:
0
4
6
Excellent. The linked list works and ends correctly - but does it START correctly? In this example yes, but in the more general case NO! What happens if we never added slot 0 to the iterator?
new
Iterator:MyIter<7>;
Iter_Add(MyIter, 4);
Iter_Add(MyIter, 6);
foreach (new i : MyIter)
{
printf("%d", i);
}
The output SHOULD BE:
4
6
But if we use our loop above we get:
14
<crash>
The problem is here:
new i = Iterator@MyIter[0];
After adding only the values "4" and "6", the array looks like:
14, 13, 12, 11, 6, 9, 7, 4
But we start at index 0, which has the value "14". We print that value, then try use that value as an index in to the array. The array has size "7 + 1", so accessing slot "14" is an out-of-bounds error and won't work (or will crash or do any other unpredictable things). So instead we need:
for (new i = Iterator@MyIter[4]; i != 7; i = Iterator@MyIter[i])
{
printf("%d", i);
}
And if we never add ANY values to the array we still have the original:
14, 13, 12, 11, 10, 9, 8, 7
And the resulting required code is below (note that this will end instantly because the value of slot 7 is "7", so the condition will fail immediately):
for (new i = Iterator@MyIter[7]; i != 7; i = Iterator@MyIter[i])
{
printf("%d", i);
}
In each case, the "new i" value must be initialised to the value stored in the first valid slot - so we need to determine WHICH is the first valid slot. That just so happens to be stored in the last slot of the array:
for (new first = Iterator@MyIter[7], i = Iterator@MyIter[first]; i != 7; i = Iterator@MyIter[i])
{
printf("%d", i);
}
This is, in essence, how "foreach" works, and that loop will now correctly print all the added values (if any) in all cases.
Now we have a basic understanding of how "foreach" works under the hood, lets look at how the macro itself works and generates significantly better code than that presented above. The simplest way to present this is to show it:
Doing:
foreach (new i : MyIter)
{
printf("%d", i);
}
Gives:
for (new i = 7; (i = Iterator@MyIter[i]) != 7; )
{
printf("%d", i);
}
This is significantly simplified from the original version, but still equivalent. The only possibly confusing part is the assignment within the condition, but this is equivalent to:
i = Iterator@MyIter[i];
if (i != 7)
{
printf("%d", i);
}
else break;
The first action in the loop is to get the NEXT item, then use that item. Thus we have to start with an item that is invalid or else the very first element would always get skipped because we would have the first item and instantly get the second item.
It might help to think of the loop like this:
for (new i = MyIter[7]; i != 7; i = MyIter[i])
{
}
Or more generically:
for (new i = MyIter[START_SLOT]; i != END_VALUE; i = MyIter[i])
{
}
But slightly more efficient. It just so happens that "START_SLOT" and "END_VALUE" are always the same, and always equal to the declared size of the iterator). We can define and use the generic values:
#define START_SLOT(%0) (sizeof (%0) - 1)
#define END_VALUE(%0) (sizeof (%0) - 1)
for (new i = START_SLOT(Iterator@MyIter); (i = Iterator@MyIter[i]) != END_VALUE(Iterator@MyIter); )
{
printf("%d", i);
}
These macros already exist in y_iterate so you don't even need the "YSII_Ag" suffixes:
for (new i = Iter_Begin(MyIter); (i = Iter_Next(MyIter, i)) != Iter_End(MyIter); )
{
}
That code is EXACTLY equivalent to the default "foreach" use, plus it gives you more control over the looping should you so desire:
stock PrintFivePlayers()
{
new
printed = 0;
foreach (new i : Player)
{
printf("%d", i);
// Limit the display.
++printed;
if (printed == 5) break;
}
}
Using the alternate form:
stock PrintFivePlayers()
{
for (new i = Iter_Begin(Player), printed = 0; printed != 5 && (i = Iter_Next(MyIter, i)) != Iter_End(MyIter); ++printed)
{
printf("%d", i);
}
}
Granted in this case the second version may be less appealing to look at, but it does avoid the use of "break" and keeps all the loop end conditions in one place. It also combines the efficiency of "foreach" with more code.
You may be tempted to "optimise" the loop by calling "Iter_End" just once with:
stock PrintFivePlayers()
{
for (new i = Iter_Begin(Player), printed = 0, end = Iter_End(MyIter); printed != 5 && (i = Iter_Next(MyIter, i)) != end; ++printed)
{
printf("%d", i);
}
}
You can, and that will run, but "Iter_End" is actually a macro not a function call and doing it this way is a rare case in which that will end up being LESS efficient unfortunately.
Using similar methods to those above you can also go BACKWARDS through an iterator:
stock PrintPlayersBackwards()
{
for (new i = Iter_End(Player); (i = Iter_Prev(MyIter, i)) != Iter_Begin(MyIter); )
{
printf("%d", i);
}
}
However, while this was added to "y_iterate" for completeness sake, it is INCREDIBLY inefficient and you are MUCH better off using this where possible:
stock PrintPlayersBackwards()
{
for (new i = MAX_PLAYERS; i--; )
{
if (IsPlayerConnected)
{
printf("%d", i);
}
}
}
This is because iterators are one-way (and very good at doing things one way), but when you try and go backwards they can still only go forwards so must go all theway through the whole loop until they hit the current value and know where they came from for EVERY iteration. Tracing the loop would give something like:
7, 0, 4, 6, 7 - Print 6
7, 0, 4, 6 - Print 4
7, 0, 4 - Print 0
7, 0 - End
I did add efficient reverse iterators to the code once, but they doubled the memory usage and were rarely (if ever) used so were basically worthless.
We will now look at one important chunk of the macros in "y_iterate", used to
determine exactly which version of foreach
has been entered. To understand
the macros however, we must first understand what we are trying to parse. The
following code demonstrates all the possible variations of foreach
that can
appear in pawn:
// Original version (implicit declaration):
foreach (Player, playerid)
// New version:
foreach (playerid : Player)
// New version with declaration:
foreach (new playerid : Player)
// New version with tag declaration:
foreach (new Group:group : PlayerGroup)
We need to write macros to correctly identify and process each of these versions of the keyword. There is also an older version of the command that looked like:
// Original version (no declaration):
foreachex (Player, playerid)
This was different from foreach
in that it DIDN'T declare playerid
as a new
variable, which the standard foreach
keyword did. This has been deprecated in
the later versions of "y_iterate" because the new
keyword is now explicit.
Fortunately, this version is much easier to express in the new system.
There are two things that we need to detect - number of colons, and number of
new
keywords. Zero colons means the old syntax is in use, anything else is
the new syntax. You could in theory have:
// Original version with tag (implicit declaration):
foreach (PlayerGroup, Group:group)
However, this was never supported and no tagged iterators were ever written for this older version. We can therefore ignore this possibility.
At this point I'd love to detail the logical progression that led to my development of these macros, in the hope that people could learn something from the thought process, but quite simply I can't! I have no idea how I came up with this system, it was just a lot of thinking resulting in a brainwave that I can't explain - which is a real shame IMHO. As a result, I can only document how the macros work.
Note that some of this section is now a little out of date. The basic structure is still the same, but special iterator support has improved significantly since this was written. Also, this still uses the old internal naming scheme of @YSII_Ag
, not Iterator@
(which is designed for better user readability). Despite this, the explanation of what the macros do should still help with understanding the current versions.
The main foreach
macro actually revolves around this macro:
#define new%0|||%9|||%1:%2||| %9|||%0|||%1|||%2|||
That actually redefines the new
keyword to something different (I'm not too
happy about having to do this). Because it redefines such a pivotal parf of the
PAWN language, I have made sure that it does not match common code. As you can
see, the pattern for matching is this:
new ||| ||| : |||
I've used spaces here instead of macro parameters (%0
etc) to make the pattern
clearer and represent optional code typed by the user. If you have any new
variable declarations with this exact sequence of operators after it then I
apologise - this will probably break you code. But I think that's unlikely!
The replacement for the macro is:
%9|||%0|||%1|||%2|||
We detect the new
keyword, but we don't have it present in the replacement, so
this code looks for new
and removes it. It also shuffles the remaining
parameters and removes the detected colon, but the result is still not valid
code.
To further understand the macro, lets look at it being used:
#define foreach%1(%0) for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||%0||| )
Here %1
simply detects extra spaces and discards them. We then add the word
new
, which as we've seen is instantly detected and removed. Why not simply
write:
#define foreach%1(%0) for (Y_FOREACH_THIRD|||Y_FOREACH_SECOND|||%0||| )
You might think that would be the result of the macros after calling the new
macro, but you would be wrong - sometimes...
The key fact to note here is that the new
macro looks for three sets of |||
AND a colon. We didn't write the colon in the replacement text for foreach
,
so it must be part of the parameter %0
. If we type:
foreach (new playerid : Player)
We have %1
as a single space (discarded) and %0
as new playerid : Player
.
Manually replacing macros gives:
// Detect and replace `foreach`....
for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||new playerid : Player||| )
// Detect and replace `new`...
for (Y_FOREACH_THIRD||| Y_FOREACH_SECOND|||new playerid ||| Player||| )
So what happens if we DON'T type a colon? The only time this happens is when we
use the old-style foreach
syntax:
foreach (Player, playerid)
// Detect and reaplce `foreach`:
for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||Player, playerid||| )
// DO NOT replace `new` because there is no colon to match.
Compare the outputs side-by-side:
foreach (new playerid : Player)
// Gives:
for (Y_FOREACH_THIRD||| Y_FOREACH_SECOND|||new playerid ||| Player||| )
foreach (Player, playerid)
// Gives:
for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||Player, playerid||| )
Each foreach
line declares a new variable (this fact is hidden in the second
one, which the main motivation for developing the new syntax). In the resulting
generated code each for
loop has a new
keyword somewhere in it, and each one
starts with a DIFFERENT macro. The first one starts with Y_FOREACH_THIRD
, the
second one starts with Y_FOREACH_SECOND
(don't worry that the first one isn't
called Y_FOREACH_FIRST
, that's just because of the order I am describing the
macros in). This demonstrates why we remove the new
keyword, and why we
shuffle the first two parameters around.
This macro deals with the old version of the syntax, and is very simple to
parse. From the earlier parts of this tutorial, we know that the end product of
a foreach
loop is:
// This:
foreach (Iter, var)
// Or this:
foreach (new var : Iter)
// Becomes:
for (new var = sizeof (Iter@YSII_Ag) - 1; (var = Iter@YSII_Ag[var]) != sizeof (Iter@YSII_Ag) - 1; )
So we can very quickly construct the Y_FOREACH_SECOND
macro and step through
its expansion:
// Macro:
#define Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||%2,%1||| %1 = sizeof (%2@YSII_Ag) - 1; _:(%1 = %2@YSII_Ag[%1]) != sizeof (%2@YSII_Ag) - 1;
// Input:
foreach (Player, playerid)
// Steps:
// Detect and reaplce `foreach`...
for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||Player, playerid||| )
// DO NOT replace `new` because there is no colon to match.
// Detect and replace `Y_FOREACH_SECOND`:
for (new playerid = sizeof (Player@YSII_Ag) - 1; _:( playerid = Player@YSII_Ag[ playerid]) != sizeof (Player@YSII_Ag) - 1; )
// Done!
If this macro is the first one listead, instead of Y_FOREACH_SECOND
, then the
user used the new syntax instead of the old syntax, so let us parse that
instead (here %9
stores the other macro name and discards it):
// Macro:
#define Y_FOREACH_THIRD|||%9|||%1|||%2||| %1 = sizeof (%2@YSII_Ag) - 1; _:(%1 = %2@YSII_Ag[%1]) != sizeof (%2@YSII_Ag) - 1;
// Input:
foreach (new playerid : Player)
// Steps:
// Detect and reaplce `foreach`...
for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||new playerid : Player||| )
// Detect and replace `new`...
for (Y_FOREACH_THIRD||| Y_FOREACH_SECOND|||new playerid ||| Player||| )
// Detect and replace `Y_FOREACH_THIRD`...
for (new playerid = sizeof ( Player@YSII_Ag) - 1; _:(new playerid = Player@YSII_Ag[new playerid ]) != sizeof ( Player@YSII_Ag) - 1; )
// DO NOT replace `new` because there are no `|||`s to match.
// Done!
BAH! So close! The new
keyword is still attached to the playerid
symbol,
so every time we use one we use the other. In this case, we need to again
detect and remove the new
keyword - an operation that we already wrote a macro
for:
#define new%0|||%9|||%1:%2||| %9|||%0|||%1|||%2|||
This is where the foreach
macros start getting really clever by repeatedly
calling this new
macro with different macro names as parameters. The first
time we passed the two macros called Y_FOREACH_SECOND
and Y_FOREACH_THIRD
,
this time we will pass different ones. Let's have a second attempt at that
Y_FOREACH_THIRD
macro:
// We previously removed the colon, but now we must put it back to be detected.
#define Y_FOREACH_THIRD|||%9|||%1|||%2||| %1|||Y_FOREACH_FOURTH|||%1:%2|||
// If there is a `new` in `%1`, then `Y_FOREACH_FOURTH` will be run.
#define Y_FOREACH_FOURTH|||%0|||%1|||%2||| new %0 = sizeof (%2@YSII_Ag) - 1; _:(%0 = %2@YSII_Ag[%0]) != sizeof (%2@YSII_Ag) - 1;
// Input:
foreach (new playerid : Player)
// Steps:
// Detect and reaplce `foreach`...
for (new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||new playerid : Player||| )
// Detect and replace `new`...
for (Y_FOREACH_THIRD||| Y_FOREACH_SECOND|||new playerid ||| Player||| )
// Detect and replace `Y_FOREACH_THIRD`...
for (new playerid |||Y_FOREACH_FOURTH|||new playerid : Player||| )
// Detect and replace `new` again...
for (Y_FOREACH_FOURTH||| playerid |||new playerid ||| Player||| )
// Detect and replace `Y_FOREACH_FOURTH`...
for (new playerid = sizeof ( Player@YSII_Ag) - 1; _:( playerid = Player@YSII_Ag[ playerid ]) != sizeof ( Player@YSII_Ag) - 1; )
// DO NOT replace `new` because there are no `|||`s to match.
// Done!
By calling the new
macro twice we have managed to strip off just the word
new
from playerid
so that we know it was present but can add it in only
where we want it.
There are a couple of ways to detect specific pieces of text - a define is one,
putting the text directly in the pattern is another. To detect the keyword
new
we could do:
#define new%0|||
Or:
#define OTHER%0new%1|||
The first will detect anything starting with new
, the second will detect
anything that starts with OTHER
(just as an example) and that includes the
text new
somewhere after it. The difference is in exactly how much is
matched. A define CALLED new
, as the first one is, will search for only that
exact word - the word newt
will NOT be matched. The second one will match
exactly the word OTHER
, but then just searches for the three letters n
, e
,
and w
in order after it, so WILL detect the following:
MY OTHER newt|||
In that case, %0
will be a single space and %1
will be the letter t
. This
is a subtle point when dealing with the pre-processor. This is important
because were we to use the second method, any variable name with new
within it
would cause a compilation error:
foreach (newt : Animal)
There is a second reason as well - the result if the match is NOT made. If we do:
#define S: MACRO_1 MACRO_2
#define MACRO_1%0MACRO_2%0new%1||| new %1; // Found `new`.
#define MACRO_2%1||| %1; // Didn't find `new`.
(Side note, the use of %0
twice in that example is valid - the second one will
overwrite the contents of the first one, but both are discarded anyway).
Then doing:
S: new var|||
Will give:
new var;
But doing:
S: printf("%d", var)|||
Will give:
MACRO_1 printf("%d", var);
Because we couldn't find new
, the MACRO_1
macro is never matched and the
text remains, leaving us with the word MACRO_1
in the source-code where it
shouldn't be, and thus giving a compilation error.
If we instead do something like:
#define S: new MACRO_3|||
#define new%0|||%1||| new other,%1;
#define MACRO_3|||%1; %1
Then doing:
S: var|||
Will give:
new other, var;
And doing:
S: var;
Will give:
new var;
In the second case, we have an excess new
left over from the first macro that
wasn't matched, but we have designed the macros such that this remainder is
still valid in this context, and so that we won't get any compile-time errors.
This is basically how all the macros in "y_iterate" are designed - if the new
keyword is not matched, it must be because we have a pattern that requires the
new
keyword to be present and therefore leaving it in the source code output
is valid.
The code presented so far is a little inaccurate, but is enough to describe the
basic principles on which the whole code block is based. They show how we use
the new
keyword to detect certain patters and strip out certain words, and how
the macros are shuffled around so that different code is run when new
is there
and when it isn't. Given this grounding, the remaining macros will not be
detailed to the same extent the first few are, but just to get you started, here
is the REAL version of Y_FOREACH_THIRD (note that %1
and %2
are the other
way around here to what they were before):
#define Y_FOREACH_THIRD|||%0|||%1|||%2||| %1=Y_FOREACH_FIFTH|||Y_FOREACH_FOURTH|||%1:%2|||
Y_FOREACH_FOURTH
is still there, but Y_FOREACH_FIFTH
has been added,
separated from whatever is in %1
by an equals sign (if %1
is a variable, a
lack of delimiter here could cause the to names to merge and no longer be
valid), to run more code when there is no new
keyword:
#define Y_FOREACH_FIFTH|||Y_FOREACH_FOURTH|||%1:%2||| sizeof (%2@YSII_Ag) - 1; _:(%1 = %2@YSII_Ag[%1]) != sizeof (%2@YSII_Ag) - 1;
// Handles:
foreach (playerid : Player)
Note that again, the real macro is slightly different, but the extra indirection handles iterator arrays and special iterators, it can basically be read as the code above:
#define Y_FOREACH_FIFTH|||Y_FOREACH_FOURTH|||%1:%2||| _Y_ITER_FOREACH_SIZE(%2);_:(%1=_Y_ITER_ARRAY$%2$YSII_Ag[%1])!=_Y_ITER_MAYBE_ARRAY(%2);
_Y_ITER_MAYBE_ARRAY
gets the size of a slot in an iterator, for both normal
iterators and iterator arrays. _Y_ITER_FOREACH_SIZE
does the same thing, but
can also detect special iterators defined with ()
syntax. _Y_ITER_ARRAY
gets the next slot in the current iterator for both normal iterators and arrays.
The real definition of Y_FOREACH_FOURTH
is not actually a final output - it is
another redirection designed to try and detect declarations with tag overrides.
It adds in the macros Y_FOREACH_SIXTH
and Y_FOREACH_SEVENTH
:
// Tries to detect a colon via `new` despite us not adding it back (as in 3rd).
#define Y_FOREACH_SEVENTH|||%9Y_FOREACH_SIXTH;%0|||%1|||%2||| new %0:%1 = %0:(sizeof (%2@YSII_Ag) - 1); _:(%1 = %0:_Y_ITER_ARRAY$%2$YSII_Ag[%1]) != sizeof (%2@YSII_Ag) - 1;
// Handles:
foreach (new Group:g : CreatedGroup)
#define Y_FOREACH_SIXTH;%1|||Y_FOREACH_SEVENTH|||%2||| %1 = sizeof (%2@YSII_Ag) - 1; _:(%1 = _Y_ITER_ARRAY$%2$YSII_Ag[%1]) != sizeof (%2@YSII_Ag) - 1;
// Handles:
foreach (new playerid : Player)
So to recap:
Y_FOREACH_SECOND
- Generates code for the original syntax.
Y_FOREACH_FIFTH
- Generates code for the new syntax with no new
.
Y_FOREACH_SEVENTH
- Generates code for the new syntax with tags.
Y_FOREACH_SIXTH
- Generates the remainder of the new syntax (new
, no tags).
The others just call new
again to try resolve which version to use.
Hopefully from this description you should now be able to read and comprehend this code, taken straight from "y_iterate":
#define foreach%1(%0) for(new Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||%0|||)
// This allows us to use `new` multiple times - stripping off ONLY whole words.
#define new%0|||%9|||%1:%2||| %9|||%0|||%1|||%2|||
// This one is called if the new syntax is required, but the state of `new` is
// as-yet unknown. This attempts to call `%1` as a macro, if it starts with
// `new` as a whole word then it will (and will also helpfully strip off the
// `new` keyword for us).
#define Y_FOREACH_THIRD|||%0|||%1|||%2||| %1=Y_FOREACH_FIFTH|||Y_FOREACH_FOURTH|||%1:%2|||
// This is called if the `new` macro is called for a second time.
#define Y_FOREACH_FOURTH|||%0=Y_FOREACH_FIFTH|||%1|||%2||| new Y_FOREACH_SIXTH;%0|||Y_FOREACH_SEVENTH|||%2|||
// This is called when there are tags on the `new` declaration.
#define Y_FOREACH_SEVENTH|||%9Y_FOREACH_SIXTH;%0|||%1|||%2||| new %0:%1=%0:_Y_ITER_FOREACH_SIZE(%2);_:(%1=%0:_Y_ITER_ARRAY$%2$YSII_Ag[%1])!=_Y_ITER_MAYBE_ARRAY(%2);
// This is called when there aren't.
#define Y_FOREACH_SIXTH;%0|||Y_FOREACH_SEVENTH|||%2||| %0=_Y_ITER_FOREACH_SIZE(%2);_:(%0=_Y_ITER_ARRAY$%2$YSII_Ag[%0])!=_Y_ITER_MAYBE_ARRAY(%2);
// This is called if `%1` didn't have `new` at the start.
#define Y_FOREACH_FIFTH|||Y_FOREACH_FOURTH|||%1:%2||| _Y_ITER_FOREACH_SIZE(%2);_:(%1=_Y_ITER_ARRAY$%2$YSII_Ag[%1])!=_Y_ITER_MAYBE_ARRAY(%2);
// This is the old version, but DON'T add `new` because that already exists from
// the failed `new` macro call above.
#define Y_FOREACH_SECOND|||Y_FOREACH_THIRD|||%1,%2||| %2=_Y_ITER_FOREACH_SIZE(%1);_:(%2=_Y_ITER_ARRAY$%1$YSII_Ag[%2])!=_Y_ITER_MAYBE_ARRAY(%1);