github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/purely_eval.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  
     7  	"github.com/u-root/u-root/cmds/core/elvish/parse"
     8  	"github.com/u-root/u-root/cmds/core/elvish/util"
     9  )
    10  
    11  var ErrImpure = errors.New("expression is impure")
    12  
    13  func PurelyEvalCompound(cn *parse.Compound) (string, error) {
    14  	return (*Evaler)(nil).PurelyEvalCompound(cn)
    15  }
    16  
    17  func (ev *Evaler) PurelyEvalCompound(cn *parse.Compound) (string, error) {
    18  	return ev.PurelyEvalPartialCompound(cn, nil)
    19  }
    20  
    21  func (ev *Evaler) PurelyEvalPartialCompound(cn *parse.Compound, upto *parse.Indexing) (string, error) {
    22  	tilde := false
    23  	head := ""
    24  	for _, in := range cn.Indexings {
    25  		if len(in.Indicies) > 0 {
    26  			return "", ErrImpure
    27  		}
    28  		switch in.Head.Type {
    29  		case parse.Tilde:
    30  			tilde = true
    31  		case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
    32  			head += in.Head.Value
    33  		case parse.Variable:
    34  			if ev == nil {
    35  				return "", ErrImpure
    36  			}
    37  			v := ev.PurelyEvalPrimary(in.Head)
    38  			if s, ok := v.(string); ok {
    39  				head += s
    40  			} else {
    41  				return "", ErrImpure
    42  			}
    43  		default:
    44  			return "", ErrImpure
    45  		}
    46  
    47  		if in == upto {
    48  			break
    49  		}
    50  	}
    51  	if tilde {
    52  		i := strings.Index(head, "/")
    53  		if i == -1 {
    54  			i = len(head)
    55  		}
    56  		uname := head[:i]
    57  		home, err := util.GetHome(uname)
    58  		if err != nil {
    59  			return "", err
    60  		}
    61  		head = home + head[i:]
    62  	}
    63  	return head, nil
    64  }
    65  
    66  // PurelyEvalPrimary evaluates a primary node without causing any side effects.
    67  // If this cannot be done, it returns nil.
    68  //
    69  // Currently, only string literals and variables with no @ can be evaluated.
    70  func (ev *Evaler) PurelyEvalPrimary(pn *parse.Primary) interface{} {
    71  	switch pn.Type {
    72  	case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
    73  		return pn.Value
    74  	case parse.Variable:
    75  		explode, ns, name := ParseVariableRef(pn.Value)
    76  		if explode {
    77  			return nil
    78  		}
    79  		ec := NewTopFrame(ev, NewInternalSource("[purely eval]"), nil)
    80  		variable := ec.ResolveVar(ns, name)
    81  		if variable != nil {
    82  			return variable.Get()
    83  		}
    84  	}
    85  	return nil
    86  }