Decode structures

This commit is contained in:
Mitchell Hashimoto 2014-08-02 16:07:54 -07:00
parent 59e0ca9d5a
commit b2ba9e7b3e
5 changed files with 135 additions and 32 deletions

View File

@ -21,16 +21,20 @@ func Decode(out interface{}, in string) error {
// DecodeAST is a lower-level version of Decode. It decodes a // DecodeAST is a lower-level version of Decode. It decodes a
// raw AST into the given output. // raw AST into the given output.
func DecodeAST(out interface{}, obj *ast.ObjectNode) error { 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 { func decode(name string, n ast.Node, result reflect.Value) error {
switch result.Kind() { switch result.Kind() {
case reflect.Int:
return decodeInt(name, n, result)
case reflect.Interface: case reflect.Interface:
// When we see an interface, we make our own thing // When we see an interface, we make our own thing
return decodeInterface(name, n, result) return decodeInterface(name, n, result)
case reflect.Map: case reflect.Map:
return decodeMap(name, n, result) return decodeMap(name, n, result)
case reflect.Slice:
return decodeSlice(name, n, result)
case reflect.String: case reflect.String:
return decodeString(name, n, result) return decodeString(name, n, result)
default: default:
@ -40,15 +44,66 @@ func decode(name string, n ast.Node, result reflect.Value) error {
return nil 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 { func decodeInterface(name string, raw ast.Node, result reflect.Value) error {
var set reflect.Value var set reflect.Value
redecode := true
switch n := raw.(type) { switch n := raw.(type) {
case ast.ObjectNode: case ast.ObjectNode:
redecode = false
result := make(map[string]interface{}) 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) set = reflect.ValueOf(result)
case ast.LiteralNode: case ast.LiteralNode:
switch n.Type { switch n.Type {
case ast.ValueTypeInt:
var result int
set = reflect.Indirect(reflect.New(reflect.TypeOf(result)))
case ast.ValueTypeString: case ast.ValueTypeString:
set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) set = reflect.Indirect(reflect.New(reflect.TypeOf("")))
default: default:
@ -62,10 +117,12 @@ func decodeInterface(name string, raw ast.Node, result reflect.Value) error {
name, raw) name, raw)
} }
// Revisit the node so that we can use the newly instantiated if redecode {
// thing and populate it. // Revisit the node so that we can use the newly instantiated
if err := decode(name, raw, set); err != nil { // thing and populate it.
return err if err := decode(name, raw, set); err != nil {
return err
}
} }
// Set the result to what its supposed to be, then reset // 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) n := elem.(ast.AssignmentNode)
// Make the field name // 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 // Get the key/value as reflection values
key := reflect.ValueOf(n.Key()) key := reflect.ValueOf(n.Key())
@ -126,6 +183,19 @@ func decodeMap(name string, raw ast.Node, result reflect.Value) error {
return nil 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 { func decodeString(name string, raw ast.Node, result reflect.Value) error {
n, ok := raw.(ast.LiteralNode) n, ok := raw.(ast.LiteralNode)
if !ok { if !ok {
@ -134,7 +204,6 @@ func decodeString(name string, raw ast.Node, result reflect.Value) error {
switch n.Type { switch n.Type {
case ast.ValueTypeString: case ast.ValueTypeString:
println(n.Value.(string))
result.SetString(n.Value.(string)) result.SetString(n.Value.(string))
default: default:
return fmt.Errorf("%s: unknown type %s", name, n.Type) return fmt.Errorf("%s: unknown type %s", name, n.Type)

View File

@ -20,6 +20,13 @@ func TestDecode(t *testing.T) {
"foo": "bar", "foo": "bar",
}, },
}, },
{
"structure.hcl",
false,
map[string]interface{}{
"foo": "bar",
},
},
} }
for _, tc := range cases { for _, tc := range cases {

View File

@ -21,9 +21,9 @@ import (
%type <list> list %type <list> list
%type <klist> objectlist %type <klist> objectlist
%type <kitem> objectitem %type <kitem> objectitem block
%type <listitem> listitem %type <listitem> listitem
%type <obj> block object %type <obj> object
%type <str> blockId %type <str> blockId
%token <num> NUMBER %token <num> NUMBER
@ -104,15 +104,26 @@ objectitem:
block: block:
blockId object blockId object
{ {
$$ = $2 $$ = ast.AssignmentNode{
$$.K = $1 K: $1,
Value: ast.ListNode{
Elem: []ast.Node{$2},
},
}
} }
| blockId block | blockId block
{ {
$$ = ast.ObjectNode{ obj := ast.ObjectNode{
K: $1, K: $2.Key(),
Elem: []ast.KeyedNode{$2}, Elem: []ast.KeyedNode{$2},
} }
$$ = ast.AssignmentNode{
K: $1,
Value: ast.ListNode{
Elem: []ast.Node{obj},
},
}
} }
blockId: blockId:

View File

@ -49,7 +49,7 @@ const hclEofCode = 1
const hclErrCode = 2 const hclErrCode = 2
const hclMaxDepth = 200 const hclMaxDepth = 200
//line parse.y:154 //line parse.y:165
//line yacctab:1 //line yacctab:1
var hclExca = []int{ var hclExca = []int{
@ -81,12 +81,12 @@ var hclPact = []int{
} }
var hclPgo = []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{ var hclR1 = []int{
0, 8, 2, 2, 6, 6, 3, 3, 3, 3, 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{ var hclR2 = []int{
@ -95,9 +95,9 @@ var hclR2 = []int{
} }
var hclChk = []int{ var hclChk = []int{
-1000, -8, -2, -3, 6, -5, -7, 9, -2, 7, -1000, -8, -2, -3, 6, -4, -7, 9, -2, 7,
-6, -5, 10, 6, 4, 9, -6, 12, -2, 11, -6, -4, 10, 6, 4, 9, -6, 12, -2, 11,
-1, -4, 4, 9, 11, 13, 5, -4, -1, -5, 4, 9, 11, 13, 5, -5,
} }
var hclDef = []int{ var hclDef = []int{
@ -412,44 +412,55 @@ hcldefault:
case 10: case 10:
//line parse.y:100 //line parse.y:100
{ {
hclVAL.kitem = hclS[hclpt-0].obj hclVAL.kitem = hclS[hclpt-0].kitem
} }
case 11: case 11:
//line parse.y:106 //line parse.y:106
{ {
hclVAL.obj = hclS[hclpt-0].obj hclVAL.kitem = ast.AssignmentNode{
hclVAL.obj.K = hclS[hclpt-1].str K: hclS[hclpt-1].str,
Value: ast.ListNode{
Elem: []ast.Node{hclS[hclpt-0].obj},
},
}
} }
case 12: case 12:
//line parse.y:111 //line parse.y:115
{ {
hclVAL.obj = ast.ObjectNode{ obj := ast.ObjectNode{
K: hclS[hclpt-1].str, K: hclS[hclpt-0].kitem.Key(),
Elem: []ast.KeyedNode{hclS[hclpt-0].obj}, 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: case 13:
//line parse.y:120 //line parse.y:131
{ {
hclVAL.str = hclS[hclpt-0].str hclVAL.str = hclS[hclpt-0].str
} }
case 14: case 14:
//line parse.y:124 //line parse.y:135
{ {
hclVAL.str = hclS[hclpt-0].str hclVAL.str = hclS[hclpt-0].str
} }
case 15: case 15:
//line parse.y:130 //line parse.y:141
{ {
hclVAL.list = []ast.Node{hclS[hclpt-0].listitem} hclVAL.list = []ast.Node{hclS[hclpt-0].listitem}
} }
case 16: case 16:
//line parse.y:134 //line parse.y:145
{ {
hclVAL.list = append(hclS[hclpt-2].list, hclS[hclpt-0].listitem) hclVAL.list = append(hclS[hclpt-2].list, hclS[hclpt-0].listitem)
} }
case 17: case 17:
//line parse.y:140 //line parse.y:151
{ {
hclVAL.listitem = ast.LiteralNode{ hclVAL.listitem = ast.LiteralNode{
Type: ast.ValueTypeInt, Type: ast.ValueTypeInt,
@ -457,7 +468,7 @@ hcldefault:
} }
} }
case 18: case 18:
//line parse.y:147 //line parse.y:158
{ {
hclVAL.listitem = ast.LiteralNode{ hclVAL.listitem = ast.LiteralNode{
Type: ast.ValueTypeString, Type: ast.ValueTypeString,

View File

@ -0,0 +1,5 @@
// This is a test structure for the lexer
foo bar "baz" {
key = 7
foo = "bar"
}