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

     1  package eval
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/u-root/u-root/cmds/core/elvish/eval/vars"
     8  	"github.com/u-root/u-root/cmds/core/elvish/parse"
     9  )
    10  
    11  // LValuesOp is an operation on an Frame that produce Variable's.
    12  type LValuesOp struct {
    13  	Body       LValuesOpBody
    14  	Begin, End int
    15  }
    16  
    17  // LValuesOpBody is the body of an LValuesOp.
    18  type LValuesOpBody interface {
    19  	Invoke(*Frame) ([]vars.Var, error)
    20  }
    21  
    22  // Exec executes an LValuesOp, producing Variable's.
    23  func (op LValuesOp) Exec(fm *Frame) ([]vars.Var, error) {
    24  	// Empty value is considered to generate no lvalues.
    25  	if op.Body == nil {
    26  		return []vars.Var{}, nil
    27  	}
    28  	fm.begin, fm.end = op.Begin, op.End
    29  	return op.Body.Invoke(fm)
    30  }
    31  
    32  // lvaluesOp compiles lvalues, returning the fixed part and, optionally a rest
    33  // part.
    34  //
    35  // In the AST an lvalue is either an Indexing node where the head is a string
    36  // literal, or a braced list of such Indexing nodes. The last Indexing node may
    37  // be prefixed by @, in which case they become the rest part. For instance, in
    38  // {a[x],b,@c[z]}, "a[x],b" is the fixed part and "c[z]" is the rest part.
    39  func (cp *compiler) lvaluesOp(n *parse.Indexing) (LValuesOp, LValuesOp) {
    40  	if n.Head.Type == parse.Braced {
    41  		// Braced list of variable specs, possibly with indicies.
    42  		if len(n.Indicies) > 0 {
    43  			cp.errorf("may not have indicies")
    44  		}
    45  		return cp.lvaluesMulti(n.Head.Braced)
    46  	}
    47  	rest, opFunc := cp.lvalueBase(n, "must be an lvalue or a braced list of those")
    48  	op := LValuesOp{opFunc, n.Begin(), n.End()}
    49  	if rest {
    50  		return LValuesOp{}, op
    51  	}
    52  	return op, LValuesOp{}
    53  }
    54  
    55  func (cp *compiler) lvaluesMulti(nodes []*parse.Compound) (LValuesOp, LValuesOp) {
    56  	opFuncs := make([]LValuesOpBody, len(nodes))
    57  	var restNode *parse.Indexing
    58  	var restOpFunc LValuesOpBody
    59  
    60  	// Compile each spec inside the brace.
    61  	fixedEnd := 0
    62  	for i, cn := range nodes {
    63  		if len(cn.Indexings) != 1 {
    64  			cp.errorpf(cn.Begin(), cn.End(), "must be an lvalue")
    65  		}
    66  		var rest bool
    67  		rest, opFuncs[i] = cp.lvalueBase(cn.Indexings[0], "must be an lvalue ")
    68  		// Only the last one may a rest part.
    69  		if rest {
    70  			if i == len(nodes)-1 {
    71  				restNode = cn.Indexings[0]
    72  				restOpFunc = opFuncs[i]
    73  			} else {
    74  				cp.errorpf(cn.Begin(), cn.End(), "only the last lvalue may have @")
    75  			}
    76  		} else {
    77  			fixedEnd = cn.End()
    78  		}
    79  	}
    80  
    81  	var restOp LValuesOp
    82  	// If there is a rest part, make LValuesOp for it and remove it from opFuncs.
    83  	if restOpFunc != nil {
    84  		restOp = LValuesOp{restOpFunc, restNode.Begin(), restNode.End()}
    85  		opFuncs = opFuncs[:len(opFuncs)-1]
    86  	}
    87  
    88  	var op LValuesOp
    89  	// If there is still anything left in opFuncs, make LValuesOp for the fixed part.
    90  	if len(opFuncs) > 0 {
    91  		op = LValuesOp{seqLValuesOpBody{opFuncs}, nodes[0].Begin(), fixedEnd}
    92  	}
    93  
    94  	return op, restOp
    95  }
    96  
    97  func (cp *compiler) lvalueBase(n *parse.Indexing, msg string) (bool, LValuesOpBody) {
    98  	qname := cp.literal(n.Head, msg)
    99  	explode, ns, name := ParseVariableRef(qname)
   100  	if len(n.Indicies) == 0 {
   101  		cp.registerVariableSet(ns, name)
   102  		return explode, varOp{ns, name}
   103  	}
   104  	return explode, cp.lvalueElement(ns, name, n)
   105  }
   106  
   107  func (cp *compiler) lvalueElement(ns, name string, n *parse.Indexing) LValuesOpBody {
   108  	cp.registerVariableGet(ns, name)
   109  
   110  	begin, end := n.Begin(), n.End()
   111  	ends := make([]int, len(n.Indicies)+1)
   112  	ends[0] = n.Head.End()
   113  	for i, idx := range n.Indicies {
   114  		ends[i+1] = idx.End()
   115  	}
   116  
   117  	indexOps := cp.arrayOps(n.Indicies)
   118  
   119  	return &elemOp{ns, name, indexOps, begin, end, ends}
   120  }
   121  
   122  type seqLValuesOpBody struct {
   123  	ops []LValuesOpBody
   124  }
   125  
   126  func (op seqLValuesOpBody) Invoke(fm *Frame) ([]vars.Var, error) {
   127  	var variables []vars.Var
   128  	for _, op := range op.ops {
   129  		moreVariables, err := op.Invoke(fm)
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  		variables = append(variables, moreVariables...)
   134  	}
   135  	return variables, nil
   136  }
   137  
   138  type varOp struct {
   139  	ns, name string
   140  }
   141  
   142  func (op varOp) Invoke(fm *Frame) ([]vars.Var, error) {
   143  	variable := fm.ResolveVar(op.ns, op.name)
   144  	if variable == nil {
   145  		if op.ns == "" || op.ns == "local" {
   146  			// New variable.
   147  			// XXX We depend on the fact that this variable will
   148  			// immeidately be set.
   149  			if strings.HasSuffix(op.name, FnSuffix) {
   150  				val := Callable(nil)
   151  				variable = vars.FromPtr(&val)
   152  			} else if strings.HasSuffix(op.name, NsSuffix) {
   153  				val := Ns(nil)
   154  				variable = vars.FromPtr(&val)
   155  			} else {
   156  				variable = vars.NewAnyWithInit(nil)
   157  			}
   158  			fm.local[op.name] = variable
   159  		} else {
   160  			return nil, fmt.Errorf("new variables can only be created in local scope")
   161  		}
   162  	}
   163  	return []vars.Var{variable}, nil
   164  }
   165  
   166  type elemOp struct {
   167  	ns       string
   168  	name     string
   169  	indexOps []ValuesOp
   170  	begin    int
   171  	end      int
   172  	ends     []int
   173  }
   174  
   175  func (op *elemOp) Invoke(fm *Frame) ([]vars.Var, error) {
   176  	return nil, nil
   177  }