diff --git a/README.md b/README.md index 905fdebb..35c80019 100644 --- a/README.md +++ b/README.md @@ -128,10 +128,20 @@ Hello World > ``` -Or interpret Go files: +Note that in interactive mode, all stdlib package are pre-imported, +you can use them directly: ```console -$ yaegi cmd/yaegi/yaegi.go +$ yaegi +> reflect.TypeOf(time.Date) +: func(int, time.Month, int, int, int, int, int, *time.Location) time.Time +> +``` + +Or interpret Go packages, directories or files, including itself: + +```console +$ yaegi -syscall -unsafe -unrestricted github.com/traefik/yaegi/cmd/yaegi > ``` diff --git a/cmd/yaegi/run.go b/cmd/yaegi/run.go index f757f7d7..0ebc69a2 100644 --- a/cmd/yaegi/run.go +++ b/cmd/yaegi/run.go @@ -6,6 +6,7 @@ import ( "go/build" "io/ioutil" "os" + "strconv" "strings" "github.com/traefik/yaegi/interp" @@ -17,19 +18,21 @@ import ( func run(arg []string) error { var interactive bool - var useSyscall bool - var useUnrestricted bool - var useUnsafe bool var tags string var cmd string var err error + // The following flags are initialized from environment. + useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL")) + useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED")) + useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE")) + rflag := flag.NewFlagSet("run", flag.ContinueOnError) rflag.BoolVar(&interactive, "i", false, "start an interactive REPL") - rflag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols") - rflag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols") + rflag.BoolVar(&useSyscall, "syscall", useSyscall, "include syscall symbols") + rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols") rflag.StringVar(&tags, "tags", "", "set a list of build tags") - rflag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols") + rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols") rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)") rflag.Usage = func() { fmt.Println("Usage: yaegi run [options] [path] [args]") @@ -46,13 +49,23 @@ func run(arg []string) error { i.Use(interp.Symbols) if useSyscall { i.Use(syscall.Symbols) + // Using a environment var allows a nested interpreter to import the syscall package. + if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil { + return err + } } if useUnsafe { i.Use(unsafe.Symbols) + if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil { + return err + } } if useUnrestricted { // Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them. i.Use(unrestricted.Symbols) + if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil { + return err + } } if cmd != "" { diff --git a/cmd/yaegi/yaegi.go b/cmd/yaegi/yaegi.go index 01c1384d..53baf2cf 100644 --- a/cmd/yaegi/yaegi.go +++ b/cmd/yaegi/yaegi.go @@ -71,7 +71,13 @@ Options: -unsafe include unsafe symbols. -Debugging support (may be removed at any time): +Environment variables: + YAEGI_SYSCALL=1 + Include syscall symbols (same as -syscall flag). + YAEGI_UNRESTRICTED=1 + Include unrestricted symbols (same as -unrestricted flag). + YAEGI_UNSAFE=1 + Include unsafe symbols (same as -unsafe flag). YAEGI_PROMPT=1 Force enable the printing of the REPL prompt and the result of last instruction, even if stdin is not a terminal. diff --git a/interp/hooks.go b/interp/hooks.go index 14d71c7c..30c538ba 100644 --- a/interp/hooks.go +++ b/interp/hooks.go @@ -2,8 +2,6 @@ package interp import "reflect" -const hooksPath = "github.com/traefik/yaegi" - // convertFn is the signature of a symbol converter. type convertFn func(from, to reflect.Type) func(src, dest reflect.Value) diff --git a/interp/interp.go b/interp/interp.go index 92761afa..0cf9a6f7 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -155,8 +155,9 @@ type Interpreter struct { } const ( - mainID = "main" - selfPath = "github.com/traefik/yaegi/interp" + mainID = "main" + selfPrefix = "github.com/traefik/yaegi" + selfPath = selfPrefix + "/interp" // DefaultSourceName is the name used by default when the name of the input // source file has not been specified for an Eval. // TODO(mpl): something even more special as a name? @@ -175,6 +176,7 @@ var Symbols = Exports{ "Interpreter": reflect.ValueOf((*Interpreter)(nil)), "Options": reflect.ValueOf((*Options)(nil)), + "Panic": reflect.ValueOf((*Panic)(nil)), }, } @@ -603,7 +605,7 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type { // they can be used in interpreted code. func (interp *Interpreter) Use(values Exports) { for k, v := range values { - if k == hooksPath { + if k == selfPrefix { interp.hooks.Parse(v) continue } diff --git a/interp/run.go b/interp/run.go index 7430a9a1..24226463 100644 --- a/interp/run.go +++ b/interp/run.go @@ -2037,7 +2037,7 @@ func doCompositeBinStruct(n *node, hasType bool) { } } else { fieldIndex[i] = []int{i} - if c.typ.cat == funcT { + if c.typ.cat == funcT && len(c.child) > 1 { convertLiteralValue(c.child[1], typ.Field(i).Type) values[i] = genFunctionWrapper(c.child[1]) } else { diff --git a/interp/src.go b/interp/src.go index 7f0c12f8..0ed8312c 100644 --- a/interp/src.go +++ b/interp/src.go @@ -33,17 +33,12 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s rPath = "." } dir = filepath.Join(filepath.Dir(interp.name), rPath, importPath) - } else { - var root string - if rPath == mainID { - root, err = interp.rootFromSourceLocation() - if err != nil { - return "", err - } - } else { - root = rPath + } else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, importPath); err != nil { + // Try again, assuming a root dir at the source location. + if rPath, err = interp.rootFromSourceLocation(); err != nil { + return "", err } - if dir, rPath, err = pkgDir(interp.context.GOPATH, root, importPath); err != nil { + if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, importPath); err != nil { return "", err } } diff --git a/interp/type.go b/interp/type.go index d8820317..f5b29f5d 100644 --- a/interp/type.go +++ b/interp/type.go @@ -559,7 +559,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { } } else { err = n.cfgErrorf("undefined selector %s.%s", lt.path, name) - panic(err) } case srcPkgT: pkg := interp.srcPkg[lt.path]