ffc script foobar {
void run() {
}
void a(int x) {
}
void b(int x) {
}
}This script yields the following message when compiling:
tmp, line 10: Error S08: There is already a variable with name x defined in this scope.ffc script foobar {
void run() {
}
void a(int x) {
}
void b(int x) {
}
}This script yields the following message when compiling:
tmp, line 10: Error S08: There is already a variable with name x defined in this scope.pkmnfrk wrote:ffc script foobar {
void run() {
}
void a(int x) {
}
void b(int x) {
}
}This script yields the following message when compiling:
tmp, line 10: Error S08: There is already a variable with name x defined in this scope.
That's because both variables are in the same scope. You can't use the same name for two variables in the same scope. That's just the way it is.
But, why would they be in the same scope? Every other language I've used has parameters to different functions be in different scopes. Plus, having unique parameters to every function in a script can be a real pain. Example:
ffc script firechaser {
void run() {
while(true) {
moveto(Link->X, Link->Y, 100); //chase link
sleep(100); //pause
makefire((this->X + 8) % 16, (this->Y + 8) % 16, 123);
}
void sleep(int frames) {
int i;
for(i=0;i<frames;i++)
WaitFrame();
}
void moveto(int x, int y, int frames) {
//calculate movement however you wish...
}
void makefire(int x, int y, int combo) {
Screen->ComboD[ComboAt(x, y)] = combo;
}
}
}This is a (nearly) fully featured custom enemy that chases link around (I omitted the moveto function since the details are irrelavent at this moment), and starts turning the floor to lava (or, whatever else combo 123 is (again, irrelavent)).
But, as it is, it'll fail compiling because x, y and frames are all used twice as parameter names, even though they're perfectly reasonable names.
pkmnfrk wrote:But, why would they be in the same scope? Every other language I've used has parameters to different functions be in different scopes. Plus, having unique parameters to every function in a script can be a real pain. Example:ffc script firechaser {
void run() {
while(true) {
moveto(Link->X, Link->Y, 100); //chase link
sleep(100); //pause
makefire((this->X + 8) % 16, (this->Y + 8) % 16, 123);
}
void sleep(int frames) {
int i;
for(i=0;i<frames;i++)
WaitFrame();
}
void moveto(int x, int y, int frames) {
//calculate movement however you wish...
}
void makefire(int x, int y, int combo) {
Screen->ComboD[ComboAt(x, y)] = combo;
}
}
}This is a (nearly) fully featured custom enemy that chases link around (I omitted the moveto function since the details are irrelavent at this moment), and starts turning the floor to lava (or, whatever else combo 123 is (again, irrelavent)).
But, as it is, it'll fail compiling because x, y and frames are all used twice as parameter names, even though they're perfectly reasonable names.
int foo = 10;
if (foo == 10)
{
foo +=10;
}
else foo = 10;
int foo = 20;
You'd get a compile error saying that foo is already declared. The scope of "foo" is from its start point until the braces it is enclosed in are closed, or the script ends, whichever is first. Take this example:
void run()
{
int foo = 10;
if (foo == 10)
{
foo = 20;
}
else if (foo == 20)
{
int bar = 30;
}
if (bar == 30)
{
bar = 40;
}
else if (bar == 40)
{
foo += 10;
bar += 10;
}
int foobar = foo + bar;
}
pkmnfrk's right, though: in other languages (including C, which ZScript is based on), the scope of a function argument is the function body, so arguments to different functions can have the same names.
pikaguy900 wrote:
You'd get a compile error saying that foo is already declared. The scope of "foo" is from its start point until the braces it is enclosed in are closed, or the script ends, whichever is first.
Yup. When compiled, the initial values of parameters should be popped off the stack into variables that happen to have the same names as the parameters did. So, the variables are actually defined in the script. (I'm not sure of how variables work in ZASM, as anything related to assembler makes me cry)
Related, it's clear that the scoping rules (or lack thereof) are totally borked. Upon further reflection, I think I understand why.
I believe that "function calls" are more cut-and-pasting of functions, rather than actual calls, per se. Example:
void a(int i) {
i = 1234;
b();
}void b() {
//do something
Trace(i)
}
This compiles to approximately:
void a(int i) {
i = 1234;
//do something
Trace(i);
}So, given this, this might be difficult to fix, other than implementing true function calls...
Edit: And, upon rereading what I wrote, I also realize the nature of this bug.
If a script (which looks a bit like a class) in ZScript is compiled into ZASM (which has no such concept, and thus everything is simplified into one big function), then all the variables get smushed into this one big function.
Ergo, the parameters of the different functions act like regular variables in the context of whatever script is calling it (cut and paste, remember), and thus they must be uniquely named (as two distinct variables can't have the same name).
Hmm.
Saffith wrote:pkmnfrk's right, though: in other languages (including C, which ZScript is based on), the scope of a function argument is the function body, so arguments to different functions can have the same names.
Function body? ...Eh, I guess I'd find out what that is if I read an online C/C++ tutorial...
Thanks, though. I never knew that. o.o
The body of a function is everything between { and }
You're wrong though. Yes, everything sorta gets mushed into "one big function," but that's how all compilers work. As in C, in ZScript function calling is handled as follows:
-The PC of the instruction after the function call is pushed onto the stack.
-Parameters are pushed onto the stack.
-Execution transfers to the function via GOTO.
-Parameters are popped from the stack.
-The function does stuff.
-The function's return value is stored in D0.
-The function's return address is popped from the stack.
-Execution returns to the caller via GOTOR.
That's as "true" as functions can get.
Two functions having the same variable name doesn't matter to the compiler; variable names are just a convenience for the programmer. One of the first tasks the compiler performs (building the symbol tables) is determining what the scopes are, and replacing all distinct variables with unique IDs.
For example, unless the ZScript compiler is extremely broken, the following should be a legal function:
int foo = 1;
{
int foo = 2;
{
int foo = 3;
{
int foo = 4;
Trace(foo);
}
Trace(foo);
}
Trace(foo);
}
Trace(foo);
Fixed in the next build.
Ahh, I read somewhere that function calling was just copy and paste. Must've been an old thread or something. That's fine, and good to hear.
Perhaps you're thinking of the import statement, which does have no sophistication beyond copy-and-paste (introducing some subtle problems that luckily nobody has found and harassed me about yet 😉 )
Please. I know I'm kinda new around here, but I know the difference between calling a function and inc... importing a header. (Damnit, why is it import, and not include?! Insanity! 😊)