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:
Sei Lisa 2015-03-05 20:12:32 +01:00
parent d4035b0723
commit 308ca7c8ff

View file

@ -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