diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go index a233f8578..c76895995 100644 --- a/cl/compile_spx_test.go +++ b/cl/compile_spx_test.go @@ -253,6 +253,34 @@ func (this *index) onInit() { `) } +func TestSpxGopEnv(t *testing.T) { + gopSpxTest(t, ` +echo "${PATH}" +`, ``, `package main + +import ( + "fmt" + "github.com/goplus/gop/cl/internal/spx" + "strconv" +) + +type bar struct { + spx.Sprite + *index +} +type index struct { + *spx.MyGame +} + +func (this *index) MainEntry() { + fmt.Println(strconv.Itoa(this.Gop_Env("PATH"))) +} +func main() { + spx.Gopt_MyGame_Main(new(index)) +} +`) +} + func TestSpxGopExec(t *testing.T) { gopSpxTest(t, ` vim "a.txt" diff --git a/cl/expr.go b/cl/expr.go index 27e4f21d6..10aefe210 100644 --- a/cl/expr.go +++ b/cl/expr.go @@ -64,15 +64,19 @@ const ( clIdentSelectorExpr // this ident is X (not Sel) of ast.SelectorExpr clIdentGoto clCallWithTwoValue - clCommandWithoutArgs - clCommandIdent + clCommandWithoutArgs // this expr is a command without args (eg. ls) + clCommandIdent // this expr is a command and an ident (eg. mkdir "abc") + clIdentInStringLitEx // this expr is an ident in a string extended literal (eg. ${PATH}) ) const ( objNormal = iota objPkgRef objCPkgRef - objGopExec + objGopExecOrEnv + + objGopEnv = objGopExecOrEnv + objGopExec = objGopExecOrEnv ) const errorPkgPath = "github.com/qiniu/x/errors" @@ -153,10 +157,15 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (pkg gox.PkgRef, k oldo, o = o, obj } else if o == nil { // for support Gop_Exec, see TestSpxGopExec - if (clCommandIdent&flags) != 0 && recv != nil && gopExecVal(cb, recv, ident) == nil { + if (clCommandIdent&flags) != 0 && recv != nil && gopMember(cb, recv, "Gop_Exec", ident) == nil { kind = objGopExec return } + // for support Gop_Env, see TestSpxGopEnv + if (clIdentInStringLitEx&flags) != 0 && recv != nil && gopMember(cb, recv, "Gop_Env", ident) == nil { + kind = objGopEnv + return + } if (clIdentGoto & flags) != 0 { l := ident.Obj.Data.(*ast.Ident) panic(ctx.newCodeErrorf(l.Pos(), "label %v is not defined", l.Name)) @@ -189,8 +198,8 @@ func classRecv(cb *gox.CodeBuilder) *types.Var { return nil } -func gopExecVal(cb *gox.CodeBuilder, recv *types.Var, ident *ast.Ident) error { - _, e := cb.Val(recv).Member("Gop_Exec", gox.MemberFlagVal, ident) +func gopMember(cb *gox.CodeBuilder, recv *types.Var, op string, src ...ast.Node) error { + _, e := cb.Val(recv).Member(op, gox.MemberFlagVal, src...) return e } @@ -269,9 +278,9 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) { flags |= clCommandIdent // for support Gop_Exec, see TestSpxGopExec } _, kind := compileIdent(ctx, v, flags) - if cmdNoArgs { + if cmdNoArgs || kind == objGopExecOrEnv { cb := ctx.cb - if kind == objGopExec { + if kind == objGopExecOrEnv { cb.Val(v.Name, v) } else { err := callCmdNoArgs(ctx, expr, false) @@ -697,7 +706,7 @@ func builtinOrGopExec(ctx *blockCtx, ifn *ast.Ident, v *ast.CallExpr, flags gox. func tryGopExec(cb *gox.CodeBuilder, ifn *ast.Ident) bool { if recv := classRecv(cb); recv != nil { cb.InternalStack().PopN(1) - if gopExecVal(cb, recv, ifn) == nil { + if gopMember(cb, recv, "Gop_Exec", ifn) == nil { cb.Val(ifn.Name, ifn) return true } @@ -943,7 +952,11 @@ func compileStringLitEx(ctx *blockCtx, cb *gox.CodeBuilder, lit *ast.BasicLit) { basicLit(cb, &ast.BasicLit{ValuePos: pos - 1, Value: quote + v + quote, Kind: token.STRING}) pos = next case ast.Expr: - compileExpr(ctx, v) + flags := 0 + if _, ok := v.(*ast.Ident); ok { + flags = clIdentInStringLitEx + } + compileExpr(ctx, v, flags) t := cb.Get(-1).Type if t.Underlying() != types.Typ[types.String] { cb.Member("string", gox.MemberFlagAutoProperty) diff --git a/cl/internal/spx/game.go b/cl/internal/spx/game.go index 6c00eed17..e7672028f 100644 --- a/cl/internal/spx/game.go +++ b/cl/internal/spx/game.go @@ -35,6 +35,10 @@ func (p *MyGame) Capout(doSth func()) (string, error) { return "", nil } +func (p *MyGame) Gop_Env(name string) int { + return 0 +} + func (p *MyGame) Gop_Exec(name string, args ...any) { }