Fix some parsing bugs, add tests for visitor pattern
This commit is contained in:
parent
0cffcbd1d0
commit
0aac9ca074
47
ast.go
47
ast.go
@ -1,5 +1,7 @@
|
|||||||
package hcl
|
package hcl
|
||||||
|
|
||||||
|
// ValueType is an enum represnting the type of a value in
|
||||||
|
// a LiteralNode.
|
||||||
type ValueType byte
|
type ValueType byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -8,23 +10,66 @@ const (
|
|||||||
ValueTypeString
|
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 {
|
type ObjectNode struct {
|
||||||
Key string
|
Key string
|
||||||
Elem []Node
|
Elem []Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssignmentNode represents a direct assignment with an equals
|
||||||
|
// sign.
|
||||||
type AssignmentNode struct {
|
type AssignmentNode struct {
|
||||||
Key string
|
Key string
|
||||||
Value Node
|
Value Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListNode represents a list or array of items.
|
||||||
type ListNode struct {
|
type ListNode struct {
|
||||||
Elem []Node
|
Elem []Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LiteralNode is a direct value.
|
||||||
type LiteralNode struct {
|
type LiteralNode struct {
|
||||||
Type ValueType
|
Type ValueType
|
||||||
Value interface{}
|
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
70
ast_test.go
Normal 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
10
parse.y
@ -134,11 +134,17 @@ listitem:
|
|||||||
}
|
}
|
||||||
| NUMBER
|
| NUMBER
|
||||||
{
|
{
|
||||||
$$ = $1
|
$$ = LiteralNode{
|
||||||
|
Type: ValueTypeInt,
|
||||||
|
Value: $1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
| STRING
|
| STRING
|
||||||
{
|
{
|
||||||
$$ = $1
|
$$ = LiteralNode{
|
||||||
|
Type: ValueTypeString,
|
||||||
|
Value: $1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
11
visitor_mock.go
Normal file
11
visitor_mock.go
Normal 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)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user