Repository: goplus/gop
Pull request #2353 (2025-05-13 12:59:30)

feat(classfile): add node method to pkgCtx for shadow main postion


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index c49f48d5..db9e94dc 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -553,7 +553,7 @@ func (*ChanType) exprNode()      {}
 // Useful for ASTs generated by code other than the Go+ parser.
 func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
 
-// NewIdentEx creates a new Ident without position and with the given kind.
+// NewIdentEx creates a new Ident with position and with the given kind.
 func NewIdentEx(pos token.Pos, name string, kind ObjKind) *Ident {
 	return &Ident{pos, name, NewObj(kind, name)}
 }
diff --git a/cl/classfile.go b/cl/classfile.go
index 0b70acc1..bd210a65 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -516,11 +516,12 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 			mainFn := stk.Pop()
 			sigParams := mainFn.Type.(*types.Signature).Params()
 			callMain := func() {
+				src := parent.lookupClassNode(proj.gameClass_)
 				stk.Push(mainFn)
 				if _, isPtr := sigParams.At(0).Type().(*types.Pointer); isPtr {
-					cb.Val(recv).MemberRef(base.Name()).UnaryOp(gotoken.AND)
+					cb.Val(recv, src).MemberRef(base.Name()).UnaryOp(gotoken.AND)
 				} else {
-					cb.Val(recv) // template recv method
+					cb.Val(recv, src) // template recv method
 				}
 			}
 
@@ -531,7 +532,7 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 				if len(sprites) == 1 && sprites[0].proto == "" { // no work class prototype
 					sp := sprites[0]
 					narg = 1 + len(sp.types)
-					genWorkClasses(pkg, cb, recv, sp, iobj, -1, callMain)
+					genWorkClasses(pkg, parent, cb, recv, sp, iobj, -1, callMain)
 				} else {
 					lstNames := make([]string, narg)
 					for i := 1; i < narg; i++ {
@@ -539,7 +540,7 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 						tn := tslice.(*types.Slice).Elem().(*types.Named)
 						sp := spriteByProto(sprites, tn.Obj().Name()) // work class
 						if n := len(sp.types); n > 0 {
-							lstNames[i] = genWorkClasses(pkg, cb, recv, sp, iobj, i, nil)
+							lstNames[i] = genWorkClasses(pkg, parent, cb, recv, sp, iobj, i, nil)
 							cb.SliceLitEx(tslice, n, false).EndInit(1)
 							iobj += n
 						}
@@ -563,7 +564,7 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 }
 
 func genWorkClasses(
-	pkg *gogen.Package, cb *gogen.CodeBuilder, recv *types.Var,
+	pkg *gogen.Package, parent *pkgCtx, cb *gogen.CodeBuilder, recv *types.Var,
 	sp *spxObj, iobj, ilst int, callMain func()) (lstName string) {
 	const (
 		indexGame     = 1
@@ -588,9 +589,10 @@ func genWorkClasses(
 	} else {
 		callMain()
 	}
-	for i := range sptypes {
+	for i, spt := range sptypes {
+		src := parent.lookupClassNode(spt)
 		objName := objNamePrefix + strconv.Itoa(iobj+i)
-		cb.VarVal(objName)
+		cb.VarVal(objName, src)
 	}
 	return
 }
diff --git a/cl/compile.go b/cl/compile.go
index e2c05495..a345bf1c 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -483,6 +483,16 @@ func (p *pkgCtx) recoverErr(e any, src ast.Node) error {
 	return err
 }
 
+// lookupClassFile looks up the class file by name.
+func (p *pkgCtx) lookupClassNode(name string) ast.Node {
+	for f, cls := range p.classes {
+		if name == cls.clsfile {
+			return f.Name
+		}
+	}
+	return nil
+}
+
 const (
 	defaultGoFile  = ""
 	skippingGoFile = "_skip"
diff --git a/cl/internal/spx4/game.go b/cl/internal/spx4/game.go
new file mode 100644
index 00000000..2e18253b
--- /dev/null
+++ b/cl/internal/spx4/game.go
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package spx4
+
+const (
+	GopPackage = "github.com/goplus/gop/cl/internal/spx/pkg"
+	Gop_sched  = "Sched,SchedNow"
+)
+
+type Sound string
+
+type MyGame struct {
+}
+
+func Gopt_MyGame_Main(game any, sprites ...Sprite) {
+}
+
+func (p *MyGame) Ls(n int) {}
+
+func (p *MyGame) Capout(doSth func()) (string, error) {
+	return "", nil
+}
+
+func (p *MyGame) Gop_Env(name string) int {
+	return 0
+}
+
+func (p *MyGame) Gop_Exec(name string, args ...any) {
+}
+
+func (p *MyGame) InitGameApp(args ...string) {
+}
+
+func (p *MyGame) Broadcast__0(msg string) {
+}
+
+func (p *MyGame) Broadcast__1(msg string, wait bool) {
+}
+
+func (p *MyGame) Broadcast__2(msg string, data any, wait bool) {
+}
+
+func (p *MyGame) Play(media string, wait ...bool) {
+}
+
+func (p *MyGame) sendMessage(data any) {
+}
+
+func (p *MyGame) SendMessage(data any) {
+	p.sendMessage(data)
+}
+
+func Gopt_MyGame_Run(game any, resource string) error {
+	return nil
+}
+
+func Sched() {
+}
+
+func SchedNow() {
+}
+
+func Rand__0(int) int {
+	return 0
+}
+
+func Rand__1(float64) float64 {
+	return 0
+}
+
+var (
+	TestIntValue int
+)
diff --git a/cl/internal/spx4/pkg/pkg.go b/cl/internal/spx4/pkg/pkg.go
new file mode 100644
index 00000000..bd2ff494
--- /dev/null
+++ b/cl/internal/spx4/pkg/pkg.go
@@ -0,0 +1,27 @@
+package pkg
+
+const (
+	GopPackage = true
+)
+
+type Vector struct {
+	X int
+	Y int
+}
+
+func NewVector(x, y int) *Vector {
+	return &Vector{x, y}
+}
+
+func (v *Vector) Add__0(x int, y int) {
+	v.X += x
+	v.Y += y
+}
+
+func (v *Vector) Add__1(o *Vector) {
+	v.Add__0(o.X, o.Y)
+}
+
+func (v *Vector) Self() *Vector {
+	return v
+}
diff --git a/cl/internal/spx4/sprite.go b/cl/internal/spx4/sprite.go
new file mode 100644
index 00000000..97ca23da
--- /dev/null
+++ b/cl/internal/spx4/sprite.go
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package spx4
+
+import (
+	"github.com/goplus/gop/cl/internal/spx/pkg"
+)
+
+type Sprite struct {
+	pos pkg.Vector
+	Entry
+}
+
+type Entry struct {
+	vec pkg.Vector
+}
+
+func (p *Entry) Vector() *pkg.Vector {
+	return &p.vec
+}
+
+func (p *Sprite) SetCostume(costume any) {
+}
+
+func (p *Sprite) Say(msg string, secs ...float64) {
+}
+
+func (p *Sprite) Position() *pkg.Vector {
+	return &p.pos
+}
+
+type Mesher interface {
+	Name() string
+}
+
+func Gopt_Sprite_Clone__0(sprite any) {
+}
+
+func Gopt_Sprite_Clone__1(sprite any, data any) {
+}
+
+func Gopt_Sprite_OnKey__0(sprite any, a string, fn func()) {
+}
+
+func Gopt_Sprite_OnKey__1(sprite any, a string, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__2(sprite any, a []string, fn func()) {
+}
+
+func Gopt_Sprite_OnKey__3(sprite any, a []string, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__4(sprite any, a []Mesher, fn func()) {
+}
+
+func Gopt_Sprite_OnKey__5(sprite any, a []Mesher, fn func(key Mesher)) {
+}
+
+func Gopt_Sprite_OnKey__6(sprite any, a []string, b []string, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__7(sprite any, a []string, b []Mesher, fn func(key string)) {
+}
+
+func Gopt_Sprite_OnKey__8(sprite any, x int, y int) {
+}
+
+func Gopt_Sprite_OnKey2(sprite any, a string, fn func(key string)) {
+}


Test patch:
diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go
index 44000f38..5343d19b 100644
--- a/cl/cltest/cltest.go
+++ b/cl/cltest/cltest.go
@@ -85,6 +85,11 @@ func LookupClass(ext string) (c *modfile.Project, ok bool) {
 				{Ext: ".t2spx", Class: "Sprite"},
 			},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
+	case ".t4gmx", ".t4spx":
+		return &modfile.Project{
+			Ext: ".t4gmx", Class: "*MyGame",
+			Works:    []*modfile.Class{{Ext: ".t4spx", Class: "Sprite"}},
+			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx4", "math"}}, true
 	case "_spx.gox":
 		return &modfile.Project{
 			Ext: "_spx.gox", Class: "Game",
diff --git a/cl/cltest/spx.go b/cl/cltest/spx.go
index 60034f86..56c26709 100644
--- a/cl/cltest/spx.go
+++ b/cl/cltest/spx.go
@@ -98,6 +98,26 @@ func SpxTest(t *testing.T, fs fsx.FileSystem, dir string, parseConf parser.Confi
 	}
 }
 
+func SpxErrorFS(t *testing.T, msg string, fs parser.FileSystem) {
+	pkgs, err := parser.ParseFSDir(Conf.Fset, fs, "/foo", spxParserConf())
+	if err != nil {
+		scanner.PrintError(os.Stderr, err)
+		t.Fatal("ParseFSDir:", err)
+	}
+	conf := *Conf
+	conf.RelativeBase = "/foo"
+	conf.Recorder = nil
+	conf.NoFileLine = false
+	bar := pkgs["main"]
+	_, err = cl.NewPackage("", bar, &conf)
+	if err == nil {
+		t.Fatal("no error?")
+	}
+	if ret := err.Error(); ret != msg {
+		t.Fatalf("\nError: \"%s\"\nExpected: \"%s\"\n", ret, msg)
+	}
+}
+
 func SpxErrorEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string) {
 	fs := memfs.TwoFiles("/foo", spxfile, spxcode, gmxfile, gmx)
 	pkgs, err := parser.ParseFSDir(Conf.Fset, fs, "/foo", spxParserConf())
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index fffc0c88..39aecf5d 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/goplus/gop/cl"
 	"github.com/goplus/gop/cl/cltest"
+	"github.com/goplus/gop/parser/fsx/memfs"
 )
 
 func gopSpxTest(t *testing.T, gmx, spxcode, expected string) {
@@ -43,6 +44,10 @@ func gopSpxErrorTestEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string)
 	cltest.SpxErrorEx(t, msg, gmx, spxcode, gmxfile, spxfile)
 }
 
+func gopSpxErrorTestMap(t *testing.T, msg string, dirs map[string][]string, files map[string]string) {
+	cltest.SpxErrorFS(t, msg, memfs.New(dirs, files))
+}
+
 func TestSpxError(t *testing.T) {
 	gopSpxErrorTestEx(t, `Game.tgmx:6:2: userScore redeclared
 	Game.tgmx:5:2 other declaration of userScore`, `
@@ -69,6 +74,33 @@ var (
 )
 println "hi"
 `, "Game.tgmx", "Kai.tspx")
+
+	gopSpxErrorTestEx(t, `Game.t4gmx:6:2: userScore redeclared
+	Game.t4gmx:5:2 other declaration of userScore
+Kai.t4spx:1:1: cannot use  (type *Kai) as type github.com/goplus/gop/cl/internal/spx4.Sprite in argument to `, `
+import "bytes"
+var (
+	Kai Kai
+	userScore int
+	userScore string
+)
+`, `
+println "hi"
+`, "Game.t4gmx", "Kai.t4spx")
+
+	gopSpxErrorTestMap(t, `Kai.t4spx:4:2: userScore redeclared
+	Kai.t4spx:3:2 other declaration of userScore
+Greem.t4spx:1:1: cannot use  (type *Greem) as type github.com/goplus/gop/cl/internal/spx4.Sprite in argument to `, map[string][]string{
+		"/foo": {"Game.t4gmx", "Kai.t4spx", "Greem.t4spx"},
+	}, map[string]string{
+		"/foo/Game.t4gmx": `println "hi"`,
+		"/foo/Kai.t4spx": `var (
+	Kai Kai
+	userScore int
+	userScore string
+)`,
+		"/foo/Greem.t4spx": ``,
+	})
 }
 
 func TestSpxBasic(t *testing.T) {