Start the decoder
This commit is contained in:
parent
3a02b49a51
commit
59e0ca9d5a
29
ast/ast.go
29
ast/ast.go
@ -17,6 +17,13 @@ type Node interface {
|
|||||||
Accept(Visitor)
|
Accept(Visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KeyedNode is a node that has a key associated with it.
|
||||||
|
type KeyedNode interface {
|
||||||
|
Node
|
||||||
|
|
||||||
|
Key() string
|
||||||
|
}
|
||||||
|
|
||||||
// Visitor is the interface that must be implemented by any
|
// Visitor is the interface that must be implemented by any
|
||||||
// structures who want to be visited as part of the visitor pattern
|
// structures who want to be visited as part of the visitor pattern
|
||||||
// on the AST.
|
// on the AST.
|
||||||
@ -29,14 +36,14 @@ type Visitor interface {
|
|||||||
// be validated/removed at a semantic check, rather than at a
|
// be validated/removed at a semantic check, rather than at a
|
||||||
// syntax level.
|
// syntax level.
|
||||||
type ObjectNode struct {
|
type ObjectNode struct {
|
||||||
Key string
|
K string
|
||||||
Elem []Node
|
Elem []KeyedNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssignmentNode represents a direct assignment with an equals
|
// AssignmentNode represents a direct assignment with an equals
|
||||||
// sign.
|
// sign.
|
||||||
type AssignmentNode struct {
|
type AssignmentNode struct {
|
||||||
Key string
|
K string
|
||||||
Value Node
|
Value Node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,24 +59,32 @@ type LiteralNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n ObjectNode) Accept(v Visitor) {
|
func (n ObjectNode) Accept(v Visitor) {
|
||||||
|
v.Visit(n)
|
||||||
|
|
||||||
for _, e := range n.Elem {
|
for _, e := range n.Elem {
|
||||||
e.Accept(v)
|
e.Accept(v)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
v.Visit(n)
|
func (n ObjectNode) Key() string {
|
||||||
|
return n.K
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n AssignmentNode) Accept(v Visitor) {
|
func (n AssignmentNode) Accept(v Visitor) {
|
||||||
n.Value.Accept(v)
|
|
||||||
v.Visit(n)
|
v.Visit(n)
|
||||||
|
n.Value.Accept(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n AssignmentNode) Key() string {
|
||||||
|
return n.K
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n ListNode) Accept(v Visitor) {
|
func (n ListNode) Accept(v Visitor) {
|
||||||
|
v.Visit(n)
|
||||||
|
|
||||||
for _, e := range n.Elem {
|
for _, e := range n.Elem {
|
||||||
e.Accept(v)
|
e.Accept(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Visit(n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n LiteralNode) Accept(v Visitor) {
|
func (n LiteralNode) Accept(v Visitor) {
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
|
|
||||||
func TestAssignmentNode_accept(t *testing.T) {
|
func TestAssignmentNode_accept(t *testing.T) {
|
||||||
n := AssignmentNode{
|
n := AssignmentNode{
|
||||||
Key: "foo",
|
K: "foo",
|
||||||
Value: LiteralNode{Value: "foo"},
|
Value: LiteralNode{Value: "foo"},
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := []Node{
|
expected := []Node{
|
||||||
n.Value,
|
|
||||||
n,
|
n,
|
||||||
|
n.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
v := new(MockVisitor)
|
v := new(MockVisitor)
|
||||||
@ -33,9 +33,9 @@ func TestListNode_accept(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expected := []Node{
|
expected := []Node{
|
||||||
|
n,
|
||||||
n.Elem[0],
|
n.Elem[0],
|
||||||
n.Elem[1],
|
n.Elem[1],
|
||||||
n,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v := new(MockVisitor)
|
v := new(MockVisitor)
|
||||||
@ -48,17 +48,19 @@ func TestListNode_accept(t *testing.T) {
|
|||||||
|
|
||||||
func TestObjectNode_accept(t *testing.T) {
|
func TestObjectNode_accept(t *testing.T) {
|
||||||
n := ObjectNode{
|
n := ObjectNode{
|
||||||
Key: "foo",
|
K: "foo",
|
||||||
Elem: []Node{
|
Elem: []KeyedNode{
|
||||||
LiteralNode{Value: "foo"},
|
AssignmentNode{K: "foo", Value: LiteralNode{Value: "foo"}},
|
||||||
LiteralNode{Value: "bar"},
|
AssignmentNode{K: "bar", Value: LiteralNode{Value: "bar"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := []Node{
|
expected := []Node{
|
||||||
n.Elem[0],
|
|
||||||
n.Elem[1],
|
|
||||||
n,
|
n,
|
||||||
|
n.Elem[0],
|
||||||
|
n.Elem[0].(AssignmentNode).Value,
|
||||||
|
n.Elem[1],
|
||||||
|
n.Elem[1].(AssignmentNode).Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
v := new(MockVisitor)
|
v := new(MockVisitor)
|
||||||
|
144
decoder.go
Normal file
144
decoder.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package hcl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/hashicorp/hcl/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode reads the given input and decodes it into the structure
|
||||||
|
// given by `out`.
|
||||||
|
func Decode(out interface{}, in string) error {
|
||||||
|
obj, err := Parse(in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecodeAST(out, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(name string, n ast.Node, result reflect.Value) error {
|
||||||
|
switch result.Kind() {
|
||||||
|
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.String:
|
||||||
|
return decodeString(name, n, result)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s: unknown kind: %s", name, result.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInterface(name string, raw ast.Node, result reflect.Value) error {
|
||||||
|
var set reflect.Value
|
||||||
|
|
||||||
|
switch n := raw.(type) {
|
||||||
|
case ast.ObjectNode:
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
set = reflect.ValueOf(result)
|
||||||
|
case ast.LiteralNode:
|
||||||
|
switch n.Type {
|
||||||
|
case ast.ValueTypeString:
|
||||||
|
set = reflect.Indirect(reflect.New(reflect.TypeOf("")))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: unknown literal type: %s",
|
||||||
|
name, n.Type)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: cannot decode into interface: %T",
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the result to what its supposed to be, then reset
|
||||||
|
// result so we don't reflect into this method anymore.
|
||||||
|
result.Set(set)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMap(name string, raw ast.Node, result reflect.Value) error {
|
||||||
|
obj, ok := raw.(ast.ObjectNode)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%s: not an object type", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultType := result.Type()
|
||||||
|
resultElemType := resultType.Elem()
|
||||||
|
resultKeyType := resultType.Key()
|
||||||
|
if resultKeyType.Kind() != reflect.String {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: map must have string keys", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a map if it is nil
|
||||||
|
resultMap := result
|
||||||
|
if result.IsNil() {
|
||||||
|
resultMap = reflect.MakeMap(
|
||||||
|
reflect.MapOf(resultKeyType, resultElemType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through each element and decode it.
|
||||||
|
for _, elem := range obj.Elem {
|
||||||
|
n := elem.(ast.AssignmentNode)
|
||||||
|
|
||||||
|
// Make the field name
|
||||||
|
fieldName := fmt.Sprintf("%s[%s]", name, n.Key())
|
||||||
|
|
||||||
|
// Get the key/value as reflection values
|
||||||
|
key := reflect.ValueOf(n.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode!
|
||||||
|
if err := decode(fieldName, n.Value, val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value on the map
|
||||||
|
resultMap.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the final map
|
||||||
|
result.Set(resultMap)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeString(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.ValueTypeString:
|
||||||
|
println(n.Value.(string))
|
||||||
|
result.SetString(n.Value.(string))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s: unknown type %s", name, n.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
41
decoder_test.go
Normal file
41
decoder_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package hcl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
File string
|
||||||
|
Err bool
|
||||||
|
Out interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"basic.hcl",
|
||||||
|
false,
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var out map[string]interface{}
|
||||||
|
err = Decode(&out, string(d))
|
||||||
|
if (err != nil) != tc.Err {
|
||||||
|
t.Fatalf("Input: %s\n\nError: %s", tc.File, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(out, tc.Out) {
|
||||||
|
t.Fatalf("Input: %s\n\n%#v", tc.File, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
hcl/parse.y
26
hcl/parse.y
@ -11,14 +11,18 @@ import (
|
|||||||
|
|
||||||
%union {
|
%union {
|
||||||
list []ast.Node
|
list []ast.Node
|
||||||
|
klist []ast.KeyedNode
|
||||||
|
kitem ast.KeyedNode
|
||||||
listitem ast.Node
|
listitem ast.Node
|
||||||
num int
|
num int
|
||||||
obj ast.ObjectNode
|
obj ast.ObjectNode
|
||||||
str string
|
str string
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <list> list objectlist
|
%type <list> list
|
||||||
%type <listitem> listitem objectitem
|
%type <klist> objectlist
|
||||||
|
%type <kitem> objectitem
|
||||||
|
%type <listitem> listitem
|
||||||
%type <obj> block object
|
%type <obj> block object
|
||||||
%type <str> blockId
|
%type <str> blockId
|
||||||
|
|
||||||
@ -32,7 +36,7 @@ top:
|
|||||||
objectlist
|
objectlist
|
||||||
{
|
{
|
||||||
hclResult = &ast.ObjectNode{
|
hclResult = &ast.ObjectNode{
|
||||||
Key: "",
|
K: "",
|
||||||
Elem: $1,
|
Elem: $1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +44,7 @@ top:
|
|||||||
objectlist:
|
objectlist:
|
||||||
objectitem
|
objectitem
|
||||||
{
|
{
|
||||||
$$ = []ast.Node{$1}
|
$$ = []ast.KeyedNode{$1}
|
||||||
}
|
}
|
||||||
| objectitem objectlist
|
| objectitem objectlist
|
||||||
{
|
{
|
||||||
@ -61,7 +65,7 @@ objectitem:
|
|||||||
IDENTIFIER EQUAL NUMBER
|
IDENTIFIER EQUAL NUMBER
|
||||||
{
|
{
|
||||||
$$ = ast.AssignmentNode{
|
$$ = ast.AssignmentNode{
|
||||||
Key: $1,
|
K: $1,
|
||||||
Value: ast.LiteralNode{
|
Value: ast.LiteralNode{
|
||||||
Type: ast.ValueTypeInt,
|
Type: ast.ValueTypeInt,
|
||||||
Value: $3,
|
Value: $3,
|
||||||
@ -71,7 +75,7 @@ objectitem:
|
|||||||
| IDENTIFIER EQUAL STRING
|
| IDENTIFIER EQUAL STRING
|
||||||
{
|
{
|
||||||
$$ = ast.AssignmentNode{
|
$$ = ast.AssignmentNode{
|
||||||
Key: $1,
|
K: $1,
|
||||||
Value: ast.LiteralNode{
|
Value: ast.LiteralNode{
|
||||||
Type: ast.ValueTypeString,
|
Type: ast.ValueTypeString,
|
||||||
Value: $3,
|
Value: $3,
|
||||||
@ -81,14 +85,14 @@ objectitem:
|
|||||||
| IDENTIFIER EQUAL object
|
| IDENTIFIER EQUAL object
|
||||||
{
|
{
|
||||||
$$ = ast.AssignmentNode{
|
$$ = ast.AssignmentNode{
|
||||||
Key: $1,
|
K: $1,
|
||||||
Value: $3,
|
Value: $3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| IDENTIFIER EQUAL LEFTBRACKET list RIGHTBRACKET
|
| IDENTIFIER EQUAL LEFTBRACKET list RIGHTBRACKET
|
||||||
{
|
{
|
||||||
$$ = ast.AssignmentNode{
|
$$ = ast.AssignmentNode{
|
||||||
Key: $1,
|
K: $1,
|
||||||
Value: ast.ListNode{Elem: $4},
|
Value: ast.ListNode{Elem: $4},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,13 +105,13 @@ block:
|
|||||||
blockId object
|
blockId object
|
||||||
{
|
{
|
||||||
$$ = $2
|
$$ = $2
|
||||||
$$.Key = $1
|
$$.K = $1
|
||||||
}
|
}
|
||||||
| blockId block
|
| blockId block
|
||||||
{
|
{
|
||||||
$$ = ast.ObjectNode{
|
$$ = ast.ObjectNode{
|
||||||
Key: $1,
|
K: $1,
|
||||||
Elem: []ast.Node{$2},
|
Elem: []ast.KeyedNode{$2},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
94
hcl/y.go
94
hcl/y.go
@ -12,6 +12,8 @@ import (
|
|||||||
type hclSymType struct {
|
type hclSymType struct {
|
||||||
yys int
|
yys int
|
||||||
list []ast.Node
|
list []ast.Node
|
||||||
|
klist []ast.KeyedNode
|
||||||
|
kitem ast.KeyedNode
|
||||||
listitem ast.Node
|
listitem ast.Node
|
||||||
num int
|
num int
|
||||||
obj ast.ObjectNode
|
obj ast.ObjectNode
|
||||||
@ -47,7 +49,7 @@ const hclEofCode = 1
|
|||||||
const hclErrCode = 2
|
const hclErrCode = 2
|
||||||
const hclMaxDepth = 200
|
const hclMaxDepth = 200
|
||||||
|
|
||||||
//line parse.y:150
|
//line parse.y:154
|
||||||
|
|
||||||
//line yacctab:1
|
//line yacctab:1
|
||||||
var hclExca = []int{
|
var hclExca = []int{
|
||||||
@ -79,12 +81,12 @@ var hclPact = []int{
|
|||||||
}
|
}
|
||||||
var hclPgo = []int{
|
var hclPgo = []int{
|
||||||
|
|
||||||
0, 29, 4, 0, 28, 25, 12, 26, 5,
|
0, 29, 4, 28, 0, 25, 12, 26, 5,
|
||||||
}
|
}
|
||||||
var hclR1 = []int{
|
var hclR1 = []int{
|
||||||
|
|
||||||
0, 8, 2, 2, 6, 6, 4, 4, 4, 4,
|
0, 8, 2, 2, 6, 6, 3, 3, 3, 3,
|
||||||
4, 5, 5, 7, 7, 1, 1, 3, 3,
|
3, 5, 5, 7, 7, 1, 1, 4, 4,
|
||||||
}
|
}
|
||||||
var hclR2 = []int{
|
var hclR2 = []int{
|
||||||
|
|
||||||
@ -93,9 +95,9 @@ var hclR2 = []int{
|
|||||||
}
|
}
|
||||||
var hclChk = []int{
|
var hclChk = []int{
|
||||||
|
|
||||||
-1000, -8, -2, -4, 6, -5, -7, 9, -2, 7,
|
-1000, -8, -2, -3, 6, -5, -7, 9, -2, 7,
|
||||||
-6, -5, 10, 6, 4, 9, -6, 12, -2, 11,
|
-6, -5, 10, 6, 4, 9, -6, 12, -2, 11,
|
||||||
-1, -3, 4, 9, 11, 13, 5, -3,
|
-1, -4, 4, 9, 11, 13, 5, -4,
|
||||||
}
|
}
|
||||||
var hclDef = []int{
|
var hclDef = []int{
|
||||||
|
|
||||||
@ -342,38 +344,38 @@ hcldefault:
|
|||||||
switch hclnt {
|
switch hclnt {
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
//line parse.y:33
|
//line parse.y:37
|
||||||
{
|
{
|
||||||
hclResult = &ast.ObjectNode{
|
hclResult = &ast.ObjectNode{
|
||||||
Key: "",
|
K: "",
|
||||||
Elem: hclS[hclpt-0].list,
|
Elem: hclS[hclpt-0].klist,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
//line parse.y:42
|
|
||||||
{
|
|
||||||
hclVAL.list = []ast.Node{hclS[hclpt-0].listitem}
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
//line parse.y:46
|
//line parse.y:46
|
||||||
{
|
{
|
||||||
hclVAL.list = append(hclS[hclpt-0].list, hclS[hclpt-1].listitem)
|
hclVAL.klist = []ast.KeyedNode{hclS[hclpt-0].kitem}
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
//line parse.y:50
|
||||||
|
{
|
||||||
|
hclVAL.klist = append(hclS[hclpt-0].klist, hclS[hclpt-1].kitem)
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
//line parse.y:52
|
//line parse.y:56
|
||||||
{
|
{
|
||||||
hclVAL.obj = ast.ObjectNode{Elem: hclS[hclpt-1].list}
|
hclVAL.obj = ast.ObjectNode{Elem: hclS[hclpt-1].klist}
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
//line parse.y:56
|
//line parse.y:60
|
||||||
{
|
{
|
||||||
hclVAL.obj = ast.ObjectNode{}
|
hclVAL.obj = ast.ObjectNode{}
|
||||||
}
|
}
|
||||||
case 6:
|
case 6:
|
||||||
//line parse.y:62
|
//line parse.y:66
|
||||||
{
|
{
|
||||||
hclVAL.listitem = ast.AssignmentNode{
|
hclVAL.kitem = ast.AssignmentNode{
|
||||||
Key: hclS[hclpt-2].str,
|
K: hclS[hclpt-2].str,
|
||||||
Value: ast.LiteralNode{
|
Value: ast.LiteralNode{
|
||||||
Type: ast.ValueTypeInt,
|
Type: ast.ValueTypeInt,
|
||||||
Value: hclS[hclpt-0].num,
|
Value: hclS[hclpt-0].num,
|
||||||
@ -381,10 +383,10 @@ hcldefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 7:
|
case 7:
|
||||||
//line parse.y:72
|
//line parse.y:76
|
||||||
{
|
{
|
||||||
hclVAL.listitem = ast.AssignmentNode{
|
hclVAL.kitem = ast.AssignmentNode{
|
||||||
Key: hclS[hclpt-2].str,
|
K: hclS[hclpt-2].str,
|
||||||
Value: ast.LiteralNode{
|
Value: ast.LiteralNode{
|
||||||
Type: ast.ValueTypeString,
|
Type: ast.ValueTypeString,
|
||||||
Value: hclS[hclpt-0].str,
|
Value: hclS[hclpt-0].str,
|
||||||
@ -392,62 +394,62 @@ hcldefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 8:
|
case 8:
|
||||||
//line parse.y:82
|
//line parse.y:86
|
||||||
{
|
{
|
||||||
hclVAL.listitem = ast.AssignmentNode{
|
hclVAL.kitem = ast.AssignmentNode{
|
||||||
Key: hclS[hclpt-2].str,
|
K: hclS[hclpt-2].str,
|
||||||
Value: hclS[hclpt-0].obj,
|
Value: hclS[hclpt-0].obj,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 9:
|
case 9:
|
||||||
//line parse.y:89
|
//line parse.y:93
|
||||||
{
|
{
|
||||||
hclVAL.listitem = ast.AssignmentNode{
|
hclVAL.kitem = ast.AssignmentNode{
|
||||||
Key: hclS[hclpt-4].str,
|
K: hclS[hclpt-4].str,
|
||||||
Value: ast.ListNode{Elem: hclS[hclpt-1].list},
|
Value: ast.ListNode{Elem: hclS[hclpt-1].list},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 10:
|
case 10:
|
||||||
//line parse.y:96
|
//line parse.y:100
|
||||||
{
|
{
|
||||||
hclVAL.listitem = hclS[hclpt-0].obj
|
hclVAL.kitem = hclS[hclpt-0].obj
|
||||||
}
|
}
|
||||||
case 11:
|
case 11:
|
||||||
//line parse.y:102
|
//line parse.y:106
|
||||||
{
|
{
|
||||||
hclVAL.obj = hclS[hclpt-0].obj
|
hclVAL.obj = hclS[hclpt-0].obj
|
||||||
hclVAL.obj.Key = hclS[hclpt-1].str
|
hclVAL.obj.K = hclS[hclpt-1].str
|
||||||
}
|
}
|
||||||
case 12:
|
case 12:
|
||||||
//line parse.y:107
|
//line parse.y:111
|
||||||
{
|
{
|
||||||
hclVAL.obj = ast.ObjectNode{
|
hclVAL.obj = ast.ObjectNode{
|
||||||
Key: hclS[hclpt-1].str,
|
K: hclS[hclpt-1].str,
|
||||||
Elem: []ast.Node{hclS[hclpt-0].obj},
|
Elem: []ast.KeyedNode{hclS[hclpt-0].obj},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 13:
|
case 13:
|
||||||
//line parse.y:116
|
|
||||||
{
|
|
||||||
hclVAL.str = hclS[hclpt-0].str
|
|
||||||
}
|
|
||||||
case 14:
|
|
||||||
//line parse.y:120
|
//line parse.y:120
|
||||||
{
|
{
|
||||||
hclVAL.str = hclS[hclpt-0].str
|
hclVAL.str = hclS[hclpt-0].str
|
||||||
}
|
}
|
||||||
|
case 14:
|
||||||
|
//line parse.y:124
|
||||||
|
{
|
||||||
|
hclVAL.str = hclS[hclpt-0].str
|
||||||
|
}
|
||||||
case 15:
|
case 15:
|
||||||
//line parse.y:126
|
//line parse.y:130
|
||||||
{
|
{
|
||||||
hclVAL.list = []ast.Node{hclS[hclpt-0].listitem}
|
hclVAL.list = []ast.Node{hclS[hclpt-0].listitem}
|
||||||
}
|
}
|
||||||
case 16:
|
case 16:
|
||||||
//line parse.y:130
|
//line parse.y:134
|
||||||
{
|
{
|
||||||
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:136
|
//line parse.y:140
|
||||||
{
|
{
|
||||||
hclVAL.listitem = ast.LiteralNode{
|
hclVAL.listitem = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeInt,
|
Type: ast.ValueTypeInt,
|
||||||
@ -455,7 +457,7 @@ hcldefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 18:
|
case 18:
|
||||||
//line parse.y:143
|
//line parse.y:147
|
||||||
{
|
{
|
||||||
hclVAL.listitem = ast.LiteralNode{
|
hclVAL.listitem = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeString,
|
Type: ast.ValueTypeString,
|
||||||
|
4
hcl_test.go
Normal file
4
hcl_test.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package hcl
|
||||||
|
|
||||||
|
// This is the directory where our test fixtures are.
|
||||||
|
const fixtureDir = "./test-fixtures"
|
@ -13,6 +13,7 @@ import (
|
|||||||
array ast.ListNode
|
array ast.ListNode
|
||||||
assign ast.AssignmentNode
|
assign ast.AssignmentNode
|
||||||
item ast.Node
|
item ast.Node
|
||||||
|
klist []ast.KeyedNode
|
||||||
list []ast.Node
|
list []ast.Node
|
||||||
num int
|
num int
|
||||||
str string
|
str string
|
||||||
@ -22,7 +23,8 @@ import (
|
|||||||
%type <array> array
|
%type <array> array
|
||||||
%type <assign> pair
|
%type <assign> pair
|
||||||
%type <item> value
|
%type <item> value
|
||||||
%type <list> elements members
|
%type <klist> members
|
||||||
|
%type <list> elements
|
||||||
%type <obj> object
|
%type <obj> object
|
||||||
|
|
||||||
%token <num> NUMBER
|
%token <num> NUMBER
|
||||||
@ -52,7 +54,7 @@ object:
|
|||||||
members:
|
members:
|
||||||
pair
|
pair
|
||||||
{
|
{
|
||||||
$$ = []ast.Node{$1}
|
$$ = []ast.KeyedNode{$1}
|
||||||
}
|
}
|
||||||
| pair COMMA members
|
| pair COMMA members
|
||||||
{
|
{
|
||||||
@ -63,7 +65,7 @@ pair:
|
|||||||
STRING COLON value
|
STRING COLON value
|
||||||
{
|
{
|
||||||
$$ = ast.AssignmentNode{
|
$$ = ast.AssignmentNode{
|
||||||
Key: $1,
|
K: $1,
|
||||||
Value: $3,
|
Value: $3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
json/y.go
57
json/y.go
@ -14,6 +14,7 @@ type jsonSymType struct {
|
|||||||
array ast.ListNode
|
array ast.ListNode
|
||||||
assign ast.AssignmentNode
|
assign ast.AssignmentNode
|
||||||
item ast.Node
|
item ast.Node
|
||||||
|
klist []ast.KeyedNode
|
||||||
list []ast.Node
|
list []ast.Node
|
||||||
num int
|
num int
|
||||||
str string
|
str string
|
||||||
@ -57,7 +58,7 @@ const jsonEofCode = 1
|
|||||||
const jsonErrCode = 2
|
const jsonErrCode = 2
|
||||||
const jsonMaxDepth = 200
|
const jsonMaxDepth = 200
|
||||||
|
|
||||||
//line parse.y:136
|
//line parse.y:138
|
||||||
|
|
||||||
//line yacctab:1
|
//line yacctab:1
|
||||||
var jsonExca = []int{
|
var jsonExca = []int{
|
||||||
@ -89,12 +90,12 @@ var jsonPact = []int{
|
|||||||
}
|
}
|
||||||
var jsonPgo = []int{
|
var jsonPgo = []int{
|
||||||
|
|
||||||
0, 34, 32, 17, 0, 23, 29, 31,
|
0, 34, 32, 17, 23, 0, 29, 31,
|
||||||
}
|
}
|
||||||
var jsonR1 = []int{
|
var jsonR1 = []int{
|
||||||
|
|
||||||
0, 7, 6, 6, 5, 5, 2, 3, 3, 3,
|
0, 7, 6, 6, 4, 4, 2, 3, 3, 3,
|
||||||
3, 3, 3, 3, 1, 1, 4, 4,
|
3, 3, 3, 3, 1, 1, 5, 5,
|
||||||
}
|
}
|
||||||
var jsonR2 = []int{
|
var jsonR2 = []int{
|
||||||
|
|
||||||
@ -103,9 +104,9 @@ var jsonR2 = []int{
|
|||||||
}
|
}
|
||||||
var jsonChk = []int{
|
var jsonChk = []int{
|
||||||
|
|
||||||
-1000, -7, -6, 11, -5, 12, -2, 10, 12, 6,
|
-1000, -7, -6, 11, -4, 12, -2, 10, 12, 6,
|
||||||
5, -5, -3, 10, 4, -6, -1, 15, 16, 17,
|
5, -4, -3, 10, 4, -6, -1, 15, 16, 17,
|
||||||
13, 14, -4, -3, 14, 6, -4,
|
13, 14, -5, -3, 14, 6, -5,
|
||||||
}
|
}
|
||||||
var jsonDef = []int{
|
var jsonDef = []int{
|
||||||
|
|
||||||
@ -352,41 +353,41 @@ jsondefault:
|
|||||||
switch jsonnt {
|
switch jsonnt {
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
//line parse.y:37
|
//line parse.y:39
|
||||||
{
|
{
|
||||||
obj := jsonS[jsonpt-0].obj
|
obj := jsonS[jsonpt-0].obj
|
||||||
jsonResult = &obj
|
jsonResult = &obj
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
//line parse.y:44
|
//line parse.y:46
|
||||||
{
|
{
|
||||||
jsonVAL.obj = ast.ObjectNode{Elem: jsonS[jsonpt-1].list}
|
jsonVAL.obj = ast.ObjectNode{Elem: jsonS[jsonpt-1].klist}
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
//line parse.y:48
|
//line parse.y:50
|
||||||
{
|
{
|
||||||
jsonVAL.obj = ast.ObjectNode{}
|
jsonVAL.obj = ast.ObjectNode{}
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
//line parse.y:54
|
//line parse.y:56
|
||||||
{
|
{
|
||||||
jsonVAL.list = []ast.Node{jsonS[jsonpt-0].assign}
|
jsonVAL.klist = []ast.KeyedNode{jsonS[jsonpt-0].assign}
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
//line parse.y:58
|
//line parse.y:60
|
||||||
{
|
{
|
||||||
jsonVAL.list = append(jsonS[jsonpt-0].list, jsonS[jsonpt-2].assign)
|
jsonVAL.klist = append(jsonS[jsonpt-0].klist, jsonS[jsonpt-2].assign)
|
||||||
}
|
}
|
||||||
case 6:
|
case 6:
|
||||||
//line parse.y:64
|
//line parse.y:66
|
||||||
{
|
{
|
||||||
jsonVAL.assign = ast.AssignmentNode{
|
jsonVAL.assign = ast.AssignmentNode{
|
||||||
Key: jsonS[jsonpt-2].str,
|
K: jsonS[jsonpt-2].str,
|
||||||
Value: jsonS[jsonpt-0].item,
|
Value: jsonS[jsonpt-0].item,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 7:
|
case 7:
|
||||||
//line parse.y:73
|
//line parse.y:75
|
||||||
{
|
{
|
||||||
jsonVAL.item = ast.LiteralNode{
|
jsonVAL.item = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeString,
|
Type: ast.ValueTypeString,
|
||||||
@ -394,7 +395,7 @@ jsondefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 8:
|
case 8:
|
||||||
//line parse.y:80
|
//line parse.y:82
|
||||||
{
|
{
|
||||||
jsonVAL.item = ast.LiteralNode{
|
jsonVAL.item = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeInt,
|
Type: ast.ValueTypeInt,
|
||||||
@ -402,17 +403,17 @@ jsondefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 9:
|
case 9:
|
||||||
//line parse.y:87
|
//line parse.y:89
|
||||||
{
|
{
|
||||||
jsonVAL.item = jsonS[jsonpt-0].obj
|
jsonVAL.item = jsonS[jsonpt-0].obj
|
||||||
}
|
}
|
||||||
case 10:
|
case 10:
|
||||||
//line parse.y:91
|
//line parse.y:93
|
||||||
{
|
{
|
||||||
jsonVAL.item = jsonS[jsonpt-0].array
|
jsonVAL.item = jsonS[jsonpt-0].array
|
||||||
}
|
}
|
||||||
case 11:
|
case 11:
|
||||||
//line parse.y:95
|
//line parse.y:97
|
||||||
{
|
{
|
||||||
jsonVAL.item = ast.LiteralNode{
|
jsonVAL.item = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeBool,
|
Type: ast.ValueTypeBool,
|
||||||
@ -420,7 +421,7 @@ jsondefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 12:
|
case 12:
|
||||||
//line parse.y:102
|
//line parse.y:104
|
||||||
{
|
{
|
||||||
jsonVAL.item = ast.LiteralNode{
|
jsonVAL.item = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeBool,
|
Type: ast.ValueTypeBool,
|
||||||
@ -428,7 +429,7 @@ jsondefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 13:
|
case 13:
|
||||||
//line parse.y:109
|
//line parse.y:111
|
||||||
{
|
{
|
||||||
jsonVAL.item = ast.LiteralNode{
|
jsonVAL.item = ast.LiteralNode{
|
||||||
Type: ast.ValueTypeNil,
|
Type: ast.ValueTypeNil,
|
||||||
@ -436,22 +437,22 @@ jsondefault:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 14:
|
case 14:
|
||||||
//line parse.y:118
|
//line parse.y:120
|
||||||
{
|
{
|
||||||
jsonVAL.array = ast.ListNode{}
|
jsonVAL.array = ast.ListNode{}
|
||||||
}
|
}
|
||||||
case 15:
|
case 15:
|
||||||
//line parse.y:122
|
//line parse.y:124
|
||||||
{
|
{
|
||||||
jsonVAL.array = ast.ListNode{Elem: jsonS[jsonpt-1].list}
|
jsonVAL.array = ast.ListNode{Elem: jsonS[jsonpt-1].list}
|
||||||
}
|
}
|
||||||
case 16:
|
case 16:
|
||||||
//line parse.y:128
|
//line parse.y:130
|
||||||
{
|
{
|
||||||
jsonVAL.list = []ast.Node{jsonS[jsonpt-0].item}
|
jsonVAL.list = []ast.Node{jsonS[jsonpt-0].item}
|
||||||
}
|
}
|
||||||
case 17:
|
case 17:
|
||||||
//line parse.y:132
|
//line parse.y:134
|
||||||
{
|
{
|
||||||
jsonVAL.list = append(jsonS[jsonpt-0].list, jsonS[jsonpt-2].item)
|
jsonVAL.list = append(jsonS[jsonpt-0].list, jsonS[jsonpt-2].item)
|
||||||
}
|
}
|
||||||
|
1
test-fixtures/basic.hcl
Normal file
1
test-fixtures/basic.hcl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo = "bar"
|
Loading…
x
Reference in New Issue
Block a user