Decode structures
This commit is contained in:
parent
59e0ca9d5a
commit
b2ba9e7b3e
83
decoder.go
83
decoder.go
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
23
hcl/parse.y
23
hcl/parse.y
@ -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:
|
||||||
|
49
hcl/y.go
49
hcl/y.go
@ -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,
|
||||||
|
5
test-fixtures/structure.hcl
Normal file
5
test-fixtures/structure.hcl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// This is a test structure for the lexer
|
||||||
|
foo bar "baz" {
|
||||||
|
key = 7
|
||||||
|
foo = "bar"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user