Still somewhat messy, but still reported as soon as it can be detected.
If an ELSE token is detected at the top level, for example, the error position will be rewound to the state change and reported there.
This means that in this situation:
x()
{
if (1)
{
state default;
x(2);
}
else ;
}
default{timer(){}}
an error will be reported in x(2), because the ELSE hasn't been found at that point, therefore the state change statement isn't found to be at fault yet.
However, in this case:
x()
{
if (1)
state default;
else
x(2);
}
default{timer(){}}
the error WILL be reported at the state change statement.
This commit also changes the position where the exception is reported, to be at the STATE token. As an inconsequential side effect, EParseCantChangeState takes precedence over undefined identifiers, in case the state change is to an undefined state, but only in cases where it can be immediately detected.
if (!cond) X; else Y; -> if (cond) Y; else X;
if (int1 == int2) X; else Y; -> if (int1 ^ int2) Y; else X;
When 'cond' is of a type other than 'key': if (cond) ; else X; -> if (!cond) X; (this required changing if(str) to its compiled equivalent if(!(str == "")), so that 'cond' is always either key or integer).
if (cond) ; -> cond; and folds it as a statement, which may eliminate it if it's SEF. This is done after eliminating 'else ;' so that it also optimizes 'if (cond) ; else ;' the same way.
This removes a TODO item.
Allows detection of empty events, for example, even if they have labels.
Also, it is OK if there's a label inserted in a nested {}; that case wasn't contemplated.
Gives us a few more opportunities for catching single-letter identifiers.
UsedNames was not restarted. It's unlikely that this had any detrimental effect on optimization, and it was certainly safe to not restart it. But it looks more correct like this.
When a constant was negative internally, it was output with the sign included. The code was not prepared to handle this, and could therefore cause double minus signs. For example, -2147483648 was output as --2147483648, and -4294967296 was output as --1.
Fixed by adding a space for floats, and by translating the number to the range 2147483648..4294967295 for integers (hex would have worked just as well).
The comment was wrong anyway. If one side changes x and the other side uses x, then order is still important, no matter whether one side is SEF.
But the reversal is safe when one side is a constant, so we still perform it, to enable optimization of some important cases.
For floats:
When const >= function.max, comparisons of function > const always yield FALSE.
When const < function.min, comparisons of function > const always yield TRUE.
When const > function.max, comparisons of function < const always yield TRUE.
When const <= function.min, comparisons of function < const always yield FALSE.
For integers:
When min = -1, cond(function > -1) is the same as cond(!~function).
When min = -1, cond(function < 0) is the same as cond(~function).
To implement the above, we got rid of the cond(x < 0) -> cond(x & 0x80000000) optimization, which has caused more trouble than it has solved for just 1 byte gain.
When min = 0, cond(function > 0) is the same as cond(function).
When min = 0, cond(function < 1) is the same as cond(!function).
Similar expressions can be obtained for max in [-1, 0], but it's not worth it, as there are no functions with -1 as maximum, and the ones with max=0 also have min=0 (always return 0).
llSubStringIndex and llListFindList with min=-1 allow optimizing this:
if (llListFindList(...) < 0) -> if (~llListFindList(...))
That has been implemented since long, but we didn't have the data.
We had dormant code to check for boolean-ness of functions, which is now active. But it didn't cover all possible booleans. Now it does.
An idea for the future is to associate ranges to expressions, and attach them to calculable functions. For example, (integer)llFrand(2) could be resolved to a boolean.
This solves a long-standing issue where we needed more data about LSL functions than just whether it's side-effect-free.
There's still some debug code, which is kept for history purposes.
- Separate library loading code into a new module. parser.__init__() no longer loads the library; it accepts (but does not depend on) a library as a parameter.
- Add an optional library argument to parse(). It's no longer mandatory to create a new parser for switching to a different builtins or seftable file.
- Move warning() and types from lslparse to lslcommon.
- Add .copy() to uses of base_keywords, to not rely on it being a frozen set.
- Adjust the test suite.
They were returning TOUCH_INVALID_TEXCOORD for num <= idx <= 15 in detection events which were not touch events. That is incorrect.
Now it correctly returns:
- ZERO_VECTOR when idx < 0 or idx > 15 or the event is known not to be a detection event.
- TOUCH_INVALID_TEXCOORD when idx == 0 and the event is known to be a detection event that is not a touch event.
- Raises ELSLCantCompute otherwise.
We oversought that the optimization that 8d33746 applied was already present, so no need to duplicate it.
A better place for handling '|' was under the code that already did so. No functionality change involved.