diff --git a/ast/ast.go b/ast/ast.go deleted file mode 100644 index 84ebd5e..0000000 --- a/ast/ast.go +++ /dev/null @@ -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) -} diff --git a/ast/ast_test.go b/ast/ast_test.go deleted file mode 100644 index d4ff289..0000000 --- a/ast/ast_test.go +++ /dev/null @@ -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) - } -} diff --git a/ast/visitor_mock.go b/ast/visitor_mock.go deleted file mode 100644 index 483b03c..0000000 --- a/ast/visitor_mock.go +++ /dev/null @@ -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) -} diff --git a/hcl/object.go b/hcl/object.go new file mode 100644 index 0000000..1ca9482 --- /dev/null +++ b/hcl/object.go @@ -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 +} diff --git a/hcl/parse.go b/hcl/parse.go index 76dc456..7998674 100644 --- a/hcl/parse.go +++ b/hcl/parse.go @@ -3,7 +3,6 @@ package hcl import ( "sync" - "github.com/hashicorp/hcl/ast" "github.com/hashicorp/terraform/helper/multierror" ) @@ -11,10 +10,10 @@ import ( // be accessed directly. var hclErrors []error var hclLock sync.Mutex -var hclResult *ast.ObjectNode +var hclResult *Object // Parse parses the given string and returns the result. -func Parse(v string) (*ast.ObjectNode, error) { +func Parse(v string) (*Object, error) { hclLock.Lock() defer hclLock.Unlock() hclErrors = nil diff --git a/hcl/parse.y b/hcl/parse.y index 6a1ff21..b43be9b 100644 --- a/hcl/parse.y +++ b/hcl/parse.y @@ -6,33 +6,22 @@ package hcl import ( "fmt" "strconv" - - "github.com/hashicorp/hcl/ast" ) %} %union { b bool - item ast.Node - list ast.ListNode - alist []ast.AssignmentNode - aitem ast.AssignmentNode - listitem ast.Node - nodes []ast.Node num int - obj ast.ObjectNode str string + obj *Object + objlist []*Object } -%type number -%type list -%type objectlist -%type objectitem block -%type listitem -%type listitems %type int -%type object +%type list listitems objectlist +%type block number object objectitem +%type listitem %type blockId frac %token BOOL @@ -45,16 +34,16 @@ import ( top: objectlist { - hclResult = &ast.ObjectNode{ - K: "", - Elem: $1, + hclResult = &Object{ + Type: ValueTypeObject, + Value: ObjectList($1).Map(), } } objectlist: objectitem { - $$ = []ast.AssignmentNode{$1} + $$ = []*Object{$1} } | objectlist objectitem { @@ -64,52 +53,53 @@ objectlist: object: LEFTBRACE objectlist RIGHTBRACE { - $$ = ast.ObjectNode{Elem: $2} + $$ = &Object{ + Type: ValueTypeObject, + Value: ObjectList($2).Map(), + } } | LEFTBRACE RIGHTBRACE { - $$ = ast.ObjectNode{} + $$ = &Object{ + Type: ValueTypeObject, + } } objectitem: IDENTIFIER EQUAL number { - $$ = ast.AssignmentNode{ - K: $1, - Value: $3, - } + $$ = $3 + $$.Key = $1 } | IDENTIFIER EQUAL BOOL { - $$ = ast.AssignmentNode{ - K: $1, - Value: ast.LiteralNode{ - Type: ast.ValueTypeBool, - Value: $3, - }, + $$ = &Object{ + Key: $1, + Type: ValueTypeBool, + Value: $3, } } | IDENTIFIER EQUAL STRING { - $$ = ast.AssignmentNode{ - K: $1, - Value: ast.LiteralNode{ - Type: ast.ValueTypeString, - Value: $3, - }, + $$ = &Object{ + Key: $1, + Type: ValueTypeString, + Value: $3, } } | IDENTIFIER EQUAL object { - $$ = ast.AssignmentNode{ - K: $1, + $$ = &Object{ + Key: $1, + Type: ValueTypeObject, Value: $3, } } | IDENTIFIER EQUAL list { - $$ = ast.AssignmentNode{ - K: $1, + $$ = &Object{ + Key: $1, + Type: ValueTypeList, Value: $3, } } @@ -121,22 +111,15 @@ objectitem: block: blockId object { - $2.K = $1 - $$ = ast.AssignmentNode{ - K: $1, - Value: $2, - } + $2.Key = $1 + $$ = $2 } | blockId block { - obj := ast.ObjectNode{ - K: $1, - Elem: []ast.AssignmentNode{$2}, - } - - $$ = ast.AssignmentNode{ - K: $1, - Value: obj, + $$ = &Object{ + Key: $1, + Type: ValueTypeObject, + Value: map[string]*Object{$1: $2}, } } @@ -153,19 +136,17 @@ blockId: list: LEFTBRACKET listitems RIGHTBRACKET { - $$ = ast.ListNode{ - Elem: $2, - } + $$ = $2 } | LEFTBRACKET RIGHTBRACKET { - $$ = ast.ListNode{} + $$ = nil } listitems: listitem { - $$ = []ast.Node{$1} + $$ = []*Object{$1} } | listitems COMMA listitem { @@ -179,8 +160,8 @@ listitem: } | STRING { - $$ = ast.LiteralNode{ - Type: ast.ValueTypeString, + $$ = &Object{ + Type: ValueTypeString, Value: $1, } } @@ -188,8 +169,8 @@ listitem: number: int { - $$ = ast.LiteralNode{ - Type: ast.ValueTypeInt, + $$ = &Object{ + Type: ValueTypeInt, Value: $1, } } @@ -201,8 +182,8 @@ number: panic(err) } - $$ = ast.LiteralNode{ - Type: ast.ValueTypeFloat, + $$ = &Object{ + Type: ValueTypeFloat, Value: f, } } diff --git a/hcl/y.go b/hcl/y.go index f5e3cd6..5dd8338 100644 --- a/hcl/y.go +++ b/hcl/y.go @@ -7,23 +7,16 @@ import __yyfmt__ "fmt" import ( "fmt" "strconv" - - "github.com/hashicorp/hcl/ast" ) -//line parse.y:15 +//line parse.y:13 type hclSymType struct { - yys int - b bool - item ast.Node - list ast.ListNode - alist []ast.AssignmentNode - aitem ast.AssignmentNode - listitem ast.Node - nodes []ast.Node - num int - obj ast.ObjectNode - str string + yys int + b bool + num int + str string + obj *Object + objlist []*Object } const BOOL = 57346 @@ -61,7 +54,7 @@ const hclEofCode = 1 const hclErrCode = 2 const hclMaxDepth = 200 -//line parse.y:226 +//line parse.y:207 //line yacctab:1 var hclExca = []int{ @@ -96,14 +89,14 @@ var hclPact = []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, } var hclR1 = []int{ - 0, 12, 3, 3, 9, 9, 4, 4, 4, 4, - 4, 4, 5, 5, 10, 10, 2, 2, 7, 7, - 6, 6, 1, 1, 8, 8, 11, + 0, 12, 4, 4, 7, 7, 8, 8, 8, 8, + 8, 8, 5, 5, 10, 10, 2, 2, 3, 3, + 9, 9, 6, 6, 1, 1, 11, } var hclR2 = []int{ @@ -113,10 +106,10 @@ var hclR2 = []int{ } var hclChk = []int{ - -1000, -12, -3, -4, 7, -5, -10, 10, -4, 8, - -9, -5, 12, 7, -1, 4, 10, -9, -2, -8, - 14, 11, 5, -3, 13, -11, 16, -7, 15, -6, - -1, 10, -8, 13, 5, 15, 6, -6, + -1000, -12, -4, -8, 7, -5, -10, 10, -8, 8, + -7, -5, 12, 7, -6, 4, 10, -7, -2, -1, + 14, 11, 5, -4, 13, -11, 16, -3, 15, -9, + -6, 10, -1, 13, 5, 15, 6, -9, } var hclDef = []int{ @@ -364,161 +357,153 @@ hcldefault: switch hclnt { case 1: - //line parse.y:47 + //line parse.y:36 { - hclResult = &ast.ObjectNode{ - K: "", - Elem: hclS[hclpt-0].alist, + hclResult = &Object{ + Type: ValueTypeObject, + Value: ObjectList(hclS[hclpt-0].objlist).Map(), } } 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: - //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: - //line parse.y:66 + //line parse.y:55 { - hclVAL.obj = ast.ObjectNode{Elem: hclS[hclpt-1].alist} - } - case 5: - //line parse.y:70 - { - hclVAL.obj = ast.ObjectNode{} - } - case 6: - //line parse.y:76 - { - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-2].str, - Value: hclS[hclpt-0].item, + hclVAL.obj = &Object{ + Type: ValueTypeObject, + Value: ObjectList(hclS[hclpt-1].objlist).Map(), } } - case 7: - //line parse.y:83 + case 5: + //line parse.y:62 { - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-2].str, - Value: ast.LiteralNode{ - Type: ast.ValueTypeBool, - Value: hclS[hclpt-0].b, - }, + hclVAL.obj = &Object{ + Type: ValueTypeObject, + } + } + case 6: + //line parse.y:70 + { + hclVAL.obj = hclS[hclpt-0].obj + hclVAL.obj.Key = hclS[hclpt-2].str + } + case 7: + //line parse.y:75 + { + hclVAL.obj = &Object{ + Key: hclS[hclpt-2].str, + Type: ValueTypeBool, + Value: hclS[hclpt-0].b, } } case 8: - //line parse.y:93 + //line parse.y:83 { - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-2].str, - Value: ast.LiteralNode{ - Type: ast.ValueTypeString, - Value: hclS[hclpt-0].str, - }, + hclVAL.obj = &Object{ + Key: hclS[hclpt-2].str, + Type: ValueTypeString, + Value: hclS[hclpt-0].str, } } case 9: - //line parse.y:103 + //line parse.y:91 { - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-2].str, + hclVAL.obj = &Object{ + Key: hclS[hclpt-2].str, + Type: ValueTypeObject, Value: hclS[hclpt-0].obj, } } case 10: - //line parse.y:110 + //line parse.y:99 { - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-2].str, - Value: hclS[hclpt-0].list, + hclVAL.obj = &Object{ + Key: hclS[hclpt-2].str, + Type: ValueTypeList, + Value: hclS[hclpt-0].objlist, } } case 11: - //line parse.y:117 + //line parse.y:107 { - hclVAL.aitem = hclS[hclpt-0].aitem + hclVAL.obj = hclS[hclpt-0].obj } case 12: - //line parse.y:123 + //line parse.y:113 { - hclS[hclpt-0].obj.K = hclS[hclpt-1].str - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-1].str, - Value: hclS[hclpt-0].obj, - } + hclS[hclpt-0].obj.Key = hclS[hclpt-1].str + hclVAL.obj = hclS[hclpt-0].obj } case 13: - //line parse.y:131 + //line parse.y:118 { - obj := ast.ObjectNode{ - K: hclS[hclpt-1].str, - Elem: []ast.AssignmentNode{hclS[hclpt-0].aitem}, - } - - hclVAL.aitem = ast.AssignmentNode{ - K: hclS[hclpt-1].str, - Value: obj, + hclVAL.obj = &Object{ + Key: hclS[hclpt-1].str, + Type: ValueTypeObject, + Value: map[string]*Object{hclS[hclpt-1].str: hclS[hclpt-0].obj}, } } case 14: - //line parse.y:145 + //line parse.y:128 { hclVAL.str = hclS[hclpt-0].str } case 15: - //line parse.y:149 + //line parse.y:132 { hclVAL.str = hclS[hclpt-0].str } case 16: - //line parse.y:155 + //line parse.y:138 { - hclVAL.list = ast.ListNode{ - Elem: hclS[hclpt-1].nodes, - } + hclVAL.objlist = hclS[hclpt-1].objlist } case 17: - //line parse.y:161 + //line parse.y:142 { - hclVAL.list = ast.ListNode{} + hclVAL.objlist = nil } 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: - //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: - //line parse.y:177 + //line parse.y:158 { - hclVAL.listitem = hclS[hclpt-0].item + hclVAL.obj = hclS[hclpt-0].obj } case 21: - //line parse.y:181 + //line parse.y:162 { - hclVAL.listitem = ast.LiteralNode{ - Type: ast.ValueTypeString, + hclVAL.obj = &Object{ + Type: ValueTypeString, Value: hclS[hclpt-0].str, } } case 22: - //line parse.y:190 + //line parse.y:171 { - hclVAL.item = ast.LiteralNode{ - Type: ast.ValueTypeInt, + hclVAL.obj = &Object{ + Type: ValueTypeInt, Value: hclS[hclpt-0].num, } } case 23: - //line parse.y:197 + //line parse.y:178 { fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str) f, err := strconv.ParseFloat(fs, 64) @@ -526,23 +511,23 @@ hcldefault: panic(err) } - hclVAL.item = ast.LiteralNode{ - Type: ast.ValueTypeFloat, + hclVAL.obj = &Object{ + Type: ValueTypeFloat, Value: f, } } case 24: - //line parse.y:212 + //line parse.y:193 { hclVAL.num = hclS[hclpt-0].num * -1 } case 25: - //line parse.y:216 + //line parse.y:197 { hclVAL.num = hclS[hclpt-0].num } case 26: - //line parse.y:222 + //line parse.y:203 { hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10) }