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 (
"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

View File

@ -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 <item> number
%type <list> list
%type <alist> objectlist
%type <aitem> objectitem block
%type <listitem> listitem
%type <nodes> listitems
%type <num> int
%type <obj> object
%type <objlist> list listitems objectlist
%type <obj> block number object objectitem
%type <obj> listitem
%type <str> blockId frac
%token <b> 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,
}
}

205
hcl/y.go
View File

@ -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)
}