github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/nash.go (about) 1 // Package nash provides a library to embed the `nash` scripting language 2 // within your program or create your own nash cli. 3 package nash 4 5 import ( 6 "bytes" 7 "fmt" 8 "io" 9 10 "github.com/madlambda/nash/ast" 11 shell "github.com/madlambda/nash/internal/sh" 12 "github.com/madlambda/nash/sh" 13 ) 14 15 type ( 16 // Shell is the execution engine of the scripting language. 17 Shell struct { 18 interp *shell.Shell 19 } 20 ) 21 22 func newShell(nashpath string, nashroot string, abort bool) (*Shell, error) { 23 var ( 24 nash Shell 25 err error 26 ) 27 28 if abort { 29 nash.interp, err = shell.NewAbortShell(nashpath, nashroot) 30 } else { 31 nash.interp, err = shell.NewShell(nashpath, nashroot) 32 } 33 if err != nil { 34 return nil, err 35 } 36 37 return &nash, nil 38 } 39 40 // New creates a new `nash.Shell` instance. 41 func New(nashpath string, nashroot string) (*Shell, error) { 42 return newShell(nashpath, nashroot, false) 43 } 44 45 // NewAbort creates a new shell that aborts in case of error on initialization. 46 // Useful for tests, to avoid trashing the output log. 47 func NewAbort(nashpath string, nashroot string) (*Shell, error) { 48 return newShell(nashpath, nashroot, true) 49 } 50 51 // SetDebug enable some logging for debug purposes. 52 func (nash *Shell) SetDebug(b bool) { 53 nash.interp.SetDebug(b) 54 } 55 56 func (nash *Shell) Log(fmt string, args ...interface{}) { 57 nash.interp.Log(fmt, args...) 58 } 59 60 // SetInteractive enables interactive (shell) mode. 61 func (nash *Shell) SetInteractive(b bool) { 62 nash.interp.SetInteractive(b) 63 } 64 65 func (nash *Shell) NashPath() string { 66 return nash.interp.NashPath() 67 } 68 69 // Environ returns the set of environment variables in the shell 70 func (nash *Shell) Environ() shell.Env { 71 return nash.interp.Environ() 72 } 73 74 // GetFn gets the function object. 75 func (nash *Shell) GetFn(name string) (sh.FnDef, error) { 76 fnObj, err := nash.interp.GetFn(name) 77 if err != nil { 78 return nil, err 79 } 80 return fnObj.Fn(), nil 81 } 82 83 // Prompt returns the environment prompt or the default one 84 func (nash *Shell) Prompt() string { 85 value, ok := nash.interp.Getenv("PROMPT") 86 87 if ok { 88 return value.String() 89 } 90 91 return "<no prompt> " 92 } 93 94 // SetNashdPath sets an alternativa path to nashd 95 func (nash *Shell) SetNashdPath(path string) { 96 nash.interp.SetNashdPath(path) 97 } 98 99 // Exec executes the code specified by string content. 100 // By default, nash uses os.Stdin, os.Stdout and os.Stderr as input, output 101 // and error file descriptors. You can change it with SetStdin, SetStdout and Stderr, 102 // respectively. 103 // The path is only used for error line reporting. If content represents a file, then 104 // setting path to this filename should improve debugging (or no). 105 func (nash *Shell) Exec(path, content string) error { 106 return nash.interp.Exec(path, content) 107 } 108 109 // ExecOutput executes the code specified by string content. 110 // 111 // It behaves like **Exec** with the exception that it will ignore any 112 // stdout parameter (and the default os.Stdout) and will return the 113 // whole stdout output in memory. 114 // 115 // This method has no side effects, it will preserve any previously 116 // setted stdout, it will only ignore the configured stdout to run 117 // the provided script content; 118 func (nash *Shell) ExecOutput(path, content string) ([]byte, error) { 119 oldstdout := nash.Stdout() 120 defer nash.SetStdout(oldstdout) 121 122 var output bytes.Buffer 123 nash.SetStdout(&output) 124 125 err := nash.interp.Exec(path, content) 126 return output.Bytes(), err 127 } 128 129 // ExecuteString executes the script content. 130 // Deprecated: Use Exec instead. 131 func (nash *Shell) ExecuteString(path, content string) error { 132 return nash.interp.Exec(path, content) 133 } 134 135 // ExecFile executes the script content of the file specified by path 136 // and passes as arguments to the script the given args slice. 137 func (nash *Shell) ExecFile(path string, args ...string) error { 138 if len(args) > 0 { 139 err := nash.ExecuteString("setting args", `var ARGS = `+args2Nash(args)) 140 if err != nil { 141 return fmt.Errorf("Failed to set nash arguments: %s", err.Error()) 142 } 143 } 144 return nash.interp.ExecFile(path) 145 } 146 147 // ExecuteFile executes the given file. 148 // Deprecated: Use ExecFile instead. 149 func (nash *Shell) ExecuteFile(path string) error { 150 return nash.interp.ExecFile(path) 151 } 152 153 // ExecuteTree executes the given tree. 154 // Deprecated: Use ExecTree instead. 155 func (nash *Shell) ExecuteTree(tr *ast.Tree) ([]sh.Obj, error) { 156 return nash.interp.ExecuteTree(tr) 157 } 158 159 // ExecTree evaluates the given abstract syntax tree. 160 // it returns the object result of eval or nil when not applied and error. 161 func (nash *Shell) ExecTree(tree *ast.Tree) ([]sh.Obj, error) { 162 return nash.interp.ExecuteTree(tree) 163 } 164 165 // SetStdout set the stdout of the nash engine. 166 func (nash *Shell) SetStdout(out io.Writer) { 167 nash.interp.SetStdout(out) 168 } 169 170 // SetStderr set the stderr of nash engine 171 func (nash *Shell) SetStderr(err io.Writer) { 172 nash.interp.SetStderr(err) 173 } 174 175 // SetStdin set the stdin of the nash engine 176 func (nash *Shell) SetStdin(in io.Reader) { 177 nash.interp.SetStdin(in) 178 } 179 180 // Stdin is the interpreter standard input 181 func (nash *Shell) Stdin() io.Reader { return nash.interp.Stdin() } 182 183 // Stdout is the interpreter standard output 184 func (nash *Shell) Stdout() io.Writer { return nash.interp.Stdout() } 185 186 // Stderr is the interpreter standard error 187 func (nash *Shell) Stderr() io.Writer { return nash.interp.Stderr() } 188 189 // Setvar sets or updates the variable in the nash session. It 190 // returns true if variable was found and properly updated. 191 func (nash *Shell) Setvar(name string, value sh.Obj) bool { 192 return nash.interp.Setvar(name, value) 193 } 194 195 // Newvar creates a new variable in the interpreter scope 196 func (nash *Shell) Newvar(name string, value sh.Obj) { 197 nash.interp.Newvar(name, value) 198 } 199 200 // Getvar retrieves a variable from nash session 201 func (nash *Shell) Getvar(name string) (sh.Obj, bool) { 202 return nash.interp.Getvar(name) 203 } 204 205 func args2Nash(args []string) string { 206 ret := "(" 207 208 for i := 0; i < len(args); i++ { 209 ret += `"` + args[i] + `"` 210 211 if i < (len(args) - 1) { 212 ret += " " 213 } 214 } 215 216 return ret + ")" 217 }