parser: implement parseObjectKey function
This commit is contained in:
parent
17205f8484
commit
628bc89026
@ -34,7 +34,7 @@ func (o *ObjectList) Pos() scanner.Pos {
|
||||
// ObjectItem represents a HCL Object Item. An item is represented with a key
|
||||
// (or keys). It can be an assignment or an object (both normal and nested)
|
||||
type ObjectItem struct {
|
||||
// keys is only one lenght long if it's of type assignment. If it's a
|
||||
// keys is only one length long if it's of type assignment. If it's a
|
||||
// nested object it can be larger than one. In that case "assign" is
|
||||
// invalid as there is no assignments for a nested object.
|
||||
keys []*ObjectKey
|
||||
@ -43,7 +43,7 @@ type ObjectItem struct {
|
||||
assign scanner.Pos
|
||||
|
||||
// val is the item itself. It can be an object,list, number, bool or a
|
||||
// string. If key lenght is larger than one, val can be only of type
|
||||
// string. If key length is larger than one, val can be only of type
|
||||
// Object.
|
||||
val Node
|
||||
}
|
||||
@ -61,7 +61,9 @@ func (o *ObjectKey) Pos() scanner.Pos {
|
||||
return o.token.Pos
|
||||
}
|
||||
|
||||
func (o *ObjectKey) IsValid() bool {
|
||||
// isValid() returns true if the underlying identifier satisfies one of the
|
||||
// valid types (IDENT or STRING)
|
||||
func (o *ObjectKey) isValid() bool {
|
||||
switch o.token.Type {
|
||||
case scanner.IDENT, scanner.STRING:
|
||||
return true
|
||||
|
@ -51,6 +51,18 @@ func (p *Parser) Parse() (Node, error) {
|
||||
func (p *Parser) parseObjectItem() (*ObjectItem, error) {
|
||||
defer un(trace(p, "ParseObjectItem"))
|
||||
|
||||
keys, err := p.parseObjectKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch len(keys) {
|
||||
case 1:
|
||||
// assignment or object
|
||||
default:
|
||||
// nested object
|
||||
}
|
||||
|
||||
tok := p.scan()
|
||||
fmt.Println(tok) // debug
|
||||
|
||||
@ -70,6 +82,47 @@ func (p *Parser) parseObjectItem() (*ObjectItem, error) {
|
||||
return nil, fmt.Errorf("not yet implemented: %s", tok.Type)
|
||||
}
|
||||
|
||||
// parseObjectKey parses an object key and returns a ObjectKey AST
|
||||
func (p *Parser) parseObjectKey() ([]*ObjectKey, error) {
|
||||
tok := p.scan()
|
||||
switch tok.Type {
|
||||
case scanner.IDENT, scanner.STRING:
|
||||
// add first found token
|
||||
keys := []*ObjectKey{&ObjectKey{tok}}
|
||||
nestedObj := false
|
||||
|
||||
// now we have three casses
|
||||
// 1. assignment: KEY = NODE
|
||||
// 2. object: KEY { }
|
||||
// 2. nested object: KEY KEY2 ... KEYN {}
|
||||
for {
|
||||
tok := p.scan()
|
||||
switch tok.Type {
|
||||
case scanner.ASSIGN:
|
||||
// assignment or object, but not nested objects
|
||||
if nestedObj {
|
||||
return nil, fmt.Errorf("nested object expected: LBRACE got: %s", tok.Type)
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
case scanner.LBRACE:
|
||||
// object
|
||||
return keys, nil
|
||||
case scanner.IDENT, scanner.STRING:
|
||||
// nested object
|
||||
nestedObj = true
|
||||
keys = append(keys, &ObjectKey{
|
||||
token: tok,
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("expected: IDENT | STRING | ASSIGN | LBRACE got: %s", tok.Type)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("expected: IDENT | STRING got: %s", tok.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// parseLiteralType parses a literal type and returns a LiteralType AST
|
||||
func (p *Parser) parseLiteralType() (*LiteralType, error) {
|
||||
defer un(trace(p, "ParseLiteral"))
|
||||
|
@ -2,28 +2,69 @@ package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/fatih/hcl/scanner"
|
||||
)
|
||||
|
||||
func TestAssignStatement(t *testing.T) {
|
||||
src := `ami = "${var.foo}"`
|
||||
p := New([]byte(src))
|
||||
p.enableTrace = true
|
||||
n, err := p.Parse()
|
||||
func TestObjectKey(t *testing.T) {
|
||||
keys := []struct {
|
||||
exp []scanner.TokenType
|
||||
src string
|
||||
}{
|
||||
{[]scanner.TokenType{scanner.IDENT}, `foo {}`},
|
||||
{[]scanner.TokenType{scanner.IDENT}, `foo = {}`},
|
||||
{[]scanner.TokenType{scanner.IDENT}, `foo = "${var.bar}`},
|
||||
{[]scanner.TokenType{scanner.STRING}, `"foo" {}`},
|
||||
{[]scanner.TokenType{scanner.STRING}, `"foo" = {}`},
|
||||
{[]scanner.TokenType{scanner.STRING}, `"foo" = "${var.bar}`},
|
||||
{[]scanner.TokenType{scanner.IDENT, scanner.IDENT}, `foo bar {}`},
|
||||
{[]scanner.TokenType{scanner.IDENT, scanner.STRING}, `foo "bar" {}`},
|
||||
{[]scanner.TokenType{scanner.STRING, scanner.IDENT}, `"foo" bar {}`},
|
||||
{[]scanner.TokenType{scanner.IDENT, scanner.IDENT, scanner.IDENT}, `foo bar baz {}`},
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
p := New([]byte(k.src))
|
||||
keys, err := p.parseObjectKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n.Pos().Line != 1 {
|
||||
t.Errorf("AssignStatement position is wrong\n\twant: '%d'\n\tgot : '%d'", 1, n.Pos().Line)
|
||||
tokens := []scanner.TokenType{}
|
||||
for _, o := range keys {
|
||||
tokens = append(tokens, o.token.Type)
|
||||
}
|
||||
|
||||
n1, ok := n.(*ObjectList)
|
||||
if !ok {
|
||||
t.Fatal("First Node should be of type Source")
|
||||
equals(t, k.exp, tokens)
|
||||
}
|
||||
|
||||
for _, ns := range n1.nodes {
|
||||
fmt.Printf("ns = %+v\n", ns)
|
||||
errKeys := []struct {
|
||||
src string
|
||||
}{
|
||||
{`foo 12 {}`},
|
||||
{`foo bar = {}`},
|
||||
{`foo []`},
|
||||
{`12 {}`},
|
||||
}
|
||||
|
||||
for _, k := range errKeys {
|
||||
p := New([]byte(k.src))
|
||||
_, err := p.parseObjectKey()
|
||||
if err == nil {
|
||||
t.Errorf("case '%s' should give an error", k.src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// equals fails the test if exp is not equal to act.
|
||||
func equals(tb testing.TB, exp, act interface{}) {
|
||||
if !reflect.DeepEqual(exp, act) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user