mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
Multi-commit:
- Fix a bunch of bugs found during the debut of the LSL calculator. - Add infrastructure for functions to be able to produce a result or not depending on arguments. Fixes the llBase64ToInteger/llXorBase64/llXorBase64StringsCorrect cases where they are not deterministic, and allows for the addition of some extra functions whose value can be determined in some cases (e.g. llDetectedType(-1) is always 0). Added several such functions in a new module. - Add the constant folding option to the help and the default options.
This commit is contained in:
parent
716be215f2
commit
db862bb4a6
6 changed files with 175 additions and 28 deletions
|
@ -54,11 +54,14 @@ class ELSLTypeMismatch(Exception):
|
|||
|
||||
class ELSLMathError(Exception):
|
||||
def __init__(self):
|
||||
super(self.ELSLMathError, self).__init__("Math Error")
|
||||
super(ELSLMathError, self).__init__("Math Error")
|
||||
|
||||
class ELSLInvalidType(Exception):
|
||||
def __init__(self):
|
||||
super(self.ELSLInvalidType, self).__init__("Internal error: Invalid type")
|
||||
super(ELSLInvalidType, self).__init__("Internal error: Invalid type")
|
||||
|
||||
class ELSLCantCompute(Exception):
|
||||
pass
|
||||
|
||||
# LSL types are translated to Python types as follows:
|
||||
# * LSL string -> Python unicode
|
||||
|
@ -604,12 +607,14 @@ def div(a, b, f32=True):
|
|||
return Vector(F32((a[0]/b, a[1]/b, a[2]/b), f32))
|
||||
if tb == Quaternion: # division by a rotation is multiplication by the conjugate of the rotation
|
||||
# defer the remaining type checks to mul()
|
||||
return mul(a, (-b[0],-b[1],-b[2],b[3]), f32)
|
||||
return mul(a, Quaternion((-b[0],-b[1],-b[2],b[3])), f32)
|
||||
raise ELSLTypeMismatch
|
||||
|
||||
def mod(a, b, f32=True):
|
||||
# defined only for integers and vectors
|
||||
if type(a) == type(b) == int:
|
||||
if b == 0:
|
||||
raise ELSLMathError
|
||||
if a < 0:
|
||||
return int(-((-a) % abs(b)))
|
||||
return int(a % abs(b))
|
||||
|
@ -634,7 +639,7 @@ def compare(a, b, Eq = True):
|
|||
else:
|
||||
ret = a == b
|
||||
return int(ret) if Eq else 1-ret
|
||||
if ta in (unicode, Key) and tb in (unicode, key):
|
||||
if ta in (unicode, Key) and tb in (unicode, Key):
|
||||
ret = 0 if a == b else 1 if a > b or not lslcommon.LSO else -1
|
||||
return int(not ret) if Eq else ret
|
||||
if ta == tb in (Vector, Quaternion):
|
||||
|
@ -779,15 +784,19 @@ def llAxisAngle2Rot(axis, angle):
|
|||
s = math.sin(ff(angle)*0.5)
|
||||
return Quaternion(F32((axis[0]*s, axis[1]*s, axis[2]*s, c)))
|
||||
|
||||
# NOTE: This one does not always return the same value in LSL, but no one should depend
|
||||
# on the garbage bytes returned. We implement it deterministically.
|
||||
# NOTE: This one does not always return the same value in LSL. When it isn't
|
||||
# deterministic, it raises ELSLCantCompute.
|
||||
def llBase64ToInteger(s):
|
||||
assert isstring(s)
|
||||
if len(s) > 8:
|
||||
return 0
|
||||
s = b64_re.match(s).group()
|
||||
i = len(s)
|
||||
s = (b64decode(s + u'='*(-i & 3)) + b'\0\0\0\0')[:4] # actually the last 3 bytes should be garbage
|
||||
s = b64decode(s + u'='*(-i & 3))
|
||||
if len(s) < 3:
|
||||
# not computable deterministically
|
||||
raise ELSLCantCompute
|
||||
s = (s + b'\0')[:4]
|
||||
i = ord(s[0]) if s[0] < b'\x80' else ord(s[0])-256
|
||||
return (i<<24)+(ord(s[1])<<16)+(ord(s[2])<<8)+ord(s[3])
|
||||
|
||||
|
@ -1040,7 +1049,7 @@ def llList2Vector(lst, pos):
|
|||
# The list should not contain integer vector components, but
|
||||
# we don't control that here. Instead we return the integer-less
|
||||
# vector when asked.
|
||||
return v2q(elem)
|
||||
return v2f(elem)
|
||||
except IndexError:
|
||||
pass
|
||||
return ZERO_VECTOR
|
||||
|
@ -1341,7 +1350,7 @@ def llRot2Angle(r):
|
|||
def llRot2Axis(r):
|
||||
assert isrotation(r)
|
||||
r = q2f(r)
|
||||
return llVecNorm((r[0], r[1], r[2]))
|
||||
return llVecNorm(Vector((r[0], r[1], r[2])))
|
||||
|
||||
def llRot2Euler(r):
|
||||
assert isrotation(r)
|
||||
|
@ -1553,7 +1562,11 @@ def llVecDist(v1, v2):
|
|||
assert isvector(v2)
|
||||
v1 = v2f(v1)
|
||||
v2 = v2f(v2)
|
||||
return llVecMag((v1[0]-v2[0],v1[1]-v2[1],v1[2]-v2[2]))
|
||||
# For improved accuracy, do the intermediate calcs as doubles
|
||||
vx = v1[0]-v2[0]
|
||||
vy = v1[1]-v2[1]
|
||||
vz = v1[2]-v2[2]
|
||||
return F32(math.sqrt(math.fsum((vx*vx, vy*vy, vz*vz))))
|
||||
|
||||
def llVecMag(v):
|
||||
assert isvector(v)
|
||||
|
@ -1568,9 +1581,6 @@ def llVecNorm(v, f32 = True):
|
|||
f = math.sqrt(math.fsum((v[0]*v[0], v[1]*v[1], v[2]*v[2])))
|
||||
return F32(Vector((v[0]/f,v[1]/f,v[2]/f)), f32)
|
||||
|
||||
# NOTE: llXorBase64 returns garbage bytes if the input xor string
|
||||
# starts with zero or one valid Base64 characters. We don't emulate that here;
|
||||
# our output is deterministic.
|
||||
def llXorBase64(s, xor):
|
||||
assert isstring(s)
|
||||
assert isstring(xor)
|
||||
|
@ -1586,9 +1596,10 @@ def llXorBase64(s, xor):
|
|||
L2 = len(xor)
|
||||
|
||||
if L2 == 0:
|
||||
# This is not accurate. This returns garbage (of undefined length) in LSL.
|
||||
# The first returned byte seems to be zero always though.
|
||||
xor = u'ABCD';
|
||||
# The input xor string starts with zero or one valid Base64 characters.
|
||||
# This produces garbage bytes (the first byte is zero though).
|
||||
# We don't produce a result in this case.
|
||||
raise ELSLCantCompute
|
||||
|
||||
s = b64decode(s + u'='*(-L1 & 3))
|
||||
xor = b64decode(xor + u'='*(-L2 & 3))
|
||||
|
@ -1670,9 +1681,10 @@ def llXorBase64StringsCorrect(s, xor):
|
|||
L2 = len(xor)
|
||||
|
||||
if L2 == 0:
|
||||
# This is not accurate. This returns garbage (of length 4?) in LSL.
|
||||
# The first returned byte seems to be zero always though.
|
||||
xor = u'ABCD'
|
||||
# The input xor string starts with zero or one valid Base64 characters.
|
||||
# This produces garbage bytes (the first byte is zero though).
|
||||
# We don't produce a result in this case.
|
||||
raise ELSLCantCompute
|
||||
|
||||
s = b64decode(s + u'='*(-L1 & 3))
|
||||
xor = b64decode(xor + u'='*(-L2 & 3)) + b'\x00'
|
||||
|
|
110
lslopt/lslextrafuncs.py
Normal file
110
lslopt/lslextrafuncs.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
from lslcommon import *
|
||||
from lslbasefuncs import ELSLCantCompute, isinteger, \
|
||||
isvector, NULL_KEY, ZERO_VECTOR, ZERO_ROTATION
|
||||
#isfloat, isstring, iskey, isrotation, islist
|
||||
|
||||
TouchEvents = ('touch', 'touch_start', 'touch_end')
|
||||
DetectionEvents = ('touch', 'touch_start', 'touch_end',
|
||||
'collision', 'collision_start', 'collision_end',
|
||||
'sensor')
|
||||
|
||||
def llCloud(v):
|
||||
assert isvector(v)
|
||||
return 0.0
|
||||
|
||||
def llAvatarOnLinkSitTarget(link):
|
||||
assert isinteger(link)
|
||||
if link > 255 or link == -2147483648:
|
||||
return Key(NULL_KEY)
|
||||
raise ELSLCantCompute
|
||||
|
||||
def llDetectedGrab(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event == 'touch' or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedGroup(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return 0
|
||||
|
||||
def llDetectedKey(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return Key(NULL_KEY)
|
||||
|
||||
def llDetectedLinkNumber(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return 0
|
||||
|
||||
def llDetectedName(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return u''
|
||||
|
||||
def llDetectedOwner(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return Key(NULL_KEY)
|
||||
|
||||
def llDetectedPos(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedRot(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_ROTATION
|
||||
|
||||
def llDetectedTouchBinormal(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in TouchEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedTouchFace(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in TouchEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return 0
|
||||
|
||||
def llDetectedTouchNormal(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in TouchEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedTouchPos(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in TouchEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedTouchST(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in TouchEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedTouchUV(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in TouchEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return ZERO_VECTOR
|
||||
|
||||
def llDetectedType(idx, event=None):
|
||||
assert isinteger(idx)
|
||||
if 0 <= idx <= 15 and (event in DetectionEvents or event is None):
|
||||
raise ELSLCantCompute
|
||||
return 0
|
||||
|
|
@ -195,9 +195,15 @@ class foldconst(object):
|
|||
elif nt == '*':
|
||||
result = lslfuncs.mul(op1, op2)
|
||||
elif nt == '/':
|
||||
result = lslfuncs.div(op1, op2)
|
||||
try:
|
||||
result = lslfuncs.div(op1, op2)
|
||||
except lslfuncs.ELSLMathError:
|
||||
return
|
||||
elif nt == '%':
|
||||
result = lslfuncs.mod(op1, op2)
|
||||
try:
|
||||
result = lslfuncs.mod(op1, op2)
|
||||
except lslfuncs.ELSLMathError:
|
||||
return
|
||||
elif nt == '<<':
|
||||
result = lslfuncs.S32(op1 << (op2 & 31))
|
||||
elif nt == '>>':
|
||||
|
@ -559,11 +565,19 @@ class foldconst(object):
|
|||
if CONSTargs:
|
||||
# Call it
|
||||
fn = self.symtab[0][node['name']]['Fn']
|
||||
value = fn(*tuple(arg['value'] for arg in child))
|
||||
if not self.foldtabs and isinstance(value, unicode) and '\t' in value:
|
||||
warning('Tab in function result and foldtabs option not used.')
|
||||
return
|
||||
parent[index] = {'nt':'CONST', 't':node['t'], 'value':value}
|
||||
try:
|
||||
if node['name'][:10] == 'llDetected':
|
||||
value = fn(*tuple(arg['value'] for arg in child),
|
||||
event=self.CurEvent)
|
||||
else:
|
||||
value = fn(*tuple(arg['value'] for arg in child))
|
||||
if not self.foldtabs and isinstance(value, unicode) and '\t' in value:
|
||||
warning('Tab in function result and foldtabs option not used.')
|
||||
return
|
||||
parent[index] = {'nt':'CONST', 't':node['t'], 'value':value}
|
||||
except lslfuncs.ELSLCantCompute:
|
||||
# Don't transform the tree if function is not computable
|
||||
pass
|
||||
elif node['name'] == 'llGetListLength' and child[0]['nt'] == 'IDENT':
|
||||
# Convert llGetListLength(ident) to (ident != [])
|
||||
node = {'nt':'CONST', 't':'list', 'value':[]}
|
||||
|
@ -588,6 +602,13 @@ class foldconst(object):
|
|||
return
|
||||
|
||||
if nt == 'FNDEF':
|
||||
# used when folding llDetected* function calls
|
||||
if 'scope' in node:
|
||||
# function definition
|
||||
self.CurEvent = None
|
||||
else:
|
||||
# event definition
|
||||
self.CurEvent = node['name']
|
||||
self.FoldTree(child, 0)
|
||||
if 'SEF' in child[0]:
|
||||
node['SEF'] = True
|
||||
|
@ -829,6 +850,7 @@ class foldconst(object):
|
|||
self.globalmode = False
|
||||
|
||||
tree = self.tree
|
||||
self.CurEvent = None
|
||||
|
||||
# Constant folding pass. It does some other optimizations along the way.
|
||||
for idx in xrange(len(tree)):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# Put all LSL functions together in one single module
|
||||
from lslbasefuncs import *
|
||||
from lsljson import *
|
||||
from lslextrafuncs import *
|
||||
|
|
|
@ -1754,8 +1754,9 @@ class parser(object):
|
|||
# No location info but none is necessary for forward
|
||||
# declarations.
|
||||
ret[name] = {'Kind':'v','Type':typ,'Scope':0}
|
||||
|
||||
while self.tok[0] != ';': # Don't stop to analyze what's before the ending ';'
|
||||
if self.tok[0] == 'EOF':
|
||||
return ret
|
||||
self.NextToken()
|
||||
self.NextToken()
|
||||
except EParseUEOF:
|
||||
|
|
3
main.py
3
main.py
|
@ -37,6 +37,7 @@ Options (+ means active by default, - means inactive by default):
|
|||
optimize + Runs the optimizer.
|
||||
optsigns + Optimize signs in float and integer constants.
|
||||
optfloats + Optimize a float when it is an integral value.
|
||||
constfold + Fold constant expressions to their values.
|
||||
foldtabs - Tabs can't be copy-pasted, so they aren't optimized by
|
||||
default. But with support from the viewer, they can be
|
||||
folded too and make it to the uploaded source. This
|
||||
|
@ -62,7 +63,7 @@ means that e.g. a + 3 + 5 is not optimized to a + 8; however a + (3 + 5) is.
|
|||
|
||||
options = set(('extendedglobalexpr','extendedtypecast','extendedassignment',
|
||||
'allowkeyconcat','allowmultistrings','skippreproc','optimize',
|
||||
'optsigns','optfloats'
|
||||
'optsigns','optfloats','constfold'
|
||||
))
|
||||
|
||||
if sys.argv[1] == '-O':
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue