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

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