diff --git a/decoder.go b/decoder.go index cd26932..51e5a93 100644 --- a/decoder.go +++ b/decoder.go @@ -21,16 +21,20 @@ func Decode(out interface{}, in string) error { // DecodeAST is a lower-level version of Decode. It decodes a // raw AST into the given output. func DecodeAST(out interface{}, obj *ast.ObjectNode) error { - return decode("", *obj, reflect.ValueOf(out).Elem()) + return decode("root", *obj, reflect.ValueOf(out).Elem()) } func decode(name string, n ast.Node, result reflect.Value) error { switch result.Kind() { + case reflect.Int: + return decodeInt(name, n, result) case reflect.Interface: // When we see an interface, we make our own thing return decodeInterface(name, n, result) case reflect.Map: return decodeMap(name, n, result) + case reflect.Slice: + return decodeSlice(name, n, result) case reflect.String: return decodeString(name, n, result) default: @@ -40,15 +44,66 @@ func decode(name string, n ast.Node, result reflect.Value) error { return nil } +func decodeInt(name string, raw ast.Node, result reflect.Value) error { + n, ok := raw.(ast.LiteralNode) + if !ok { + return fmt.Errorf("%s: not a literal type", name) + } + + switch n.Type { + case ast.ValueTypeInt: + result.SetInt(int64(n.Value.(int))) + default: + return fmt.Errorf("%s: unknown type %s", name, n.Type) + } + + return nil +} + func decodeInterface(name string, raw ast.Node, result reflect.Value) error { var set reflect.Value + redecode := true switch n := raw.(type) { case ast.ObjectNode: + redecode = false result := make(map[string]interface{}) + + for _, elem := range n.Elem { + n := elem.(ast.AssignmentNode) + + raw := new(interface{}) + err := decode( + name, n.Value, reflect.Indirect(reflect.ValueOf(raw))) + if err != nil { + return err + } + + result[n.Key()] = *raw + } + + set = reflect.ValueOf(result) + case ast.ListNode: + redecode = false + result := make([]interface{}, 0, len(n.Elem)) + + for _, elem := range n.Elem { + raw := new(interface{}) + err := decode( + name, elem, reflect.Indirect(reflect.ValueOf(raw))) + if err != nil { + return err + } + + result = append(result, *raw) + } + set = reflect.ValueOf(result) case ast.LiteralNode: switch n.Type { + case ast.ValueTypeInt: + var result int + set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) case ast.ValueTypeString: set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) default: @@ -62,10 +117,12 @@ func decodeInterface(name string, raw ast.Node, result reflect.Value) error { name, raw) } - // Revisit the node so that we can use the newly instantiated - // thing and populate it. - if err := decode(name, raw, set); err != nil { - return err + if redecode { + // Revisit the node so that we can use the newly instantiated + // thing and populate it. + if err := decode(name, raw, set); err != nil { + return err + } } // Set the result to what its supposed to be, then reset @@ -100,7 +157,7 @@ func decodeMap(name string, raw ast.Node, result reflect.Value) error { n := elem.(ast.AssignmentNode) // Make the field name - fieldName := fmt.Sprintf("%s[%s]", name, n.Key()) + fieldName := fmt.Sprintf("%s.%s", name, n.Key()) // Get the key/value as reflection values key := reflect.ValueOf(n.Key()) @@ -126,6 +183,19 @@ func decodeMap(name string, raw ast.Node, result reflect.Value) error { return nil } +func decodeSlice(name string, raw ast.Node, result reflect.Value) error { + // Create the slice + resultType := result.Type() + resultElemType := resultType.Elem() + resultSliceType := reflect.SliceOf(resultElemType) + resultSlice := reflect.MakeSlice( + resultSliceType, 0, 0) + + result.Set(resultSlice) + + return nil +} + func decodeString(name string, raw ast.Node, result reflect.Value) error { n, ok := raw.(ast.LiteralNode) if !ok { @@ -134,7 +204,6 @@ func decodeString(name string, raw ast.Node, result reflect.Value) error { switch n.Type { case ast.ValueTypeString: - println(n.Value.(string)) result.SetString(n.Value.(string)) default: return fmt.Errorf("%s: unknown type %s", name, n.Type) diff --git a/decoder_test.go b/decoder_test.go index 72a008a..40e6a56 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -20,6 +20,13 @@ func TestDecode(t *testing.T) { "foo": "bar", }, }, + { + "structure.hcl", + false, + map[string]interface{}{ + "foo": "bar", + }, + }, } for _, tc := range cases { diff --git a/hcl/parse.y b/hcl/parse.y index 5b2d1f9..04f29fb 100644 --- a/hcl/parse.y +++ b/hcl/parse.y @@ -21,9 +21,9 @@ import ( %type list %type objectlist -%type objectitem +%type objectitem block %type listitem -%type block object +%type object %type blockId %token NUMBER @@ -104,15 +104,26 @@ objectitem: block: blockId object { - $$ = $2 - $$.K = $1 + $$ = ast.AssignmentNode{ + K: $1, + Value: ast.ListNode{ + Elem: []ast.Node{$2}, + }, + } } | blockId block { - $$ = ast.ObjectNode{ - K: $1, + obj := ast.ObjectNode{ + K: $2.Key(), Elem: []ast.KeyedNode{$2}, } + + $$ = ast.AssignmentNode{ + K: $1, + Value: ast.ListNode{ + Elem: []ast.Node{obj}, + }, + } } blockId: diff --git a/hcl/y.go b/hcl/y.go index 8591976..3a85945 100644 --- a/hcl/y.go +++ b/hcl/y.go @@ -49,7 +49,7 @@ const hclEofCode = 1 const hclErrCode = 2 const hclMaxDepth = 200 -//line parse.y:154 +//line parse.y:165 //line yacctab:1 var hclExca = []int{ @@ -81,12 +81,12 @@ var hclPact = []int{ } var hclPgo = []int{ - 0, 29, 4, 28, 0, 25, 12, 26, 5, + 0, 29, 4, 28, 25, 0, 12, 26, 5, } var hclR1 = []int{ 0, 8, 2, 2, 6, 6, 3, 3, 3, 3, - 3, 5, 5, 7, 7, 1, 1, 4, 4, + 3, 4, 4, 7, 7, 1, 1, 5, 5, } var hclR2 = []int{ @@ -95,9 +95,9 @@ var hclR2 = []int{ } var hclChk = []int{ - -1000, -8, -2, -3, 6, -5, -7, 9, -2, 7, - -6, -5, 10, 6, 4, 9, -6, 12, -2, 11, - -1, -4, 4, 9, 11, 13, 5, -4, + -1000, -8, -2, -3, 6, -4, -7, 9, -2, 7, + -6, -4, 10, 6, 4, 9, -6, 12, -2, 11, + -1, -5, 4, 9, 11, 13, 5, -5, } var hclDef = []int{ @@ -412,44 +412,55 @@ hcldefault: case 10: //line parse.y:100 { - hclVAL.kitem = hclS[hclpt-0].obj + hclVAL.kitem = hclS[hclpt-0].kitem } case 11: //line parse.y:106 { - hclVAL.obj = hclS[hclpt-0].obj - hclVAL.obj.K = hclS[hclpt-1].str + hclVAL.kitem = ast.AssignmentNode{ + K: hclS[hclpt-1].str, + Value: ast.ListNode{ + Elem: []ast.Node{hclS[hclpt-0].obj}, + }, + } } case 12: - //line parse.y:111 + //line parse.y:115 { - hclVAL.obj = ast.ObjectNode{ - K: hclS[hclpt-1].str, - Elem: []ast.KeyedNode{hclS[hclpt-0].obj}, + obj := ast.ObjectNode{ + K: hclS[hclpt-0].kitem.Key(), + Elem: []ast.KeyedNode{hclS[hclpt-0].kitem}, + } + + hclVAL.kitem = ast.AssignmentNode{ + K: hclS[hclpt-1].str, + Value: ast.ListNode{ + Elem: []ast.Node{obj}, + }, } } case 13: - //line parse.y:120 + //line parse.y:131 { hclVAL.str = hclS[hclpt-0].str } case 14: - //line parse.y:124 + //line parse.y:135 { hclVAL.str = hclS[hclpt-0].str } case 15: - //line parse.y:130 + //line parse.y:141 { hclVAL.list = []ast.Node{hclS[hclpt-0].listitem} } case 16: - //line parse.y:134 + //line parse.y:145 { hclVAL.list = append(hclS[hclpt-2].list, hclS[hclpt-0].listitem) } case 17: - //line parse.y:140 + //line parse.y:151 { hclVAL.listitem = ast.LiteralNode{ Type: ast.ValueTypeInt, @@ -457,7 +468,7 @@ hcldefault: } } case 18: - //line parse.y:147 + //line parse.y:158 { hclVAL.listitem = ast.LiteralNode{ Type: ast.ValueTypeString, diff --git a/test-fixtures/structure.hcl b/test-fixtures/structure.hcl new file mode 100644 index 0000000..92592fb --- /dev/null +++ b/test-fixtures/structure.hcl @@ -0,0 +1,5 @@ +// This is a test structure for the lexer +foo bar "baz" { + key = 7 + foo = "bar" +}