forked from TrueCloudLab/neoneo-go
compiler: support for-range loops
This commit is contained in:
parent
bcff9faac4
commit
310f7449d7
2 changed files with 122 additions and 0 deletions
|
@ -660,6 +660,45 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
|
|
||||||
return nil
|
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.
|
// We dont really care about assertions for the core logic.
|
||||||
// The only thing we need is to please the compiler type checking.
|
// The only thing we need is to please the compiler type checking.
|
||||||
// For this to work properly, we only need to walk the expression
|
// For this to work properly, we only need to walk the expression
|
||||||
|
|
|
@ -2,9 +2,13 @@ package compiler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/compiler"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEntryPointWithMethod(t *testing.T) {
|
func TestEntryPointWithMethod(t *testing.T) {
|
||||||
|
@ -397,3 +401,82 @@ func TestForLoopNoPost(t *testing.T) {
|
||||||
`
|
`
|
||||||
eval(t, src, big.NewInt(10))
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue