diff --git a/lslopt/lslcommon.py b/lslopt/lslcommon.py index 64d351d..0c46fc2 100644 --- a/lslopt/lslcommon.py +++ b/lslopt/lslcommon.py @@ -67,6 +67,10 @@ LSLType2Python = {'integer':int, 'float':float, 'string':unicode, 'key':Key, 'vector':Vector, 'rotation':Quaternion, 'list':list} +LSLTypeDefaults = {'integer':0, 'float':0.0, 'string':u'', 'key':Key(u''), + 'vector':Vector((0.,0.,0.)), 'rotation':Quaternion((0.,0.,0.,1.)), + 'list':[]} + def warning(txt): assert type(txt) == unicode sys.stderr.write(u"WARNING: " + txt + u"\n") diff --git a/lslopt/lslfoldconst.py b/lslopt/lslfoldconst.py index 7861908..3ec0812 100644 --- a/lslopt/lslfoldconst.py +++ b/lslopt/lslfoldconst.py @@ -1543,8 +1543,6 @@ class foldconst(object): or child[idx]['nt'] == '{}' and not child[idx]['ch']: del child[idx] else: - if 'StSw' in child[idx]: - node['StSw'] = True idx += 1 if issef: node['SEF'] = True @@ -1558,27 +1556,14 @@ class foldconst(object): # We might be able to remove one of the branches. if lslfuncs.cond(child[0]['value']): self.FoldTree(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]: - # TODO: Get rid of StSw craziness and make another pass - # to put them under conditionals if present (if bald - # state switches are present, it means they are the - # result of optimization so they must be wrapped in an - # IF statement). The current approach leaves unnecessary - # IFs behind. - if len(child) == 3 and child[2]['nt'] != '@': - del child[2] # Delete ELSE if present - return - else: - self.FoldStmt(child, 1) - if len(child) == 3 and child[2]['nt'] == '@': - # Corner case. The label is in the same scope as - # this statement, so it must be preserved just in - # case it's jumped to. - return - parent[index] = child[1] + self.FoldStmt(child, 1) + if len(child) == 3 and child[2]['nt'] == '@': + # Corner case. The label is in the same scope as + # this statement, so it must be preserved just in + # case it's jumped to. return + parent[index] = child[1] + return elif len(child) == 3: self.FoldTree(child, 2) self.FoldStmt(child, 2) @@ -1788,7 +1773,6 @@ class foldconst(object): if nt == 'STSW': # State switch always has side effects. - node['StSw'] = True return if nt == 'SUBIDX': diff --git a/lslopt/lsllastpass.py b/lslopt/lsllastpass.py index bacb971..7c6dd41 100644 --- a/lslopt/lsllastpass.py +++ b/lslopt/lsllastpass.py @@ -31,6 +31,33 @@ class lastpass(object): nt = node['nt'] child = node['ch'] if 'ch' in node else None + if nt == 'FNDEF': + # StChAreBad will be True if this is a user-defined function, + # where state changes are considered bad. + # BadStCh will be True if at least one state change statement + # is found while monitoring state changes. + self.subinfo['StChAreBad'] = 'scope' in node + self.BadStCh = False + + if nt == 'IF': + if len(child) == 2: + # Don't monitor the children. + self.subinfo['StChAreBad'] = False + + if nt == 'DO': + self.subinfo['StChAreBad'] = False + + if nt == 'FOR': + self.subinfo['StChAreBad'] = False + + if nt == 'WHILE': + self.subinfo['StChAreBad'] = False + + if nt == 'STSW': + if self.subinfo['StChAreBad']: + # Found one. + self.BadStCh = True + if (self.optlistadd and not self.globalmode and (nt == 'CONST' and node['t'] == 'list' or nt == 'LIST' or nt == '+' and child[0]['t'] == 'list' and @@ -61,10 +88,9 @@ class lastpass(object): 'SEF':True, 'value':elem } if listnode['nt'] == 'CONST' else elem - left = self.Cast(elemnode, 'list') if left is None else { - 'nt':'+', 't':'list', 'SEF':True, - 'ch':[left, elemnode] - } + left = (self.Cast(elemnode, 'list') if left is None + else {'nt':'+', 't':'list', 'SEF':True, + 'ch':[left, elemnode]}) del elemnode if left is not None: # it's none for empty lists parent[index] = left @@ -75,9 +101,40 @@ class lastpass(object): del listnode, left def LastPassPostOrder(self, parent, index): - pass + node = parent[index] + nt = node['nt'] + child = node['ch'] if 'ch' in node else None + + if nt == 'FNDEF': + if 'scope' in node and self.BadStCh: + # There is at least one bad state change statement in the + # function (must be the result of optimization). + # Insert dummy IF(1){...} statement covering the whole function + # (if it returs a value, insert a return statement too). + child[0] = {'nt':'{}', 't':None, 'ch':[ + {'nt':'IF', 't':None, 'ch':[ + {'nt':'CONST', 't':'integer', 'value':1}, + child[0] + ]} + ]} + child = node['ch'] + if node['t'] is not None: + # Inserting a state switch in a function that returns a + # value must count as one of the dumbest things to do. + # We do as best as we can: add a return statement with the + # default value for the type. + child[0]['ch'].append({'nt':'RETURN', 't':None, 'ch':[ + {'nt':'CONST', 't':node['t'], + 'value':lslcommon.LSLTypeDefaults[node['t']] + }] + }) + del self.BadStCh + return + def RecursiveLastPass(self, parent, index): + subinfo = self.subinfo + self.subinfo = subinfo.copy() self.LastPassPreOrder(parent, index) if 'ch' in parent[index]: @@ -88,6 +145,7 @@ class lastpass(object): idx += 1 self.LastPassPostOrder(parent, index) + self.subinfo = subinfo def LastPass(self): self.globalmode = False @@ -95,6 +153,8 @@ class lastpass(object): tree = self.tree # Last optimizations pass + # self.subinfo is subtree-local info. + self.subinfo = {} for idx in xrange(len(tree)): if tree[idx]['nt'] == 'DECL': self.globalmode = True diff --git a/testparser.py b/testparser.py index 3fce13a..1901768 100644 --- a/testparser.py +++ b/testparser.py @@ -272,7 +272,7 @@ class Test03_Optimizer(UnitTestCase): vector vvvv2=vvvv; float ffff3 = v.z; integer fn(){ - if (1) state default; else return 2; + if (1) state default; return fn();} default{touch(integer n){