Fix some parsing bugs, add tests for visitor pattern

This commit is contained in:
Mitchell Hashimoto 2014-08-01 08:31:17 -07:00
parent 0cffcbd1d0
commit 0aac9ca074
5 changed files with 136 additions and 4 deletions

47
ast.go
View File

@ -1,5 +1,7 @@
package hcl
// ValueType is an enum represnting the type of a value in
// a LiteralNode.
type ValueType byte
const (
@ -8,23 +10,66 @@ const (
ValueTypeString
)
type Node interface{}
// Node is implemented by all AST nodes for HCL.
type Node interface {
Accept(Visitor)
}
// 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 {
Key string
Elem []Node
}
// AssignmentNode represents a direct assignment with an equals
// sign.
type AssignmentNode struct {
Key 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) {
for _, e := range n.Elem {
e.Accept(v)
}
v.Visit(n)
}
func (n AssignmentNode) Accept(v Visitor) {
n.Value.Accept(v)
v.Visit(n)
}
func (n ListNode) Accept(v Visitor) {
for _, e := range n.Elem {
e.Accept(v)
}
v.Visit(n)
}
func (n LiteralNode) Accept(v Visitor) {
v.Visit(n)
}

70
ast_test.go Normal file
View File

@ -0,0 +1,70 @@
package hcl
import (
"reflect"
"testing"
)
func TestAssignmentNode_accept(t *testing.T) {
n := AssignmentNode{
Key: "foo",
Value: LiteralNode{Value: "foo"},
}
expected := []Node{
n.Value,
n,
}
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.Elem[0],
n.Elem[1],
n,
}
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{
Key: "foo",
Elem: []Node{
LiteralNode{Value: "foo"},
LiteralNode{Value: "bar"},
},
}
expected := []Node{
n.Elem[0],
n.Elem[1],
n,
}
v := new(MockVisitor)
n.Accept(v)
if !reflect.DeepEqual(v.Nodes, expected) {
t.Fatalf("bad: %#v", v.Nodes)
}
}

10
parse.y
View File

@ -134,11 +134,17 @@ listitem:
}
| NUMBER
{
$$ = $1
$$ = LiteralNode{
Type: ValueTypeInt,
Value: $1,
}
}
| STRING
{
$$ = $1
$$ = LiteralNode{
Type: ValueTypeString,
Value: $1,
}
}
%%

View File

@ -9,7 +9,7 @@ import (
func TestParse(t *testing.T) {
cases := []struct {
Name string
Err bool
Err bool
}{
{
"comment.hcl",

11
visitor_mock.go Normal file
View File

@ -0,0 +1,11 @@
package hcl
// 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)
}