Repository: goplus/gop
Pull request #559 (2020-08-19 14:19:47)

cl: support var define
cl: support var define

Go+ code
```
import "image"

type Point struct {
	X int
	Y int
}

var (
	i,j,k int = 100,200,i+j
)

func main() {
	var x = Point{10,20}
	var pt image.Point
	pt.X = 10
	pt.Y = -10
	println(i,j,k,x,pt)
}
```
generate Golang code error
```
package main

import (
	fmt "fmt"
	image "image"
)

type Point struct {
	X int
	Y int
}

var (
	i int
	j int
	k int
)

func init() { 
//line ./main.gop:9
	i = 100
//line ./main.gop:9
	j = 200
//line ./main.gop:9
	k = i + j
}
func main() {
	var (
		x  Point
		pt image.Point
	)
//line ./main.gop:14
	x = Point{X: 10, Y: 20}
//line ./main.gop:16
	pt.X = 10
//line ./main.gop:17
	pt.Y = -10
//line ./main.gop:18
	fmt.Println(i, j, k, x, pt)
}
```


Patch:
diff --git a/cl/compile.go b/cl/compile.go
index f8694ec5..81579fc2 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -238,6 +238,7 @@ func loadFile(ctx *blockCtx, f *ast.File) {
 	file := newFileCtx(ctx)
 	last := len(f.Decls) - 1
 	ctx.file = file
+
 	for i, decl := range f.Decls {
 		switch d := decl.(type) {
 		case *ast.FuncDecl:
@@ -251,7 +252,7 @@ func loadFile(ctx *blockCtx, f *ast.File) {
 			case token.CONST:
 				loadConsts(ctx, d)
 			case token.VAR:
-				loadVars(ctx, d)
+				compileStmt(ctx, &ast.DeclStmt{decl})
 			default:
 				log.Panicln("tok:", d.Tok, "spec:", reflect.TypeOf(d.Specs).Elem())
 			}
@@ -306,13 +307,41 @@ func loadType(ctx *blockCtx, spec *ast.TypeSpec) {
 func loadConsts(ctx *blockCtx, d *ast.GenDecl) {
 }
 
-func loadVars(ctx *blockCtx, d *ast.GenDecl) {
+func loadVars(ctx *blockCtx, d *ast.GenDecl, stmt ast.Stmt) {
 	for _, item := range d.Specs {
-		loadVar(ctx, item.(*ast.ValueSpec))
+		spec := item.(*ast.ValueSpec)
+		for i := 0; i < len(spec.Names); i++ {
+			start := ctx.out.StartStmt(stmt)
+			name := spec.Names[i].Name
+			if len(spec.Values) > i {
+				loadVar(ctx, name, spec.Type, spec.Values[i])
+			} else {
+				loadVar(ctx, name, spec.Type, nil)
+			}
+			ctx.out.EndStmt(stmt, start)
+		}
 	}
 }
 
-func loadVar(ctx *blockCtx, spec *ast.ValueSpec) {
+func loadVar(ctx *blockCtx, name string, typ ast.Expr, value ast.Expr) {
+	var t reflect.Type
+	if typ != nil {
+		t = toType(ctx, typ).(reflect.Type)
+	}
+	if value != nil {
+		expr := compileExpr(ctx, value)
+		in := ctx.infer.Get(-1)
+		if t == nil {
+			t = boundType(in.(iValue))
+		}
+		expr()
+		addr := ctx.insertVar(name, t)
+		checkType(addr.getType(), in, ctx.out)
+		ctx.infer.PopN(1)
+		ctx.out.StoreVar(addr.v)
+	} else {
+		ctx.insertVar(name, t)
+	}
 }
 
 func loadFunc(ctx *blockCtx, d *ast.FuncDecl, isUnnamed bool) {
diff --git a/cl/ctx_check.go b/cl/ctx_check.go
index 0211ede5..a9e83ac7 100644
--- a/cl/ctx_check.go
+++ b/cl/ctx_check.go
@@ -82,6 +82,8 @@ func isNoExecCtxStmt(ctx *blockCtx, stmt ast.Stmt) bool {
 		return isNoExecCtxCallExpr(ctx, v.Call)
 	case *ast.GoStmt:
 		return isNoExecCtxCallExpr(ctx, v.Call)
+	case *ast.DeclStmt:
+		return isNoExecCtxDeclStmt(ctx, v)
 	default:
 		log.Panicln("isNoExecCtxStmt failed: unknown -", reflect.TypeOf(v))
 	}
@@ -349,6 +351,29 @@ func isNoExecCtxAssignStmt(ctx *blockCtx, expr *ast.AssignStmt) bool {
 	return true
 }
 
+func isNoExecCtxDeclStmt(ctx *blockCtx, expr *ast.DeclStmt) bool {
+	switch d := expr.Decl.(type) {
+	case *ast.GenDecl:
+		switch d.Tok {
+		case token.VAR, token.CONST:
+			for _, spec := range d.Specs {
+				vs := spec.(*ast.ValueSpec)
+				if vs.Values != nil {
+					if noExecCtx := isNoExecCtxExprs(ctx, vs.Values); !noExecCtx {
+						return false
+					}
+				}
+				for i := len(vs.Names) - 1; i >= 0; i-- {
+					if noExecCtx := isNoExecCtxExprLHS(ctx, vs.Names[i], lhsAssign); !noExecCtx {
+						return false
+					}
+				}
+			}
+		}
+	}
+	return true
+}
+
 func isNoExecCtxExprLHS(ctx *blockCtx, expr ast.Expr, mode compileMode) bool {
 	switch v := expr.(type) {
 	case *ast.Ident:
diff --git a/cl/stmt.go b/cl/stmt.go
index 5ddd64d3..f34271fb 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -81,6 +81,8 @@ func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
 		compileDeferStmt(ctx, v)
 	case *ast.GoStmt:
 		compileGoStmt(ctx, v)
+	case *ast.DeclStmt:
+		compileDeclStmt(ctx, v)
 	case *ast.EmptyStmt:
 		// do nothing
 	default:
@@ -553,6 +555,22 @@ func compileExprStmt(ctx *blockCtx, expr *ast.ExprStmt) {
 	ctx.infer.PopN(1)
 }
 
+func compileDeclStmt(ctx *blockCtx, expr *ast.DeclStmt) {
+	switch d := expr.Decl.(type) {
+	case *ast.GenDecl:
+		switch d.Tok {
+		case token.TYPE:
+			loadTypes(ctx, d)
+		case token.CONST:
+			loadConsts(ctx, d)
+		case token.VAR:
+			loadVars(ctx, d, expr)
+		default:
+			log.Panicln("tok:", d.Tok, "spec:", reflect.TypeOf(d.Specs).Elem())
+		}
+	}
+}
+
 func compileIncDecStmt(ctx *blockCtx, expr *ast.IncDecStmt) {
 	compileExpr(ctx, expr.X)()
 	compileExprLHS(ctx, expr.X, expr.Tok)
diff --git a/exec/golang/builder.go b/exec/golang/builder.go
index ba6beacd..5c1beb1f 100644
--- a/exec/golang/builder.go
+++ b/exec/golang/builder.go
@@ -158,10 +158,18 @@ func (p *Builder) Resolve() *Code {
 		decls = append(decls, gblvars)
 	}
 	p.endBlockStmt(0)
+
 	if len(p.gblScope.stmts) != 0 {
+		fnName := "main"
+		for _, decl := range p.gblDecls {
+			if d, ok := decl.(*ast.FuncDecl); ok && d.Name.Name == "main" {
+				fnName = "init"
+				break
+			}
+		}
 		body := &ast.BlockStmt{List: p.gblScope.stmts}
 		fn := &ast.FuncDecl{
-			Name: Ident("main"),
+			Name: Ident(fnName),
 			Type: FuncType(p, tyMainFunc),
 			Body: body,
 		}


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 9f55b4b3..760719c3 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -555,6 +555,78 @@ func TestArray(t *testing.T) {
 		"x[1:]: [2.3 3.1 4]\n4\n")
 }
 
+func TestLoadVar(t *testing.T) {
+	cltest.Expect(t, `
+		var x1 int
+		var x2 int = 10
+		var x3 = 10
+		println("x:",x1,x2,x3)
+		`,
+		"x: 0 10 10\n")
+	cltest.Expect(t, `
+		type Point struct {
+			X int
+			Y int
+		}
+		var x1 Point
+		var x2 Point = Point{10,20}
+		var x3 = Point{-10,-20}
+		println("x:",x1,x2,x3)
+		`,
+		"x: {0 0} {10 20} {-10 -20}\n")
+}
+
+func TestLoadVar2(t *testing.T) {
+	cltest.Expect(t, `
+		func main() {
+			var x1 int
+			var x2 int = 10
+			var x3 = 10
+			println("x:",x1,x2,x3)
+		}`,
+		"x: 0 10 10\n")
+	cltest.Expect(t, `
+		type Point struct {
+			X int
+			Y int
+		}
+		func main() {
+			var x1 Point
+			var x2 Point = Point{10,20}
+			var x3 = Point{-10,-20}
+			println("x:",x1,x2,x3)
+		}`,
+		"x: {0 0} {10 20} {-10 -20}\n")
+
+	cltest.Expect(t, `
+		func main() {
+			type Point struct {
+				X int
+				Y int
+			}
+			var x1 Point
+			var x2 Point = Point{10,20}
+			var x3 = Point{-10,-20}
+			println("x:",x1,x2,x3)
+		}`,
+		"x: {0 0} {10 20} {-10 -20}\n")
+}
+
+func TestLoadVar3(t *testing.T) {
+	cltest.Expect(t, `
+		var x1,x2,x3 = 10,20,x1+x2
+		println("x:",x1,x2,x3)
+		`,
+		"x: 10 20 30\n")
+	cltest.Expect(t, `
+		var x1,x2,x3 = 10,20,x1+x2
+		func main() {
+			println("x:",x1,x2,x3)
+		}
+		`,
+		"x: 10 20 30\n")
+}
+
 func TestMap(t *testing.T) {
 	cltest.Expect(t, `
 		x := map[string]float64{"Hello": 1, "xsw": 3.4}
@@ -722,6 +794,19 @@ func TestRational(t *testing.T) {
 	`).Equal(big.NewRat(7, 3))
 }
 
+func TestIsNoExecCtx(t *testing.T) {
+	cltest.Expect(t, `
+	fns := make([]func() int, 3)
+	for i, x <- [3, 15, 777] {
+		var v = x
+		var fn = func() int {
+			return v
+		}
+		fns[i] = fn
+	}
+	println("values:", fns[0](), fns[1](), fns[2]())`, "values: 3 15 777\n")
+}
+
 type testData struct {
 	clause string
 	want   string
diff --git a/exec/golang/testdata/29.var-def/var_def.gop b/exec/golang/testdata/29.var-def/var_def.gop
new file mode 100644
index 00000000..f324ae95
--- /dev/null
+++ b/exec/golang/testdata/29.var-def/var_def.gop
@@ -0,0 +1,16 @@
+type Point struct {
+	X int
+	Y int
+}
+
+var (
+	i, j, k int = 100, 200, i + j
+)
+
+func main() {
+	var x = Point{10, 20}
+	var pt Point
+	pt.X = 10
+	pt.Y = -10
+	println(i, j, k, x, pt)
+}