package interpimport ()// importSrc calls gta on the source code for the package identified by// importPath. rPath is the relative path to the directory containing the source// code for the package. It can also be "main" as a special value.func ( *Interpreter) (, string, bool) (string, error) {varstringvarerrorif .srcPkg[] != nil { , := .pkgNames[]if ! {return"", fmt.Errorf("inconsistent knowledge about %s", ) }return , nil }// For relative import paths in the form "./xxx" or "../xxx", the initial // base path is the directory of the interpreter input file, or "." if no file // was provided. // In all other cases, absolute import paths are resolved from the GOPATH // and the nested "vendor" directories.ifisPathRelative() {if == mainID { = "." } = filepath.Join(filepath.Dir(.name), , ) } elseif , , = .pkgDir(.context.GOPATH, , ); != nil {// Try again, assuming a root dir at the source location.if , = .rootFromSourceLocation(); != nil {return"", }if , , = .pkgDir(.context.GOPATH, , ); != nil {return"", } }if .rdir[] {return"", fmt.Errorf("import cycle not allowed\n\timports %s", ) } .rdir[] = true , := fs.ReadDir(.opt.filesystem, )if != nil {return"", }var []*nodevar []*node := make(map[string][]*node)var *nodevarstring// Parse source files.for , := range { := .Name()ifskipFile(&.context, , ) {continue } = filepath.Join(, )var []byteif , = fs.ReadFile(.opt.filesystem, ); != nil {return"", } , := .parse(string(), , false)if != nil {return"", }if == nil {continue }varstringif , , = .ast(); != nil {return"", }if == nil {continue }if .astDot { := .dotCmdif == "" { = defaultDotCmd(, "yaegi-ast-") } .astDot(dotWriter(), ) }if == "" { = } elseif != && {return"", fmt.Errorf("found packages %s and %s in %s", , , ) } = append(, ) := effectivePkg(, )var []*node , = .gta(, , , )if != nil {return"", } [] = append([], ...) }// Revisit incomplete nodes where GTA could not complete.for , := range {if = .gtaRetry(, , ); != nil {return"", } }// Generate control flow graphs.for , := range {var []*nodeif , = .cfg(, nil, , ); != nil {return"", } = append(, ...) }// Register source package in the interpreter. The package contains only // the global symbols in the package scope. .mutex.Lock() := .scopes[]if == nil { .mutex.Unlock()// A nil scope means that no even an empty package is created from source.return"", fmt.Errorf("no Go files in %s", ) } .srcPkg[] = .sym .pkgNames[] = .frame.mutex.Lock() .resizeFrame() .frame.mutex.Unlock() .mutex.Unlock()// Once all package sources have been parsed, execute entry points then init functions.for , := range {if = genRun(); != nil {return"", } .run(, nil) }// Wire and execute global vars in global scope gs. , := genGlobalVars(, )if != nil {return"", } .run(, nil)// Add main to list of functions to run, after all inits.if := .sym[mainID]; == mainID && != nil && { = append(, .node) }for , := range { .run(, .frame) }return , nil}// rootFromSourceLocation returns the path to the directory containing the input// Go file given to the interpreter, relative to $GOPATH/src.// It is meant to be called in the case when the initial input is a main package.func ( *Interpreter) () (string, error) { := .nameif == DefaultSourceName {return"", nil } , := os.Getwd()if != nil {return"", } := filepath.Join(, filepath.Dir()) := strings.TrimPrefix(, filepath.Join(.context.GOPATH, "src")+"/")if == {return"", fmt.Errorf("package location %s not in GOPATH", ) }return , nil}// pkgDir returns the absolute path in filesystem for a package given its import path// and the root of the subtree dependencies.func ( *Interpreter) ( string, , string) (string, string, error) { := filepath.Join(, "vendor") := filepath.Join(, "src", , )if , := fs.Stat(.opt.filesystem, ); == nil {return , , nil// found! } = filepath.Join(, "src", effectivePkg(, ))if , := fs.Stat(.opt.filesystem, ); == nil {return , , nil// found! }if == "" {if .context.GOPATH == "" {return"", "", fmt.Errorf("unable to find source related to: %q. Either the GOPATH environment variable, or the Interpreter.Options.GoPath needs to be set", ) }return"", "", fmt.Errorf("unable to find source related to: %q", ) } := filepath.Join(, "src", ) , := previousRoot(.opt.filesystem, , )if != nil {return"", "", }return .(, , )}const vendor = "vendor"// Find the previous source root (vendor > vendor > ... > GOPATH).func previousRoot( fs.FS, , string) (string, error) { = filepath.Clean() , := filepath.Split() = filepath.Clean()// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.if != mainID && != vendor { = strings.TrimSuffix(, string(filepath.Separator)) := strings.TrimSuffix(strings.TrimSuffix(, ), string(filepath.Separator))// look for the closest vendor in one of our direct ancestors, as it takes priority.varstringfor { , := fs.Stat(, filepath.Join(, vendor))if == nil && .IsDir() { = strings.TrimPrefix(strings.TrimPrefix(, ), string(filepath.Separator))break }if !errors.Is(, fs.ErrNotExist) {return"", }// stop when we reach GOPATH/srcif == {break }// stop when we reach GOPATH/src/blah = filepath.Dir()if == {break }// just an additional failsafe, stop if we reach the filesystem root, or dot (if // we are dealing with relative paths). // TODO(mpl): It should probably be a critical error actually, // as we shouldn't have gone that high up in the tree. // TODO(dennwc): This partially fails on Windows, since it cannot recognize drive letters as "root".if == string(filepath.Separator) || == "." || == "" {break } }if != "" {return , nil } }// TODO(mpl): the algorithm below might be redundant with the one above, // but keeping it for now. Investigate/simplify/remove later. := strings.Split(, string(filepath.Separator))varintfor := len() - 1; >= 0; -- {if [] == "vendor" { = break } }if == 0 {return"", nil }returnfilepath.Join([:]...), nil}func effectivePkg(, string) string { := strings.Split(, string(filepath.Separator)) := strings.Split(, string(filepath.Separator))var []string := 0 := 0for := 0; < len(); ++ { := [len()-1-] := len() - 1 - if > 0 && == [] && != 0 { = ++ } elseif == { = append(, ) } }varstringfor := len() - 1; >= 0; -- { = filepath.Join(, []) }returnfilepath.Join(, )}// isPathRelative returns true if path starts with "./" or "../".// It is intended for use on import paths, where "/" is always the directory separator.func isPathRelative( string) bool {returnstrings.HasPrefix(, "./") || strings.HasPrefix(, "../")}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.