src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/compile_lvalue.go (about) 1 package eval 2 3 import ( 4 "errors" 5 6 "src.elv.sh/pkg/diag" 7 "src.elv.sh/pkg/eval/errs" 8 "src.elv.sh/pkg/eval/vals" 9 "src.elv.sh/pkg/eval/vars" 10 "src.elv.sh/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 type lvalueFlag uint 30 31 const ( 32 setLValue lvalueFlag = 1 << iota 33 newLValue 34 ) 35 36 func (cp *compiler) parseCompoundLValues(ns []*parse.Compound, f lvalueFlag) lvaluesGroup { 37 g := lvaluesGroup{nil, -1} 38 for _, n := range ns { 39 if len(n.Indexings) != 1 { 40 cp.errorpf(n, "lvalue may not be composite expressions") 41 break 42 } 43 more := cp.parseIndexingLValue(n.Indexings[0], f) 44 if more.rest == -1 { 45 g.lvalues = append(g.lvalues, more.lvalues...) 46 } else if g.rest != -1 { 47 cp.errorpf(n, "at most one rest variable is allowed") 48 } else { 49 g.rest = len(g.lvalues) + more.rest 50 g.lvalues = append(g.lvalues, more.lvalues...) 51 } 52 } 53 return g 54 } 55 56 var dummyLValuesGroup = lvaluesGroup{[]lvalue{{}}, -1} 57 58 func (cp *compiler) parseIndexingLValue(n *parse.Indexing, f lvalueFlag) lvaluesGroup { 59 if n.Head.Type == parse.Braced { 60 // Braced list of lvalues may not have indices. 61 if len(n.Indices) > 0 { 62 cp.errorpf(n, "braced list may not have indices when used as lvalue") 63 return dummyLValuesGroup 64 } 65 return cp.parseCompoundLValues(n.Head.Braced, f) 66 } 67 // A basic lvalue. 68 if !parse.ValidLHSVariable(n.Head, true) { 69 cp.errorpf(n.Head, "lvalue must be valid literal variable names") 70 return dummyLValuesGroup 71 } 72 varUse := n.Head.Value 73 sigil, qname := SplitSigil(varUse) 74 if qname == "" { 75 cp.errorpf(n, "variable name must not be empty") 76 return dummyLValuesGroup 77 } 78 79 var ref *varRef 80 if f&setLValue != 0 { 81 ref = resolveVarRef(cp, qname, n) 82 if ref != nil && len(ref.subNames) == 0 && ref.info.readOnly { 83 cp.errorpf(n, "variable $%s is read-only", parse.Quote(qname)) 84 return dummyLValuesGroup 85 } 86 } 87 if ref == nil { 88 if f&newLValue == 0 { 89 cp.autofixUnresolvedVar(qname) 90 cp.errorpf(n, "cannot find variable $%s", parse.Quote(qname)) 91 return dummyLValuesGroup 92 } 93 if len(n.Indices) > 0 { 94 cp.errorpf(n, "new variable $%s must not have indices", parse.Quote(qname)) 95 return dummyLValuesGroup 96 } 97 segs := SplitQNameSegs(qname) 98 if len(segs) == 1 { 99 // Unqualified name - implicit local 100 name := segs[0] 101 ref = &varRef{localScope, 102 staticVarInfo{name, false, false}, cp.thisScope().add(name), nil} 103 } else { 104 cp.errorpf(n, "cannot create variable $%s; "+ 105 "new variables can only be created in the current scope", 106 parse.Quote(qname)) 107 return dummyLValuesGroup 108 } 109 } 110 111 ends := make([]int, len(n.Indices)+1) 112 ends[0] = n.Head.Range().To 113 for i, idx := range n.Indices { 114 ends[i+1] = idx.Range().To 115 } 116 lv := lvalue{n.Range(), ref, cp.arrayOps(n.Indices), ends} 117 restIndex := -1 118 if sigil == "@" { 119 restIndex = 0 120 } 121 // TODO: Support % (and other sigils?) if https://b.elv.sh/584 is implemented for map explosion. 122 return lvaluesGroup{[]lvalue{lv}, restIndex} 123 } 124 125 type assignOp struct { 126 diag.Ranging 127 lhs lvaluesGroup 128 rhs valuesOp 129 temp bool 130 } 131 132 func (op *assignOp) exec(fm *Frame) Exception { 133 variables := make([]vars.Var, len(op.lhs.lvalues)) 134 for i, lvalue := range op.lhs.lvalues { 135 variable, err := derefLValue(fm, lvalue) 136 if err != nil { 137 return fm.errorp(op.lhs.lvalues[i], err) 138 } 139 variables[i] = variable 140 } 141 142 values, exc := op.rhs.exec(fm) 143 if exc != nil { 144 return exc 145 } 146 147 rest, temp := op.lhs.rest, op.temp 148 if rest == -1 { 149 if len(variables) != len(values) { 150 return fm.errorp(op, errs.ArityMismatch{What: "assignment right-hand-side", 151 ValidLow: len(variables), ValidHigh: len(variables), Actual: len(values)}) 152 } 153 for i, variable := range variables { 154 exc := set(fm, op.lhs.lvalues[i], temp, variable, values[i]) 155 if exc != nil { 156 return exc 157 } 158 } 159 } else { 160 if len(values) < len(variables)-1 { 161 return fm.errorp(op, errs.ArityMismatch{What: "assignment right-hand-side", 162 ValidLow: len(variables) - 1, ValidHigh: -1, Actual: len(values)}) 163 } 164 for i := 0; i < rest; i++ { 165 exc := set(fm, op.lhs.lvalues[i], temp, variables[i], values[i]) 166 if exc != nil { 167 return exc 168 } 169 } 170 restOff := len(values) - len(variables) 171 exc := set(fm, op.lhs.lvalues[rest], temp, 172 variables[rest], vals.MakeList(values[rest:rest+restOff+1]...)) 173 if exc != nil { 174 return exc 175 } 176 for i := rest + 1; i < len(variables); i++ { 177 exc := set(fm, op.lhs.lvalues[i], temp, variables[i], values[i+restOff]) 178 if exc != nil { 179 return exc 180 } 181 } 182 } 183 return nil 184 } 185 186 func set(fm *Frame, r diag.Ranger, temp bool, variable vars.Var, value any) Exception { 187 if temp { 188 saved := variable.Get() 189 190 needUnset := false 191 unsettable, ok := variable.(vars.UnsettableVar) 192 if ok { 193 needUnset = !unsettable.IsSet() 194 } 195 196 err := variable.Set(value) 197 if err != nil { 198 return fm.errorp(r, err) 199 } 200 fm.addDefer(func(fm *Frame) Exception { 201 if needUnset { 202 if err := unsettable.Unset(); err != nil { 203 return fm.errorpf(r, "unset variable: %w", err) 204 } 205 return nil 206 } 207 208 err := variable.Set(saved) 209 if err != nil { 210 return fm.errorpf(r, "restore variable: %w", err) 211 } 212 return nil 213 }) 214 return nil 215 } 216 err := variable.Set(value) 217 if err != nil { 218 return fm.errorp(r, err) 219 } 220 return nil 221 } 222 223 // NoSuchVariable returns an error representing that a variable can't be found. 224 func NoSuchVariable(name string) error { 225 return noSuchVariableError{name} 226 } 227 228 type noSuchVariableError struct{ name string } 229 230 func (er noSuchVariableError) Error() string { return "no variable $" + er.name } 231 232 func derefLValue(fm *Frame, lv lvalue) (vars.Var, error) { 233 variable := deref(fm, lv.ref) 234 if variable == nil { 235 return nil, NoSuchVariable(fm.srcMeta.Code[lv.From:lv.To]) 236 } 237 if len(lv.indexOps) == 0 { 238 return variable, nil 239 } 240 indices := make([]any, len(lv.indexOps)) 241 for i, op := range lv.indexOps { 242 values, exc := op.exec(fm) 243 if exc != nil { 244 return nil, exc 245 } 246 // TODO: Implement multi-indexing. 247 if len(values) != 1 { 248 return nil, errors.New("multi indexing not implemented") 249 } 250 indices[i] = values[0] 251 } 252 elemVar, err := vars.MakeElement(variable, indices) 253 if err != nil { 254 level := vars.ElementErrorLevel(err) 255 if level < 0 { 256 return nil, fm.errorp(lv, err) 257 } 258 return nil, fm.errorp(diag.Ranging{From: lv.From, To: lv.ends[level]}, err) 259 } 260 return elemVar, nil 261 }