When a global list includes a reference to a global variable of type key, the corresponding list entry type is string, not key (SCR-295, possibly caused by SVC-1710 or SVC-4485).
This implementation is fishy, because it hard-codes the type in the node regardless of the child types. But in some quick experimenting, it seemed to work. And since the main purpose is to document LSO's behaviour, rather than actually being usable, it's OK like that.
The previous commit didn't work as expected. "from module import var" freezes the value at load time; changing it later has no effect. A reference to the module needs to be used.
Fix that and the similar problem with LSO. Also revert some "from lslcommon import *" introduced earlier.
That also revealed another bug about missing 'cond' in the import list of lslextrafuncs. This should fix all functions that return values on null key input.
Instead of using an option in the command line, use a global in lslcommon, settable by the main program (only the main LSLCalc program, which differs from LSL-PyOptimizer's main, changes it).
lslcalc is currently derived from a snapshot of PyOptimizer at some point in past, making it difficult to maintain when bug fixes are applied to the optimizer. Solve this by incorporating expression evaluation capabilities.
Literal strings were not conforming to Mono's strict "NUL is end-of-string" rules, so a file with an embedded NUL within a string literal would let the NUL be part of the string. Mostly academic, because it's probably not possible to upload a file with an embedded NUL to SL, but fixed it regardless.
As an enhancement over LSL, we trigger a Type Mismatch when there are void expressions in list constructors, because in LSL, while accepted, they trigger an ugly runtime exception.
This works fine, but then expression lists, where this are checked, are not exclusive of list constructor; they are used in other places. One of these places is the initializer and the iterator of FOR loops. As a consequence, we didn't allow void functions in the FOR initializer or iterator.
Fix by adding another possible value to the parameter 'expected_types' in Parse_expression_list. False means don't allow void either (what Null did before); Null now means allow anything. All callers to Parse_expression_list are changed accordingly. Added corresponding regression test.
This caused "Label not defined within scope" when breakcont was active:
default{timer(){jump x;while(0)@x;}}
The problem was that breakcont opens a new scope for the case where it has to deal with a loop which is the single statement of an outer statement, e.g.
default{timer(){if(1)while(1)break;}}
would add braces to jump to the correct break point:
default{timer(){if(1){while(1)jump brk;@brk;}}
To avoid excessive complication, a new scope was always opened for the whole statement in each of the loops, regardless of whether we needed braces later on or not. That should be transparent most of the time, but then if the statement was a label declaration, the label would be in a new scope that would be invisible outside the loop.
Fix that by checking explicitly for a label to temporarily get rid of the new scope in that case, and add a test case for it.
The pragma warnings were duplicated, one during globals scan, another during actual parsing. This should fix it.
This is somewhat potentially dangerous, as some directives (pragmas, notably) could in future affect the global scan phase by changing the language.
Commit b73805e introduced lazy lists in assignments only. Commit 890e960 generalized it, allowing any identifier to be followed by brackets, removing the need for assignment-specific treatment. However, we forgot to remove the assignment-specific code parsing, so do it now.
Well, within reasonable limits.
For break, it's explained in the code. If the block is empty of actual code (code that generates output), then the break position is eliminated.
For default, if the default label is at the top of the block, the jump and the label are both eliminated. This check doesn't include verifying that there are other non-code-generating statements in between, so there's room for improvement (added corresponding TODO item).
This way, we're on par with FS, in case someone (ab)used the FS brokeness when the 'default' label was absent. All one has to do now is add the default label at the top, and the generated code will be the same as it was in FS when abusing that bug, with no extra burden.
Example: In FS, the code at the top acted as default and there was no jump for it:
default { timer() {
switch(1)
{
0;
case 1:
1;
}
}}
This generated roughly:
default { timer() {
{
if (1 == 1) jump CASE_1;
0;
@CASE_1;
1;
}
}}
In this system, it would trigger an error by default. To obtain the FS behaviour, the scripter can do instead:
default { timer() {
switch(1)
{
default:
0;
case 1:
1;
}
}}
Thanks to this optimization, the code will be the same as in FS. Without it, an extra default label and corresponding jump would be present.
Changed my mind. This looks saner. Now, if the 'default:' label is missing, an error will be thrown by default. It has to be explicitly disabled if normal C-like behaviour is desired (namely to jump to the 'break' label if no condition is met).
In the absence of a 'default' label within a switch, FS falls through to the first CASE label (or to the code before the first CASE if there's any).
This commit adds an option, active by default for FS compatibility, that mimics this broken behaviour, as well as a warning in case the 'default' label is absent, which also triggers another warning when the option is active.
For example, this script reported the type mismatch in default:
key k=0;
default{timer(){}}
Now it's reported at the semicolon, which is as early as it can be identified and much more meaningful.
The funcoverride option allows defining multiple functions with the same name, each overriding the former. That's for compatibility with Firestorm, whose optimizer does that.
While on it, fix a bug where defining a function whose name matches a library function was not reporting an error, and rename self.functions to self.funclibrary for clarity. It also brings consistency with other parts of the code and with the code documentation.