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 }