diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go index 32041a5f3..04c7780a8 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() { diff --git a/cl/expr.go b/cl/expr.go index 0acc4d165..0495b708a 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 0b7123f98..0c591af55 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) { }