github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/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/xiaq/persistent/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 i, v := range saveVars { 272 // XXX(xiaq): If the variable to save is a elemVariable, save 273 // the outermost variable instead. 274 if u := vars.HeadOfElement(v); u != nil { 275 v = u 276 saveVars[i] = v 277 } 278 val := v.Get() 279 saveVals = append(saveVals, val) 280 logger.Printf("saved %s = %s", v, val) 281 } 282 // Do assignment. 283 for _, subop := range op.assignmentOps { 284 err := subop.Exec(fm) 285 if err != nil { 286 return err 287 } 288 } 289 // Defer variable restoration. Will be executed even if an error 290 // occurs when evaling other part of the form. 291 defer func() { 292 for i, v := range saveVars { 293 val := saveVals[i] 294 if val == nil { 295 // XXX Old value is nonexistent. We should delete the 296 // variable. However, since the compiler now doesn't delete 297 // it, we don't delete it in the evaler either. 298 val = "" 299 } 300 err := v.Set(val) 301 if err != nil { 302 errRet = err 303 } 304 logger.Printf("restored %s = %s", v, val) 305 } 306 }() 307 } 308 309 // redirs 310 for _, redirOp := range op.redirOps { 311 err := redirOp.Exec(fm) 312 if err != nil { 313 return err 314 } 315 } 316 317 if op.specialOpBody != nil { 318 return op.specialOpBody.Invoke(fm) 319 } 320 var headFn Callable 321 var args []interface{} 322 if op.headOp.Body != nil { 323 // head 324 headFn = fm.ExecAndUnwrap("head of command", op.headOp).One().Callable() 325 326 // args 327 for _, argOp := range op.argOps { 328 moreArgs, err := argOp.Exec(fm) 329 if err != nil { 330 return err 331 } 332 args = append(args, moreArgs...) 333 } 334 } 335 336 // opts 337 // XXX This conversion should be avoided. 338 optValues, err := op.optsOp.Invoke(fm) 339 if err != nil { 340 return err 341 } 342 opts := optValues[0].(hashmap.Map) 343 convertedOpts := make(map[string]interface{}) 344 for it := opts.Iterator(); it.HasElem(); it.Next() { 345 k, v := it.Elem() 346 if ks, ok := k.(string); ok { 347 convertedOpts[ks] = v 348 } else { 349 return fmt.Errorf("Option key must be string, got %s", vals.Kind(k)) 350 } 351 } 352 353 fm.begin, fm.end = op.begin, op.end 354 355 if headFn != nil { 356 return headFn.Call(fm, args, convertedOpts) 357 } else { 358 return op.spaceyAssignOp.Exec(fm) 359 } 360 } 361 362 func allTrue(vs []interface{}) bool { 363 for _, v := range vs { 364 if !vals.Bool(v) { 365 return false 366 } 367 } 368 return true 369 } 370 371 func (cp *compiler) assignment(n *parse.Assignment) OpBody { 372 variablesOp, restOp := cp.lvaluesOp(n.Left) 373 valuesOp := cp.compoundOp(n.Right) 374 return &assignmentOp{variablesOp, restOp, valuesOp} 375 } 376 377 // ErrMoreThanOneRest is returned when the LHS of an assignment contains more 378 // than one rest variables. 379 var ErrMoreThanOneRest = errors.New("more than one @ lvalue") 380 381 type assignmentOp struct { 382 variablesOp LValuesOp 383 restOp LValuesOp 384 valuesOp ValuesOp 385 } 386 387 func (op *assignmentOp) Invoke(fm *Frame) (errRet error) { 388 variables, err := op.variablesOp.Exec(fm) 389 if err != nil { 390 return err 391 } 392 rest, err := op.restOp.Exec(fm) 393 if err != nil { 394 return err 395 } 396 397 // If any LHS ends up being nil, assign an empty string to all of them. 398 // 399 // This is to fix #176, which only happens in the top level of REPL; in 400 // other cases, a failure in the evaluation of the RHS causes this 401 // level to fail, making the variables unaccessible. 402 // 403 // XXX(xiaq): Should think about how to get rid of this. 404 defer fixNilVariables(variables, &errRet) 405 defer fixNilVariables(rest, &errRet) 406 407 values, err := op.valuesOp.Exec(fm) 408 if err != nil { 409 return err 410 } 411 412 if len(rest) > 1 { 413 return ErrMoreThanOneRest 414 } 415 if len(rest) == 1 { 416 if len(variables) > len(values) { 417 return ErrArityMismatch 418 } 419 } else { 420 if len(variables) != len(values) { 421 return ErrArityMismatch 422 } 423 } 424 425 for i, variable := range variables { 426 err := variable.Set(values[i]) 427 if err != nil { 428 return err 429 } 430 } 431 432 if len(rest) == 1 { 433 err := rest[0].Set(vals.MakeList(values[len(variables):]...)) 434 if err != nil { 435 return err 436 } 437 } 438 return nil 439 } 440 441 func fixNilVariables(vs []vars.Var, perr *error) { 442 for _, v := range vs { 443 if vars.IsBlackhole(v) { 444 continue 445 } 446 if v.Get() == nil { 447 err := v.Set("") 448 *perr = util.Errors(*perr, err) 449 } 450 } 451 } 452 453 func (cp *compiler) literal(n *parse.Primary, msg string) string { 454 switch n.Type { 455 case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted: 456 return n.Value 457 default: 458 cp.compiling(n) 459 cp.errorf(msg) 460 return "" // not reached 461 } 462 } 463 464 const defaultFileRedirPerm = 0644 465 466 // redir compiles a Redir into a op. 467 func (cp *compiler) redir(n *parse.Redir) OpBody { 468 var dstOp ValuesOp 469 if n.Left != nil { 470 dstOp = cp.compoundOp(n.Left) 471 } 472 flag := makeFlag(n.Mode) 473 if flag == -1 { 474 // TODO: Record and get redirection sign position 475 cp.errorf("bad redirection sign") 476 } 477 return &redirOp{dstOp, cp.compoundOp(n.Right), n.RightIsFd, n.Mode, flag} 478 } 479 480 type redirOp struct { 481 dstOp ValuesOp 482 srcOp ValuesOp 483 srcIsFd bool 484 mode parse.RedirMode 485 flag int 486 } 487 488 func (op *redirOp) Invoke(fm *Frame) error { 489 var dst int 490 if op.dstOp.Body == nil { 491 // use default dst fd 492 switch op.mode { 493 case parse.Read: 494 dst = 0 495 case parse.Write, parse.ReadWrite, parse.Append: 496 dst = 1 497 default: 498 panic("bad RedirMode; parser bug") 499 } 500 } else { 501 // dst must be a valid fd 502 dst = fm.ExecAndUnwrap("Fd", op.dstOp).One().NonNegativeInt() 503 } 504 505 fm.growPorts(dst + 1) 506 // Logger.Printf("closing old port %d of %s", dst, ec.context) 507 fm.ports[dst].Close() 508 509 srcUnwrap := fm.ExecAndUnwrap("redirection source", op.srcOp).One() 510 if op.srcIsFd { 511 src := srcUnwrap.FdOrClose() 512 if src == -1 { 513 // close 514 fm.ports[dst] = &Port{} 515 } else { 516 fm.ports[dst] = fm.ports[src].Fork() 517 } 518 } else { 519 switch src := srcUnwrap.Any().(type) { 520 case string: 521 f, err := os.OpenFile(src, op.flag, defaultFileRedirPerm) 522 if err != nil { 523 return fmt.Errorf("failed to open file %s: %s", vals.Repr(src, vals.NoPretty), err) 524 } 525 fm.ports[dst] = &Port{ 526 File: f, Chan: BlackholeChan, 527 CloseFile: true, 528 } 529 case vals.File: 530 fm.ports[dst] = &Port{ 531 File: src.Inner, Chan: BlackholeChan, 532 CloseFile: false, 533 } 534 case vals.Pipe: 535 var f *os.File 536 switch op.mode { 537 case parse.Read: 538 f = src.ReadEnd 539 case parse.Write: 540 f = src.WriteEnd 541 default: 542 return errors.New("can only use < or > with pipes") 543 } 544 fm.ports[dst] = &Port{ 545 File: f, Chan: BlackholeChan, 546 CloseFile: false, 547 } 548 default: 549 srcUnwrap.error("string or file", "%s", vals.Kind(src)) 550 } 551 } 552 return nil 553 } 554 555 type seqOp struct{ subops []Op } 556 557 func (op seqOp) Invoke(fm *Frame) error { 558 for _, subop := range op.subops { 559 err := subop.Exec(fm) 560 if err != nil { 561 return err 562 } 563 } 564 return nil 565 } 566 567 type funcOp func(*Frame) error 568 569 func (op funcOp) Invoke(fm *Frame) error { 570 return op(fm) 571 }