2017-08-09 19:45:46 +02:00
# (C) Copyright 2015-2017 Sei Lisa. All rights reserved.
2015-03-05 23:18:41 +01:00
#
# This file is part of LSL PyOptimizer.
#
# LSL PyOptimizer is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# LSL PyOptimizer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LSL PyOptimizer. If not, see <http://www.gnu.org/licenses/>.
# Constant folding and simplification of expressions and statements.
2014-08-13 14:02:05 +02:00
2016-12-21 06:01:03 +01:00
import lslcommon
2014-08-13 14:02:05 +02:00
import lslfuncs
2015-03-06 23:26:58 +01:00
import math
2014-08-13 14:02:05 +02:00
from lslparse import warning
2015-04-21 04:56:09 +02:00
from lslfuncparams import OptimizeParams
2014-08-13 14:02:05 +02:00
2014-08-13 14:19:58 +02:00
class foldconst ( object ) :
2014-08-13 14:02:05 +02:00
2015-03-27 02:12:32 +01:00
def isLocalVar ( self , node ) :
name = node [ ' name ' ]
scope = node [ ' scope ' ]
return self . symtab [ scope ] [ name ] [ ' Kind ' ] == ' v ' \
and ' Loc ' not in self . symtab [ scope ] [ name ]
2017-01-05 02:55:06 +01:00
2014-08-13 14:02:05 +02:00
def FoldAndRemoveEmptyStmts ( self , lst ) :
""" Utility function for elimination of useless expressions in FOR """
idx = 0
while idx < len ( lst ) :
self . FoldTree ( lst , idx )
self . FoldStmt ( lst , idx )
# If eliminated, it must be totally removed. A ';' won't do.
if lst [ idx ] [ ' nt ' ] == ' ; ' :
del lst [ idx ]
else :
idx + = 1
2017-01-05 02:55:06 +01:00
def DoesSomething ( self , node ) :
""" Tell if a subtree does something or is just empty statements
( a pure combination of ' ; ' and ' {} ' )
2017-02-04 04:08:18 +01:00
Not to be confused with lslparse . does_something which includes labels ,
and applies to a block ' s statement list, not to a node.
2017-01-05 02:55:06 +01:00
"""
if node [ ' nt ' ] != ' ; ' :
if node [ ' nt ' ] == ' {} ' :
2017-02-04 04:08:18 +01:00
for subnode in node [ ' ch ' ] :
if self . DoesSomething ( subnode ) :
return True
2017-01-05 02:55:06 +01:00
else :
return True
return False
2014-08-13 14:02:05 +02:00
def FoldStmt ( self , parent , index ) :
""" Simplify a statement. """
node = parent [ index ]
if node [ ' nt ' ] == ' EXPR ' :
node = node [ ' ch ' ] [ 0 ]
# If the statement is side-effect-free, remove it as it does nothing.
if ' SEF ' in node :
# Side-effect free means that a statement does nothing except
# wasting CPU, and can thus be removed without affecting the
# program. But side effect freedom is propagated from the
# constituents of the statement, e.g. function calls in expressions
# or substatements in FOR, or even individual variables.
#
# Many library functions like llSameGroup or llGetVel() are
# side-effect free. Many other functions like llSleep() or
# llSetScale() are not. User functions may or may not be.
#
# Assignments do have side effects, except those of the form x = x.
# Pre- and post-increment and decrement also have side effects.
# Type casts do not add side effects. Neither do binary operators.
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
return
# Post-increments take more space than pre-increments.
if node [ ' nt ' ] in ( ' V++ ' , ' V-- ' ) :
node [ ' nt ' ] = ' ++V ' if node [ ' nt ' ] == ' V++ ' else ' --V ' ;
2015-03-13 23:48:24 +01:00
# Function calls are SEF if both the function and the args are SEF.
# If the statement is a function call and the function is marked as SEF
# at this point, it means the arguments are not SEF. Replace the node
# in that case with a block.
if node [ ' nt ' ] == ' FNCALL ' and ' SEF ' in self . symtab [ 0 ] [ node [ ' name ' ] ] and ' Loc ' in self . symtab [ 0 ] [ node [ ' name ' ] ] :
parent [ index ] = { ' nt ' : ' {} ' , ' t ' : None , ' ch ' :
[ { ' nt ' : ' EXPR ' , ' t ' : x [ ' t ' ] , ' ch ' : [ x ] } for x in node [ ' ch ' ] ] }
self . FoldTree ( parent , index )
return
2015-02-28 05:14:52 +01:00
def IsBool ( self , node ) :
""" Some operators return 0 or 1, and that allows simplification of
2015-03-02 23:28:14 +01:00
boolean expressions . This function returns whether we know for sure
that the result is boolean .
"""
2015-03-28 23:49:10 +01:00
nt = node [ ' nt ' ]
if nt in ( ' < ' , ' ! ' , ' > ' , ' <= ' , ' >= ' , ' == ' , ' || ' , ' && ' ) \
or nt == ' != ' and node [ ' ch ' ] [ 0 ] [ ' t ' ] != ' list ' \
2017-08-02 01:05:17 +02:00
or nt == ' & ' and ( self . IsBool ( node [ ' ch ' ] [ 0 ] ) or self . IsBool ( node [ ' ch ' ] [ 1 ] ) ) \
or nt in ( ' | ' , ' ^ ' , ' * ' ) and self . IsBool ( node [ ' ch ' ] [ 0 ] ) and self . IsBool ( node [ ' ch ' ] [ 1 ] ) \
2015-03-28 23:49:10 +01:00
or nt == ' CONST ' and node [ ' t ' ] == ' integer ' and node [ ' value ' ] in ( 0 , 1 ) :
return True
if nt == ' FNCALL ' :
sym = self . symtab [ 0 ] [ node [ ' name ' ] ]
2015-06-17 02:51:46 +02:00
if sym [ ' Type ' ] == ' integer ' and ' min ' in sym and ' max ' in sym \
2015-03-28 23:49:10 +01:00
and sym [ ' min ' ] == 0 and sym [ ' max ' ] == 1 :
return True
return False
2015-02-28 05:14:52 +01:00
2015-03-29 03:47:54 +02:00
def FoldCond ( self , parent , index , ParentIsNegation = False ) :
2014-08-13 14:02:05 +02:00
""" When we know that the parent is interested only in the truth value
of the node , we can perform further optimizations . This function deals
with them .
"""
node = parent [ index ]
2015-02-28 05:14:52 +01:00
nt = node [ ' nt ' ]
if nt in ( ' CONST ' , ' IDENT ' , ' FLD ' ) :
2014-08-13 14:02:05 +02:00
if node [ ' nt ' ] == ' CONST ' :
node [ ' t ' ] = ' integer '
2017-01-04 00:41:08 +01:00
node [ ' value ' ] = 1 if lslfuncs . cond ( node [ ' value ' ] ) else 0
2014-08-13 14:02:05 +02:00
return # Nothing to do if it's already simplified.
2015-02-28 05:14:52 +01:00
child = node [ ' ch ' ] if ' ch ' in node else None
2017-08-15 13:07:30 +02:00
if nt == ' ! ' :
self . FoldCond ( child , 0 , True )
if child [ 0 ] [ ' nt ' ] == ' ! ' :
# bool(!!a) equals bool(a)
parent [ index ] = child [ 0 ] [ ' ch ' ] [ 0 ]
return
2015-02-28 05:14:52 +01:00
if nt == ' NEG ' :
# bool(-a) equals bool(a)
parent [ index ] = child [ 0 ]
2017-08-15 13:07:30 +02:00
self . FoldCond ( parent , index , ParentIsNegation )
2015-02-28 05:14:52 +01:00
return
if nt in self . binary_ops and child [ 0 ] [ ' t ' ] == child [ 1 ] [ ' t ' ] == ' integer ' :
if nt == ' != ' :
if child [ 0 ] [ ' nt ' ] == ' CONST ' and child [ 0 ] [ ' value ' ] == 1 \
or child [ 1 ] [ ' nt ' ] == ' CONST ' and child [ 1 ] [ ' value ' ] == 1 :
# a != 1 -> a - 1 (which FoldTree will transform to ~-a)
node [ ' nt ' ] = ' - '
else :
# This converts != to ^; FoldTree will simplify ^-1 to ~
# and optimize out ^0.
node [ ' nt ' ] = ' ^ '
self . FoldTree ( parent , index )
2015-03-28 23:47:45 +01:00
return
if nt == ' == ' :
2015-02-28 05:14:52 +01:00
if child [ 0 ] [ ' nt ' ] == ' CONST ' and - 1 < = child [ 0 ] [ ' value ' ] < = 1 \
or child [ 1 ] [ ' nt ' ] == ' CONST ' and - 1 < = child [ 1 ] [ ' value ' ] < = 1 :
# Transform a==b into !(a-b) if either a or b are in [-1, 1]
parent [ index ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ node ] }
node [ ' nt ' ] = ' - '
self . FoldTree ( parent , index )
2015-03-28 23:47:45 +01:00
return
2015-02-28 05:14:52 +01:00
2015-02-28 18:30:04 +01:00
if nt == ' | ' :
2015-03-28 20:16:06 +01:00
# In a boolean context, the operands count as booleans.
self . FoldCond ( child , 0 )
self . FoldCond ( child , 1 )
2015-03-29 00:16:17 +01:00
# Deal with operands in any order
2015-02-28 18:30:04 +01:00
a , b = 0 , 1
2015-03-28 23:49:10 +01:00
# Put constant in child[b] if present
if child [ b ] [ ' nt ' ] != ' CONST ' :
2015-02-28 18:30:04 +01:00
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' and child [ b ] [ ' value ' ] and ' SEF ' in child [ a ] :
parent [ index ] = child [ b ]
2015-03-28 23:49:10 +01:00
child [ b ] [ ' value ' ] = - 1
2015-02-28 18:30:04 +01:00
return
2015-03-28 23:49:10 +01:00
2015-03-29 03:47:54 +02:00
# Check if the operands are a negation ('!') or can be inverted
# without adding more than 1 byte and are boolean.
# We only support '<' and some cases of '&' (are there more?)
2015-03-29 05:43:38 +02:00
Invertible = [ False , False ]
for a in ( 0 , 1 ) :
Invertible [ a ] = child [ a ] [ ' nt ' ] == ' ! '
if child [ a ] [ ' nt ' ] == ' < ' \
and child [ a ] [ ' ch ' ] [ 0 ] [ ' t ' ] == child [ a ] [ ' ch ' ] [ 1 ] [ ' t ' ] == ' integer ' :
if child [ a ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' \
and child [ a ] [ ' ch ' ] [ 0 ] [ ' value ' ] != 2147483647 \
or child [ a ] [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' \
and child [ a ] [ ' ch ' ] [ 1 ] [ ' value ' ] != int ( - 2147483648 ) :
Invertible [ a ] = True
# Deal with our optimization of a<0 -> a&0x80000000 (see below)
if child [ a ] [ ' nt ' ] == ' & ' and (
child [ a ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' and child [ a ] [ ' ch ' ] [ 0 ] [ ' value ' ] == int ( - 2147483648 )
or child [ a ] [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' and child [ a ] [ ' ch ' ] [ 1 ] [ ' value ' ] == int ( - 2147483648 )
) :
Invertible [ a ] | = ParentIsNegation
if ( Invertible [ 0 ] or Invertible [ 1 ] ) and ParentIsNegation :
# !(!a|b) -> a&-!b or a&!b
2015-04-21 04:56:09 +02:00
# This deals with the part after the first !, transforming
# it into (!a|!!b) so that the outer node can optimize the
# negated version to a simple &.
2015-03-29 05:43:38 +02:00
for a in ( 0 , 1 ) :
if not Invertible [ a ] :
child [ a ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' ,
' ch ' : [ { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ a ] ] } ]
}
Invertible [ a ] = True
if Invertible [ 0 ] and Invertible [ 1 ] :
2015-03-29 03:47:54 +02:00
# Both operands are negated, or negable.
# Make them a negation if they aren't already.
for a in ( 0 , 1 ) :
if child [ a ] [ ' nt ' ] == ' < ' :
if child [ a ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' :
child [ a ] [ ' ch ' ] [ 0 ] [ ' value ' ] + = 1
else :
child [ a ] [ ' ch ' ] [ 1 ] [ ' value ' ] - = 1
child [ a ] [ ' ch ' ] [ 0 ] , child [ a ] [ ' ch ' ] [ 1 ] = \
child [ a ] [ ' ch ' ] [ 1 ] , child [ a ] [ ' ch ' ] [ 0 ]
child [ a ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ a ] ] }
elif child [ a ] [ ' nt ' ] == ' & ' :
child [ a ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' ,
' ch ' : [ { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ a ] ] } ]
}
self . FoldTree ( child [ a ] [ ' ch ' ] , 0 )
2015-03-28 23:49:10 +01:00
# If they are boolean, the expression can be turned into
# !(a&b) which hopefully will have a ! uptree if it came
2015-03-29 00:16:17 +01:00
# from a '&&' and cancel out (if not, we still remove one
2015-03-28 23:49:10 +01:00
# ! so it's good). If one is bool, another transformation
# can be performed: !nonbool|!bool -> !(nonbool&-bool)
# which is still a gain.
2015-03-29 03:47:54 +02:00
# Deal with operands in any order
2015-03-28 23:49:10 +01:00
a , b = 0 , 1
# Put the bool in child[b]['ch'][0].
if not self . IsBool ( child [ b ] [ ' ch ' ] [ 0 ] ) :
a , b = 1 , 0
if self . IsBool ( child [ b ] [ ' ch ' ] [ 0 ] ) :
if not self . IsBool ( child [ a ] [ ' ch ' ] [ 0 ] ) :
2015-03-29 03:47:54 +02:00
child [ b ] [ ' ch ' ] [ 0 ] = { ' nt ' : ' NEG ' , ' t ' : ' integer ' ,
2015-03-28 23:49:10 +01:00
' ch ' : [ child [ b ] [ ' ch ' ] [ 0 ] ] }
node = parent [ index ] = { ' nt ' : ' ! ' , ' t ' : ' integer ' ,
' ch ' : [ { ' nt ' : ' & ' , ' t ' : ' integer ' ,
' ch ' : [ child [ 0 ] [ ' ch ' ] [ 0 ] ,
child [ 1 ] [ ' ch ' ] [ 0 ] ]
} ]
}
# Fold the node we've just synthesized
# (this deals with SEF)
self . FoldTree ( parent , index )
2015-02-28 05:14:52 +01:00
2015-03-29 00:16:17 +01:00
return
2015-03-29 03:47:54 +02:00
# This optimization turns out to be counter-productive for some
# common cases, e.g. it turns i >= 0 into !(i & 0x80000000)
# instead of the more optimal (integer)-1 < i. So we revert it
# where appropriate (in FoldTree, case '!', and above, case '|').
2015-03-29 00:16:17 +01:00
if nt == ' < ' :
if child [ 1 ] [ ' nt ' ] == ' CONST ' and child [ 1 ] [ ' value ' ] == 0 :
nt = node [ ' nt ' ] = ' & '
2015-03-29 03:47:54 +02:00
child [ 1 ] [ ' value ' ] = int ( - 2147483648 )
2015-03-29 00:16:17 +01:00
# Fall through to check & 0x80000000
2015-03-03 17:49:57 +01:00
2015-03-29 00:16:17 +01:00
if nt == ' & ' :
# Deal with operands in any order
a , b = 0 , 1
# Put constant in child[b], if present
if child [ b ] [ ' nt ' ] != ' CONST ' :
a , b = 1 , 0
2015-03-29 03:47:54 +02:00
if child [ b ] [ ' nt ' ] == ' CONST ' and child [ b ] [ ' value ' ] == int ( - 2147483648 ) \
2015-03-29 00:16:17 +01:00
and child [ a ] [ ' nt ' ] == ' FNCALL ' :
sym = self . symtab [ 0 ] [ child [ a ] [ ' name ' ] ]
if ' min ' in sym and sym [ ' min ' ] == - 1 :
node = parent [ index ] = { ' nt ' : ' ~ ' , ' t ' : ' integer ' ,
' ch ' : [ child [ a ] ] }
2015-03-29 03:47:54 +02:00
self . FoldTree ( parent , index )
2015-03-29 00:16:17 +01:00
return
2014-08-13 14:02:05 +02:00
def CopyNode ( self , node ) :
2015-03-29 00:16:17 +01:00
''' This is mainly for simple_expr so no need to go deeper than 1 level
'''
2014-08-13 14:02:05 +02:00
ret = node . copy ( )
if ' ch ' in ret :
new = [ ]
for subnode in ret [ ' ch ' ] :
new . append ( self . CopyNode ( subnode ) )
ret [ ' ch ' ] = new
return ret
def FoldTree ( self , parent , index ) :
""" Recursively traverse the tree to fold constants, changing it in
place .
Also optimizes away IF , WHILE , etc .
"""
node = parent [ index ]
nt = node [ ' nt ' ]
child = node [ ' ch ' ] if ' ch ' in node else None
if nt == ' CONST ' :
# Job already done. But mark as side-effect free.
node [ ' SEF ' ] = True
return
if nt == ' CAST ' :
self . FoldTree ( child , 0 )
if ' SEF ' in child [ 0 ] :
node [ ' SEF ' ] = True
2015-03-13 22:13:00 +01:00
if child [ 0 ] [ ' nt ' ] == ' CONST ' :
2014-08-13 14:02:05 +02:00
# Enable key constants. We'll typecast them back on output, but
# this enables some optimizations.
#if node['t'] != 'key': # key constants not possible
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] , ' SEF ' : True ,
' value ' : lslfuncs . typecast (
child [ 0 ] [ ' value ' ] , self . LSL2PythonType [ node [ ' t ' ] ] ) }
return
if nt == ' NEG ' :
self . FoldTree ( child , 0 )
2015-03-29 06:09:26 +02:00
if child [ 0 ] [ ' nt ' ] == ' + ' and ( child [ 0 ] [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' NEG '
or child [ 0 ] [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' NEG ' ) :
node = parent [ index ] = child [ 0 ]
child = node [ ' ch ' ]
for a in ( 0 , 1 ) :
if child [ a ] [ ' nt ' ] == ' NEG ' :
child [ a ] = child [ a ] [ ' ch ' ] [ 0 ]
else :
child [ a ] = { ' nt ' : ' NEG ' , ' t ' : child [ a ] [ ' t ' ] , ' ch ' : [ child [ a ] ] }
self . FoldTree ( child , a )
return
2014-08-13 14:02:05 +02:00
if child [ 0 ] [ ' nt ' ] == ' NEG ' :
2015-03-28 23:47:45 +01:00
# Double negation: - - expr -> expr
2014-12-13 14:16:28 +01:00
node = parent [ index ] = child [ 0 ] [ ' ch ' ] [ 0 ]
child = node [ ' ch ' ] if ' ch ' in node else None
2014-08-13 14:02:05 +02:00
elif child [ 0 ] [ ' nt ' ] == ' CONST ' :
node = parent [ index ] = child [ 0 ]
node [ ' value ' ] = lslfuncs . neg ( node [ ' value ' ] )
2014-12-13 14:16:28 +01:00
child = None
2014-08-13 14:02:05 +02:00
elif ' SEF ' in child [ 0 ] :
# propagate Side Effect Free flag
node [ ' SEF ' ] = True
2014-12-13 14:16:28 +01:00
if child and node [ ' nt ' ] == ' NEG ' and child [ 0 ] [ ' nt ' ] == ' ~ ' :
track = child [ 0 ] [ ' ch ' ] [ 0 ]
const = 1
while track [ ' nt ' ] == ' NEG ' and track [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' ~ ' :
const + = 1
track = track [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ]
if const > 2 :
# -~-~-~expr -> expr+3
node = { ' nt ' : ' CONST ' , ' t ' : ' integer ' , ' SEF ' : True , ' value ' : const }
node = { ' nt ' : ' + ' , ' t ' : ' integer ' , ' ch ' : [ node , track ] }
2015-02-28 00:43:26 +01:00
if ' SEF ' in track :
node [ ' SEF ' ] = True
parent [ index ] = node
2014-12-13 14:16:28 +01:00
2014-08-13 14:02:05 +02:00
return
if nt == ' ! ' :
self . FoldTree ( child , 0 )
2015-03-29 03:47:54 +02:00
self . FoldCond ( child , 0 , True )
2015-04-21 04:56:09 +02:00
# !! does *not* cancel out (unless in cond)
2014-08-13 14:02:05 +02:00
subexpr = child [ 0 ]
2015-03-29 03:47:54 +02:00
snt = subexpr [ ' nt ' ]
2017-08-14 21:41:08 +02:00
if snt == ' FNCALL ' and subexpr [ ' name ' ] == ' llStringLength ' :
# !llStringLength(expr) -> expr == ""
parent [ index ] = { ' nt ' : ' == ' , ' t ' : ' integer ' ,
' ch ' : [ subexpr [ ' ch ' ] [ 0 ] ,
{ ' nt ' : ' CONST ' , ' t ' : ' string ' ,
' value ' : u " " } ] }
# new node is SEF if the argument to llStringLength is
if ' SEF ' in subexpr [ ' ch ' ] [ 0 ] :
parent [ index ] [ ' SEF ' ] = True
return
2014-08-13 14:02:05 +02:00
if ' SEF ' in subexpr :
node [ ' SEF ' ] = True
if subexpr [ ' nt ' ] == ' CONST ' :
node = parent [ index ] = subexpr
node [ ' value ' ] = int ( not node [ ' value ' ] )
2015-03-29 03:47:54 +02:00
return
if snt == ' < ' :
lop = subexpr [ ' ch ' ] [ 0 ]
rop = subexpr [ ' ch ' ] [ 1 ]
if lop [ ' nt ' ] == ' CONST ' and lop [ ' t ' ] == rop [ ' t ' ] == ' integer ' \
and lop [ ' value ' ] < 2147483647 :
lop [ ' value ' ] + = 1
subexpr [ ' ch ' ] [ 0 ] , subexpr [ ' ch ' ] [ 1 ] = subexpr [ ' ch ' ] [ 1 ] , subexpr [ ' ch ' ] [ 0 ]
parent [ index ] = subexpr # remove !
return
if rop [ ' nt ' ] == ' CONST ' and lop [ ' t ' ] == rop [ ' t ' ] == ' integer ' \
and rop [ ' value ' ] > int ( - 2147483648 ) :
rop [ ' value ' ] - = 1
subexpr [ ' ch ' ] [ 0 ] , subexpr [ ' ch ' ] [ 1 ] = subexpr [ ' ch ' ] [ 1 ] , subexpr [ ' ch ' ] [ 0 ]
parent [ index ] = subexpr # remove !
return
if snt == ' & ' :
a , b = 0 , 1
if subexpr [ ' ch ' ] [ b ] [ ' nt ' ] != ' CONST ' :
a , b = 1 , 0
if subexpr [ ' ch ' ] [ b ] [ ' nt ' ] == ' CONST ' and subexpr [ ' ch ' ] [ b ] [ ' value ' ] == int ( - 2147483648 ) :
# !(i & 0x80000000) -> -1 < i (because one of our
# optimizations can be counter-productive, see FoldCond)
subexpr [ ' nt ' ] = ' < '
subexpr [ ' ch ' ] [ b ] [ ' value ' ] = - 1
subexpr [ ' ch ' ] = [ subexpr [ ' ch ' ] [ b ] , subexpr [ ' ch ' ] [ a ] ]
parent [ index ] = subexpr
return
2014-08-13 14:02:05 +02:00
return
if nt == ' ~ ' :
self . FoldTree ( child , 0 )
subexpr = child [ 0 ]
if ' SEF ' in subexpr :
node [ ' SEF ' ] = True
if subexpr [ ' nt ' ] == ' ~ ' :
# Double negation: ~~expr
parent [ index ] = subexpr [ ' ch ' ] [ 0 ]
elif subexpr [ ' nt ' ] == ' CONST ' :
node = parent [ index ] = child [ 0 ]
node [ ' value ' ] = ~ node [ ' value ' ]
return
if nt in self . binary_ops :
# RTL evaluation
self . FoldTree ( child , 1 )
self . FoldTree ( child , 0 )
if ' SEF ' in child [ 0 ] and ' SEF ' in child [ 1 ] :
# Propagate SEF flag if both sides are side-effect free.
node [ ' SEF ' ] = True
2015-02-12 07:47:35 +01:00
optype = node [ ' t ' ]
lval = child [ 0 ]
ltype = lval [ ' t ' ]
lnt = lval [ ' nt ' ]
rval = child [ 1 ]
rtype = rval [ ' t ' ]
rnt = rval [ ' nt ' ]
if lnt == rnt == ' CONST ' :
op1 = lval [ ' value ' ]
op2 = rval [ ' value ' ]
2014-08-13 14:02:05 +02:00
if nt == ' + ' :
2015-02-28 20:37:18 +01:00
if ltype == rtype == ' string ' and not self . addstrings :
return
result = lslfuncs . add ( op1 , op2 )
2014-08-13 14:02:05 +02:00
elif nt == ' - ' :
result = lslfuncs . sub ( op1 , op2 )
elif nt == ' * ' :
result = lslfuncs . mul ( op1 , op2 )
elif nt == ' / ' :
2015-02-11 05:43:13 +01:00
try :
result = lslfuncs . div ( op1 , op2 )
except lslfuncs . ELSLMathError :
return
2014-08-13 14:02:05 +02:00
elif nt == ' % ' :
2015-02-11 05:43:13 +01:00
try :
result = lslfuncs . mod ( op1 , op2 )
except lslfuncs . ELSLMathError :
return
2014-08-13 14:02:05 +02:00
elif nt == ' << ' :
result = lslfuncs . S32 ( op1 << ( op2 & 31 ) )
elif nt == ' >> ' :
result = lslfuncs . S32 ( op1 >> ( op2 & 31 ) )
elif nt == ' == ' or nt == ' != ' :
result = lslfuncs . compare ( op1 , op2 , Eq = ( nt == ' == ' ) )
elif nt in ( ' < ' , ' <= ' , ' > ' , ' >= ' ) :
if nt in ( ' > ' , ' <= ' ) :
result = lslfuncs . less ( op2 , op1 )
else :
result = lslfuncs . less ( op1 , op2 )
if nt in ( ' >= ' , ' <= ' ) :
result = 1 - result
elif nt == ' | ' :
result = op1 | op2
elif nt == ' ^ ' :
result = op1 ^ op2
elif nt == ' & ' :
result = op1 & op2
elif nt == ' || ' :
result = int ( bool ( op1 ) or bool ( op2 ) )
elif nt == ' && ' :
result = int ( bool ( op1 ) and bool ( op2 ) )
else :
assert False , ' Internal error: Operator not found: ' + nt # pragma: no cover
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] , ' SEF ' : True , ' value ' : result }
return
# Simplifications for particular operands
if nt == ' - ' :
if optype in ( ' vector ' , ' rotation ' ) :
if lnt == ' CONST ' and all ( component == 0 for component in lval [ ' value ' ] ) :
# Change <0,0,0[,0]>-expr -> -expr
parent [ index ] = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ rval ] }
if ' SEF ' in rval :
parent [ index ] [ ' SEF ' ] = True
elif rnt == ' CONST ' and all ( component == 0 for component in rval [ ' value ' ] ) :
# Change expr-<0,0,0[,0]> -> expr
parent [ index ] = lval
return
# Change - to + - for int/float
nt = node [ ' nt ' ] = ' + '
if child [ 1 ] [ ' nt ' ] == ' CONST ' :
rval [ ' value ' ] = lslfuncs . neg ( rval [ ' value ' ] )
else :
rnt = ' NEG '
RSEF = ' SEF ' in rval
rval = child [ 1 ] = { ' nt ' : rnt , ' t ' : rval [ ' t ' ] , ' ch ' : [ rval ] }
2015-03-29 06:09:26 +02:00
self . FoldTree ( child , 1 )
2014-08-13 14:02:05 +02:00
if RSEF :
rval [ ' SEF ' ] = True
# rtype unchanged
# Fall through to simplify it as '+'
if nt == ' + ' :
# Tough one. Remove neutral elements for the diverse types,
# and more.
2014-08-19 19:45:47 +02:00
2015-02-28 20:37:18 +01:00
# Addition of integers, strings, and lists is associative.
2015-02-12 07:47:35 +01:00
# Addition of floats, vectors and rotations would be, except
# for FP precision.
2015-02-27 19:18:28 +01:00
# TODO: associative addition of lists
# Associative lists are trickier, because unlike the others,
# the types of the operands may not be lists
# so e.g. list+(integer+integer) != (list+integer)+integer.
2015-02-28 20:37:18 +01:00
if optype == ' integer ' or optype == ' string ' and self . addstrings :
2015-02-12 07:47:35 +01:00
if lnt == ' + ' and rnt == ' CONST ' and lval [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' :
# (var + ct1) + ct2 -> var + (ct1 + ct2)
child [ 1 ] = { ' nt ' : ' + ' , ' t ' : optype , ' ch ' : [ lval [ ' ch ' ] [ 1 ] , rval ] , ' SEF ' : True }
lval = child [ 0 ] = lval [ ' ch ' ] [ 0 ]
lnt = lval [ ' nt ' ]
ltype = lval [ ' t ' ]
rtype = optype
# Fold the RHS again now that we have it constant
self . FoldTree ( child , 1 )
rval = child [ 1 ]
rnt = rval [ ' nt ' ]
2014-08-13 14:02:05 +02:00
if optype == ' list ' and not ( ltype == rtype == ' list ' ) :
2014-08-17 16:31:46 +02:00
if lnt == ' CONST ' and not lval [ ' value ' ] :
# [] + nonlist -> (list)nonlist
parent [ index ] = self . Cast ( rval , optype )
2017-08-14 21:23:09 +02:00
# node is SEF if rval is
parent [ index ] [ ' SEF ' ] = ' SEF ' in rval
2014-08-13 14:02:05 +02:00
return
if optype in ( ' vector ' , ' rotation ' ) :
# not much to do with vectors or quaternions either
2015-02-28 21:46:19 +01:00
if lnt == ' CONST ' and all ( x == 0 for x in lval [ ' value ' ] ) :
2014-08-13 14:02:05 +02:00
# Change <0,0,0[,0]>+expr -> expr
parent [ index ] = rval
2015-02-28 21:46:19 +01:00
elif rnt == ' CONST ' and all ( x == 0 for x in rval [ ' value ' ] ) :
2014-08-13 14:02:05 +02:00
# Change expr+<0,0,0[,0]> -> expr
parent [ index ] = lval
return
# Can't be key, as no combo of addition operands returns key
2014-08-17 16:31:46 +02:00
# All these types evaluate to boolean False when they are
2014-08-13 14:02:05 +02:00
# the neutral addition element.
if optype in ( ' string ' , ' float ' , ' list ' ) :
if lnt == ' CONST ' and not lval [ ' value ' ] :
2014-08-17 16:31:46 +02:00
# 0. + expr -> expr
2014-08-13 14:02:05 +02:00
# "" + expr -> expr
# [] + expr -> expr
parent [ index ] = self . Cast ( rval , optype )
2017-08-14 21:23:09 +02:00
# node is SEF if rval is
parent [ index ] [ ' SEF ' ] = ' SEF ' in rval
2017-08-09 16:41:36 +02:00
return
if rnt == ' CONST ' and not rval [ ' value ' ] :
2014-08-17 16:31:46 +02:00
# expr + 0. -> expr
2014-08-13 14:02:05 +02:00
# expr + "" -> expr
# expr + [] -> expr
parent [ index ] = self . Cast ( lval , optype )
2017-08-14 21:23:09 +02:00
# node is SEF if lval is
parent [ index ] [ ' SEF ' ] = ' SEF ' in lval
2017-08-09 16:41:36 +02:00
return
if ltype == rtype == ' list ' :
if ( rnt == ' LIST ' and len ( rval [ ' ch ' ] ) == 1
or rnt == ' CAST ' ) :
# list + (list)element -> list + element
# list + [element] -> list + element
while True :
# Remove nested typecasts: (list)(list)x -> x
rval = parent [ index ] [ ' ch ' ] [ 1 ] = rval [ ' ch ' ] [ 0 ]
if rval [ ' nt ' ] != ' CAST ' or rval [ ' t ' ] != ' list ' :
break
return
if rnt == ' CONST ' and len ( rval [ ' value ' ] ) == 1 :
# list + [constant] -> list + constant
rval [ ' value ' ] = rval [ ' value ' ] [ 0 ]
rtype = rval [ ' t ' ] = self . PythonType2LSL [ type ( rval [ ' value ' ] ) ]
return
if ( lnt == ' LIST ' and len ( lval [ ' ch ' ] ) == 1
or lnt == ' CAST ' ) :
# (list)element + list -> element + list
# [element] + list -> element + list
while True :
# Remove nested typecasts: (list)(list)x -> x
lval = parent [ index ] [ ' ch ' ] [ 0 ] = lval [ ' ch ' ] [ 0 ]
if lval [ ' nt ' ] != ' CAST ' or lval [ ' t ' ] != ' list ' :
break
return
if lnt == ' CONST ' and len ( lval [ ' value ' ] ) == 1 :
# [constant] + list -> constant + list
lval [ ' value ' ] = lval [ ' value ' ] [ 0 ]
ltype = lval [ ' t ' ] = self . PythonType2LSL [ type ( lval [ ' value ' ] ) ]
return
2014-08-13 14:02:05 +02:00
return
# Must be two integers. This allows for a number of
# optimizations. First the most obvious ones.
if lnt == ' CONST ' and lval [ ' value ' ] == 0 :
parent [ index ] = rval
return
if rnt == ' CONST ' and rval [ ' value ' ] == 0 :
parent [ index ] = lval
return
if lnt != ' CONST ' != rnt :
# Neither is const. Two chances to optimize.
# 1. -expr + -expr -> -(expr + expr) (saves 1 byte)
# 2. lvalue + -lvalue -> 0
# There may be other possibilities for optimization,
# e.g. (type)ident + -(type)ident but we only do lvalues
# here. Note these are integers, no NaN involved.
# TODO: Compare the subtrees if they are SEF. If they are
# the same subtree, they can cancel out.
if lnt == rnt == ' NEG ' :
node = { ' nt ' : ' + ' , ' t ' : optype , ' ch ' : [ lval [ ' ch ' ] [ 0 ] , rval [ ' ch ' ] [ 0 ] ] }
SEF = ' SEF ' in lval [ ' ch ' ] [ 0 ] and ' SEF ' in rval [ ' ch ' ] [ 0 ]
if SEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
if SEF :
node [ ' SEF ' ] = True
parent [ index ] = node
return
if lnt == ' NEG ' :
# Swap to treat always as expr + -expr for simplicity.
lnt , lval , rnt , rval = rnt , rval , lnt , lval
if lnt == ' IDENT ' and rnt == ' NEG ' and rval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' IDENT ' \
and lval [ ' name ' ] == rval [ ' ch ' ] [ 0 ] [ ' name ' ] :
# Replace with 0
parent [ index ] = { ' nt ' : ' CONST ' , ' SEF ' : True , ' t ' : optype , ' value ' : 0 }
return
2014-08-19 19:45:47 +02:00
if lnt == ' + ' and ( lval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST '
2014-12-13 14:16:28 +01:00
or lval [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' ) :
# We have expr + const + const or const + expr + const.
2014-08-19 19:45:47 +02:00
# Addition of integers mod 2^32 is associative and
# commutative, so constants can be merged.
if lval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST ' :
rval [ ' value ' ] = lslfuncs . S32 ( rval [ ' value ' ] + lval [ ' ch ' ] [ 0 ] [ ' value ' ] )
lval = child [ 0 ] = lval [ ' ch ' ] [ 1 ]
else :
rval [ ' value ' ] = lslfuncs . S32 ( rval [ ' value ' ] + lval [ ' ch ' ] [ 1 ] [ ' value ' ] )
lval = child [ 0 ] = lval [ ' ch ' ] [ 0 ]
lnt = lval [ ' nt ' ]
2015-02-28 00:43:26 +01:00
if rnt == ' + ' and ( rval [ ' ch ' ] [ 0 ] [ ' nt ' ] == ' CONST '
or rval [ ' ch ' ] [ 1 ] [ ' nt ' ] == ' CONST ' ) :
2014-12-13 14:16:28 +01:00
# const + (expr + const) or const + (const + expr)
# same as above, join them
2015-02-28 20:39:23 +01:00
# FIXME: Isn't this covered by the associative sum above?
2014-12-13 14:16:28 +01:00
2015-03-05 06:20:11 +01:00
pass # TODO: implement const + (expr + const) or const + (const + expr)
2014-12-13 14:16:28 +01:00
2014-08-13 14:02:05 +02:00
if rnt == ' CONST ' :
# Swap the vars to deal with const in lval always
lval , lnt , rval , rnt = rval , rnt , lval , lnt
RSEF = ' SEF ' in rval
2015-02-27 20:05:14 +01:00
if lval [ ' value ' ] == - 1 or lval [ ' value ' ] == - 2 :
if rnt == ' NEG ' : # Cancel the NEG
2015-02-28 00:43:26 +01:00
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : rval [ ' ch ' ] }
2015-02-27 20:05:14 +01:00
if RSEF :
node [ ' SEF ' ] = True
else : # Add the NEG
2015-02-27 20:12:17 +01:00
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ rval ] }
2015-02-27 20:05:14 +01:00
if RSEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
if lval [ ' value ' ] == - 2 :
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
2014-08-13 14:02:05 +02:00
if RSEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
parent [ index ] = node
return
2015-02-27 20:05:14 +01:00
if lval [ ' value ' ] == 1 or lval [ ' value ' ] == 2 :
if rnt == ' ~ ' : # Cancel the ~
2015-02-27 20:12:17 +01:00
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : rval [ ' ch ' ] }
2014-08-13 14:02:05 +02:00
if RSEF :
node [ ' SEF ' ] = True
2015-02-27 20:05:14 +01:00
else :
2015-02-28 00:43:26 +01:00
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ rval ] }
2014-08-13 14:02:05 +02:00
if RSEF :
node [ ' SEF ' ] = True
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
2015-02-27 20:05:14 +01:00
if lval [ ' value ' ] == 2 :
2014-08-13 14:02:05 +02:00
node = { ' nt ' : ' ~ ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
2015-02-27 20:05:14 +01:00
node = { ' nt ' : ' NEG ' , ' t ' : optype , ' ch ' : [ node ] }
if RSEF :
node [ ' SEF ' ] = True
2014-08-13 14:02:05 +02:00
parent [ index ] = node
return
# More than 2 becomes counter-productive.
return
2014-08-19 19:45:47 +02:00
if nt == ' << ' and child [ 1 ] [ ' nt ' ] == ' CONST ' :
2014-08-13 14:02:05 +02:00
# Transforming << into multiply saves some bytes.
if child [ 1 ] [ ' value ' ] & 31 :
# x << 3 --> x * 8
2015-02-28 05:14:52 +01:00
# we have {<<, something, {CONST n}}
# we transform it into {*, something, {CONST n}}
2015-03-28 14:43:11 +01:00
nt = node [ ' nt ' ] = ' * '
2014-08-13 14:02:05 +02:00
child [ 1 ] [ ' value ' ] = 1 << ( child [ 1 ] [ ' value ' ] & 31 )
2015-02-28 05:14:52 +01:00
# Fall through to optimize product
2014-08-13 14:02:05 +02:00
else : # x << 0 --> x
parent [ index ] = child [ 0 ]
2015-02-28 05:14:52 +01:00
return
2015-02-28 18:30:04 +01:00
if nt == ' % ' \
and child [ 1 ] [ ' nt ' ] == ' CONST ' \
and child [ 1 ] [ ' t ' ] == ' integer ' \
and abs ( child [ 1 ] [ ' value ' ] ) == 1 :
# a%1 -> a&0
# a%-1 -> a&0
# (SEF analysis performed below)
nt = node [ ' nt ' ] = ' & '
child [ 1 ] [ ' value ' ] = 0
if nt in ( ' * ' , ' / ' ) :
2015-03-03 01:46:53 +01:00
# Extract signs outside
if child [ 0 ] [ ' nt ' ] == ' NEG ' or child [ 1 ] [ ' nt ' ] == ' NEG ' :
a , b = 0 , 1
if child [ b ] [ ' nt ' ] == ' NEG ' :
a , b = 1 , 0
child [ a ] = child [ a ] [ ' ch ' ] [ 0 ]
parent [ index ] = node = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ node ] }
if ' SEF ' in node [ ' ch ' ] [ 0 ] :
node [ ' SEF ' ] = True
# Fold the new expression
self . FoldTree ( parent , index )
return
2015-03-02 23:28:14 +01:00
2015-02-28 18:30:04 +01:00
# Deal with operands in any order
a , b = 0 , 1
if child [ a ] [ ' nt ' ] == ' CONST ' and child [ a ] [ ' t ' ] in ( ' float ' , ' integer ' ) :
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' :
val = child [ b ] [ ' value ' ]
# Optimize out signs if possible.
# Note that (-intvar)*floatconst needs cornermath because
# -intvar could equal intvar if intvar = -2147483648,
# so the sign is a no-op and pushing it to floatconst would
# make the result be different.
if child [ a ] [ ' nt ' ] == ' NEG ' \
and ( self . cornermath
or child [ a ] [ ' t ' ] != ' integer '
or child [ b ] [ ' t ' ] != ' float '
) :
# Expression is of the form (-float)*const or (-float)/const or const/(-float)
2015-03-29 03:47:54 +02:00
if val != int ( - 2147483648 ) or child [ a ] [ ' t ' ] == ' integer ' : # can't be optimized otherwise
2015-02-28 18:30:04 +01:00
child [ a ] = child [ a ] [ ' ch ' ] [ 0 ] # remove NEG
child [ b ] [ ' value ' ] = val = - val
# Five optimizations corresponding to -2, -1, 0, 1, 2
# for product, and two for division:
# expr * 1 -> expr
# expr * 0 -> 0 if side-effect free
# expr * -1 -> -expr
2015-03-03 02:16:00 +01:00
# ident * 2 -> ident + ident (only if ident is local)
# ident * -2 -> -(ident + ident) (only if ident is local)
2015-02-28 18:30:04 +01:00
# expr/1 -> expr
# expr/-1 -> -expr
if nt == ' * ' and child [ b ] [ ' t ' ] in ( ' float ' , ' integer ' ) \
and val in ( - 2 , - 1 , 0 , 1 , 2 ) \
or nt == ' / ' and b == 1 and val in ( - 1 , 1 ) :
if val == 1 :
parent [ index ] = child [ a ]
return
if val == 0 :
if ' SEF ' in child [ a ] :
parent [ index ] = child [ b ]
return
if val == - 1 :
# Note 0.0*-1 equals -0.0 in LSL, so this is safe
node = parent [ index ] = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ child [ a ] ] }
if ' SEF ' in child [ a ] :
node [ ' SEF ' ] = True
return
# only -2, 2 remain
2015-03-27 02:12:32 +01:00
if child [ a ] [ ' nt ' ] == ' IDENT ' and self . isLocalVar ( child [ a ] ) :
2015-03-03 00:49:14 +01:00
child [ b ] = child [ a ] . copy ( )
node [ ' nt ' ] = ' + '
if val == - 2 :
parent [ index ] = { ' nt ' : ' NEG ' , ' t ' : node [ ' t ' ] , ' ch ' : [ node ] }
if ' SEF ' in node :
parent [ index ] [ ' SEF ' ] = True
2015-02-28 18:30:04 +01:00
return
return
2015-02-28 05:14:52 +01:00
2015-03-28 20:23:47 +01:00
if nt in ( ' <= ' , ' >= ' ) or nt == ' != ' and child [ 0 ] [ ' t ' ] != ' list ' :
# Except for list != list, all these comparisons are compiled
# as !(a>b) etc. so we transform them here in order to reduce
# the number of cases to check.
# a<=b --> !(a>b); a>=b --> !(a<b); a!=b --> !(a==b)
2015-03-03 02:16:00 +01:00
node [ ' nt ' ] = { ' <= ' : ' > ' , ' >= ' : ' < ' , ' != ' : ' == ' } [ nt ]
2015-04-21 06:09:57 +02:00
parent [ index ] = { ' nt ' : ' ! ' , ' t ' : node [ ' t ' ] , ' ch ' : [ node ] }
2015-03-29 03:47:54 +02:00
self . FoldTree ( parent , index )
2015-04-21 06:09:57 +02:00
return
2015-03-28 20:23:47 +01:00
if nt == ' > ' :
# Invert the inequalities to avoid doubling the cases to check.
# a>b --> b<a
nt = node [ ' nt ' ] = ' < '
child [ 1 ] , child [ 0 ] = child [ 0 ] , child [ 1 ]
# fall through to check for '<'
if nt == ' < ' :
# Convert 2147483647<i and i<-2147483648 to i&0
if child [ 0 ] [ ' t ' ] == child [ 1 ] [ ' t ' ] == ' integer ' \
and ( child [ 0 ] [ ' nt ' ] == ' CONST ' and child [ 0 ] [ ' value ' ] == 2147483647
2015-03-29 03:47:54 +02:00
or child [ 1 ] [ ' nt ' ] == ' CONST ' and child [ 1 ] [ ' value ' ] == int ( - 2147483648 ) ) :
2015-03-28 20:23:47 +01:00
a , b = 0 , 1
# Put the constant in child[b]
if child [ a ] [ ' nt ' ] == ' CONST ' :
a , b = 1 , 0
2015-03-03 02:16:00 +01:00
nt = node [ ' nt ' ] = ' & '
child [ b ] [ ' value ' ] = 0
# fall through to check for '&'
2015-03-28 20:23:47 +01:00
else :
return
2015-02-28 20:39:23 +01:00
2015-02-28 18:30:04 +01:00
if nt in ( ' & ' , ' | ' ) :
# Deal with operands in any order
a , b = 0 , 1
2015-03-28 20:23:47 +01:00
# Put constant in child[b]
if child [ b ] [ ' nt ' ] != ' CONST ' :
2015-02-28 18:30:04 +01:00
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' :
val = child [ b ] [ ' value ' ]
2017-08-02 01:40:48 +02:00
if nt == ' | ' and val == 0 or nt == ' & ' and ( val == - 1 or val == 1 and self . IsBool ( child [ a ] ) ) :
2015-02-28 18:30:04 +01:00
# a|0 -> a
# a&-1 -> a
2017-08-02 01:40:48 +02:00
# a&1 -> a if a is boolean
2015-02-28 18:30:04 +01:00
parent [ index ] = child [ a ]
return
2017-08-02 01:40:48 +02:00
if nt == ' | ' and ( val == - 1 or val == 1 and self . IsBool ( child [ a ] ) ) or nt == ' & ' and val == 0 :
2015-02-28 18:30:04 +01:00
# a|-1 -> -1 if a is SEF
2017-08-02 01:40:48 +02:00
# a|1 -> 1 if a is bool and SEF
2015-02-28 18:30:04 +01:00
# a&0 -> 0 if a is SEF
if ' SEF ' in child [ a ] :
parent [ index ] = child [ b ]
2015-03-28 20:23:47 +01:00
return
2015-02-28 05:14:52 +01:00
if nt == ' ^ ' :
2015-02-28 18:30:04 +01:00
a , b = 0 , 1
if child [ a ] [ ' nt ' ] == ' CONST ' :
a , b = 1 , 0
if child [ b ] [ ' nt ' ] == ' CONST ' and child [ b ] [ ' value ' ] in ( 0 , - 1 ) :
if child [ b ] [ ' value ' ] == 0 :
parent [ index ] = child [ a ]
2015-02-28 05:14:52 +01:00
else :
node [ ' nt ' ] = ' ~ '
2015-02-28 18:30:04 +01:00
node [ ' ch ' ] = [ child [ a ] ]
2015-02-28 05:14:52 +01:00
return
if nt == ' && ' or nt == ' || ' :
2015-02-28 18:30:04 +01:00
SEF = ' SEF ' in node
2015-02-28 05:14:52 +01:00
if nt == ' || ' :
parent [ index ] = node = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' | ' , ' t ' : ' integer ' , ' ch ' : [ child [ 0 ] , child [ 1 ] ] }
] } ] }
if SEF :
node [ ' SEF ' ] = node [ ' ch ' ] [ 0 ] [ ' SEF ' ] = node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] [ ' SEF ' ] = True
else :
parent [ index ] = node = { ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' | ' , ' t ' : ' integer ' , ' ch ' : [
{ ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ 0 ] ] }
,
{ ' nt ' : ' ! ' , ' t ' : ' integer ' , ' ch ' : [ child [ 1 ] ] }
] } ] }
if SEF :
node [ ' SEF ' ] = node [ ' ch ' ] [ 0 ] [ ' SEF ' ] = True
2015-02-28 18:30:04 +01:00
if ' SEF ' in node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] :
node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 0 ] [ ' SEF ' ] = True
if ' SEF ' in node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 1 ] [ ' ch ' ] [ 0 ] :
node [ ' ch ' ] [ 0 ] [ ' ch ' ] [ 1 ] [ ' SEF ' ] = True
# Make another pass with the substitution
2015-02-28 05:14:52 +01:00
self . FoldTree ( parent , index )
return
2014-08-13 14:02:05 +02:00
return
if nt in self . assign_ops :
# Transform the whole thing into a regular assignment, as there are
# no gains and it simplifies the optimization.
# An assignment has no side effects only if it's of the form x = x.
if nt != ' = ' :
# Replace the node with the expression alone
2015-02-28 00:43:26 +01:00
# e.g. a += b -> a + b
2014-08-13 14:02:05 +02:00
node [ ' nt ' ] = nt [ : - 1 ]
2015-02-28 00:43:26 +01:00
# Linden Craziness: int *= float; is valid (but no other
# int op= float is). It's actually performed as
# i = (integer)(i + (f));
# This breaks equivalence of x op= y as x = x op (y) so we add
# the explicit type cast here.
2014-08-13 14:02:05 +02:00
if nt == ' *= ' and child [ 0 ] [ ' t ' ] == ' integer ' and child [ 1 ] [ ' t ' ] == ' float ' :
node [ ' t ' ] = ' float ' # Addition shall return float.
node = self . Cast ( node , ' integer ' )
# And wrap it in an assignment.
child = [ child [ 0 ] . copy ( ) , node ]
node = parent [ index ] = { ' nt ' : ' = ' , ' t ' : child [ 0 ] [ ' t ' ] , ' ch ' : child }
# We have a regular assignment either way now. Simplify the RHS.
self . FoldTree ( node [ ' ch ' ] , 1 )
if child [ 0 ] [ ' nt ' ] == child [ 1 ] [ ' nt ' ] == ' IDENT ' \
and child [ 1 ] [ ' name ' ] == child [ 0 ] [ ' name ' ] \
and child [ 1 ] [ ' scope ' ] == child [ 0 ] [ ' scope ' ] \
or child [ 0 ] [ ' nt ' ] == child [ 1 ] [ ' nt ' ] == ' FLD ' \
and child [ 1 ] [ ' ch ' ] [ 0 ] [ ' name ' ] == child [ 0 ] [ ' ch ' ] [ 0 ] [ ' name ' ] \
and child [ 1 ] [ ' ch ' ] [ 0 ] [ ' scope ' ] == child [ 0 ] [ ' ch ' ] [ 0 ] [ ' scope ' ] \
and child [ 1 ] [ ' fld ' ] == child [ 0 ] [ ' fld ' ] :
node [ ' SEF ' ] = True
self . FoldStmt ( parent , index )
return
if nt == ' IDENT ' or nt == ' FLD ' :
node [ ' SEF ' ] = True
if self . globalmode :
ident = child [ 0 ] if nt == ' FLD ' else node
# Resolve constant values so they can be optimized
sym = self . symtab [ ident [ ' scope ' ] ] [ ident [ ' name ' ] ]
defn = self . tree [ sym [ ' Loc ' ] ]
assert defn [ ' name ' ] == ident [ ' name ' ]
# Assume we already were there
if ' ch ' in defn :
val = defn [ ' ch ' ] [ 0 ]
if val [ ' nt ' ] != ' CONST ' or ident [ ' t ' ] == ' key ' :
return
val = val . copy ( )
else :
val = { ' nt ' : ' CONST ' , ' t ' : defn [ ' t ' ] ,
' value ' : self . DefaultValues [ defn [ ' t ' ] ] }
if nt == ' FLD ' :
val = { ' nt ' : ' CONST ' , ' t ' : ' float ' ,
' value ' : val [ ' value ' ] [ ' xyzs ' . index ( node [ ' fld ' ] ) ] }
parent [ index ] = val
return
if nt == ' FNCALL ' :
SEFargs = True
CONSTargs = True
for idx in xrange ( len ( child ) - 1 , - 1 , - 1 ) :
self . FoldTree ( child , idx )
# Function is not SEF if any argument is not SEF
if ' SEF ' not in child [ idx ] :
SEFargs = False
# Function is not a constant if any argument is not a constant
if child [ idx ] [ ' nt ' ] != ' CONST ' :
CONSTargs = False
2016-12-21 06:01:03 +01:00
sym = self . symtab [ 0 ] [ node [ ' name ' ] ]
OptimizeParams ( node , sym )
2017-01-08 05:53:10 +01:00
if ' Fn ' in sym and ( ' SEF ' in sym and sym [ ' SEF ' ] or lslcommon . IsCalc ) :
# It's side-effect free if the children are and the function
# is marked as SEF.
2014-08-13 14:02:05 +02:00
if SEFargs :
node [ ' SEF ' ] = True
if CONSTargs :
# Call it
2016-12-21 06:01:03 +01:00
fn = sym [ ' Fn ' ]
2016-12-22 01:24:44 +01:00
args = [ arg [ ' value ' ] for arg in child ]
argtypes = sym [ ' ParamTypes ' ]
assert ( len ( args ) == len ( argtypes ) )
for argnum in range ( len ( args ) ) :
# Adapt types of params
if argtypes [ argnum ] == ' string ' :
args [ argnum ] = lslfuncs . fs ( args [ argnum ] )
elif argtypes [ argnum ] == ' key ' :
args [ argnum ] = lslfuncs . fk ( args [ argnum ] )
elif argtypes [ argnum ] == ' float ' :
args [ argnum ] = lslfuncs . ff ( args [ argnum ] )
elif argtypes [ argnum ] == ' vector ' :
args [ argnum ] = lslfuncs . v2f ( args [ argnum ] )
elif argtypes [ argnum ] == ' quaternion ' :
args [ argnum ] = lslfuncs . q2f ( args [ argnum ] )
elif argtypes [ argnum ] == ' list ' :
# ensure vectors and quaternions passed to
# functions have only float components
assert type ( args [ argnum ] ) == list
# make a shallow copy
args [ argnum ] = args [ argnum ] [ : ]
for i in range ( len ( args [ argnum ] ) ) :
if type ( args [ argnum ] [ i ] ) == lslcommon . Quaternion :
args [ argnum ] [ i ] = lslfuncs . q2f ( args [ argnum ] [ i ] )
elif type ( args [ argnum ] [ i ] ) == lslcommon . Vector :
args [ argnum ] [ i ] = lslfuncs . v2f ( args [ argnum ] [ i ] )
del argtypes
2015-02-11 05:43:13 +01:00
try :
if node [ ' name ' ] [ : 10 ] == ' llDetected ' :
2016-12-21 06:01:03 +01:00
value = fn ( * args , event = self . CurEvent )
2015-02-11 05:43:13 +01:00
else :
2016-12-21 06:01:03 +01:00
value = fn ( * args )
2015-02-11 05:43:13 +01:00
except lslfuncs . ELSLCantCompute :
# Don't transform the tree if function is not computable
2016-12-25 00:25:49 +01:00
return
2016-12-22 01:24:44 +01:00
del args
if not self . foldtabs :
generatesTabs = (
isinstance ( value , unicode ) and ' \t ' in value
or type ( value ) == list and any ( isinstance ( x , unicode ) and ' \t ' in x for x in value )
)
if generatesTabs :
if self . warntabs :
warning ( u " Can ' t optimize call to %s because it would generate a tab character (you can force the optimization with the ' foldtabs ' option, or disable this warning by disabling the ' warntabs ' option). " % node [ ' name ' ] . decode ( ' utf8 ' ) )
return
2017-01-17 01:00:04 +01:00
parent [ index ] = { ' nt ' : ' CONST ' , ' t ' : node [ ' t ' ] , ' value ' : value , ' SEF ' : True }
2017-01-25 19:22:36 +01:00
elif self . optlistlength and node [ ' name ' ] == ' llGetListLength ' :
2015-07-13 07:16:32 +02:00
# Convert llGetListLength(expr) to (expr != [])
2014-08-13 14:02:05 +02:00
node = { ' nt ' : ' CONST ' , ' t ' : ' list ' , ' value ' : [ ] }
2017-08-15 12:52:48 +02:00
parent [ index ] = node = { ' nt ' : ' != ' , ' t ' : ' integer ' ,
2017-06-15 21:11:54 +02:00
' ch ' : [ child [ 0 ] , node ] }
2017-08-14 21:23:09 +02:00
# SEF if the list is
node [ ' SEF ' ] = ' SEF ' in child [ 0 ]
2017-05-31 14:39:42 +02:00
elif ( node [ ' name ' ] == ' llDumpList2String '
and 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 '
2017-06-15 21:11:54 +02:00
del child [ 1 ]
2017-05-31 14:39:42 +02:00
del node [ ' name ' ]
2014-08-13 14:02:05 +02:00
elif SEFargs and ' SEF ' in self . symtab [ 0 ] [ node [ ' name ' ] ] :
# The function is marked as SEF in the symbol table, and the
# arguments are all side-effect-free. The result is SEF.
node [ ' SEF ' ] = True
return
if nt == ' PRINT ' :
self . FoldTree ( child , 0 )
# PRINT is considered to have side effects. If it's there, assume
# there's a reason.
return
if nt == ' EXPR ' :
self . FoldTree ( child , 0 )
if ' SEF ' in child [ 0 ] :
node [ ' SEF ' ] = True
return
if nt == ' FNDEF ' :
2015-02-11 05:43:13 +01:00
# used when folding llDetected* function calls
if ' scope ' in node :
# function definition
self . CurEvent = None
else :
# event definition
self . CurEvent = node [ ' name ' ]
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , 0 )
2015-03-13 23:46:23 +01:00
# TODO: This works, but analysis of code paths is DCR's thing
# and this is incomplete, e.g. x(){{return;}} is not detected.
while ' ch ' in child [ 0 ] and child [ 0 ] [ ' ch ' ] :
last = child [ 0 ] [ ' ch ' ] [ - 1 ]
if last [ ' nt ' ] != ' RETURN ' or ' ch ' in last :
break
del child [ 0 ] [ ' ch ' ] [ - 1 ]
2014-08-13 14:02:05 +02:00
if ' SEF ' in child [ 0 ] :
node [ ' SEF ' ] = True
if node [ ' name ' ] in self . symtab [ 0 ] :
# Mark the symbol table entry if it's not an event.
self . symtab [ 0 ] [ node [ ' name ' ] ] [ ' SEF ' ] = True
return
if nt in ( ' VECTOR ' , ' ROTATION ' , ' LIST ' ) :
isconst = True
issef = True
2017-01-23 01:18:50 +01:00
for idx in xrange ( len ( child ) ) :
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , idx )
if child [ idx ] [ ' nt ' ] != ' CONST ' :
isconst = False
if ' SEF ' not in child [ idx ] :
issef = False
if isconst :
value = [ elem [ ' value ' ] for elem in child ]
if nt == ' VECTOR ' :
value = lslfuncs . Vector ( [ lslfuncs . ff ( x ) for x in value ] )
elif nt == ' ROTATION ' :
value = lslfuncs . Quaternion ( [ lslfuncs . ff ( x ) for x in value ] )
2015-03-13 22:13:00 +01:00
parent [ index ] = { ' nt ' : ' CONST ' , ' SEF ' : True , ' t ' : node [ ' t ' ] ,
' value ' : value }
return
2014-08-13 14:02:05 +02:00
if issef :
node [ ' SEF ' ] = True
return
if nt == ' STDEF ' :
for idx in xrange ( len ( child ) ) :
self . FoldTree ( child , idx )
return
if nt == ' {} ' :
idx = 0
issef = True
while idx < len ( child ) :
self . FoldTree ( child , idx )
self . FoldStmt ( child , idx )
if ' SEF ' not in child [ idx ] :
issef = False
if child [ idx ] [ ' nt ' ] == ' ; ' \
2015-03-13 23:45:56 +01:00
or child [ idx ] [ ' nt ' ] == ' {} ' and not child [ idx ] [ ' ch ' ] :
2014-08-13 14:02:05 +02:00
del child [ idx ]
else :
if ' StSw ' in child [ idx ] :
node [ ' StSw ' ] = True
idx + = 1
if issef :
node [ ' SEF ' ] = True
return
if nt == ' IF ' :
2015-03-05 06:20:11 +01:00
# TODO: Swap IF/ELSE if both present and cond starts with !
2014-08-13 14:02:05 +02:00
self . FoldTree ( child , 0 )
self . FoldCond ( child , 0 )
if child [ 0 ] [ ' nt ' ] == ' CONST ' :
# We might be able to remove one of the branches.
if child [ 0 ] [ ' value ' ] :
self . FoldTree ( child , 1 )
# If it has a state switch, the if() must be preserved
# (but the else branch may be removed).
if ' StSw ' in child [ 1 ] :
# TODO: Get rid of StSw craziness and make another pass
# to put them under conditionals if present (if bald
# state switches are present, it means they are the
# result of optimization so they must be wrapped in an
# IF statement). The current approach leaves unnecessary
# IFs behind.
2017-01-05 02:55:06 +01:00
if len ( child ) == 3 and child [ 2 ] [ ' nt ' ] != ' @ ' :
2014-08-13 14:02:05 +02:00
del child [ 2 ] # Delete ELSE if present
return
else :
self . FoldStmt ( child , 1 )
2017-01-05 02:55:06 +01:00
if len ( child ) == 3 and child [ 2 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as
# this statement, so it must be preserved just in
# case it's jumped to.
return
2014-08-13 14:02:05 +02:00
parent [ index ] = child [ 1 ]
return
elif len ( child ) == 3 :
self . FoldTree ( child , 2 )
self . FoldStmt ( child , 2 )
2017-01-05 02:55:06 +01:00
if child [ 1 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as this
# statement, so it must be preserved just in case it's
# jumped to.
if not self . DoesSomething ( child [ 2 ] ) :
del child [ 2 ]
return
2014-08-13 14:02:05 +02:00
parent [ index ] = child [ 2 ]
return
else :
# No ELSE branch, replace the statement with an empty one.
2017-01-05 02:55:06 +01:00
if child [ 1 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as this
# statement, so it must be preserved just in case it's
# jumped to.
parent [ index ] = child [ 1 ]
return
2014-08-13 14:02:05 +02:00
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
return
else :
self . FoldTree ( child , 1 )
self . FoldStmt ( child , 1 )
if len ( child ) > 2 :
self . FoldTree ( child , 2 )
self . FoldStmt ( child , 2 )
2017-01-05 02:55:06 +01:00
if not self . DoesSomething ( child [ 2 ] ) :
2014-08-13 14:02:05 +02:00
# no point in "... else ;" - remove else branch
del child [ 2 ]
if all ( ' SEF ' in subnode for subnode in child ) :
node [ ' SEF ' ] = True
return
if nt == ' WHILE ' :
# Loops are not considered side-effect free. If the expression is
# TRUE, it's definitely not SEF. If it's FALSE, it will be optimized
# anyway. Otherwise we just don't know if it may be infinite, even
# if every component is SEF.
self . FoldTree ( child , 0 )
self . FoldCond ( child , 0 )
if child [ 0 ] [ ' nt ' ] == ' CONST ' :
# See if the whole WHILE can be eliminated.
if child [ 0 ] [ ' value ' ] :
# Endless loop which must be kept.
# Recurse on the statement.
self . FoldTree ( child , 1 )
self . FoldStmt ( child , 1 )
else :
2017-01-05 02:55:06 +01:00
if child [ 1 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as this
# statement, so it must be preserved just in case it's
# jumped to.
parent [ index ] = child [ 1 ]
else :
# Whole statement can be removed.
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
2014-08-13 14:02:05 +02:00
return
else :
self . FoldTree ( child , 1 )
self . FoldStmt ( child , 1 )
return
if nt == ' DO ' :
self . FoldTree ( child , 0 ) # This one is always executed.
self . FoldStmt ( child , 0 )
self . FoldTree ( child , 1 )
self . FoldCond ( child , 1 )
# See if the latest part is a constant.
if child [ 1 ] [ ' nt ' ] == ' CONST ' :
if not child [ 1 ] [ ' value ' ] :
# Only one go. Replace with the statement(s).
parent [ index ] = child [ 0 ]
return
if nt == ' FOR ' :
assert child [ 0 ] [ ' nt ' ] == ' EXPRLIST '
assert child [ 2 ] [ ' nt ' ] == ' EXPRLIST '
self . FoldAndRemoveEmptyStmts ( child [ 0 ] [ ' ch ' ] )
self . FoldTree ( child , 1 ) # Condition.
self . FoldCond ( child , 1 )
if child [ 1 ] [ ' nt ' ] == ' CONST ' :
# FOR is delicate. It can have multiple expressions at start.
# And if there is more than one, these expressions will need a
# new block, which means new scope, which is dangerous.
# They are expressions, no declarations or labels allowed, thus
2017-01-04 00:41:08 +01:00
# no new identifiers may be created in the new scope, but it
# still feels dodgy.
2014-08-13 14:02:05 +02:00
if child [ 1 ] [ ' value ' ] :
# Endless loop. Traverse the loop and the iterator.
self . FoldTree ( child , 3 )
self . FoldStmt ( child , 3 )
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
else :
# Convert expression list to code block.
exprlist = [ ]
for expr in child [ 0 ] [ ' ch ' ] :
# Fold into expression statements.
exprlist . append ( { ' nt ' : ' EXPR ' , ' t ' : expr [ ' t ' ] , ' ch ' : [ expr ] } )
2017-01-05 02:55:06 +01:00
if ( exprlist or child [ 2 ] [ ' ch ' ] ) and child [ 3 ] [ ' nt ' ] == ' @ ' :
# Corner case. We can't optimize this to one single
# statement, so we leave it as-is.
self . FoldTree ( child , 3 )
self . FoldStmt ( child , 3 )
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
return
2014-08-13 14:02:05 +02:00
# returns type None, as FOR does
if exprlist :
# We're in the case where there are expressions. If any
# remain, they are not SEF (or they would have been
# removed earlier) so don't mark this node as SEF.
parent [ index ] = { ' nt ' : ' {} ' , ' t ' : None , ' ch ' : exprlist }
else :
2017-01-05 02:55:06 +01:00
if child [ 3 ] [ ' nt ' ] == ' @ ' :
# Corner case. The label is in the same scope as
# this statement, so it must be preserved. Also,
# jumping inside the loop would execute the
# iterator, so we fold it.
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
if not child [ 2 ] [ ' ch ' ] :
# if there's something in the 2nd list,
# preserve the whole statement, otherwise
# replace it with the label
parent [ index ] = child [ 3 ]
else :
parent [ index ] = { ' nt ' : ' ; ' , ' t ' : None , ' SEF ' : True }
2014-08-13 14:02:05 +02:00
return
else :
self . FoldTree ( child , 3 )
self . FoldStmt ( child , 3 )
self . FoldAndRemoveEmptyStmts ( child [ 2 ] [ ' ch ' ] )
return
if nt == ' RETURN ' :
if child :
self . FoldTree ( child , 0 )
return
if nt == ' DECL ' :
if child :
# Check if child is a simple_expr. If it is, then we keep the
# original attached to the folded node to use it in the output.
if child [ 0 ] . pop ( ' Simple ' , False ) :
orig = self . CopyNode ( child [ 0 ] )
self . FoldTree ( child , 0 )
child [ 0 ] [ ' orig ' ] = orig
else :
self . FoldTree ( child , 0 )
# Remove assignment if integer zero.
if node [ ' t ' ] == ' integer ' and child [ 0 ] [ ' nt ' ] == ' CONST ' \
and not child [ 0 ] [ ' value ' ] :
del node [ ' ch ' ]
child = None
return
else :
# Add assignment if vector, rotation or float.
if node [ ' t ' ] in ( ' float ' , ' vector ' , ' rotation ' ) :
typ = node [ ' t ' ]
node [ ' ch ' ] = [ { ' nt ' : ' CONST ' , ' t ' : typ , ' SEF ' : True ,
' value ' : 0.0 if typ == ' float ' else
lslfuncs . ZERO_VECTOR if typ == ' vector ' else
lslfuncs . ZERO_ROTATION } ]
# Declarations always have side effects.
return
if nt == ' STSW ' :
# State switch always has side effects.
node [ ' StSw ' ] = True
return
2015-03-13 06:38:35 +01:00
if nt == ' SUBIDX ' :
# Recurse to every child. It's SEF if all children are.
idx = 0
issef = True
while idx < len ( child ) :
self . FoldTree ( child , idx )
if ' SEF ' not in child [ idx ] :
issef = False
idx + = 1
if issef :
node [ ' SEF ' ] = True
return
2014-08-13 14:02:05 +02:00
if nt == ' ; ' :
node [ ' SEF ' ] = True
return
2015-03-03 17:59:51 +01:00
if nt in ( ' JUMP ' , ' @ ' , ' V++ ' , ' V-- ' , ' --V ' , ' ++V ' , ' LAMBDA ' ) :
# Except LAMBDA, these all have side effects, as in, can't be
# eliminated as statements.
# LAMBDA can't be eliminated without scrolling Loc's.
2014-08-13 14:02:05 +02:00
return
assert False , ' Internal error: This should not happen, node type = ' \
+ nt # pragma: no cover
2015-03-06 23:26:58 +01:00
def IsValidGlobalIdOrConst ( self , node ) :
2016-01-06 01:21:04 +01:00
# nan can't be represented as a simple constant; all others are valid
2015-03-06 23:26:58 +01:00
return not ( node [ ' nt ' ] == ' CONST ' and node [ ' t ' ] == ' float '
2016-01-06 01:21:04 +01:00
and math . isnan ( node [ ' value ' ] ) )
2015-03-06 23:26:58 +01:00
2014-08-13 14:02:05 +02:00
def IsValidGlobalConstant ( self , decl ) :
if ' ch ' not in decl :
return True
expr = decl [ ' ch ' ] [ 0 ]
if expr [ ' nt ' ] in ( ' CONST ' , ' IDENT ' ) :
2015-03-06 23:26:58 +01:00
return self . IsValidGlobalIdOrConst ( expr )
2014-08-13 14:02:05 +02:00
if expr [ ' nt ' ] not in ( ' VECTOR ' , ' ROTATION ' , ' LIST ' ) :
return False
2015-03-06 23:26:58 +01:00
return all ( elem [ ' nt ' ] in ( ' CONST ' , ' IDENT ' )
and self . IsValidGlobalIdOrConst ( elem )
for elem in expr [ ' ch ' ] )
2014-08-13 14:02:05 +02:00
2015-03-06 23:26:58 +01:00
def FoldScript ( self , warningpass = True ) :
2014-08-13 14:02:05 +02:00
""" Optimize the symbolic table symtab in place. Requires a table of
predefined functions for folding constants .
"""
self . globalmode = False
2014-08-13 14:19:58 +02:00
tree = self . tree
2015-02-11 05:43:13 +01:00
self . CurEvent = None
2014-08-13 14:19:58 +02:00
2014-08-13 14:02:05 +02:00
# Constant folding pass. It does some other optimizations along the way.
for idx in xrange ( len ( tree ) ) :
if tree [ idx ] [ ' nt ' ] == ' DECL ' :
self . globalmode = True
self . FoldTree ( tree , idx )
self . globalmode = False
2015-03-06 23:26:58 +01:00
if warningpass and not self . IsValidGlobalConstant ( tree [ idx ] ) :
2016-12-20 19:22:48 +01:00
warning ( u " Expression in globals doesn ' t resolve to a simple constant. " )
2014-08-13 14:02:05 +02:00
else :
self . FoldTree ( tree , idx )