Commit graph

163 commits

Author SHA1 Message Date
Sei Lisa
81906475ff Update copyright year 2021-01-05 23:27:21 +01:00
Sei Lisa
f8cf78dfac Progress towards dual Python 2 & 3 2020-11-09 02:28:57 +01:00
Sei Lisa
0affbf13dd Fix "" + list_expr producing list_expr
The type checks were not strong enough. While on it, strengthen other similar checks as well.

Reported by @Tonaie. Fixes #13.
2020-04-23 13:47:57 +02:00
Sei Lisa
234b9b23a3 Bump copyright year 2020-04-23 00:38:20 +02:00
Sei Lisa
980b588986 Add FIXME and TODO 2019-10-11 19:38:05 +02:00
Sei Lisa
c02998518a Rename FoldCond -> FoldAsBool which better expresses the idea 2019-07-25 17:44:45 +02:00
Sei Lisa
1ab9dd69b4 Fix optimization of fn > -1 and fn < 0 when fn's minimum is -1 in conditions
E.g. llSubStringIndex(...) > -1 was converted into !~llSubStringIndex(...) which is incorrect. We even had a test case for it... with a wrong expected response file.

Bug report and test case by Sinha Hynes (thanks!)
2019-07-16 01:58:19 +02:00
Sei Lisa
1bdaff32ab When optimizing nonconst * const, cast the result to the type of the operator.
Per bug report by @Welcius.

Fixes #8.
2019-07-02 15:40:20 +02:00
Sei Lisa
fe2dd9a721 First baby steps towards dual Python2+3 compatibility 2019-01-16 00:15:06 +01:00
Sei Lisa
06f8370886 Add a reference to the author name's origin
Add TODO on vectors and rotations, too.
2019-01-10 21:56:26 +01:00
Sei Lisa
1b3c8a4d89 Check min and max to determine truth value of condition
Also added some min/max values for a few functions.

This allows optimizing things like:

  ! llGetNumberOfPrims()  ->  0
2019-01-07 00:15:38 +01:00
Sei Lisa
7fbde0269c Fix indentation typo (cosmetic) 2019-01-04 19:35:36 +01:00
Sei Lisa
42750c56d7 Fix some comments 2019-01-02 00:38:44 +01:00
Sei Lisa
f243f3a3c1 New copyright year 2019-01-01 22:54:34 +01:00
Sei Lisa
76f483fc11 Add scope field to {} nodes
Since we need to add variables, we need to know which scope to add them to. Add this information to the {} node, which is what creates a new scope.

An alternative would be to scan for any variable or label declaration within the braces and use that or create a new one if none, which is more expensive and may waste symbol tables.
2018-12-29 21:10:14 +01:00
Sei Lisa
6f32b4710a Remove handling of corner cases that we decided to not support
There was some remaining code trying to deal with labels in the single statement part of IFs and FORs.
2018-12-26 19:25:11 +01:00
Sei Lisa
b329b2b28e Avoid a possibly unnecessary expansion
When transforming if(!non-bool | !bool) to if(!(non-bool & -bool)), don't negate the bool if non-bool is actually an AND-bool.
2018-12-24 17:22:54 +01:00
Sei Lisa
c9f73bd429 Extend an optimization to cover some (admittedly rare) cases.
The value of bool|const is const when bit 0 of const is 1. This is a generalization of the case bool|1 = 1.
2018-12-24 17:22:54 +01:00
Sei Lisa
141301d7ff Reformatting 2018-12-24 17:22:54 +01:00
Sei Lisa
e123866e6e Apply DeMorgan in some cases that were not caught.
Example: the optimization of if(i < 2 || i) was suboptimal, because FoldTree was done before FoldCond. In a first stage, the || was removed: if(!!(i < 2 | i)); when folded, the inner ! was optimized first and the result was if(!(1 < i & (!i))), which FoldCond wasn't smart enough to handle.

The new optimization takes over from there, and converts if(!(!a & b)) with b boolean, in any order, to if(a | !b). When applied to the above expression, it gets folded to if(i < 2 | i) as expected, which is optimal.

Also, new function: IsAndBool (see docstring), used on b in the above example.
2018-12-24 17:18:48 +01:00
Sei Lisa
0a6155bb13 Apply an associativity rule to strings
It's not general associativity, it just folds CONST + (CONST + expr). Some day we'll implement sum flattening, to make this possible.
2018-12-22 10:27:37 +01:00
Sei Lisa
b9fe0b6c85 Add TODO item about side-effect analysis of UDFs 2018-11-19 21:15:52 +01:00
Sei Lisa
18d19b4653 Fix bug where 1 + (2 + function) was folded as 4 + function
After folding, our cached values changed under us. Re-cache them.
2018-11-19 20:43:58 +01:00
Sei Lisa
960cbfaa09 Fix "Value of unknown type" on bit shift optimization
When converting x << constant to a product, we neglected to truncate the converted constant to S32.

Per bug report by Tonaie Resident.
2018-07-25 15:59:04 +02:00
Sei Lisa
6502a1a0b6 Remove TODO because there was nothing to do after checking. 2018-06-08 00:14:16 +02:00
Sei Lisa
e88e39127b Save a comparison (fixes a FIXME) 2018-06-08 00:07:36 +02:00
Sei Lisa
0808938894 Optimizations based on addition algebra for floats and integers
Float addition is commutative. Swap the constant to the left side if it's not there. This is a "cheap" version of a bigger change that is planned, to minimize stack usage as much as possible on savepoints.

Float addition is not associative, therefore we don't optimize e.g. 1 + (2 + float).

Integer addition is commutative and associative. If there's a constant, we swap it to the left side. If there's a chained summation of the form const + (const + expression), we apply associativity to turn it into (const + const) + expression and reoptimize.

This doesn't cover all possible cases. Expressions of the form (const + expr) + (const + expr) are not optimized. We need to flatten sums if we want to do the right thing here, but that's not yet implemented.

Get rid of some older code and TODOs that are no longer needed.
2018-06-07 23:57:21 +02:00
Sei Lisa
ac254efb77 Minor details
Assert that it can't be key where we say it can't, and that it is integer where we say it must.

Adjust some comments.
2018-06-07 02:14:15 +02:00
Sei Lisa
a33dc8540f Implement ~-~-~-expr -> expr + -3
Gets rid of a TODO
2018-06-07 02:14:15 +02:00
Sei Lisa
89c627b0c0 Remove jumps that just go to the next statement
This might help a bit with switch().
2018-05-17 09:46:06 +02:00
Sei Lisa
285c7172fd Get rid of DoesSomething() in favour of SEF
The missing bit was to mark labels are SEF when they are not referenced. Label references are now counted at parse time, enabling us to do this.

Also, make FoldStmt clearer when the node is an expression.
2018-05-17 09:08:48 +02:00
Sei Lisa
a93ea0ca19 Delete statements in a {} block when they are SEF
This is probably equivalent to what we did, as FoldStmt changes SEF statements to ';' statements, but it's simpler.
2018-05-17 09:08:35 +02:00
Sei Lisa
caf50c4e14 Change while (cond) /*empty*/; into do /*empty*/; while (cond); 2018-05-17 06:38:33 +02:00
Sei Lisa
8f93386108 Get rid of label checks in some situations no longer supported 2018-05-17 06:38:12 +02:00
Sei Lisa
6ef4c03994 Remove support for labels as immediate children of IF/ELSE/WHILE/FOR/DO.
This extremely uncommon coding pattern was becoming a hell to support. It has caused many bugs in past that need them being treated as special cases.

Getting rid of the possibility entirely seems like the best approach.

It's still supported if the code is not to be optimized (e.g. with --pretty).
2018-04-01 20:05:35 +02:00
Sei Lisa
075d3aba0c Change the AST node type from dict to object
That was long overdue. Obviously, this is a large commit.

The new nr (node record) class has built-in dump capabilities, rather than using print_node().

SEF always exists now, and is a boolean, rather than using the existence of SEF as the flag. This was changed for sanity. However, other flags like 'X' are still possibly absent, and in some cases the absence itself has meaning (in the case of 'X', its absence means that the node has not yet been analyzed).

Similarly, an event is distinguished from a UDF by checking for the existence of the 'scope' attribute. This trick works because events are not in the symbol table therefore they have no scope. But this should probably be changed in future to something more rational and faster.

A few minor bugfixes were applied while going through the code.

- Some tabs used as Unicode were written as byte strings. Add the u'\t' prefix.
- After simplifying a%1 -> a&0, fold again the node and return. It's not clear why it didn't return, and whether it depended on subsequent passes (e.g. after DCR) for possibly optimizing out the result. Now we're sure.
- A few places lacked a SEF declaration.
- Formatting changes to split lines that spilled the margin.
- Some comment changes.
- Expanded lazy_list_set definition while adapting it to object format. The plan was to re-compress it after done, but decided to leave it in expanded form.
- Added a few TODOs & FIXMEs, resisting the temptation to fix them in the same commit:
  - TODO: ~-~-~-expr  ->  expr + -3.
  - FIXME: Now that we have CompareTrees, we can easily check if expr + -expr cancels out and remove a TODO. Low-hanging fruit.
  - TODO: Check what we can do when comparing non-SEF and non-CONST values in '>' (current code relies on converting '>' to '<' for applying more optimizations, but that may miss some opportunities).
  - FIXME: Could remove one comparison in nt == '&&' or nt == '||'. Low-hanging fruit.
2018-04-01 02:14:00 +02:00
Sei Lisa
877d5fc10c Optimize -a == -b, !(a - b), !(a + b)
-a == -b  ->  a == b

If both a and b either are constants or have a minus sign, negate both.

!(a - b) can be optimized to a == b.

!(a + b) can be optimized to -a == b, relying on the first optimization to remove redundant minus signs.
2018-03-23 19:02:42 +01:00
Sei Lisa
d0d6d6744b Bump copyright year
The help text in main.py was still at 2015. Oops.
2018-03-23 16:36:45 +01:00
Sei Lisa
a1f1f13739 Better optimization of (non-)equality comparisons.
int != int was not properly optimized, because the != was transformed into the equivalent !(int == int) at an earlier stage. Fixed.

!(a ^ b) can be optimized to a == b, so do it.
2018-03-23 16:04:18 +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
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
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
70f39d7f5e Remove "magic names" where possible, using new data from fndata.txt 2017-10-27 23:39:26 +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