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