Repository: goplus/gop
Pull request #1736 (2024-02-13 10:43:01)

Gop_Exec support


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index 0acc4d16..0495b708 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -64,30 +64,34 @@ const (
 	clIdentGoto
 	clCallWithTwoValue
 	clCommandWithoutArgs
+	clCommandIdent
 )
 
 const (
 	objNormal = iota
 	objPkgRef
 	objCPkgRef
+	objGopExec
 )
 
 const errorPkgPath = "github.com/qiniu/x/errors"
 
 func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (pkg gox.PkgRef, kind int) {
 	fvalue := (flags&clIdentSelectorExpr) != 0 || (flags&clIdentLHS) == 0
+	cb := ctx.cb
 	name := ident.Name
 	if name == "_" {
 		if fvalue {
 			panic(ctx.newCodeError(ident.Pos(), "cannot use _ as value"))
 		}
-		ctx.cb.VarRef(nil)
+		cb.VarRef(nil)
 		return
 	}
 
+	var recv *types.Var
 	var oldo types.Object
 	scope := ctx.pkg.Types.Scope()
-	at, o := ctx.cb.Scope().LookupParent(name, token.NoPos)
+	at, o := cb.Scope().LookupParent(name, token.NoPos)
 	if o != nil {
 		if at != scope && at != types.Universe { // local object
 			goto find
@@ -95,10 +99,10 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (pkg gox.PkgRef, k
 	}
 
 	if ctx.isClass { // in a Go+ class file
-		if fn := ctx.cb.Func(); fn != nil {
+		if fn := cb.Func(); fn != nil {
 			sig := fn.Ancestor().Type().(*types.Signature)
-			if recv := sig.Recv(); recv != nil {
-				ctx.cb.Val(recv)
+			if recv = sig.Recv(); recv != nil {
+				cb.Val(recv)
 				chkFlag := flags // &^ clCommandWithoutArgs (TODO: why?)
 				if chkFlag&clIdentSelectorExpr != 0 {
 					chkFlag = clIdentCanAutoCall
@@ -106,7 +110,7 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (pkg gox.PkgRef, k
 				if compileMember(ctx, ident, name, chkFlag) == nil { // class member object
 					return
 				}
-				ctx.cb.InternalStack().PopN(1)
+				cb.InternalStack().PopN(1)
 			}
 		}
 	}
@@ -150,6 +154,12 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (pkg gox.PkgRef, k
 		}
 		oldo, o = o, obj
 	} else if o == nil {
+		if (clCommandIdent&flags) != 0 && recv != nil { // for support Gop_Exec, see TestSpxGopExec
+			if _, e := cb.Val(recv).Member("Gop_Exec", gox.MemberFlagVal, ident); e == nil {
+				kind = objGopExec
+				return
+			}
+		}
 		if (clIdentGoto & flags) != 0 {
 			l := ident.Obj.Data.(*ast.Ident)
 			panic(ctx.newCodeErrorf(l.Pos(), "label %v is not defined", l.Name))
@@ -159,13 +169,13 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (pkg gox.PkgRef, k
 
 find:
 	if fvalue {
-		ctx.cb.Val(o, ident)
+		cb.Val(o, ident)
 	} else {
-		ctx.cb.VarRef(o, ident)
+		cb.VarRef(o, ident)
 	}
 	if rec := ctx.recorder(); rec != nil {
-		e := ctx.cb.Get(-1)
-		if oldo != nil && gox.IsTypeEx(e.Type) {
+		e := cb.Get(-1)
+		if oldo != nil && gox.IsTypeEx(e.Type) { // for builtin object
 			rec.recordIdent(ctx, ident, oldo)
 			return
 		}
@@ -564,8 +574,17 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 	var ifn *ast.Ident
 	switch fn := v.Fun.(type) {
 	case *ast.Ident:
-		compileIdent(ctx, fn, clIdentAllowBuiltin|inFlags)
-		ifn = fn
+		if v.IsCommand() { // for support Gop_Exec, see TestSpxGopExec
+			inFlags |= clCommandIdent
+		}
+		if _, kind := compileIdent(ctx, fn, clIdentAllowBuiltin|inFlags); kind == objGopExec {
+			args := make([]ast.Expr, 1, len(v.Args)+1)
+			args[0] = &ast.BasicLit{ValuePos: fn.NamePos, Kind: token.STRING, Value: strconv.Quote(fn.Name)}
+			args = append(args, v.Args...)
+			v = &ast.CallExpr{Fun: fn, Args: args, Ellipsis: v.Ellipsis, NoParenEnd: v.NoParenEnd}
+		} else {
+			ifn = fn
+		}
 	case *ast.SelectorExpr:
 		compileSelectorExpr(ctx, fn, 0)
 	case *ast.ErrWrapExpr:
@@ -615,7 +634,7 @@ func mayBuiltin(ctx *blockCtx, ifn *ast.Ident, v *ast.CallExpr, flags gox.InstrF
 	switch name := ifn.Name; name {
 	case "new", "delete":
 		cb := ctx.cb
-		cb.InternalStack().Pop()
+		cb.InternalStack().PopN(1)
 		o := ctx.pkg.Builtin().Ref(name)
 		cb.Val(o, ifn)
 		for _, arg := range v.Args {
@@ -665,7 +684,7 @@ func compileCallArgs(fn *fnType, ctx *blockCtx, v *ast.CallExpr, ellipsis bool,
 			}
 			typetype := fn.typetype && t != nil
 			if typetype {
-				stk.Pop()
+				stk.PopN(1)
 			}
 			compileSliceLit(ctx, expr, t)
 			if typetype {
diff --git a/cl/internal/spx/game.go b/cl/internal/spx/game.go
index 0b7123f9..0c591af5 100644
--- a/cl/internal/spx/game.go
+++ b/cl/internal/spx/game.go
@@ -29,6 +29,9 @@ type MyGame struct {
 func Gopt_MyGame_Main(game interface{}) {
 }
 
+func (p *MyGame) Gop_Exec(name string, args ...any) {
+}
+
 func (p *MyGame) InitGameApp(args ...string) {
 }
 


Test patch:
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 32041a5f..04c7780a 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -253,6 +253,30 @@ func (this *index) onInit() {
 `)
 }
 
+func TestSpxGopExec(t *testing.T) {
+	gopSpxTest(t, `
+vim "a.txt"
+`, ``, `package main
+
+import "github.com/goplus/gop/cl/internal/spx"
+
+type bar struct {
+	spx.Sprite
+	*index
+}
+type index struct {
+	*spx.MyGame
+}
+
+func (this *index) MainEntry() {
+	this.Gop_Exec("vim", "a.txt")
+}
+func main() {
+	spx.Gopt_MyGame_Main(new(index))
+}
+`)
+}
+
 func TestSpxMethod(t *testing.T) {
 	gopSpxTestEx(t, `
 func onInit() {