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  }