github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/object/reflect/array.go (about) 1 package reflect 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/hirochachacha/plua/internal/tables" 8 "github.com/hirochachacha/plua/object" 9 "github.com/hirochachacha/plua/object/fnutil" 10 ) 11 12 func buildArrayMT() { 13 mt := tables.NewTableSize(0, 7) 14 15 mt.Set(object.TM_METATABLE, object.True) 16 mt.Set(object.TM_NAME, object.String("ARRAY*")) 17 mt.Set(object.TM_TOSTRING, object.GoFunction(atostring)) 18 19 mt.Set(object.TM_INDEX, object.GoFunction(aindex)) 20 mt.Set(object.TM_LEN, object.GoFunction(alength)) 21 mt.Set(object.TM_PAIRS, object.GoFunction(apairs)) 22 23 mt.Set(object.TM_EQ, cmp(func(x, y reflect.Value) bool { return aeq(x, y) }, toArray)) 24 25 arrayMT = mt 26 } 27 28 func toArray(ap *fnutil.ArgParser, n int) (reflect.Value, *object.RuntimeError) { 29 val, err := toValue(ap, n, "ARRAY*") 30 if err != nil { 31 return reflect.Value{}, err 32 } 33 if val.Kind() != reflect.Array { 34 return reflect.Value{}, ap.TypeError(n, "ARRAY*") 35 } 36 return val, nil 37 } 38 39 func atostring(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 40 ap := fnutil.NewArgParser(th, args) 41 42 a, err := toArray(ap, 0) 43 if err != nil { 44 return nil, err 45 } 46 47 return []object.Value{object.String(fmt.Sprintf("go array[%d]", a.Len()))}, nil 48 } 49 50 func alength(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 51 ap := fnutil.NewArgParser(th, args) 52 53 a, err := toArray(ap, 0) 54 if err != nil { 55 return nil, err 56 } 57 58 return []object.Value{object.Integer(a.Len())}, nil 59 } 60 61 func aeq(x, y reflect.Value) bool { 62 xlen := x.Len() 63 ylen := y.Len() 64 65 if xlen == ylen { 66 for i := 0; i < xlen; i++ { 67 if x.Index(i).Interface() != y.Index(i).Interface() { 68 return false 69 } 70 } 71 return true 72 } 73 74 return false 75 } 76 77 func aindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 78 ap := fnutil.NewArgParser(th, args) 79 80 a, err := toArray(ap, 0) 81 if err != err { 82 return nil, err 83 } 84 85 index, err := ap.ToGoInt(1) 86 if err != nil { 87 name, err := ap.ToGoString(1) 88 if err != nil { 89 return nil, err 90 } 91 92 if !isPublic(name) { 93 return nil, nil 94 } 95 96 method := a.MethodByName(name) 97 98 if !method.IsValid() { 99 if a.CanAddr() { 100 method = a.Addr().MethodByName(name) 101 } else { 102 self2 := reflect.New(a.Type()) 103 self2.Elem().Set(a) 104 method = self2.MethodByName(name) 105 } 106 107 if !method.IsValid() { 108 return nil, nil 109 } 110 } 111 112 return []object.Value{valueOfReflect(method, false)}, nil 113 } 114 115 index-- 116 117 if 0 <= index && index < a.Len() { 118 rval := a.Index(index) 119 120 return []object.Value{valueOfReflect(rval, false)}, nil 121 } 122 123 return nil, nil 124 } 125 126 func apairs(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 127 ap := fnutil.NewArgParser(th, args) 128 129 _, err := toArray(ap, 0) 130 if err != nil { 131 return nil, err 132 } 133 134 return []object.Value{object.GoFunction(anext), args[0], object.Integer(0)}, nil 135 } 136 137 func anext(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 138 ap := fnutil.NewArgParser(th, args) 139 140 a, err := toArray(ap, 0) 141 if err != nil { 142 return nil, err 143 } 144 145 index, err := ap.ToGoInt(1) 146 if err != nil { 147 return nil, err 148 } 149 150 if index >= a.Len() { 151 return nil, nil 152 } 153 154 rval := a.Index(index) 155 156 index++ 157 158 return []object.Value{object.Integer(index), valueOfReflect(rval, false)}, nil 159 } 160 161 func buildSliceMT() { 162 mt := tables.NewTableSize(0, 7) 163 164 mt.Set(object.TM_METATABLE, object.True) 165 mt.Set(object.TM_NAME, object.String("SLICE*")) 166 mt.Set(object.TM_TOSTRING, object.GoFunction(stostring)) 167 168 mt.Set(object.TM_INDEX, object.GoFunction(sindex)) 169 mt.Set(object.TM_NEWINDEX, object.GoFunction(snewindex)) 170 mt.Set(object.TM_LEN, object.GoFunction(slength)) 171 mt.Set(object.TM_PAIRS, object.GoFunction(spairs)) 172 173 mt.Set(object.TM_EQ, cmp(func(x, y reflect.Value) bool { return x.Pointer() == y.Pointer() }, toSlice)) 174 175 sliceMT = mt 176 } 177 178 func toSlice(ap *fnutil.ArgParser, n int) (reflect.Value, *object.RuntimeError) { 179 val, err := toValue(ap, n, "SLICE*") 180 if err != nil { 181 return reflect.Value{}, err 182 } 183 if val.Kind() != reflect.Slice { 184 return reflect.Value{}, ap.TypeError(n, "SLICE*") 185 } 186 return val, nil 187 } 188 189 func stostring(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 190 ap := fnutil.NewArgParser(th, args) 191 192 s, err := toSlice(ap, 0) 193 if err != nil { 194 return nil, err 195 } 196 197 return []object.Value{object.String(fmt.Sprintf("go slice (0x%x)", s.Pointer()))}, nil 198 } 199 200 func slength(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 201 ap := fnutil.NewArgParser(th, args) 202 203 s, err := toSlice(ap, 0) 204 if err != nil { 205 return nil, err 206 } 207 208 return []object.Value{object.Integer(s.Len())}, nil 209 } 210 211 func sindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 212 ap := fnutil.NewArgParser(th, args) 213 214 s, err := toSlice(ap, 0) 215 if err != err { 216 return nil, err 217 } 218 219 index, err := ap.ToGoInt(1) 220 if err != nil { 221 name, err := ap.ToGoString(1) 222 if err != nil { 223 return nil, err 224 } 225 226 if !isPublic(name) { 227 return nil, nil 228 } 229 230 method := s.MethodByName(name) 231 232 if !method.IsValid() { 233 if s.CanAddr() { 234 method = s.Addr().MethodByName(name) 235 } else { 236 self2 := reflect.New(s.Type()) 237 self2.Elem().Set(s) 238 method = self2.MethodByName(name) 239 } 240 241 if !method.IsValid() { 242 return nil, nil 243 } 244 } 245 246 return []object.Value{valueOfReflect(method, false)}, nil 247 } 248 249 index-- 250 251 if 0 <= index && index < s.Len() { 252 rval := s.Index(index) 253 254 return []object.Value{valueOfReflect(rval, false)}, nil 255 } 256 257 return nil, object.NewRuntimeError(fmt.Sprintf("invalid array index %d (out of bounds for %d-element array)", index, s.Len())) 258 } 259 260 func snewindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 261 ap := fnutil.NewArgParser(th, args) 262 263 s, err := toSlice(ap, 0) 264 if err != err { 265 return nil, err 266 } 267 268 index, err := ap.ToGoInt(1) 269 if err != nil { 270 return nil, err 271 } 272 273 val, err := ap.ToValue(2) 274 if err != nil { 275 return nil, err 276 } 277 278 index-- 279 280 if 0 <= index && index < s.Len() { 281 styp := s.Type() 282 vtyp := styp.Elem() 283 284 if rval := toReflectValue(vtyp, val); rval.IsValid() { 285 s.Index(index).Set(rval) 286 287 return nil, nil 288 } 289 290 return nil, object.NewRuntimeError(fmt.Sprintf("non-%s array index %q", vtyp, reflect.TypeOf(val))) 291 } 292 293 return nil, object.NewRuntimeError(fmt.Sprintf("invalid array index %d (out of bounds for %d-element array)", index, s.Len())) 294 } 295 296 func spairs(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 297 ap := fnutil.NewArgParser(th, args) 298 299 _, err := toSlice(ap, 0) 300 if err != nil { 301 return nil, err 302 } 303 304 return []object.Value{object.GoFunction(snext), args[0], object.Integer(0)}, nil 305 } 306 307 func snext(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 308 ap := fnutil.NewArgParser(th, args) 309 310 s, err := toSlice(ap, 0) 311 if err != nil { 312 return nil, err 313 } 314 315 index, err := ap.ToGoInt(1) 316 if err != nil { 317 return nil, err 318 } 319 320 if index >= s.Len() { 321 return nil, nil 322 } 323 324 rval := s.Index(index) 325 326 index++ 327 328 return []object.Value{object.Integer(index), valueOfReflect(rval, false)}, nil 329 }