Repository: goplus/gop
Pull request #1749 (2024-02-16 06:41:01)

ParseEntries; support .gsh as builtin classfile


Patch:
diff --git a/go.mod b/go.mod
index 395d6158..c0a02ec9 100644
--- a/go.mod
+++ b/go.mod
@@ -4,10 +4,10 @@ go 1.18
 
 require (
 	github.com/fsnotify/fsnotify v1.7.0
-	github.com/goplus/c2go v0.7.22
+	github.com/goplus/c2go v0.7.23
 	github.com/goplus/gox v1.14.12
-	github.com/goplus/mod v0.13.5
-	github.com/qiniu/x v1.13.5
+	github.com/goplus/mod v0.13.7
+	github.com/qiniu/x v1.13.8
 	golang.org/x/tools v0.18.0
 )
 
diff --git a/go.sum b/go.sum
index dc65fca9..a2b811ca 100644
--- a/go.sum
+++ b/go.sum
@@ -4,13 +4,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/goplus/c2go v0.7.22 h1:TnOf6VJTvVKFhp8+zTP6POwJ894mdNGV7qKRYY1o2oY=
-github.com/goplus/c2go v0.7.22/go.mod h1:f3v+Td4JQwhToKfQK5tGvjMoMBrIR9IJjHNqzDC6NIE=
-github.com/goplus/gox v1.14.10/go.mod h1:G7Hz+cAOUyJyN9pPHrpqhfQPDUtiJNmoRVTwoaQ9nw0=
+github.com/goplus/c2go v0.7.23 h1:MccDB9xBwDg9Bb9w+S9Lzbhg47ZtRFZHzqkr2OvxarM=
+github.com/goplus/c2go v0.7.23/go.mod h1:xBHI3uJ7hzQEYhw8Ql//1DHfJeuEgO5119QyoAs6JfY=
 github.com/goplus/gox v1.14.12 h1:VIrkU4T/MQ48olkTGgNk5ybF4U7YqkzIAv7By10gf9Y=
 github.com/goplus/gox v1.14.12/go.mod h1:6b6XYHmyiCevhwuEHcV/jzm7Z2FXLDBhuxgvkjceA+o=
-github.com/goplus/mod v0.13.5 h1:rEQ4UFmLKU1J5IK82DwC/sAKIhAMJZ2VbBrKU/k448c=
-github.com/goplus/mod v0.13.5/go.mod h1:PBGRYvD381NxJdeI+KyYHiDX9fgnDiuttzyCTG9LfHE=
+github.com/goplus/mod v0.13.7 h1:2tIe3cT/fmV+rOoPf896OH+AYcRu94Q0IeJU1LHVU4o=
+github.com/goplus/mod v0.13.7/go.mod h1:edZE5Qs+9mRnIjvWhyMNUkm5ayJ+dMlJ3J3YeXODNwA=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
@@ -19,8 +18,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/qiniu/x v1.13.5 h1:YJz5i3RD/7srMLEmp7UAcYmZ9qrhOPk0EL0pGiuxGbA=
-github.com/qiniu/x v1.13.5/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
+github.com/qiniu/x v1.13.8 h1:xXXD1LuTw4VaqTWxFKonm0F6XlhQNE59+vgPYP3ij8Q=
+github.com/qiniu/x v1.13.8/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -28,11 +27,10 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
 golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -41,7 +39,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -56,15 +54,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
-golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -77,7 +75,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
-golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
 golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
 golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/imp.go b/imp.go
index 103fa877..95531cbb 100644
--- a/imp.go
+++ b/imp.go
@@ -60,11 +60,12 @@ const (
 	gopMod = "github.com/goplus/gop"
 )
 
+// Import imports a Go/Go+ package.
 func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
 	if strings.HasPrefix(pkgPath, gopMod) {
 		if suffix := pkgPath[len(gopMod):]; suffix == "" || suffix[0] == '/' {
 			gopRoot := p.gop.Root
-			if suffix == "/cl/internal/gop-in-go/foo" {
+			if suffix == "/cl/internal/gop-in-go/foo" { // for test github.com/goplus/gop/cl
 				if err = p.genGoExtern(gopRoot+suffix, false); err != nil {
 					return
 				}
@@ -72,12 +73,12 @@ func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
 			return p.impFrom.ImportFrom(pkgPath, gopRoot, 0)
 		}
 	}
+	if isPkgInMod(pkgPath, "github.com/qiniu/x") {
+		return p.impFrom.ImportFrom(pkgPath, p.gop.Root, 0)
+	}
 	if mod := p.mod; mod.HasModfile() {
 		ret, e := mod.Lookup(pkgPath)
 		if e != nil {
-			if isPkgInMod(pkgPath, "github.com/qiniu/x") {
-				return p.impFrom.ImportFrom(pkgPath, p.gop.Root, 0)
-			}
 			return nil, e
 		}
 		switch ret.Type {
diff --git a/load.go b/load.go
index 392f7140..ea58ce56 100644
--- a/load.go
+++ b/load.go
@@ -154,14 +154,14 @@ func LoadMod(dir string) (mod *gopmod.Module, err error) {
 		err = errors.NewWith(err, `gopmod.Load(dir, 0)`, -2, "gopmod.Load", dir, 0)
 		return
 	}
-	if mod != nil {
-		err = mod.ImportClasses()
-		if err != nil {
-			err = errors.NewWith(err, `mod.RegisterClasses()`, -2, "(*gopmod.Module).RegisterClasses", mod)
-		}
-		return
+	if mod == nil {
+		mod = gopmod.Default
+	}
+	err = mod.ImportClasses()
+	if err != nil {
+		err = errors.NewWith(err, `mod.ImportClasses()`, -2, "(*gopmod.Module).ImportClasses", mod)
 	}
-	return gopmod.Default, nil
+	return
 }
 
 // FilterNoTestFiles filters to skip all testing files.
@@ -322,7 +322,11 @@ func LoadFiles(dir string, files []string, conf *Config) (out *gox.Package, err
 	if fset == nil {
 		fset = token.NewFileSet()
 	}
-	pkgs, err := parser.ParseFiles(fset, files, parser.ParseComments|parser.SaveAbsFile)
+	pkgs, err := parser.ParseEntries(fset, files, parser.Config{
+		ClassKind: mod.ClassKind,
+		Filter:    conf.Filter,
+		Mode:      parser.ParseComments | parser.SaveAbsFile,
+	})
 	if err != nil {
 		err = errors.NewWith(err, `parser.ParseFiles(fset, files, parser.ParseComments)`, -2, "parser.ParseFiles", fset, files, parser.ParseComments)
 		return
diff --git a/parser/parser_gop.go b/parser/parser_gop.go
index 16a15971..356ad96c 100644
--- a/parser/parser_gop.go
+++ b/parser/parser_gop.go
@@ -247,20 +247,22 @@ func reqPkg(pkgs map[string]*ast.Package, name string) *ast.Package {
 func defaultClassKind(fname string) (isProj bool, ok bool) {
 	ext := path.Ext(fname)
 	switch ext {
-	case ".gmx":
-		return true, true
 	case ".spx":
 		return fname == "main.spx", true
+	case ".gsh", ".gmx":
+		return true, true
 	}
 	return
 }
 
 // -----------------------------------------------------------------------------
 
+// ParseFiles parses Go+ source files and returns the corresponding packages.
 func ParseFiles(fset *token.FileSet, files []string, mode Mode) (map[string]*ast.Package, error) {
 	return ParseFSFiles(fset, fsx.Local, files, mode)
 }
 
+// ParseFSFiles parses Go+ source files and returns the corresponding packages.
 func ParseFSFiles(fset *token.FileSet, fs FileSystem, files []string, mode Mode) (map[string]*ast.Package, error) {
 	ret := map[string]*ast.Package{}
 	fabs := (mode & SaveAbsFile) != 0
@@ -286,6 +288,39 @@ func ParseFSFiles(fset *token.FileSet, fs FileSystem, files []string, mode Mode)
 	return ret, nil
 }
 
+// ParseEntries parses Go+ source files and returns the corresponding packages.
+// Compared to ParseFiles, ParseEntries detects fileKind by its filename.
+func ParseEntries(fset *token.FileSet, files []string, conf Config) (map[string]*ast.Package, error) {
+	return ParseFSEntries(fset, fsx.Local, files, conf)
+}
+
+// ParseFSEntries parses Go+ source files and returns the corresponding packages.
+// Compared to ParseFSFiles, ParseFSEntries detects fileKind by its filename.
+func ParseFSEntries(fset *token.FileSet, fs FileSystem, files []string, conf Config) (map[string]*ast.Package, error) {
+	ret := map[string]*ast.Package{}
+	fabs := (conf.Mode & SaveAbsFile) != 0
+	if fabs {
+		conf.Mode &^= SaveAbsFile
+	}
+	for _, file := range files {
+		if fabs {
+			file, _ = fs.Abs(file)
+		}
+		f, err := ParseFSEntry(fset, fs, file, nil, conf)
+		if err != nil {
+			return nil, err
+		}
+		pkgName := f.Name.Name
+		pkg, ok := ret[pkgName]
+		if !ok {
+			pkg = &ast.Package{Name: pkgName, Files: make(map[string]*ast.File)}
+			ret[pkgName] = pkg
+		}
+		pkg.Files[file] = f
+	}
+	return ret, nil
+}
+
 // -----------------------------------------------------------------------------
 
 // ParseFile parses the source code of a single Go+ source file and returns the corresponding ast.File node.
diff --git a/x/gopprojs/proj.go b/x/gopprojs/proj.go
index 92aabbc0..e27e4d57 100644
--- a/x/gopprojs/proj.go
+++ b/x/gopprojs/proj.go
@@ -65,11 +65,8 @@ func ParseOne(args ...string) (proj Proj, next []string, err error) {
 }
 
 func isFile(fname string) bool {
-	switch filepath.Ext(fname) {
-	case ".gop", ".go":
-		return true
-	}
-	return false
+	n := len(filepath.Ext(fname))
+	return n > 1
 }
 
 func isLocal(ns string) bool {


Test patch:
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 3bec85ac..0831a73b 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -25,6 +25,7 @@ import (
 	"syscall"
 	"testing"
 
+	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/parser/fsx"
 	"github.com/goplus/gop/parser/fsx/memfs"
 	"github.com/goplus/gop/parser/parsertest"
@@ -145,7 +146,41 @@ func TestParseGoFiles(t *testing.T) {
 	}
 }
 
+func TestParseEntries(t *testing.T) {
+	doTestParseEntries(t, Config{})
+	_, err := ParseEntries(nil, []string{"/not-found/gopfile.gox"}, Config{})
+	if err == nil {
+		t.Fatal("ParseEntries: no error?")
+	}
+}
+
+func TestParseEntries_SaveAbsFile(t *testing.T) {
+	doTestParseEntries(t, Config{Mode: SaveAbsFile})
+}
+
+func doTestParseEntries(t *testing.T, confReal Config) {
+	doTestParseEntry(t, func(fset *token.FileSet, filename string, src interface{}, conf Config) (f *ast.File, err error) {
+		fs, _ := memfs.File(filename, src)
+		pkgs, err := ParseFSEntries(fset, fs, []string{filename}, confReal)
+		if err != nil {
+			return
+		}
+		for _, pkg := range pkgs {
+			for _, file := range pkg.Files {
+				f = file
+				return
+			}
+		}
+		t.Fatal("TestParseEntries: no source?")
+		return nil, syscall.ENOENT
+	})
+}
+
 func TestParseEntry(t *testing.T) {
+	doTestParseEntry(t, ParseEntry)
+}
+
+func doTestParseEntry(t *testing.T, parseEntry func(fset *token.FileSet, filename string, src interface{}, conf Config) (f *ast.File, err error)) {
 	fset := token.NewFileSet()
 	src, err := os.ReadFile("./_testdata/functype/functype.go")
 	if err != nil {
@@ -153,7 +188,7 @@ func TestParseEntry(t *testing.T) {
 	}
 	conf := Config{}
 	t.Run(".gop file", func(t *testing.T) {
-		f, err := ParseEntry(fset, "./functype.gop", src, conf)
+		f, err := parseEntry(fset, "./functype.gop", src, conf)
 		if err != nil {
 			t.Fatal("ParseEntry failed:", err)
 		}
@@ -162,7 +197,7 @@ func TestParseEntry(t *testing.T) {
 		}
 	})
 	t.Run(".gox file", func(t *testing.T) {
-		f, err := ParseEntry(fset, "./functype.gox", src, conf)
+		f, err := parseEntry(fset, "./functype.gox", src, conf)
 		if err != nil {
 			t.Fatal("ParseEntry failed:", err)
 		}
@@ -171,7 +206,7 @@ func TestParseEntry(t *testing.T) {
 		}
 	})
 	t.Run(".foo.gox file", func(t *testing.T) {
-		f, err := ParseEntry(fset, "./functype.foo.gox", src, conf)
+		f, err := parseEntry(fset, "./functype.foo.gox", src, conf)
 		if err != nil {
 			t.Fatal("ParseEntry failed:", err)
 		}
@@ -180,13 +215,13 @@ func TestParseEntry(t *testing.T) {
 		}
 	})
 	t.Run(".foo file", func(t *testing.T) {
-		_, err := ParseEntry(fset, "./functype.foo", src, conf)
+		_, err := parseEntry(fset, "./functype.foo", src, conf)
 		if err != ErrUnknownFileKind {
 			t.Fatal("ParseEntry failed:", err)
 		}
 	})
 	t.Run(".spx file", func(t *testing.T) {
-		f, err := ParseEntry(fset, "./main.spx", src, conf)
+		f, err := parseEntry(fset, "./main.spx", src, conf)
 		if err != nil {
 			t.Fatal("ParseEntry failed:", err)
 		}