DarkDragon
10-20-2006, 08:47 AM
One of the elements of the ZScript language that I originally designed poorly, as has been pointed out by several of you recently, are the script variables, which have various problems currently such as malfunctioning when multiple instances of the same script are running on the same screen. In this post I'll present some thoughts and opinions on how these variables should be redesigned, and invite comments from those of you who have shown interest in the matter.
First, a brief description of how script variables are currently implemented, for those not familiar with ZScript's inner workings. ZScripts are built in two broad steps:
1) The ZScript source is compiled into ZAsm assembly. This is the source you see when you look in allegro.log after compiling a ZScript.
2) Scripts are assigned to slots, and the ZAsm is copied verbatim into those slots.
We'll see the consequences of this two-step process in a minute.
Each running script has a stack associated with it, which is invisible to ZScript coders but is relied upon heavily for things like function calls and, relevant to this discussion, local variables. At compile time each function is scanned looking for declared variables, and each of these variables is reserved a place on the stack. For instance, in the snippet
void foo() {
int a;
for(int b=0; b<3; b++)
int c;
}
the three variables a,b,c would be assigned stack slots 0,1,and 2. Since each running script has its own stack, each script has its own separate copy of a, b, and c, and there are no problems.
But now consider a script variable:
ffc script foo {
int a;
}
Since a has to be visible to other scripts, a cannot be stored on the stack, as one script's stack cannot currently be read by a different script. Thus at the moment a is assigned a global variable, say GD0. But now if two scripts are running foo, because the ZASM code is just copied verbatim into both slots, both will be using GD0 simultaneously, leading to undesirable interference.
The goal, then, is to redesign the script variables so that they
1) still allow inter-script communication, and
2) are distinct for different running instances of the same script.
Satisfying 2) is not too difficult, and can be accomplished for instance by storing script variables on the stack, like local variables are.
But now we have a problem with 1). Currently there's no way in ASM to read a different script's stack, but that's not really a problem, a way to do so could be implemented. The actual problem is that which script is running in which slot is not known until runtime, whereas the ZScripts must be compiled at script-import time. For instance, consider the pair of scripts
ffc script one {int a;}
ffc script two {int a; int b;}
and you want to interact with a script running in slot 1. You can get a pointer to that script using LoadFFC:
ffc ptr = Screen->LoadFFC(1);
But now how would you go about specifying you want to change ptr's script variables? We could allow code like
ptr->b = 4; but the problem is that there's no way to know at compile time where on ptr's stack variable b is stored, and moreover we don't even know that ptr is an instance of two. Even worse
ptr->a might point to two different stack locations depending on whether ptr is a one or a two.
Other languages resolve this ambiguity using casts, ie
((one)ptr)->a = 4; but I'm not convinced this is the optimal approach. Alternatively, maybe there's a way to encode script information so that the script variable accesses can be validated and resolved entirely at runtime.
Thoughts?
First, a brief description of how script variables are currently implemented, for those not familiar with ZScript's inner workings. ZScripts are built in two broad steps:
1) The ZScript source is compiled into ZAsm assembly. This is the source you see when you look in allegro.log after compiling a ZScript.
2) Scripts are assigned to slots, and the ZAsm is copied verbatim into those slots.
We'll see the consequences of this two-step process in a minute.
Each running script has a stack associated with it, which is invisible to ZScript coders but is relied upon heavily for things like function calls and, relevant to this discussion, local variables. At compile time each function is scanned looking for declared variables, and each of these variables is reserved a place on the stack. For instance, in the snippet
void foo() {
int a;
for(int b=0; b<3; b++)
int c;
}
the three variables a,b,c would be assigned stack slots 0,1,and 2. Since each running script has its own stack, each script has its own separate copy of a, b, and c, and there are no problems.
But now consider a script variable:
ffc script foo {
int a;
}
Since a has to be visible to other scripts, a cannot be stored on the stack, as one script's stack cannot currently be read by a different script. Thus at the moment a is assigned a global variable, say GD0. But now if two scripts are running foo, because the ZASM code is just copied verbatim into both slots, both will be using GD0 simultaneously, leading to undesirable interference.
The goal, then, is to redesign the script variables so that they
1) still allow inter-script communication, and
2) are distinct for different running instances of the same script.
Satisfying 2) is not too difficult, and can be accomplished for instance by storing script variables on the stack, like local variables are.
But now we have a problem with 1). Currently there's no way in ASM to read a different script's stack, but that's not really a problem, a way to do so could be implemented. The actual problem is that which script is running in which slot is not known until runtime, whereas the ZScripts must be compiled at script-import time. For instance, consider the pair of scripts
ffc script one {int a;}
ffc script two {int a; int b;}
and you want to interact with a script running in slot 1. You can get a pointer to that script using LoadFFC:
ffc ptr = Screen->LoadFFC(1);
But now how would you go about specifying you want to change ptr's script variables? We could allow code like
ptr->b = 4; but the problem is that there's no way to know at compile time where on ptr's stack variable b is stored, and moreover we don't even know that ptr is an instance of two. Even worse
ptr->a might point to two different stack locations depending on whether ptr is a one or a two.
Other languages resolve this ambiguity using casts, ie
((one)ptr)->a = 4; but I'm not convinced this is the optimal approach. Alternatively, maybe there's a way to encode script information so that the script variable accesses can be validated and resolved entirely at runtime.
Thoughts?