mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
Make parentheses no longer explicit in the AST (Beta).
The output module adds parentheses where necessary, depending on the evaluation order in the tree. Or that's the idea. Prone to bugs, let's see how it bodes.
This commit is contained in:
parent
59451b90e5
commit
1dea1bd12c
5 changed files with 75 additions and 113 deletions
|
@ -384,11 +384,7 @@ class deadcode(object):
|
|||
else: # assumed VECTOR or ROTATION per SymbolReplacedOrDeleted
|
||||
SEF = 'SEF' in value
|
||||
value = self.Cast(value['ch'][fieldidx], 'float')
|
||||
# Replace it, in parentheses
|
||||
if value['nt'] not in ('CONST','()','FLD','IDENT','FNCALL'):
|
||||
node = curnode['ch'][index] = \
|
||||
{'nt':'()', 'X':True, 't':value['t'], 'ch':[value]}
|
||||
else:
|
||||
# Replace it
|
||||
node = curnode['ch'][index] = value
|
||||
if SEF:
|
||||
node['SEF'] = True
|
||||
|
@ -400,18 +396,11 @@ class deadcode(object):
|
|||
new = sym['W']
|
||||
new['X'] = True
|
||||
|
||||
if new['nt'] not in ('CONST','()','FLD','IDENT','FNCALL'):
|
||||
new = {'nt':'()', 'X':True, 't':sym['W']['t'],
|
||||
'ch':[new]}
|
||||
SEF = 'SEF' in sym['W']
|
||||
if SEF:
|
||||
new['SEF'] = True
|
||||
if new['t'] != node['t']:
|
||||
new = self.Cast(new, node['t'])
|
||||
if new['nt'] not in ('CONST','()','FLD','IDENT','FNCALL'):
|
||||
new = {'nt':'()', 'X':True, 't':node['t'], 'ch':[new]}
|
||||
if SEF:
|
||||
new['SEF'] = True
|
||||
curnode['ch'][index] = node = new
|
||||
# Delete orig if present, as we've eliminated the original
|
||||
#if 'orig' in sym['W']:
|
||||
|
|
|
@ -96,9 +96,6 @@ class foldconst(object):
|
|||
|
||||
if nt == 'NEG':
|
||||
self.FoldTree(child, 0)
|
||||
while child[0]['nt'] == '()' and child[0]['ch'][0]['nt'] == 'NEG':
|
||||
# Remove parentheses: - ( - expr ) --> - - expr
|
||||
child[0] = child[0]['ch'][0]
|
||||
if child[0]['nt'] == 'NEG':
|
||||
# Double negation: - - expr --> expr
|
||||
# NOTE: Not 100% sure this doesn't need parentheses around expr.
|
||||
|
@ -120,12 +117,11 @@ class foldconst(object):
|
|||
track = track['ch'][0]['ch'][0]
|
||||
if const > 2:
|
||||
# -~-~-~expr -> expr+3
|
||||
SEF = 'SEF' in track
|
||||
node = {'nt':'CONST', 't':'integer', 'SEF':True, 'value':const}
|
||||
node = {'nt':'+', 't':'integer', 'ch':[node, track]}
|
||||
parent[index] = node = {'nt':'()', 't':'integer', 'ch':[node]}
|
||||
if SEF:
|
||||
node['SEF'] = node['ch'][0]['SEF'] = True
|
||||
if 'SEF' in track:
|
||||
node['SEF'] = True
|
||||
parent[index] = node
|
||||
|
||||
return
|
||||
|
||||
|
@ -136,8 +132,6 @@ class foldconst(object):
|
|||
subexpr = child[0]
|
||||
if 'SEF' in subexpr:
|
||||
node['SEF'] = True
|
||||
while subexpr['nt'] == '()' and subexpr['ch'][0]['nt'] in ('()', '~', '!', '++V', '--V'):
|
||||
subexpr = child[0] = subexpr['ch'][0] # Remove parentheses
|
||||
if subexpr['nt'] == '!' and subexpr['ch'][0]['nt'] == '!':
|
||||
# Simplify !!! to !
|
||||
subexpr = child[0] = subexpr['ch'][0]['ch'][0]
|
||||
|
@ -151,9 +145,6 @@ class foldconst(object):
|
|||
subexpr = child[0]
|
||||
if 'SEF' in subexpr:
|
||||
node['SEF'] = True
|
||||
while subexpr['nt'] == '()' and subexpr['ch'][0]['nt'] in ('()',
|
||||
'~', '!', '++V', '--V'):
|
||||
subexpr = child[0] = subexpr['ch'][0] # Remove parentheses
|
||||
if subexpr['nt'] == '~':
|
||||
# Double negation: ~~expr
|
||||
parent[index] = subexpr['ch'][0]
|
||||
|
@ -162,22 +153,6 @@ class foldconst(object):
|
|||
node['value'] = ~node['value']
|
||||
return
|
||||
|
||||
if nt == '()':
|
||||
self.FoldTree(child, 0)
|
||||
if 'SEF' in child[0]:
|
||||
node['SEF'] = True
|
||||
if child[0]['nt'] in ('()', 'CONST', 'VECTOR', 'ROTATION', 'LIST',
|
||||
'IDENT', 'FIELD', 'V++', 'V--', 'FUNCTION', 'PRINT'):
|
||||
# Child is an unary postfix expression (highest priority);
|
||||
# parentheses are redundant and can be removed safely. Not
|
||||
# strictly an optimization but it helps keeping the output
|
||||
# tidy-ish a bit, and helps the optimizer do its work. It's not
|
||||
# done in general (e.g. (a * b) + c does not need parentheses
|
||||
# but these are not eliminated). Only cases like (3) or
|
||||
# (myvar++) are simplified.
|
||||
parent[index] = child[0]
|
||||
return
|
||||
|
||||
if nt in self.binary_ops:
|
||||
# RTL evaluation
|
||||
self.FoldTree(child, 1)
|
||||
|
@ -272,14 +247,6 @@ class foldconst(object):
|
|||
# Tough one. Remove neutral elements for the diverse types,
|
||||
# and more.
|
||||
|
||||
# Remove redundant parentheses
|
||||
if lnt == '()' and lval['ch'][0]['nt'] in ('+', '-', '*', '/', '%', '~', '!', 'NEG', 'CAST', 'V++', 'V--', '++V', '--V'):
|
||||
# (a op b) + c -> a op b + c
|
||||
# (op b) + c -> op b + c
|
||||
# where op's priority is that of + or greater
|
||||
lval = child[0] = lval['ch'][0]
|
||||
lnt = lval['nt']
|
||||
|
||||
# Addition of integers, strings, and lists is associative
|
||||
# Addition of floats, vectors and rotations would be, except
|
||||
# for FP precision.
|
||||
|
@ -343,15 +310,6 @@ class foldconst(object):
|
|||
parent[index] = lval
|
||||
return
|
||||
|
||||
# Remove parentheses if they enclose a NEG, to unhide their
|
||||
# operators. Precedence rules allow us.
|
||||
if lnt == '()' and lval['ch'][0]['nt'] == 'NEG':
|
||||
# (-expr) + expr -> -expr + expr
|
||||
lval = child[0] = lval['ch'][0]
|
||||
if rnt == '()' and rval['ch'][0]['nt'] == 'NEG':
|
||||
# expr + (-expr) -> expr + -expr
|
||||
rval = child[1] = rval['ch'][0]
|
||||
|
||||
if lnt != 'CONST' != rnt:
|
||||
# Neither is const. Two chances to optimize.
|
||||
# 1. -expr + -expr -> -(expr + expr) (saves 1 byte)
|
||||
|
@ -364,9 +322,6 @@ class foldconst(object):
|
|||
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':'()', 't':optype, 'ch':[node]}
|
||||
if SEF:
|
||||
node['SEF'] = True
|
||||
node = {'nt':'NEG', 't':optype, 'ch':[node]}
|
||||
|
@ -398,9 +353,8 @@ class foldconst(object):
|
|||
lval = child[0] = lval['ch'][0]
|
||||
lnt = lval['nt']
|
||||
|
||||
if rnt == '+' and rval['ch'][0]['nt'] == '()' and rval['ch'][0]['ch'][0]['nt'] == '+' \
|
||||
and (rval['ch'][0]['ch'][0]['nt'] == 'CONST'
|
||||
or rval['ch'][0]['ch'][1]['nt'] == 'CONST'):
|
||||
if rnt == '+' and (rval['ch'][0]['nt'] == 'CONST'
|
||||
or rval['ch'][1]['nt'] == 'CONST'):
|
||||
# const + (expr + const) or const + (const + expr)
|
||||
# same as above, join them
|
||||
|
||||
|
@ -416,17 +370,10 @@ class foldconst(object):
|
|||
# output rather than introducing the parens in the tree.
|
||||
if lval['value'] == -1 or lval['value'] == -2:
|
||||
if rnt == 'NEG': # Cancel the NEG
|
||||
node = {'nt':'()', 't':optype, 'ch':rval['ch']}
|
||||
if RSEF:
|
||||
node['SEF'] = True
|
||||
node = {'nt':'~', 't':optype, 'ch':[node]}
|
||||
node = {'nt':'~', 't':optype, 'ch':rval['ch']}
|
||||
if RSEF:
|
||||
node['SEF'] = True
|
||||
else: # Add the NEG
|
||||
#node = {'nt':'()', 't':optype, 'ch':[rval]}
|
||||
#if RSEF:
|
||||
# node['SEF'] = True
|
||||
#node = {'nt':'NEG', 't':optype, 'ch':[node]}
|
||||
node = {'nt':'NEG', 't':optype, 'ch':[rval]}
|
||||
if RSEF:
|
||||
node['SEF'] = True
|
||||
|
@ -445,18 +392,11 @@ class foldconst(object):
|
|||
|
||||
if lval['value'] == 1 or lval['value'] == 2:
|
||||
if rnt == '~': # Cancel the ~
|
||||
#node = {'nt':'()', 't':optype, 'ch':rval['ch']}
|
||||
#if RSEF:
|
||||
# node['SEF'] = True
|
||||
#node = {'nt':'NEG', 't':optype, 'ch':[node]}
|
||||
node = {'nt':'NEG', 't':optype, 'ch':rval['ch']}
|
||||
if RSEF:
|
||||
node['SEF'] = True
|
||||
else:
|
||||
node = {'nt':'()', 't':optype, 'ch':[rval]}
|
||||
if RSEF:
|
||||
node['SEF'] = True
|
||||
node = {'nt':'~', 't':optype, 'ch':[node]}
|
||||
node = {'nt':'~', 't':optype, 'ch':[rval]}
|
||||
if RSEF:
|
||||
node['SEF'] = True
|
||||
node = {'nt':'NEG', 't':optype, 'ch':[node]}
|
||||
|
@ -486,13 +426,6 @@ class foldconst(object):
|
|||
# have e.g. {<< {& x y} 3}; there will be explicit
|
||||
# parentheses here always, so we don't need to worry.
|
||||
|
||||
# Operands with priority between * (not included) and <<
|
||||
# (included).
|
||||
if child[0]['nt'] in ('+', '-', 'NEG', '<<', '>>'):
|
||||
SEF = 'SEF' in child[0]
|
||||
child[0] = {'nt':'()', 't':child[0]['t'], 'ch':[child[0]]}
|
||||
if SEF:
|
||||
child[0]['SEF'] = True
|
||||
# we have {<<, something, {CONST n}}, transform into {*, something, {CONST n}}
|
||||
node['nt'] = '*'
|
||||
child[1]['value'] = 1 << (child[1]['value'] & 31)
|
||||
|
@ -515,13 +448,14 @@ class foldconst(object):
|
|||
|
||||
if nt != '=':
|
||||
# Replace the node with the expression alone
|
||||
child[1] = {'nt':'()', 't':child[1]['t'], 'ch':[child[1]]}
|
||||
# e.g. a += b -> a + b
|
||||
node['nt'] = nt[:-1]
|
||||
|
||||
# Linden Craziness: i *= f; is valid (but no other i op= f is).
|
||||
# It's actually performed as i = (integer)(i + (f)). This breaks
|
||||
# regular equivalence of x op= y as x = x op (y) so we add
|
||||
# the type cast here.
|
||||
# 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.
|
||||
if nt == '*=' and child[0]['t'] == 'integer' and child[1]['t'] == 'float':
|
||||
node['t'] = 'float' # Addition shall return float.
|
||||
node = self.Cast(node, 'integer')
|
||||
|
@ -605,8 +539,7 @@ class foldconst(object):
|
|||
elif node['name'] == 'llGetListLength' and child[0]['nt'] == 'IDENT':
|
||||
# Convert llGetListLength(ident) to (ident != [])
|
||||
node = {'nt':'CONST', 't':'list', 'value':[]}
|
||||
node = {'nt':'!=', 't':'list', 'ch':[child[0], node]}
|
||||
parent[index] = {'nt':'()', 't':'list', 'ch':[node]}
|
||||
parent[index] = node = {'nt':'!=', 't':'list', 'ch':[child[0], node]}
|
||||
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.
|
||||
|
|
|
@ -31,13 +31,6 @@ class optimizer(foldconst, renamer, deadcode):
|
|||
# value unchanged
|
||||
if value['t'] == newtype:
|
||||
return value
|
||||
if value not in ('CONST','()','FLD','IDENT','FNCALL','V++','V--',
|
||||
'VECTOR','ROTATION','LIST'):
|
||||
value = {'nt':'()', 't':newtype, 'ch':[value]}
|
||||
if 'SEF' in value['ch'][0]:
|
||||
value['SEF'] = True
|
||||
if 'X' in value['ch'][0]:
|
||||
value['X'] = value['ch'][0]['X']
|
||||
ret = {'nt':'CAST', 't':newtype, 'ch':[value]}
|
||||
if 'SEF' in value:
|
||||
ret['SEF'] = True
|
||||
|
|
|
@ -10,6 +10,11 @@ class outscript(object):
|
|||
))
|
||||
extended_assignments = frozenset(('&=', '|=', '^=', '<<=', '>>='))
|
||||
unary_operands = frozenset(('NEG', '!', '~'))
|
||||
op_priority = {'=':0, '+=':0, '-=':0, '*=':0, '/=':0, '%=':0, '&=':0,
|
||||
'|=':0, '^=':0, '<<=':0, '>>=':0,
|
||||
'||':1, '&&':1, '|':2, '^':3, '&':4, '==':5, '!=':5,
|
||||
'<':6, '<=':6, '>':6, '>=':6, '<<':7, '>>':7, '+':8, '-':8,# 'NEG':8,
|
||||
'*':9, '/':9, '%':9}#, '!':10, '~':10, '++':10, '--':10, }
|
||||
|
||||
def Value2LSL(self, value):
|
||||
tvalue = type(value)
|
||||
|
@ -174,11 +179,33 @@ class outscript(object):
|
|||
if 'ch' in expr:
|
||||
child = expr['ch']
|
||||
|
||||
if nt == '()':
|
||||
return '(' + self.OutExpr(child[0]) + ')'
|
||||
|
||||
if nt in self.binary_operands:
|
||||
return self.OutExpr(child[0]) + ' ' + nt + ' ' + self.OutExpr(child[1])
|
||||
lnt = child[0]['nt']
|
||||
lparen = False
|
||||
rnt = child[1]['nt']
|
||||
rparen = False
|
||||
if nt in self.op_priority:
|
||||
base_pri = self.op_priority[nt]
|
||||
if lnt in self.op_priority:
|
||||
if self.op_priority[lnt] < base_pri:
|
||||
lparen = True
|
||||
elif lnt == 'NEG' and base_pri > self.op_priority['-']:
|
||||
lparen = True
|
||||
|
||||
if rnt in self.op_priority:
|
||||
if self.op_priority[rnt] <= base_pri:
|
||||
rparen = True
|
||||
|
||||
if lparen:
|
||||
ret = '(' + self.OutExpr(child[0]) + ')'
|
||||
else:
|
||||
ret = self.OutExpr(child[0])
|
||||
ret += ' ' + nt + ' '
|
||||
if rparen:
|
||||
ret += '(' + self.OutExpr(child[1]) + ')'
|
||||
else:
|
||||
ret += self.OutExpr(child[1])
|
||||
return ret
|
||||
|
||||
if nt == 'IDENT':
|
||||
return self.FindName(expr)
|
||||
|
@ -190,7 +217,7 @@ class outscript(object):
|
|||
ret = '(' + expr['t'] + ')'
|
||||
expr = child[0]
|
||||
if expr['nt'] in ('CONST', 'IDENT', 'V++', 'V--', 'VECTOR',
|
||||
'ROTATION', 'LIST', 'FIELD', 'PRINT', 'FUNCTION', '()'):
|
||||
'ROTATION', 'LIST', 'FIELD', 'PRINT', 'FUNCTION'):
|
||||
return ret + self.OutExpr(expr)
|
||||
return ret + '(' + self.OutExpr(expr) + ')'
|
||||
|
||||
|
@ -201,7 +228,17 @@ class outscript(object):
|
|||
return ret
|
||||
|
||||
if nt in ('VECTOR', 'ROTATION'):
|
||||
return '<' + self.OutExprList(child) + '>'
|
||||
ret = ('<' + self.OutExpr(child[0]) + ','
|
||||
+ self.OutExpr(child[1]) + ',')
|
||||
if nt == 'ROTATION':
|
||||
ret += self.OutExpr(child[2]) + ','
|
||||
lnt = child[-1]['nt']
|
||||
if lnt in self.op_priority \
|
||||
and self.op_priority[lnt] <= self.op_priority['>']:
|
||||
ret += '(' + self.OutExpr(child[-1]) + ')'
|
||||
else:
|
||||
ret += self.OutExpr(child[-1])
|
||||
return ret + '>'
|
||||
|
||||
if nt == 'FNCALL':
|
||||
return self.FindName(expr) + '(' + self.OutExprList(child) + ')'
|
||||
|
@ -210,9 +247,22 @@ class outscript(object):
|
|||
return 'print(' + self.OutExpr(child[0]) + ')'
|
||||
|
||||
if nt in self.unary_operands:
|
||||
ret = nt
|
||||
lnt = child[0]['nt']
|
||||
paren = False
|
||||
if nt == 'NEG':
|
||||
nt = '- '
|
||||
return nt + self.OutExpr(child[0])
|
||||
ret = '- '
|
||||
if lnt in self.op_priority:
|
||||
paren = self.op_priority[lnt] <= self.op_priority['-']
|
||||
else:
|
||||
if lnt in self.op_priority:
|
||||
paren = True
|
||||
|
||||
if paren:
|
||||
ret += '(' + self.OutExpr(child[0]) + ')'
|
||||
else:
|
||||
ret += self.OutExpr(child[0])
|
||||
return ret
|
||||
|
||||
if nt == 'FLD':
|
||||
return self.OutExpr(child[0]) + '.' + expr['fld']
|
||||
|
|
|
@ -798,8 +798,6 @@ class parser(object):
|
|||
|
||||
if tok0 == '(':
|
||||
# Parenthesized expression or typecast
|
||||
# TODO: Don't include parentheses in the tree, defer to the output
|
||||
# module to add them where necessary.
|
||||
|
||||
self.NextToken()
|
||||
if self.tok[0] != 'TYPE':
|
||||
|
@ -807,7 +805,7 @@ class parser(object):
|
|||
expr = self.Parse_expression()
|
||||
self.expect(')')
|
||||
self.NextToken()
|
||||
return {'nt':'()', 't':expr['t'], 'ch':[expr]}
|
||||
return expr
|
||||
|
||||
# Typecast
|
||||
typ = self.tok[1]
|
||||
|
@ -825,7 +823,6 @@ class parser(object):
|
|||
expr = self.Parse_expression()
|
||||
self.expect(')')
|
||||
self.NextToken()
|
||||
expr = {'nt':'()', 't':expr['t'], 'ch':[expr]}
|
||||
else:
|
||||
expr = self.Parse_unary_postfix_expression(AllowAssignment = False)
|
||||
basetype = expr['t']
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue