Repository: goplus/gop
Pull request #563 (2020-08-20 11:11:55)

re-implement range

codecov-commenter: # [Codecov](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=h1) Report
> Merging [#563](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=desc) into [master](https://codecov.io/gh/goplus/gop/commit/57fa3c6f480a461b817e6397ee47ac76b5b5ee5f&el=desc) will **decrease** coverage by `0.09%`.
> The diff coverage is `84.67%`.

[![Impacted file tree graph](https://codecov.io/gh/goplus/gop/pull/563/graphs/tree.svg?width=650&height=150&src=pr&token=4qln9Wk3Pn)](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #563      +/-   ##
==========================================
- Coverage   90.22%   90.12%   -0.10%     
==========================================
  Files          40       41       +1     
  Lines       10329    10473     +144     
==========================================
+ Hits         9319     9439     +120     
- Misses        722      744      +22     
- Partials      288      290       +2     
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [ast/gopiter/gop\_iter.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-YXN0L2dvcGl0ZXIvZ29wX2l0ZXIuZ28=) | `62.22% <62.22%> (ø)` | |
| [cl/stmt.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-Y2wvc3RtdC5nbw==) | `94.04% <97.36%> (+0.45%)` | :arrow_up: |
| [exec/golang/expr.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvZXhwci5nbw==) | `94.16% <100.00%> (+0.04%)` | :arrow_up: |
| [exec/bytecode/goinstr\_lstcompr.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9nb2luc3RyX2xzdGNvbXByLmdv) | `92.34% <0.00%> (-1.06%)` | :arrow_down: |
| [exec/golang/stmt.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvc3RtdC5nbw==) | `95.96% <0.00%> (-0.81%)` | :arrow_down: |
| [cmd/internal/export/gopkg/export.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-Y21kL2ludGVybmFsL2V4cG9ydC9nb3BrZy9leHBvcnQuZ28=) | `91.55% <0.00%> (+0.05%)` | :arrow_up: |
| [cl/expr.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-Y2wvZXhwci5nbw==) | `86.45% <0.00%> (+0.17%)` | :arrow_up: |
| [cmd/internal/export/gopkg/exporter.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-Y21kL2ludGVybmFsL2V4cG9ydC9nb3BrZy9leHBvcnRlci5nbw==) | `93.18% <0.00%> (+0.27%)` | :arrow_up: |
| [cl/type\_decl.go](https://codecov.io/gh/goplus/gop/pull/563/diff?src=pr&el=tree#diff-Y2wvdHlwZV9kZWNsLmdv) | `85.71% <0.00%> (+0.99%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=footer). Last update [57fa3c6...33c5ee9](https://codecov.io/gh/goplus/gop/pull/563?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/ast/gopiter/gop_iter.go b/ast/gopiter/gop_iter.go
new file mode 100644
index 00000000..cb998058
--- /dev/null
+++ b/ast/gopiter/gop_iter.go
@@ -0,0 +1,67 @@
+package gopiter
+
+import (
+	"log"
+	"reflect"
+)
+
+type sliceIter struct {
+	slice    reflect.Value
+	sliceLen int
+	cursor   int
+}
+
+func newSliceIter(val reflect.Value) *sliceIter {
+	return &sliceIter{
+		slice:    val,
+		sliceLen: val.Len(),
+		cursor:   0,
+	}
+}
+
+func (p *sliceIter) Next() bool {
+	p.cursor++
+	return p.cursor-1 < p.sliceLen
+}
+
+func (p *sliceIter) Key() reflect.Value {
+	return reflect.ValueOf(p.cursor - 1)
+}
+
+func (p *sliceIter) Value() reflect.Value {
+	return p.slice.Index(p.cursor - 1)
+}
+
+type Iterator interface {
+	Next() bool
+	Key() reflect.Value
+	Value() reflect.Value
+}
+
+func NewIter(container interface{}) (iter Iterator) {
+	data := reflect.ValueOf(container)
+	switch data.Kind() {
+	case reflect.Map:
+		iter = data.MapRange()
+	case reflect.Slice, reflect.Array, reflect.String:
+		iter = newSliceIter(reflect.Indirect(data))
+	case reflect.Struct:
+		if v, ok := container.(Iterator); ok {
+			iter = v
+		} else {
+			log.Panicln("iter must be Iterator")
+		}
+	}
+	return
+}
+
+func Key(i Iterator, key interface{}) {
+	reflect.ValueOf(key).Elem().Set(i.Key())
+}
+
+func Value(i Iterator, value interface{}) {
+	reflect.ValueOf(value).Elem().Set(i.Value())
+}
+func Next(i Iterator) bool {
+	return i.Next()
+}
diff --git a/cl/stmt.go b/cl/stmt.go
index 1f83a98f..5ddd64d3 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -98,16 +98,18 @@ func compileForPhraseStmt(parent *blockCtx, v *ast.ForPhraseStmt) {
 }
 
 func compileRangeStmt(parent *blockCtx, v *ast.RangeStmt) {
-	noExecCtx := isNoExecCtx(parent, v.Body)
-	f := ast.ForPhrase{
-		For:    v.For,
-		TokPos: v.TokPos,
-		X:      v.X,
-	}
+	kvDef := map[string]reflect.Type{}
+	newIterName := "_gop_NewIter"
+	nextName := "_gop_Next"
+	keyName := "_gop_Key"
+	valueName := "_gop_Value"
+	iter := &ast.Ident{Name: "_gop_iter", NamePos: v.For}
+
+	var keyIdent, valIdent *ast.Ident
 	switch v.Tok {
 	case token.DEFINE:
-		f.Key = toIdent(v.Key)
-		f.Value = toIdent(v.Value)
+		keyIdent = toIdent(v.Key)
+		valIdent = toIdent(v.Value)
 	case token.ASSIGN:
 		var lhs, rhs [2]ast.Expr
 		var idx int
@@ -118,8 +120,8 @@ func compileRangeStmt(parent *blockCtx, v *ast.RangeStmt) {
 			}
 			if assign {
 				k0 := ast.NewObj(ast.Var, "_gop_k")
-				f.Key = &ast.Ident{Name: k0.Name, Obj: k0}
-				lhs[idx], rhs[idx] = v.Key, f.Key
+				keyIdent = &ast.Ident{Name: k0.Name, Obj: k0}
+				lhs[idx], rhs[idx] = v.Key, keyIdent
 				idx++
 			}
 		}
@@ -130,8 +132,8 @@ func compileRangeStmt(parent *blockCtx, v *ast.RangeStmt) {
 			}
 			if assign {
 				v0 := ast.NewObj(ast.Var, "_gop_v")
-				f.Value = &ast.Ident{Name: v0.Name, Obj: v0}
-				lhs[idx], rhs[idx] = v.Value, f.Value
+				valIdent = &ast.Ident{Name: v0.Name, Obj: v0}
+				lhs[idx], rhs[idx] = v.Value, valIdent
 				idx++
 			}
 		}
@@ -141,10 +143,86 @@ func compileRangeStmt(parent *blockCtx, v *ast.RangeStmt) {
 			Rhs: rhs[0:idx],
 		}}, v.Body.List...)
 	}
-	ctx, exprFor := compileForPhrase(parent, f, noExecCtx)
-	exprFor(func() {
-		compileBlockStmtWithout(ctx, v.Body)
-	})
+
+	compileExpr(parent, v.X)
+	typData := boundType(parent.infer.Pop().(iValue))
+	var typKey, typVal reflect.Type
+	var forStmts []ast.Stmt
+	switch kind := typData.Kind(); kind {
+	case reflect.String:
+		typKey = exec.TyInt
+		typVal = exec.TyByte
+	case reflect.Slice, reflect.Array:
+		typKey = exec.TyInt
+		typVal = typData.Elem()
+	case reflect.Map:
+		typKey = typData.Key()
+		typVal = typData.Elem()
+	default:
+		log.Panicln("compileRangeStmt: require slice, array or map")
+	}
+	if keyIdent != nil {
+		// Key(iter,&k)
+		if id, ok := v.Key.(*ast.Ident); !ok || (ok && id.Name != "_") {
+			kvDef[keyIdent.Name] = typKey
+			forStmts = append(forStmts, &ast.ExprStmt{X: &ast.CallExpr{
+				Fun: &ast.Ident{Name: keyName, NamePos: v.For},
+				Args: []ast.Expr{iter, &ast.UnaryExpr{
+					Op: token.AND,
+					X:  keyIdent,
+				}},
+			}})
+		}
+	}
+	if valIdent != nil {
+		// Value(iter,&v)
+		if id, ok := v.Value.(*ast.Ident); !ok || (ok && id.Name != "_") {
+			kvDef[valIdent.Name] = typVal
+			forStmts = append(forStmts, &ast.ExprStmt{X: &ast.CallExpr{
+				Fun: &ast.Ident{Name: valueName, NamePos: v.For},
+				Args: []ast.Expr{iter, &ast.UnaryExpr{
+					Op: token.AND,
+					X:  valIdent,
+				}},
+			}})
+		}
+	}
+	// iter:=_gop_NewIter(obj)
+	init := &ast.AssignStmt{
+		Lhs: []ast.Expr{iter},
+		Tok: token.DEFINE,
+		Rhs: []ast.Expr{&ast.CallExpr{
+			Fun:    &ast.Ident{Name: newIterName},
+			Lparen: v.For,
+			Rparen: v.For,
+			Args:   []ast.Expr{v.X},
+		}},
+		TokPos: v.For,
+	}
+	// range => for
+	fs := &ast.ForStmt{
+		For: v.For,
+		Cond: &ast.CallExpr{
+			Fun:    &ast.Ident{Name: nextName},
+			Lparen: v.For,
+			Rparen: v.For,
+			Args:   []ast.Expr{iter},
+		},
+		Post: nil,
+		Body: &ast.BlockStmt{
+			Lbrace: v.Body.Lbrace,
+			List:   append(forStmts, v.Body.List...),
+			Rbrace: v.Body.Rbrace,
+		},
+	}
+	parent.out.DefineBlock()
+	defer parent.out.EndBlock()
+	ctx := newNormBlockCtx(parent)
+	for k, v := range kvDef {
+		ctx.insertVar(k, v)
+	}
+	compileStmt(ctx, init)
+	compileStmt(ctx, fs)
 }
 
 func toIdent(e ast.Expr) *ast.Ident {
diff --git a/exec/golang/expr.go b/exec/golang/expr.go
index 82d185c5..9c206c32 100644
--- a/exec/golang/expr.go
+++ b/exec/golang/expr.go
@@ -387,6 +387,11 @@ func (p *Builder) Call(narg int, ellipsis bool, args ...ast.Expr) *Builder {
 func (p *Builder) CallGoFunc(fun exec.GoFuncAddr, nexpr int) *Builder {
 	gfi := defaultImpl.GetGoFuncInfo(fun)
 	pkgPath, name := gfi.Pkg.PkgPath(), gfi.Name
+	if pkgPath == "" {
+		if alias, ok := builtin.FuncGoInfo(name); ok {
+			pkgPath, name = alias[0], alias[1]
+		}
+	}
 	fn := p.GoSymIdent(pkgPath, name)
 	p.rhs.Push(fn)
 	return p.Call(nexpr, false)
@@ -406,14 +411,6 @@ func (p *Builder) CallGoFuncv(fun exec.GoFuncvAddr, nexpr, arity int) *Builder {
 	return p.Call(nexpr, arity == -1)
 }
 
-var builtinFnvs = map[string][2]string{
-	"errorf":  {"fmt", "Errorf"},
-	"print":   {"fmt", "Print"},
-	"printf":  {"fmt", "Printf"},
-	"println": {"fmt", "Println"},
-	"fprintf": {"fmt", "Fprintf"},
-}
-
 // LoadGoVar instr
 func (p *Builder) LoadGoVar(addr exec.GoVarAddr) *Builder {
 	gvi := defaultImpl.GetGoVarInfo(addr)
diff --git a/lib/builtin/exports.go b/lib/builtin/exports.go
index 10f51b36..b0696b9a 100644
--- a/lib/builtin/exports.go
+++ b/lib/builtin/exports.go
@@ -25,6 +25,7 @@ import (
 	"reflect"
 
 	"github.com/goplus/gop"
+	"github.com/goplus/gop/ast/gopiter"
 
 	qspec "github.com/goplus/gop/exec.spec"
 	exec "github.com/goplus/gop/exec/bytecode"
@@ -74,6 +75,32 @@ func QexecIs(_ int, p *gop.Context) {
 	p.Ret(2, is)
 }
 
+// QNewIter instr
+func QNewIter(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	p.Ret(1, gopiter.NewIter(args[0]))
+}
+
+// QNext instr
+func QNext(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	p.Ret(1, gopiter.Next(args[0].(gopiter.Iterator)))
+}
+
+// QKey instr
+func QKey(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	gopiter.Key(args[0].(gopiter.Iterator), args[1])
+	p.PopN(2)
+}
+
+// QValue instr
+func QValue(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	gopiter.Value(args[0].(gopiter.Iterator), args[1])
+	p.PopN(2)
+}
+
 // FuncGoInfo returns Go package and function name of a Go+ builtin function.
 func FuncGoInfo(f string) ([2]string, bool) {
 	fi, ok := builtinFnvs[f]
@@ -92,17 +119,25 @@ func execPanic(_ int, p *gop.Context) {
 var I = gop.NewGoPackage("")
 
 var builtinFnvs = map[string][2]string{
-	"errorf":  {"fmt", "Errorf"},
-	"print":   {"fmt", "Print"},
-	"printf":  {"fmt", "Printf"},
-	"println": {"fmt", "Println"},
-	"fprintf": {"fmt", "Fprintf"},
+	"errorf":       {"fmt", "Errorf"},
+	"print":        {"fmt", "Print"},
+	"printf":       {"fmt", "Printf"},
+	"println":      {"fmt", "Println"},
+	"fprintf":      {"fmt", "Fprintf"},
+	"_gop_NewIter": {"github.com/goplus/gop/ast/gopiter", "NewIter"},
+	"_gop_Next":    {"github.com/goplus/gop/ast/gopiter", "Next"},
+	"_gop_Key":     {"github.com/goplus/gop/ast/gopiter", "Key"},
+	"_gop_Value":   {"github.com/goplus/gop/ast/gopiter", "Value"},
 }
 
 func init() {
 	I.RegisterFuncs(
 		I.Func("panic", qlPanic, execPanic),
 		I.Func("is", errors.Is, QexecIs),
+		I.Func("_gop_NewIter", gopiter.NewIter, QNewIter),
+		I.Func("_gop_Next", gopiter.Next, QNext),
+		I.Func("_gop_Key", gopiter.Key, QKey),
+		I.Func("_gop_Value", gopiter.Value, QValue),
 	)
 	I.RegisterFuncvs(
 		I.Funcv("errorf", fmt.Errorf, QexecErrorf),


Test patch:
diff --git a/ast/gopiter/gop_iter_test.go b/ast/gopiter/gop_iter_test.go
new file mode 100644
index 00000000..1df3ec02
--- /dev/null
+++ b/ast/gopiter/gop_iter_test.go
@@ -0,0 +1,27 @@
+package gopiter
+
+import (
+	"fmt"
+	"testing"
+)
+
+func Test_Slice(t *testing.T) {
+	c := []int{1, 2, 3, 4, 5}
+	iter := NewIter(c)
+	cnt := 0
+	keySum := 0
+	valSum := 0
+	k := 0
+	v := 0
+	for iter.Next() {
+		Key(iter, &k)
+		Value(iter, &v)
+		keySum += k
+		valSum += v
+		cnt++
+	}
+	fmt.Println(cnt, keySum, valSum)
+	if cnt != 5 || keySum != 10 || valSum != 15 {
+		t.Fail()
+	}
+}