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:
Sei Lisa 2015-02-28 00:43:26 +01:00
parent 59451b90e5
commit 1dea1bd12c
5 changed files with 75 additions and 113 deletions

View file

@ -384,12 +384,8 @@ class deadcode(object):
else: # assumed VECTOR or ROTATION per SymbolReplacedOrDeleted else: # assumed VECTOR or ROTATION per SymbolReplacedOrDeleted
SEF = 'SEF' in value SEF = 'SEF' in value
value = self.Cast(value['ch'][fieldidx], 'float') value = self.Cast(value['ch'][fieldidx], 'float')
# Replace it, in parentheses # Replace it
if value['nt'] not in ('CONST','()','FLD','IDENT','FNCALL'): node = curnode['ch'][index] = value
node = curnode['ch'][index] = \
{'nt':'()', 'X':True, 't':value['t'], 'ch':[value]}
else:
node = curnode['ch'][index] = value
if SEF: if SEF:
node['SEF'] = True node['SEF'] = True
@ -400,18 +396,11 @@ class deadcode(object):
new = sym['W'] new = sym['W']
new['X'] = True 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'] SEF = 'SEF' in sym['W']
if SEF: if SEF:
new['SEF'] = True new['SEF'] = True
if new['t'] != node['t']: if new['t'] != node['t']:
new = self.Cast(new, 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 curnode['ch'][index] = node = new
# Delete orig if present, as we've eliminated the original # Delete orig if present, as we've eliminated the original
#if 'orig' in sym['W']: #if 'orig' in sym['W']:

View file

@ -96,9 +96,6 @@ class foldconst(object):
if nt == 'NEG': if nt == 'NEG':
self.FoldTree(child, 0) 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': if child[0]['nt'] == 'NEG':
# Double negation: - - expr --> expr # Double negation: - - expr --> expr
# NOTE: Not 100% sure this doesn't need parentheses around 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] track = track['ch'][0]['ch'][0]
if const > 2: if const > 2:
# -~-~-~expr -> expr+3 # -~-~-~expr -> expr+3
SEF = 'SEF' in track
node = {'nt':'CONST', 't':'integer', 'SEF':True, 'value':const} node = {'nt':'CONST', 't':'integer', 'SEF':True, 'value':const}
node = {'nt':'+', 't':'integer', 'ch':[node, track]} node = {'nt':'+', 't':'integer', 'ch':[node, track]}
parent[index] = node = {'nt':'()', 't':'integer', 'ch':[node]} if 'SEF' in track:
if SEF: node['SEF'] = True
node['SEF'] = node['ch'][0]['SEF'] = True parent[index] = node
return return
@ -136,8 +132,6 @@ class foldconst(object):
subexpr = child[0] subexpr = child[0]
if 'SEF' in subexpr: if 'SEF' in subexpr:
node['SEF'] = True 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'] == '!': if subexpr['nt'] == '!' and subexpr['ch'][0]['nt'] == '!':
# Simplify !!! to ! # Simplify !!! to !
subexpr = child[0] = subexpr['ch'][0]['ch'][0] subexpr = child[0] = subexpr['ch'][0]['ch'][0]
@ -151,9 +145,6 @@ class foldconst(object):
subexpr = child[0] subexpr = child[0]
if 'SEF' in subexpr: if 'SEF' in subexpr:
node['SEF'] = True 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'] == '~': if subexpr['nt'] == '~':
# Double negation: ~~expr # Double negation: ~~expr
parent[index] = subexpr['ch'][0] parent[index] = subexpr['ch'][0]
@ -162,22 +153,6 @@ class foldconst(object):
node['value'] = ~node['value'] node['value'] = ~node['value']
return 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: if nt in self.binary_ops:
# RTL evaluation # RTL evaluation
self.FoldTree(child, 1) self.FoldTree(child, 1)
@ -272,14 +247,6 @@ class foldconst(object):
# Tough one. Remove neutral elements for the diverse types, # Tough one. Remove neutral elements for the diverse types,
# and more. # 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 integers, strings, and lists is associative
# Addition of floats, vectors and rotations would be, except # Addition of floats, vectors and rotations would be, except
# for FP precision. # for FP precision.
@ -343,15 +310,6 @@ class foldconst(object):
parent[index] = lval parent[index] = lval
return 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: if lnt != 'CONST' != rnt:
# Neither is const. Two chances to optimize. # Neither is const. Two chances to optimize.
# 1. -expr + -expr -> -(expr + expr) (saves 1 byte) # 1. -expr + -expr -> -(expr + expr) (saves 1 byte)
@ -364,9 +322,6 @@ class foldconst(object):
if lnt == rnt == 'NEG': if lnt == rnt == 'NEG':
node = {'nt':'+', 't':optype, 'ch':[lval['ch'][0], rval['ch'][0]]} node = {'nt':'+', 't':optype, 'ch':[lval['ch'][0], rval['ch'][0]]}
SEF = 'SEF' in lval['ch'][0] and 'SEF' in 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: if SEF:
node['SEF'] = True node['SEF'] = True
node = {'nt':'NEG', 't':optype, 'ch':[node]} node = {'nt':'NEG', 't':optype, 'ch':[node]}
@ -398,9 +353,8 @@ class foldconst(object):
lval = child[0] = lval['ch'][0] lval = child[0] = lval['ch'][0]
lnt = lval['nt'] lnt = lval['nt']
if rnt == '+' and rval['ch'][0]['nt'] == '()' and rval['ch'][0]['ch'][0]['nt'] == '+' \ if rnt == '+' and (rval['ch'][0]['nt'] == 'CONST'
and (rval['ch'][0]['ch'][0]['nt'] == 'CONST' or rval['ch'][1]['nt'] == 'CONST'):
or rval['ch'][0]['ch'][1]['nt'] == 'CONST'):
# const + (expr + const) or const + (const + expr) # const + (expr + const) or const + (const + expr)
# same as above, join them # same as above, join them
@ -416,17 +370,10 @@ class foldconst(object):
# output rather than introducing the parens in the tree. # output rather than introducing the parens in the tree.
if lval['value'] == -1 or lval['value'] == -2: if lval['value'] == -1 or lval['value'] == -2:
if rnt == 'NEG': # Cancel the NEG if rnt == 'NEG': # Cancel the NEG
node = {'nt':'()', 't':optype, 'ch':rval['ch']} node = {'nt':'~', 't':optype, 'ch':rval['ch']}
if RSEF:
node['SEF'] = True
node = {'nt':'~', 't':optype, 'ch':[node]}
if RSEF: if RSEF:
node['SEF'] = True node['SEF'] = True
else: # Add the NEG 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]} node = {'nt':'NEG', 't':optype, 'ch':[rval]}
if RSEF: if RSEF:
node['SEF'] = True node['SEF'] = True
@ -445,18 +392,11 @@ class foldconst(object):
if lval['value'] == 1 or lval['value'] == 2: if lval['value'] == 1 or lval['value'] == 2:
if rnt == '~': # Cancel the ~ 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']} node = {'nt':'NEG', 't':optype, 'ch':rval['ch']}
if RSEF: if RSEF:
node['SEF'] = True node['SEF'] = True
else: else:
node = {'nt':'()', 't':optype, 'ch':[rval]} node = {'nt':'~', 't':optype, 'ch':[rval]}
if RSEF:
node['SEF'] = True
node = {'nt':'~', 't':optype, 'ch':[node]}
if RSEF: if RSEF:
node['SEF'] = True node['SEF'] = True
node = {'nt':'NEG', 't':optype, 'ch':[node]} node = {'nt':'NEG', 't':optype, 'ch':[node]}
@ -486,13 +426,6 @@ class foldconst(object):
# have e.g. {<< {& x y} 3}; there will be explicit # have e.g. {<< {& x y} 3}; there will be explicit
# parentheses here always, so we don't need to worry. # 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}} # we have {<<, something, {CONST n}}, transform into {*, something, {CONST n}}
node['nt'] = '*' node['nt'] = '*'
child[1]['value'] = 1 << (child[1]['value'] & 31) child[1]['value'] = 1 << (child[1]['value'] & 31)
@ -515,13 +448,14 @@ class foldconst(object):
if nt != '=': if nt != '=':
# Replace the node with the expression alone # 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] node['nt'] = nt[:-1]
# Linden Craziness: i *= f; is valid (but no other i op= f is). # Linden Craziness: int *= float; is valid (but no other
# It's actually performed as i = (integer)(i + (f)). This breaks # int op= float is). It's actually performed as
# regular equivalence of x op= y as x = x op (y) so we add # i = (integer)(i + (f));
# the type cast here. # 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': if nt == '*=' and child[0]['t'] == 'integer' and child[1]['t'] == 'float':
node['t'] = 'float' # Addition shall return float. node['t'] = 'float' # Addition shall return float.
node = self.Cast(node, 'integer') node = self.Cast(node, 'integer')
@ -605,8 +539,7 @@ class foldconst(object):
elif node['name'] == 'llGetListLength' and child[0]['nt'] == 'IDENT': elif node['name'] == 'llGetListLength' and child[0]['nt'] == 'IDENT':
# Convert llGetListLength(ident) to (ident != []) # Convert llGetListLength(ident) to (ident != [])
node = {'nt':'CONST', 't':'list', 'value':[]} node = {'nt':'CONST', 't':'list', 'value':[]}
node = {'nt':'!=', 't':'list', 'ch':[child[0], node]} parent[index] = node = {'nt':'!=', 't':'list', 'ch':[child[0], node]}
parent[index] = {'nt':'()', 't':'list', 'ch':[node]}
elif SEFargs and 'SEF' in self.symtab[0][node['name']]: elif SEFargs and 'SEF' in self.symtab[0][node['name']]:
# The function is marked as SEF in the symbol table, and the # The function is marked as SEF in the symbol table, and the
# arguments are all side-effect-free. The result is SEF. # arguments are all side-effect-free. The result is SEF.

View file

@ -31,13 +31,6 @@ class optimizer(foldconst, renamer, deadcode):
# value unchanged # value unchanged
if value['t'] == newtype: if value['t'] == newtype:
return value 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]} ret = {'nt':'CAST', 't':newtype, 'ch':[value]}
if 'SEF' in value: if 'SEF' in value:
ret['SEF'] = True ret['SEF'] = True

View file

@ -10,6 +10,11 @@ class outscript(object):
)) ))
extended_assignments = frozenset(('&=', '|=', '^=', '<<=', '>>=')) extended_assignments = frozenset(('&=', '|=', '^=', '<<=', '>>='))
unary_operands = frozenset(('NEG', '!', '~')) 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): def Value2LSL(self, value):
tvalue = type(value) tvalue = type(value)
@ -174,11 +179,33 @@ class outscript(object):
if 'ch' in expr: if 'ch' in expr:
child = expr['ch'] child = expr['ch']
if nt == '()':
return '(' + self.OutExpr(child[0]) + ')'
if nt in self.binary_operands: 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': if nt == 'IDENT':
return self.FindName(expr) return self.FindName(expr)
@ -190,7 +217,7 @@ class outscript(object):
ret = '(' + expr['t'] + ')' ret = '(' + expr['t'] + ')'
expr = child[0] expr = child[0]
if expr['nt'] in ('CONST', 'IDENT', 'V++', 'V--', 'VECTOR', 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)
return ret + '(' + self.OutExpr(expr) + ')' return ret + '(' + self.OutExpr(expr) + ')'
@ -201,7 +228,17 @@ class outscript(object):
return ret return ret
if nt in ('VECTOR', 'ROTATION'): 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': if nt == 'FNCALL':
return self.FindName(expr) + '(' + self.OutExprList(child) + ')' return self.FindName(expr) + '(' + self.OutExprList(child) + ')'
@ -210,9 +247,22 @@ class outscript(object):
return 'print(' + self.OutExpr(child[0]) + ')' return 'print(' + self.OutExpr(child[0]) + ')'
if nt in self.unary_operands: if nt in self.unary_operands:
ret = nt
lnt = child[0]['nt']
paren = False
if nt == 'NEG': if nt == 'NEG':
nt = '- ' ret = '- '
return nt + self.OutExpr(child[0]) 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': if nt == 'FLD':
return self.OutExpr(child[0]) + '.' + expr['fld'] return self.OutExpr(child[0]) + '.' + expr['fld']

View file

@ -798,8 +798,6 @@ class parser(object):
if tok0 == '(': if tok0 == '(':
# Parenthesized expression or typecast # Parenthesized expression or typecast
# TODO: Don't include parentheses in the tree, defer to the output
# module to add them where necessary.
self.NextToken() self.NextToken()
if self.tok[0] != 'TYPE': if self.tok[0] != 'TYPE':
@ -807,7 +805,7 @@ class parser(object):
expr = self.Parse_expression() expr = self.Parse_expression()
self.expect(')') self.expect(')')
self.NextToken() self.NextToken()
return {'nt':'()', 't':expr['t'], 'ch':[expr]} return expr
# Typecast # Typecast
typ = self.tok[1] typ = self.tok[1]
@ -825,7 +823,6 @@ class parser(object):
expr = self.Parse_expression() expr = self.Parse_expression()
self.expect(')') self.expect(')')
self.NextToken() self.NextToken()
expr = {'nt':'()', 't':expr['t'], 'ch':[expr]}
else: else:
expr = self.Parse_unary_postfix_expression(AllowAssignment = False) expr = self.Parse_unary_postfix_expression(AllowAssignment = False)
basetype = expr['t'] basetype = expr['t']