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

View file

@ -127,6 +127,66 @@ var switchTestCases = []testCase{
}`, }`,
big.NewInt(4), 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) { func TestSwitch(t *testing.T) {