github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/elvish/eval/compile_op.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "sync" 8 9 "github.com/u-root/u-root/cmds/elvish/eval/vals" 10 "github.com/u-root/u-root/cmds/elvish/eval/vars" 11 "github.com/u-root/u-root/cmds/elvish/parse" 12 "github.com/u-root/u-root/cmds/elvish/util" 13 "github.com/u-root/u-root/cmds/elvish/hashmap" 14 ) 15 16 // Op is an operation on an Frame. 17 type Op struct { 18 Body OpBody 19 Begin, End int 20 } 21 22 // OpBody is the body of an Op. 23 type OpBody interface { 24 Invoke(*Frame) error 25 } 26 27 // Exec executes an Op. 28 func (op Op) Exec(fm *Frame) error { 29 fm.begin, fm.end = op.Begin, op.End 30 return op.Body.Invoke(fm) 31 } 32 33 func (cp *compiler) chunk(n *parse.Chunk) OpBody { 34 return chunkOp{cp.pipelineOps(n.Pipelines)} 35 } 36 37 type chunkOp struct { 38 subops []Op 39 } 40 41 func (op chunkOp) Invoke(fm *Frame) error { 42 for _, subop := range op.subops { 43 err := subop.Exec(fm) 44 if err != nil { 45 return err 46 } 47 } 48 // Check for interrupts after the chunk. 49 // We also check for interrupts before each pipeline, so there is no 50 // need to check it before the chunk or after each pipeline. 51 if fm.IsInterrupted() { 52 return ErrInterrupted 53 } 54 return nil 55 } 56 57 func (cp *compiler) pipeline(n *parse.Pipeline) OpBody { 58 return &pipelineOp{n.Background, n.SourceText(), cp.formOps(n.Forms)} 59 } 60 61 type pipelineOp struct { 62 bg bool 63 source string 64 subops []Op 65 } 66 67 const pipelineChanBufferSize = 32 68 69 func (op *pipelineOp) Invoke(fm *Frame) error { 70 if fm.IsInterrupted() { 71 return ErrInterrupted 72 } 73 74 if op.bg { 75 fm = fm.fork("background job" + op.source) 76 fm.intCh = nil 77 fm.background = true 78 79 if fm.Editor != nil { 80 // TODO: Redirect output in interactive mode so that the line 81 // editor does not get messed up. 82 } 83 } 84 85 nforms := len(op.subops) 86 87 var wg sync.WaitGroup 88 wg.Add(nforms) 89 errors := make([]*Exception, nforms) 90 91 var nextIn *Port 92 93 // For each form, create a dedicated evalCtx and run asynchronously 94 for i, op := range op.subops { 95 hasChanInput := i > 0 96 newFm := fm.fork("[form op]") 97 if i > 0 { 98 newFm.ports[0] = nextIn 99 } 100 if i < nforms-1 { 101 // Each internal port pair consists of a (byte) pipe pair and a 102 // channel. 103 // os.Pipe sets O_CLOEXEC, which is what we want. 104 reader, writer, e := os.Pipe() 105 if e != nil { 106 return fmt.Errorf("failed to create pipe: %s", e) 107 } 108 ch := make(chan interface{}, pipelineChanBufferSize) 109 newFm.ports[1] = &Port{ 110 File: writer, Chan: ch, CloseFile: true, CloseChan: true} 111 nextIn = &Port{ 112 File: reader, Chan: ch, CloseFile: true, CloseChan: false} 113 } 114 thisOp := op 115 thisError := &errors[i] 116 go func() { 117 err := newFm.Eval(thisOp) 118 newFm.Close() 119 if err != nil { 120 *thisError = err.(*Exception) 121 } 122 wg.Done() 123 if hasChanInput { 124 // If the command has channel input, drain it. This 125 // mitigates the effect of erroneous pipelines like 126 // "range 100 | cat"; without draining the pipeline will 127 // lock up. 128 for range newFm.ports[0].Chan { 129 } 130 } 131 }() 132 } 133 134 if op.bg { 135 // Background job, wait for form termination asynchronously. 136 go func() { 137 wg.Wait() 138 msg := "job " + op.source + " finished" 139 err := ComposeExceptionsFromPipeline(errors) 140 if err != nil { 141 msg += ", errors = " + err.Error() 142 } 143 if fm.Editor != nil { 144 fm.Editor.Notify("%s", msg) 145 } else { 146 fm.ports[2].File.WriteString(msg + "\n") 147 } 148 }() 149 return nil 150 } else { 151 wg.Wait() 152 return ComposeExceptionsFromPipeline(errors) 153 } 154 } 155 156 func (cp *compiler) form(n *parse.Form) OpBody { 157 var saveVarsOps []LValuesOp 158 var assignmentOps []Op 159 if len(n.Assignments) > 0 { 160 assignmentOps = cp.assignmentOps(n.Assignments) 161 if n.Head == nil && n.Vars == nil { 162 // Permanent assignment. 163 return seqOp{assignmentOps} 164 } 165 for _, a := range n.Assignments { 166 v, r := cp.lvaluesOp(a.Left) 167 saveVarsOps = append(saveVarsOps, v, r) 168 } 169 logger.Println("temporary assignment of", len(n.Assignments), "pairs") 170 } 171 172 // Depending on the type of the form, exactly one of the three below will be 173 // set. 174 var ( 175 specialOpFunc OpBody 176 headOp ValuesOp 177 spaceyAssignOp Op 178 ) 179 180 // Forward declaration; needed when compiling assignment forms. 181 var argOps []ValuesOp 182 183 if n.Head != nil { 184 headStr, ok := oneString(n.Head) 185 if ok { 186 compileForm, ok := builtinSpecials[headStr] 187 if ok { 188 // Special form. 189 specialOpFunc = compileForm(cp, n) 190 } else { 191 var headOpFunc ValuesOpBody 192 explode, ns, name := ParseVariableRef(headStr) 193 if !explode && cp.registerVariableGet(ns, name+FnSuffix) { 194 // $head~ resolves. 195 headOpFunc = variableOp{false, ns, name + FnSuffix} 196 } else { 197 // Fall back to $e:head~. 198 headOpFunc = literalValues(ExternalCmd{headStr}) 199 } 200 headOp = ValuesOp{headOpFunc, n.Head.Begin(), n.Head.End()} 201 } 202 } else { 203 // Head exists and is not a literal string. Evaluate as a normal 204 // expression. 205 headOp = cp.compoundOp(n.Head) 206 } 207 } else { 208 // Assignment form. 209 varsOp, restOp := cp.lvaluesMulti(n.Vars) 210 // This cannot be replaced with newSeqValuesOp as it depends on the fact 211 // that argOps will be changed later. 212 argsOp := ValuesOp{ 213 funcValuesOp(func(fm *Frame) ([]interface{}, error) { 214 var values []interface{} 215 for _, op := range argOps { 216 moreValues, err := op.Exec(fm) 217 if err != nil { 218 return nil, err 219 } 220 values = append(values, moreValues...) 221 } 222 return values, nil 223 }), -1, -1} 224 if len(argOps) > 0 { 225 argsOp.Begin = argOps[0].Begin 226 argsOp.End = argOps[len(argOps)-1].End 227 } 228 spaceyAssignOp = Op{ 229 &assignmentOp{varsOp, restOp, argsOp}, 230 n.Begin(), argsOp.End, 231 } 232 } 233 234 argOps = cp.compoundOps(n.Args) 235 optsOp := cp.mapPairs(n.Opts) 236 redirOps := cp.redirOps(n.Redirs) 237 // TODO: n.ErrorRedir 238 239 return &formOp{saveVarsOps, assignmentOps, redirOps, specialOpFunc, headOp, argOps, optsOp, spaceyAssignOp, n.Begin(), n.End()} 240 } 241 242 type formOp struct { 243 saveVarsOps []LValuesOp 244 assignmentOps []Op 245 redirOps []Op 246 specialOpBody OpBody 247 headOp ValuesOp 248 argOps []ValuesOp 249 optsOp ValuesOpBody 250 spaceyAssignOp Op 251 begin, end int 252 } 253 254 func (op *formOp) Invoke(fm *Frame) (errRet error) { 255 // ec here is always a subevaler created in compiler.pipeline, so it can 256 // be safely modified. 257 258 // Temporary assignment. 259 if len(op.saveVarsOps) > 0 { 260 // There is a temporary assignment. 261 // Save variables. 262 var saveVars []vars.Var 263 var saveVals []interface{} 264 for _, op := range op.saveVarsOps { 265 moreSaveVars, err := op.Exec(fm) 266 if err != nil { 267 return err 268 } 269 saveVars = append(saveVars, moreSaveVars...) 270 } 271 for _, v := range saveVars { 272 // XXX(xiaq): If the variable to save is a elemVariable, save 273 // the outermost variable instead. 274 val := v.Get() 275 saveVals = append(saveVals, val) 276 logger.Printf("saved %s = %s", v, val) 277 } 278 // Do assignment. 279 for _, subop := range op.assignmentOps { 280 err := subop.Exec(fm) 281 if err != nil { 282 return err 283 } 284 } 285 // Defer variable restoration. Will be executed even if an error 286 // occurs when evaling other part of the form. 287 defer func() { 288 for i, v := range saveVars { 289 val := saveVals[i] 290 if val == nil { 291 // XXX Old value is nonexistent. We should delete the 292 // variable. However, since the compiler now doesn't delete 293 // it, we don't delete it in the evaler either. 294 val = "" 295 } 296 err := v.Set(val) 297 if err != nil { 298 errRet = err 299 } 300 logger.Printf("restored %s = %s", v, val) 301 } 302 }() 303 } 304 305 // redirs 306 for _, redirOp := range op.redirOps { 307 err := redirOp.Exec(fm) 308 if err != nil { 309 return err 310 } 311 } 312 313 if op.specialOpBody != nil { 314 return op.specialOpBody.Invoke(fm) 315 } 316 var headFn Callable 317 var args []interface{} 318 if op.headOp.Body != nil { 319 // head 320 headFn = fm.ExecAndUnwrap("head of command", op.headOp).One().Callable() 321 322 // args 323 for _, argOp := range op.argOps { 324 moreArgs, err := argOp.Exec(fm) 325 if err != nil { 326 return err 327 } 328 args = append(args, moreArgs...) 329 } 330 } 331 332 // opts 333 // XXX This conversion should be avoided. 334 optValues, err := op.optsOp.Invoke(fm) 335 if err != nil { 336 return err 337 } 338 opts := optValues[0].(hashmap.Map) 339 convertedOpts := make(map[string]interface{}) 340 for it := opts.Iterator(); it.HasElem(); it.Next() { 341 k, v := it.Elem() 342 if ks, ok := k.(string); ok { 343 convertedOpts[ks] = v 344 } else { 345 return fmt.Errorf("Option key must be string, got %s", vals.Kind(k)) 346 } 347 } 348 349 fm.begin, fm.end = op.begin, op.end 350 351 if headFn != nil { 352 return headFn.Call(fm, args, convertedOpts) 353 } else { 354 return op.spaceyAssignOp.Exec(fm) 355 } 356 } 357 358 func allTrue(vs []interface{}) bool { 359 for _, v := range vs { 360 if !vals.Bool(v) { 361 return false 362 } 363 } 364 return true 365 } 366 367 func (cp *compiler) assignment(n *parse.Assignment) OpBody { 368 variablesOp, restOp := cp.lvaluesOp(n.Left) 369 valuesOp := cp.compoundOp(n.Right) 370 return &assignmentOp{variablesOp, restOp, valuesOp} 371 } 372 373 // ErrMoreThanOneRest is returned when the LHS of an assignment contains more 374 // than one rest variables. 375 var ErrMoreThanOneRest = errors.New("more than one @ lvalue") 376 377 type assignmentOp struct { 378 variablesOp LValuesOp 379 restOp LValuesOp 380 valuesOp ValuesOp 381 } 382 383 func (op *assignmentOp) Invoke(fm *Frame) (errRet error) { 384 variables, err := op.variablesOp.Exec(fm) 385 if err != nil { 386 return err 387 } 388 rest, err := op.restOp.Exec(fm) 389 if err != nil { 390 return err 391 } 392 393 // If any LHS ends up being nil, assign an empty string to all of them. 394 // 395 // This is to fix #176, which only happens in the top level of REPL; in 396 // other cases, a failure in the evaluation of the RHS causes this 397 // level to fail, making the variables unaccessible. 398 // 399 // XXX(xiaq): Should think about how to get rid of this. 400 defer fixNilVariables(variables, &errRet) 401 defer fixNilVariables(rest, &errRet) 402 403 values, err := op.valuesOp.Exec(fm) 404 if err != nil { 405 return err 406 } 407 408 if len(rest) > 1 { 409 return ErrMoreThanOneRest 410 } 411 if len(rest) == 1 { 412 if len(variables) > len(values) { 413 return ErrArityMismatch 414 } 415 } else { 416 if len(variables) != len(values) { 417 return ErrArityMismatch 418 } 419 } 420 421 for i, variable := range variables { 422 err := variable.Set(values[i]) 423 if err != nil { 424 return err 425 } 426 } 427 428 if len(rest) == 1 { 429 err := rest[0].Set(vals.MakeList(values[len(variables):]...)) 430 if err != nil { 431 return err 432 } 433 } 434 return nil 435 } 436 437 func fixNilVariables(vs []vars.Var, perr *error) { 438 for _, v := range vs { 439 if vars.IsBlackhole(v) { 440 continue 441 } 442 if v.Get() == nil { 443 err := v.Set("") 444 *perr = util.Errors(*perr, err) 445 } 446 } 447 } 448 449 func (cp *compiler) literal(n *parse.Primary, msg string) string { 450 switch n.Type { 451 case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted: 452 return n.Value 453 default: 454 cp.compiling(n) 455 cp.errorf(msg) 456 return "" // not reached 457 } 458 } 459 460 const defaultFileRedirPerm = 0644 461 462 // redir compiles a Redir into a op. 463 func (cp *compiler) redir(n *parse.Redir) OpBody { 464 var dstOp ValuesOp 465 if n.Left != nil { 466 dstOp = cp.compoundOp(n.Left) 467 } 468 flag := makeFlag(n.Mode) 469 if flag == -1 { 470 // TODO: Record and get redirection sign position 471 cp.errorf("bad redirection sign") 472 } 473 return &redirOp{dstOp, cp.compoundOp(n.Right), n.RightIsFd, n.Mode, flag} 474 } 475 476 type redirOp struct { 477 dstOp ValuesOp 478 srcOp ValuesOp 479 srcIsFd bool 480 mode parse.RedirMode 481 flag int 482 } 483 484 func (op *redirOp) Invoke(fm *Frame) error { 485 var dst int 486 if op.dstOp.Body == nil { 487 // use default dst fd 488 switch op.mode { 489 case parse.Read: 490 dst = 0 491 case parse.Write, parse.ReadWrite, parse.Append: 492 dst = 1 493 default: 494 panic("bad RedirMode; parser bug") 495 } 496 } else { 497 // dst must be a valid fd 498 dst = fm.ExecAndUnwrap("Fd", op.dstOp).One().NonNegativeInt() 499 } 500 501 fm.growPorts(dst + 1) 502 // Logger.Printf("closing old port %d of %s", dst, ec.context) 503 fm.ports[dst].Close() 504 505 srcUnwrap := fm.ExecAndUnwrap("redirection source", op.srcOp).One() 506 if op.srcIsFd { 507 src := srcUnwrap.FdOrClose() 508 if src == -1 { 509 // close 510 fm.ports[dst] = &Port{} 511 } else { 512 fm.ports[dst] = fm.ports[src].Fork() 513 } 514 } else { 515 switch src := srcUnwrap.Any().(type) { 516 case string: 517 f, err := os.OpenFile(src, op.flag, defaultFileRedirPerm) 518 if err != nil { 519 return fmt.Errorf("failed to open file %s: %s", vals.Repr(src, vals.NoPretty), err) 520 } 521 fm.ports[dst] = &Port{ 522 File: f, Chan: BlackholeChan, 523 CloseFile: true, 524 } 525 case vals.File: 526 fm.ports[dst] = &Port{ 527 File: src.Inner, Chan: BlackholeChan, 528 CloseFile: false, 529 } 530 case vals.Pipe: 531 var f *os.File 532 switch op.mode { 533 case parse.Read: 534 f = src.ReadEnd 535 case parse.Write: 536 f = src.WriteEnd 537 default: 538 return errors.New("can only use < or > with pipes") 539 } 540 fm.ports[dst] = &Port{ 541 File: f, Chan: BlackholeChan, 542 CloseFile: false, 543 } 544 default: 545 srcUnwrap.error("string or file", "%s", vals.Kind(src)) 546 } 547 } 548 return nil 549 } 550 551 type seqOp struct{ subops []Op } 552 553 func (op seqOp) Invoke(fm *Frame) error { 554 for _, subop := range op.subops { 555 err := subop.Exec(fm) 556 if err != nil { 557 return err 558 } 559 } 560 return nil 561 } 562 563 type funcOp func(*Frame) error 564 565 func (op funcOp) Invoke(fm *Frame) error { 566 return op(fm) 567 }