Commit graph

609 commits

Author SHA1 Message Date
Sei Lisa
ed05a2e022 Make PreparePreproc Unicode-aware.
Fixes mismatches in column number output after a multiline string, if the last line of the string contains non-ASCII Unicode characters.
2017-11-26 14:10:33 +01:00
Sei Lisa
eba4df6903 Add preproc user args before internal args, rather than after them.
This allows using e.g. --precmd=python --prearg=mypreproc.py and still benefit from automatic defines, but on the negative side (or not?), it doesn't allow user options to override mcpp/gcpp internal options; user is forced to use -p ext in that case.
2017-11-26 14:02:04 +01:00
Sei Lisa
e2de571f98 Allow --prenodef to be used anywhere in the command line. 2017-11-26 14:00:46 +01:00
Sei Lisa
26c4770bab Remove mcpp_mode.
Use preproc=='mcpp' instead, which works the same and adds less noise.
2017-11-26 13:33:18 +01:00
Sei Lisa
f492d3e291 Add --prettify option, to reformat a script (losing comments).
Since our syntax extensions transform the source at parse time, all syntax extensions are disabled. The optimizations are disabled too, as it doesn't make sense to prettify and optimize at the same time (the optimizer would remove the constants that we're trying to keep).

Addresses #4 in a more user-friendly way.
2017-11-20 20:59:45 +01:00
Sei Lisa
e42479756b Fix bug with nested if's; fix missing EXPR wrap.
097c054 introduced a bug that we hadn't caught until now.

In some occasions, it could swap nested conditions in such a way that the 'else' of the outer statement was made to belong to the inner one, like this:

    if (a)
        if (b)
            stuff;
    else
        stuff;

That is of course parsed with the 'else' belonging to if(b).

Fix implemented at output time, by detecting 'if(a) stmt; else y;' with stmt being an 'if' without 'else', and wrapping the stmt in {} like this: 'if(a){if(b) x;} else y;'. This has some similarity with parenthesis addition.

But the fix has the corner case that, since {} hides visibility of labels, when the inner 'if' has a label as direct child, it can't be swapped lest the label becomes out of scope. So these cases are detected and skipped in the constant folding module.

In the case of 'if(cond);', we transform it to 'cond;', but we forgot to wrap the cond in an EXPR node as required. Fixed too.
2017-11-13 04:04:13 +01:00
Sei Lisa
a4b3c1eadd Handle list+list more sanely. 2017-11-04 22:50:32 +01:00
Sei Lisa
0b1ad7c110 Add more results of operating an expression with itself.
Implemented a<a=0, a^a=0, a+-a=0, -a+a=0, a|a=a, a&a=a
2017-11-03 00:45:11 +01:00
Sei Lisa
4251d4c7f9 Improve CompareTrees.
Reorganize into different statements with early return.

Add constants, unary operators and binary operators. Check if operator is commutative and check with operands swapped when so.

Constant equality is somewhat sketchy at the moment: just compare the values with Python's ==.
2017-11-03 00:12:10 +01:00
Sei Lisa
1cdf9f7ff0 Collect used library functions as reusable names for the renamer.
Implements another TODO.

There was a TODO about a new counter per scope, but that makes no sense. The renamer only acts on global variables, global function and parameter names, state names, and event parameters. We're already restarting the counters at every function, which is the closest to what that TODO was about.
2017-11-02 23:19:33 +01:00
Sei Lisa
93828b9286 Move a block. No functionality changes. 2017-11-02 21:50:55 +01:00
Sei Lisa
9c66380972 Add some early returns for performance. 2017-11-02 21:49:29 +01:00
Sei Lisa
a87022b73f Get rid of StSw craziness and use lsllastpass to make another pass.
This has been a TODO item for long. Now that we have lsllastpass, it's actually easy to implement.

Adds an LSLTypeDefaults dictionary to lslcommon, just in case the state-changing function returns a value and we need to insert a return statement.

We've also added subtree-local info to lsllastpass (lost when we return to the parent after visiting a subtree).

This fixes a bug where naked switch statements could appear as a result of optimization, and cause the compilation to fail.
2017-11-02 18:20:14 +01:00
Sei Lisa
e4eaab9e84 Move the print_node() subtree dump debug function to lslcommon. 2017-11-02 16:34:35 +01:00
Sei Lisa
ef6ed30536 Fix EParseCantChangeState so that it is always properly reported.
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.
2017-11-02 13:45:01 +01:00
Sei Lisa
097c054494 Optimize 'if' statements better.
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.
2017-10-30 19:23:01 +01:00
Sei Lisa
ed49170d87 Remove empty SEF events.
If the state becomes empty, add an empty timer() event. timer() is the parameter-less event with the shortest name.
2017-10-30 18:05:35 +01:00
Sei Lisa
e13f210695 Make DoesSomething apply to labels optionally.
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.
2017-10-30 18:05:28 +01:00
Sei Lisa
f2a6243695 Reuse state names for function parameters; restart UsedNames.
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.
2017-10-28 23:39:25 +02:00
Sei Lisa
70f39d7f5e Remove "magic names" where possible, using new data from fndata.txt 2017-10-27 23:39:26 +02:00
Sei Lisa
b80b157489 Add a space between a minus sign and a negative constant.
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).
2017-10-27 21:02:00 +02:00
Sei Lisa
14b13dc4e5 Implement flags aimed at removing "magic names" in the code.
strlen for llStringLength, detect for llDetected* and touch*/collision*/sensor, touch for touch*, grab for touch().
2017-10-27 18:49:58 +02:00
Sei Lisa
3bb5102f06 Improve llStringLength detection, to catch more cases.
For example, if (llStringLength(s) > 0)  ->  if (!(s == ""))
2017-10-27 16:07:36 +02:00
Sei Lisa
4c8227bae5 Set min for llStringLength and llGetListLength to 0. 2017-10-27 11:25:18 +02:00
Sei Lisa
1dde2a1fb9 Use lslfuncs.less instead of > >= < <= to ensure proper type handling.
Float functions could be compared against integer args, or vice versa.
2017-10-27 11:20:42 +02:00
Sei Lisa
c0176ad738 Fix order change in comparisons when one side is not SEF.
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.
2017-10-27 11:13:03 +02:00
Sei Lisa
106bb81543 Refine a comment.
Powers of two can't be zero anyway.
2017-10-26 01:07:48 +02:00
Sei Lisa
4ba71993a4 Optimize min/max in comparisons; undo problematic optimization.
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).
2017-10-26 00:58:14 +02:00
Sei Lisa
22e057e2ec Move side-effect-free check for functions to a method.
This will in future allow us to check argument-dependent SEFness.
2017-10-25 19:24:12 +02:00
Sei Lisa
0bbfb08234 Add some missing but important minimum values.
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.
2017-10-25 18:36:48 +02:00
Sei Lisa
2580248c17 Minor reorganization and documentation of a section. 2017-10-25 18:04:27 +02:00
Sei Lisa
d5f5ab8b88 Add side-effect-free information for events. 2017-10-25 17:46:50 +02:00
Sei Lisa
2d823d8eae Fix Unicode in one error message. 2017-10-25 14:11:34 +02:00
Sei Lisa
adde423447 Fix SEFness of llRequestPermissions. It's never SEF. 2017-10-25 14:07:32 +02:00
Sei Lisa
7e39635b09 Transform !(x != y) into x == y.
Can happen with lists.
2017-10-21 20:38:54 +02:00
Sei Lisa
fd815f0bdb Fix compare and llListSort for LSO.
In LSO, they compare UTF-8 bytes, not Unicode codepoints.
2017-10-21 12:32:41 +02:00
Sei Lisa
a832f8d786 Remove min, max when min > max, and SEF when both SEF and delay present. 2017-10-21 11:47:29 +02:00
Sei Lisa
923309e4a1 Refine test for min and max.
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.
2017-10-21 11:31:53 +02:00
Sei Lisa
ea9711642c Add type casting comparison to CompareTrees.
It can now resolve e.g. (string)llGetPermissions() == (string)llGetPermissions() to TRUE.
2017-10-21 11:03:43 +02:00
Sei Lisa
dc117c2cbf Use CompareTrees to optimize the == operator.
Now we can.
2017-10-21 11:02:38 +02:00
Sei Lisa
e6b23a2d7a Fix CompareTrees to not compare unstable functions as equal. 2017-10-21 10:44:09 +02:00
Sei Lisa
4d92cc8838 Mark user functions as unstable.
Maybe in future we can perform further analysis to find some that are stable.
2017-10-21 10:42:46 +02:00
Sei Lisa
cdacc45bb0 Make functions unstable by default, for safety.
That ensures that if there's no data about a function, it is not optimizable.
2017-10-21 10:32:56 +02:00
Sei Lisa
ee8c2cde98 Cosmetic fixes. 2017-10-21 10:32:41 +02:00
Sei Lisa
b7700b7aab Remove debug code and seftable.txt 2017-10-21 10:29:12 +02:00
Sei Lisa
214d4a8a57 Add and read function properties table.
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.
2017-10-21 10:00:31 +02:00
Sei Lisa
3f6f8ed8ad Internal code reorganization.
- 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.
2017-10-20 18:19:48 +02:00
Sei Lisa
1a1531cb40 Use sys.getfilesystemencoding() to determine filename encoding. 2017-10-20 13:59:02 +02:00
Sei Lisa
cf3e4c21ec Comment fix.
Specify the name of the last llGetObjectDetails constant, to simplify manual checking.
2017-10-20 10:18:44 +02:00
Sei Lisa
57df2558e7 New upstream version of builtins.txt. 2017-10-20 10:00:24 +02:00