Commit graph

143 commits

Author SHA1 Message Date
Sei Lisa
1afe1643c0 Allow lists to contain void elements if not optimizing and not lazy
LSO allows this. The compiler does too, but it chokes in RAIL.

This affected a test, which has been adjusted too.

Untyped lazy list elements can no longer be used in isolation in expression lists (including FOR initializator and iterator).

Also rename the terribly named 'self.forbidlabels' to 'self.optenabled' which is more descriptive.
2018-04-09 18:49:47 +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
4ebd84f0ed Raise EParseSyntax on wrong minus sign (not followed by int or float)
While not strictly a bug because it would be caught later in the function (it passes the tests either way), it made me nervous to leave a dangling NextToken().
2018-04-01 02:14:53 +02:00
Sei Lisa
dc30d461e2 Format some docstrings better; make one more precise about the grammar 2018-04-01 02:14:53 +02:00
Sei Lisa
8d0b995f07 Accept library function names as vars in simple_expr in globals
This may cause more trouble than it's worth, but it's how LSL behaves and one of our objectives is to document the darker corners of LSL. Mono chokes at the RAIL postprocessing stage, not in compilation proper. LSO chokes at runtime for string, key and list, and works fine for the other types.
2018-04-01 02:14:53 +02:00
Sei Lisa
8289c14c81 Fix bug where types of expressions in vectors/rotations were not checked
E.g. this was a valid vector literal: <"",0,0>

Parse_simple_expr didn't have that problem, though.
2018-04-01 02:14:53 +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
d890f0b5fa Comment-at-eol cleanup
Ensure every comment has a double space after the code.
2018-03-27 13:29:04 +02: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
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
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
d5f5ab8b88 Add side-effect-free information for events. 2017-10-25 17:46:50 +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
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
41d2c68cf8 Add some more functions with predictable results.
Also minor reformatting forgotten in a previous commit.
2017-10-12 12:43:54 +02:00
Sei Lisa
1071941301 Implement accurate error reporting through #line directives.
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.
2017-10-11 05:04:13 +02:00
Sei Lisa
4ba0518353 Report EParseReturnIsEmpty at return, EParseReturnShouldBeEmpty at expression.
Also change idpos to savepos, for consistency, and add note on why EParseReturnShouldBeEmpty is named like that.
2017-10-09 11:35:59 +02:00
Sei Lisa
4633c87a7c Report EParseUndefined at the identifier causing it.
It was being reported at the next token in some cases, due to needing to resolve the scope.
2017-10-05 18:51:53 +02:00
Sei Lisa
c544b51e37 Rewrite ReportError() and change EParse to report columns in chars.
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.
2017-10-05 18:50:45 +02:00
Sei Lisa
6738615360 Separate library function optimization into a different file.
No other functional changes. This required quite some reorganization affecting many files. As a side effect, PythonType2LSL and LSLType2Python aren't duplicate anymore.
2017-08-25 20:35:24 +02:00
Sei Lisa
2e09a3a986 Make some simplifications and keep PyFlakes happy. 2017-08-23 11:55:37 +02:00
Sei Lisa
fe574bb462 Bump copyright year.
'bout time.
2017-08-09 19:45:46 +02:00
Sei Lisa
e0fa1678a7 Optimize list addition when one list is known to have one element.
list + [element]  ->  list + element
list + (list)element  ->  list + element
[element] + list  ->  element + list
(list)element + list  ->  element + list
2017-08-09 16:41:36 +02:00
Sei Lisa
f1b05dd2ff Give more meaningful errors when break/continue parameter is wrong.
Adds a new EParseInvalidBrkContArg exception. Previously it raised EParseInvalidBreak or EParseInvalidCont, whose text was misleading for this type of error.
2017-04-29 13:47:45 +02:00
Sei Lisa
84e4543300 Allow empty list as constant in builtins.txt, for testing purposes. 2017-04-29 03:32:16 +02:00
Sei Lisa
0af2349ef9 Add command-line options to change default builtins and SEF table files. 2017-04-28 23:43:15 +02:00
Sei Lisa
2d78239d23 print returns the same type as the argument. 2017-04-28 23:04:42 +02:00
Sei Lisa
c71b0eea2f Fix error type on non-var global identifier in expression.
The error message should be EParseUndefined, not EParseTypeMismatch.
2017-01-30 06:07:52 +01:00
Sei Lisa
ab8f8a28a9 'default' is also a switch keyword.
This is academic, because 'default' is also an LSL keyword, which is why it wasn't included, but just for correctness' sake, we add it.
2017-01-07 20:24:28 +01:00
Sei Lisa
c7e8c04349 Tentatively add support for an LSO quirk with global lists.
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.
2017-01-04 05:10:12 +01:00
Sei Lisa
991e811f2d Fix Python gotcha with module globals, and missing 'cond' in lslextrafuncs.
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.
2016-12-21 00:22:49 +01:00
Sei Lisa
7c2c09188d Change how LSLCalc is handled.
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).
2016-12-20 21:25:33 +01:00
Sei Lisa
39261cbfc8 Make Unicode some strings that should be, and improve error reporting in builtins.txt
Also work towards the convention that strings that represent text (possibly translatable) are in double quotes, not single.
2016-12-20 20:13:53 +01:00
Sei Lisa
dff7b0e9ac Another follow-up to 2f4f403 (lslcalc support)
Forgot to check for EOF.
2016-12-15 04:12:02 +01:00
Sei Lisa
2f4f403535 Add lslcalc support.
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.
2016-12-15 02:48:39 +01:00
Sei Lisa
52c38b0980 Fix embedded NUL in literal strings.
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.
2016-11-18 16:58:48 +01:00
Sei Lisa
37483a72cb Fix void expressions in FOR loops.
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.
2016-07-10 01:29:11 +02:00
Sei Lisa
e60457f00e Fix rare bug with breakcont and label scope.
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.
2016-07-09 23:41:27 +02:00
Sei Lisa
9d0eb307e1 Explain the purpose of PushScope() in loops when breakcont is active. 2016-07-09 22:50:18 +02:00
Sei Lisa
30a07db4fb Mark a couple bugs found to not lose track, and add a regression test. 2016-06-28 03:47:48 +02:00
Sei Lisa
0c3ad9b938 Change skippreproc -> processpre, add #pragma option processing.
Usage:

Options are case insensitive.
2016-06-28 03:20:21 +02:00
Sei Lisa
a13b1cb77e Expand warning text 2016-06-27 22:38:19 +02:00
Sei Lisa
5ad68906e9 Name the groups of keywords.
This is in preparation for having pragmas/defines to select compliation options on the fly within the code.
2016-06-27 20:33:47 +02:00
Sei Lisa
8488254d53 Generate labels sequentially rather than randomly. 2016-06-27 20:32:59 +02:00
Sei Lisa
79dff25769 Update copyright years of some files; add legalese to seftable.txt 2016-06-27 20:06:41 +02:00
Sei Lisa
8fc4240142 Ignore preprocessor commands while scanning for globals.
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.
2016-06-27 19:07:20 +02:00
Sei Lisa
633c31df6c The RE wasn't correct, since we want to isolate the directive.
Also check for pragma and emit a warning.
2016-06-27 18:38:40 +02:00
Sei Lisa
fb83279706 Add infrastructure to process preprocessor directives.
Also check that they appear at the beginning of the line.
2016-06-27 16:50:09 +02:00
Sei Lisa
ee091c63a6 Someone's been misusing Python's re.match... O:) 2016-06-26 22:33:10 +02:00