diff --git a/lslopt/lsloptimizer.py b/lslopt/lsloptimizer.py index 60c83c8..e0692cd 100644 --- a/lslopt/lsloptimizer.py +++ b/lslopt/lsloptimizer.py @@ -3,8 +3,9 @@ import lslfuncs from lslparse import warning from lslrenamer import renamer +from lsldeadcode import deadcode -class optimizer(renamer): +class optimizer(renamer, deadcode): # Default values per type when declaring variables DefaultValues = {'integer': 0, 'float': 0.0, 'string': u'', @@ -450,20 +451,30 @@ class optimizer(renamer): for idx in xrange(len(child)): self.FoldTree(child, idx) self.FoldStmt(child, idx) + if 'StSw' in child[idx]: + node['StSw'] = True return if nt == 'IF': self.FoldTree(child, 0) if child[0]['nt'] == 'CONST': - # We can remove one of the branches safely. + # We might be able to remove one of the branches. if lslfuncs.cond(child[0]['value']): self.FoldTree(child, 1) - parent[index] = child[1] - self.FoldStmt(child, 1) + # If it has a state switch, the if() must be preserved + # (but the else branch may be removed). + if 'StSw' in child[1]: + if len(child) > 2: + del child[2] # Delete ELSE if present + child[0]['t'] = 'integer' + child[0]['value'] = 1 + else: + self.FoldStmt(child, 1) + parent[index] = child[1] elif len(child) > 2: self.FoldTree(child, 2) - parent[index] = child[2] self.FoldStmt(child, 2) + parent[index] = child[2] else: # No ELSE branch, replace the statement with an empty one. parent[index] = {'nt':';', 't':None} @@ -568,6 +579,10 @@ class optimizer(renamer): lslfuncs.ZERO_ROTATION}] return + if nt == 'STSW': + node['StSw'] = True + return + if nt in self.ignored_stmts: return diff --git a/lslopt/lslparse.py b/lslopt/lslparse.py index 79bdc69..644b3f9 100644 --- a/lslopt/lslparse.py +++ b/lslopt/lslparse.py @@ -1,3 +1,4 @@ +# TODO: Check "Not All Code Paths return a Value" from lslcommon import Key, Vector, Quaternion import lslfuncs @@ -92,6 +93,11 @@ class EParseDeclarationScope(EParse): super(EParseDeclarationScope, self).__init__(parser, u"Declaration requires a new scope -- use { and }") +class EParseCantChangeState(EParse): + def __init__(self, parser): + super(EParseCantChangeState, self).__init__(parser, + u"Global functions can't change state") + class EParseDuplicateLabel(EParse): def __init__(self, parser): super(EParseDuplicateLabel, self).__init__(parser, @@ -1127,7 +1133,7 @@ class parser(object): raise EParseFunctionMismatch(self) return ret - def Parse_statement(self, ReturnType, AllowDecl = False): + def Parse_statement(self, ReturnType, AllowDecl = False, AllowStSw = False): """Grammar parsed here: statement: ';' | single_statement | code_block @@ -1219,6 +1225,8 @@ class parser(object): self.NextToken() self.expect(';') self.NextToken() + if self.localevents is None and not AllowStSw: + raise EParseCantChangeState(self) return {'nt':'STSW', 't':None, 'name':name, 'scope':0} if tok0 == 'RETURN': self.NextToken() @@ -1245,7 +1253,12 @@ class parser(object): cur['ch'].append(self.Parse_expression()) self.expect(')') self.NextToken() - cur['ch'].append(self.Parse_statement(ReturnType)) + # INCOMPATIBILITY NOTE: This is more permissive than LSL. + # In LSL, an if...then...else does NOT allow a state change + # in either branch. Only an if...then without else does. + # BUT we're not going to check the branch after the fact, just + # to report that error. The compiler will report it. + cur['ch'].append(self.Parse_statement(ReturnType, AllowStSw = True)) if self.tok[0] == 'ELSE': self.NextToken() if self.tok[0] == 'IF': @@ -1262,10 +1275,11 @@ class parser(object): condition = self.Parse_expression() self.expect(')') self.NextToken() - return {'nt':'WHILE', 't':None, 'ch':[condition, self.Parse_statement(ReturnType)]} + return {'nt':'WHILE', 't':None, 'ch':[condition, + self.Parse_statement(ReturnType, AllowStSw = True)]} if tok0 == 'DO': self.NextToken() - stmt = self.Parse_statement(ReturnType) + stmt = self.Parse_statement(ReturnType, AllowStSw = True) self.expect('WHILE') self.NextToken() self.expect('(') @@ -1289,7 +1303,7 @@ class parser(object): iterator = self.Parse_optional_expression_list() self.expect(')') self.NextToken() - stmt = self.Parse_statement(ReturnType) + stmt = self.Parse_statement(ReturnType, AllowStSw = True) return {'nt':'FOR', 't':None, 'ch':[{'nt':'EXPRLIST','t':None, 'ch':initializer}, condition, @@ -1561,6 +1575,7 @@ class parser(object): params = self.Parse_optional_param_list() self.expect(')') self.NextToken() + self.localevents = None self.locallabels = set() body = self.Parse_code_block(typ) del self.locallabels