github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/object/reflect/map.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 buildMapMT() {
    13  	mt := tables.NewTableSize(0, 7)
    14  
    15  	mt.Set(object.TM_METATABLE, object.True)
    16  	mt.Set(object.TM_NAME, object.String("MAP*"))
    17  	mt.Set(object.TM_TOSTRING, object.GoFunction(mtostring))
    18  
    19  	mt.Set(object.TM_INDEX, object.GoFunction(mindex))
    20  	mt.Set(object.TM_NEWINDEX, object.GoFunction(mnewindex))
    21  	mt.Set(object.TM_LEN, object.GoFunction(mlength))
    22  	mt.Set(object.TM_PAIRS, object.GoFunction(mpairs))
    23  
    24  	mt.Set(object.TM_EQ, cmp(func(x, y reflect.Value) bool { return x.Pointer() == y.Pointer() }, toMap))
    25  
    26  	mapMT = mt
    27  }
    28  
    29  func toMap(ap *fnutil.ArgParser, n int) (reflect.Value, *object.RuntimeError) {
    30  	val, err := toValue(ap, n, "MAP*")
    31  	if err != nil {
    32  		return reflect.Value{}, err
    33  	}
    34  	if val.Kind() != reflect.Map {
    35  		return reflect.Value{}, ap.TypeError(n, "MAP*")
    36  	}
    37  	return val, nil
    38  }
    39  
    40  func mtostring(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    41  	ap := fnutil.NewArgParser(th, args)
    42  
    43  	m, err := toMap(ap, 0)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	return []object.Value{object.String(fmt.Sprintf("go map (0x%x)", m.Pointer()))}, nil
    49  }
    50  
    51  func mlength(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    52  	ap := fnutil.NewArgParser(th, args)
    53  
    54  	m, err := toMap(ap, 0)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return []object.Value{object.Integer(m.Len())}, nil
    60  }
    61  
    62  func mindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    63  	ap := fnutil.NewArgParser(th, args)
    64  
    65  	m, err := toMap(ap, 0)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	key, err := ap.ToValue(1)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	ktyp := m.Type().Key()
    76  
    77  	if rkey := toReflectValue(ktyp, key); rkey.IsValid() {
    78  		rval := m.MapIndex(rkey)
    79  
    80  		return []object.Value{valueOfReflect(rval, false)}, nil
    81  	}
    82  
    83  	return nil, object.NewRuntimeError(fmt.Sprintf("cannot use %v (type %s) as type %s in map index", key, reflect.TypeOf(key), ktyp))
    84  }
    85  
    86  func mnewindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    87  	ap := fnutil.NewArgParser(th, args)
    88  
    89  	m, err := toMap(ap, 0)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	key, err := ap.ToValue(1)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	val, err := ap.ToValue(2)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	styp := m.Type()
   105  	ktyp := styp.Key()
   106  	vtyp := styp.Elem()
   107  
   108  	if rkey := toReflectValue(ktyp, key); rkey.IsValid() {
   109  		if rval := toReflectValue(vtyp, val); rval.IsValid() {
   110  			m.SetMapIndex(rkey, rval)
   111  
   112  			return nil, nil
   113  		}
   114  
   115  		return nil, object.NewRuntimeError(fmt.Sprintf("cannot use %v (type %s) as type %s in map assignment", val, reflect.TypeOf(val), vtyp))
   116  	}
   117  
   118  	return nil, object.NewRuntimeError(fmt.Sprintf("cannot use %v (type %s) as type %s in map index", key, reflect.TypeOf(key), ktyp))
   119  }
   120  
   121  func mpairs(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   122  	ap := fnutil.NewArgParser(th, args)
   123  
   124  	m, err := toMap(ap, 0)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	keys := m.MapKeys()
   130  	length := len(keys)
   131  
   132  	i := 0
   133  
   134  	next := func(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   135  		if i == length {
   136  			return nil, nil
   137  		}
   138  
   139  		key := keys[i]
   140  		rval := m.MapIndex(key)
   141  
   142  		i++
   143  
   144  		return []object.Value{valueOfReflect(key, false), valueOfReflect(rval, false)}, nil
   145  	}
   146  
   147  	return []object.Value{object.GoFunction(next)}, nil
   148  }