src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/compile_value.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"src.elv.sh/pkg/diag"
     9  	"src.elv.sh/pkg/eval/errs"
    10  	"src.elv.sh/pkg/eval/vals"
    11  	"src.elv.sh/pkg/eval/vars"
    12  	"src.elv.sh/pkg/fsutil"
    13  	"src.elv.sh/pkg/glob"
    14  	"src.elv.sh/pkg/parse"
    15  )
    16  
    17  // An operation that produces values.
    18  type valuesOp interface {
    19  	diag.Ranger
    20  	exec(*Frame) ([]any, Exception)
    21  }
    22  
    23  var outputCaptureBufferSize = 16
    24  
    25  // Can be mutated for testing.
    26  var getHome = fsutil.GetHome
    27  
    28  func (cp *compiler) compoundOp(n *parse.Compound) valuesOp {
    29  	if len(n.Indexings) == 0 {
    30  		return literalValues(n, "")
    31  	}
    32  
    33  	tilde := false
    34  	indexings := n.Indexings
    35  
    36  	if n.Indexings[0].Head.Type == parse.Tilde {
    37  		// A lone ~.
    38  		if len(n.Indexings) == 1 {
    39  			return loneTildeOp{n.Range()}
    40  		}
    41  		tilde = true
    42  		indexings = indexings[1:]
    43  	}
    44  
    45  	return compoundOp{n.Range(), tilde, cp.indexingOps(indexings)}
    46  }
    47  
    48  type loneTildeOp struct{ diag.Ranging }
    49  
    50  func (op loneTildeOp) exec(fm *Frame) ([]any, Exception) {
    51  	home, err := getHome("")
    52  	if err != nil {
    53  		return nil, fm.errorp(op, err)
    54  	}
    55  	return []any{home}, nil
    56  }
    57  
    58  func (cp *compiler) compoundOps(ns []*parse.Compound) []valuesOp {
    59  	ops := make([]valuesOp, len(ns))
    60  	for i, n := range ns {
    61  		ops[i] = cp.compoundOp(n)
    62  	}
    63  	return ops
    64  }
    65  
    66  type compoundOp struct {
    67  	diag.Ranging
    68  	tilde  bool
    69  	subops []valuesOp
    70  }
    71  
    72  func (op compoundOp) exec(fm *Frame) ([]any, Exception) {
    73  	// Accumulator.
    74  	vs, exc := op.subops[0].exec(fm)
    75  	if exc != nil {
    76  		return nil, exc
    77  	}
    78  
    79  	for _, subop := range op.subops[1:] {
    80  		us, exc := subop.exec(fm)
    81  		if exc != nil {
    82  			return nil, exc
    83  		}
    84  		var err error
    85  		vs, err = outerProduct(vs, us, vals.Concat)
    86  		if err != nil {
    87  			return nil, fm.errorp(op, err)
    88  		}
    89  	}
    90  	if op.tilde {
    91  		newvs := make([]any, len(vs))
    92  		for i, v := range vs {
    93  			tilded, err := doTilde(v)
    94  			if err != nil {
    95  				return nil, fm.errorp(op, err)
    96  			}
    97  			newvs[i] = tilded
    98  		}
    99  		vs = newvs
   100  	}
   101  	hasGlob := false
   102  	for _, v := range vs {
   103  		if _, ok := v.(globPattern); ok {
   104  			hasGlob = true
   105  			break
   106  		}
   107  	}
   108  	if hasGlob {
   109  		newvs := make([]any, 0, len(vs))
   110  		for _, v := range vs {
   111  			if gp, ok := v.(globPattern); ok {
   112  				results, err := doGlob(fm.Context(), gp)
   113  				if err != nil {
   114  					return nil, fm.errorp(op, err)
   115  				}
   116  				newvs = append(newvs, results...)
   117  			} else {
   118  				newvs = append(newvs, v)
   119  			}
   120  		}
   121  		vs = newvs
   122  	}
   123  	return vs, nil
   124  }
   125  
   126  func outerProduct(vs []any, us []any, f func(any, any) (any, error)) ([]any, error) {
   127  	ws := make([]any, len(vs)*len(us))
   128  	nu := len(us)
   129  	for i, v := range vs {
   130  		for j, u := range us {
   131  			var err error
   132  			ws[i*nu+j], err = f(v, u)
   133  			if err != nil {
   134  				return nil, err
   135  			}
   136  		}
   137  	}
   138  	return ws, nil
   139  }
   140  
   141  // Errors thrown when globbing.
   142  var (
   143  	ErrBadglobPattern          = errors.New("bad globPattern; elvish bug")
   144  	ErrCannotDetermineUsername = errors.New("cannot determine user name from glob pattern")
   145  )
   146  
   147  func doTilde(v any) (any, error) {
   148  	switch v := v.(type) {
   149  	case string:
   150  		s := v
   151  		// TODO: Make this correct on Windows.
   152  		i := strings.Index(s, "/")
   153  		var uname, rest string
   154  		if i == -1 {
   155  			uname = s
   156  		} else {
   157  			uname = s[:i]
   158  			rest = s[i:]
   159  		}
   160  		dir, err := getHome(uname)
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  		return dir + rest, nil
   165  	case globPattern:
   166  		if len(v.Segments) == 0 {
   167  			return nil, ErrBadglobPattern
   168  		}
   169  		switch seg := v.Segments[0].(type) {
   170  		case glob.Literal:
   171  			if len(v.Segments) == 1 {
   172  				return nil, ErrBadglobPattern
   173  			}
   174  			_, isSlash := v.Segments[1].(glob.Slash)
   175  			if isSlash {
   176  				// ~username/xxx. Replace the first segment with the home
   177  				// directory of the specified user.
   178  				dir, err := getHome(seg.Data)
   179  				if err != nil {
   180  					return nil, err
   181  				}
   182  				v.Segments[0] = glob.Literal{Data: dir}
   183  				return v, nil
   184  			}
   185  		case glob.Slash:
   186  			dir, err := getHome("")
   187  			if err != nil {
   188  				return nil, err
   189  			}
   190  			v.DirOverride = dir
   191  			return v, nil
   192  		}
   193  		return nil, ErrCannotDetermineUsername
   194  	default:
   195  		return nil, fmt.Errorf("tilde doesn't work on value of type %s", vals.Kind(v))
   196  	}
   197  }
   198  
   199  func (cp *compiler) arrayOp(n *parse.Array) valuesOp {
   200  	return seqValuesOp{n.Range(), cp.compoundOps(n.Compounds)}
   201  }
   202  
   203  func (cp *compiler) arrayOps(ns []*parse.Array) []valuesOp {
   204  	ops := make([]valuesOp, len(ns))
   205  	for i, n := range ns {
   206  		ops[i] = cp.arrayOp(n)
   207  	}
   208  	return ops
   209  }
   210  
   211  func (cp *compiler) indexingOp(n *parse.Indexing) valuesOp {
   212  	if len(n.Indices) == 0 {
   213  		return cp.primaryOp(n.Head)
   214  	}
   215  	return &indexingOp{n.Range(), cp.primaryOp(n.Head), cp.arrayOps(n.Indices)}
   216  }
   217  
   218  func (cp *compiler) indexingOps(ns []*parse.Indexing) []valuesOp {
   219  	ops := make([]valuesOp, len(ns))
   220  	for i, n := range ns {
   221  		ops[i] = cp.indexingOp(n)
   222  	}
   223  	return ops
   224  }
   225  
   226  type indexingOp struct {
   227  	diag.Ranging
   228  	headOp   valuesOp
   229  	indexOps []valuesOp
   230  }
   231  
   232  func (op *indexingOp) exec(fm *Frame) ([]any, Exception) {
   233  	vs, exc := op.headOp.exec(fm)
   234  	if exc != nil {
   235  		return nil, exc
   236  	}
   237  	for _, indexOp := range op.indexOps {
   238  		indices, exc := indexOp.exec(fm)
   239  		if exc != nil {
   240  			return nil, exc
   241  		}
   242  		newvs := make([]any, 0, len(vs)*len(indices))
   243  		for _, v := range vs {
   244  			for _, index := range indices {
   245  				result, err := vals.Index(v, index)
   246  				if err != nil {
   247  					return nil, fm.errorp(op, err)
   248  				}
   249  				newvs = append(newvs, result)
   250  			}
   251  		}
   252  		vs = newvs
   253  	}
   254  	return vs, nil
   255  }
   256  
   257  func (cp *compiler) primaryOp(n *parse.Primary) valuesOp {
   258  	switch n.Type {
   259  	case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
   260  		return literalValues(n, n.Value)
   261  	case parse.Variable:
   262  		sigil, qname := SplitSigil(n.Value)
   263  		ref := resolveVarRef(cp, qname, n)
   264  		if ref == nil {
   265  			cp.autofixUnresolvedVar(qname)
   266  			cp.errorpf(n, "variable $%s not found", parse.Quote(qname))
   267  		}
   268  		return &variableOp{n.Range(), sigil != "", qname, ref}
   269  	case parse.Wildcard:
   270  		seg, err := wildcardToSegment(parse.SourceText(n))
   271  		if err != nil {
   272  			cp.errorpf(n, "%s", err)
   273  		}
   274  		vs := []any{
   275  			globPattern{Pattern: glob.Pattern{Segments: []glob.Segment{seg}, DirOverride: ""},
   276  				Flags: 0, Buts: nil, TypeCb: nil}}
   277  		return literalValues(n, vs...)
   278  	case parse.Tilde:
   279  		cp.errorpf(n, "compiler bug: Tilde not handled in .compound")
   280  		return literalValues(n, "~")
   281  	case parse.ExceptionCapture:
   282  		return exceptionCaptureOp{n.Range(), cp.chunkOp(n.Chunk)}
   283  	case parse.OutputCapture:
   284  		return outputCaptureOp{n.Range(), cp.chunkOp(n.Chunk)}
   285  	case parse.List:
   286  		return listOp{n.Range(), cp.compoundOps(n.Elements)}
   287  	case parse.Lambda:
   288  		return cp.lambda(n)
   289  	case parse.Map:
   290  		return mapOp{n.Range(), cp.mapPairs(n.MapPairs)}
   291  	case parse.Braced:
   292  		return seqValuesOp{n.Range(), cp.compoundOps(n.Braced)}
   293  	default:
   294  		cp.errorpf(n, "bad PrimaryType; parser bug")
   295  		return literalValues(n, parse.SourceText(n))
   296  	}
   297  }
   298  
   299  func (cp *compiler) primaryOps(ns []*parse.Primary) []valuesOp {
   300  	ops := make([]valuesOp, len(ns))
   301  	for i, n := range ns {
   302  		ops[i] = cp.primaryOp(n)
   303  	}
   304  	return ops
   305  }
   306  
   307  type variableOp struct {
   308  	diag.Ranging
   309  	explode bool
   310  	qname   string
   311  	ref     *varRef
   312  }
   313  
   314  func (op variableOp) exec(fm *Frame) ([]any, Exception) {
   315  	variable := deref(fm, op.ref)
   316  	if variable == nil {
   317  		return nil, fm.errorpf(op, "variable $%s not found", parse.Quote(op.qname))
   318  	}
   319  	value := variable.Get()
   320  	if op.explode {
   321  		vs, err := vals.Collect(value)
   322  		return vs, fm.errorp(op, err)
   323  	}
   324  	return []any{value}, nil
   325  }
   326  
   327  type listOp struct {
   328  	diag.Ranging
   329  	subops []valuesOp
   330  }
   331  
   332  func (op listOp) exec(fm *Frame) ([]any, Exception) {
   333  	list := vals.EmptyList
   334  	for _, subop := range op.subops {
   335  		moreValues, exc := subop.exec(fm)
   336  		if exc != nil {
   337  			return nil, exc
   338  		}
   339  		for _, moreValue := range moreValues {
   340  			list = list.Conj(moreValue)
   341  		}
   342  	}
   343  	return []any{list}, nil
   344  }
   345  
   346  type exceptionCaptureOp struct {
   347  	diag.Ranging
   348  	subop effectOp
   349  }
   350  
   351  func (op exceptionCaptureOp) exec(fm *Frame) ([]any, Exception) {
   352  	exc := op.subop.exec(fm)
   353  	if exc == nil {
   354  		return []any{OK}, nil
   355  	}
   356  	return []any{exc}, nil
   357  }
   358  
   359  type outputCaptureOp struct {
   360  	diag.Ranging
   361  	subop effectOp
   362  }
   363  
   364  func (op outputCaptureOp) exec(fm *Frame) ([]any, Exception) {
   365  	outPort, collect, err := ValueCapturePort()
   366  	if err != nil {
   367  		return nil, fm.errorp(op, err)
   368  	}
   369  	exc := op.subop.exec(fm.forkWithOutput("[output capture]", outPort))
   370  	return collect(), exc
   371  }
   372  
   373  func (cp *compiler) lambda(n *parse.Primary) valuesOp {
   374  	// Parse signature.
   375  	var (
   376  		argNames      []string
   377  		restArg       int = -1
   378  		optNames      []string
   379  		optDefaultOps []valuesOp
   380  	)
   381  	if len(n.Elements) > 0 {
   382  		// Argument list.
   383  		argNames = make([]string, len(n.Elements))
   384  		seenName := make(map[string]bool)
   385  		for i, arg := range n.Elements {
   386  			ref := stringLiteralOrError(cp, arg, "argument name")
   387  			sigil, qname := SplitSigil(ref)
   388  			name, rest := SplitQName(qname)
   389  			if rest != "" {
   390  				cp.errorpf(arg, "argument name must be unqualified")
   391  			}
   392  			if name == "" {
   393  				cp.errorpf(arg, "argument name must not be empty")
   394  			}
   395  			if sigil == "@" {
   396  				if restArg != -1 {
   397  					cp.errorpf(arg, "only one argument may have @ prefix")
   398  				}
   399  				restArg = i
   400  			}
   401  			if name != "_" {
   402  				if seenName[name] {
   403  					cp.errorpf(arg, "duplicate argument name '%s'", name)
   404  				} else {
   405  					seenName[name] = true
   406  				}
   407  			}
   408  			argNames[i] = name
   409  		}
   410  	}
   411  	if len(n.MapPairs) > 0 {
   412  		optNames = make([]string, len(n.MapPairs))
   413  		optDefaultOps = make([]valuesOp, len(n.MapPairs))
   414  		for i, opt := range n.MapPairs {
   415  			qname := stringLiteralOrError(cp, opt.Key, "option name")
   416  			name, rest := SplitQName(qname)
   417  			if rest != "" {
   418  				cp.errorpf(opt.Key, "option name must be unqualified")
   419  			}
   420  			if name == "" {
   421  				cp.errorpf(opt.Key, "option name must not be empty")
   422  			}
   423  			optNames[i] = name
   424  			if opt.Value == nil {
   425  				cp.errorpf(opt.Key, "option must have default value")
   426  			} else {
   427  				optDefaultOps[i] = cp.compoundOp(opt.Value)
   428  			}
   429  		}
   430  	}
   431  
   432  	local, capture := cp.pushScope()
   433  	for _, argName := range argNames {
   434  		local.add(argName)
   435  	}
   436  	for _, optName := range optNames {
   437  		local.add(optName)
   438  	}
   439  	scopeSizeInit := len(local.infos)
   440  	chunkOp := cp.chunkOp(n.Chunk)
   441  	newLocal := local.infos[scopeSizeInit:]
   442  	cp.popScope()
   443  
   444  	return &lambdaOp{n.Range(), argNames, restArg, optNames, optDefaultOps, newLocal, capture, chunkOp, cp.srcMeta}
   445  }
   446  
   447  type lambdaOp struct {
   448  	diag.Ranging
   449  	argNames      []string
   450  	restArg       int
   451  	optNames      []string
   452  	optDefaultOps []valuesOp
   453  	newLocal      []staticVarInfo
   454  	capture       *staticUpNs
   455  	subop         effectOp
   456  	srcMeta       parse.Source
   457  }
   458  
   459  func (op *lambdaOp) exec(fm *Frame) ([]any, Exception) {
   460  	capture := &Ns{
   461  		make([]vars.Var, len(op.capture.infos)),
   462  		make([]staticVarInfo, len(op.capture.infos))}
   463  	for i, info := range op.capture.infos {
   464  		if info.local {
   465  			capture.slots[i] = fm.local.slots[info.index]
   466  			capture.infos[i] = fm.local.infos[info.index]
   467  		} else {
   468  			capture.slots[i] = fm.up.slots[info.index]
   469  			capture.infos[i] = fm.up.infos[info.index]
   470  		}
   471  	}
   472  	optDefaults := make([]any, len(op.optDefaultOps))
   473  	for i, op := range op.optDefaultOps {
   474  		defaultValue, err := evalForValue(fm, op, "option default value")
   475  		if err != nil {
   476  			return nil, err
   477  		}
   478  		optDefaults[i] = defaultValue
   479  	}
   480  	return []any{&Closure{op.argNames, op.restArg, op.optNames, optDefaults, op.srcMeta, op.Range(), op.subop, op.newLocal, capture}}, nil
   481  }
   482  
   483  type mapOp struct {
   484  	diag.Ranging
   485  	pairsOp *mapPairsOp
   486  }
   487  
   488  func (op mapOp) exec(fm *Frame) ([]any, Exception) {
   489  	m := vals.EmptyMap
   490  	exc := op.pairsOp.exec(fm, func(k, v any) Exception {
   491  		m = m.Assoc(k, v)
   492  		return nil
   493  	})
   494  	if exc != nil {
   495  		return nil, exc
   496  	}
   497  	return []any{m}, nil
   498  }
   499  
   500  func (cp *compiler) mapPairs(pairs []*parse.MapPair) *mapPairsOp {
   501  	npairs := len(pairs)
   502  	keysOps := make([]valuesOp, npairs)
   503  	valuesOps := make([]valuesOp, npairs)
   504  	begins, ends := make([]int, npairs), make([]int, npairs)
   505  	for i, pair := range pairs {
   506  		keysOps[i] = cp.compoundOp(pair.Key)
   507  		if pair.Value == nil {
   508  			p := pair.Range().To
   509  			valuesOps[i] = literalValues(diag.PointRanging(p), true)
   510  		} else {
   511  			valuesOps[i] = cp.compoundOp(pairs[i].Value)
   512  		}
   513  		begins[i], ends[i] = pair.Range().From, pair.Range().To
   514  	}
   515  	return &mapPairsOp{keysOps, valuesOps, begins, ends}
   516  }
   517  
   518  type mapPairsOp struct {
   519  	keysOps   []valuesOp
   520  	valuesOps []valuesOp
   521  	begins    []int
   522  	ends      []int
   523  }
   524  
   525  func (op *mapPairsOp) exec(fm *Frame, f func(k, v any) Exception) Exception {
   526  	for i := range op.keysOps {
   527  		keys, exc := op.keysOps[i].exec(fm)
   528  		if exc != nil {
   529  			return exc
   530  		}
   531  		values, exc := op.valuesOps[i].exec(fm)
   532  		if exc != nil {
   533  			return exc
   534  		}
   535  		if len(keys) != len(values) {
   536  			return fm.errorpf(diag.Ranging{From: op.begins[i], To: op.ends[i]},
   537  				"%d keys but %d values", len(keys), len(values))
   538  		}
   539  		for j, key := range keys {
   540  			err := f(key, values[j])
   541  			if err != nil {
   542  				return err
   543  			}
   544  		}
   545  	}
   546  	return nil
   547  }
   548  
   549  type literalValuesOp struct {
   550  	diag.Ranging
   551  	values []any
   552  }
   553  
   554  func (op literalValuesOp) exec(*Frame) ([]any, Exception) {
   555  	return op.values, nil
   556  }
   557  
   558  func literalValues(r diag.Ranger, vs ...any) valuesOp {
   559  	return literalValuesOp{r.Range(), vs}
   560  }
   561  
   562  type seqValuesOp struct {
   563  	diag.Ranging
   564  	subops []valuesOp
   565  }
   566  
   567  func (op seqValuesOp) exec(fm *Frame) ([]any, Exception) {
   568  	var values []any
   569  	for _, subop := range op.subops {
   570  		moreValues, exc := subop.exec(fm)
   571  		if exc != nil {
   572  			return nil, exc
   573  		}
   574  		values = append(values, moreValues...)
   575  	}
   576  	return values, nil
   577  }
   578  
   579  type nopValuesOp struct{ diag.Ranging }
   580  
   581  func (nopValuesOp) exec(fm *Frame) ([]any, Exception) { return nil, nil }
   582  
   583  func evalForValue(fm *Frame, op valuesOp, what string) (any, Exception) {
   584  	values, exc := op.exec(fm)
   585  	if exc != nil {
   586  		return nil, exc
   587  	}
   588  	if len(values) != 1 {
   589  		return nil, fm.errorp(op, errs.ArityMismatch{What: what,
   590  			ValidLow: 1, ValidHigh: 1, Actual: len(values)})
   591  	}
   592  	return values[0], nil
   593  }