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.
CleanNode was too greedy, because children of global declarations (particularly lists) are not marked executable. Make a special case for them and don't recurse, since what matters is whether the declaration itself is executed. Its contents can't be cleaned up.
In Python, NaN*Indet in any order returns the second operand, and NaN/Indet in any order returns the first operand. LSL consistently returns NaN in all cases.
The force type functions ff(), fi(), fs()... should normally trigger ELSLTypeMismatch when the input is not in the expected range of types, rather than ELSLInvalidType, which is reserved for the case where the type is not a valid LSL type.
It can't be done always: flag1 and flag2 must be nonzero powers of two. In that case, we can transform it to:
!~(x|~(flag1|flag2)) = !~(x|constant)
The -2147483648 case has trouble with the sign hack detector and I couldn't trigger it.
Rather than assert that the types are correct, use the force type functions on the parameters:
ff, fk, fs, q2f, v2f, and the new fi, fl.
These functions have also been modified to ensure that the input type supports an implicit typecast to the target type and perform it, or emit ELSLInvalidType otherwise, rather than an assertion failure. fl in particular returns the original list if it isn't changed, or a copy if it is.
A couple bugs were found in testfuncs.py as a result, which have been fixed as well. A test has been added to ensure that the exception that caught these bugs remains in place.
The isxxxx functions are no longer necessary, so they are removed. Same goes for the painful cast handling process in foldconst, which was basically performing this task, and not necessarily well.
This approach is much more robust and should have been used since the beginning, but I didn't figure it out then.
Also simplify and fix the matching expression for #line (gcc inserts numeric flags at the end).
It still has many problems. It's O(n^2). It's calculated at every EParse, and EParse can be triggered and ignored while scanning vectors or globals. UniConvScript doesn't read #line at all, thus failing to report a meaningful input line. But at least it's a start.
ReportError() needed to account for terminal encodings that don't support the characters being printed. It was also reporting an inaccurate column number and its corresponding marker position, because the count was in bytes, not in characters, so that has been fixed.
Now EParse.__init__() calls a new function GetErrLineCol() that calculates the line and column corresponding to an error position.
The algorithm for finding the start of the line has also been changed in both ReportError() and EParse.__init__(); as a result, function fieldpos() has been removed.
The exception's lno and cno fields have been changed to be 1-based, rather than 0-based.
Thanks to @Jomik for the report. Fixes#5.
lslcleanup: Variables renamed, order changed, comments added.
Other changes: remove semicolon at end of sentence, use self.Cast instead of creating a CAST node on the fly.
That's exactly what 'cond' is for. Things like a loop with a constant zero vector condition should be recognized.
It was correct before just by chance, because FoldCond currently transforms a constant node into an integer node, but let's not rely on that.
When the index is good, on non-touch functions:
- llDetectedTouchFace returns -1.
- llDetectedTouchST and llDetectedTouchUV return TOUCH_INVALID_TEXCOORD.
We were returning 0 and ZERO_VECTOR respectively.
No other functional changes. This required quite some reorganization affecting many files. As a side effect, PythonType2LSL and LSLType2Python aren't duplicate anymore.
- Resolve for lists that aren't constants, but that are SEF, and reference a constant element, e.g. llList2String([llGetKey(), 5], 1) -> 5.
- Make it work with invalid conversions, if they are SEF.
- Simplify invalid conversions when the list argument is a llGetObjectDetails call and SEF.
- Simplify llList2String(llGetObjectDetails(id, [single_element]), 0) (or -1) to (string)llGetObjectDetails...; same with llList2Key and llList2Integer, converting it to (integer)((string)llGetObjectDetails...).
- Add TODO item to do it similarly for llGet[Link]PrimitiveParams.
Adds auxiliary functions to return a node or constant from a list in any of its forms, its length, and a constant for a node or constant.
Adds some utility members for LSL<->Python type conversion (one of them duplicated from lslparse.py).
Needs maintenance of the types returned by llGetObjectDetails.