diff --git a/lslopt/lsldeadcode.py b/lslopt/lsldeadcode.py index cb0cc8d..fa43597 100644 --- a/lslopt/lsldeadcode.py +++ b/lslopt/lsldeadcode.py @@ -384,12 +384,8 @@ 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: - node = curnode['ch'][index] = value + # 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']: diff --git a/lslopt/lslfoldconst.py b/lslopt/lslfoldconst.py index 1f3a215..2742d50 100644 --- a/lslopt/lslfoldconst.py +++ b/lslopt/lslfoldconst.py @@ -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. diff --git a/lslopt/lsloptimizer.py b/lslopt/lsloptimizer.py index b346c0b..4587f03 100644 --- a/lslopt/lsloptimizer.py +++ b/lslopt/lsloptimizer.py @@ -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 diff --git a/lslopt/lsloutput.py b/lslopt/lsloutput.py index 9d29574..7701ca8 100644 --- a/lslopt/lsloutput.py +++ b/lslopt/lsloutput.py @@ -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'] diff --git a/lslopt/lslparse.py b/lslopt/lslparse.py index aa72a3f..e989fca 100644 --- a/lslopt/lslparse.py +++ b/lslopt/lslparse.py @@ -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']