github.com/traefik/yaegi@v0.15.1/cmd/yaegi/run.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "go/build" 7 "os" 8 "reflect" 9 "strconv" 10 "strings" 11 12 "github.com/traefik/yaegi/interp" 13 "github.com/traefik/yaegi/stdlib" 14 "github.com/traefik/yaegi/stdlib/syscall" 15 "github.com/traefik/yaegi/stdlib/unrestricted" 16 "github.com/traefik/yaegi/stdlib/unsafe" 17 ) 18 19 func run(arg []string) error { 20 var interactive bool 21 var noAutoImport bool 22 var tags string 23 var cmd string 24 var err error 25 26 // The following flags are initialized from environment. 27 useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL")) 28 useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED")) 29 useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE")) 30 31 rflag := flag.NewFlagSet("run", flag.ContinueOnError) 32 rflag.BoolVar(&interactive, "i", false, "start an interactive REPL") 33 rflag.BoolVar(&useSyscall, "syscall", useSyscall, "include syscall symbols") 34 rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols") 35 rflag.StringVar(&tags, "tags", "", "set a list of build tags") 36 rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols") 37 rflag.BoolVar(&noAutoImport, "noautoimport", false, "do not auto import pre-compiled packages. Import names that would result in collisions (e.g. rand from crypto/rand and rand from math/rand) are automatically renamed (crypto_rand and math_rand)") 38 rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)") 39 rflag.Usage = func() { 40 fmt.Println("Usage: yaegi run [options] [path] [args]") 41 fmt.Println("Options:") 42 rflag.PrintDefaults() 43 } 44 if err = rflag.Parse(arg); err != nil { 45 return err 46 } 47 args := rflag.Args() 48 49 i := interp.New(interp.Options{ 50 GoPath: build.Default.GOPATH, 51 BuildTags: strings.Split(tags, ","), 52 Env: os.Environ(), 53 Unrestricted: useUnrestricted, 54 }) 55 if err := i.Use(stdlib.Symbols); err != nil { 56 return err 57 } 58 if err := i.Use(interp.Symbols); err != nil { 59 return err 60 } 61 if useSyscall { 62 if err := i.Use(syscall.Symbols); err != nil { 63 return err 64 } 65 // Using a environment var allows a nested interpreter to import the syscall package. 66 if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil { 67 return err 68 } 69 } 70 if useUnsafe { 71 if err := i.Use(unsafe.Symbols); err != nil { 72 return err 73 } 74 if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil { 75 return err 76 } 77 } 78 if useUnrestricted { 79 // Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them. 80 if err := i.Use(unrestricted.Symbols); err != nil { 81 return err 82 } 83 if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil { 84 return err 85 } 86 } 87 88 if cmd != "" { 89 if !noAutoImport { 90 i.ImportUsed() 91 } 92 var v reflect.Value 93 v, err = i.Eval(cmd) 94 if len(args) == 0 && v.IsValid() { 95 fmt.Println(v) 96 } 97 } 98 99 if len(args) == 0 { 100 if cmd == "" || interactive { 101 showError(err) 102 if !noAutoImport { 103 i.ImportUsed() 104 } 105 _, err = i.REPL() 106 } 107 return err 108 } 109 110 // Skip first os arg to set command line as expected by interpreted main. 111 path := args[0] 112 os.Args = arg 113 flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError) 114 115 if isFile(path) { 116 err = runFile(i, path, noAutoImport) 117 } else { 118 _, err = i.EvalPath(path) 119 } 120 121 if err != nil { 122 return err 123 } 124 125 if interactive { 126 _, err = i.REPL() 127 } 128 return err 129 } 130 131 func isFile(path string) bool { 132 fi, err := os.Stat(path) 133 return err == nil && fi.Mode().IsRegular() 134 } 135 136 func runFile(i *interp.Interpreter, path string, noAutoImport bool) error { 137 b, err := os.ReadFile(path) 138 if err != nil { 139 return err 140 } 141 142 if s := string(b); strings.HasPrefix(s, "#!") { 143 // Allow executable go scripts, Have the same behavior as in interactive mode. 144 s = strings.Replace(s, "#!", "//", 1) 145 if !noAutoImport { 146 i.ImportUsed() 147 } 148 _, err = i.Eval(s) 149 return err 150 } 151 152 // Files not starting with "#!" are supposed to be pure Go, directly Evaled. 153 _, err = i.EvalPath(path) 154 return err 155 } 156 157 func showError(err error) { 158 if err == nil { 159 return 160 } 161 fmt.Fprintln(os.Stderr, err) 162 if p, ok := err.(interp.Panic); ok { 163 fmt.Fprintln(os.Stderr, string(p.Stack)) 164 } 165 }