github.com/elves/elvish@v0.15.0/pkg/eval/compile_lvalue.go (about) 1 package eval 2 3 import ( 4 "errors" 5 6 "github.com/elves/elvish/pkg/diag" 7 "github.com/elves/elvish/pkg/eval/errs" 8 "github.com/elves/elvish/pkg/eval/vals" 9 "github.com/elves/elvish/pkg/eval/vars" 10 "github.com/elves/elvish/pkg/parse" 11 ) 12 13 // Parsed group of lvalues. 14 type lvaluesGroup struct { 15 lvalues []lvalue 16 // Index of the rest variable within lvalues. If there is no rest variable, 17 // the index is -1. 18 rest int 19 } 20 21 // Parsed lvalue. 22 type lvalue struct { 23 diag.Ranging 24 ref *varRef 25 indexOps []valuesOp 26 ends []int 27 } 28 29 func (cp *compiler) parseCompoundLValues(ns []*parse.Compound) lvaluesGroup { 30 g := lvaluesGroup{nil, -1} 31 for _, n := range ns { 32 if len(n.Indexings) != 1 { 33 cp.errorpf(n, "lvalue may not be composite expressions") 34 } 35 more := cp.parseIndexingLValue(n.Indexings[0]) 36 if more.rest == -1 { 37 g.lvalues = append(g.lvalues, more.lvalues...) 38 } else if g.rest != -1 { 39 cp.errorpf(n, "at most one rest variable is allowed") 40 } else { 41 g.rest = len(g.lvalues) + more.rest 42 g.lvalues = append(g.lvalues, more.lvalues...) 43 } 44 } 45 return g 46 } 47 48 func (cp *compiler) parseIndexingLValue(n *parse.Indexing) lvaluesGroup { 49 if n.Head.Type == parse.Braced { 50 // Braced list of lvalues may not have indices. 51 if len(n.Indicies) > 0 { 52 cp.errorpf(n, "braced list may not have indices when used as lvalue") 53 } 54 return cp.parseCompoundLValues(n.Head.Braced) 55 } 56 // A basic lvalue. 57 if !parse.ValidLHSVariable(n.Head, true) { 58 cp.errorpf(n.Head, "lvalue must be valid literal variable names") 59 } 60 varUse := n.Head.Value 61 sigil, qname := SplitSigil(varUse) 62 var ref *varRef 63 if len(n.Indicies) == 0 { 64 ref = resolveVarRef(cp, qname, nil) 65 if ref == nil { 66 segs := SplitQNameSegs(qname) 67 if len(segs) == 1 { 68 // Unqualified name - implicit local 69 ref = &varRef{localScope, cp.thisScope().addInner(segs[0]), nil} 70 } else if len(segs) == 2 && (segs[0] == "local:" || segs[0] == ":") { 71 // Qualified local name 72 ref = &varRef{localScope, cp.thisScope().addInner(segs[1]), nil} 73 } else { 74 cp.errorpf(n, "cannot create variable $%s; new variables can only be created in the local scope", qname) 75 } 76 } 77 } else { 78 ref = resolveVarRef(cp, qname, n) 79 if ref == nil { 80 cp.errorpf(n, "cannot find variable $%s", qname) 81 } 82 } 83 ends := make([]int, len(n.Indicies)+1) 84 ends[0] = n.Head.Range().To 85 for i, idx := range n.Indicies { 86 ends[i+1] = idx.Range().To 87 } 88 lv := lvalue{n.Range(), ref, cp.arrayOps(n.Indicies), ends} 89 restIndex := -1 90 if sigil == "@" { 91 restIndex = 0 92 } 93 // TODO: Deal with other sigils when they exist. 94 return lvaluesGroup{[]lvalue{lv}, restIndex} 95 } 96 97 type assignOp struct { 98 diag.Ranging 99 lhs lvaluesGroup 100 rhs valuesOp 101 } 102 103 func (op *assignOp) exec(fm *Frame) Exception { 104 variables := make([]vars.Var, len(op.lhs.lvalues)) 105 for i, lvalue := range op.lhs.lvalues { 106 variable, err := derefLValue(fm, lvalue) 107 if err != nil { 108 return fm.errorp(op, err) 109 } 110 variables[i] = variable 111 } 112 113 values, exc := op.rhs.exec(fm) 114 if exc != nil { 115 return exc 116 } 117 118 if op.lhs.rest == -1 { 119 if len(variables) != len(values) { 120 return fm.errorp(op, errs.ArityMismatch{ 121 What: "assignment right-hand-side", 122 ValidLow: len(variables), ValidHigh: len(variables), Actual: len(values)}) 123 } 124 for i, variable := range variables { 125 err := variable.Set(values[i]) 126 if err != nil { 127 return fm.errorp(op, err) 128 } 129 } 130 } else { 131 if len(values) < len(variables)-1 { 132 return fm.errorp(op, errs.ArityMismatch{ 133 What: "assignment right-hand-side", 134 ValidLow: len(variables) - 1, ValidHigh: -1, Actual: len(values)}) 135 } 136 rest := op.lhs.rest 137 for i := 0; i < rest; i++ { 138 err := variables[i].Set(values[i]) 139 if err != nil { 140 return fm.errorp(op, err) 141 } 142 } 143 restOff := len(values) - len(variables) 144 err := variables[rest].Set(vals.MakeList(values[rest : rest+restOff+1]...)) 145 if err != nil { 146 return fm.errorp(op, err) 147 } 148 for i := rest + 1; i < len(variables); i++ { 149 err := variables[i].Set(values[i+restOff]) 150 if err != nil { 151 return fm.errorp(op, err) 152 } 153 } 154 } 155 return nil 156 } 157 158 // NoSuchVariable returns an error representing that a variable can't be found. 159 func NoSuchVariable(name string) error { 160 return noSuchVariableError{name} 161 } 162 163 type noSuchVariableError struct{ name string } 164 165 func (er noSuchVariableError) Error() string { return "no variable $" + er.name } 166 167 func derefLValue(fm *Frame, lv lvalue) (vars.Var, error) { 168 variable := deref(fm, lv.ref) 169 if variable == nil { 170 return nil, NoSuchVariable(fm.srcMeta.Code[lv.From:lv.To]) 171 } 172 if len(lv.indexOps) == 0 { 173 return variable, nil 174 } 175 indices := make([]interface{}, len(lv.indexOps)) 176 for i, op := range lv.indexOps { 177 values, exc := op.exec(fm) 178 if exc != nil { 179 return nil, exc 180 } 181 // TODO: Implement multi-indexing. 182 if len(values) != 1 { 183 return nil, errors.New("multi indexing not implemented") 184 } 185 indices[i] = values[0] 186 } 187 elemVar, err := vars.MakeElement(variable, indices) 188 if err != nil { 189 level := vars.ElementErrorLevel(err) 190 if level < 0 { 191 return nil, fm.errorp(lv, err) 192 } 193 return nil, fm.errorp(diag.Ranging{From: lv.From, To: lv.ends[level]}, err) 194 } 195 return elemVar, nil 196 }