Add duplabels option (not yet implemented).

When disabled, it now disallows duplicate labels. The plan is that when enabled, it will auto-rename labels so that there are no repetitions within a function.

Add coverage tests too, and also a coverage test that was missed after the latest changes.
This commit is contained in:
Sei Lisa 2014-08-01 00:33:20 +02:00
parent 523857ed23
commit e29f16d3eb
3 changed files with 35 additions and 9 deletions

View file

@ -1,3 +1,4 @@
from lslcommon import Key, Vector, Quaternion from lslcommon import Key, Vector, Quaternion
import lslfuncs import lslfuncs
import sys, re import sys, re
@ -89,6 +90,11 @@ class EParseDeclarationScope(EParse):
super(EParseDeclarationScope, self).__init__(parser, super(EParseDeclarationScope, self).__init__(parser,
u"Declaration requires a new scope -- use { and }") u"Declaration requires a new scope -- use { and }")
class EParseDuplicateLabel(EParse):
def __init__(self, parser):
super(EParseDuplicateLabel, self).__init__(parser,
u"Duplicate local label name. That won't allow the Mono script to be saved, and will not work as expected in LSO.")
class EInternal(Exception): class EInternal(Exception):
"""This exception is a construct to allow a different function to cause an """This exception is a construct to allow a different function to cause an
immediate return of EOF from parser.GetToken(). Reused elsewhere for immediate return of EOF from parser.GetToken(). Reused elsewhere for
@ -1150,6 +1156,10 @@ class parser(object):
name = self.tok[1] name = self.tok[1]
if name in self.symtab[self.scopeindex]: if name in self.symtab[self.scopeindex]:
raise EParseAlreadyDefined(self) raise EParseAlreadyDefined(self)
if not self.duplabels and name in self.locallabels:
raise EParseDuplicateLabel(self)
# All labels go to a common pool local to the current function.
self.locallabels.add(name)
self.AddSymbol('l', self.scopeindex, name) self.AddSymbol('l', self.scopeindex, name)
self.NextToken() self.NextToken()
self.expect(';') self.expect(';')
@ -1441,7 +1451,9 @@ class parser(object):
raise EParseSyntax(self) raise EParseSyntax(self)
self.expect(')') self.expect(')')
self.NextToken() self.NextToken()
self.locallabels = set()
body = self.Parse_code_block(None) body = self.Parse_code_block(None)
del self.locallabels
ret.append({'nt':'FNDEF', 't':None, 'name':name, ret.append({'nt':'FNDEF', 't':None, 'name':name,
'pscope':self.scopeindex, 'ptypes':params[0], 'pnames':params[1], 'pscope':self.scopeindex, 'ptypes':params[0], 'pnames':params[1],
'ch':[body]}) 'ch':[body]})
@ -1519,7 +1531,9 @@ class parser(object):
params = self.Parse_optional_param_list() params = self.Parse_optional_param_list()
self.expect(')') self.expect(')')
self.NextToken() self.NextToken()
self.locallabels = set()
body = self.Parse_code_block(typ) body = self.Parse_code_block(typ)
del self.locallabels
paramscope = self.scopeindex paramscope = self.scopeindex
self.AddSymbol('f', 0, name, Loc=len(self.tree), Type=typ, self.AddSymbol('f', 0, name, Loc=len(self.tree), Type=typ,
ParamTypes=params[0], ParamNames=params[1]) ParamTypes=params[0], ParamNames=params[1])
@ -1748,6 +1762,9 @@ class parser(object):
# TODO: Enable brackets for list elements e.g. (float)mylist[3], or mylist[5]=4 # TODO: Enable brackets for list elements e.g. (float)mylist[3], or mylist[5]=4
#self.lazylists = 'lazylists' in options #self.lazylists = 'lazylists' in options
# Enable use of local labels with duplicate names
self.duplabels = 'duplabels' in options
# Symbol table: # Symbol table:
# This is a list of all local and global symbol tables. # This is a list of all local and global symbol tables.
# The first element (0) is the global scope. Each symbol table is a # The first element (0) is the global scope. Each symbol table is a

13
main.py
View file

@ -14,14 +14,14 @@ Usage: %s [-O [+|-]option[,[+|-]option[,...]]] filename
That's an upper case o, not the number zero. That's an upper case o, not the number zero.
If filename is a dash (-) then standard input is used. If filename is a dash (-) then standard input is used.
Options (+ means default): Options (+ means active by default, - means inactive by default):
extendedglobalexpr + Enables arbitrary expressions in globals (as opposed to extendedglobalexpr + Enables arbitrary expressions in globals (as opposed to
dull simple expressions allowed by regular LSL). Needs dull simple expressions allowed by regular LSL). Needs
the optimizer to run for the result to be compilable. the optimizer to run for the result to be compilable.
extendedtypecast + Allows extended typecast syntax e.g. (string)(integer)a extendedtypecast + Allows extended typecast syntax e.g. (string)(integer)a
is valid with this option. is valid with this option.
extendedassignment + Enables &=, |=, ^=, <<=, >>= assignment operators. extendedassignment + Enables &=, |=, ^=, <<=, >>= assignment operators.
explicitcast Add explicit casts where they are implicit. This option explicitcast - Add explicit casts where they are implicit. This option
is useless with 'optimize' and 'optsigns', and is of is useless with 'optimize' and 'optsigns', and is of
basically no use in general. basically no use in general.
allowkeyconcat + Allow string + key and key + string (both return string) allowkeyconcat + Allow string + key and key + string (both return string)
@ -37,12 +37,19 @@ Options (+ means default):
optimize + Runs the optimizer. optimize + Runs the optimizer.
optsigns + Optimize signs in float and integer constants. optsigns + Optimize signs in float and integer constants.
optfloats + Optimize a float when it is an integral value. optfloats + Optimize a float when it is an integral value.
foldtabs Tabs can't be copy-pasted, so they aren't optimized by foldtabs - Tabs can't be copy-pasted, so they aren't optimized by
default. But with support from the viewer, they can be default. But with support from the viewer, they can be
folded too and make it to the uploaded source. This folded too and make it to the uploaded source. This
option overrides that check, enabling optimization of option overrides that check, enabling optimization of
strings with tabs. The resulting source isn't guaranteed strings with tabs. The resulting source isn't guaranteed
to be copy-paste-able to a different script, though. to be copy-paste-able to a different script, though.
duplabels - Normally, a duplicate label within a function is allowed
by the syntax by using {} blocks; however, the server
will just refuse to save the script (under Mono) or do
something completely unexpected (under LSO: all jumps
will go to the last label with that name). This flag
works around that limitation by replacing the names of
the labels in the output with unique ones.
Note that the optimizer doesn't reorder expressions to fold constants. This Note that the optimizer doesn't reorder expressions to fold constants. This
means that e.g. a + 3 + 5 is not optimized to a + 8; however a + (3 + 5) is. means that e.g. a + 3 + 5 is not optimized to a + 8; however a + (3 + 5) is.

View file

@ -1,7 +1,7 @@
from lslopt.lslparse import parser,EParseSyntax,EParseUEOF,EParseAlreadyDefined,\ from lslopt.lslparse import parser,EParseSyntax,EParseUEOF,EParseAlreadyDefined,\
EParseUndefined,EParseTypeMismatch,EParseReturnShouldBeEmpty,EParseReturnIsEmpty,\ EParseUndefined,EParseTypeMismatch,EParseReturnShouldBeEmpty,EParseReturnIsEmpty,\
EParseInvalidField,EParseFunctionMismatch,EParseDeclarationScope,\ EParseInvalidField,EParseFunctionMismatch,EParseDeclarationScope,\
fieldpos EParseDuplicateLabel,fieldpos
from lslopt.lsloutput import outscript from lslopt.lsloutput import outscript
from lslopt.lsloptimizer import optimizer from lslopt.lsloptimizer import optimizer
from lslopt import lslfuncs from lslopt import lslfuncs
@ -44,7 +44,7 @@ const string q="\t"
parser() parser()
class Test02_Compiler(UnitTestCase): class Test02_Parser(UnitTestCase):
def setUp(self): def setUp(self):
self.parser = parser() self.parser = parser()
self.outscript = outscript() self.outscript = outscript()
@ -171,13 +171,14 @@ class Test02_Compiler(UnitTestCase):
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){string i;!i;}''') self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){string i;!i;}''')
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){string i;++i;}''') self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){string i;++i;}''')
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''g(){integer k;k=g();}''') self.assertRaises(EParseTypeMismatch, self.parser.parse, '''g(){integer k;k=g();}''')
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''g(){@x;x;}state x{}''') self.assertRaises(EParseTypeMismatch, self.parser.parse, '''g(){@x;x;}default{}state x{}''')
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''g(){print(g());}state x{}''') self.assertRaises(EParseTypeMismatch, self.parser.parse, '''g(){print(g());}default{}state x{}''')
self.assertRaises(EParseUndefined, self.parser.parse, '''g(){integer k;k();}''') self.assertRaises(EParseUndefined, self.parser.parse, '''g(){integer k;k();}''')
self.assertRaises(EParseUndefined, self.parser.parse, '''g(){++x;}state x{}''') self.assertRaises(EParseUndefined, self.parser.parse, '''g(){++x;}state x{}''')
self.assertRaises(EParseUndefined, self.parser.parse, '''g(){print(x);}state x{}''') self.assertRaises(EParseUndefined, self.parser.parse, '''g(){print(x);}state x{}''')
self.assertRaises(EParseUEOF, self.parser.parse, '''f(){(integer)''') self.assertRaises(EParseUEOF, self.parser.parse, '''f(){(integer)''')
self.assertRaises(EParseInvalidField, self.parser.parse, '''f(){vector v;v.s;}''') self.assertRaises(EParseInvalidField, self.parser.parse, '''f(){vector v;v.s;}''')
self.assertRaises(EParseDuplicateLabel, self.parser.parse, 'f(){@x;{@x;}}')
self.assertRaises(EParseSyntax, self.parser.parse, '''f(){<1,2,3,4==5>;}''') self.assertRaises(EParseSyntax, self.parser.parse, '''f(){<1,2,3,4==5>;}''')
self.assertRaises(EParseSyntax, self.parser.parse, '''#blah;\ndefault{timer(){}}''') self.assertRaises(EParseSyntax, self.parser.parse, '''#blah;\ndefault{timer(){}}''')
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){<1,2,3,4>"">;}''') self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){<1,2,3,4>"">;}''')
@ -209,11 +210,11 @@ class Test02_Compiler(UnitTestCase):
i |= i; i |= i;
"a" "b" "c"; "a" "b" "c";
"a"+(key)"b"; (key)"a" + "b"; "a"+(key)"b"; (key)"a" + "b";
i>>=i; i>>=i; {@x;{@x;jump x;}jump x;}
}}''', }}''',
['explicitcast','extendedtypecast','extendedassignment', ['explicitcast','extendedtypecast','extendedassignment',
'extendedglobalexpr', 'allowmultistrings', 'allowkeyconcat', 'extendedglobalexpr', 'allowmultistrings', 'allowkeyconcat',
'skippreproc'] 'skippreproc', 'duplabels']
)) ))
print self.parser.scopeindex print self.parser.scopeindex
#self.assertRaises(EParseUnexpected, self.parser.PopScope) #self.assertRaises(EParseUnexpected, self.parser.PopScope)
@ -241,6 +242,7 @@ class Test03_Optimizer(UnitTestCase):
list L1 = L; list L1 = L;
list L2 = [1,2,3,4,5,6.0]; list L2 = [1,2,3,4,5,6.0];
list L3 = []; list L3 = [];
list L4 = [1,2,3,4,5,6.0,""]+[];
integer RemovesInt = 0; integer RemovesInt = 0;
vector AddsVector; vector AddsVector;
vector v=<1,2,f>; vector v=<1,2,f>;