mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 15:48:21 +00:00
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.
This commit is contained in:
parent
d890f0b5fa
commit
075d3aba0c
9 changed files with 1294 additions and 1201 deletions
|
@ -19,6 +19,52 @@
|
|||
|
||||
import sys
|
||||
|
||||
_exclusions = frozenset(('nt','t','name','value','ch', 'X','SEF'))
|
||||
|
||||
# Node Record type. Used for AST nodes.
|
||||
class nr(object):
|
||||
nt = None # node type
|
||||
t = None # LSL type
|
||||
ch = None # children
|
||||
SEF = False # Side Effect-Free flag
|
||||
def __init__(self, **kwargs):
|
||||
for k in kwargs:
|
||||
setattr(self, k, kwargs[k])
|
||||
|
||||
def copy(self):
|
||||
new = nr()
|
||||
for k, v in self.__dict__.items():
|
||||
setattr(new, k, v)
|
||||
return new
|
||||
|
||||
# Debug output
|
||||
|
||||
def __str__(self, indent = 0):
|
||||
spaces = ' ' * (4 * indent)
|
||||
s = '\n{sp}{{ nt:{nr.nt}\n{sp} ,t:{nr.t}'.format(sp=spaces, nr=self)
|
||||
if hasattr(self, 'name'):
|
||||
s += '\n{sp} ,name:{nr.name}'.format(sp=spaces, nr=self)
|
||||
if hasattr(self, 'value'):
|
||||
s += '\n{sp} ,value:{v}'.format(sp=spaces, v=repr(self.value))
|
||||
for k in sorted(self.__dict__):
|
||||
if k not in _exclusions:
|
||||
v = self.__dict__[k]
|
||||
s += '\n{sp} ,{k}:{v}'.format(sp=spaces, k=k, v=repr(v))
|
||||
if self.ch is not None:
|
||||
if self.ch:
|
||||
s += '\n{sp} ,ch:['.format(sp=spaces)
|
||||
isFirst = True
|
||||
for v in self.ch:
|
||||
if not isFirst:
|
||||
s += ','
|
||||
isFirst = False
|
||||
s += v.__str__(indent + 1)
|
||||
s += '\n{sp} ]'.format(sp=spaces)
|
||||
else:
|
||||
s += '\n{sp} ,ch:[]'.format(sp=spaces)
|
||||
s += '\n{sp}}}'.format(sp=spaces)
|
||||
return s if indent > 0 else s[1:] # remove leading \n at level 0
|
||||
|
||||
# These types just wrap the Python types to make type() work on them.
|
||||
# There are no ops defined on them or anything.
|
||||
|
||||
|
@ -76,24 +122,24 @@ def warning(txt):
|
|||
sys.stderr.write(u"WARNING: " + txt + u"\n")
|
||||
|
||||
# Debug function
|
||||
def print_node(node, indent = 0):
|
||||
nt = node['nt']
|
||||
write = sys.stdout.write
|
||||
spaces = ' ' * (indent*4+2)
|
||||
write('%s{ nt:%s\n' % (' '*(indent*4), nt))
|
||||
if 't' in node:
|
||||
write('%s,t:%s\n' % (spaces, node['t']))
|
||||
if 'name' in node:
|
||||
write('%s,name:%s\n' % (spaces, node['name']))
|
||||
if 'value' in node:
|
||||
write('%s,value:%s\n' % (spaces, repr(node['value'])))
|
||||
|
||||
for prop in node:
|
||||
if prop not in ('ch', 'nt', 't', 'name', 'value','X','SEF'):
|
||||
write('%s,%s:%s\n' % (spaces, prop, repr(node[prop])))
|
||||
if 'ch' in node:
|
||||
write(spaces + ',ch:[\n')
|
||||
for subnode in node['ch']:
|
||||
print_node(subnode, indent+1)
|
||||
write(spaces + ']\n')
|
||||
write(' '*(indent*4) + '}\n\n')
|
||||
#def print_node(node, indent = 0):
|
||||
# nt = node['nt']
|
||||
# write = sys.stdout.write
|
||||
# spaces = ' ' * (indent*4+2)
|
||||
# write('%s{ nt:%s\n' % (' '*(indent*4), nt))
|
||||
# if 't' in node:
|
||||
# write('%s,t:%s\n' % (spaces, node['t']))
|
||||
# if 'name' in node:
|
||||
# write('%s,name:%s\n' % (spaces, node['name']))
|
||||
# if 'value' in node:
|
||||
# write('%s,value:%s\n' % (spaces, repr(node['value'])))
|
||||
#
|
||||
# for prop in node:
|
||||
# if prop not in ('ch', 'nt', 't', 'name', 'value','X','SEF'):
|
||||
# write('%s,%s:%s\n' % (spaces, prop, repr(node[prop])))
|
||||
# if 'ch' in node:
|
||||
# write(spaces + ',ch:[\n')
|
||||
# for subnode in node['ch']:
|
||||
# print_node(subnode, indent+1)
|
||||
# write(spaces + ']\n')
|
||||
# write(' '*(indent*4) + '}\n\n')
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
# Dead Code Removal optimization
|
||||
|
||||
import lslfuncs
|
||||
from lslcommon import nr
|
||||
|
||||
class deadcode(object):
|
||||
|
||||
|
@ -30,25 +31,25 @@ class deadcode(object):
|
|||
# The 'X' key, when present, indicates whether a node is executed.
|
||||
# Its value means whether this instruction will proceed to the next
|
||||
# (True: it will; False: it won't).
|
||||
if 'X' in node:
|
||||
return node['X'] # branch already analyzed
|
||||
if hasattr(node, 'X'):
|
||||
return node.X # branch already analyzed
|
||||
|
||||
nt = node['nt']
|
||||
child = node['ch'] if 'ch' in node else None
|
||||
nt = node.nt
|
||||
child = node.ch
|
||||
|
||||
# Control flow statements
|
||||
|
||||
if nt == 'STSW':
|
||||
node['X'] = False # Executed but path-breaking.
|
||||
sym = self.symtab[0][node['name']]
|
||||
if 'X' not in self.tree[sym['Loc']]:
|
||||
node.X = False # Executed but path-breaking.
|
||||
sym = self.symtab[0][node.name]
|
||||
if not hasattr(self.tree[sym['Loc']], 'X'):
|
||||
self.MarkReferences(self.tree[sym['Loc']])
|
||||
|
||||
return False
|
||||
|
||||
if nt == 'JUMP':
|
||||
node['X'] = False # Executed but path-breaking.
|
||||
sym = self.symtab[node['scope']][node['name']]
|
||||
node.X = False # Executed but path-breaking.
|
||||
sym = self.symtab[node.scope][node.name]
|
||||
if 'R' in sym:
|
||||
sym['R'] += 1
|
||||
else:
|
||||
|
@ -56,87 +57,87 @@ class deadcode(object):
|
|||
return False
|
||||
|
||||
if nt == 'RETURN':
|
||||
node['X'] = False # Executed but path-breaking.
|
||||
node.X = False # Executed but path-breaking.
|
||||
if child:
|
||||
self.MarkReferences(child[0])
|
||||
return False
|
||||
|
||||
if nt == 'IF':
|
||||
# "When you get to a fork in the road, take it."
|
||||
node['X'] = None # provisional value, refined later
|
||||
node.X = None # provisional value, refined later
|
||||
self.MarkReferences(child[0])
|
||||
condnode = child[0]
|
||||
if condnode['nt'] == 'CONST':
|
||||
if lslfuncs.cond(condnode['value']):
|
||||
if condnode.nt == 'CONST':
|
||||
if lslfuncs.cond(condnode.value):
|
||||
# TRUE - 'then' branch always executed.
|
||||
node['X'] = self.MarkReferences(child[1])
|
||||
return node['X']
|
||||
node.X = self.MarkReferences(child[1])
|
||||
return node.X
|
||||
elif len(child) == 3:
|
||||
# FALSE - 'else' branch always executed.
|
||||
node['X'] = self.MarkReferences(child[2])
|
||||
return node['X']
|
||||
node.X = self.MarkReferences(child[2])
|
||||
return node.X
|
||||
# else fall through
|
||||
else:
|
||||
cont = self.MarkReferences(child[1])
|
||||
if len(child) == 3:
|
||||
if not cont:
|
||||
cont = self.MarkReferences(child[2])
|
||||
node['X'] = cont
|
||||
node.X = cont
|
||||
return cont
|
||||
self.MarkReferences(child[2])
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
return True
|
||||
|
||||
if nt == 'WHILE':
|
||||
node['X'] = None # provisional value, refined later
|
||||
node.X = None # provisional value, refined later
|
||||
self.MarkReferences(child[0])
|
||||
|
||||
if child[0]['nt'] == 'CONST':
|
||||
if lslfuncs.cond(child[0]['value']):
|
||||
if child[0].nt == 'CONST':
|
||||
if lslfuncs.cond(child[0].value):
|
||||
# Infinite loop - unless it returns, it stops
|
||||
# execution. But it is executed itself.
|
||||
self.MarkReferences(child[1])
|
||||
node['X'] = False
|
||||
return node['X']
|
||||
node.X = False
|
||||
return node.X
|
||||
# else the inside isn't executed at all, so don't mark it
|
||||
else:
|
||||
self.MarkReferences(child[1])
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
return True
|
||||
|
||||
if nt == 'DO':
|
||||
node['X'] = None # provisional value, refined later
|
||||
node.X = None # provisional value, refined later
|
||||
if not self.MarkReferences(child[0]):
|
||||
node['X'] = False
|
||||
node.X = False
|
||||
return False
|
||||
self.MarkReferences(child[1])
|
||||
# It proceeds to the next statement unless it's an infinite loop
|
||||
node['X'] = not (child[1]['nt'] == 'CONST' and lslfuncs.cond(child[1]['value']))
|
||||
return node['X']
|
||||
node.X = not (child[1].nt == 'CONST' and lslfuncs.cond(child[1].value))
|
||||
return node.X
|
||||
|
||||
if nt == 'FOR':
|
||||
node['X'] = None # provisional value, refined later
|
||||
node.X = None # provisional value, refined later
|
||||
self.MarkReferences(child[0])
|
||||
self.MarkReferences(child[1])
|
||||
if child[1]['nt'] == 'CONST':
|
||||
if lslfuncs.cond(child[1]['value']):
|
||||
if child[1].nt == 'CONST':
|
||||
if lslfuncs.cond(child[1].value):
|
||||
# Infinite loop - unless it returns, it stops
|
||||
# execution. But it is executed itself.
|
||||
node['X'] = False
|
||||
node.X = False
|
||||
self.MarkReferences(child[3])
|
||||
self.MarkReferences(child[2]) # this can't stop execution
|
||||
return node['X']
|
||||
return node.X
|
||||
# else the body and the iterator aren't executed at all, so
|
||||
# don't mark them
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
else:
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
self.MarkReferences(child[3])
|
||||
self.MarkReferences(child[2])
|
||||
# Mark the EXPRLIST as always executed, but not the subexpressions.
|
||||
# That forces the EXPRLIST (which is a syntactic requirement) to be
|
||||
# kept, while still simplifying the contents properly.
|
||||
child[2]['X'] = True
|
||||
child[2].X = True
|
||||
return True
|
||||
|
||||
if nt == '{}':
|
||||
|
@ -146,16 +147,16 @@ class deadcode(object):
|
|||
# True.
|
||||
|
||||
continues = True
|
||||
node['X'] = None # provisional
|
||||
node.X = None # provisional
|
||||
for stmt in child:
|
||||
if continues or stmt['nt'] == '@':
|
||||
if continues or stmt.nt == '@':
|
||||
continues = self.MarkReferences(stmt)
|
||||
node['X'] = continues
|
||||
node.X = continues
|
||||
return continues
|
||||
|
||||
if nt == 'FNCALL':
|
||||
node['X'] = None # provisional
|
||||
sym = self.symtab[0][node['name']]
|
||||
node.X = None # provisional
|
||||
sym = self.symtab[0][node.name]
|
||||
|
||||
fdef = self.tree[sym['Loc']] if 'Loc' in sym else None
|
||||
for idx in xrange(len(child)-1, -1, -1):
|
||||
|
@ -164,18 +165,18 @@ class deadcode(object):
|
|||
# writes to a and b.
|
||||
self.MarkReferences(child[idx])
|
||||
if fdef is not None:
|
||||
psym = self.symtab[fdef['pscope']][fdef['pnames'][idx]]
|
||||
psym = self.symtab[fdef.pscope][fdef.pnames[idx]]
|
||||
if 'W' in psym:
|
||||
psym['W'] = False
|
||||
else:
|
||||
psym['W'] = child[idx]
|
||||
|
||||
if 'Loc' in sym:
|
||||
if 'X' not in self.tree[sym['Loc']]:
|
||||
if not hasattr(self.tree[sym['Loc']], 'X'):
|
||||
self.MarkReferences(self.tree[sym['Loc']])
|
||||
node['X'] = self.tree[sym['Loc']]['X']
|
||||
node.X = self.tree[sym['Loc']].X
|
||||
else:
|
||||
node['X'] = 'stop' not in sym
|
||||
node.X = 'stop' not in sym
|
||||
# Note that JUMP analysis is incomplete. To do it correctly, we
|
||||
# should follow the jump right to its destination, in order to know
|
||||
# if that branch leads to a RETURN or completely stops the event.
|
||||
|
@ -200,40 +201,40 @@ class deadcode(object):
|
|||
# fn2() { fn(); x = 1; }
|
||||
|
||||
|
||||
return node['X']
|
||||
return node.X
|
||||
|
||||
if nt == 'DECL':
|
||||
sym = self.symtab[node['scope']][node['name']]
|
||||
sym = self.symtab[node.scope][node.name]
|
||||
if child is not None:
|
||||
sym['W'] = child[0]
|
||||
else:
|
||||
sym['W'] = {'nt':'CONST', 't':node['t'],
|
||||
'value':self.DefaultValues[node['t']]}
|
||||
sym['W'] = nr(nt='CONST', t=node.t,
|
||||
value=self.DefaultValues[node.t])
|
||||
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
if child is not None:
|
||||
if 'orig' in child[0]:
|
||||
orig = child[0]['orig']
|
||||
if hasattr(child[0], 'orig'):
|
||||
orig = child[0].orig
|
||||
self.MarkReferences(orig)
|
||||
child[0]['X'] = orig['X']
|
||||
if orig['nt'] == 'LIST':
|
||||
child[0].X = orig.X
|
||||
if orig.nt == 'LIST':
|
||||
# Add fake writes to variables used in list elements in
|
||||
# 'orig', so they don't get deleted (Issue #3)
|
||||
for subnode in orig['ch']:
|
||||
if subnode['nt'] == 'IDENT':
|
||||
for subnode in orig.ch:
|
||||
if subnode.nt == 'IDENT':
|
||||
# can only happen in globals
|
||||
assert subnode['scope'] == 0
|
||||
sym = self.symtab[0][subnode['name']]
|
||||
assert subnode.scope == 0
|
||||
sym = self.symtab[0][subnode.name]
|
||||
sym['W'] = False
|
||||
self.tree[sym['Loc']]['X'] = True
|
||||
elif subnode['nt'] in ('VECTOR', 'ROTATION'):
|
||||
for sub2node in subnode['ch']:
|
||||
if sub2node['nt'] == 'IDENT':
|
||||
self.tree[sym['Loc']].X = True
|
||||
elif subnode.nt in ('VECTOR', 'ROTATION'):
|
||||
for sub2node in subnode.ch:
|
||||
if sub2node.nt == 'IDENT':
|
||||
# can only happen in globals
|
||||
assert sub2node['scope'] == 0
|
||||
sym = self.symtab[0][sub2node['name']]
|
||||
assert sub2node.scope == 0
|
||||
sym = self.symtab[0][sub2node.name]
|
||||
sym['W'] = False
|
||||
self.tree[sym['Loc']]['X'] = True
|
||||
self.tree[sym['Loc']].X = True
|
||||
else:
|
||||
self.MarkReferences(child[0])
|
||||
return True
|
||||
|
@ -241,14 +242,14 @@ class deadcode(object):
|
|||
# ---- Starting here, all node types return through the bottom
|
||||
# (except '=').
|
||||
|
||||
node['X'] = None # provisional
|
||||
node.X = None # provisional
|
||||
if nt in self.assign_ops or nt in ('--V', '++V', 'V++', 'V--'):
|
||||
ident = node['ch'][0]
|
||||
if ident['nt'] == 'FLD':
|
||||
ident = ident['ch'][0]
|
||||
assert ident['nt'] == 'IDENT'
|
||||
sym = self.symtab[ident['scope']][ident['name']]
|
||||
if ident['scope'] == 0:
|
||||
ident = node.ch[0]
|
||||
if ident.nt == 'FLD':
|
||||
ident = ident.ch[0]
|
||||
assert ident.nt == 'IDENT'
|
||||
sym = self.symtab[ident.scope][ident.name]
|
||||
if ident.scope == 0:
|
||||
# Mark the global first.
|
||||
self.MarkReferences(self.tree[sym['Loc']])
|
||||
# In any case, this is at least the second write, so mark it as such
|
||||
|
@ -259,18 +260,18 @@ class deadcode(object):
|
|||
# Prevent the first node from being mistaken as a read, by
|
||||
# recursing only on the RHS node.
|
||||
self.MarkReferences(child[1])
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
return True
|
||||
|
||||
elif nt == 'FLD':
|
||||
# Mark this variable as referenced by a Field (recursing will mark
|
||||
# the ident as read later)
|
||||
self.symtab[child[0]['scope']][child[0]['name']]['Fld'] = True
|
||||
self.symtab[child[0].scope][child[0].name]['Fld'] = True
|
||||
|
||||
elif nt == 'IDENT':
|
||||
sym = self.symtab[node['scope']][node['name']]
|
||||
sym = self.symtab[node.scope][node.name]
|
||||
# Mark global if it's one.
|
||||
if 'W' not in sym and node['scope'] == 0:
|
||||
if 'W' not in sym and node.scope == 0:
|
||||
self.MarkReferences(self.tree[sym['Loc']])
|
||||
# Increase read counter
|
||||
if 'R' in sym:
|
||||
|
@ -278,7 +279,7 @@ class deadcode(object):
|
|||
else:
|
||||
sym['R'] = 1
|
||||
|
||||
node['X'] = True
|
||||
node.X = True
|
||||
if child is not None:
|
||||
for subnode in child:
|
||||
self.MarkReferences(subnode)
|
||||
|
@ -304,7 +305,7 @@ class deadcode(object):
|
|||
# - Floats are removed if their value has no decimals or if
|
||||
# used no more than N times (for some N).
|
||||
# - Strings, keys and integers are just removed.
|
||||
sym = self.symtab[curnode['scope']][curnode['name']]
|
||||
sym = self.symtab[curnode.scope][curnode.name]
|
||||
|
||||
if 'R' not in sym:
|
||||
return True # if not used, it can be removed
|
||||
|
@ -316,21 +317,21 @@ class deadcode(object):
|
|||
if sym['W'] is not False:
|
||||
|
||||
node = sym['W']
|
||||
nt = node['nt']
|
||||
nt = node.nt
|
||||
if nt == 'CONST':
|
||||
tcurnode = curnode['t']
|
||||
tcurnode = curnode.t
|
||||
if tcurnode in ('integer', 'string', 'key'):
|
||||
return sym
|
||||
|
||||
if tcurnode == 'float':
|
||||
if sym['R'] <= 3 or type(node['value']) == int:
|
||||
if sym['R'] <= 3 or type(node.value) == int:
|
||||
return sym
|
||||
elif tcurnode == 'vector' \
|
||||
or tcurnode == 'list' and len(node['value']) <= 3:
|
||||
or tcurnode == 'list' and len(node.value) <= 3:
|
||||
if sym['R'] <= 1:
|
||||
return sym
|
||||
elif tcurnode == 'rotation' \
|
||||
or tcurnode == 'list' and len(node['value']) <= 4:
|
||||
or tcurnode == 'list' and len(node.value) <= 4:
|
||||
if sym['R'] <= 1:
|
||||
return sym
|
||||
return False
|
||||
|
@ -346,8 +347,8 @@ class deadcode(object):
|
|||
# the name i is redefined after j is assigned. shrinknames prevents
|
||||
# that.
|
||||
# FIXME: EMERGENCY FIX: shrinknames is not enough guarantee. See nposerlv.lsl.
|
||||
#if not self.shrinknames or 'SEF' not in node:
|
||||
if True or 'SEF' not in node:
|
||||
#if not self.shrinknames or not node.SEF:
|
||||
if True or not node.SEF:
|
||||
return False
|
||||
|
||||
if nt not in ('VECTOR', 'ROTATION'):
|
||||
|
@ -371,51 +372,50 @@ class deadcode(object):
|
|||
"""Recursively checks if the children are used, deleting those that are
|
||||
not.
|
||||
"""
|
||||
if 'ch' not in curnode or (curnode['nt'] == 'DECL'
|
||||
and curnode['scope'] == 0):
|
||||
if curnode.ch is None or (curnode.nt == 'DECL'
|
||||
and curnode.scope == 0):
|
||||
return
|
||||
# NOTE: Should not depend on 'Loc', since the nodes that are the
|
||||
# destination of 'Loc' are renumbered as we delete stuff from globals.
|
||||
|
||||
index = int(curnode['nt'] in self.assign_ops) # don't recurse into a lvalue
|
||||
index = int(curnode.nt in self.assign_ops) # don't recurse into a lvalue
|
||||
|
||||
while index < len(curnode['ch']):
|
||||
node = curnode['ch'][index]
|
||||
while index < len(curnode.ch):
|
||||
node = curnode.ch[index]
|
||||
|
||||
if 'X' not in node:
|
||||
del curnode['ch'][index]
|
||||
if not hasattr(node, 'X'):
|
||||
del curnode.ch[index]
|
||||
continue
|
||||
|
||||
nt = node['nt']
|
||||
nt = node.nt
|
||||
|
||||
if nt == 'DECL':
|
||||
if self.SymbolReplacedOrDeleted(node):
|
||||
if 'ch' not in node or 'SEF' in node['ch'][0]:
|
||||
del curnode['ch'][index]
|
||||
if not node.ch or node.ch[0].SEF:
|
||||
del curnode.ch[index]
|
||||
continue
|
||||
node = curnode['ch'][index] = {'nt':'EXPR', 't':node['t'],
|
||||
'ch':[self.Cast(node['ch'][0], node['t'])]}
|
||||
node = curnode.ch[index] = nr(nt='EXPR', t=node.t,
|
||||
ch=[self.Cast(node.ch[0], node.t)])
|
||||
|
||||
elif nt == 'FLD':
|
||||
sym = self.SymbolReplacedOrDeleted(node['ch'][0])
|
||||
sym = self.SymbolReplacedOrDeleted(node.ch[0])
|
||||
if sym:
|
||||
value = sym['W']
|
||||
# Mark as executed, so it isn't optimized out.
|
||||
value['X'] = True
|
||||
fieldidx = 'xyzs'.index(node['fld'])
|
||||
if value['nt'] == 'CONST':
|
||||
value = value['value'][fieldidx]
|
||||
value = {'nt':'CONST', 'X':True, 'SEF':True,
|
||||
't':self.PythonType2LSL[type(value)], 'value':value}
|
||||
value.X = True
|
||||
fieldidx = 'xyzs'.index(node.fld)
|
||||
if value.nt == 'CONST':
|
||||
value = value.value[fieldidx]
|
||||
value = nr(nt='CONST', X=True, SEF=True,
|
||||
t=self.PythonType2LSL[type(value)], value=value)
|
||||
value = self.Cast(value, 'float')
|
||||
SEF = True
|
||||
else: # assumed VECTOR or ROTATION per SymbolReplacedOrDeleted
|
||||
SEF = 'SEF' in value
|
||||
value = self.Cast(value['ch'][fieldidx], 'float')
|
||||
SEF = value.SEF
|
||||
value = self.Cast(value.ch[fieldidx], 'float')
|
||||
# Replace it
|
||||
node = curnode['ch'][index] = value
|
||||
if SEF:
|
||||
node['SEF'] = True
|
||||
node = curnode.ch[index] = value
|
||||
node.SEF = SEF
|
||||
|
||||
elif nt == 'IDENT':
|
||||
sym = self.SymbolReplacedOrDeleted(node)
|
||||
|
@ -425,42 +425,40 @@ class deadcode(object):
|
|||
# TODO: Needs more analysis to see if it's correct or not.
|
||||
# (See constant_anomaly.lsl)
|
||||
new = sym['W'].copy()
|
||||
if 'orig' in new:
|
||||
del new['orig']
|
||||
if hasattr(new, 'orig'):
|
||||
del new.orig
|
||||
|
||||
new['X'] = True
|
||||
new.X = True
|
||||
|
||||
# this part makes no sense?
|
||||
#SEF = 'SEF' in sym['W']
|
||||
#if SEF:
|
||||
# new['SEF'] = True
|
||||
#new.SEF = sym['W'].SEF
|
||||
|
||||
if new['t'] != node['t']:
|
||||
new = self.Cast(new, node['t'])
|
||||
curnode['ch'][index] = node = new
|
||||
if new.t != node.t:
|
||||
new = self.Cast(new, node.t)
|
||||
curnode.ch[index] = node = new
|
||||
# Delete orig if present, as we've eliminated the original
|
||||
#if 'orig' in sym['W']:
|
||||
# del sym['W']['orig']
|
||||
#if hasattr(sym['W'], 'orig'):
|
||||
# del sym['W'].orig
|
||||
|
||||
elif nt in self.assign_ops:
|
||||
ident = node['ch'][0]
|
||||
if ident['nt'] == 'FLD':
|
||||
ident = ident['ch'][0]
|
||||
ident = node.ch[0]
|
||||
if ident.nt == 'FLD':
|
||||
ident = ident.ch[0]
|
||||
sym = self.SymbolReplacedOrDeleted(ident)
|
||||
if sym:
|
||||
node = curnode['ch'][index] = self.Cast(node['ch'][1], node['t'])
|
||||
node = curnode.ch[index] = self.Cast(node.ch[1], node.t)
|
||||
|
||||
elif nt in ('IF', 'WHILE', 'DO', 'FOR'):
|
||||
# If the mandatory statement is to be removed, replace it
|
||||
# with a ; to prevent leaving the statement empty.
|
||||
child = node['ch']
|
||||
child = node.ch
|
||||
idx = 3 if nt == 'FOR' else 0 if nt == 'DO' else 1
|
||||
if 'X' not in child[idx]:
|
||||
child[idx] = {'nt':';', 't':None, 'X':True, 'SEF':True}
|
||||
if nt == 'DO' and 'X' not in child[1]:
|
||||
if not hasattr(child[idx], 'X'):
|
||||
child[idx] = nr(nt=';', t=None, X=True, SEF=True)
|
||||
if nt == 'DO' and not hasattr(child[1],'X'):
|
||||
# Mandatory condition but not executed - replace
|
||||
child[1] = {'nt':'CONST','X':True,'SEF':True,'t':'integer',
|
||||
'value':0}
|
||||
child[1] = nr(nt='CONST', X=True, SEF=True, t='integer',
|
||||
value=0)
|
||||
|
||||
self.CleanNode(node)
|
||||
index += 1
|
||||
|
@ -495,7 +493,7 @@ class deadcode(object):
|
|||
return
|
||||
|
||||
statedef = self.tree[self.symtab[0]['default']['Loc']]
|
||||
assert statedef['nt'] == 'STDEF' and statedef['name'] == 'default'
|
||||
assert statedef.nt == 'STDEF' and statedef.name == 'default'
|
||||
self.MarkReferences(statedef)
|
||||
|
||||
# Track removal of global lines, to reasign locations later.
|
||||
|
@ -511,9 +509,9 @@ class deadcode(object):
|
|||
node = self.tree[idx]
|
||||
|
||||
delete = False
|
||||
if 'X' not in node:
|
||||
if not hasattr(node, 'X'):
|
||||
delete = True
|
||||
elif node['nt'] == 'DECL':
|
||||
elif node.nt == 'DECL':
|
||||
delete = self.SymbolReplacedOrDeleted(node)
|
||||
|
||||
if delete:
|
||||
|
@ -521,8 +519,8 @@ class deadcode(object):
|
|||
# We can't remove it here because there may be more references
|
||||
# that we will remove in CleanNode later, that hold the
|
||||
# original value.
|
||||
if node['nt'] == 'DECL' or node['nt'] == 'STDEF':
|
||||
GlobalDeletions.append(node['name'])
|
||||
if node.nt == 'DECL' or node.nt == 'STDEF':
|
||||
GlobalDeletions.append(node.name)
|
||||
del self.tree[idx]
|
||||
del LocMap[idx]
|
||||
else:
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,20 +19,21 @@
|
|||
# This is dependent on the LSL function library.
|
||||
|
||||
import lslcommon
|
||||
from lslcommon import Key, Vector, Quaternion
|
||||
from lslcommon import Key, Vector, Quaternion, nr
|
||||
import lslfuncs
|
||||
|
||||
def OptimizeArgs(node, sym):
|
||||
"""Transform function arguments to shorter equivalents where possible."""
|
||||
assert node['nt'] == 'FNCALL'
|
||||
params = node['ch']
|
||||
name = node['name']
|
||||
assert node.nt == 'FNCALL'
|
||||
params = node.ch
|
||||
name = node.name
|
||||
|
||||
if name in ('llSensor', 'llSensorRepeat'):
|
||||
# The cutoff value is at a bit less than 3.1275 for some reason,
|
||||
# but we use 3.14159.
|
||||
if params[4]['nt'] == 'CONST' and params[4]['t'] == 'float' and params[4]['value'] > 3.14159:
|
||||
params[4]['value'] = 4.0
|
||||
if (params[4].nt == 'CONST' and params[4].t == 'float'
|
||||
and params[4].value > 3.14159):
|
||||
params[4].value = 4.0
|
||||
|
||||
types = sym['ParamTypes']
|
||||
if name != 'llMessageLinked':
|
||||
|
@ -40,10 +41,10 @@ def OptimizeArgs(node, sym):
|
|||
# llMessageLinked is the exception.
|
||||
for i in range(len(types)):
|
||||
if types[i] == 'key':
|
||||
if params[i]['nt'] == 'CONST':
|
||||
if not lslfuncs.cond(Key(params[i]['value'])):
|
||||
params[i]['value'] = u""
|
||||
params[i]['type'] = 'string'
|
||||
if params[i].nt == 'CONST':
|
||||
if not lslfuncs.cond(Key(params[i].value)):
|
||||
params[i].value = u""
|
||||
params[i].type = 'string'
|
||||
|
||||
|
||||
# Type of each entry in llGetObjectDetails. Last: 38 (OBJECT_SIT_COUNT).
|
||||
|
@ -118,57 +119,54 @@ def OptimizeFunc(self, parent, index):
|
|||
library function semantics.
|
||||
"""
|
||||
node = parent[index]
|
||||
assert node['nt'] == 'FNCALL'
|
||||
name = node['name']
|
||||
child = node['ch']
|
||||
assert node.nt == 'FNCALL'
|
||||
name = node.name
|
||||
child = node.ch
|
||||
if self.optlistlength and name == 'llGetListLength':
|
||||
# Convert llGetListLength(expr) to (expr != [])
|
||||
node = {'nt':'CONST', 't':'list', 'value':[]}
|
||||
parent[index] = node = {'nt':'!=', 't':'integer',
|
||||
'ch':[child[0], node]}
|
||||
# SEF if the list is
|
||||
node['SEF'] = 'SEF' in child[0]
|
||||
node = nr(nt='CONST', t='list', value=[], SEF=True)
|
||||
parent[index] = node = nr(nt='!=', t='integer',
|
||||
ch=[child[0], node], SEF=child[0].SEF)
|
||||
if name == 'llDumpList2String':
|
||||
if (child[1]['nt'] == 'CONST'
|
||||
and child[1]['t'] in ('string', 'key')
|
||||
and child[1]['value'] == u""
|
||||
if (child[1].nt == 'CONST'
|
||||
and child[1].t in ('string', 'key')
|
||||
and child[1].value == u""
|
||||
):
|
||||
# Convert llDumpList2String(expr, "") to (string)(expr)
|
||||
node['nt'] = 'CAST'
|
||||
node.nt = 'CAST'
|
||||
del child[1]
|
||||
del node['name']
|
||||
del node.name
|
||||
return
|
||||
|
||||
if (name in ('llList2String', 'llList2Key', 'llList2Integer',
|
||||
'llList2Float', 'llList2Vector', 'llList2Rot')
|
||||
and child[1]['nt'] == 'CONST'
|
||||
and child[1].nt == 'CONST'
|
||||
):
|
||||
# 2nd arg to llList2XXXX must be integer
|
||||
assert child[1]['t'] == 'integer'
|
||||
assert child[1].t == 'integer'
|
||||
|
||||
listarg = child[0]
|
||||
idx = child[1]['value']
|
||||
idx = child[1].value
|
||||
value = self.GetListNodeElement(listarg, idx)
|
||||
tvalue = self.TypeFromNodeOrConst(value)
|
||||
const = self.ConstFromNodeOrConst(value)
|
||||
if const is not False and 'SEF' in node:
|
||||
if const is not False and node.SEF:
|
||||
# Managed to get a constant from a list, even if the
|
||||
# list wasn't constant. Handle the type conversion.
|
||||
if (node['t'][0] + tvalue[0]) in listCompat:
|
||||
if (node.t[0] + tvalue[0]) in listCompat:
|
||||
const = lslfuncs.InternalTypecast(const,
|
||||
lslcommon.LSLType2Python[node['t']],
|
||||
lslcommon.LSLType2Python[node.t],
|
||||
InList=True, f32=True)
|
||||
else:
|
||||
const = defaultListVals[name]
|
||||
|
||||
parent[index] = {'nt':'CONST', 't':node['t'],
|
||||
'value':const, 'SEF':True}
|
||||
parent[index] = nr(nt='CONST', t=node.t, value=const, SEF=True)
|
||||
return
|
||||
|
||||
if listarg['nt'] == 'FNCALL' \
|
||||
and listarg['name'] == 'llGetObjectDetails':
|
||||
if listarg.nt == 'FNCALL' \
|
||||
and listarg.name == 'llGetObjectDetails':
|
||||
|
||||
listarg = listarg['ch'][1] # make it the list argument of llGetObjectDetails
|
||||
listarg = listarg.ch[1] # make it the list argument of llGetObjectDetails
|
||||
value = self.GetListNodeElement(listarg, idx)
|
||||
tvalue = self.TypeFromNodeOrConst(value)
|
||||
const = self.ConstFromNodeOrConst(value)
|
||||
|
@ -176,9 +174,9 @@ def OptimizeFunc(self, parent, index):
|
|||
# Some of these can be handled with a typecast to string.
|
||||
if name == 'llList2String':
|
||||
# turn the node into a cast of arg 0 to string
|
||||
node['nt'] = 'CAST'
|
||||
node.nt = 'CAST'
|
||||
del child[1]
|
||||
del node['name']
|
||||
del node.name
|
||||
return
|
||||
# The other ones that support cast to string then to
|
||||
# the final type in some cases (depending on the
|
||||
|
@ -189,12 +187,12 @@ def OptimizeFunc(self, parent, index):
|
|||
and finaltype in ('s', 'i')) # won't work for floats
|
||||
or (name == 'llList2Float'
|
||||
and finaltype in ('s', 'i')) # won't work for floats
|
||||
) and (node['t'][0] + finaltype) in listCompat:
|
||||
) and (node.t[0] + finaltype) in listCompat:
|
||||
# -> (key)((string)llGetObjectDetails...)
|
||||
# or (integer)((string)llGetObjectDetails...)
|
||||
node['nt'] = 'CAST'
|
||||
node.nt = 'CAST'
|
||||
del child[1]
|
||||
del node['name']
|
||||
del node.name
|
||||
child[0] = self.Cast(child[0], 'string')
|
||||
return
|
||||
|
||||
|
@ -202,18 +200,17 @@ def OptimizeFunc(self, parent, index):
|
|||
# and replace node with a constant if that's the case
|
||||
if (value is False
|
||||
or type(const) == int
|
||||
and (node['t'][0] + objDetailsTypes[const])
|
||||
and (node.t[0] + objDetailsTypes[const])
|
||||
not in listCompat
|
||||
) and 'SEF' in node:
|
||||
parent[index] = {'nt':'CONST', 't':node['t'],
|
||||
'value':defaultListVals[name],
|
||||
'SEF':True}
|
||||
) and node.SEF:
|
||||
parent[index] = nr(nt='CONST', t=node.t,
|
||||
value=defaultListVals[name], SEF=True)
|
||||
|
||||
elif listarg['nt'] == 'FNCALL' and listarg['name'] in (
|
||||
elif listarg.nt == 'FNCALL' and listarg.name in (
|
||||
'llGetPrimitiveParams', 'llGetLinkPrimitiveParams'):
|
||||
# We're going to work with the primitive params list.
|
||||
listarg = listarg['ch'][
|
||||
0 if listarg['name'] == 'llGetPrimitiveParams'
|
||||
listarg = listarg.ch[
|
||||
0 if listarg.name == 'llGetPrimitiveParams'
|
||||
else 1]
|
||||
length = self.GetListNodeLength(listarg)
|
||||
if length is not False:
|
||||
|
@ -249,9 +246,9 @@ def OptimizeFunc(self, parent, index):
|
|||
and idx in (0, -1)
|
||||
):
|
||||
if name == 'llList2String':
|
||||
node['nt'] = 'CAST'
|
||||
node.nt = 'CAST'
|
||||
del child[1]
|
||||
del node['name']
|
||||
del node.name
|
||||
return
|
||||
if ((name == 'llList2Key'
|
||||
or name == 'llList2Integer'
|
||||
|
@ -259,16 +256,14 @@ def OptimizeFunc(self, parent, index):
|
|||
or name == 'llList2Float'
|
||||
and returntypes in ('s', 'i')
|
||||
)
|
||||
and (node['t'][0] + returntypes)
|
||||
and (node.t[0] + returntypes)
|
||||
in listCompat
|
||||
):
|
||||
node['nt'] = 'CAST'
|
||||
node.nt = 'CAST'
|
||||
del child[1]
|
||||
del node['name']
|
||||
child[0] = {'nt':'CAST', 't':'string',
|
||||
'ch':[child[0]]}
|
||||
if 'SEF' in child[0]['ch'][0]:
|
||||
child[0]['SEF'] = True
|
||||
del node.name
|
||||
child[0] = nr(nt='CAST', t='string',
|
||||
ch=[child[0]], SEF=child[0].SEF)
|
||||
return
|
||||
|
||||
if (returntypes.find('*') == -1
|
||||
|
@ -282,13 +277,11 @@ def OptimizeFunc(self, parent, index):
|
|||
# s[-1:0] doesn't return the last char
|
||||
# so we have to compensate
|
||||
idx += len(returntypes)
|
||||
if (node['t'][0] + returntypes[idx:idx+1]) \
|
||||
not in listCompat \
|
||||
and 'SEF' in node:
|
||||
parent[index] = {'nt':'CONST',
|
||||
't':node['t'],
|
||||
'value':defaultListVals[name],
|
||||
'SEF':True}
|
||||
if ((node.t[0] + returntypes[idx:idx+1])
|
||||
not in listCompat
|
||||
and node.SEF):
|
||||
parent[index] = nr(nt='CONST', t=node.t,
|
||||
value=defaultListVals[name], SEF=True)
|
||||
return
|
||||
|
||||
del returntypes
|
||||
|
@ -302,20 +295,20 @@ def OptimizeFunc(self, parent, index):
|
|||
0))
|
||||
if type(button) == unicode and button == u'OK':
|
||||
# remove the element, as 'OK' is the default button in SL
|
||||
child[2] = {'nt':'CONST','t':'list','value':[],'SEF':True}
|
||||
child[2] = nr(nt='CONST', t='list', value=[], SEF=True)
|
||||
return
|
||||
|
||||
if (name == 'llDeleteSubList'
|
||||
or name == 'llListReplaceList' and child[1]['nt'] == 'CONST'
|
||||
and not child[1]['value']
|
||||
or name == 'llListReplaceList' and child[1].nt == 'CONST'
|
||||
and not child[1].value
|
||||
):
|
||||
# llDeleteSubList(x, 0, -1) -> [] if x is SEF
|
||||
# llListReplaceList(x, [], 0, -1) -> [] if x is SEF
|
||||
if ('SEF' in child[0]
|
||||
and child[-2]['nt'] == 'CONST' and child[-1]['nt'] == 'CONST'
|
||||
and child[-2]['value'] == 0 and child[-1]['value'] == -1
|
||||
if (child[0].SEF
|
||||
and child[-2].nt == 'CONST' and child[-1].nt == 'CONST'
|
||||
and child[-2].value == 0 and child[-1].value == -1
|
||||
):
|
||||
parent[index] = {'nt':'CONST','SEF':True,'t':'list','value':[]}
|
||||
parent[index] = nr(nt='CONST', t='list', value=[], SEF=True)
|
||||
return
|
||||
|
||||
def FuncOptSetup():
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
# Optimizations that have a negative effect on other stages.
|
||||
|
||||
import lslcommon
|
||||
from lslcommon import nr
|
||||
#from lslcommon import Vector, Quaternion
|
||||
#import lslfuncs
|
||||
#from lslfuncs import ZERO_VECTOR, ZERO_ROTATION
|
||||
|
@ -28,14 +29,14 @@ import lslcommon
|
|||
class lastpass(object):
|
||||
def LastPassPreOrder(self, parent, index):
|
||||
node = parent[index]
|
||||
nt = node['nt']
|
||||
child = node['ch'] if 'ch' in node else None
|
||||
nt = node.nt
|
||||
child = node.ch
|
||||
|
||||
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
|
||||
(child[1]['nt'] == 'CONST' and child[1]['t'] == 'list'
|
||||
or child[1]['nt'] == 'LIST')
|
||||
and (nt == 'CONST' and node.t == 'list' or nt == 'LIST'
|
||||
or nt == '+' and child[0].t == 'list' and
|
||||
(child[1].nt == 'CONST' and child[1].t == 'list'
|
||||
or child[1].nt == 'LIST')
|
||||
)
|
||||
):
|
||||
# Perform these transformations if the list is SEF:
|
||||
|
@ -53,17 +54,16 @@ class lastpass(object):
|
|||
# left = None means the first element of the list must be
|
||||
# turned into a cast within the loop.
|
||||
left = child[0] if nt == '+' else None
|
||||
if 'SEF' in listnode:
|
||||
for elem in (listnode['value'] if listnode['nt'] == 'CONST'
|
||||
else listnode['ch']):
|
||||
elemnode = {'nt':'CONST',
|
||||
't':lslcommon.PythonType2LSL[type(elem)],
|
||||
'SEF':True,
|
||||
'value':elem
|
||||
} if listnode['nt'] == 'CONST' else elem
|
||||
if listnode.SEF:
|
||||
for elem in (listnode.value if listnode.nt == 'CONST'
|
||||
else listnode.ch):
|
||||
elemnode = nr(nt='CONST',
|
||||
t=lslcommon.PythonType2LSL[type(elem)],
|
||||
value=elem, SEF=True
|
||||
) if listnode.nt == 'CONST' else elem
|
||||
left = (self.Cast(elemnode, 'list') if left is None
|
||||
else {'nt':'+', 't':'list', 'SEF':True,
|
||||
'ch':[left, elemnode]})
|
||||
else nr(nt='+', t='list', SEF=True,
|
||||
ch=[left, elemnode]))
|
||||
del elemnode
|
||||
if left is not None: # it's none for empty lists
|
||||
parent[index] = left
|
||||
|
@ -78,7 +78,7 @@ class lastpass(object):
|
|||
# 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.subinfo['StChAreBad'] = hasattr(node, 'scope')
|
||||
self.BadStCh = False
|
||||
return
|
||||
|
||||
|
@ -109,38 +109,38 @@ class lastpass(object):
|
|||
if nt == 'FNCALL':
|
||||
# lslrenamer can benefit from a list of used library functions,
|
||||
# so provide it.
|
||||
if 'Loc' not in self.symtab[0][node['name']]:
|
||||
if 'Loc' not in self.symtab[0][node.name]:
|
||||
# system library function
|
||||
self.usedlibfuncs.add(node['name'])
|
||||
self.usedlibfuncs.add(node.name)
|
||||
|
||||
def LastPassPostOrder(self, parent, index):
|
||||
node = parent[index]
|
||||
nt = node['nt']
|
||||
child = node['ch'] if 'ch' in node else None
|
||||
nt = node.nt
|
||||
child = node.ch
|
||||
|
||||
if nt == 'FNDEF':
|
||||
if 'scope' in node and self.BadStCh:
|
||||
if hasattr(node, 'scope') 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] = nr(nt='{}', t=None, ch=[
|
||||
nr(nt='IF', t=None, ch=[
|
||||
nr(nt='CONST', t='integer', value=1, SEF=True),
|
||||
child[0]
|
||||
]}
|
||||
]}
|
||||
child = node['ch']
|
||||
if node['t'] is not None:
|
||||
])
|
||||
])
|
||||
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']]
|
||||
}]
|
||||
})
|
||||
child[0].ch.append(nr(nt='RETURN', t=None, ch=[
|
||||
nr(nt='CONST', t=node.t, SEF=True,
|
||||
value=lslcommon.LSLTypeDefaults[node.t]
|
||||
)]
|
||||
))
|
||||
del self.BadStCh
|
||||
return
|
||||
|
||||
|
@ -150,8 +150,8 @@ class lastpass(object):
|
|||
self.subinfo = subinfo.copy()
|
||||
self.LastPassPreOrder(parent, index)
|
||||
|
||||
if 'ch' in parent[index]:
|
||||
child = parent[index]['ch']
|
||||
if parent[index].ch is not None:
|
||||
child = parent[index].ch
|
||||
idx = 0
|
||||
while idx < len(child):
|
||||
self.RecursiveLastPass(child, idx)
|
||||
|
@ -172,7 +172,7 @@ class lastpass(object):
|
|||
# self.subinfo is subtree-local info.
|
||||
self.subinfo = {}
|
||||
for idx in xrange(len(tree)):
|
||||
if tree[idx]['nt'] == 'DECL':
|
||||
if tree[idx].nt == 'DECL':
|
||||
self.globalmode = True
|
||||
self.RecursiveLastPass(tree, idx)
|
||||
self.globalmode = False
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import lslfuncs
|
||||
|
||||
from lslcommon import nr
|
||||
from lslfoldconst import foldconst
|
||||
from lslrenamer import renamer
|
||||
from lsldeadcode import deadcode
|
||||
|
@ -41,13 +42,13 @@ class optimizer(foldconst, renamer, deadcode, lastpass):
|
|||
"""Return a CAST node if the types are not equal, otherwise the
|
||||
value unchanged.
|
||||
"""
|
||||
if value['t'] == newtype:
|
||||
if value.t == newtype:
|
||||
return value
|
||||
ret = {'nt':'CAST', 't':newtype, 'ch':[value]}
|
||||
if 'SEF' in value:
|
||||
ret['SEF'] = True
|
||||
if 'X' in value:
|
||||
ret['X'] = value['X']
|
||||
ret = nr(nt='CAST', t=newtype, ch=[value], SEF=value.SEF)
|
||||
if value.SEF:
|
||||
ret.SEF = True
|
||||
if hasattr(value, 'X'):
|
||||
ret.X = value.X
|
||||
return ret
|
||||
|
||||
def optimize(self, treesymtab, options = ('optimize','constfold','dcr',
|
||||
|
|
|
@ -55,7 +55,7 @@ class outscript(object):
|
|||
else:
|
||||
pfx = '((key)'
|
||||
sfx = ')'
|
||||
if '\t' in value and self.warntabs:
|
||||
if u'\t' in value and self.warntabs:
|
||||
warning(u"A string contains a tab. Tabs are expanded to four"
|
||||
" spaces by the viewer when copy-pasting the code"
|
||||
" (disable this warning by disabling the 'warntabs'"
|
||||
|
@ -178,22 +178,23 @@ class outscript(object):
|
|||
def FindName(self, node, scope = None):
|
||||
if scope is None:
|
||||
# node is a node
|
||||
if 'scope' in node and 'NewName' in self.symtab[node['scope']][node['name']]:
|
||||
return self.symtab[node['scope']][node['name']]['NewName']
|
||||
if node['nt'] == 'FNCALL' and 'NewName' in self.symtab[0][node['name']]:
|
||||
return self.symtab[0][node['name']]['NewName']
|
||||
if (hasattr(node, 'scope')
|
||||
and 'NewName' in self.symtab[node.scope][node.name]):
|
||||
return self.symtab[node.scope][node.name]['NewName']
|
||||
if node.nt == 'FNCALL' and 'NewName' in self.symtab[0][node.name]:
|
||||
return self.symtab[0][node.name]['NewName']
|
||||
|
||||
return node['name']
|
||||
return node.name
|
||||
# node is a name
|
||||
if 'NewName' in self.symtab[scope][node]:
|
||||
return self.symtab[scope][node]['NewName']
|
||||
return node
|
||||
|
||||
def OutIndented(self, node):
|
||||
if node['nt'] != '{}':
|
||||
if node.nt != '{}':
|
||||
self.indentlevel += 1
|
||||
ret = self.OutCode(node)
|
||||
if node['nt'] != '{}':
|
||||
if node.nt != '{}':
|
||||
self.indentlevel -= 1
|
||||
return ret
|
||||
|
||||
|
@ -210,14 +211,13 @@ class outscript(object):
|
|||
|
||||
def OutExpr(self, expr):
|
||||
# Handles expression nodes (as opposed to statement nodes)
|
||||
nt = expr['nt']
|
||||
if 'ch' in expr:
|
||||
child = expr['ch']
|
||||
nt = expr.nt
|
||||
child = expr.ch
|
||||
|
||||
if nt in self.binary_operands:
|
||||
lnt = child[0]['nt']
|
||||
lnt = child[0].nt
|
||||
lparen = False
|
||||
rnt = child[1]['nt']
|
||||
rnt = child[1].nt
|
||||
rparen = False
|
||||
if nt in self.assignment_ops and nt in self.op_priority:
|
||||
# Assignment is right-associative, so it needs to be dealt with
|
||||
|
@ -274,16 +274,17 @@ class outscript(object):
|
|||
return self.FindName(expr)
|
||||
|
||||
if nt == 'CONST':
|
||||
if self.foldconst and expr['t'] == 'list' and len(expr['value'])==1 and not self.globalmode:
|
||||
return '(list)' + self.Value2LSL(expr['value'][0])
|
||||
return self.Value2LSL(expr['value'])
|
||||
if (self.foldconst and expr.t == 'list' and len(expr.value) == 1
|
||||
and not self.globalmode):
|
||||
return '(list)' + self.Value2LSL(expr.value[0])
|
||||
return self.Value2LSL(expr.value)
|
||||
|
||||
if nt == 'CAST' or self.foldconst and nt in ('LIST', 'CONST') and len(child)==1 and not self.globalmode:
|
||||
ret = '(' + expr['t'] + ')'
|
||||
ret = '(' + expr.t + ')'
|
||||
expr = child[0]
|
||||
if expr['nt'] in ('CONST', 'IDENT', 'V++', 'V--', 'VECTOR',
|
||||
if expr.nt in ('CONST', 'IDENT', 'V++', 'V--', 'VECTOR',
|
||||
'ROTATION', 'LIST', 'FIELD', 'PRINT', 'FNCALL'):
|
||||
if expr['nt'] != 'LIST' or len(expr['ch']) != 1:
|
||||
if expr.nt != 'LIST' or len(expr.ch) != 1:
|
||||
return ret + self.OutExpr(expr)
|
||||
return ret + '(' + self.OutExpr(expr) + ')'
|
||||
|
||||
|
@ -309,7 +310,7 @@ class outscript(object):
|
|||
+ self.OutExpr(child[1]) + ',')
|
||||
if nt == 'ROTATION':
|
||||
ret += self.OutExpr(child[2]) + ','
|
||||
lnt = child[-1]['nt']
|
||||
lnt = child[-1].nt
|
||||
if lnt in self.op_priority \
|
||||
and self.op_priority[lnt] <= self.op_priority['>']:
|
||||
ret += '(' + self.OutExpr(child[-1]) + ')'
|
||||
|
@ -325,22 +326,22 @@ class outscript(object):
|
|||
|
||||
if nt in self.unary_operands:
|
||||
ret = nt
|
||||
lnt = child[0]['nt']
|
||||
lnt = child[0].nt
|
||||
paren = False
|
||||
if nt == 'NEG':
|
||||
ret = '-'
|
||||
if (lnt == 'CONST' and child[0]['t'] == 'integer'
|
||||
and child[0]['value'] < 0
|
||||
if (lnt == 'CONST' and child[0].t == 'integer'
|
||||
and child[0].value < 0
|
||||
):
|
||||
# shortcut
|
||||
ret += str(child[0]['value'] + 4294967296)
|
||||
ret += str(child[0].value + 4294967296)
|
||||
return ret
|
||||
if lnt in self.op_priority:
|
||||
paren = self.op_priority[lnt] <= self.op_priority['-']
|
||||
elif (lnt == 'NEG' or lnt == '--V'
|
||||
or lnt == 'CONST'
|
||||
and child[0]['t'] == 'float'
|
||||
and child[0]['value'] < 0
|
||||
and child[0].t == 'float'
|
||||
and child[0].value < 0
|
||||
):
|
||||
ret += ' ' # don't output "--" as that's a different token
|
||||
else:
|
||||
|
@ -354,7 +355,7 @@ class outscript(object):
|
|||
return ret
|
||||
|
||||
if nt == 'FLD':
|
||||
return self.OutExpr(child[0]) + '.' + expr['fld']
|
||||
return self.OutExpr(child[0]) + '.' + expr.fld
|
||||
|
||||
if nt in ('V--', 'V++'):
|
||||
return self.OutExpr(child[0]) + ('++' if nt == 'V++' else '--')
|
||||
|
@ -375,11 +376,8 @@ class outscript(object):
|
|||
assert False, 'Internal error: expression type "' + nt + '" not handled' # pragma: no cover
|
||||
|
||||
def OutCode(self, node):
|
||||
nt = node['nt']
|
||||
if 'ch' in node:
|
||||
child = node['ch']
|
||||
else:
|
||||
child = None
|
||||
nt = node.nt
|
||||
child = node.ch
|
||||
|
||||
if nt == 'IF':
|
||||
ret = self.dent()
|
||||
|
@ -390,9 +388,9 @@ class outscript(object):
|
|||
if len(child) == 3:
|
||||
testnode = child[1]
|
||||
# Find last IF in an ELSE IF chain
|
||||
while testnode['nt'] == 'IF' and len(testnode['ch']) == 3:
|
||||
testnode = testnode['ch'][2]
|
||||
if testnode['nt'] == 'IF':
|
||||
while testnode.nt == 'IF' and len(testnode.ch) == 3:
|
||||
testnode = testnode.ch[2]
|
||||
if testnode.nt == 'IF':
|
||||
# hit an IF without ELSE at the end of the chain
|
||||
needs_braces = True
|
||||
if needs_braces:
|
||||
|
@ -403,12 +401,12 @@ class outscript(object):
|
|||
ret += self.OutIndented(child[1])
|
||||
if len(child) < 3:
|
||||
return ret
|
||||
if child[2]['nt'] != 'IF':
|
||||
if child[2].nt != 'IF':
|
||||
ret += self.dent() + 'else\n' + self.OutIndented(child[2])
|
||||
return ret
|
||||
ret += self.dent() + 'else '
|
||||
node = child[2]
|
||||
child = node['ch']
|
||||
child = node.ch
|
||||
if nt == 'WHILE':
|
||||
ret = self.dent() + 'while (' + self.OutExpr(child[0]) + ')\n'
|
||||
ret += self.OutIndented(child[1])
|
||||
|
@ -436,12 +434,12 @@ class outscript(object):
|
|||
return self.dent() + 'return ' + self.OutExpr(child[0]) + ';\n'
|
||||
return self.dent() + 'return;\n'
|
||||
if nt == 'DECL':
|
||||
ret = self.dent() + node['t'] + ' ' + self.FindName(node)
|
||||
ret = self.dent() + node.t + ' ' + self.FindName(node)
|
||||
if child:
|
||||
if 'orig' in child[0] and (child[0]['orig']['nt'] != 'IDENT'
|
||||
or child[0]['orig']['name']
|
||||
in self.symtab[child[0]['orig']['scope']]):
|
||||
ret += ' = ' + self.OutExpr(child[0]['orig'])
|
||||
if hasattr(child[0], 'orig') and (child[0].orig.nt != 'IDENT'
|
||||
or child[0].orig.name
|
||||
in self.symtab[child[0].orig.scope]):
|
||||
ret += ' = ' + self.OutExpr(child[0].orig)
|
||||
else:
|
||||
ret += ' = ' + self.OutExpr(child[0])
|
||||
return ret + ';\n'
|
||||
|
@ -451,26 +449,26 @@ class outscript(object):
|
|||
if nt in ('STDEF', '{}'):
|
||||
ret = ''
|
||||
if nt == 'STDEF':
|
||||
if node['name'] == 'default':
|
||||
if node.name == 'default':
|
||||
ret = self.dent() + 'default\n'
|
||||
else:
|
||||
ret = self.dent() + 'state ' + self.FindName(node) + '\n'
|
||||
|
||||
ret += self.dent() + '{\n'
|
||||
self.indentlevel += 1
|
||||
for stmt in node['ch']:
|
||||
for stmt in node.ch:
|
||||
ret += self.OutCode(stmt)
|
||||
self.indentlevel -= 1
|
||||
return ret + self.dent() + '}\n'
|
||||
|
||||
if nt == 'FNDEF':
|
||||
ret = self.dent()
|
||||
if node['t'] is not None:
|
||||
ret += node['t'] + ' '
|
||||
if node.t is not None:
|
||||
ret += node.t + ' '
|
||||
ret += self.FindName(node) + '('
|
||||
scope = node['pscope']
|
||||
scope = node.pscope
|
||||
ret += ', '.join(typ + ' ' + self.FindName(name, scope)
|
||||
for typ, name in zip(node['ptypes'], node['pnames']))
|
||||
for typ, name in zip(node.ptypes, node.pnames))
|
||||
return ret + ')\n' + self.OutCode(child[0])
|
||||
|
||||
if nt == 'EXPR':
|
||||
|
@ -502,7 +500,7 @@ class outscript(object):
|
|||
self.globalmode = False
|
||||
self.listmode = False
|
||||
for node in self.tree:
|
||||
self.globalmode = node['nt'] == 'DECL'
|
||||
self.globalmode = node.nt == 'DECL'
|
||||
ret += self.OutCode(node)
|
||||
self.globalmode = False
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -114,9 +114,9 @@ class renamer(object):
|
|||
name = entry['NewName'] = self.GetNextShortest()
|
||||
stateNames.append(name)
|
||||
# Find also the event names it uses, to add them for reuse.
|
||||
for node in self.tree[entry['Loc']]['ch']:
|
||||
assert node['nt'] == 'FNDEF'
|
||||
event_name = node['name']
|
||||
for node in self.tree[entry['Loc']].ch:
|
||||
assert node.nt == 'FNDEF'
|
||||
event_name = node.name
|
||||
# These events have their names translated.
|
||||
if event_name == 'on_rez':
|
||||
event_name = 'rez'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue