Roger Ramjet
New member
The following code demonstrates what appears to be inconsistencies in the treatment of the foreach statement in ASH. The code was verified and run in r10016 and r10058, and shows no errors.
(Note that this code is designed to demonstrate certain specific issues, not to actually perform any work, although it is derived from functional code).
There are three issues demonstrated by this code ...
Issue 1:
First, look at routine A. If the line "item x;" is commented-out (as shown), this verifies fine. If that line is not commented-out, a verify produces an error; "Key variable 'x' is already defined (foreach test.ash, line 7)", apparently due to the redeclaration of "x" in the foreach statement.
Next look at routine B. It is effectively the same as routine A, with the line "item y;" included, but with the foreach statement within the bounds of the if statement. This verifies as correct, despite the declaration of "y".
Why does routine A produce an error but routine B does not produce one? If the foreach in routine B self-declares its key, should not this cause a error? If the foreach is not self-declaring its key, then why does routine A produce an error if "item x;" is included? This is not consistent behavior in the compiler. The situation appears to depend on whether or not the foreach is at the top level of a routine or not.
Issue 2:
Look at routines B and C. They are effectively the same, except routine B includes a declaration for the key in the foreach statement (the line "item y;"). As noted in Issue 1, this would, if consistent with routine A, produce an error; but it does not.
It would appear, at first glance, that ASH does not care if a foreach key is also declared within a routine, as long as the foreach is not in the highest scope of the routine with the variable declaration.
This, however, allows the anomalous behavior shown in routine D. There, "item i" is declared at the routine level and used as the foreach key. Executing the script produces the following output for routine D:
D before foreach item i = maiden wig
D before foreach int j = 5
D in foreach item i = heavy D
D in foreach int j = 5
D in foreach item i = original G
D in foreach int j = 5
D after foreach item i = maiden wig
D after foreach int j = 5
Clearly there are two separate variables "i" active within routine D. One in most of the routine, and one which is limited to the scope of the foreach statement. Further, based on the results of routine A, this changing scope only appears to happen when foreach statements are within the scope of other control statements, giving the foreach statement different behaviors at different points in code. This is not the way that most programmers would expect code to work, and is something which will produce hard to debug errors.
Issue 3:
To those who write the ASH documentation and tutorials ...
No where that I have seen (and I read everything I could find) did anyone mention that the ASH foreach statement self-declares its key variable and that the key is defined exclusively within the scope of its foreach statement. To C# programmers this may seem normal (except that C# requires a redundant type identifier within its foreach key); however, this action is not typical of many other programming languages. It should be specifically mentioned in the documentation.
(Note that this code is designed to demonstrate certain specific issues, not to actually perform any work, although it is derived from functional code).
Code:
// When running this code change the three if statements
// "if (!have_skill($skill[pulverize]))" to be evaluate to true for the
// skill set of the character (i.e., leave or remove the "!").
void A()
{
// If the line "item x;" is present, a verify error occurs.
// Key variable 'x' is already defined (foreach test.ash, line 7)
// item x;
foreach x in $items[heavy D, original G]
{ print("A item x = " + x);
}
return;
}
void B()
{
// Note the presence of "item y;" here but there is no verify error for
// the foreach statement.
item y;
if (!have_skill($skill[pulverize]))
{ foreach y in $items[heavy D, original G]
{ print("B item y = " + y);
}
}
return;
}
void C()
{
// Note no declaration of "item z".
if (!have_skill($skill[pulverize]))
{ foreach z in $items[heavy D, original G]
{ print("C item h = " + z);
}
}
return;
}
void D()
{
item i;
int j;
i = $item[maiden wig];
j = 5;
print("D before foreach item i = " + i);
print("D before foreach int j = " + j);
if (!have_skill($skill[pulverize]))
{ foreach i in $items[heavy D, original G]
{ print("D in foreach item i = " + i);
print("D in foreach int j = " + j);
}
}
print("D after foreach item i = " + i);
print("D after foreach int j = " + j);
return;
}
void main()
{
A();
B();
C();
D();
}
There are three issues demonstrated by this code ...
Issue 1:
First, look at routine A. If the line "item x;" is commented-out (as shown), this verifies fine. If that line is not commented-out, a verify produces an error; "Key variable 'x' is already defined (foreach test.ash, line 7)", apparently due to the redeclaration of "x" in the foreach statement.
Next look at routine B. It is effectively the same as routine A, with the line "item y;" included, but with the foreach statement within the bounds of the if statement. This verifies as correct, despite the declaration of "y".
Why does routine A produce an error but routine B does not produce one? If the foreach in routine B self-declares its key, should not this cause a error? If the foreach is not self-declaring its key, then why does routine A produce an error if "item x;" is included? This is not consistent behavior in the compiler. The situation appears to depend on whether or not the foreach is at the top level of a routine or not.
Issue 2:
Look at routines B and C. They are effectively the same, except routine B includes a declaration for the key in the foreach statement (the line "item y;"). As noted in Issue 1, this would, if consistent with routine A, produce an error; but it does not.
It would appear, at first glance, that ASH does not care if a foreach key is also declared within a routine, as long as the foreach is not in the highest scope of the routine with the variable declaration.
This, however, allows the anomalous behavior shown in routine D. There, "item i" is declared at the routine level and used as the foreach key. Executing the script produces the following output for routine D:
D before foreach item i = maiden wig
D before foreach int j = 5
D in foreach item i = heavy D
D in foreach int j = 5
D in foreach item i = original G
D in foreach int j = 5
D after foreach item i = maiden wig
D after foreach int j = 5
Clearly there are two separate variables "i" active within routine D. One in most of the routine, and one which is limited to the scope of the foreach statement. Further, based on the results of routine A, this changing scope only appears to happen when foreach statements are within the scope of other control statements, giving the foreach statement different behaviors at different points in code. This is not the way that most programmers would expect code to work, and is something which will produce hard to debug errors.
Issue 3:
To those who write the ASH documentation and tutorials ...
No where that I have seen (and I read everything I could find) did anyone mention that the ASH foreach statement self-declares its key variable and that the key is defined exclusively within the scope of its foreach statement. To C# programmers this may seem normal (except that C# requires a redundant type identifier within its foreach key); however, this action is not typical of many other programming languages. It should be specifically mentioned in the documentation.