github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/purely_eval.go (about)

     1  package eval
     2  
     3  import (
     4  	"strings"
     5  
     6  	"src.elv.sh/pkg/fsutil"
     7  	"src.elv.sh/pkg/parse"
     8  )
     9  
    10  func (ev *Evaler) PurelyEvalCompound(cn *parse.Compound) (string, bool) {
    11  	return ev.PurelyEvalPartialCompound(cn, -1)
    12  }
    13  
    14  func (ev *Evaler) PurelyEvalPartialCompound(cn *parse.Compound, upto int) (string, bool) {
    15  	tilde := false
    16  	head := ""
    17  	for _, in := range cn.Indexings {
    18  		if len(in.Indicies) > 0 {
    19  			return "", false
    20  		}
    21  		if upto >= 0 && in.To > upto {
    22  			break
    23  		}
    24  		switch in.Head.Type {
    25  		case parse.Tilde:
    26  			tilde = true
    27  		case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
    28  			head += in.Head.Value
    29  		case parse.Variable:
    30  			if ev == nil {
    31  				return "", false
    32  			}
    33  			v := ev.PurelyEvalPrimary(in.Head)
    34  			if s, ok := v.(string); ok {
    35  				head += s
    36  			} else {
    37  				return "", false
    38  			}
    39  		default:
    40  			return "", false
    41  		}
    42  	}
    43  	if tilde {
    44  		i := strings.Index(head, "/")
    45  		if i == -1 {
    46  			i = len(head)
    47  		}
    48  		uname := head[:i]
    49  		home, err := fsutil.GetHome(uname)
    50  		if err != nil {
    51  			return "", false
    52  		}
    53  		head = home + head[i:]
    54  	}
    55  	return head, true
    56  }
    57  
    58  // PurelyEvalPrimary evaluates a primary node without causing any side effects.
    59  // If this cannot be done, it returns nil.
    60  //
    61  // Currently, only string literals and variables with no @ can be evaluated.
    62  func (ev *Evaler) PurelyEvalPrimary(pn *parse.Primary) interface{} {
    63  	switch pn.Type {
    64  	case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
    65  		return pn.Value
    66  	case parse.Variable:
    67  		sigil, qname := SplitSigil(pn.Value)
    68  		if sigil != "" {
    69  			return nil
    70  		}
    71  		fm := &Frame{Evaler: ev, local: ev.Global(), up: new(Ns)}
    72  		ref := resolveVarRef(fm, qname, nil)
    73  		if ref != nil {
    74  			variable := deref(fm, ref)
    75  			if variable == nil {
    76  				return nil
    77  			}
    78  			return variable.Get()
    79  		}
    80  	}
    81  	return nil
    82  }