Repository: goplus/gop
Pull request #2255 (2025-04-06 04:37:15)

classfile: Classprojname


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index fad1a8ca..b3193e93 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -22,6 +22,7 @@ import (
 	gotoken "go/token"
 	"go/types"
 	"log"
+	"os"
 	"path/filepath"
 	"strings"
 
@@ -48,7 +49,8 @@ type spxObj struct {
 type gmxProject struct {
 	gameClass  string    // <gmtype>.gmx
 	game       gogen.Ref // Game (project base class)
-	spfeats    spriteFeat
+	gmfeats    classFeat
+	spfeats    classFeat
 	sprite     map[string]spxObj // .spx => Sprite
 	sptypes    []string          // <sptype>.spx
 	scheds     []string
@@ -63,14 +65,15 @@ type gmxProject struct {
 	hasMain_   bool
 }
 
-type spriteFeat uint
+type classFeat uint
 
 const (
-	spriteClassfname spriteFeat = 1 << iota
+	spriteClassfname classFeat = 1 << iota
 	spriteClassclone
+	gameClassprojname
 )
 
-func spriteFeatures(game gogen.Ref) (feats spriteFeat, classclone *types.Signature) {
+func classFeatures(game gogen.Ref) (gmfeats, spfeats classFeat, classclone *types.Signature) {
 	if mainFn := findMethod(game, "Main"); mainFn != nil {
 		sig := mainFn.Type().(*types.Signature)
 		if t, ok := gogen.CheckSigFuncEx(sig); ok {
@@ -78,8 +81,8 @@ func spriteFeatures(game gogen.Ref) (feats spriteFeat, classclone *types.Signatu
 				sig = t.Func.Type().(*types.Signature)
 			}
 		}
+		in := sig.Params()
 		if sig.Variadic() {
-			in := sig.Params()
 			last := in.At(in.Len() - 1)
 			elt := last.Type().(*types.Slice).Elem()
 			if tn, ok := elt.(*types.Named); ok {
@@ -89,10 +92,24 @@ func spriteFeatures(game gogen.Ref) (feats spriteFeat, classclone *types.Signatu
 				for i, n := 0, intf.NumMethods(); i < n; i++ {
 					switch m := intf.Method(i); m.Name() {
 					case "Classfname":
-						feats |= spriteClassfname
+						spfeats |= spriteClassfname
 					case "Classclone":
 						classclone = m.Type().(*types.Signature)
-						feats |= spriteClassclone
+						spfeats |= spriteClassclone
+					}
+				}
+			}
+		}
+		if in.Len() > 0 {
+			first := in.At(0).Type()
+			if tn, ok := first.(*types.Named); ok {
+				first = tn.Underlying()
+			}
+			if intf, ok := first.(*types.Interface); ok {
+				for i, n := 0, intf.NumMethods(); i < n; i++ {
+					switch m := intf.Method(i); m.Name() {
+					case "Classprojname":
+						gmfeats |= gameClassprojname
 					}
 				}
 			}
@@ -204,7 +221,7 @@ func loadClass(ctx *pkgCtx, pkg *gogen.Package, file string, f *ast.File, conf *
 		spx := p.pkgImps[0]
 		if gt.Class != "" {
 			p.game, p.gameIsPtr = spxRef(spx, gt.Class)
-			p.spfeats, p.classclone = spriteFeatures(p.game)
+			p.gmfeats, p.spfeats, p.classclone = classFeatures(p.game)
 		}
 		p.sprite = make(map[string]spxObj)
 		for _, v := range gt.Works {
@@ -462,6 +479,27 @@ func makeMainSig(recv *types.Var, f *types.Func) *types.Signature {
 	return types.NewSignatureType(recv, nil, nil, types.NewTuple(params...), nil, false)
 }
 
+func projNameOf(file string) string {
+	dir := filepath.Dir(file)
+	if dir == "." {
+		dir, _ = os.Getwd()
+	}
+	dname := filepath.Base(dir)
+	if len(dname) <= 1 && (dname == string(os.PathSeparator) || dname == ".") {
+		dname = "root"
+	}
+	return dname
+}
+
+func genClassprojname(ctx *blockCtx, projname string) {
+	pkg := ctx.pkg
+	recv := toRecv(ctx, ctx.classRecv)
+	ret := types.NewTuple(pkg.NewParam(token.NoPos, "", types.Typ[types.String]))
+	pkg.NewFunc(recv, "Classprojname", nil, ret, false).BodyStart(pkg).
+		Val(projname).Return(1).
+		End()
+}
+
 func genClassfname(ctx *blockCtx, c *gmxClass) {
 	pkg := ctx.pkg
 	recv := toRecv(ctx, ctx.classRecv)
diff --git a/cl/compile.go b/cl/compile.go
index cc2794fa..8c6bc8a3 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -568,8 +568,9 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
 	}
 
 	for _, f := range sfiles {
+		fpath := f.path
 		fileLine := !conf.NoFileLine
-		fileScope := types.NewScope(p.Types.Scope(), f.Pos(), f.End(), f.path)
+		fileScope := types.NewScope(p.Types.Scope(), f.Pos(), f.End(), fpath)
 		ctx := &blockCtx{
 			pkg: p, pkgCtx: ctx, cb: p.CB(), relBaseDir: relBaseDir, fileScope: fileScope,
 			fileLine: fileLine, isClass: f.IsClass, rec: rec, imports: make(map[string]pkgImp), isGopFile: true,
@@ -577,7 +578,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
 		if rec := ctx.rec; rec != nil {
 			rec.Scope(f.File, fileScope)
 		}
-		preloadGopFile(p, ctx, f.path, f.File, conf)
+		preloadGopFile(p, ctx, fpath, f.File, conf)
 	}
 
 	proj, multi := gmxCheckProjs(p, ctx)
@@ -727,8 +728,8 @@ func preloadGopFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, c
 	var testType string
 	var baseTypeName string
 	var baseType types.Type
+	var feats classFeat
 	var spxProj string
-	var spfeats spriteFeat
 	var spxClass bool
 	var goxTestFile bool
 	var parent = ctx.pkgCtx
@@ -761,13 +762,14 @@ func preloadGopFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, c
 				if proj.gameIsPtr {
 					baseType = types.NewPointer(baseType)
 				}
+				feats = proj.gmfeats
 			} else {
 				sp := proj.sprite[c.ext]
 				o := sp.obj
 				ctx.baseClass = o
 				baseTypeName, baseType, spxProj = o.Name(), o.Type(), sp.proj
 				spxClass = true
-				spfeats = proj.spfeats
+				feats = proj.spfeats
 			}
 		}
 	}
@@ -881,9 +883,16 @@ func preloadGopFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, c
 	}
 
 	preloadFile(p, ctx, f, goFile, !conf.Outline)
-	if spfeats != 0 {
+	if feats != 0 {
 		ld := getTypeLoader(parent, parent.syms, token.NoPos, classType)
-		if (spfeats & spriteClassfname) != 0 { // Classfname() string
+		if (feats & gameClassprojname) != 0 { // Classprojname() string
+			ld.methods = append(ld.methods, func() {
+				old, _ := p.SetCurFile(goFile, true)
+				defer p.RestoreCurFile(old)
+				doInitType(ld)
+				genClassprojname(ctx, projNameOf(file))
+			})
+		} else if (feats & spriteClassfname) != 0 { // Classfname() string
 			ld.methods = append(ld.methods, func() {
 				old, _ := p.SetCurFile(goFile, true)
 				defer p.RestoreCurFile(old)
@@ -891,7 +900,7 @@ func preloadGopFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, c
 				genClassfname(ctx, c)
 			})
 		}
-		if (spfeats & spriteClassclone) != 0 { // Classclone() clonetype
+		if (feats & spriteClassclone) != 0 { // Classclone() clonetype
 			ld.methods = append(ld.methods, func() {
 				old, _ := p.SetCurFile(goFile, true)
 				defer p.RestoreCurFile(old)
diff --git a/cl/internal/spx3/spx3.go b/cl/internal/spx3/spx3.go
index 06aa8f44..eb2bdef9 100644
--- a/cl/internal/spx3/spx3.go
+++ b/cl/internal/spx3/spx3.go
@@ -27,8 +27,6 @@ func New() *Game {
 	return nil
 }
 
-func (p *Game) initGame() {}
-
 func (p *Game) Run() {}
 
 type Sprite struct {
@@ -40,11 +38,15 @@ func (p *Sprite) Name() string {
 
 func (p *Sprite) Main(name string) {}
 
+type iGameProto interface {
+	Classprojname() string
+}
+
 type Handler interface {
 	Main(name string)
 	Classfname() string
 	Classclone() Handler
 }
 
-func Gopt_Game_Main(game interface{ initGame() }, workers ...Handler) {
+func Gopt_Game_Main(game iGameProto, workers ...Handler) {
 }


Test patch:
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index c18dac11..003f5c5d 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -39,6 +39,15 @@ func getGoxConf() *gogen.Config {
 	return &gogen.Config{Fset: fset, Importer: imp}
 }
 
+func TestProjNameOf(t *testing.T) {
+	if projNameOf("./a.gop") != "cl" {
+		t.Fatal("projNameOf: ./a.gop")
+	}
+	if projNameOf("/a.gop") != "root" {
+		t.Fatal("projNameOf: /a.gop")
+	}
+}
+
 func TestLoadExpr(t *testing.T) {
 	var ni nodeInterp
 	if v := ni.LoadExpr(&ast.Ident{Name: "x"}); v != "" {
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 4c6c1ac2..06f378b7 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -496,6 +496,9 @@ type Game struct {
 func (this *Game) MainEntry() {
 	this.Run()
 }
+func (this *Game) Classprojname() string {
+	return "foo"
+}
 func (this *Game) Main() {
 	spx3.Gopt_Game_Main(this, new(Kai))
 }
@@ -543,6 +546,9 @@ func (this *Game) MainEntry() {
 	b := new(spx3.Sprite)
 	fmt.Println(b.Name())
 }
+func (this *Game) Classprojname() string {
+	return "foo"
+}
 func (this *Game) Main() {
 	spx3.Gopt_Game_Main(this, new(Kai))
 }