mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-04 12:14:36 -07:00
Add support for 'break n;' and 'continue n;'. Also simplify the break/continue stacks handling code and fix some bugs.
This commit is contained in:
parent
d4035b0723
commit
308ca7c8ff
1 changed files with 95 additions and 110 deletions
|
@ -1401,14 +1401,8 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
# if braces are not used.
|
# if braces are not used.
|
||||||
self.PushScope()
|
self.PushScope()
|
||||||
|
|
||||||
brk = self.GenerateLabel()
|
self.breakstack.append([self.GenerateLabel(), self.scopeindex, False])
|
||||||
self.breaktargets.append(brk)
|
self.continuestack.append([self.GenerateLabel(), None, False])
|
||||||
self.breakscopes.append(self.scopeindex)
|
|
||||||
self.breaksused.append(False)
|
|
||||||
cnt = self.GenerateLabel()
|
|
||||||
self.continuetargets.append(cnt)
|
|
||||||
self.continuescopes.append(None)
|
|
||||||
self.continuesused.append(False)
|
|
||||||
self.expect('(')
|
self.expect('(')
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
condition = self.Parse_expression()
|
condition = self.Parse_expression()
|
||||||
|
@ -1417,22 +1411,16 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
ret = {'nt':'WHILE', 't':None, 'ch':[condition,
|
ret = {'nt':'WHILE', 't':None, 'ch':[condition,
|
||||||
self.Parse_statement(ReturnType, AllowStSw = True, InsideLoop = True)]}
|
self.Parse_statement(ReturnType, AllowStSw = True, InsideLoop = True)]}
|
||||||
if self.breakcont:
|
if self.breakcont:
|
||||||
if self.continuesused.pop():
|
last = self.continuestack.pop()
|
||||||
assert ret['ch'][0]['nt'] == '{}'
|
if last[2]:
|
||||||
ret['ch'][0]['ch'].append(
|
assert ret['ch'][1]['nt'] == '{}'
|
||||||
{'nt':'@', 't':None, 'name':cnt, 'scope':self.continuescopes[-1]}
|
ret['ch'][1]['ch'].append({'nt':'@', 't':None, 'name':last[0], 'scope':last[1]})
|
||||||
)
|
self.AddSymbol('l', last[1], last[0])
|
||||||
self.AddSymbol('l', self.continuescopes[-1], cnt)
|
|
||||||
self.continuescopes.pop()
|
|
||||||
self.continuetargets.pop()
|
|
||||||
|
|
||||||
if self.breaksused.pop():
|
last = self.breakstack.pop()
|
||||||
ret = {'nt':'{}', 't':None, 'ch':[ret,
|
if last[2]:
|
||||||
{'nt':'@', 't':None, 'name':brk, 'scope':self.scopeindex}
|
ret = {'nt':'{}', 't':None, 'ch':[ret, {'nt':'@', 't':None, 'name':last[0], 'scope':last[1]}]}
|
||||||
]}
|
self.AddSymbol('l', last[1], last[0])
|
||||||
self.AddSymbol('l', self.scopeindex, brk)
|
|
||||||
self.breakscopes.pop()
|
|
||||||
self.breaktargets.pop()
|
|
||||||
self.PopScope()
|
self.PopScope()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -1441,14 +1429,8 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
if self.breakcont:
|
if self.breakcont:
|
||||||
self.PushScope()
|
self.PushScope()
|
||||||
|
|
||||||
brk = self.GenerateLabel()
|
self.breakstack.append([self.GenerateLabel(), self.scopeindex, False])
|
||||||
self.breaktargets.append(brk)
|
self.continuestack.append([self.GenerateLabel(), None, False])
|
||||||
self.breakscopes.append(self.scopeindex)
|
|
||||||
self.breaksused.append(False)
|
|
||||||
cnt = self.GenerateLabel()
|
|
||||||
self.continuetargets.append(cnt)
|
|
||||||
self.continuescopes.append(None)
|
|
||||||
self.continuesused.append(False)
|
|
||||||
stmt = self.Parse_statement(ReturnType, AllowStSw = True, InsideLoop = True)
|
stmt = self.Parse_statement(ReturnType, AllowStSw = True, InsideLoop = True)
|
||||||
self.expect('WHILE')
|
self.expect('WHILE')
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
|
@ -1461,22 +1443,16 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
ret = {'nt':'DO', 't':None, 'ch':[stmt, condition]}
|
ret = {'nt':'DO', 't':None, 'ch':[stmt, condition]}
|
||||||
if self.breakcont:
|
if self.breakcont:
|
||||||
if self.continuesused.pop():
|
last = self.continuestack.pop()
|
||||||
|
if last[2]:
|
||||||
assert ret['ch'][0]['nt'] == '{}'
|
assert ret['ch'][0]['nt'] == '{}'
|
||||||
ret['ch'][0]['ch'].append(
|
ret['ch'][0]['ch'].append({'nt':'@', 't':None, 'name':last[0], 'scope':last[1]})
|
||||||
{'nt':'@', 't':None, 'name':cnt, 'scope':self.continuescopes[-1]}
|
self.AddSymbol('l', last[1], last[0])
|
||||||
)
|
|
||||||
self.AddSymbol('l', self.continuescopes[-1], cnt)
|
|
||||||
self.continuescopes.pop()
|
|
||||||
self.continuetargets.pop()
|
|
||||||
|
|
||||||
if self.breaksused.pop():
|
last = self.breakstack.pop()
|
||||||
ret = {'nt':'{}', 't':None, 'ch':[ret,
|
if last[2]:
|
||||||
{'nt':'@', 't':None, 'name':brk, 'scope':self.scopeindex}
|
ret = {'nt':'{}', 't':None, 'ch':[ret, {'nt':'@', 't':None, 'name':last[0], 'scope':last[1]}]}
|
||||||
]}
|
self.AddSymbol('l', last[1], last[0])
|
||||||
self.AddSymbol('l', self.scopeindex, brk)
|
|
||||||
self.breakscopes.pop()
|
|
||||||
self.breaktargets.pop()
|
|
||||||
self.PopScope()
|
self.PopScope()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -1485,14 +1461,8 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
if self.breakcont:
|
if self.breakcont:
|
||||||
self.PushScope()
|
self.PushScope()
|
||||||
|
|
||||||
brk = self.GenerateLabel()
|
self.breakstack.append([self.GenerateLabel(), self.scopeindex, False])
|
||||||
self.breaktargets.append(brk)
|
self.continuestack.append([self.GenerateLabel(), None, False])
|
||||||
self.breakscopes.append(self.scopeindex)
|
|
||||||
self.breaksused.append(False)
|
|
||||||
cnt = self.GenerateLabel()
|
|
||||||
self.continuetargets.append(cnt)
|
|
||||||
self.continuescopes.append(None)
|
|
||||||
self.continuesused.append(False)
|
|
||||||
self.expect('(')
|
self.expect('(')
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
initializer = self.Parse_optional_expression_list()
|
initializer = self.Parse_optional_expression_list()
|
||||||
|
@ -1511,22 +1481,16 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
{'nt':'EXPRLIST','t':None, 'ch':iterator},
|
{'nt':'EXPRLIST','t':None, 'ch':iterator},
|
||||||
stmt]}
|
stmt]}
|
||||||
if self.breakcont:
|
if self.breakcont:
|
||||||
if self.continuesused.pop():
|
last = self.continuestack.pop()
|
||||||
assert ret['ch'][0]['nt'] == '{}'
|
if last[2]:
|
||||||
ret['ch'][0]['ch'].append(
|
assert ret['ch'][3]['nt'] == '{}'
|
||||||
{'nt':'@', 't':None, 'name':cnt, 'scope':self.continuescopes[-1]}
|
ret['ch'][3]['ch'].append({'nt':'@', 't':None, 'name':last[0], 'scope':last[1]})
|
||||||
)
|
self.AddSymbol('l', last[1], last[0])
|
||||||
self.AddSymbol('l', self.continuescopes[-1], cnt)
|
|
||||||
self.continuescopes.pop()
|
|
||||||
self.continuetargets.pop()
|
|
||||||
|
|
||||||
if self.breaksused.pop():
|
last = self.breakstack.pop()
|
||||||
ret = {'nt':'{}', 't':None, 'ch':[ret,
|
if last[2]:
|
||||||
{'nt':'@', 't':None, 'name':brk, 'scope':self.scopeindex}
|
ret = {'nt':'{}', 't':None, 'ch':[ret, {'nt':'@', 't':None, 'name':last[0], 'scope':last[1]}]}
|
||||||
]}
|
self.AddSymbol('l', last[1], last[0])
|
||||||
self.AddSymbol('l', self.scopeindex, brk)
|
|
||||||
self.breakscopes.pop()
|
|
||||||
self.breaktargets.pop()
|
|
||||||
self.PopScope()
|
self.PopScope()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -1538,13 +1502,10 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
self.expect(')')
|
self.expect(')')
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
brk = self.GenerateLabel()
|
brk = self.GenerateLabel()
|
||||||
self.breaktargets.append(brk)
|
self.breakstack.append([brk, None, False])
|
||||||
self.breakscopes.append(None) # not known yet - entering the block will tell us
|
|
||||||
self.breaksused.append(False)
|
|
||||||
blk = self.Parse_code_block(ReturnType, AllowStSw = AllowStSw,
|
blk = self.Parse_code_block(ReturnType, AllowStSw = AllowStSw,
|
||||||
InsideSwitch = True, InsideLoop = InsideLoop)
|
InsideSwitch = True, InsideLoop = InsideLoop)
|
||||||
blkscope = self.breakscopes[-1]
|
blkscope = self.breakstack[-1][1]
|
||||||
self.AddSymbol('l', blkscope, brk)
|
|
||||||
|
|
||||||
# Replace the block
|
# Replace the block
|
||||||
# switch (expr1) { case expr2: stmts1; break; default: stmts2; }
|
# switch (expr1) { case expr2: stmts1; break; default: stmts2; }
|
||||||
|
@ -1569,7 +1530,7 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
# Since label scope rules prevent us from being able to jump inside
|
# Since label scope rules prevent us from being able to jump inside
|
||||||
# a nested block, only one nesting level is considered.
|
# a nested block, only one nesting level is considered.
|
||||||
assert blk['nt'] == '{}'
|
assert blk['nt'] == '{}'
|
||||||
blk = blk['ch']
|
blk = blk['ch'] # Disregard the '{}' - we'll add it back later
|
||||||
for idx in xrange(len(blk)):
|
for idx in xrange(len(blk)):
|
||||||
if blk[idx]['nt'] == 'CASE':
|
if blk[idx]['nt'] == 'CASE':
|
||||||
lbl = self.GenerateLabel()
|
lbl = self.GenerateLabel()
|
||||||
|
@ -1602,21 +1563,22 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
|
|
||||||
if switchcasedefault is None:
|
if switchcasedefault is None:
|
||||||
switchcasedefault = brk
|
switchcasedefault = brk
|
||||||
self.breaksused[-1] = True
|
self.breakstack[-1][2] = True
|
||||||
prelude.append({'nt':'JUMP', 't':None, 'name':switchcasedefault,
|
prelude.append({'nt':'JUMP', 't':None, 'name':switchcasedefault,
|
||||||
'scope':blkscope})
|
'scope':blkscope})
|
||||||
self.breaktargets.pop()
|
last = self.breakstack.pop()
|
||||||
self.breakscopes.pop()
|
if last[2]:
|
||||||
if self.breaksused.pop():
|
|
||||||
blk.append({'nt':'@', 'name':brk, 'scope':blkscope})
|
blk.append({'nt':'@', 'name':brk, 'scope':blkscope})
|
||||||
|
self.AddSymbol('l', blkscope, brk)
|
||||||
return {'nt':'{}', 't':None, 'ch':prelude + blk}
|
return {'nt':'{}', 't':None, 'ch':prelude + blk}
|
||||||
|
|
||||||
if tok0 == 'CASE':
|
if tok0 == 'CASE':
|
||||||
if not InsideSwitch:
|
if not InsideSwitch:
|
||||||
raise EParseInvalidCase(self, u"case")
|
raise EParseInvalidCase(self, u"case")
|
||||||
if self.scopeindex != self.breakscopes[-1]:
|
if self.scopeindex != self.breakstack[-1][1]:
|
||||||
# If this block is nested and not the main switch block, this
|
# If this block is nested and not the main switch block, this
|
||||||
# won't work. Label scope rules don't expose the nested labels.
|
# won't work. LSL label scope rules don't expose the nested
|
||||||
|
# labels. Nothing we can do about that.
|
||||||
raise EParseCaseNotAllowed(self, u"case")
|
raise EParseCaseNotAllowed(self, u"case")
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
expr = self.Parse_expression()
|
expr = self.Parse_expression()
|
||||||
|
@ -1628,9 +1590,10 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
if self.enableswitch:
|
if self.enableswitch:
|
||||||
if not InsideSwitch:
|
if not InsideSwitch:
|
||||||
raise EParseInvalidCase(self, u"default")
|
raise EParseInvalidCase(self, u"default")
|
||||||
if self.scopeindex != self.breakscopes[-1]:
|
if self.scopeindex != self.breakstack[-1][1]:
|
||||||
# If this block is nested and not the main switch block, this
|
# If this block is nested and not the main switch block, this
|
||||||
# won't work. Label scope rules don't expose the nested labels.
|
# won't work. Label scope rules don't expose the nested
|
||||||
|
# labels. Nothing we can do about that.
|
||||||
raise EParseCaseNotAllowed(self, u"default")
|
raise EParseCaseNotAllowed(self, u"default")
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
self.expect(':')
|
self.expect(':')
|
||||||
|
@ -1639,27 +1602,55 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
# else fall through to eventually fail
|
# else fall through to eventually fail
|
||||||
|
|
||||||
if tok0 == 'BREAK':
|
if tok0 == 'BREAK':
|
||||||
if not self.breaktargets:
|
if not self.breakstack:
|
||||||
raise EParseInvalidBreak(self)
|
raise EParseInvalidBreak(self)
|
||||||
self.breaksused[-1] = True
|
self.NextToken()
|
||||||
|
n = -1
|
||||||
|
if self.tok[0] == 'INTEGER_VALUE':
|
||||||
|
if self.tok[1] <= 0:
|
||||||
|
raise EParseInvalidBreak(self)
|
||||||
|
n = -self.tok[1]
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
self.expect(';')
|
self.expect(';')
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
return {'nt':'JUMP', 't':None, 'name':self.breaktargets[-1],
|
try:
|
||||||
'scope':self.breakscopes[-1]}
|
self.breakstack[n][2] = True
|
||||||
|
except IndexError:
|
||||||
|
raise EParseInvalidBreak(self)
|
||||||
|
return {'nt':'JUMP', 't':None, 'name':self.breakstack[n][0],
|
||||||
|
'scope':self.breakstack[n][1]}
|
||||||
|
|
||||||
if tok0 == 'CONTINUE':
|
if tok0 == 'CONTINUE':
|
||||||
if not self.continuetargets:
|
if not self.continuestack:
|
||||||
raise EParseInvalidCont(self)
|
raise EParseInvalidCont(self)
|
||||||
if self.continuescopes[-1] is None:
|
self.NextToken()
|
||||||
# We're not inside a block - 'continue' is essentially a nop
|
n = -1
|
||||||
return {'nt':';', 't':'None'}
|
if self.tok[0] == 'INTEGER_VALUE':
|
||||||
self.continuesused[-1] = True
|
if self.tok[1] <= 0:
|
||||||
|
raise EParseInvalidCont(self)
|
||||||
|
n = -self.tok[1]
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
self.expect(';')
|
self.expect(';')
|
||||||
self.NextToken()
|
self.NextToken()
|
||||||
return {'nt':'JUMP', 't':None, 'name':self.continuetargets[-1],
|
if n == -1 and self.continuestack[-1][1] is None:
|
||||||
'scope':self.continuescopes[-1]}
|
# We're not inside a block - 'continue' is essentially a nop
|
||||||
|
# e.g. while (cond) continue; is the same as while (cond) ;
|
||||||
|
return {'nt':';', 't':'None'}
|
||||||
|
try:
|
||||||
|
if self.continuestack[n][1] is None:
|
||||||
|
# this can happen with e.g.:
|
||||||
|
# while (cond) while (cond) while (cond) continue 3;
|
||||||
|
# Transform to while(cond) while(cond) while(cond) break 2;
|
||||||
|
# which is equivalent since there are no {}.
|
||||||
|
n += 1 # e.g. -3 -> -2
|
||||||
|
self.breakstack[n][2] = True # mark the break as used
|
||||||
|
return {'nt':'JUMP', 't':None, 'name':self.breakstack[n][0],
|
||||||
|
'scope':self.breakstack[n][1]}
|
||||||
|
except IndexError:
|
||||||
|
raise EParseInvalidCont(self)
|
||||||
|
self.continuestack[n][2] = True
|
||||||
|
return {'nt':'JUMP', 't':None, 'name':self.continuestack[n][0],
|
||||||
|
'scope':self.continuestack[n][1]}
|
||||||
|
|
||||||
if tok0 == 'TYPE':
|
if tok0 == 'TYPE':
|
||||||
if not AllowDecl:
|
if not AllowDecl:
|
||||||
|
@ -1703,11 +1694,11 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
|
|
||||||
# Kludge to find the scope of the break (for switch) /
|
# Kludge to find the scope of the break (for switch) /
|
||||||
# continue (for loops) labels.
|
# continue (for loops) labels.
|
||||||
if self.breakscopes: # non-empty iff inside loop or switch
|
if self.breakstack: # non-empty iff inside loop or switch
|
||||||
if InsideSwitch and self.breakscopes[-1] is None:
|
if InsideSwitch and self.breakstack[-1][1] is None:
|
||||||
self.breakscopes[-1] = self.scopeindex
|
self.breakstack[-1][1] = self.scopeindex
|
||||||
if InsideLoop and self.continuescopes[-1] is None:
|
if InsideLoop and self.continuestack[-1][1] is None:
|
||||||
self.continuescopes[-1] = self.scopeindex
|
self.continuestack[-1][1] = self.scopeindex
|
||||||
|
|
||||||
body = []
|
body = []
|
||||||
LastIsReturn = False
|
LastIsReturn = False
|
||||||
|
@ -2197,18 +2188,12 @@ list lazy_list_set(list L, integer i, list v)
|
||||||
if self.breakcont:
|
if self.breakcont:
|
||||||
self.keywords |= frozenset(('break', 'continue'))
|
self.keywords |= frozenset(('break', 'continue'))
|
||||||
|
|
||||||
# Stack to track the labels for break targets.
|
# Stack to track the labels for break targets, their scope table index,
|
||||||
self.breaktargets = []
|
# and whether they are used.
|
||||||
# Stack to track the scope of the break label.
|
self.breakstack = []
|
||||||
self.breakscopes = []
|
# Stack to track the labels for continue targets, their scope index,
|
||||||
# Stack to track whether the break target label is used.
|
# and whether they are used.
|
||||||
self.breaksused = []
|
self.continuestack = []
|
||||||
# Stack to track the labels for continue targets.
|
|
||||||
self.continuetargets = []
|
|
||||||
# Stack to track the scope for continue targets.
|
|
||||||
self.continuescopes = []
|
|
||||||
# Stack to track whether the continue target label is used.
|
|
||||||
self.continuesused = []
|
|
||||||
|
|
||||||
# Enable use of local labels with duplicate names
|
# Enable use of local labels with duplicate names
|
||||||
self.duplabels = 'duplabels' in options
|
self.duplabels = 'duplabels' in options
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue