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 }