github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/compile_lvalue.go (about)

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