compiler: support continue statement in for loops

This commit is contained in:
Evgenii Stratonikov 2020-02-17 17:19:54 +03:00
parent fa2edc46e0
commit 03dc6f7cbb
2 changed files with 83 additions and 1 deletions

View file

@ -55,6 +55,7 @@ type labelOffsetType byte
const ( const (
labelStart labelOffsetType = iota // labelStart is a default label type labelStart labelOffsetType = iota // labelStart is a default label type
labelEnd // labelEnd is a type for labels that are targets for break labelEnd // labelEnd is a type for labels that are targets for break
labelPost // labelPost is a type for labels that are targets for continue
) )
type labelWithType struct { type labelWithType struct {
@ -692,7 +693,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
end := c.getLabelOffset(labelEnd, label) end := c.getLabelOffset(labelEnd, label)
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(end)) emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(end))
case token.CONTINUE: case token.CONTINUE:
c.prog.Err = fmt.Errorf("continue statement is not supported yet") post := c.getLabelOffset(labelPost, label)
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(post))
} }
return nil return nil
@ -707,6 +709,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.ForStmt: case *ast.ForStmt:
fstart, label := c.generateLabel(labelStart) fstart, label := c.generateLabel(labelStart)
fend := c.newNamedLabel(labelEnd, label) fend := c.newNamedLabel(labelEnd, label)
fpost := c.newNamedLabel(labelPost, label)
lastLabel := c.currentFor lastLabel := c.currentFor
c.currentFor = label c.currentFor = label
@ -725,6 +728,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// Walk body followed by the iterator (post stmt). // Walk body followed by the iterator (post stmt).
ast.Walk(c, n.Body) ast.Walk(c, n.Body)
c.setLabel(fpost)
if n.Post != nil { if n.Post != nil {
ast.Walk(c, n.Post) ast.Walk(c, n.Post)
} }

View file

@ -538,6 +538,84 @@ func TestForLoopNestedBreakLabel(t *testing.T) {
eval(t, src, big.NewInt(5)) eval(t, src, big.NewInt(5))
} }
func TestForLoopContinue(t *testing.T) {
src := `
package foo
func Main() int {
var i, j int
for i < 10 {
i++
if i >= 5 {
continue
}
j++
}
return j
}`
eval(t, src, big.NewInt(4))
}
func TestForLoopContinueLabel(t *testing.T) {
src := `
package foo
func Main() int {
var i, j int
loop:
for i < 10 {
i++
if i >= 5 {
continue loop
}
j++
}
return j
}`
eval(t, src, big.NewInt(4))
}
func TestForLoopNestedContinue(t *testing.T) {
src := `
package foo
func Main() int {
var i, k int
for i < 10 {
i++
for j := 0; j < 3; j++ {
if j >= 2 {
continue
}
k++
}
}
return k
}`
eval(t, src, big.NewInt(20))
}
func TestForLoopNestedContinueLabel(t *testing.T) {
src := `
package foo
func Main() int {
var i int
loop:
for ; i < 10; i += 10 {
i++
for j := 0; j < 4; j++ {
if i == 5 {
continue loop
}
i++
}
}
return i
}`
eval(t, src, big.NewInt(15))
}
func TestForLoopRangeNoVariable(t *testing.T) { func TestForLoopRangeNoVariable(t *testing.T) {
src := ` src := `
package foo package foo