compiler: support for-range loops

This commit is contained in:
Evgenii Stratonikov 2020-02-11 15:26:11 +03:00
parent bcff9faac4
commit 310f7449d7
2 changed files with 122 additions and 0 deletions

View file

@ -660,6 +660,45 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil
case *ast.RangeStmt:
// currently only simple for-range loops are supported
// for i := range ...
if n.Value != nil {
c.prog.Err = errors.New("range loops with value variable are not supported")
return nil
}
start := c.newLabel()
end := c.newLabel()
ast.Walk(c, n.X)
emit.Opcode(c.prog.BinWriter, opcode.ARRAYSIZE)
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
c.setLabel(start)
emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcode(c.prog.BinWriter, opcode.LTE) // finish if len <= i
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, int16(end))
if n.Key != nil {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
pos := c.scope.loadLocal(n.Key.(*ast.Ident).Name)
c.emitStoreLocal(pos)
}
ast.Walk(c, n.Body)
emit.Opcode(c.prog.BinWriter, opcode.INC)
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(start))
c.setLabel(end)
return nil
// We dont really care about assertions for the core logic.
// The only thing we need is to please the compiler type checking.
// For this to work properly, we only need to walk the expression

View file

@ -2,9 +2,13 @@ package compiler_test
import (
"math/big"
"strings"
"testing"
"github.com/CityOfZion/neo-go/pkg/compiler"
"github.com/CityOfZion/neo-go/pkg/vm"
"github.com/stretchr/testify/require"
)
func TestEntryPointWithMethod(t *testing.T) {
@ -397,3 +401,82 @@ func TestForLoopNoPost(t *testing.T) {
`
eval(t, src, big.NewInt(10))
}
func TestForLoopRange(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
arr := []int{1, 2, 3}
for i := range arr {
sum += arr[i]
}
return sum
}`
eval(t, src, big.NewInt(6))
}
func TestForLoopRangeGlobalIndex(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
i := 0
arr := []int{1, 2, 3}
for i = range arr {
sum += arr[i]
}
return sum + i
}`
eval(t, src, big.NewInt(8))
}
func TestForLoopRangeChangeVariable(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
arr := []int{1, 2, 3}
for i := range arr {
sum += arr[i]
i++
sum += i
}
return sum
}`
eval(t, src, big.NewInt(12))
}
func TestForLoopRangeNoVariable(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
arr := []int{1, 2, 3}
for range arr {
sum += 1
}
return sum
}`
eval(t, src, big.NewInt(3))
}
func TestForLoopRangeCompilerError(t *testing.T) {
src := `
package foo
func f(a int) int { return 0 }
func Main() int {
arr := []int{1, 2, 3}
for _, v := range arr {
f(v)
}
return 0
}`
_, err := compiler.Compile(strings.NewReader(src))
require.Error(t, err)
}