Browse Source

A functional version of decoder

Joachim M. Giæver 6 years ago
parent
commit
73d944e6aa
5 changed files with 617 additions and 88 deletions
  1. 77 12
      go-ini.go
  2. 199 3
      ini/decode.go
  3. 127 45
      ini/element.go
  4. 159 2
      ini/encode.go
  5. 55 26
      ini/scanner.go

+ 77 - 12
go-ini.go

@@ -2,15 +2,68 @@ package main
 
 import (
 	"fmt"
+	"strconv"
+	"strings"
+	"time"
 
 	"git.giaever.org/joachimmg/go-ini.git/ini"
 )
 
+type IniDate time.Time
+
+func (i IniDate) UnmarshalINI(b []byte) error {
+	return nil
+}
+
+type TestUnm struct {
+	myname  string
+	mydate  int
+	mymonth int
+	myyear  int
+}
+
+func (t *TestUnm) UnmarshalINI(b []byte) error {
+	str := strings.Split(string(b), ",")
+
+	if len(str) != 4 {
+		return fmt.Errorf("must contain 4 entries, got %d", len(str))
+	}
+
+	d, err := strconv.Atoi(str[1])
+
+	if err != nil {
+		return err
+	}
+
+	m, err := strconv.Atoi(str[2])
+
+	if err != nil {
+		return err
+	}
+
+	y, err := strconv.Atoi(str[3])
+
+	if err != nil {
+		return err
+	}
+
+	t.myname = str[0]
+	t.mydate = d
+	t.mymonth = m
+	t.myyear = y
+
+	fmt.Println(str[0], d, m, y, t)
+
+	return nil
+}
+
 type Test struct {
 	String         string
 	UnquotedString string
 	MultipleLines  string
 	WithoutValue   bool
+	AboutMe        *TestUnm
+	Date           IniDate `ini:"date"`
 
 	Int struct {
 		Int   int
@@ -20,11 +73,11 @@ type Test struct {
 		Int64 int64
 
 		Uint struct {
-			Uint    uint
-			Uint8   uint8
-			Unint16 uint16
-			Uint32  uint32
-			Uint64  uint64
+			Uint   uint
+			Uint8  uint8
+			Uint16 uint16
+			Uint32 uint32
+			Uint64 uint64
 		}
 	}
 }
@@ -34,20 +87,21 @@ func main() {
 	text := `
 
 String = "This is some text"
-UnquotedString = This is some unquoted text
+UnquotedString = This is some unquoted text            
 MultipleLines = This is \
 multiple lines \
 going on
 WithoutValue
+AboutMe = joachim,23,09,85
 
-[int]
+[Int]
 Int = -1
 Int8 = -8
 Int16 = -16
 Int32 = -32
 Int64 = -64
 
-[int.uint]
+[Int.Uint]
 Uint = 1
 Uint8 = 8
 Uint16 = 16
@@ -60,14 +114,25 @@ Uint64 = 64
 
 	fmt.Println("INI", text)
 
-	test1 := Test{}
+	test1 := &Test{}
 	test2 := Test{
 		String:         "This is some text",
 		UnquotedString: "Helloooo... Not possible to not quote this",
 		MultipleLines:  "This is \nmultiple lines \ngoing on",
 		WithoutValue:   true,
+		Date:           IniDate(time.Now()),
 	}
-	//test2 := &Test{}
-	ini.Unmarshal([]byte(text), test1)
-	ini.Marshal(test2)
+	test2.Int.Int = -1
+	test2.Int.Int8 = -8
+	test2.Int.Int16 = -16
+	test2.Int.Int32 = -32
+	test2.Int.Int64 = -64
+	test2.Int.Uint.Uint = 1
+	test2.Int.Uint.Uint8 = 8
+	test2.Int.Uint.Uint16 = 16
+	test2.Int.Uint.Uint32 = 32
+	test2.Int.Uint.Uint64 = 64
+	fmt.Println("Test1", test1)
+	fmt.Println("Result", ini.Unmarshal([]byte(text), test1), test1, test1.AboutMe)
+	//fmt.Println(ini.Marshal(test2))
 }

+ 199 - 3
ini/decode.go

@@ -1,21 +1,45 @@
 package ini
 
 import (
+	"encoding"
 	"fmt"
+	"reflect"
+	"strconv"
+)
+
+var (
+	unmarshalerType     = reflect.TypeOf(new(Unmarshaler)).Elem()
+	textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
 )
 
 type Unmarshaler interface {
 	UnmarshalINI([]byte) error
 }
 
-type decode struct {
+type valueDecoder func([]byte) error
+
+type field struct {
+	ns string
+
+	t reflect.Type
+	v reflect.Value
+
+	p interface{}
+
+	f valueDecoder
+}
+
+type decoder struct {
 	scan *scanner
+	ns   map[string]*field
+	nss  string
 }
 
 func Unmarshal(d []byte, v interface{}) error {
 
-	dec := &decode{
-		newScanner(d),
+	dec := &decoder{
+		scan: newScanner(d),
+		ns:   make(map[string]*field),
 	}
 
 	if err := dec.scan.valid(); err != nil {
@@ -23,5 +47,177 @@ func Unmarshal(d []byte, v interface{}) error {
 		return err
 	}
 
+	return dec.unmarshal(v)
+}
+
+func (d *decoder) unmarshal(v interface{}) error {
+	rv := reflect.ValueOf(v)
+	d.imap("", rv, nil, 0)
+	fmt.Println(d.ns)
+
+	ns := d.nss
+	for elem, err := d.scan.next(); err == EOL; elem, err = d.scan.next() {
+
+		switch elem.t {
+		case section:
+			ns = d.nss + "." + string(elem.key)
+		case variable:
+			key := ns + "." + string(elem.key)
+
+			if f, ok := d.ns[key]; ok {
+				if err := f.f(elem.val); err != nil {
+					fmt.Println(err)
+				}
+			}
+		}
+	}
+
 	return nil
 }
+
+func (d *decoder) ns_append(ns string, f *field) string {
+	if f.p == nil {
+		f.ns = ns + "." + f.t.Name()
+		return f.ns
+	}
+
+	if tag := f.p.(reflect.StructField).Tag.Get("ini"); len(tag) != 0 {
+		f.ns = ns + "." + tag
+		return f.ns
+	}
+
+	f.ns = ns + "." + f.p.(reflect.StructField).Name
+	return f.ns
+}
+
+func (d *decoder) imap(ns string, v reflect.Value, p interface{}, i int) {
+
+	f := &field{
+		t: v.Type(),
+		v: v,
+		p: nil,
+	}
+
+	if p != nil {
+		f.p = p.(reflect.Type).Field(i)
+	}
+
+	if f.t.Implements(unmarshalerType) {
+		f.f = f.unmDecoder
+		d.ns[d.ns_append(ns, f)] = f
+		return
+	}
+
+	switch f.v.Kind() {
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		f.f = f.uintDecoder
+		d.ns[d.ns_append(ns, f)] = f
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		f.f = f.intDecoder
+		d.ns[d.ns_append(ns, f)] = f
+	case reflect.String:
+		f.f = f.strDecoder
+		d.ns[d.ns_append(ns, f)] = f
+	case reflect.Bool:
+		f.f = f.boolDecoder
+		d.ns[d.ns_append(ns, f)] = f
+	case reflect.Struct:
+		if len(d.nss) == 0 {
+			d.nss = d.ns_append(ns, f)
+		}
+		for i := 0; i < f.v.NumField(); i++ {
+			d.imap(d.ns_append(ns, f), f.v.Field(i), f.t, i)
+		}
+	case reflect.Ptr:
+		if f.v.IsNil() {
+			return
+		}
+		d.imap(ns, f.v.Elem(), nil, i)
+	default:
+		fmt.Printf("OTHER TYPE: %v", f.t.Name())
+	}
+}
+
+func (f *field) valid() error {
+
+	if f.v.CanAddr() {
+		return nil
+	}
+
+	return fmt.Errorf("Field '%s' not addressable", f.ns)
+}
+
+func (f *field) uintDecoder(b []byte) error {
+
+	if err := f.valid(); err != nil {
+		return err
+	}
+
+	i, err := strconv.ParseUint(string(b), 10, 64)
+
+	if err != nil {
+		return err
+	}
+
+	f.v.SetUint(i)
+
+	return nil
+}
+
+func (f *field) intDecoder(b []byte) error {
+
+	if err := f.valid(); err != nil {
+		return err
+	}
+
+	i, err := strconv.ParseInt(string(b), 10, 64)
+
+	if err != nil {
+		return err
+	}
+
+	f.v.SetInt(i)
+
+	return nil
+}
+
+func (f *field) strDecoder(b []byte) error {
+	if err := f.valid(); err != nil {
+		return err
+	}
+	f.v.SetString(string(b))
+	return nil
+}
+
+func (f *field) boolDecoder(b []byte) error {
+
+	if err := f.valid(); err != nil {
+		return err
+	}
+	f.v.SetBool(true)
+	return nil
+}
+
+func (f *field) unmDecoder(b []byte) error {
+
+	if err := f.valid(); err != nil {
+		return err
+	}
+
+	v := f.v
+
+	if v.Kind() != reflect.Ptr && v.Type().Name() != "" {
+		v = v.Addr()
+	}
+
+	if v.IsNil() {
+		v.Set(reflect.New(v.Type().Elem()))
+	}
+
+	if i, ok := v.Interface().(Unmarshaler); ok {
+		err := i.UnmarshalINI(b)
+		return err
+	}
+
+	return fmt.Errorf("Cannot load Unmarshaler")
+}

+ 127 - 45
ini/element.go

@@ -5,6 +5,7 @@ import (
 )
 
 type elementType int
+type elementByte []byte
 
 const (
 	section  elementType = 1 << iota
@@ -12,74 +13,155 @@ const (
 )
 
 type element struct {
-	eType elementType
-	eKey  []byte
-	eVal  []byte
+	t      elementType
+	key    elementByte
+	val    elementByte
+	quoted bool
 }
 
 func newElement() *element {
-	return &element{}
+	return &element{quoted: false}
 }
 
-func (e *element) setValue(v interface{}) bool {
-	switch v.(type) {
-	case byte:
-		e.eVal = append(e.eVal, v.(byte))
-		return true
-	case []byte:
-		e.eVal = v.([]byte)
-		return true
+func (e *element) setType(t elementType) {
+	e.t = t
+}
+
+func (e *element) trim() {
+	e.key.trim()
+	e.quoted = e.val.trim()
+}
+
+func (e *element) String() string {
+	switch e.t {
+	case section:
+		return fmt.Sprintf(
+			"section: %s", e.key,
+		)
+	case variable:
+		if e.val.len() == 0 {
+			return fmt.Sprintf(
+				"variable: %s -> true", e.val,
+			)
+		}
+		if e.quoted {
+			return fmt.Sprintf(
+				"variable: %s -> '\"%s\"'", e.key, e.val,
+			)
+		}
+		return fmt.Sprintf(
+			"variable: %s -> '%s'", e.key, e.val,
+		)
+	default:
+		return "unknown element"
 	}
+}
 
-	fmt.Printf("Uknown type: %T", v)
-	return false
+func (e *elementByte) add(v ...interface{}) bool {
+	b := i2byte(v)
+
+	if len(b) == 0 {
+		return false
+	}
+
+	*e = append(*e, b...)
+
+	return true
 }
 
-func (e *element) removeLastValueByteMatching(c byte) {
-	if n := len(e.eVal); n != 0 && e.eVal[n-1] == c {
-		e.eVal = e.eVal[0 : n-1]
+func (e *elementByte) len() int {
+	return len(*e)
+}
+
+func (e *elementByte) lastByteMatching(c byte) int {
+	if n := e.len(); n != 0 && (*e)[n-1] == c {
+		return n - 1
 	}
+
+	return -1
 }
 
-func (e *element) setKey(k interface{}) bool {
-	switch k.(type) {
-	case byte:
-		e.eKey = append(e.eKey, k.(byte))
-		return true
-	case []byte:
-		e.eKey = k.([]byte)
+func (e *elementByte) firstByteMatching(c byte) int {
+	if n := e.len(); n != 0 && (*e)[0] == c {
+		return 0
+	}
+
+	return -1
+}
+
+func (e *elementByte) removeFirstByteMatching(c byte) bool {
+	if n := e.firstByteMatching(c); n == 0 {
+		*e = (*e)[1:]
 		return true
 	}
 
 	return false
 }
 
-func (e *element) removeLastKeyByteMatching(c byte) {
-	if n := len(e.eKey); n != 0 && e.eKey[n-1] == c {
-		e.eKey = e.eKey[0 : n-1]
+func (e *elementByte) removeLastByteMatching(c byte) bool {
+	if n := e.lastByteMatching(c); n >= 0 {
+		*e = (*e)[0:n]
+		return true
 	}
+
+	return false
 }
 
-func (e *element) setType(t elementType) {
-	e.eType = t
+func (e *elementByte) trim() (quoted bool) {
+
+	for b := e.removeFirstByteMatching(' '); b == true; b = e.removeFirstByteMatching(' ') {
+		// remove leading whitespaces
+	}
+
+	for b := e.removeLastByteMatching(' '); b == true; b = e.removeLastByteMatching(' ') {
+		// remove trailing whitespaces
+	}
+
+	if f := e.firstByteMatching('"'); f == 0 {
+		if l := e.lastByteMatching('"'); l >= 0 {
+			*e = (*e)[1:]
+			*e = (*e)[0 : l-1]
+			return true
+		}
+	}
+
+	return false
 }
 
-func (e *element) String() string {
-	switch e.eType {
-	case section:
-		return fmt.Sprintf(
-			"section: %s", e.eKey,
-		)
-	case variable:
-		if len(e.eVal) == 0 {
-			return fmt.Sprintf(
-				"variable: %s -> true", e.eKey,
-			)
+func i2byte(v ...interface{}) []byte {
+
+	var b []byte
+
+	for _, i := range v {
+		switch i.(type) {
+		case byte, int8:
+			b = append(b, i.(byte))
+		case int:
+			if i.(int) < 256 {
+				b = append(b, byte(i.(int)))
+			}
+		case int16:
+			if i.(int16) < 256 {
+				b = append(b, byte(i.(int16)))
+			}
+		case int32:
+			if i.(int32) < 256 {
+				b = append(b, byte(i.(int32)))
+			}
+		case int64:
+			if i.(int64) < 256 {
+				b = append(b, byte(i.(int64)))
+			}
+		case []byte:
+			b = append(b, i.([]byte)...)
+		case string:
+			b = append(b, i2byte([]byte(i.(string)))...)
+		case []interface{}:
+			for _, ii := range i.([]interface{}) {
+				b = append(b, i2byte(ii)...)
+			}
 		}
-		return fmt.Sprintf(
-			"variable: %s -> %s", e.eKey, e.eVal,
-		)
-	default:
-		return "unknown element"
 	}
+
+	return b
 }

+ 159 - 2
ini/encode.go

@@ -1,11 +1,168 @@
 package ini
 
 import (
+	"bytes"
+	"encoding"
 	"fmt"
+	"reflect"
 )
 
+var (
+	marshalerType     = reflect.TypeOf(new(Marshaler)).Elem()
+	textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
+)
+
+type Marshaler interface {
+	MarshalINI() ([]byte, error)
+}
+
+type encoder interface {
+	encode()
+}
+
+type encode struct {
+	bytes.Buffer
+	scratch [64]byte
+
+	/*cache sync.Map TODO */
+}
+
 func Marshal(v interface{}) ([]byte, error) {
 
-	fmt.Println(v)
-	return nil, nil
+	fmt.Println("Interface", v)
+	enc := &encode{}
+
+	if err := enc.marshal(v); err != nil {
+		return nil, err
+	}
+
+	return enc.Bytes(), nil
+}
+
+func (e *encode) marshal(v interface{}) error {
+
+	s := "Text"
+
+	e.reflectValue(reflect.ValueOf(v))
+	e.reflectValue(reflect.ValueOf(1))
+	e.reflectValue(reflect.ValueOf(s))
+	return nil
+}
+
+func (e *encode) reflectValue(v reflect.Value) {
+	fmt.Println("ReflectValue", v)
+	valueEncoder(e, v).encode()
+}
+
+func valueEncoder(e *encode, v reflect.Value) encoder {
+	if !v.IsValid() {
+		return invalidValueEncoder{e, v, v.Type()}
+	}
+
+	return e.encoder(v)
+}
+
+func (e *encode) encoder(v reflect.Value) encoder {
+	t := v.Type()
+
+	if t.Implements(marshalerType) {
+		fmt.Println("t.Implements(marshalerType)", t.Implements(marshalerType))
+		return marshalerEncoder{e, v, t}
+	}
+
+	if t.Kind() != reflect.Ptr {
+		if reflect.PtrTo(t).Implements(marshalerType) {
+			fmt.Println("Allow marshalType")
+		}
+	}
+
+	if t.Implements(textMarshalerType) {
+		fmt.Println("t.Implements(textMarshalerType)", t.Implements(textMarshalerType))
+		return &textMarshalerEncoder{e, v, t}
+	}
+
+	if t.Kind() != reflect.Ptr {
+		if reflect.PtrTo(t).Implements(textMarshalerType) {
+			fmt.Println("Allow TextmarshalType")
+		}
+	}
+
+	switch t.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		fmt.Println("INT")
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		fmt.Println("UINT")
+	case reflect.Float32, reflect.Float64:
+		fmt.Println("Float32/64")
+	case reflect.String:
+		fmt.Println("STRING")
+		return &stringEncoder{e, v, t}
+	case reflect.Struct:
+		fmt.Println("STRUCT")
+		return &structEncoder{e, v, t}
+	case reflect.Interface:
+		fmt.Println("Interface")
+	case reflect.Slice, reflect.Array:
+		fmt.Println("Array or slice")
+	default:
+		fmt.Printf("Other kind: %s", t.Kind())
+	}
+
+	return &invalidValueEncoder{e, v, t}
+}
+
+type marshalerEncoder struct {
+	e *encode
+	v reflect.Value
+	t reflect.Type
+}
+
+func (m marshalerEncoder) encode() {
+
+}
+
+type textMarshalerEncoder struct {
+	e *encode
+	v reflect.Value
+	t reflect.Type
+}
+
+func (m textMarshalerEncoder) encode() {
+
+}
+
+type structEncoder struct {
+	e *encode
+	v reflect.Value
+	t reflect.Type
+}
+
+func (s structEncoder) encode() {
+	//fmt.Println("STRUCT", s.t)
+
+	fmt.Printf("NAME: %s", s.t)
+
+	for i := 0; i < s.v.NumField(); i++ {
+		s.e.encoder(s.v.Field(i)).encode()
+	}
+}
+
+type stringEncoder struct {
+	e *encode
+	v reflect.Value
+	t reflect.Type
+}
+
+func (t *stringEncoder) encode() {
+	fmt.Println("STRING ENCODER", t.v, t.t)
+}
+
+type invalidValueEncoder struct {
+	e *encode
+	v reflect.Value
+	t reflect.Type
+}
+
+func (i invalidValueEncoder) encode() {
+
 }

+ 55 - 26
ini/scanner.go

@@ -11,6 +11,7 @@ const (
 	scanContinue scanState = 1 << iota
 	scanEOL
 	scanEOF
+	scanError
 
 	scanBegin
 	scanSection
@@ -30,7 +31,7 @@ type SyntaxError struct {
 	off int
 }
 
-func (s *SyntaxError) Error() string {
+func (s SyntaxError) Error() string {
 	if s.got == 0 {
 		return fmt.Sprintf("Error on position '%d: %s'", s.off, s.msg)
 	}
@@ -65,10 +66,18 @@ func newScanner(d []byte) *scanner {
 	return s
 }
 
+func (s *scanner) reset() {
+	s.prev = 0
+	s.scan = s.begin
+	s.read = 0
+	s.err = nil
+}
+
 func (s *scanner) loop() (*element, error) {
 	e := newElement()
 	for _, c := range s.data[s.read:] {
 		s.read++
+
 		state := s.scan(c, e)
 		s.prev = c
 
@@ -88,9 +97,18 @@ func (s *scanner) loop() (*element, error) {
 }
 
 func (s *scanner) valid() error {
+	defer s.reset()
+
 	for _, err := s.next(); ; _, err = s.next() {
-		if err != EOF && err != EOL {
-			return err
+		switch err {
+		case EOL:
+			continue
+		case EOF:
+			return nil
+		default:
+			if err != nil {
+				return err
+			}
 		}
 	}
 
@@ -99,28 +117,36 @@ func (s *scanner) valid() error {
 
 func (s *scanner) next() (*element, error) {
 	e, err := s.loop()
+
+	if e != nil {
+		e.trim()
+	}
+
 	return e, err
 }
 
 func (s *scanner) ret(state scanState) scanState {
 	if state != scanEOF && s.read == s.len {
-		s.scan = s.begin
-		return scanEOF
+		state = scanEOF
 	}
 
-	if state == scanEOL {
+	if state == scanEOL || state == scanEOF {
 		s.scan = s.begin
 	}
 
 	return state
 }
 
+func (s *scanner) space(c byte) bool {
+	return c == ' ' || c == '\n' || c == '\t' || c == '\r'
+}
+
 func (s *scanner) begin(c byte, e *element) scanState {
 
-	switch c {
-	case ' ', '\n', '\t', '\r':
+	switch {
+	case s.space(c):
 		return s.ret(scanContinue)
-	case '[':
+	case c == '[':
 		e.setType(section)
 		s.scan = s.section
 		return s.ret(scanContinue)
@@ -132,19 +158,27 @@ func (s *scanner) begin(c byte, e *element) scanState {
 }
 
 func (s *scanner) section(c byte, e *element) scanState {
-	switch c {
-	case '\n':
-		s.err = &SyntaxError{
-			msg: "Missing valid character",
+	switch {
+	case s.space(c):
+		s.err = SyntaxError{
 			got: c,
 			exp: ']',
 			off: s.read,
+			msg: "Invalid space character.",
 		}
-		fallthrough
-	case ']':
+		return scanError
+	case s.read == s.len:
+		s.err = SyntaxError{
+			got: 0x3,
+			exp: ']',
+			off: s.read,
+			msg: "Invalid EOF",
+		}
+		return scanError
+	case c == ']':
 		return s.ret(scanEOL)
 	default:
-		e.setKey(c)
+		e.key.add(c)
 		return s.ret(scanContinue)
 	}
 }
@@ -154,30 +188,25 @@ func (s *scanner) variable(c byte, e *element) scanState {
 	case '\n', '\r':
 		return s.ret(scanEOL)
 	case '=':
-		e.removeLastKeyByteMatching(' ')
 		s.scan = s.variableValue
 		return s.ret(scanContinue)
 	default:
-		e.setKey(c)
+		e.key.add(c)
 		return s.ret(scanContinue)
 	}
 }
 
 func (s *scanner) variableValue(c byte, e *element) scanState {
 	switch c {
-	case '"', '\'':
-		return s.ret(scanContinue)
 	case '\n', '\r':
-		if s.prev == '\\' {
-			e.setValue(byte('n'))
+		if e.val.lastByteMatching('\\') >= 0 {
+			e.val.add('n')
 			return s.ret(scanContinue)
 		}
+
 		return s.ret(scanEOL)
 	default:
-		if s.prev == '=' && c == ' ' {
-			return scanContinue
-		}
-		e.setValue(c)
+		e.val.add(c)
 		return s.ret(scanContinue)
 	}
 }