mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
Turned everything upside down, and fixed a couple bugs.
Bugs fixed: - %= and the new assignment operators were not emitting error on invalid types. - List globals referenced in another global were duplicated entirely. - Properly recognize -option in the command line. Rest: - Complete overhaul of the internal data structure. - Got rid of the symbol table plus mini-trees, and made everything one big tree plus an auxiliary symbol table. - No more special case hacks like using tuples instead of lists... - Got rid of the EXPR hack. - Dict-based, rather than list-based. Allows adding arbitrary data to any node or symbol entry. - Added a few coverage tests for the new code. - Return values can now be chained; the functions parameter requirement is gone. Still not fully convinced, though. My guess is that a parser object should be passed between functions instead. Will do for now.
This commit is contained in:
parent
5d4abf967d
commit
fb68273eed
5 changed files with 691 additions and 734 deletions
|
@ -1,11 +1,15 @@
|
||||||
|
|
||||||
import lslfuncs
|
import lslfuncs
|
||||||
from lslparse import S, warning
|
from lslparse import warning
|
||||||
|
|
||||||
CONSTANT = S['CONSTANT']
|
|
||||||
|
|
||||||
class optimizer(object):
|
class optimizer(object):
|
||||||
|
|
||||||
|
# Default values per type when declaring variables
|
||||||
|
DefaultValues = {'integer': 0, 'float': 0.0, 'string': u'',
|
||||||
|
'key': lslfuncs.Key(u''), 'vector': lslfuncs.ZERO_VECTOR,
|
||||||
|
'rotation': lslfuncs.ZERO_ROTATION, 'list': []
|
||||||
|
}
|
||||||
|
|
||||||
# explicitly exclude assignments
|
# explicitly exclude assignments
|
||||||
binary_ops = frozenset(('+','-','*','/','%','<<','>>','<','<=','>','>=',
|
binary_ops = frozenset(('+','-','*','/','%','<<','>>','<','<=','>','>=',
|
||||||
'==','!=','|','^','&','||','&&'))
|
'==','!=','|','^','&','||','&&'))
|
||||||
|
@ -17,91 +21,93 @@ class optimizer(object):
|
||||||
|
|
||||||
def FoldAndRemoveEmptyStmts(self, lst):
|
def FoldAndRemoveEmptyStmts(self, lst):
|
||||||
"""Utility function for elimination of useless expressions in FOR"""
|
"""Utility function for elimination of useless expressions in FOR"""
|
||||||
x = 0
|
idx = 0
|
||||||
while x < len(lst):
|
while idx < len(lst):
|
||||||
self.FoldTree(lst[x])
|
self.FoldTree(lst, idx)
|
||||||
self.FoldStmt(lst[x])
|
self.FoldStmt(lst, idx)
|
||||||
# If eliminated, it must be totally removed. A ';' won't do.
|
# If eliminated, it must be totally removed. A ';' won't do.
|
||||||
if lst[x][0] == ';':
|
if lst[idx]['node'] == ';':
|
||||||
del lst[x]
|
del lst[idx]
|
||||||
else:
|
else:
|
||||||
x += 1
|
idx += 1
|
||||||
|
|
||||||
def FoldStmt(self, code):
|
def FoldStmt(self, parent, index):
|
||||||
"""If the statement is a constant or an identifier, remove it as it does
|
"""If the statement is a constant or an identifier, remove it as it does
|
||||||
nothing.
|
nothing.
|
||||||
"""
|
"""
|
||||||
# Ideally this should consider side effect analysis of the whole thing.
|
# Ideally this should consider side effect analysis of the whole thing.
|
||||||
if code[0] in (CONSTANT, 'IDENT', 'FIELD'):
|
if parent[index]['node'] in ('CONST', 'IDENT', 'FIELD'):
|
||||||
code[:] = [S[';'], None]
|
parent[index] = {'node':';','type':None}
|
||||||
else:
|
|
||||||
code[:] = code
|
|
||||||
|
|
||||||
def FoldTree(self, code):
|
def FoldTree(self, parent, index):
|
||||||
"""Recursively traverse the tree to fold constants, changing it in
|
"""Recursively traverse the tree to fold constants, changing it in
|
||||||
place.
|
place.
|
||||||
|
|
||||||
Also optimizes away IF, WHILE, etc.
|
Also optimizes away IF, WHILE, etc.
|
||||||
"""
|
"""
|
||||||
while code[0] == 'EXPR':
|
code = parent[index]
|
||||||
if type(code) == tuple:
|
if code is None: return # Deleted statement
|
||||||
# just enter
|
node = code['node']
|
||||||
code = code[2]
|
child = code['br'] if 'br' in code else None
|
||||||
else:
|
|
||||||
# unfold
|
|
||||||
code[:] = code[2]
|
|
||||||
|
|
||||||
code0 = code[0]
|
if node == 'CONST':
|
||||||
|
|
||||||
if code0 == CONSTANT:
|
|
||||||
# Job already done
|
# Job already done
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'CAST':
|
if node == 'CAST':
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] == CONSTANT:
|
if child[0]['node'] == 'CONST':
|
||||||
# Enable key constants. We'll typecast them back on output, but
|
# Enable key constants. We'll typecast them back on output, but
|
||||||
# this enables some optimizations.
|
# this enables some optimizations.
|
||||||
#if code[1] != 'key': # key constants not possible
|
#if code['type'] != 'key': # key constants not possible
|
||||||
|
|
||||||
code[:] = [CONSTANT, code[1], lslfuncs.typecast(code[2][2], self.LSL2PythonType[code[1]])]
|
parent[index] = {'node':'CONST', 'type':code['type'],
|
||||||
|
'value':lslfuncs.typecast(
|
||||||
|
child[0]['value'], self.LSL2PythonType[code['type']])}
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'NEG':
|
if node == 'NEG':
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] == CONSTANT:
|
if child[0]['node'] == 'CONST':
|
||||||
code[:] = [CONSTANT, code[1], lslfuncs.neg(code[2][2])]
|
code = parent[index] = child[0]
|
||||||
|
code['value'] = lslfuncs.neg(code['value'])
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == '!':
|
if node == '!':
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] == CONSTANT:
|
if child[0]['node'] == 'CONST':
|
||||||
code[:] = [CONSTANT, code[1], int(not code[2][2])]
|
code = parent[index] = child[0]
|
||||||
|
code['value'] = int(not code['value'])
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == '~':
|
if node == '~':
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] == CONSTANT:
|
if child[0]['node'] == 'CONST':
|
||||||
code[:] = [CONSTANT, code[1], ~code[2][2]]
|
code = parent[index] = child[0]
|
||||||
|
code['value'] = ~code['value']
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == '()':
|
if node == '()':
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] in (CONSTANT, 'VECTOR', 'ROTATION', 'LIST',
|
if child[0]['node'] in ('CONST', 'VECTOR', 'ROTATION', 'LIST',
|
||||||
'IDENT', 'FIELD', 'V++', 'V--', 'FUNCTION', 'PRINT'):
|
'IDENT', 'FIELD', 'V++', 'V--', 'FUNCTION', 'PRINT'):
|
||||||
# Child is an unary postfix expression; parentheses can be
|
# Child is an unary postfix expression; parentheses are
|
||||||
# removed safely.
|
# redundant and can be removed safely. Not strictly an
|
||||||
code[:] = code[2]
|
# optimization but it helps keep the output tidy-ish a bit.
|
||||||
|
# It's not done in general (e.g. (a * b) + c does not need
|
||||||
|
# parentheses but these are not eliminated). Only the cases
|
||||||
|
# like (myvar) are simplified.
|
||||||
|
parent[index] = child[0]
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 in self.binary_ops:
|
if node in self.binary_ops:
|
||||||
# RTL evaluation
|
# RTL evaluation
|
||||||
self.FoldTree(code[3])
|
self.FoldTree(child, 1)
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] == code[3][0] == CONSTANT:
|
if child[0]['node'] == child[1]['node'] == 'CONST':
|
||||||
op = code0
|
op = node
|
||||||
op1 = code[2][2]
|
op1 = child[0]['value']
|
||||||
op2 = code[3][2]
|
op2 = child[1]['value']
|
||||||
if op == '+':
|
if op == '+':
|
||||||
result = lslfuncs.add(op1, op2)
|
result = lslfuncs.add(op1, op2)
|
||||||
elif op == '-':
|
elif op == '-':
|
||||||
|
@ -117,7 +123,7 @@ class optimizer(object):
|
||||||
elif op == '>>':
|
elif op == '>>':
|
||||||
result = lslfuncs.S32(op1 >> (op2 & 31))
|
result = lslfuncs.S32(op1 >> (op2 & 31))
|
||||||
elif op == '==' or op == '!=':
|
elif op == '==' or op == '!=':
|
||||||
result = lslfuncs.compare(op1, op2, op == '==')
|
result = lslfuncs.compare(op1, op2, Eq = (op == '=='))
|
||||||
elif op in ('<', '<=', '>', '>='):
|
elif op in ('<', '<=', '>', '>='):
|
||||||
if op in ('>', '<='):
|
if op in ('>', '<='):
|
||||||
result = lslfuncs.less(op2, op1)
|
result = lslfuncs.less(op2, op1)
|
||||||
|
@ -137,25 +143,33 @@ class optimizer(object):
|
||||||
result = int(op1 and op2)
|
result = int(op1 and op2)
|
||||||
else:
|
else:
|
||||||
raise Exception(u'Internal error: Operator not found: ' + op.decode('utf8')) # pragma: no cover
|
raise Exception(u'Internal error: Operator not found: ' + op.decode('utf8')) # pragma: no cover
|
||||||
code[:] = [CONSTANT, code[1], result]
|
parent[index] = {'node':'CONST', 'type':code['type'], 'value':result}
|
||||||
elif code[0] == '-' and code[2][1] in ('integer', 'float') and code[3][1] in ('integer', 'float'):
|
elif node == '-' and child[0]['type'] in ('integer', 'float') \
|
||||||
|
and child[1]['type'] in ('integer', 'float'):
|
||||||
# Change - to + - for int/float
|
# Change - to + - for int/float
|
||||||
if code[3][0] == CONSTANT:
|
if child[1]['node'] == 'CONST':
|
||||||
if code[3][2] == 0:
|
if child[1]['value'] == 0:
|
||||||
code[:] = code[2]
|
parent[index] = child[0]
|
||||||
else:
|
else:
|
||||||
code[0] = S['+']
|
code['node'] = '+'
|
||||||
code[3][2] = lslfuncs.neg(code[3][2])
|
child[1]['value'] = lslfuncs.neg(child[1]['value'])
|
||||||
|
#TODO: Implement to transform 0-x into -x: elif child[0]['node'] == 'CONST':
|
||||||
else:
|
else:
|
||||||
code[:] = [S['+'], code[1], code[2], [S['NEG'], code[3][1], code[3]]]
|
code['node'] = '+'
|
||||||
elif code[0] == '<<' and code[3][0] == CONSTANT:
|
child[1] = {'node':'NEG', 'type':child[1]['type'], 'br':[child[1]]}
|
||||||
|
elif node == '<<' and child[1]['node'] == 'CONST':
|
||||||
# Transforming << into multiply saves some bytes.
|
# Transforming << into multiply saves some bytes.
|
||||||
if code[2][0] in ('+', '-', 'NEG'): # operands with priority between * and <<
|
if child[1]['value'] & 31:
|
||||||
code[2] = [S['()'], code[2][1], code[2]]
|
# x << 3 --> x * 8
|
||||||
if not (code[3][2] & 31):
|
# Do we need parentheses for *? It depends on x
|
||||||
code[:] = code[2]
|
# e.g. x+3<<3 needs parentheses when converted to (x+3)*8
|
||||||
else:
|
if child[0]['node'] in ('+', '-', 'NEG'): # operands with priority between * and << #TODO: CHECK
|
||||||
code[:] = [S['*'], code[1], code[2], [CONSTANT, 'integer', 1<<(code[3][2] & 31)]]
|
child[0] = {'node':'()', 'type':child[0]['type'], 'br':[child[0]]}
|
||||||
|
# we have {<<, something, {CONST n}}, transform into {*, something, {CONST n}}
|
||||||
|
code['node'] = '*'
|
||||||
|
child[1]['value'] = 1<<(child[1]['value'] & 31)
|
||||||
|
else: # x << 0 --> x
|
||||||
|
parent[index] = child[0]
|
||||||
else:
|
else:
|
||||||
pass # TODO: Eliminate redundancy (x+0, x*1, x*-1, v+ZERO_VECTOR, perhaps x-1=~-x, etc.)
|
pass # TODO: Eliminate redundancy (x+0, x*1, x*-1, v+ZERO_VECTOR, perhaps x-1=~-x, etc.)
|
||||||
# Include != to ^ and || to | and maybe && to &
|
# Include != to ^ and || to | and maybe && to &
|
||||||
|
@ -165,205 +179,195 @@ class optimizer(object):
|
||||||
# Maybe turn != -1 into ~ in if()'s.
|
# Maybe turn != -1 into ~ in if()'s.
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 in self.assign_ops:
|
if node in self.assign_ops:
|
||||||
# TODO: Eliminate redundant operations, e.g. a += 0; etc.
|
# TODO: Eliminate redundant operations, e.g. a += 0; etc.
|
||||||
# Consider also e.g. x -= 1 or x -= a transforming it into +=.
|
# Consider also e.g. x -= 1 or x -= a transforming it into +=.
|
||||||
self.FoldTree(code[3])
|
# Actually just consider transforming the whole thing into a
|
||||||
|
# regular assignment, as there are no gains and it simplifies the
|
||||||
|
# optimization.
|
||||||
|
self.FoldTree(child, 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'IDENT':
|
if node == 'IDENT' or node == 'FLD':
|
||||||
if self.globalmode:
|
if self.globalmode:
|
||||||
val = self.symtab[code[3]][code[2]][2]
|
ident = code if node == 'IDENT' else child[0]
|
||||||
if val is not None:
|
# Resolve constant values so they can be optimized
|
||||||
if type(val) == tuple:
|
sym = self.symtab[ident['scope']][ident['name']]
|
||||||
# Infinite recursion is prevented at the parser level, by
|
|
||||||
# not allowing forward globals in global var definitions.
|
defn = self.tree[sym['Loc']]
|
||||||
self.FoldTree(val)
|
assert defn['name'] == ident['name']
|
||||||
if val[0] != 'EXPR' or val[2][0] != CONSTANT:
|
|
||||||
return
|
# Assume we already were there
|
||||||
val = val[2][2]
|
if 'br' in defn:
|
||||||
if code[1] != 'key' and val is not None:
|
val = defn['br'][0]
|
||||||
code[:] = [CONSTANT, code[1], val]
|
if val['node'] != 'CONST' or ident['type'] in ('list', 'key'):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
val = {'node':'CONST', 'type':defn['type'],
|
||||||
|
'value':self.DefaultValues[defn['type']]}
|
||||||
|
if node == 'FLD':
|
||||||
|
val = {'node':'CONST', 'type':'float',
|
||||||
|
'value':val['value']['xyzs'.index(code['fld'])]}
|
||||||
|
parent[index] = val
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'FUNCTION':
|
if node == 'FNCALL':
|
||||||
for x in code[3][::-1]:
|
for idx in xrange(len(child)-1, -1, -1):
|
||||||
self.FoldTree(x)
|
self.FoldTree(child, idx)
|
||||||
if code[2] in self.functions and self.functions[code[2]][2] is not None:
|
if code['name'] in self.symtab[0]:
|
||||||
for x in code[3]:
|
fn = self.symtab[0][code['name']]['Loc']
|
||||||
if x[0] != CONSTANT:
|
if fn is not None and type(fn) != int and all(arg['node'] == 'CONST' for arg in child):
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Call it
|
# Call it
|
||||||
val = self.functions[code[2]][2](*tuple(x[2] for x in code[3]))
|
value = fn(*tuple(arg['value'] for arg in child))
|
||||||
if not self.foldtabs and isinstance(val, unicode) and '\t' in val:
|
if not self.foldtabs and isinstance(value, unicode) and '\t' in value:
|
||||||
warning('WARNING: Tab in function result and foldtabs option not used.')
|
warning('WARNING: Tab in function result and foldtabs option not used.')
|
||||||
return
|
return
|
||||||
code[:] = [CONSTANT, code[1], val]
|
parent[index] = {'node':'CONST', 'type':code['type'], 'value':value}
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'PRINT':
|
if node == 'PRINT':
|
||||||
# useless but who knows
|
# useless but who knows
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 in ('VECTOR', 'ROTATION', 'LIST'):
|
if node in ('VECTOR', 'ROTATION', 'LIST'):
|
||||||
isconst = True
|
isconst = True
|
||||||
for x in code[:1:-1]:
|
for idx in xrange(len(child)-1, -1, -1):
|
||||||
self.FoldTree(x)
|
self.FoldTree(child, idx)
|
||||||
if x[0] != CONSTANT:
|
if child[idx]['node'] != 'CONST':
|
||||||
isconst = False
|
isconst = False
|
||||||
if isconst:
|
if isconst:
|
||||||
value = [x[2] for x in code[2:]]
|
value = [elem['value'] for elem in child]
|
||||||
if code0 == 'VECTOR':
|
if node == 'VECTOR':
|
||||||
value = lslfuncs.Vector([lslfuncs.ff(x) for x in value])
|
value = lslfuncs.Vector([lslfuncs.ff(x) for x in value])
|
||||||
elif code0 == 'ROTATION':
|
elif node == 'ROTATION':
|
||||||
value = lslfuncs.Quaternion([lslfuncs.ff(x) for x in value])
|
value = lslfuncs.Quaternion([lslfuncs.ff(x) for x in value])
|
||||||
code[:] = [CONSTANT, code[1], value]
|
parent[index] = {'node':'CONST', 'type':code['type'], 'value':value}
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'FIELD':
|
if node in ('{}', 'FNDEF', 'STATEDEF'):
|
||||||
if self.globalmode:
|
for idx in xrange(len(child)):
|
||||||
# We can fold a global vector or rotation field as they are
|
self.FoldTree(child, idx)
|
||||||
# constant, but that involves resolving the symbols that aren't
|
self.FoldStmt(child, idx)
|
||||||
# already.
|
|
||||||
assert code[2][0] == 'IDENT' # that should be granted
|
|
||||||
glob = self.symtab[code[2][3]][code[2][2]]
|
|
||||||
origin = glob[2]
|
|
||||||
if type(origin) == tuple:
|
|
||||||
# We have to do this due to not processing globals in order.
|
|
||||||
self.FoldTree(origin)
|
|
||||||
# Unfold constant expression
|
|
||||||
if origin[0] != 'EXPR' or origin[2][0] != CONSTANT:
|
|
||||||
return
|
|
||||||
origin = origin[2][2]
|
|
||||||
self.symtab[code[2][3]][code[2][2]] = glob[:2] + (origin,) + glob[3:]
|
|
||||||
if type(origin) not in (lslfuncs.Vector, lslfuncs.Quaternion):
|
|
||||||
# Precondition not met
|
|
||||||
return # pragma: no cover
|
|
||||||
code[:] = [CONSTANT, 'float', lslfuncs.ff(origin['xyzs'.index(code[3])])]
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == '{}':
|
if node == 'IF':
|
||||||
for x in code[2:]:
|
self.FoldTree(child, 0)
|
||||||
self.FoldTree(x)
|
if child[0]['node'] == 'CONST':
|
||||||
self.FoldStmt(x)
|
|
||||||
return
|
|
||||||
|
|
||||||
if code0 == 'IF':
|
|
||||||
self.FoldTree(code[2])
|
|
||||||
if code[2][0] == CONSTANT:
|
|
||||||
# We can remove one of the branches safely.
|
# We can remove one of the branches safely.
|
||||||
if lslfuncs.cond(code[2][2]):
|
if lslfuncs.cond(child[0]['value']):
|
||||||
self.FoldTree(code[3])
|
self.FoldTree(child, 1)
|
||||||
code[:] = code[3]
|
parent[index] = child[1]
|
||||||
self.FoldStmt(code)
|
self.FoldStmt(child, 1)
|
||||||
elif len(code) > 4:
|
elif len(child) > 2:
|
||||||
self.FoldTree(code[4])
|
self.FoldTree(child, 2)
|
||||||
code[:] = code[4]
|
parent[index] = child[2]
|
||||||
self.FoldStmt(code)
|
self.FoldStmt(child, 2)
|
||||||
else:
|
else:
|
||||||
# No ELSE branch, replace the statement with an empty one.
|
# No ELSE branch, replace the statement with an empty one.
|
||||||
code[:] = [S[';'], None]
|
parent[index] = {'node':';', 'type':None}
|
||||||
else:
|
else:
|
||||||
self.FoldTree(code[3])
|
self.FoldTree(child, 1)
|
||||||
self.FoldStmt(code[3])
|
self.FoldStmt(child, 1)
|
||||||
if len(code) > 4:
|
if len(child) > 2:
|
||||||
self.FoldTree(code[4])
|
self.FoldTree(child, 2)
|
||||||
self.FoldStmt(code[4])
|
self.FoldStmt(child, 2)
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'WHILE':
|
if node == 'WHILE':
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
if code[2][0] == CONSTANT:
|
if child[0]['node'] == 'CONST':
|
||||||
# See if the whole WHILE can be eliminated.
|
# See if the whole WHILE can be eliminated.
|
||||||
if lslfuncs.cond(code[2][2]):
|
if lslfuncs.cond(child[0]['value']):
|
||||||
# Endless loop which must be kept.
|
# Endless loop which must be kept.
|
||||||
# First, replace the constant.
|
# First, replace the constant.
|
||||||
code[2][1:2] = [S['integer'], 1]
|
child[0].update({'type':'integer', 'value':1})
|
||||||
# Recurse on the statement.
|
# Recurse on the statement.
|
||||||
self.FoldTree(code[3])
|
self.FoldTree(child, 1)
|
||||||
self.FoldStmt(code[3])
|
self.FoldStmt(child, 1)
|
||||||
else:
|
else:
|
||||||
# Can be removed.
|
# Can be removed.
|
||||||
code[:] = [S[';'], None]
|
parent[index] = {'node':';', 'type':None}
|
||||||
else:
|
else:
|
||||||
self.FoldTree(code[3])
|
self.FoldTree(child, 1)
|
||||||
self.FoldStmt(code[3])
|
self.FoldStmt(child, 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'DO':
|
if node == 'DO':
|
||||||
self.FoldTree(code[2]) # This one is always executed.
|
self.FoldTree(child, 0) # This one is always executed.
|
||||||
self.FoldStmt(code[2])
|
self.FoldStmt(child, 0)
|
||||||
self.FoldTree(code[3])
|
self.FoldTree(child, 1)
|
||||||
# See if the latest part is a constant.
|
# See if the latest part is a constant.
|
||||||
if code[3][0] == CONSTANT:
|
if child[1]['node'] == 'CONST':
|
||||||
if lslfuncs.cond(code[3][2]):
|
if lslfuncs.cond(child[1]['value']):
|
||||||
# Endless loop. Replace the constant.
|
# Endless loop. Replace the constant.
|
||||||
code[3][1:2] = [S['integer'], 1]
|
child[1].update({'type':'integer', 'value':1})
|
||||||
else:
|
else:
|
||||||
# Only one go. Replace with the statement(s).
|
# Only one go. Replace with the statement(s).
|
||||||
code[:] = code[2]
|
parent[index] = child[0]
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'FOR':
|
if node == 'FOR':
|
||||||
self.FoldAndRemoveEmptyStmts(code[2])
|
assert child[0]['node'] == 'EXPRLIST'
|
||||||
|
assert child[2]['node'] == 'EXPRLIST'
|
||||||
|
self.FoldAndRemoveEmptyStmts(child[0]['br'])
|
||||||
|
|
||||||
self.FoldTree(code[3]) # Condition.
|
self.FoldTree(child, 1) # Condition.
|
||||||
if code[3][0] == CONSTANT:
|
if child[1]['node'] == 'CONST':
|
||||||
# FOR is delicate. It can have multiple expressions at start.
|
# FOR is delicate. It can have multiple expressions at start.
|
||||||
# And if there is more than one, these expressions will need a
|
# And if there is more than one, these expressions will need a
|
||||||
# new block, which means new scope, which is dangerous.
|
# new block, which means new scope, which is dangerous.
|
||||||
# They are expressions, no declarations or labels allowed, but
|
# They are expressions, no declarations or labels allowed, but
|
||||||
# it feels creepy.
|
# it feels creepy.
|
||||||
if lslfuncs.cond(code[3][2]):
|
if lslfuncs.cond(child[1]['value']):
|
||||||
# Endless loop. Just replace the constant and traverse the rest.
|
# Endless loop. Just replace the constant and traverse the rest.
|
||||||
code[3][1:2] = [S['integer'], 1]
|
child[1].update({'type':'integer', 'value':1})
|
||||||
self.FoldAndRemoveEmptyStmts(code[4])
|
self.FoldAndRemoveEmptyStmts(child[2]['br'])
|
||||||
self.FoldTree(code[5])
|
self.FoldTree(child, 3)
|
||||||
self.FoldStmt(code[5])
|
self.FoldStmt(child, 3)
|
||||||
elif len(code[2]) > 1:
|
elif len(child[0]['br']) > 1:
|
||||||
code[:] = [S['{}'], None] + code[2]
|
parent[index] = {'node':'{}', 'type':None, 'br':child[0]['br']}
|
||||||
elif code[2]:
|
elif child[0]['br']:
|
||||||
code[:] = code[2][0]
|
parent[index] = child[0]['br'][0]
|
||||||
else:
|
else:
|
||||||
code[:] = [S[';'], None]
|
parent[index] = {'node':';', 'type':None}
|
||||||
else:
|
else:
|
||||||
self.FoldAndRemoveEmptyStmts(code[4])
|
self.FoldAndRemoveEmptyStmts(child[2]['br'])
|
||||||
self.FoldTree(code[5])
|
self.FoldTree(child, 3)
|
||||||
self.FoldStmt(code[5])
|
self.FoldStmt(child, 3)
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'RETURN':
|
if node == 'RETURN':
|
||||||
if code[2] is not None:
|
if child:
|
||||||
self.FoldTree(code[2])
|
self.FoldTree(child, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 == 'DECL':
|
if node == 'DECL':
|
||||||
# The expression code is elsewhere.
|
# The expression code is elsewhere.
|
||||||
expr = self.symtab[code[3]][code[2]][2]
|
if child:
|
||||||
# Irrelevant if list or string or key.
|
self.FoldTree(child, 0)
|
||||||
if expr is not None:
|
|
||||||
self.FoldTree(expr)
|
|
||||||
# TODO: Remove assignment if integer zero.
|
# TODO: Remove assignment if integer zero.
|
||||||
else:
|
else:
|
||||||
# TODO: Add assignment if vector, rotation or float.
|
# TODO: Add assignment if vector, rotation or float.
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
|
|
||||||
if code0 in self.ignored_stmts:
|
if node in self.ignored_stmts:
|
||||||
return
|
return
|
||||||
|
|
||||||
raise Exception('Internal error: This should not happen, node = ' + code0) # pragma: no cover
|
raise Exception('Internal error: This should not happen, node = ' + node) # pragma: no cover
|
||||||
|
|
||||||
def IsValidGlobalConstant(self, value):
|
def IsValidGlobalConstant(self, decl):
|
||||||
if value[0] == 'EXPR':
|
if 'br' not in decl:
|
||||||
value = value[2]
|
return True
|
||||||
if value[0] not in ('VECTOR', 'ROTATION', 'LIST'):
|
expr = decl['br'][0]
|
||||||
|
if expr['node'] in ('CONST', 'IDENT'):
|
||||||
|
return True
|
||||||
|
if expr['node'] not in ('VECTOR', 'ROTATION', 'LIST'):
|
||||||
return False
|
return False
|
||||||
return all(x[0] in (CONSTANT, 'IDENT') for x in value[2:])
|
return all(elem['node'] in ('CONST', 'IDENT') for elem in expr['br'])
|
||||||
|
|
||||||
def optimize(self, symtab, functions, options = ('optimize',)):
|
def optimize(self, treesymtab, options = ('optimize',)):
|
||||||
"""Optimize the symbolic table symtab in place. Requires a table of
|
"""Optimize the symbolic table symtab in place. Requires a table of
|
||||||
predefined functions for folding constants.
|
predefined functions for folding constants.
|
||||||
"""
|
"""
|
||||||
|
@ -375,27 +379,22 @@ class optimizer(object):
|
||||||
|
|
||||||
# TODO: Add option to handle local jumps properly.
|
# TODO: Add option to handle local jumps properly.
|
||||||
|
|
||||||
self.functions = functions
|
tree, symtab = self.tree, self.symtab = treesymtab
|
||||||
self.symtab = symtab
|
|
||||||
|
|
||||||
self.globalmode = False
|
self.globalmode = False
|
||||||
|
|
||||||
# Fold constants etc.
|
# Constant folding pass. It does some other optimizations along the way.
|
||||||
for name in symtab[0]:
|
for idx in xrange(len(tree)):
|
||||||
if name == -1:
|
if tree[idx]['node'] == 'DECL':
|
||||||
continue
|
self.globalmode = True
|
||||||
entry = symtab[0][name]
|
self.FoldTree(tree, idx)
|
||||||
if entry[1] == 'State':
|
|
||||||
for event in entry[2]:
|
|
||||||
self.FoldTree(entry[2][event][2])
|
|
||||||
elif type(entry[2]) == tuple:
|
|
||||||
self.globalmode = len(entry) == 3
|
|
||||||
self.FoldTree(entry[2]) # global
|
|
||||||
if self.globalmode:
|
|
||||||
val = entry[2]
|
|
||||||
# Unfold constant
|
|
||||||
if val[0] == 'EXPR' and val[2][0] == CONSTANT:
|
|
||||||
symtab[0][name] = entry[:2] + (val[2][2],) + entry[3:]
|
|
||||||
elif not self.IsValidGlobalConstant(val):
|
|
||||||
warning('WARNING: Expression does not collapse to a single constant.')
|
|
||||||
self.globalmode = False
|
self.globalmode = False
|
||||||
|
if not self.IsValidGlobalConstant(tree[idx]):
|
||||||
|
warning('WARNING: Expression does not collapse to a single constant.')
|
||||||
|
else:
|
||||||
|
self.FoldTree(tree, idx)
|
||||||
|
|
||||||
|
treesymtab = (self.tree, self.symtab)
|
||||||
|
del self.tree
|
||||||
|
del self.symtab
|
||||||
|
return treesymtab
|
||||||
|
|
|
@ -77,7 +77,7 @@ class outscript(object):
|
||||||
neg = '-'
|
neg = '-'
|
||||||
# Try harder
|
# Try harder
|
||||||
point = news.index('.') + 1 - len(news) # Remove point
|
point = news.index('.') + 1 - len(news) # Remove point
|
||||||
news = str(int(news[:point-1] + news[point:]) + 1) # Increment
|
news = str(int(news[:point-1] + news[point:]) + 1).zfill(len(news)-1) # Increment
|
||||||
news = news[:point + len(news)] + '.' + news[point + len(news):] # Reinsert point
|
news = news[:point + len(news)] + '.' + news[point + len(news):] # Reinsert point
|
||||||
# Repeat the operation with the incremented number
|
# Repeat the operation with the incremented number
|
||||||
while news[-1] != '.' and lslfuncs.F32(float(neg+news[:-1]+exp)) == value:
|
while news[-1] != '.' and lslfuncs.F32(float(neg+news[:-1]+exp)) == value:
|
||||||
|
@ -129,230 +129,190 @@ class outscript(object):
|
||||||
self.indentlevel -= 1
|
self.indentlevel -= 1
|
||||||
return ret + self.dent() + self.indent + ']'
|
return ret + self.dent() + self.indent + ']'
|
||||||
|
|
||||||
if tvalue == tuple and value[0] == 'IDENT': # HACK
|
|
||||||
return value[2]
|
|
||||||
assert False, u'Value of unknown type in Value2LSL: ' + repr(value)
|
assert False, u'Value of unknown type in Value2LSL: ' + repr(value)
|
||||||
|
|
||||||
def dent(self):
|
def dent(self):
|
||||||
return self.indent * self.indentlevel
|
return self.indent * self.indentlevel
|
||||||
|
|
||||||
def OutIndented(self, code):
|
def OutIndented(self, code):
|
||||||
if code[0] != '{}':
|
if code['node'] != '{}':
|
||||||
self.indentlevel += 1
|
self.indentlevel += 1
|
||||||
ret = self.OutCode(code)
|
ret = self.OutCode(code)
|
||||||
if code[0] != '{}':
|
if code['node'] != '{}':
|
||||||
self.indentlevel -= 1
|
self.indentlevel -= 1
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def OutExprList(self, L):
|
def OutExprList(self, L):
|
||||||
ret = ''
|
ret = ''
|
||||||
if L:
|
if L:
|
||||||
|
First = True
|
||||||
for item in L:
|
for item in L:
|
||||||
if ret != '':
|
if not First:
|
||||||
ret += ', '
|
ret += ', '
|
||||||
ret += self.OutExpr(item)
|
ret += self.OutExpr(item)
|
||||||
|
First = False
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def OutExpr(self, expr):
|
def OutExpr(self, expr):
|
||||||
# Save some recursion by unwrapping the expression
|
# Handles expression nodes (as opposed to statement nodes)
|
||||||
while expr[0] == 'EXPR':
|
node = expr['node']
|
||||||
expr = expr[2]
|
if 'br' in expr:
|
||||||
node = expr[0]
|
child = expr['br']
|
||||||
|
|
||||||
if node == '()':
|
if node == '()':
|
||||||
return '(' + self.OutExpr(expr[2]) + ')'
|
return '(' + self.OutExpr(child[0]) + ')'
|
||||||
|
|
||||||
if node in self.binary_operands:
|
if node in self.binary_operands:
|
||||||
return self.OutExpr(expr[2]) + ' ' + node + ' ' + self.OutExpr(expr[3])
|
return self.OutExpr(child[0]) + ' ' + node + ' ' + self.OutExpr(child[1])
|
||||||
|
|
||||||
if node == 'IDENT':
|
if node == 'IDENT':
|
||||||
return expr[2]
|
return expr['name']
|
||||||
if node == 'CONSTANT':
|
|
||||||
return self.Value2LSL(expr[2])
|
if node == 'CONST':
|
||||||
|
return self.Value2LSL(expr['value'])
|
||||||
|
|
||||||
if node == 'CAST':
|
if node == 'CAST':
|
||||||
ret = '(' + expr[1] + ')'
|
ret = '(' + expr['type'] + ')'
|
||||||
expr = expr[2]
|
expr = child[0]
|
||||||
if expr[0] == 'EXPR':
|
if expr['node'] in ('CONST', 'IDENT', 'V++', 'V--', 'VECTOR',
|
||||||
expr = expr[2]
|
'ROTATION', 'LIST', 'FIELD', 'PRINT', 'FUNCTION', '()'):
|
||||||
if expr[0] in ('CONSTANT', 'IDENT', 'V++', 'V--', 'VECTOR',
|
return ret + self.OutExpr(expr)
|
||||||
'ROTATION', 'LIST', 'FIELD', 'PRINT', 'FUNCTION', '()'):
|
return ret + '(' + self.OutExpr(expr) + ')'
|
||||||
ret += self.OutExpr(expr)
|
|
||||||
else:
|
|
||||||
ret += '(' + self.OutExpr(expr) + ')'
|
|
||||||
return ret
|
|
||||||
if node == 'LIST':
|
if node == 'LIST':
|
||||||
if len(expr) == 2:
|
self.listmode = True
|
||||||
return '[]'
|
ret = '[' + self.OutExprList(child) + ']'
|
||||||
return '[' + self.OutExprList(expr[2:]) + ']'
|
self.listmode = False
|
||||||
if node == 'VECTOR':
|
return ret
|
||||||
return '<' + self.OutExpr(expr[2]) + ', ' + self.OutExpr(expr[3]) \
|
|
||||||
+ ', ' + self.OutExpr(expr[4]) + '>'
|
if node in ('VECTOR', 'ROTATION'):
|
||||||
if node == 'ROTATION':
|
return '<' + self.OutExprList(child) + '>'
|
||||||
return '<' + self.OutExpr(expr[2]) + ', ' + self.OutExpr(expr[3]) \
|
|
||||||
+ ', ' + self.OutExpr(expr[4]) + ', ' + self.OutExpr(expr[5]) + '>'
|
if node == 'FNCALL':
|
||||||
if node == 'FUNCTION':
|
return expr['name'] + '(' + self.OutExprList(child) + ')'
|
||||||
return expr[2] + '(' + self.OutExprList(expr[3]) + ')'
|
|
||||||
if node == 'PRINT':
|
if node == 'PRINT':
|
||||||
return 'print(' + self.OutExpr(expr[2]) + ')'
|
return 'print(' + self.OutExpr(child[0]) + ')'
|
||||||
|
|
||||||
if node in self.unary_operands:
|
if node in self.unary_operands:
|
||||||
if node == 'NEG':
|
if node == 'NEG':
|
||||||
node = '- '
|
node = '- '
|
||||||
return node + self.OutExpr(expr[2])
|
return node + self.OutExpr(child[0])
|
||||||
|
|
||||||
if node == 'FIELD':
|
if node == 'FLD':
|
||||||
return self.OutExpr(expr[2]) + '.' + expr[3]
|
return self.OutExpr(child[0]) + '.' + expr['fld']
|
||||||
|
|
||||||
if node in ('V--', 'V++'):
|
if node in ('V--', 'V++'):
|
||||||
return self.OutExpr(expr[2]) + node[1:]
|
return self.OutExpr(child[0]) + ('++' if node == 'V++' else '--')
|
||||||
|
|
||||||
if node in ('--V', '++V'):
|
if node in ('--V', '++V'):
|
||||||
return node[:-1] + self.OutExpr(expr[2])
|
return ('++' if node == '++V' else '--') + self.OutExpr(child[0])
|
||||||
|
|
||||||
if node in self.extended_assignments:
|
if node in self.extended_assignments:
|
||||||
op = self.OutExpr(expr[2])
|
lvalue = self.OutExpr(child[0])
|
||||||
return op + ' = ' + op + ' ' + node[:-1] + ' (' + self.OutExpr(expr[3]) + ')'
|
return lvalue + ' = ' + lvalue + ' ' + node[:-1] + ' (' + self.OutExpr(child[1]) + ')'
|
||||||
|
|
||||||
|
if node == 'EXPRLIST':
|
||||||
|
return self.OutExprList(child)
|
||||||
|
|
||||||
assert False, 'Internal error: expression type "' + node + '" not handled' # pragma: no cover
|
assert False, 'Internal error: expression type "' + node + '" not handled' # pragma: no cover
|
||||||
|
|
||||||
def OutCode(self, code):
|
def OutCode(self, code):
|
||||||
#return self.dent() + '{\n' + self.dent() + '}\n'
|
node = code['node']
|
||||||
node = code[0]
|
if 'br' in code:
|
||||||
if node == '{}':
|
child = code['br']
|
||||||
ret = self.dent() + '{\n'
|
else:
|
||||||
self.indentlevel += 1
|
child = None
|
||||||
for stmt in code[2:]:
|
|
||||||
ret += self.OutCode(stmt)
|
|
||||||
self.indentlevel -= 1
|
|
||||||
return ret + self.dent() + '}\n'
|
|
||||||
if node == 'IF':
|
if node == 'IF':
|
||||||
ret = self.dent()
|
ret = self.dent()
|
||||||
while True:
|
while True:
|
||||||
ret += 'if (' + self.OutExpr(code[2]) + ')\n' + self.OutIndented(code[3])
|
ret += 'if (' + self.OutExpr(child[0]) + ')\n' + self.OutIndented(child[1])
|
||||||
if len(code) < 5:
|
if len(child) < 3:
|
||||||
return ret
|
return ret
|
||||||
if code[4][0] != 'IF':
|
if child[2]['node'] != 'IF':
|
||||||
ret += self.dent() + 'else\n' + self.OutIndented(code[4])
|
ret += self.dent() + 'else\n' + self.OutIndented(child[2])
|
||||||
return ret
|
return ret
|
||||||
ret += self.dent() + 'else '
|
ret += self.dent() + 'else '
|
||||||
code = code[4]
|
code = child[2]
|
||||||
|
child = code['br']
|
||||||
if node == 'WHILE':
|
if node == 'WHILE':
|
||||||
ret = self.dent() + 'while (' + self.OutExpr(code[2]) + ')\n'
|
ret = self.dent() + 'while (' + self.OutExpr(child[0]) + ')\n'
|
||||||
ret += self.OutIndented(code[3])
|
ret += self.OutIndented(child[1])
|
||||||
return ret
|
return ret
|
||||||
if node == 'DO':
|
if node == 'DO':
|
||||||
ret = self.dent() + 'do\n'
|
ret = self.dent() + 'do\n'
|
||||||
ret += self.OutIndented(code[2])
|
ret += self.OutIndented(child[0])
|
||||||
return ret + self.dent() + 'while (' + self.OutExpr(code[3]) + ');\n'
|
return ret + self.dent() + 'while (' + self.OutExpr(child[1]) + ');\n'
|
||||||
if node == 'FOR':
|
if node == 'FOR':
|
||||||
ret = self.dent() + 'for ('
|
ret = self.dent() + 'for ('
|
||||||
if code[2]:
|
ret += self.OutExpr(child[0])
|
||||||
ret += self.OutExpr(code[2][0])
|
ret += '; ' + self.OutExpr(child[1]) + '; '
|
||||||
if len(code[2]) > 1:
|
ret += self.OutExpr(child[2])
|
||||||
for expr in code[2][1:]:
|
|
||||||
ret += ', ' + self.OutExpr(expr)
|
|
||||||
ret += '; ' + self.OutExpr(code[3]) + '; '
|
|
||||||
if code[4]:
|
|
||||||
ret += self.OutExpr(code[4][0])
|
|
||||||
if len(code[4]) > 1:
|
|
||||||
for expr in code[4][1:]:
|
|
||||||
ret += ', ' + self.OutExpr(expr)
|
|
||||||
ret += ')\n'
|
ret += ')\n'
|
||||||
ret += self.OutIndented(code[5])
|
ret += self.OutIndented(child[3])
|
||||||
return ret
|
return ret
|
||||||
if node == '@':
|
if node == '@':
|
||||||
return self.dent() + '@' + code[2] + ';\n'
|
return self.dent() + '@' + code['name'] + ';\n'
|
||||||
if node == 'JUMP':
|
if node == 'JUMP':
|
||||||
assert code[2][0:2] == ['IDENT', 'Label']
|
return self.dent() + 'jump ' + code['name'] + ';\n'
|
||||||
return self.dent() + 'jump ' + code[2][2] + ';\n'
|
|
||||||
if node == 'STATE':
|
if node == 'STATE':
|
||||||
name = 'default'
|
return self.dent() + 'state ' + code['name'] + ';\n'
|
||||||
if code[2] != 'DEFAULT':
|
|
||||||
assert code[2][0:2] == ['IDENT', 'State']
|
|
||||||
name = code[2][2]
|
|
||||||
return self.dent() + 'state ' + name + ';\n'
|
|
||||||
if node == 'RETURN':
|
if node == 'RETURN':
|
||||||
if code[2] is None:
|
if child:
|
||||||
return self.dent() + 'return;\n'
|
return self.dent() + 'return ' + self.OutExpr(child[0]) + ';\n'
|
||||||
return self.dent() + 'return ' + self.OutExpr(code[2]) + ';\n'
|
return self.dent() + 'return;\n'
|
||||||
if node == 'DECL':
|
if node == 'DECL':
|
||||||
sym = self.symtab[code[3]][code[2]]
|
ret = self.dent() + code['type'] + ' ' + code['name']
|
||||||
ret = self.dent() + sym[1] + ' ' + code[2]
|
if child:
|
||||||
if sym[2] is not None:
|
ret += ' = ' + self.OutExpr(child[0])
|
||||||
ret += ' = ' + self.OutExpr(sym[2])
|
|
||||||
return ret + ';\n'
|
return ret + ';\n'
|
||||||
if node == ';':
|
if node == ';':
|
||||||
return self.dent() + ';\n'
|
return self.dent() + ';\n'
|
||||||
|
|
||||||
|
if node in ('STATEDEF', '{}'):
|
||||||
|
ret = ''
|
||||||
|
if node == 'STATEDEF':
|
||||||
|
if code['name'] == 'default':
|
||||||
|
ret = self.dent() + 'default\n'
|
||||||
|
else:
|
||||||
|
ret = self.dent() + 'state ' + code['name'] + '\n'
|
||||||
|
|
||||||
|
ret += self.dent() + '{\n'
|
||||||
|
self.indentlevel += 1
|
||||||
|
for stmt in code['br']:
|
||||||
|
ret += self.OutCode(stmt)
|
||||||
|
self.indentlevel -= 1
|
||||||
|
return ret + self.dent() + '}\n'
|
||||||
|
|
||||||
|
if node == 'FNDEF':
|
||||||
|
ret = self.dent()
|
||||||
|
if code['type'] is not None:
|
||||||
|
ret += code['type'] + ' '
|
||||||
|
ret += code['name'] + '('
|
||||||
|
ret += ', '.join(typ + ' ' + name for typ, name in zip(code['ptypes'], code['pnames']))
|
||||||
|
return ret + ')\n' + self.OutCode(child[0])
|
||||||
|
|
||||||
return self.dent() + self.OutExpr(code) + ';\n'
|
return self.dent() + self.OutExpr(code) + ';\n'
|
||||||
|
|
||||||
def OutFunc(self, typ, name, paramlist, paramsymtab, code):
|
def output(self, treesymtab, options = ('optsigns',)):
|
||||||
ret = self.dent()
|
|
||||||
if typ is not None:
|
|
||||||
ret += typ + ' '
|
|
||||||
ret += name + '('
|
|
||||||
first = True
|
|
||||||
if paramlist:
|
|
||||||
for name in paramlist:
|
|
||||||
if not first:
|
|
||||||
ret += ', '
|
|
||||||
ret += paramsymtab[name][1] + ' ' + name
|
|
||||||
first = False
|
|
||||||
return ret + ')\n' + self.OutCode(code)
|
|
||||||
|
|
||||||
def output(self, symtab, options = ('optimizesigns',)):
|
|
||||||
# Build a sorted list of dict entries
|
# Build a sorted list of dict entries
|
||||||
order = []
|
self.tree, self.symtab = treesymtab
|
||||||
self.symtab = symtab
|
|
||||||
|
|
||||||
# Optimize signs
|
# Optimize signs
|
||||||
self.optsigns = 'optimizesigns' in options
|
self.optsigns = 'optsigns' in options
|
||||||
|
|
||||||
for i in symtab:
|
|
||||||
item = []
|
|
||||||
for j in sorted(i.items(), key=lambda k: -1 if k[0]==-1 else k[1][0]):
|
|
||||||
if j[0] != -1:
|
|
||||||
item.append(j[0])
|
|
||||||
order.append(item)
|
|
||||||
|
|
||||||
ret = ''
|
ret = ''
|
||||||
self.indent = ' '
|
self.indent = ' '
|
||||||
self.indentlevel = 0
|
self.indentlevel = 0
|
||||||
self.globalmode = False
|
self.globalmode = False
|
||||||
self.listmode = False
|
self.listmode = False
|
||||||
for name in order[0]:
|
for code in self.tree:
|
||||||
sym = symtab[0][name]
|
if code['node'] == 'DECL':
|
||||||
|
|
||||||
ret += self.dent()
|
|
||||||
if sym[1] == 'State':
|
|
||||||
if name == 'default':
|
|
||||||
ret += 'default\n{\n'
|
|
||||||
else:
|
|
||||||
ret += 'state ' + name + '\n{\n'
|
|
||||||
|
|
||||||
self.indentlevel += 1
|
|
||||||
eventorder = []
|
|
||||||
for event in sorted(sym[2].items(), key=lambda k: k[1][0]):
|
|
||||||
eventorder.append(event[0])
|
|
||||||
for name in eventorder:
|
|
||||||
eventdef = sym[2][name]
|
|
||||||
ret += self.OutFunc(eventdef[1], name, eventdef[3], symtab[eventdef[4]], eventdef[2])
|
|
||||||
self.indentlevel -= 1
|
|
||||||
ret += self.dent() + '}\n'
|
|
||||||
|
|
||||||
elif len(sym) > 3: # function definition
|
|
||||||
ret += self.OutFunc(sym[1], name, sym[3], symtab[sym[4]], sym[2])
|
|
||||||
|
|
||||||
else: # global var
|
|
||||||
|
|
||||||
self.globalmode = True
|
self.globalmode = True
|
||||||
ret += sym[1] + ' ' + name
|
ret += self.OutCode(code)
|
||||||
if sym[2] is not None:
|
|
||||||
ret += ' = '
|
|
||||||
if type(sym[2]) == tuple:
|
|
||||||
ret += self.OutExpr(sym[2])
|
|
||||||
else:
|
|
||||||
ret += self.Value2LSL(sym[2])
|
|
||||||
|
|
||||||
ret += ';\n'
|
|
||||||
self.globalmode = False
|
self.globalmode = False
|
||||||
|
else:
|
||||||
|
ret += self.OutCode(code)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
File diff suppressed because it is too large
Load diff
14
main.py
14
main.py
|
@ -64,7 +64,7 @@ means that e.g. a + 3 + 5 is not optimized to a + 8; however a + (3 + 5) is.
|
||||||
return 1
|
return 1
|
||||||
optchanges = sys.argv[2].split(',')
|
optchanges = sys.argv[2].split(',')
|
||||||
for chg in optchanges:
|
for chg in optchanges:
|
||||||
if chg[0:1] != '+':
|
if chg[0:1] not in ('+', '-'):
|
||||||
chg = '+' + chg
|
chg = '+' + chg
|
||||||
if chg[0] == '-':
|
if chg[0] == '-':
|
||||||
options.discard(chg[1:])
|
options.discard(chg[1:])
|
||||||
|
@ -78,24 +78,22 @@ means that e.g. a + 3 + 5 is not optimized to a + 8; however a + (3 + 5) is.
|
||||||
try:
|
try:
|
||||||
if fname == '-':
|
if fname == '-':
|
||||||
script = sys.stdin.read()
|
script = sys.stdin.read()
|
||||||
p.parse(script, options)
|
ts = p.parse(script, options)
|
||||||
else:
|
else:
|
||||||
p.parsefile(fname, options)
|
ts = p.parsefile(fname, options)
|
||||||
funcs = p.functions
|
|
||||||
symtab = p.symtab
|
|
||||||
except EParse as e:
|
except EParse as e:
|
||||||
print e.message
|
print e.message
|
||||||
return 1
|
return 1
|
||||||
del p
|
del p
|
||||||
|
|
||||||
opt = optimizer()
|
opt = optimizer()
|
||||||
opt.optimize(symtab, funcs, options)
|
ts = opt.optimize(ts, options)
|
||||||
del opt
|
del opt
|
||||||
|
|
||||||
outs = outscript()
|
outs = outscript()
|
||||||
script = outs.output(symtab, options)
|
script = outs.output(ts, options)
|
||||||
del outs
|
del outs
|
||||||
del symtab
|
del ts
|
||||||
sys.stdout.write(script)
|
sys.stdout.write(script)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
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,EParseUnexpected,\
|
EParseInvalidField,EParseFunctionMismatch,EParseDeclarationScope,\
|
||||||
fieldpos
|
fieldpos
|
||||||
from lslopt.lsloutput import outscript
|
from lslopt.lsloutput import outscript
|
||||||
from lslopt.lsloptimizer import optimizer
|
from lslopt.lsloptimizer import optimizer
|
||||||
|
@ -69,8 +69,8 @@ class Test02_Compiler(UnitTestCase):
|
||||||
float f;
|
float f;
|
||||||
float ff = f;
|
float ff = f;
|
||||||
list L = [];
|
list L = [];
|
||||||
list L2 = [2,3,4,5,6];
|
list L2 = [2,3,4,5,-6];
|
||||||
list L3 = [2,3,f,5,6];
|
list L3 = [2,3,f,5,-6.0];
|
||||||
rotation QQ = <f,f,f,f>;
|
rotation QQ = <f,f,f,f>;
|
||||||
integer fn(integer x){
|
integer fn(integer x){
|
||||||
if (1) for (f=3,f=4,f=5;3;f++,f++) do while(0); while(0); else if (2) return 2; else;
|
if (1) for (f=3,f=4,f=5;3;f++,f++) do while(0); while(0); else if (2) return 2; else;
|
||||||
|
@ -88,7 +88,7 @@ class Test02_Compiler(UnitTestCase):
|
||||||
1e37;1.1e22;1.;
|
1e37;1.1e22;1.;
|
||||||
print(V *= 3);
|
print(V *= 3);
|
||||||
fwd("","","");
|
fwd("","","");
|
||||||
L"\n\t\rxxxx";
|
L"\n\t\rxxxx";@lbl;jump lbl;
|
||||||
{f;}
|
{f;}
|
||||||
[1,2,3];
|
[1,2,3];
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,8 @@ class Test02_Compiler(UnitTestCase):
|
||||||
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){""%4;}''')
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){""%4;}''')
|
||||||
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){3%<2,3,4>;}''')
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){3%<2,3,4>;}''')
|
||||||
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){""%4;}''')
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){""%4;}''')
|
||||||
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){float i;i%=2;}''')
|
||||||
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){float i;i&=2;}''', ['extendedassignment'])
|
||||||
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){(vector)4;}''')
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){(vector)4;}''')
|
||||||
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){key k;k+=k;}''')
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){key k;k+=k;}''')
|
||||||
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){string i;i++;}''')
|
self.assertRaises(EParseTypeMismatch, self.parser.parse, '''f(){string i;i++;}''')
|
||||||
|
@ -213,7 +215,7 @@ class Test02_Compiler(UnitTestCase):
|
||||||
'skippreproc']
|
'skippreproc']
|
||||||
))
|
))
|
||||||
print self.parser.scopeindex
|
print self.parser.scopeindex
|
||||||
self.assertRaises(EParseUnexpected, self.parser.PopScope)
|
#self.assertRaises(EParseUnexpected, self.parser.PopScope)
|
||||||
|
|
||||||
self.assertEqual(fieldpos("a,b",",",3),-1)
|
self.assertEqual(fieldpos("a,b",",",3),-1)
|
||||||
self.assertEqual(self.outscript.Value2LSL(lslfuncs.Key(u'')), '((key)"")')
|
self.assertEqual(self.outscript.Value2LSL(lslfuncs.Key(u'')), '((key)"")')
|
||||||
|
@ -235,8 +237,11 @@ class Test03_Optimizer(UnitTestCase):
|
||||||
float g = f;
|
float g = f;
|
||||||
string s = "1" "2";
|
string s = "1" "2";
|
||||||
list L = [(key)""];
|
list L = [(key)""];
|
||||||
|
list L1 = L;
|
||||||
|
list L2 = [1,2,3,4,5,6.0];
|
||||||
|
list L3 = [];
|
||||||
vector v=<1,2,f>;
|
vector v=<1,2,f>;
|
||||||
float ffff2 = v.x; // This needs a bit of luck for coverage, as it's order-dependent.
|
float ffff2 = v.x;
|
||||||
vector vvvv = <1,2,llGetNumberOfSides()>;
|
vector vvvv = <1,2,llGetNumberOfSides()>;
|
||||||
float ffff=vvvv.x;
|
float ffff=vvvv.x;
|
||||||
vector vvvv2=vvvv;
|
vector vvvv2=vvvv;
|
||||||
|
@ -269,14 +274,14 @@ class Test03_Optimizer(UnitTestCase):
|
||||||
['explicitcast','extendedtypecast','extendedassignment',
|
['explicitcast','extendedtypecast','extendedassignment',
|
||||||
'extendedglobalexpr', 'allowmultistrings', 'allowkeyconcat']
|
'extendedglobalexpr', 'allowmultistrings', 'allowkeyconcat']
|
||||||
)
|
)
|
||||||
self.opt.optimize(p, self.parser.functions)
|
self.opt.optimize(p)
|
||||||
self.opt.optimize(p, self.parser.functions, ())
|
self.opt.optimize(p, ())
|
||||||
print self.outscript.output(p)
|
print self.outscript.output(p)
|
||||||
p = self.parser.parse('''string s = llUnescapeURL("%09");default{timer(){float f=llSqrt(-1);}}''',
|
p = self.parser.parse('''string s = llUnescapeURL("%09");default{timer(){float f=llSqrt(-1);}}''',
|
||||||
['explicitcast','extendedtypecast','extendedassignment',
|
['explicitcast','extendedtypecast','extendedassignment',
|
||||||
'extendedglobalexpr', 'allowmultistrings', 'allowkeyconcat']
|
'extendedglobalexpr', 'allowmultistrings', 'allowkeyconcat']
|
||||||
)
|
)
|
||||||
self.opt.optimize(p, self.parser.functions, ['optimize','foldtabs'])
|
self.opt.optimize(p, ['optimize','foldtabs'])
|
||||||
print self.outscript.output(p)
|
print self.outscript.output(p)
|
||||||
def test_regression(self):
|
def test_regression(self):
|
||||||
p = self.parser.parse('''
|
p = self.parser.parse('''
|
||||||
|
@ -284,7 +289,7 @@ class Test03_Optimizer(UnitTestCase):
|
||||||
x() { if (1) { string s = "x"; s = s + (string)a; } }
|
x() { if (1) { string s = "x"; s = s + (string)a; } }
|
||||||
default { timer() { } }
|
default { timer() { } }
|
||||||
''', ['extendedassignment'])
|
''', ['extendedassignment'])
|
||||||
self.opt.optimize(p, self.parser.functions)
|
self.opt.optimize(p)
|
||||||
self.outscript.output(p)
|
self.outscript.output(p)
|
||||||
p = self.parser.parse('''
|
p = self.parser.parse('''
|
||||||
key k = "blah";
|
key k = "blah";
|
||||||
|
@ -294,10 +299,11 @@ class Test03_Optimizer(UnitTestCase):
|
||||||
|
|
||||||
default{timer(){}}
|
default{timer(){}}
|
||||||
''', ['extendedassignment'])
|
''', ['extendedassignment'])
|
||||||
self.opt.optimize(p, self.parser.functions)
|
self.opt.optimize(p)
|
||||||
out = self.outscript.output(p)
|
out = self.outscript.output(p)
|
||||||
|
print out
|
||||||
self.assertEqual(out, 'key k = "blah";\nlist L = [k, "xxxx", 1.];\n'
|
self.assertEqual(out, 'key k = "blah";\nlist L = [k, "xxxx", 1.];\n'
|
||||||
'float f;\nvector v = <f, 3, 4>;\ndefault\n{\n timer()\n'
|
'float f;\nvector v = <0, 3, 4>;\ndefault\n{\n timer()\n'
|
||||||
' {\n }\n}\n')
|
' {\n }\n}\n')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue