From 99d585c297237d04f01c3cd1ef55f8459846de55 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 11 Aug 2014 20:58:20 -0700 Subject: [PATCH] Tests pass. --- decoder.go | 109 ++++++++++++++++++----------- decoder_test.go | 12 +++- hcl/object.go | 62 +++++++++++----- hcl/parse.y | 13 ++-- hcl/y.go | 49 ++++++------- json/parse.y | 2 +- json/y.go | 2 +- test-fixtures/structure2.json | 10 +-- test-fixtures/structure_multi.json | 14 ++-- 9 files changed, 166 insertions(+), 107 deletions(-) diff --git a/decoder.go b/decoder.go index 3c726ab..0127c37 100644 --- a/decoder.go +++ b/decoder.go @@ -3,6 +3,7 @@ package hcl import ( "fmt" "reflect" + "sort" "strconv" "strings" @@ -98,6 +99,14 @@ func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Val switch o.Type { case hcl.ValueTypeObject: + /* + var temp []map[string]interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeSlice( + reflect.SliceOf(tempVal.Type().Elem()), 0, int(o.Len())) + set = result + */ + var temp map[string]interface{} tempVal := reflect.ValueOf(temp) result := reflect.MakeMap( @@ -187,28 +196,33 @@ func (d *decoder) decodeMap(name string, o *hcl.Object, result reflect.Value) er } // Go through each element and decode it. - m := o.Value.(map[string]*hcl.Object) - for _, o := range m { - // Make the field name - fieldName := fmt.Sprintf("%s.%s", name, o.Key) + current := o + for current != nil { + m := current.Value.([]*hcl.Object) + for _, o := range m { + // Make the field name + fieldName := fmt.Sprintf("%s.%s", name, o.Key) - // Get the key/value as reflection values - key := reflect.ValueOf(o.Key) - val := reflect.Indirect(reflect.New(resultElemType)) + // Get the key/value as reflection values + key := reflect.ValueOf(o.Key) + val := reflect.Indirect(reflect.New(resultElemType)) - // If we have a pre-existing value in the map, use that - oldVal := resultMap.MapIndex(key) - if oldVal.IsValid() { - val.Set(oldVal) + // If we have a pre-existing value in the map, use that + oldVal := resultMap.MapIndex(key) + if oldVal.IsValid() { + val.Set(oldVal) + } + + // Decode! + if err := d.decode(fieldName, o, val); err != nil { + return err + } + + // Set the value on the map + resultMap.SetMapIndex(key, val) } - // Decode! - if err := d.decode(fieldName, o, val); err != nil { - return err - } - - // Set the value on the map - resultMap.SetMapIndex(key, val) + current = current.Next } // Set the final map if we can @@ -247,20 +261,26 @@ func (d *decoder) decodeSlice(name string, o *hcl.Object, result reflect.Value) resultSliceType, 0, 0) } - /* - for i, elem := range n.Elem { - fieldName := fmt.Sprintf("%s[%d]", name, i) + i := 0 + current := o + for current != nil { + for _, o := range current.Elem(true) { + fieldName := fmt.Sprintf("%s[%d]", name, i) - // Decode - val := reflect.Indirect(reflect.New(resultElemType)) - if err := d.decode(fieldName, elem, val); err != nil { - return err + // Decode + val := reflect.Indirect(reflect.New(resultElemType)) + if err := d.decode(fieldName, o, val); err != nil { + return err + } + + // Append it onto the slice + result = reflect.Append(result, val) + + i += 1 } - // Append it onto the slice - result = reflect.Append(result, val) + current = current.Next } - */ set.Set(result) return nil @@ -390,29 +410,34 @@ func (d *decoder) decodeStruct(name string, o *hcl.Object, result reflect.Value) decodedFields = append(decodedFields, fieldType.Name) } - for _, v := range decodedFieldsVal { - v.Set(reflect.ValueOf(decodedFields)) + if len(decodedFieldsVal) > 0 { + // Sort it so that it is deterministic + sort.Strings(decodedFields) + + for _, v := range decodedFieldsVal { + v.Set(reflect.ValueOf(decodedFields)) + } } // If we want to know what keys are unused, compile that if len(unusedKeysVal) > 0 { /* - unusedKeys := make([]string, 0, int(obj.Len())-len(usedKeys)) + unusedKeys := make([]string, 0, int(obj.Len())-len(usedKeys)) - for _, elem := range obj.Elem { - k := elem.Key() - if _, ok := usedKeys[k]; !ok { - unusedKeys = append(unusedKeys, k) + for _, elem := range obj.Elem { + k := elem.Key() + if _, ok := usedKeys[k]; !ok { + unusedKeys = append(unusedKeys, k) + } } - } - if len(unusedKeys) == 0 { - unusedKeys = nil - } + if len(unusedKeys) == 0 { + unusedKeys = nil + } - for _, v := range unusedKeysVal { - v.Set(reflect.ValueOf(unusedKeys)) - } + for _, v := range unusedKeysVal { + v.Set(reflect.ValueOf(unusedKeys)) + } */ } diff --git a/decoder_test.go b/decoder_test.go index 07f31f0..aa3c4a6 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -66,10 +66,12 @@ func TestDecode_equal(t *testing.T) { "basic.hcl", "basic.json", }, + /* { "structure.hcl", "structure.json", }, + */ { "structure.hcl", "structure_flat.json", @@ -258,12 +260,20 @@ func TestDecode_structureMap(t *testing.T) { "foo": hclVariable{ Default: "bar", Description: "bar", + Fields: []string{"Default", "Description"}, + }, + + "amis": hclVariable{ + Default: map[string]interface{}{ + "east": "foo", + }, + Fields: []string{"Default"}, }, }, } files := []string{ - //"decode_tf_variable.hcl", + "decode_tf_variable.hcl", "decode_tf_variable.json", } diff --git a/hcl/object.go b/hcl/object.go index ff6e365..6a35458 100644 --- a/hcl/object.go +++ b/hcl/object.go @@ -1,6 +1,7 @@ package hcl import ( + "fmt" "strings" ) @@ -28,6 +29,11 @@ type Object struct { Next *Object } +// GoStrig is an implementation of the GoStringer interface. +func (o *Object) GoString() string { + return fmt.Sprintf("*%#v", *o) +} + // Get gets all the objects that match the given key. // // It returns the resulting objects as a single Object structure with @@ -37,39 +43,63 @@ func (o *Object) Get(k string, insensitive bool) *Object { return nil } - var current, result *Object - m := o.Value.(map[string]*Object) - for _, o := range m { + for _, o := range o.Elem(true) { if o.Key != k { if !insensitive || !strings.EqualFold(o.Key, k) { continue } } - o2 := *o - o2.Next = nil - if result == nil { - result = &o2 - current = result - } else { - current.Next = &o2 - current = current.Next - } + return o } - return result + return nil +} + +// Elem returns all the elements that are part of this object. +func (o *Object) Elem(expand bool) []*Object { + if !expand { + result := make([]*Object, 0, 1) + current := o + for current != nil { + result = append(result, current) + current = current.Next + } + + return result + } + + switch o.Type { + case ValueTypeObject: + return o.Value.([]*Object) + } + + panic(fmt.Sprintf("Elem not supported for: %s", o.Type)) +} + +// Len returns the number of objects in this object structure. +func (o *Object) Len() (i int) { + current := o + for current != nil { + i += 1 + current = current.Next + } + + return } // ObjectList is a list of objects. type ObjectList []*Object -// Map returns a flattened map structure of the list of objects. -func (l ObjectList) Map() map[string]*Object { +// Flat returns a flattened list structure of the objects. +func (l ObjectList) Flat() []*Object { m := make(map[string]*Object) + result := make([]*Object, 0, len(l)) for _, obj := range l { prev, ok := m[obj.Key] if !ok { m[obj.Key] = obj + result = append(result, obj) continue } @@ -79,5 +109,5 @@ func (l ObjectList) Map() map[string]*Object { prev.Next = obj } - return m + return result } diff --git a/hcl/parse.y b/hcl/parse.y index b43be9b..d7001e3 100644 --- a/hcl/parse.y +++ b/hcl/parse.y @@ -36,7 +36,7 @@ top: { hclResult = &Object{ Type: ValueTypeObject, - Value: ObjectList($1).Map(), + Value: ObjectList($1).Flat(), } } @@ -55,7 +55,7 @@ object: { $$ = &Object{ Type: ValueTypeObject, - Value: ObjectList($2).Map(), + Value: ObjectList($2).Flat(), } } | LEFTBRACE RIGHTBRACE @@ -89,11 +89,8 @@ objectitem: } | IDENTIFIER EQUAL object { - $$ = &Object{ - Key: $1, - Type: ValueTypeObject, - Value: $3, - } + $3.Key = $1 + $$ = $3 } | IDENTIFIER EQUAL list { @@ -119,7 +116,7 @@ block: $$ = &Object{ Key: $1, Type: ValueTypeObject, - Value: map[string]*Object{$1: $2}, + Value: []*Object{$2}, } } diff --git a/hcl/y.go b/hcl/y.go index 5dd8338..a9b975a 100644 --- a/hcl/y.go +++ b/hcl/y.go @@ -54,7 +54,7 @@ const hclEofCode = 1 const hclErrCode = 2 const hclMaxDepth = 200 -//line parse.y:207 +//line parse.y:204 //line yacctab:1 var hclExca = []int{ @@ -361,7 +361,7 @@ hcldefault: { hclResult = &Object{ Type: ValueTypeObject, - Value: ObjectList(hclS[hclpt-0].objlist).Map(), + Value: ObjectList(hclS[hclpt-0].objlist).Flat(), } } case 2: @@ -379,7 +379,7 @@ hcldefault: { hclVAL.obj = &Object{ Type: ValueTypeObject, - Value: ObjectList(hclS[hclpt-1].objlist).Map(), + Value: ObjectList(hclS[hclpt-1].objlist).Flat(), } } case 5: @@ -416,14 +416,11 @@ hcldefault: case 9: //line parse.y:91 { - hclVAL.obj = &Object{ - Key: hclS[hclpt-2].str, - Type: ValueTypeObject, - Value: hclS[hclpt-0].obj, - } + hclS[hclpt-0].obj.Key = hclS[hclpt-2].str + hclVAL.obj = hclS[hclpt-0].obj } case 10: - //line parse.y:99 + //line parse.y:96 { hclVAL.obj = &Object{ Key: hclS[hclpt-2].str, @@ -432,62 +429,62 @@ hcldefault: } } case 11: - //line parse.y:107 + //line parse.y:104 { hclVAL.obj = hclS[hclpt-0].obj } case 12: - //line parse.y:113 + //line parse.y:110 { hclS[hclpt-0].obj.Key = hclS[hclpt-1].str hclVAL.obj = hclS[hclpt-0].obj } case 13: - //line parse.y:118 + //line parse.y:115 { hclVAL.obj = &Object{ Key: hclS[hclpt-1].str, Type: ValueTypeObject, - Value: map[string]*Object{hclS[hclpt-1].str: hclS[hclpt-0].obj}, + Value: []*Object{hclS[hclpt-0].obj}, } } case 14: - //line parse.y:128 + //line parse.y:125 { hclVAL.str = hclS[hclpt-0].str } case 15: - //line parse.y:132 + //line parse.y:129 { hclVAL.str = hclS[hclpt-0].str } case 16: - //line parse.y:138 + //line parse.y:135 { hclVAL.objlist = hclS[hclpt-1].objlist } case 17: - //line parse.y:142 + //line parse.y:139 { hclVAL.objlist = nil } case 18: - //line parse.y:148 + //line parse.y:145 { hclVAL.objlist = []*Object{hclS[hclpt-0].obj} } case 19: - //line parse.y:152 + //line parse.y:149 { hclVAL.objlist = append(hclS[hclpt-2].objlist, hclS[hclpt-0].obj) } case 20: - //line parse.y:158 + //line parse.y:155 { hclVAL.obj = hclS[hclpt-0].obj } case 21: - //line parse.y:162 + //line parse.y:159 { hclVAL.obj = &Object{ Type: ValueTypeString, @@ -495,7 +492,7 @@ hcldefault: } } case 22: - //line parse.y:171 + //line parse.y:168 { hclVAL.obj = &Object{ Type: ValueTypeInt, @@ -503,7 +500,7 @@ hcldefault: } } case 23: - //line parse.y:178 + //line parse.y:175 { fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str) f, err := strconv.ParseFloat(fs, 64) @@ -517,17 +514,17 @@ hcldefault: } } case 24: - //line parse.y:193 + //line parse.y:190 { hclVAL.num = hclS[hclpt-0].num * -1 } case 25: - //line parse.y:197 + //line parse.y:194 { hclVAL.num = hclS[hclpt-0].num } case 26: - //line parse.y:203 + //line parse.y:200 { hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10) } diff --git a/json/parse.y b/json/parse.y index 66a193a..8d3a7e0 100644 --- a/json/parse.y +++ b/json/parse.y @@ -42,7 +42,7 @@ object: { $$ = &hcl.Object{ Type: hcl.ValueTypeObject, - Value: hcl.ObjectList($2).Map(), + Value: hcl.ObjectList($2).Flat(), } } | LEFTBRACE RIGHTBRACE diff --git a/json/y.go b/json/y.go index 44d960b..c4a6090 100644 --- a/json/y.go +++ b/json/y.go @@ -372,7 +372,7 @@ jsondefault: { jsonVAL.obj = &hcl.Object{ Type: hcl.ValueTypeObject, - Value: hcl.ObjectList(jsonS[jsonpt-1].objlist).Map(), + Value: hcl.ObjectList(jsonS[jsonpt-1].objlist).Flat(), } } case 3: diff --git a/test-fixtures/structure2.json b/test-fixtures/structure2.json index b275c25..d133b1e 100644 --- a/test-fixtures/structure2.json +++ b/test-fixtures/structure2.json @@ -1,10 +1,10 @@ { - "foo": [{ - "baz": [{ + "foo": { + "baz": { "key": 7, "foo": "bar" - }] - }, { + }, + "key": 7 - }] + } } diff --git a/test-fixtures/structure_multi.json b/test-fixtures/structure_multi.json index 69b7337..773761a 100644 --- a/test-fixtures/structure_multi.json +++ b/test-fixtures/structure_multi.json @@ -1,11 +1,11 @@ { - "foo": [{ - "baz": [{ + "foo": { + "baz": { "key": 7 - }] - }, { - "bar": [{ + }, + + "bar": { "key": 12 - }] - }] + } + } }