github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/internal/sh/fncall.go (about) 1 package sh 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/madlambda/nash/ast" 9 "github.com/madlambda/nash/errors" 10 "github.com/madlambda/nash/sh" 11 ) 12 13 type ( 14 FnArg struct { 15 Name string 16 IsVariadic bool 17 } 18 19 UserFn struct { 20 argNames []sh.FnArg // argNames store parameter name 21 done chan error // for async execution 22 results []sh.Obj 23 24 name string // debugging purposes 25 parent *Shell 26 subshell *Shell 27 28 environ []string 29 30 stdin io.Reader 31 stdout, stderr io.Writer 32 33 body *ast.Tree 34 repr string 35 closeAfterWait []io.Closer 36 } 37 ) 38 39 func NewUserFn(name string, args []sh.FnArg, body *ast.Tree, parent *Shell) *UserFn { 40 fn := &UserFn{ 41 name: name, 42 argNames: args, 43 body: body, 44 done: make(chan error), 45 parent: parent, 46 stdin: parent.Stdin(), 47 stdout: parent.Stdout(), 48 stderr: parent.Stderr(), 49 subshell: NewSubShell(name, parent), 50 } 51 52 fn.subshell.SetTree(fn.body) 53 fn.subshell.SetRepr(fn.repr) 54 fn.subshell.SetDebug(fn.parent.debug) 55 fn.subshell.SetStdout(fn.stdout) 56 fn.subshell.SetStderr(fn.stderr) 57 fn.subshell.SetStdin(fn.stdin) 58 fn.subshell.SetEnviron(fn.environ) 59 return fn 60 } 61 62 func (fn *UserFn) ArgNames() []sh.FnArg { return fn.argNames } 63 64 func (fn *UserFn) AddArgName(arg sh.FnArg) { 65 fn.argNames = append(fn.argNames, arg) 66 } 67 68 func (fn *UserFn) SetArgs(args []sh.Obj) error { 69 var ( 70 isVariadic bool 71 countNormalArgs int 72 ) 73 74 for i, argName := range fn.argNames { 75 if argName.IsVariadic { 76 if i != len(fn.argNames)-1 { 77 return errors.NewError("variadic expansion must be last argument") 78 } 79 isVariadic = true 80 } else { 81 countNormalArgs++ 82 } 83 } 84 85 if !isVariadic && len(args) != len(fn.argNames) { 86 return errors.NewError("Wrong number of arguments for function %s. "+ 87 "Expected %d but found %d", 88 fn.name, len(fn.argNames), len(args)) 89 } 90 91 if isVariadic { 92 if len(args) < countNormalArgs { 93 return errors.NewError("Wrong number of arguments for function %s. "+ 94 "Expected at least %d arguments but found %d", fn.name, 95 countNormalArgs, len(args)) 96 } 97 98 if len(args) == 0 { 99 // there's only a variadic (optional) argument 100 // and user supplied no argument... 101 // then only initialize the variadic variable to 102 // empty list 103 fn.subshell.Newvar(fn.argNames[0].Name, sh.NewListObj([]sh.Obj{})) 104 return nil 105 } 106 } 107 108 var i int 109 for i = 0; i < len(fn.argNames) && i < len(args); i++ { 110 arg := args[i] 111 argName := fn.argNames[i].Name 112 isVariadic := fn.argNames[i].IsVariadic 113 114 if isVariadic { 115 var valist []sh.Obj 116 for ; i < len(args); i++ { 117 arg = args[i] 118 valist = append(valist, arg) 119 } 120 valistarg := sh.NewListObj(valist) 121 fn.subshell.Newvar(argName, valistarg) 122 } else { 123 fn.subshell.Newvar(argName, arg) 124 } 125 } 126 127 // set remaining (variadic) list 128 if len(fn.argNames) > 0 && i < len(fn.argNames) { 129 last := fn.argNames[len(fn.argNames)-1] 130 if !last.IsVariadic { 131 return errors.NewError("internal error: optional arguments only for variadic parameter") 132 } 133 134 fn.subshell.Newvar(last.Name, sh.NewListObj([]sh.Obj{})) 135 } 136 137 return nil 138 } 139 140 func (fn *UserFn) Name() string { return fn.name } 141 142 func (fn *UserFn) SetRepr(repr string) { 143 fn.repr = repr 144 } 145 146 func (fn *UserFn) closeDescriptors(closers []io.Closer) { 147 for _, fd := range closers { 148 fd.Close() 149 } 150 } 151 152 func (fn *UserFn) execute() ([]sh.Obj, error) { 153 if fn.body != nil { 154 return fn.subshell.ExecuteTree(fn.body) 155 } 156 157 return nil, fmt.Errorf("fn not properly created") 158 } 159 160 func (fn *UserFn) Start() error { 161 go func() { 162 var err error 163 fn.results, err = fn.execute() 164 fn.done <- err 165 }() 166 167 return nil 168 } 169 170 func (fn *UserFn) Results() []sh.Obj { return fn.results } 171 172 func (fn *UserFn) Wait() error { 173 err := <-fn.done 174 175 fn.closeDescriptors(fn.closeAfterWait) 176 fn.subshell = nil 177 return err 178 } 179 180 func (fn *UserFn) SetEnviron(env []string) { 181 fn.environ = env 182 } 183 184 func (fn *UserFn) SetStderr(w io.Writer) { 185 fn.stderr = w 186 } 187 188 func (fn *UserFn) SetStdout(w io.Writer) { 189 fn.stdout = w 190 } 191 192 func (fn *UserFn) SetStdin(r io.Reader) { 193 fn.stdin = r 194 } 195 196 func (fn *UserFn) Stdin() io.Reader { return fn.stdin } 197 func (fn *UserFn) Stdout() io.Writer { return fn.stdout } 198 func (fn *UserFn) Stderr() io.Writer { return fn.stderr } 199 200 func (fn *UserFn) String() string { 201 if fn.body != nil { 202 return fn.body.String() 203 } 204 panic("fn not initialized") 205 } 206 207 func (fn *UserFn) StdoutPipe() (io.ReadCloser, error) { 208 pr, pw, err := os.Pipe() 209 210 if err != nil { 211 return nil, err 212 } 213 214 fn.subshell.SetStdout(pw) 215 216 // As fn doesn't fork, both fd can be closed after wait is called 217 fn.closeAfterWait = append(fn.closeAfterWait, pw, pr) 218 return pr, nil 219 }