github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/object/reflect/struct.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 buildStructMT() {
    13  	mt := tables.NewTableSize(0, 5)
    14  
    15  	mt.Set(object.TM_METATABLE, object.True)
    16  	mt.Set(object.TM_NAME, object.String("STRUCT*"))
    17  	mt.Set(object.TM_TOSTRING, object.GoFunction(sttostring))
    18  
    19  	mt.Set(object.TM_INDEX, object.GoFunction(stindex))
    20  	mt.Set(object.TM_NEWINDEX, object.GoFunction(stnewindex))
    21  
    22  	mt.Set(object.TM_EQ, cmp(func(x, y reflect.Value) bool { return x.Interface() == y.Interface() }, toStruct))
    23  
    24  	structMT = mt
    25  }
    26  
    27  func toStruct(ap *fnutil.ArgParser, n int) (reflect.Value, *object.RuntimeError) {
    28  	val, err := toValue(ap, n, "STRUCT*")
    29  	if err != nil {
    30  		return reflect.Value{}, err
    31  	}
    32  	if val.Kind() != reflect.Struct {
    33  		return reflect.Value{}, ap.TypeError(n, "STRUCT*")
    34  	}
    35  	return val, nil
    36  }
    37  
    38  func sttostring(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    39  	ap := fnutil.NewArgParser(th, args)
    40  
    41  	_, err := toStruct(ap, 0)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	return []object.Value{object.String(fmt.Sprintf("go struct"))}, nil
    47  }
    48  
    49  func stindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    50  	ap := fnutil.NewArgParser(th, args)
    51  
    52  	s, err := toStruct(ap, 0)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	name, err := ap.ToGoString(1)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	if !isPublic(name) {
    63  		return nil, nil
    64  	}
    65  
    66  	method := s.MethodByName(name)
    67  
    68  	if !method.IsValid() {
    69  		if s.CanAddr() {
    70  			method = s.Addr().MethodByName(name)
    71  		} else {
    72  			self2 := reflect.New(s.Type())
    73  			self2.Elem().Set(s)
    74  			method = self2.MethodByName(name)
    75  		}
    76  
    77  		if !method.IsValid() {
    78  			field := s.FieldByName(name)
    79  			if !field.IsValid() {
    80  				return nil, nil
    81  			}
    82  			return []object.Value{valueOfReflect(field, false)}, nil
    83  		}
    84  		return nil, nil
    85  	}
    86  
    87  	return []object.Value{valueOfReflect(method, false)}, nil
    88  }
    89  
    90  func stnewindex(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    91  	ap := fnutil.NewArgParser(th, args)
    92  
    93  	s, err := toStruct(ap, 0)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	name, err := ap.ToGoString(1)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	val, err := ap.ToValue(2)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	if !isPublic(name) {
   109  		return nil, object.NewRuntimeError(fmt.Sprintf("%s is not public method or field", name))
   110  	}
   111  
   112  	field := s.FieldByName(name)
   113  	if field.IsValid() {
   114  		if field.Kind() == reflect.Ptr {
   115  			field = field.Elem()
   116  		}
   117  
   118  		if rval := toReflectValue(field.Type(), val); rval.IsValid() {
   119  			field.Set(rval)
   120  
   121  			return nil, nil
   122  		}
   123  
   124  		return nil, object.NewRuntimeError(fmt.Sprintf("cannot use %v (type %s) as type %s in field assignment", val, reflect.TypeOf(val), field.Type()))
   125  	}
   126  
   127  	return nil, object.NewRuntimeError(fmt.Sprintf("type %s has no field %s", s.Type(), name))
   128  }