Fix parenthesization of unary minus

The algorithm for adding parentheses around unary operators was not working properly. It converted a * (-b) * c into a * -b * c, which LSL handles as a * -(b * c).

Fix and add test cases for that. One of the test cases shows an example where the difference matters: 0 * (-1e20) * 1e20 should result in 0.0, but if wrongly parenthesized, it gives NaN, because 1e20*1e20 gives infinity due to float overflow, and minus infinity times 0 is indeterminate.

The addition of parentheses has been improved, but it still does not eliminate every redundant parenthesis.

Also fix the horrendous typo of using "operands" where it should be "operators".
This commit is contained in:
Sei Lisa 2019-05-04 23:07:45 +02:00
parent 9d540798b4
commit ec509b3840
6 changed files with 78 additions and 21 deletions

View file

@ -26,11 +26,11 @@ debugScopes = False
class outscript(object): class outscript(object):
binary_operands = frozenset(('||','&&','^','|','&','==','!=','<','<=','>', binary_operators = frozenset(('||','&&','^','|','&','==','!=','<','<=','>',
'>=','<<','>>','+','-','*','/','%', '=', '+=', '-=', '*=', '/=','%=', '>=','<<','>>','+','-','*','/','%', '=', '+=', '-=', '*=', '/=','%=',
)) ))
extended_assignments = frozenset(('&=', '|=', '^=', '<<=', '>>=')) extended_assignments = frozenset(('&=', '|=', '^=', '<<=', '>>='))
unary_operands = frozenset(('NEG', '!', '~')) unary_operators = frozenset(('NEG', '!', '~'))
op_priority = {'=':0, '+=':0, '-=':0, '*=':0, '/=':0, '%=':0, '&=':0, op_priority = {'=':0, '+=':0, '-=':0, '*=':0, '/=':0, '%=':0, '&=':0,
'|=':0, '^=':0, '<<=':0, '>>=':0, '|=':0, '^=':0, '<<=':0, '>>=':0,
'||':1, '&&':1, '|':2, '^':3, '&':4, '==':5, '!=':5, '||':1, '&&':1, '|':2, '^':3, '&':4, '==':5, '!=':5,
@ -216,7 +216,7 @@ class outscript(object):
nt = expr.nt nt = expr.nt
child = expr.ch child = expr.ch
if nt in self.binary_operands: if nt in self.binary_operators:
lnt = child[0].nt lnt = child[0].nt
lparen = False lparen = False
rnt = child[1].nt rnt = child[1].nt
@ -237,7 +237,8 @@ class outscript(object):
lparen = True lparen = True
# This situation has ugly cases due to the strange precedence # This situation has ugly cases due to the strange precedence
# of unary minus. Consider the following two statements: # of unary minus. Consider the following two statements, paying
# attention to the binding power of the binary not ~ operator:
# (~-a) * a # (~-a) * a
# a * (~-a) * a # a * (~-a) * a
# In one case, the (~-a) is a left child; in the other, it's # In one case, the (~-a) is a left child; in the other, it's
@ -246,20 +247,44 @@ class outscript(object):
# ~-(a * a) # ~-(a * a)
# a * ~-(a * a) # a * ~-(a * a)
# Yet the tree structure makes it quite hard to detect these. # Yet the tree structure makes it quite hard to detect these.
# So as a safeguard, for now we parenthesize all ~ and ! within # We have to descend down the left nodes as we keep finding
# binary operands, as they have a deceitful binding power when # binary operators or unary operators, to find whether the
# there's a unary minus downstream. # symbol is followed by unary minus. If that's the case, we
# # need to act as if there was a negation right here.
# TODO: See if the parenthesizing of ~ and ! can be improved.
elif lnt in ('~', '!'): elif lnt in ('~', '!'):
lparen = True lnode = child[0]
while True:
lnode = lnode.ch[0]
if (lnode.nt not in self.op_priority
and lnode.nt not in ('~', '!')
):
break
if lnode.nt == 'NEG' and base_pri > self.op_priority['-']:
lparen = True
if rnt in self.op_priority: if rnt in self.op_priority:
if self.op_priority[rnt] <= base_pri: if self.op_priority[rnt] <= base_pri:
rparen = True rparen = True
elif rnt == 'NEG' and self.op_priority['-'] < base_pri:
rparen = True
# see above # see above
elif rnt in ('~', '!'): elif rnt in ('~', '!'):
rparen = True lnode = child[1]
while True:
# Descend down the left nodes of the right node, to
# find whether it's immediately followed by -
lnode = lnode.ch[0]
if lnode.nt not in self.op_priority and lnode.nt not in ('~', '!'):
break
if lnode.nt == 'NEG' and base_pri > self.op_priority['-']:
# TODO: Improve right operand parenthesis removal
# for minus signs.
# Shouldn't this look into the RHS node?
# a * -b * c
# is currently translated to
# a * (-b * c)
# which is correct but not optimal.
rparen = True
if lparen: if lparen:
ret = '(' + self.OutExpr(child[0]) + ')' ret = '(' + self.OutExpr(child[0]) + ')'
@ -328,7 +353,7 @@ class outscript(object):
if nt == 'PRINT': if nt == 'PRINT':
return 'print(' + self.OutExpr(child[0]) + ')' return 'print(' + self.OutExpr(child[0]) + ')'
if nt in self.unary_operands: if nt in self.unary_operators:
ret = nt ret = nt
lnt = child[0].nt lnt = child[0].nt
paren = False paren = False

View file

@ -15,9 +15,9 @@ default
list L; list L;
f += (float)i; f += (float)i;
L += (list)((integer)((float)i)); L += (list)((integer)((float)i));
i = (~(integer)-2) * 3; i = ~(integer)-2 * 3;
i = (~(integer)-2.) * 3; i = ~(integer)-2. * 3;
i = (~(integer)(-i)) * 3; i = ~(integer)(-i) * 3;
"" + (string)((key)""); "" + (string)((key)"");
(string)((key)"") + ""; (string)((key)"") + "";
@J; @J;

View file

@ -23,9 +23,9 @@
, 3 + llGetLinkNumber() , 3 + llGetLinkNumber()
, ~llGetLinkNumber() , ~llGetLinkNumber()
, llGetLinkNumber() , llGetLinkNumber()
, -((~-~llGetLinkNumber()) + (~-~llGetLinkNumber())) , -(~-~llGetLinkNumber() + ~-~llGetLinkNumber())
, -~-~llGetLinkNumber() + (~-~-llGetLinkNumber()) , -~-~llGetLinkNumber() + ~-~-llGetLinkNumber()
, (~-~-llGetLinkNumber()) + (~-~-llGetLinkNumber()) , ~-~-llGetLinkNumber() + ~-~-llGetLinkNumber()
, 5 + llGetLinkNumber() + (5 + llGetLinkNumber()) , 5 + llGetLinkNumber() + (5 + llGetLinkNumber())
, 0 , 0
] ]

View file

@ -5,7 +5,7 @@ default
string s = llGetObjectDesc(); string s = llGetObjectDesc();
key k = llGenerateKey(); key k = llGenerateKey();
integer i = llGetLinkNumber(); integer i = llGetLinkNumber();
if (!((!(s == "A")) | k == llGenerateKey() | (~llSubStringIndex(s, "B")))) if (!(!(s == "A") | k == llGenerateKey() | ~llSubStringIndex(s, "B")))
llDie(); llDie();
if (-!(i ^ 5 | i ^ 9) & llGetLinkNumber()) if (-!(i ^ 5 | i ^ 9) & llGetLinkNumber())
llDie(); llDie();

View file

@ -1 +1,17 @@
(-3)*4 | -(3*4) | ~-3*4 | -~3*4 | ~(-3)*4 & (-(~3)*4 | -~(3*4)) | - -3 [ (-3) * 4
, -3 * 4
, -(3 * 4)
, ~-3 * 4
, -~3 * 4 | (~(-3) * 4 & (-(~3)*4 | -~(3 * 4))) | - -3
, (3 * ~-4) * 5
, 3 * ~-4 * 5
, 3 * (~-4) * 5
, 3 * ~(-4) * 5
, 3 * ~(-4 * 5)
, 3 * (-~4) * 5
, 3 * -(~4) * 5
, 3 * -(~4 * 5)
, 3 * -~(4 * 5)
, 0 * (-1e20) * 1e20
, 0 * -1e20 * 1e20
]

View file

@ -1 +1,17 @@
(-3) * 4 | -3 * 4 | (~-3 * 4) | -(~3) * 4 | (~-3) * 4 & (-(~3) * 4 | -~(3 * 4)) | - -3 [ (-3) * 4
, -3 * 4
, -3 * 4
, ~-3 * 4
, -~3 * 4 | (~-3) * 4 & (-~3 * 4 | -~(3 * 4)) | - -3
, 3 * (~-4) * 5
, 3 * (~-4 * 5)
, 3 * (~-4) * 5
, 3 * (~-4) * 5
, 3 * (~-4 * 5)
, 3 * (-~4) * 5
, 3 * (-~4 * 5)
, 3 * (-~4 * 5)
, 3 * (-~(4 * 5))
, 0 * (-1e20) * 1e20
, 0 * (-1e20 * 1e20)
]