hcl: use new Object structure

This commit is contained in:
Mitchell Hashimoto 2014-08-11 16:13:24 -07:00
parent a4f0365393
commit a3b46cc5d6
7 changed files with 191 additions and 418 deletions

View File

@ -1,134 +0,0 @@
package ast
import (
"strings"
)
// ValueType is an enum represnting the type of a value in
// a LiteralNode.
type ValueType byte
const (
ValueTypeUnknown ValueType = iota
ValueTypeFloat
ValueTypeInt
ValueTypeString
ValueTypeBool
ValueTypeNil
)
// Node is implemented by all AST nodes for HCL.
type Node interface {
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
// structures who want to be visited as part of the visitor pattern
// on the AST.
type Visitor interface {
Visit(Node)
}
// ObjectNode represents an object that has multiple elements.
// An object's elements may repeat (keys). This is expected to
// be validated/removed at a semantic check, rather than at a
// syntax level.
type ObjectNode struct {
K string
Elem []AssignmentNode
}
// AssignmentNode represents a direct assignment with an equals
// sign.
type AssignmentNode struct {
K string
Value Node
}
// ListNode represents a list or array of items.
type ListNode struct {
Elem []Node
}
// LiteralNode is a direct value.
type LiteralNode struct {
Type ValueType
Value interface{}
}
func (n ObjectNode) Accept(v Visitor) {
v.Visit(n)
for _, e := range n.Elem {
e.Accept(v)
}
}
// Get returns all the elements of this object with the given key.
// This is a case-sensitive search.
func (n ObjectNode) Get(k string, insensitive bool) []Node {
result := make([]Node, 0, 1)
for _, elem := range n.Elem {
if elem.Key() != k {
if !insensitive || !strings.EqualFold(elem.Key(), k) {
continue
}
}
result = append(result, elem.Value)
}
return result
}
// Key returns the key of this object. If this is "", then it is
// the root object.
func (n ObjectNode) Key() string {
return n.K
}
// Len returns the number of elements of this object.
func (n ObjectNode) Len() int {
return len(n.Elem)
}
func (n AssignmentNode) Accept(v Visitor) {
v.Visit(n)
n.Value.Accept(v)
}
func (n AssignmentNode) Key() string {
return n.K
}
func (n ListNode) Accept(v Visitor) {
v.Visit(n)
for _, e := range n.Elem {
e.Accept(v)
}
}
func (n ListNode) Flatten() ListNode {
var elem []Node
for _, e := range n.Elem {
if ln, ok := e.(ListNode); ok {
elem = append(elem, ln.Flatten().Elem...)
} else {
elem = append(elem, e)
}
}
return ListNode{Elem: elem}
}
func (n LiteralNode) Accept(v Visitor) {
v.Visit(n)
}

View File

@ -1,94 +0,0 @@
package ast
import (
"reflect"
"testing"
)
func TestAssignmentNode_accept(t *testing.T) {
n := AssignmentNode{
K: "foo",
Value: LiteralNode{Value: "foo"},
}
expected := []Node{
n,
n.Value,
}
v := new(MockVisitor)
n.Accept(v)
if !reflect.DeepEqual(v.Nodes, expected) {
t.Fatalf("bad: %#v", v.Nodes)
}
}
func TestListNode_accept(t *testing.T) {
n := ListNode{
Elem: []Node{
LiteralNode{Value: "foo"},
LiteralNode{Value: "bar"},
},
}
expected := []Node{
n,
n.Elem[0],
n.Elem[1],
}
v := new(MockVisitor)
n.Accept(v)
if !reflect.DeepEqual(v.Nodes, expected) {
t.Fatalf("bad: %#v", v.Nodes)
}
}
func TestObjectNode_accept(t *testing.T) {
n := ObjectNode{
K: "foo",
Elem: []AssignmentNode{
AssignmentNode{K: "foo", Value: LiteralNode{Value: "foo"}},
AssignmentNode{K: "bar", Value: LiteralNode{Value: "bar"}},
},
}
expected := []Node{
n,
n.Elem[0],
n.Elem[0].Value,
n.Elem[1],
n.Elem[1].Value,
}
v := new(MockVisitor)
n.Accept(v)
if !reflect.DeepEqual(v.Nodes, expected) {
t.Fatalf("bad: %#v", v.Nodes)
}
}
func TestObjectNodeGet(t *testing.T) {
n := ObjectNode{
K: "foo",
Elem: []AssignmentNode{
AssignmentNode{K: "foo", Value: LiteralNode{Value: "foo"}},
AssignmentNode{K: "bar", Value: LiteralNode{Value: "bar"}},
AssignmentNode{K: "foo", Value: LiteralNode{Value: "baz"}},
},
}
expected := []Node{
LiteralNode{Value: "foo"},
LiteralNode{Value: "baz"},
}
actual := n.Get("foo", false)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}

View File

@ -1,11 +0,0 @@
package ast
// MockVisitor is a visitor implementation that can be used for tests
// and simply records the nodes that it has visited.
type MockVisitor struct {
Nodes []Node
}
func (v *MockVisitor) Visit(n Node) {
v.Nodes = append(v.Nodes, n)
}

47
hcl/object.go Normal file
View File

@ -0,0 +1,47 @@
package hcl
// ValueType is an enum represnting the type of a value in
// a LiteralNode.
type ValueType byte
const (
ValueTypeUnknown ValueType = iota
ValueTypeFloat
ValueTypeInt
ValueTypeString
ValueTypeBool
ValueTypeNil
ValueTypeList
ValueTypeObject
)
// Object represents any element of HCL: an object itself, a list,
// a literal, etc.
type Object struct {
Key string
Type ValueType
Value interface{}
Next *Object
}
// 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 {
m := make(map[string]*Object)
for _, obj := range l {
prev, ok := m[obj.Key]
if !ok {
m[obj.Key] = obj
continue
}
for prev.Next != nil {
prev = prev.Next
}
prev.Next = obj
}
return m
}

View File

@ -3,7 +3,6 @@ package hcl
import ( import (
"sync" "sync"
"github.com/hashicorp/hcl/ast"
"github.com/hashicorp/terraform/helper/multierror" "github.com/hashicorp/terraform/helper/multierror"
) )
@ -11,10 +10,10 @@ import (
// be accessed directly. // be accessed directly.
var hclErrors []error var hclErrors []error
var hclLock sync.Mutex var hclLock sync.Mutex
var hclResult *ast.ObjectNode var hclResult *Object
// Parse parses the given string and returns the result. // Parse parses the given string and returns the result.
func Parse(v string) (*ast.ObjectNode, error) { func Parse(v string) (*Object, error) {
hclLock.Lock() hclLock.Lock()
defer hclLock.Unlock() defer hclLock.Unlock()
hclErrors = nil hclErrors = nil

View File

@ -6,33 +6,22 @@ package hcl
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/hashicorp/hcl/ast"
) )
%} %}
%union { %union {
b bool b bool
item ast.Node
list ast.ListNode
alist []ast.AssignmentNode
aitem ast.AssignmentNode
listitem ast.Node
nodes []ast.Node
num int num int
obj ast.ObjectNode
str string str string
obj *Object
objlist []*Object
} }
%type <item> number
%type <list> list
%type <alist> objectlist
%type <aitem> objectitem block
%type <listitem> listitem
%type <nodes> listitems
%type <num> int %type <num> int
%type <obj> object %type <objlist> list listitems objectlist
%type <obj> block number object objectitem
%type <obj> listitem
%type <str> blockId frac %type <str> blockId frac
%token <b> BOOL %token <b> BOOL
@ -45,16 +34,16 @@ import (
top: top:
objectlist objectlist
{ {
hclResult = &ast.ObjectNode{ hclResult = &Object{
K: "", Type: ValueTypeObject,
Elem: $1, Value: ObjectList($1).Map(),
} }
} }
objectlist: objectlist:
objectitem objectitem
{ {
$$ = []ast.AssignmentNode{$1} $$ = []*Object{$1}
} }
| objectlist objectitem | objectlist objectitem
{ {
@ -64,52 +53,53 @@ objectlist:
object: object:
LEFTBRACE objectlist RIGHTBRACE LEFTBRACE objectlist RIGHTBRACE
{ {
$$ = ast.ObjectNode{Elem: $2} $$ = &Object{
Type: ValueTypeObject,
Value: ObjectList($2).Map(),
}
} }
| LEFTBRACE RIGHTBRACE | LEFTBRACE RIGHTBRACE
{ {
$$ = ast.ObjectNode{} $$ = &Object{
Type: ValueTypeObject,
}
} }
objectitem: objectitem:
IDENTIFIER EQUAL number IDENTIFIER EQUAL number
{ {
$$ = ast.AssignmentNode{ $$ = $3
K: $1, $$.Key = $1
Value: $3,
}
} }
| IDENTIFIER EQUAL BOOL | IDENTIFIER EQUAL BOOL
{ {
$$ = ast.AssignmentNode{ $$ = &Object{
K: $1, Key: $1,
Value: ast.LiteralNode{ Type: ValueTypeBool,
Type: ast.ValueTypeBool,
Value: $3, Value: $3,
},
} }
} }
| IDENTIFIER EQUAL STRING | IDENTIFIER EQUAL STRING
{ {
$$ = ast.AssignmentNode{ $$ = &Object{
K: $1, Key: $1,
Value: ast.LiteralNode{ Type: ValueTypeString,
Type: ast.ValueTypeString,
Value: $3, Value: $3,
},
} }
} }
| IDENTIFIER EQUAL object | IDENTIFIER EQUAL object
{ {
$$ = ast.AssignmentNode{ $$ = &Object{
K: $1, Key: $1,
Type: ValueTypeObject,
Value: $3, Value: $3,
} }
} }
| IDENTIFIER EQUAL list | IDENTIFIER EQUAL list
{ {
$$ = ast.AssignmentNode{ $$ = &Object{
K: $1, Key: $1,
Type: ValueTypeList,
Value: $3, Value: $3,
} }
} }
@ -121,22 +111,15 @@ objectitem:
block: block:
blockId object blockId object
{ {
$2.K = $1 $2.Key = $1
$$ = ast.AssignmentNode{ $$ = $2
K: $1,
Value: $2,
}
} }
| blockId block | blockId block
{ {
obj := ast.ObjectNode{ $$ = &Object{
K: $1, Key: $1,
Elem: []ast.AssignmentNode{$2}, Type: ValueTypeObject,
} Value: map[string]*Object{$1: $2},
$$ = ast.AssignmentNode{
K: $1,
Value: obj,
} }
} }
@ -153,19 +136,17 @@ blockId:
list: list:
LEFTBRACKET listitems RIGHTBRACKET LEFTBRACKET listitems RIGHTBRACKET
{ {
$$ = ast.ListNode{ $$ = $2
Elem: $2,
}
} }
| LEFTBRACKET RIGHTBRACKET | LEFTBRACKET RIGHTBRACKET
{ {
$$ = ast.ListNode{} $$ = nil
} }
listitems: listitems:
listitem listitem
{ {
$$ = []ast.Node{$1} $$ = []*Object{$1}
} }
| listitems COMMA listitem | listitems COMMA listitem
{ {
@ -179,8 +160,8 @@ listitem:
} }
| STRING | STRING
{ {
$$ = ast.LiteralNode{ $$ = &Object{
Type: ast.ValueTypeString, Type: ValueTypeString,
Value: $1, Value: $1,
} }
} }
@ -188,8 +169,8 @@ listitem:
number: number:
int int
{ {
$$ = ast.LiteralNode{ $$ = &Object{
Type: ast.ValueTypeInt, Type: ValueTypeInt,
Value: $1, Value: $1,
} }
} }
@ -201,8 +182,8 @@ number:
panic(err) panic(err)
} }
$$ = ast.LiteralNode{ $$ = &Object{
Type: ast.ValueTypeFloat, Type: ValueTypeFloat,
Value: f, Value: f,
} }
} }

181
hcl/y.go
View File

@ -7,23 +7,16 @@ import __yyfmt__ "fmt"
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/hashicorp/hcl/ast"
) )
//line parse.y:15 //line parse.y:13
type hclSymType struct { type hclSymType struct {
yys int yys int
b bool b bool
item ast.Node
list ast.ListNode
alist []ast.AssignmentNode
aitem ast.AssignmentNode
listitem ast.Node
nodes []ast.Node
num int num int
obj ast.ObjectNode
str string str string
obj *Object
objlist []*Object
} }
const BOOL = 57346 const BOOL = 57346
@ -61,7 +54,7 @@ const hclEofCode = 1
const hclErrCode = 2 const hclErrCode = 2
const hclMaxDepth = 200 const hclMaxDepth = 200
//line parse.y:226 //line parse.y:207
//line yacctab:1 //line yacctab:1
var hclExca = []int{ var hclExca = []int{
@ -96,14 +89,14 @@ var hclPact = []int{
} }
var hclPgo = []int{ var hclPgo = []int{
0, 41, 47, 10, 1, 43, 0, 46, 2, 39, 0, 2, 47, 46, 10, 43, 41, 39, 1, 0,
45, 44, 18, 45, 44, 18,
} }
var hclR1 = []int{ var hclR1 = []int{
0, 12, 3, 3, 9, 9, 4, 4, 4, 4, 0, 12, 4, 4, 7, 7, 8, 8, 8, 8,
4, 4, 5, 5, 10, 10, 2, 2, 7, 7, 8, 8, 5, 5, 10, 10, 2, 2, 3, 3,
6, 6, 1, 1, 8, 8, 11, 9, 9, 6, 6, 1, 1, 11,
} }
var hclR2 = []int{ var hclR2 = []int{
@ -113,10 +106,10 @@ var hclR2 = []int{
} }
var hclChk = []int{ var hclChk = []int{
-1000, -12, -3, -4, 7, -5, -10, 10, -4, 8, -1000, -12, -4, -8, 7, -5, -10, 10, -8, 8,
-9, -5, 12, 7, -1, 4, 10, -9, -2, -8, -7, -5, 12, 7, -6, 4, 10, -7, -2, -1,
14, 11, 5, -3, 13, -11, 16, -7, 15, -6, 14, 11, 5, -4, 13, -11, 16, -3, 15, -9,
-1, 10, -8, 13, 5, 15, 6, -6, -6, 10, -1, 13, 5, 15, 6, -9,
} }
var hclDef = []int{ var hclDef = []int{
@ -364,161 +357,153 @@ hcldefault:
switch hclnt { switch hclnt {
case 1: case 1:
//line parse.y:47 //line parse.y:36
{ {
hclResult = &ast.ObjectNode{ hclResult = &Object{
K: "", Type: ValueTypeObject,
Elem: hclS[hclpt-0].alist, Value: ObjectList(hclS[hclpt-0].objlist).Map(),
} }
} }
case 2: case 2:
//line parse.y:56 //line parse.y:45
{ {
hclVAL.alist = []ast.AssignmentNode{hclS[hclpt-0].aitem} hclVAL.objlist = []*Object{hclS[hclpt-0].obj}
} }
case 3: case 3:
//line parse.y:60 //line parse.y:49
{ {
hclVAL.alist = append(hclS[hclpt-1].alist, hclS[hclpt-0].aitem) hclVAL.objlist = append(hclS[hclpt-1].objlist, hclS[hclpt-0].obj)
} }
case 4: case 4:
//line parse.y:66 //line parse.y:55
{ {
hclVAL.obj = ast.ObjectNode{Elem: hclS[hclpt-1].alist} hclVAL.obj = &Object{
Type: ValueTypeObject,
Value: ObjectList(hclS[hclpt-1].objlist).Map(),
}
} }
case 5: case 5:
//line parse.y:70 //line parse.y:62
{ {
hclVAL.obj = ast.ObjectNode{} hclVAL.obj = &Object{
Type: ValueTypeObject,
}
} }
case 6: case 6:
//line parse.y:76 //line parse.y:70
{ {
hclVAL.aitem = ast.AssignmentNode{ hclVAL.obj = hclS[hclpt-0].obj
K: hclS[hclpt-2].str, hclVAL.obj.Key = hclS[hclpt-2].str
Value: hclS[hclpt-0].item,
}
} }
case 7: case 7:
//line parse.y:83 //line parse.y:75
{ {
hclVAL.aitem = ast.AssignmentNode{ hclVAL.obj = &Object{
K: hclS[hclpt-2].str, Key: hclS[hclpt-2].str,
Value: ast.LiteralNode{ Type: ValueTypeBool,
Type: ast.ValueTypeBool,
Value: hclS[hclpt-0].b, Value: hclS[hclpt-0].b,
},
} }
} }
case 8: case 8:
//line parse.y:93 //line parse.y:83
{ {
hclVAL.aitem = ast.AssignmentNode{ hclVAL.obj = &Object{
K: hclS[hclpt-2].str, Key: hclS[hclpt-2].str,
Value: ast.LiteralNode{ Type: ValueTypeString,
Type: ast.ValueTypeString,
Value: hclS[hclpt-0].str, Value: hclS[hclpt-0].str,
},
} }
} }
case 9: case 9:
//line parse.y:103 //line parse.y:91
{ {
hclVAL.aitem = ast.AssignmentNode{ hclVAL.obj = &Object{
K: hclS[hclpt-2].str, Key: hclS[hclpt-2].str,
Type: ValueTypeObject,
Value: hclS[hclpt-0].obj, Value: hclS[hclpt-0].obj,
} }
} }
case 10: case 10:
//line parse.y:110 //line parse.y:99
{ {
hclVAL.aitem = ast.AssignmentNode{ hclVAL.obj = &Object{
K: hclS[hclpt-2].str, Key: hclS[hclpt-2].str,
Value: hclS[hclpt-0].list, Type: ValueTypeList,
Value: hclS[hclpt-0].objlist,
} }
} }
case 11: case 11:
//line parse.y:117 //line parse.y:107
{ {
hclVAL.aitem = hclS[hclpt-0].aitem hclVAL.obj = hclS[hclpt-0].obj
} }
case 12: case 12:
//line parse.y:123 //line parse.y:113
{ {
hclS[hclpt-0].obj.K = hclS[hclpt-1].str hclS[hclpt-0].obj.Key = hclS[hclpt-1].str
hclVAL.aitem = ast.AssignmentNode{ hclVAL.obj = hclS[hclpt-0].obj
K: hclS[hclpt-1].str,
Value: hclS[hclpt-0].obj,
}
} }
case 13: case 13:
//line parse.y:131 //line parse.y:118
{ {
obj := ast.ObjectNode{ hclVAL.obj = &Object{
K: hclS[hclpt-1].str, Key: hclS[hclpt-1].str,
Elem: []ast.AssignmentNode{hclS[hclpt-0].aitem}, Type: ValueTypeObject,
} Value: map[string]*Object{hclS[hclpt-1].str: hclS[hclpt-0].obj},
hclVAL.aitem = ast.AssignmentNode{
K: hclS[hclpt-1].str,
Value: obj,
} }
} }
case 14: case 14:
//line parse.y:145 //line parse.y:128
{ {
hclVAL.str = hclS[hclpt-0].str hclVAL.str = hclS[hclpt-0].str
} }
case 15: case 15:
//line parse.y:149 //line parse.y:132
{ {
hclVAL.str = hclS[hclpt-0].str hclVAL.str = hclS[hclpt-0].str
} }
case 16: case 16:
//line parse.y:155 //line parse.y:138
{ {
hclVAL.list = ast.ListNode{ hclVAL.objlist = hclS[hclpt-1].objlist
Elem: hclS[hclpt-1].nodes,
}
} }
case 17: case 17:
//line parse.y:161 //line parse.y:142
{ {
hclVAL.list = ast.ListNode{} hclVAL.objlist = nil
} }
case 18: case 18:
//line parse.y:167 //line parse.y:148
{ {
hclVAL.nodes = []ast.Node{hclS[hclpt-0].listitem} hclVAL.objlist = []*Object{hclS[hclpt-0].obj}
} }
case 19: case 19:
//line parse.y:171 //line parse.y:152
{ {
hclVAL.nodes = append(hclS[hclpt-2].nodes, hclS[hclpt-0].listitem) hclVAL.objlist = append(hclS[hclpt-2].objlist, hclS[hclpt-0].obj)
} }
case 20: case 20:
//line parse.y:177 //line parse.y:158
{ {
hclVAL.listitem = hclS[hclpt-0].item hclVAL.obj = hclS[hclpt-0].obj
} }
case 21: case 21:
//line parse.y:181 //line parse.y:162
{ {
hclVAL.listitem = ast.LiteralNode{ hclVAL.obj = &Object{
Type: ast.ValueTypeString, Type: ValueTypeString,
Value: hclS[hclpt-0].str, Value: hclS[hclpt-0].str,
} }
} }
case 22: case 22:
//line parse.y:190 //line parse.y:171
{ {
hclVAL.item = ast.LiteralNode{ hclVAL.obj = &Object{
Type: ast.ValueTypeInt, Type: ValueTypeInt,
Value: hclS[hclpt-0].num, Value: hclS[hclpt-0].num,
} }
} }
case 23: case 23:
//line parse.y:197 //line parse.y:178
{ {
fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str) fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str)
f, err := strconv.ParseFloat(fs, 64) f, err := strconv.ParseFloat(fs, 64)
@ -526,23 +511,23 @@ hcldefault:
panic(err) panic(err)
} }
hclVAL.item = ast.LiteralNode{ hclVAL.obj = &Object{
Type: ast.ValueTypeFloat, Type: ValueTypeFloat,
Value: f, Value: f,
} }
} }
case 24: case 24:
//line parse.y:212 //line parse.y:193
{ {
hclVAL.num = hclS[hclpt-0].num * -1 hclVAL.num = hclS[hclpt-0].num * -1
} }
case 25: case 25:
//line parse.y:216 //line parse.y:197
{ {
hclVAL.num = hclS[hclpt-0].num hclVAL.num = hclS[hclpt-0].num
} }
case 26: case 26:
//line parse.y:222 //line parse.y:203
{ {
hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10) hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10)
} }