Repository: goplus/gop
Pull request #1403 (2023-08-23 08:23:29)

rename .gopx => .gox


Patch:
diff --git a/.gitattributes b/.gitattributes
index 5cbe6c61..04f25b95 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,4 +3,4 @@
 
 *.gop text eol=lf
 *.go text eol=lf
-*.gopx text eol=lf
+*.gox text eol=lf
diff --git a/cl/classfile.go b/cl/classfile.go
index c46e5934..cd2079fb 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -24,6 +24,7 @@ import (
 	"strings"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/parser"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 )
@@ -60,7 +61,7 @@ func (p *gmxSettings) getScheds(cb *gox.CodeBuilder) []goast.Stmt {
 }
 
 func newGmx(ctx *pkgCtx, pkg *gox.Package, file string, conf *Config) *gmxSettings {
-	ext := filepath.Ext(file)
+	ext, _ := parser.ClassFileExt(file)
 	gt, ok := conf.LookupClass(ext)
 	if !ok {
 		panic("TODO: class not found")
diff --git a/cl/compile.go b/cl/compile.go
index 3736a67b..232e27ea 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -22,13 +22,13 @@ import (
 	"go/types"
 	"log"
 	"os"
-	"path/filepath"
 	"reflect"
 	"sort"
 	"strings"
 
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/ast/fromgo"
+	"github.com/goplus/gop/parser"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 	"github.com/goplus/gox/cpackages"
@@ -408,9 +408,11 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 	}
 	if ctx.gmxSettings == nil {
 		for file, gmx := range files {
-			if gmx.IsClass && filepath.Ext(file) != ".gopx" {
-				ctx.gmxSettings = newGmx(ctx, p, file, conf)
-				break
+			if gmx.IsClass {
+				if ext, _ := parser.ClassFileExt(file); ext != ".gox" {
+					ctx.gmxSettings = newGmx(ctx, p, file, conf)
+					break
+				}
 			}
 		}
 	}
@@ -560,7 +562,8 @@ func preloadGopFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, con
 	case f.IsClass:
 		classType = getDefaultClass(file)
 		if parent.gmxSettings != nil {
-			o, ok := parent.sprite[filepath.Ext(file)]
+			ext, _ := parser.ClassFileExt(file)
+			o, ok := parent.sprite[ext]
 			if ok {
 				baseTypeName, baseType, spxClass = o.Name(), o.Type(), true
 			}
diff --git a/cmd/gopfmt/gopfmt.go b/cmd/gopfmt/gopfmt.go
index e627fc86..9855735e 100644
--- a/cmd/gopfmt/gopfmt.go
+++ b/cmd/gopfmt/gopfmt.go
@@ -138,7 +138,7 @@ func (w *walker) walk(path string, d fs.DirEntry, err error) error {
 					switch ext {
 					case ".go", ".gop":
 						ok = true
-					case ".gopx", ".spx", ".gmx":
+					case ".gox", ".spx", ".gmx":
 						ok, class = true, true
 					default:
 						_, class = mod.IsClass(ext)
@@ -153,7 +153,7 @@ func (w *walker) walk(path string, d fs.DirEntry, err error) error {
 					switch ext {
 					case ".go", ".gop":
 						ok = true
-					case ".gopx", ".spx", ".gmx":
+					case ".gox", ".spx", ".gmx":
 						ok, class = true, true
 					}
 					return
diff --git a/cmd/internal/gopfmt/fmt.go b/cmd/internal/gopfmt/fmt.go
index 41a1e5bb..a90c2de8 100644
--- a/cmd/internal/gopfmt/fmt.go
+++ b/cmd/internal/gopfmt/fmt.go
@@ -147,7 +147,7 @@ func (w *walker) walk(path string, d fs.DirEntry, err error) error {
 					switch ext {
 					case ".go", ".gop":
 						ok = true
-					case ".gopx", ".spx", ".gmx":
+					case ".gox", ".spx", ".gmx":
 						ok, class = true, true
 					default:
 						_, class = mod.IsClass(ext)
@@ -162,7 +162,7 @@ func (w *walker) walk(path string, d fs.DirEntry, err error) error {
 					switch ext {
 					case ".go", ".gop":
 						ok = true
-					case ".gopx", ".spx", ".gmx":
+					case ".gox", ".spx", ".gmx":
 						ok, class = true, true
 					}
 					return
diff --git a/parser/parser_gop.go b/parser/parser_gop.go
index 8642ecd4..3e7d6f74 100644
--- a/parser/parser_gop.go
+++ b/parser/parser_gop.go
@@ -19,6 +19,7 @@ package parser
 import (
 	"bytes"
 	"errors"
+	"fmt"
 	"io"
 	"io/fs"
 	"io/ioutil"
@@ -114,6 +115,17 @@ func ParseDirEx(fset *token.FileSet, path string, conf Config) (pkgs map[string]
 	return ParseFSDir(fset, local, path, conf)
 }
 
+// ClassFileExt returns the classfile extension
+func ClassFileExt(path string) (ext string, compositeGox bool) {
+	ext = filepath.Ext(path)
+	if ext == ".gox" {
+		if c := filepath.Ext(path[:len(path)-4]); c != "" {
+			return c, true
+		}
+	}
+	return
+}
+
 // ParseFSDir calls ParseFile for all files with names ending in ".gop" in the
 // directory specified by path and returns a map of package name -> package
 // AST with all the packages found.
@@ -140,7 +152,7 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 			continue
 		}
 		fname := d.Name()
-		ext := filepath.Ext(fname)
+		ext, compositeGox := ClassFileExt(fname)
 		var isProj, isClass, useGoParser bool
 		switch ext {
 		case ".gop":
@@ -149,10 +161,13 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 				continue
 			}
 			useGoParser = (conf.Mode & ParseGoAsGoPlus) == 0
-		case ".gopx":
+		case ".gox":
 			isClass = true
 		default:
 			if isProj, isClass = conf.IsClass(ext); !isClass {
+				if compositeGox {
+					return nil, fmt.Errorf("not found Go+ class by ext %q for %q", ext, fname)
+				}
 				continue
 			}
 		}


Test patch:
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index e7a64022..cd0a8b1a 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -555,3 +555,43 @@ func (this *Kai) onMsg(msg string) {
 }
 `, "Game.t2gmx", "Kai.t2spx")
 }
+
+func TestSpxGoxBasic(t *testing.T) {
+	gopSpxTestEx(t, `
+func onInit() {
+	for {
+	}
+}
+`, `
+func onMsg(msg string) {
+	for {
+		say "Hi"
+	}
+}
+`, `package main
+
+import spx "github.com/goplus/gop/cl/internal/spx"
+
+type Game struct {
+	*spx.MyGame
+}
+
+func (this *Game) onInit() {
+	for {
+		spx.SchedNow()
+	}
+}
+
+type Kai struct {
+	spx.Sprite
+	*Game
+}
+
+func (this *Kai) onMsg(msg string) {
+	for {
+		spx.Sched()
+		this.Say("Hi")
+	}
+}
+`, "Game.tgmx.gox", "Kai.tspx.gox")
+}
diff --git a/cl/compile_test.go b/cl/compile_test.go
index acec2b7f..3335a989 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -4349,7 +4349,7 @@ type Rect struct {
 func (this *Rect) Area() float64 {
 	return this.Width * this.Height
 }
-`, "Rect.gopx")
+`, "Rect.gox")
 	gopClTestFile(t, `
 import "bytes"
 var (
@@ -4366,7 +4366,7 @@ type Rect struct {
 
 func (this *Rect) test() {
 }
-`, "Rect.gopx")
+`, "Rect.gox")
 	gopClTestFile(t, `
 import "bytes"
 var (
@@ -4383,7 +4383,7 @@ type Rect struct {
 
 func (this *Rect) test() {
 }
-`, "Rect.gopx")
+`, "Rect.gox")
 	gopClTestFile(t, `
 import "bytes"
 var (
@@ -4404,7 +4404,7 @@ type Rect struct {
 
 func (this *Rect) test() {
 }
-`, "Rect.gopx")
+`, "Rect.gox")
 }
 
 func TestOverload(t *testing.T) {
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index dfeee851..28e92643 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -868,9 +868,9 @@ import (
 }
 
 func TestErrClassFileGopx(t *testing.T) {
-	codeErrorTestEx(t, "main", "Rect.gopx",
-		`./Rect.gopx:5:2: A redeclared
-	./Rect.gopx:3:2 other declaration of A`, `
+	codeErrorTestEx(t, "main", "Rect.gox",
+		`./Rect.gox:5:2: A redeclared
+	./Rect.gox:3:2 other declaration of A`, `
 var (
 	A
 	i int
diff --git a/parser/_testdata/gopxtest/bar.gopx b/parser/_testdata/goxtest/bar.gox
similarity index 100%
rename from parser/_testdata/gopxtest/bar.gopx
rename to parser/_testdata/goxtest/bar.gox
diff --git a/parser/_testdata/gopxtest/parser.expect b/parser/_testdata/goxtest/parser.expect
similarity index 99%
rename from parser/_testdata/gopxtest/parser.expect
rename to parser/_testdata/goxtest/parser.expect
index e8e3f13e..bc1af21d 100644
--- a/parser/_testdata/gopxtest/parser.expect
+++ b/parser/_testdata/goxtest/parser.expect
@@ -1,6 +1,6 @@
 package main
 
-file bar.gopx
+file bar.gox
 ast.GenDecl:
   Tok: var
   Specs:
diff --git a/parser/parser_test.go b/parser/parser_test.go
index a3e8fa1d..028e574f 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -59,7 +59,7 @@ func testClassErrCode(t *testing.T, code string, errExp, panicExp string) {
 		}
 	}()
 	fset := token.NewFileSet()
-	_, err := Parse(fset, "/foo/bar.gopx", code, ParseGoPlusClass)
+	_, err := Parse(fset, "/foo/bar.gox", code, ParseGoPlusClass)
 	if err == nil || err.Error() != errExp {
 		t.Fatal("testErrCode error:", err)
 	}
@@ -395,36 +395,36 @@ func TestClassErrCode(t *testing.T) {
 	A,B
 	v int
 )
-`, `/foo/bar.gopx:2:2: missing variable type or initialization`, ``)
+`, `/foo/bar.gox:2:2: missing variable type or initialization`, ``)
 	testClassErrCode(t, `var (
 	A.*B
 	v int
 )
-`, `/foo/bar.gopx:2:4: expected 'IDENT', found '*' (and 2 more errors)`, ``)
+`, `/foo/bar.gox:2:4: expected 'IDENT', found '*' (and 2 more errors)`, ``)
 	testClassErrCode(t, `var (
 	[]A
 	v int
 )
-`, `/foo/bar.gopx:2:2: expected 'IDENT', found '['`, ``)
+`, `/foo/bar.gox:2:2: expected 'IDENT', found '['`, ``)
 	testClassErrCode(t, `var (
 	*[]A
 	v int
 )
-`, `/foo/bar.gopx:2:3: expected 'IDENT', found '['`, ``)
+`, `/foo/bar.gox:2:3: expected 'IDENT', found '['`, ``)
 	testClassErrCode(t, `var (
 	v int = 10
 )
-`, `/foo/bar.gopx:2:8: syntax error: cannot assign value to field in class file`, ``)
+`, `/foo/bar.gox:2:8: syntax error: cannot assign value to field in class file`, ``)
 	testClassErrCode(t, `var (
 	v = 10
 )
-`, `/foo/bar.gopx:2:4: syntax error: cannot assign value to field in class file`, ``)
+`, `/foo/bar.gox:2:4: syntax error: cannot assign value to field in class file`, ``)
 	testClassErrCode(t, `
 var (
 )
 const c = 100
 const d
-`, `/foo/bar.gopx:5:7: missing constant value`, ``)
+`, `/foo/bar.gox:5:7: missing constant value`, ``)
 }
 
 // -----------------------------------------------------------------------------
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 99f2016a..bb84de9f 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -173,6 +173,12 @@ func TestErrParse(t *testing.T) {
 	if err != syscall.ENOENT {
 		t.Fatal("ParseFSDir:", err)
 	}
+
+	fs = parsertest.NewSingleFileFS("/foo", "test.abc.gox", `package foo`)
+	_, err = ParseFSDir(fset, fs, "/foo", Config{})
+	if err == nil {
+		t.Fatal("ParseFSDir test.gop: no error?")
+	}
 }
 
 func testFromDir(t *testing.T, sel, relDir string) {
diff --git a/printer/gop_test.go b/printer/gop_test.go
index 19b4fd58..30bd7aba 100644
--- a/printer/gop_test.go
+++ b/printer/gop_test.go
@@ -122,7 +122,7 @@ func testFrom(t *testing.T, fpath, sel string, mode int) {
 	if (mode & excludeFormatSource) == 0 {
 		t.Run("format.Source "+fpath, func(t *testing.T) {
 			var class bool
-			if filepath.Ext(fpath) == ".gopx" {
+			if filepath.Ext(fpath) == ".gox" {
 				class = true
 			}
 			res, err := format.Source(src, class, fpath)
@@ -137,7 +137,7 @@ func testFrom(t *testing.T, fpath, sel string, mode int) {
 		t.Run("format.Node "+fpath, func(t *testing.T) {
 			fset := token.NewFileSet()
 			m := parser.ParseComments
-			if filepath.Ext(fpath) == ".gopx" {
+			if filepath.Ext(fpath) == ".gox" {
 				m |= parser.ParseGoPlusClass
 			}
 			f, err := parser.ParseFile(fset, fpath, src, m)
@@ -191,7 +191,7 @@ func TestFromParse(t *testing.T) {
 		}
 		name := info.Name()
 		ext := filepath.Ext(name)
-		if !info.IsDir() && (ext == ".gop" || ext == ".gopx") {
+		if !info.IsDir() && (ext == ".gop" || ext == ".gox") {
 			testFrom(t, path, sel, 0)
 		}
 		return nil