package asld import ( "bytes" "fmt" "reflect" "strconv" "strings" ) const ( intSize = 32 << (^uint(0) >> 63) // unexported from "math" ) var ( bitSize = map[reflect.Kind]int{ reflect.Int: intSize, reflect.Int8: 8, reflect.Int16: 16, reflect.Int32: 32, reflect.Int64: 64, reflect.Uint: intSize, reflect.Uint8: 8, reflect.Uint16: 16, reflect.Uint32: 32, reflect.Uint64: 64, reflect.Float32: 32, reflect.Float64: 64, reflect.Complex64: 64, reflect.Complex128: 128, } nullWalker = newWalker([]byte{'n', 'u', 'l', 'l'}) ) func arrayMembers(w walker) ([]walker, error) { var ( members = []walker{} final bool ) elements, ok := w.SliceInner() if !ok { return nil, fmt.Errorf("%s %w", string(w.content[w.position]), ErrNoMatching) } for !final { elem, err := elements.CommaOrEnd() if err != nil { return nil, fmt.Errorf("comma or end %s: %w", string(elem.content), err) } final = elem.status == statusWalkerNotAffected value := elem.walker.Until().Reset() if !final { // Incremenet elem.walker.position here so // that it skips over the comma that we're // on right now. as there's not really anything // valid to stop on in arrays aside from them // so we can't just call ToNext() later. elem.walker = elem.walker.Pos(elem.walker.position + 1) } else { value = elem.walker } elements = elem.walker.Sub() members = append(members, value) } return members, nil } func mapMembers(w walker) (map[string]walker, error) { var ( members = map[string]walker{} lastLine bool //debug prev string ) w, sym := w.StayOrNext() if sym == symbolEOB { w, sym = w.StayOrNext() panic("idk") } // Because this gets called recursively with all kinds of // inputs and spacing, we should do a Stay/Walk If Space situation elements, ok := w.SliceInner() if !ok { return nil, fmt.Errorf("%s %w", string(w.content[w.position]), ErrNoMatching) } w.Debug() for !lastLine { lineInfo, err := elements.CommaOrEnd() if err != nil { return nil, fmt.Errorf("comma or end: %s: %w", string(elements.content), err) } lastLine = lineInfo.status == statusWalkerNotAffected line := lineInfo.walker if !lastLine { line = line.Until() } name, ok := line.Reset().ToOrStay('"') if !ok { continue } nameString, ok := name.SliceInner() if !ok { // TODO: maybe these should have global position return nil, fmt.Errorf("%s %w", string(name.Current()), ErrNoMatching) } // We know this is OK because the above SliceInner called it name, _ = name.To('"') wNext, sym := name.Next() if sym != symbolEOB && wNext.Current() != ':' && isIRI(nameString.content) { panic("IRI expansion not implemented") } else if sym != symbolEOB && wNext.Current() != ':' { return nil, fmt.Errorf("%s at pos %d: %w", string(nameString.content), wNext.globPos, ErrEntryWithoutValue) } value, sym := wNext.Next() if sym == symbolEOB { if value.position < value.len-1 { value = value.Pos(value.position + 1).Sub() } else { return nil, fmt.Errorf("non-IRI %s: %w", string(nameString.content), ErrEntryWithoutValue) } } else if sym == symbolNullStart { value = nullWalker } // Walk to next viable token value = value.WalkThroughSpaces().Sub() elements = lineInfo.walker.Sub() n := string(nameString.content) print(n + "\n") fmt.Println(value) members[n] = value prev = n } fmt.Print(prev) return members, nil } func unmarshalStruct(out reflect.Value, w walker) error { members, err := mapMembers(w) if err != nil { return fmt.Errorf("getting members: %w", err) } outType := out.Type() // Deconstruct the struct fields for index := 0; index < out.NumField(); index++ { field := out.Field(index) fType := outType.Field(index) tagInfo := fType.Tag.Get(tagName) // TODO: support expandible/collapsible/whatever I name it // and omitempty probably tagParts := strings.Split(tagInfo, ",") name := tagParts[0] if tagInfo == "" || name == "" { name = fType.Name } // mimic encoding/json behavior if name == "-" && len(tagParts) == 1 { continue } wField, exists := members[name] if !exists { continue } fmt.Println(wField, field) // if err := setValue(field, wField, fType); err != nil { // return fmt.Errorf("field %s set: %w", name, err) // } } return nil } func setMap(field reflect.Value, w walker) error { if field.IsNil() { field.Set(reflect.MakeMap(field.Type())) } keyType := field.Type().Key() if keyType.Kind() != reflect.String { return ErrMapNotStringIndexed } members, err := mapMembers(w) if err != nil { return fmt.Errorf("getting members for map: %w", err) } valueType := field.Type().Elem() for key, member := range members { def := reflect.Indirect(reflect.New(valueType)) err = setValue(def, member) if err != nil { return fmt.Errorf( "could not set %s to %s: %w", string(w.content), valueType.Kind().String(), err, ) } field.SetMapIndex(reflect.ValueOf(key), def) } return nil } func setValue(field reflect.Value, w walker, a ...reflect.StructField) error { var ( err error setter func() ) if w.len >= 4 && bytes.Equal(null, w.content[:4]) { // default value return nil } k := field.Kind() switch k { case reflect.String: if w.content[0] != '"' { err = fmt.Errorf("%s is not a string", string(w.content)) } setter = func() { field.SetString(w.String()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var i int64 i, err = strconv.ParseInt(string(w.content), 10, bitSize[field.Kind()]) setter = func() { field.SetInt(i) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var i uint64 i, err = strconv.ParseUint(string(w.content), 10, bitSize[field.Kind()]) setter = func() { field.SetUint(i) } case reflect.Float32, reflect.Float64: var f float64 f, err = strconv.ParseFloat(string(w.content), bitSize[field.Kind()]) setter = func() { field.SetFloat(f) } case reflect.Complex64, reflect.Complex128: var c complex128 c, err = strconv.ParseComplex(string(w.content), bitSize[field.Kind()]) setter = func() { field.SetComplex(c) } case reflect.Pointer: if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } // prob oops if its nil fDeref := reflect.Indirect(field) return setValue(fDeref, w) case reflect.Struct, reflect.Map, reflect.Array, reflect.Slice: if k == reflect.Array || k == reflect.Slice && len(a) > 0 { fmt.Println("$$$$$$$$$$$", a[0].Name) } return unmarshal(field, w) case reflect.Bool: var b bool b, err = strconv.ParseBool(string(w.content)) setter = func() { field.SetBool(b) } case reflect.Chan, reflect.Func, reflect.UnsafePointer: // Ignore return nil default: panic("not implemented") } if err != nil { return fmt.Errorf("could not parse %s as %s: %w", string(w.content), field.Kind().String(), err) } setter() return nil } func unmarshal(out reflect.Value, w walker) error { switch out.Kind() { case reflect.Struct: return unmarshalStruct(out, w) case reflect.Map: return setMap(out, w) case reflect.Array, reflect.Slice: // do array stuff here panic("todo") default: panic(out.Kind().String() + " not yet supported") } } func Unmarshal[T any](data []byte) (T, error) { tPtr := new(T) tValue := reflect.Indirect(reflect.ValueOf(tPtr)) w := newWalker(data) // Might be array/map/interface at top level, should check return *tPtr, unmarshal(tValue, w) }