compiler: support break in switch statements

This commit is contained in:
Evgenii Stratonikov 2020-02-18 17:07:38 +03:00
parent 03dc6f7cbb
commit ccb53414f2
2 changed files with 76 additions and 2 deletions

View file

@ -43,6 +43,8 @@ type codegen struct {
// A label for the for-loop being currently visited.
currentFor string
// A label for the switch statement being visited.
currentSwitch string
// A label to be used in the next statement.
nextLabel string
@ -403,7 +405,10 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.Tag)
eqOpcode := c.getEqualityOpcode(n.Tag)
switchEnd := c.newLabel()
switchEnd, label := c.generateLabel(labelEnd)
lastSwitch := c.currentSwitch
c.currentSwitch = label
for i := range n.Body.List {
lEnd := c.newLabel()
@ -434,6 +439,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.setLabel(switchEnd)
emit.Opcode(c.prog.BinWriter, opcode.DROP)
c.currentSwitch = lastSwitch
return nil
case *ast.BasicLit:
@ -683,9 +690,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil
case *ast.BranchStmt:
label := c.currentFor
var label string
if n.Label != nil {
label = n.Label.Name
} else if n.Tok == token.BREAK {
label = c.currentSwitch
} else if n.Tok == token.CONTINUE {
label = c.currentFor
}
switch n.Tok {
@ -712,7 +723,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
fpost := c.newNamedLabel(labelPost, label)
lastLabel := c.currentFor
lastSwitch := c.currentSwitch
c.currentFor = label
c.currentSwitch = label
// Walk the initializer and condition.
if n.Init != nil {
@ -738,6 +751,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.setLabel(fend)
c.currentFor = lastLabel
c.currentSwitch = lastSwitch
return nil

View file

@ -127,6 +127,66 @@ var switchTestCases = []testCase{
}`,
big.NewInt(4),
},
{
"break from switch",
`package main
func Main() int {
i := 3
switch i {
case 2: return 2
case 3:
i = 1
break
return 3
case 4: return 4
}
return i
}`,
big.NewInt(1),
},
{
"break from outer for",
`package main
func Main() int {
i := 3
loop:
for i < 10 {
i++
switch i {
case 5:
i = 7
break loop
return 3
case 6: return 4
}
}
return i
}`,
big.NewInt(7),
},
{
"continue outer for",
`package main
func Main() int {
i := 2
for i < 10 {
i++
switch i {
case 3:
i = 7
continue
case 4, 5, 6, 7: return 5
case 8: return 2
}
if i == 7 {
return 6
}
}
return i
}`,
big.NewInt(2),
},
}
func TestSwitch(t *testing.T) {