diff options
Diffstat (limited to 'vendor/github.com/hashicorp/hcl/hcl/parser')
32 files changed, 1240 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/error.go b/vendor/github.com/hashicorp/hcl/hcl/parser/error.go new file mode 100644 index 000000000..5c99381df --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/error.go @@ -0,0 +1,17 @@ +package parser + +import ( + "fmt" + + "github.com/hashicorp/hcl/hcl/token" +) + +// PosError is a parse error that contains a position. +type PosError struct { + Pos token.Pos + Err error +} + +func (e *PosError) Error() string { + return fmt.Sprintf("At %s: %s", e.Pos, e.Err) +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/error_test.go b/vendor/github.com/hashicorp/hcl/hcl/parser/error_test.go new file mode 100644 index 000000000..32399fec5 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/error_test.go @@ -0,0 +1,9 @@ +package parser + +import ( + "testing" +) + +func TestPosError_impl(t *testing.T) { + var _ error = new(PosError) +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go new file mode 100644 index 000000000..6e54bed97 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go @@ -0,0 +1,514 @@ +// Package parser implements a parser for HCL (HashiCorp Configuration +// Language) +package parser + +import ( + "errors" + "fmt" + "strings" + + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/scanner" + "github.com/hashicorp/hcl/hcl/token" +) + +type Parser struct { + sc *scanner.Scanner + + // Last read token + tok token.Token + commaPrev token.Token + + comments []*ast.CommentGroup + leadComment *ast.CommentGroup // last lead comment + lineComment *ast.CommentGroup // last line comment + + enableTrace bool + indent int + n int // buffer size (max = 1) +} + +func newParser(src []byte) *Parser { + return &Parser{ + sc: scanner.New(src), + } +} + +// Parse returns the fully parsed source and returns the abstract syntax tree. +func Parse(src []byte) (*ast.File, error) { + p := newParser(src) + return p.Parse() +} + +var errEofToken = errors.New("EOF token found") + +// Parse returns the fully parsed source and returns the abstract syntax tree. +func (p *Parser) Parse() (*ast.File, error) { + f := &ast.File{} + var err, scerr error + p.sc.Error = func(pos token.Pos, msg string) { + scerr = &PosError{Pos: pos, Err: errors.New(msg)} + } + + f.Node, err = p.objectList(false) + if scerr != nil { + return nil, scerr + } + if err != nil { + return nil, err + } + + f.Comments = p.comments + return f, nil +} + +// objectList parses a list of items within an object (generally k/v pairs). +// The parameter" obj" tells this whether to we are within an object (braces: +// '{', '}') or just at the top level. If we're within an object, we end +// at an RBRACE. +func (p *Parser) objectList(obj bool) (*ast.ObjectList, error) { + defer un(trace(p, "ParseObjectList")) + node := &ast.ObjectList{} + + for { + if obj { + tok := p.scan() + p.unscan() + if tok.Type == token.RBRACE { + break + } + } + + n, err := p.objectItem() + if err == errEofToken { + break // we are finished + } + + // we don't return a nil node, because might want to use already + // collected items. + if err != nil { + return node, err + } + + node.Add(n) + + // object lists can be optionally comma-delimited e.g. when a list of maps + // is being expressed, so a comma is allowed here - it's simply consumed + tok := p.scan() + if tok.Type != token.COMMA { + p.unscan() + } + } + return node, nil +} + +func (p *Parser) consumeComment() (comment *ast.Comment, endline int) { + endline = p.tok.Pos.Line + + // count the endline if it's multiline comment, ie starting with /* + if len(p.tok.Text) > 1 && p.tok.Text[1] == '*' { + // don't use range here - no need to decode Unicode code points + for i := 0; i < len(p.tok.Text); i++ { + if p.tok.Text[i] == '\n' { + endline++ + } + } + } + + comment = &ast.Comment{Start: p.tok.Pos, Text: p.tok.Text} + p.tok = p.sc.Scan() + return +} + +func (p *Parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) { + var list []*ast.Comment + endline = p.tok.Pos.Line + + for p.tok.Type == token.COMMENT && p.tok.Pos.Line <= endline+n { + var comment *ast.Comment + comment, endline = p.consumeComment() + list = append(list, comment) + } + + // add comment group to the comments list + comments = &ast.CommentGroup{List: list} + p.comments = append(p.comments, comments) + + return +} + +// objectItem parses a single object item +func (p *Parser) objectItem() (*ast.ObjectItem, error) { + defer un(trace(p, "ParseObjectItem")) + + keys, err := p.objectKey() + if len(keys) > 0 && err == errEofToken { + // We ignore eof token here since it is an error if we didn't + // receive a value (but we did receive a key) for the item. + err = nil + } + if len(keys) > 0 && err != nil && p.tok.Type == token.RBRACE { + // This is a strange boolean statement, but what it means is: + // We have keys with no value, and we're likely in an object + // (since RBrace ends an object). For this, we set err to nil so + // we continue and get the error below of having the wrong value + // type. + err = nil + + // Reset the token type so we don't think it completed fine. See + // objectType which uses p.tok.Type to check if we're done with + // the object. + p.tok.Type = token.EOF + } + if err != nil { + return nil, err + } + + o := &ast.ObjectItem{ + Keys: keys, + } + + if p.leadComment != nil { + o.LeadComment = p.leadComment + p.leadComment = nil + } + + switch p.tok.Type { + case token.ASSIGN: + o.Assign = p.tok.Pos + o.Val, err = p.object() + if err != nil { + return nil, err + } + case token.LBRACE: + o.Val, err = p.objectType() + if err != nil { + return nil, err + } + default: + keyStr := make([]string, 0, len(keys)) + for _, k := range keys { + keyStr = append(keyStr, k.Token.Text) + } + + return nil, fmt.Errorf( + "key '%s' expected start of object ('{') or assignment ('=')", + strings.Join(keyStr, " ")) + } + + // do a look-ahead for line comment + p.scan() + if len(keys) > 0 && o.Val.Pos().Line == keys[0].Pos().Line && p.lineComment != nil { + o.LineComment = p.lineComment + p.lineComment = nil + } + p.unscan() + return o, nil +} + +// objectKey parses an object key and returns a ObjectKey AST +func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { + keyCount := 0 + keys := make([]*ast.ObjectKey, 0) + + for { + tok := p.scan() + switch tok.Type { + case token.EOF: + // It is very important to also return the keys here as well as + // the error. This is because we need to be able to tell if we + // did parse keys prior to finding the EOF, or if we just found + // a bare EOF. + return keys, errEofToken + case token.ASSIGN: + // assignment or object only, but not nested objects. this is not + // allowed: `foo bar = {}` + if keyCount > 1 { + return nil, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("nested object expected: LBRACE got: %s", p.tok.Type), + } + } + + if keyCount == 0 { + return nil, &PosError{ + Pos: p.tok.Pos, + Err: errors.New("no object keys found!"), + } + } + + return keys, nil + case token.LBRACE: + var err error + + // If we have no keys, then it is a syntax error. i.e. {{}} is not + // allowed. + if len(keys) == 0 { + err = &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("expected: IDENT | STRING got: %s", p.tok.Type), + } + } + + // object + return keys, err + case token.IDENT, token.STRING: + keyCount++ + keys = append(keys, &ast.ObjectKey{Token: p.tok}) + case token.ILLEGAL: + return keys, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("illegal character"), + } + default: + return keys, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("expected: IDENT | STRING | ASSIGN | LBRACE got: %s", p.tok.Type), + } + } + } +} + +// object parses any type of object, such as number, bool, string, object or +// list. +func (p *Parser) object() (ast.Node, error) { + defer un(trace(p, "ParseType")) + tok := p.scan() + + switch tok.Type { + case token.NUMBER, token.FLOAT, token.BOOL, token.STRING, token.HEREDOC: + return p.literalType() + case token.LBRACE: + return p.objectType() + case token.LBRACK: + return p.listType() + case token.COMMENT: + // implement comment + case token.EOF: + return nil, errEofToken + } + + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf("Unknown token: %+v", tok), + } +} + +// objectType parses an object type and returns a ObjectType AST +func (p *Parser) objectType() (*ast.ObjectType, error) { + defer un(trace(p, "ParseObjectType")) + + // we assume that the currently scanned token is a LBRACE + o := &ast.ObjectType{ + Lbrace: p.tok.Pos, + } + + l, err := p.objectList(true) + + // if we hit RBRACE, we are good to go (means we parsed all Items), if it's + // not a RBRACE, it's an syntax error and we just return it. + if err != nil && p.tok.Type != token.RBRACE { + return nil, err + } + + // No error, scan and expect the ending to be a brace + if tok := p.scan(); tok.Type != token.RBRACE { + return nil, fmt.Errorf("object expected closing RBRACE got: %s", tok.Type) + } + + o.List = l + o.Rbrace = p.tok.Pos // advanced via parseObjectList + return o, nil +} + +// listType parses a list type and returns a ListType AST +func (p *Parser) listType() (*ast.ListType, error) { + defer un(trace(p, "ParseListType")) + + // we assume that the currently scanned token is a LBRACK + l := &ast.ListType{ + Lbrack: p.tok.Pos, + } + + needComma := false + for { + tok := p.scan() + if needComma { + switch tok.Type { + case token.COMMA, token.RBRACK: + default: + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf( + "error parsing list, expected comma or list end, got: %s", + tok.Type), + } + } + } + switch tok.Type { + case token.BOOL, token.NUMBER, token.FLOAT, token.STRING, token.HEREDOC: + node, err := p.literalType() + if err != nil { + return nil, err + } + + // If there is a lead comment, apply it + if p.leadComment != nil { + node.LeadComment = p.leadComment + p.leadComment = nil + } + + l.Add(node) + needComma = true + case token.COMMA: + // get next list item or we are at the end + // do a look-ahead for line comment + p.scan() + if p.lineComment != nil && len(l.List) > 0 { + lit, ok := l.List[len(l.List)-1].(*ast.LiteralType) + if ok { + lit.LineComment = p.lineComment + l.List[len(l.List)-1] = lit + p.lineComment = nil + } + } + p.unscan() + + needComma = false + continue + case token.LBRACE: + // Looks like a nested object, so parse it out + node, err := p.objectType() + if err != nil { + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf( + "error while trying to parse object within list: %s", err), + } + } + l.Add(node) + needComma = true + case token.LBRACK: + node, err := p.listType() + if err != nil { + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf( + "error while trying to parse list within list: %s", err), + } + } + l.Add(node) + case token.RBRACK: + // finished + l.Rbrack = p.tok.Pos + return l, nil + default: + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf("unexpected token while parsing list: %s", tok.Type), + } + } + } +} + +// literalType parses a literal type and returns a LiteralType AST +func (p *Parser) literalType() (*ast.LiteralType, error) { + defer un(trace(p, "ParseLiteral")) + + return &ast.LiteralType{ + Token: p.tok, + }, nil +} + +// scan returns the next token from the underlying scanner. If a token has +// been unscanned then read that instead. In the process, it collects any +// comment groups encountered, and remembers the last lead and line comments. +func (p *Parser) scan() token.Token { + // If we have a token on the buffer, then return it. + if p.n != 0 { + p.n = 0 + return p.tok + } + + // Otherwise read the next token from the scanner and Save it to the buffer + // in case we unscan later. + prev := p.tok + p.tok = p.sc.Scan() + + if p.tok.Type == token.COMMENT { + var comment *ast.CommentGroup + var endline int + + // fmt.Printf("p.tok.Pos.Line = %+v prev: %d endline %d \n", + // p.tok.Pos.Line, prev.Pos.Line, endline) + if p.tok.Pos.Line == prev.Pos.Line { + // The comment is on same line as the previous token; it + // cannot be a lead comment but may be a line comment. + comment, endline = p.consumeCommentGroup(0) + if p.tok.Pos.Line != endline { + // The next token is on a different line, thus + // the last comment group is a line comment. + p.lineComment = comment + } + } + + // consume successor comments, if any + endline = -1 + for p.tok.Type == token.COMMENT { + comment, endline = p.consumeCommentGroup(1) + } + + if endline+1 == p.tok.Pos.Line && p.tok.Type != token.RBRACE { + switch p.tok.Type { + case token.RBRACE, token.RBRACK: + // Do not count for these cases + default: + // The next token is following on the line immediately after the + // comment group, thus the last comment group is a lead comment. + p.leadComment = comment + } + } + + } + + return p.tok +} + +// unscan pushes the previously read token back onto the buffer. +func (p *Parser) unscan() { + p.n = 1 +} + +// ---------------------------------------------------------------------------- +// Parsing support + +func (p *Parser) printTrace(a ...interface{}) { + if !p.enableTrace { + return + } + + const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + const n = len(dots) + fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) + + i := 2 * p.indent + for i > n { + fmt.Print(dots) + i -= n + } + // i <= n + fmt.Print(dots[0:i]) + fmt.Println(a...) +} + +func trace(p *Parser, msg string) *Parser { + p.printTrace(msg, "(") + p.indent++ + return p +} + +// Usage pattern: defer un(trace(p, "...")) +func un(p *Parser) { + p.indent-- + p.printTrace(")") +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go b/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go new file mode 100644 index 000000000..575865838 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go @@ -0,0 +1,566 @@ +package parser + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "reflect" + "runtime" + "strings" + "testing" + + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/token" +) + +func TestType(t *testing.T) { + var literals = []struct { + typ token.Type + src string + }{ + {token.STRING, `foo = "foo"`}, + {token.NUMBER, `foo = 123`}, + {token.NUMBER, `foo = -29`}, + {token.FLOAT, `foo = 123.12`}, + {token.FLOAT, `foo = -123.12`}, + {token.BOOL, `foo = true`}, + {token.HEREDOC, "foo = <<EOF\nHello\nWorld\nEOF"}, + } + + for _, l := range literals { + p := newParser([]byte(l.src)) + item, err := p.objectItem() + if err != nil { + t.Error(err) + } + + lit, ok := item.Val.(*ast.LiteralType) + if !ok { + t.Errorf("node should be of type LiteralType, got: %T", item.Val) + } + + if lit.Token.Type != l.typ { + t.Errorf("want: %s, got: %s", l.typ, lit.Token.Type) + } + } +} + +func TestListType(t *testing.T) { + var literals = []struct { + src string + tokens []token.Type + }{ + { + `foo = ["123", 123]`, + []token.Type{token.STRING, token.NUMBER}, + }, + { + `foo = [123, "123",]`, + []token.Type{token.NUMBER, token.STRING}, + }, + { + `foo = [false]`, + []token.Type{token.BOOL}, + }, + { + `foo = []`, + []token.Type{}, + }, + { + `foo = [1, +"string", +<<EOF +heredoc contents +EOF +]`, + []token.Type{token.NUMBER, token.STRING, token.HEREDOC}, + }, + } + + for _, l := range literals { + p := newParser([]byte(l.src)) + item, err := p.objectItem() + if err != nil { + t.Error(err) + } + + list, ok := item.Val.(*ast.ListType) + if !ok { + t.Errorf("node should be of type LiteralType, got: %T", item.Val) + } + + tokens := []token.Type{} + for _, li := range list.List { + if tp, ok := li.(*ast.LiteralType); ok { + tokens = append(tokens, tp.Token.Type) + } + } + + equals(t, l.tokens, tokens) + } +} + +func TestListOfMaps(t *testing.T) { + src := `foo = [ + {key = "bar"}, + {key = "baz", key2 = "qux"}, + ]` + p := newParser([]byte(src)) + + file, err := p.Parse() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Here we make all sorts of assumptions about the input structure w/ type + // assertions. The intent is only for this to be a "smoke test" ensuring + // parsing actually performed its duty - giving this test something a bit + // more robust than _just_ "no error occurred". + expected := []string{`"bar"`, `"baz"`, `"qux"`} + actual := make([]string, 0, 3) + ol := file.Node.(*ast.ObjectList) + objItem := ol.Items[0] + list := objItem.Val.(*ast.ListType) + for _, node := range list.List { + obj := node.(*ast.ObjectType) + for _, item := range obj.List.Items { + val := item.Val.(*ast.LiteralType) + actual = append(actual, val.Token.Text) + } + + } + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("Expected: %#v, got %#v", expected, actual) + } +} + +func TestListOfMaps_requiresComma(t *testing.T) { + src := `foo = [ + {key = "bar"} + {key = "baz"} + ]` + p := newParser([]byte(src)) + + _, err := p.Parse() + if err == nil { + t.Fatalf("Expected error, got none!") + } + + expected := "error parsing list, expected comma or list end" + if !strings.Contains(err.Error(), expected) { + t.Fatalf("Expected err:\n %s\nTo contain:\n %s\n", err, expected) + } +} + +func TestListType_leadComment(t *testing.T) { + var literals = []struct { + src string + comment []string + }{ + { + `foo = [ + 1, + # bar + 2, + 3, + ]`, + []string{"", "# bar", ""}, + }, + } + + for _, l := range literals { + p := newParser([]byte(l.src)) + item, err := p.objectItem() + if err != nil { + t.Fatal(err) + } + + list, ok := item.Val.(*ast.ListType) + if !ok { + t.Fatalf("node should be of type LiteralType, got: %T", item.Val) + } + + if len(list.List) != len(l.comment) { + t.Fatalf("bad: %d", len(list.List)) + } + + for i, li := range list.List { + lt := li.(*ast.LiteralType) + comment := l.comment[i] + + if (lt.LeadComment == nil) != (comment == "") { + t.Fatalf("bad: %#v", lt) + } + + if comment == "" { + continue + } + + actual := lt.LeadComment.List[0].Text + if actual != comment { + t.Fatalf("bad: %q %q", actual, comment) + } + } + } +} + +func TestListType_lineComment(t *testing.T) { + var literals = []struct { + src string + comment []string + }{ + { + `foo = [ + 1, + 2, # bar + 3, + ]`, + []string{"", "# bar", ""}, + }, + } + + for _, l := range literals { + p := newParser([]byte(l.src)) + item, err := p.objectItem() + if err != nil { + t.Fatal(err) + } + + list, ok := item.Val.(*ast.ListType) + if !ok { + t.Fatalf("node should be of type LiteralType, got: %T", item.Val) + } + + if len(list.List) != len(l.comment) { + t.Fatalf("bad: %d", len(list.List)) + } + + for i, li := range list.List { + lt := li.(*ast.LiteralType) + comment := l.comment[i] + + if (lt.LineComment == nil) != (comment == "") { + t.Fatalf("bad: %s", lt) + } + + if comment == "" { + continue + } + + actual := lt.LineComment.List[0].Text + if actual != comment { + t.Fatalf("bad: %q %q", actual, comment) + } + } + } +} + +func TestObjectType(t *testing.T) { + var literals = []struct { + src string + nodeType []ast.Node + itemLen int + }{ + { + `foo = {}`, + nil, + 0, + }, + { + `foo = { + bar = "fatih" + }`, + []ast.Node{&ast.LiteralType{}}, + 1, + }, + { + `foo = { + bar = "fatih" + baz = ["arslan"] + }`, + []ast.Node{ + &ast.LiteralType{}, + &ast.ListType{}, + }, + 2, + }, + { + `foo = { + bar {} + }`, + []ast.Node{ + &ast.ObjectType{}, + }, + 1, + }, + { + `foo { + bar {} + foo = true + }`, + []ast.Node{ + &ast.ObjectType{}, + &ast.LiteralType{}, + }, + 2, + }, + } + + for _, l := range literals { + t.Logf("Source: %s", l.src) + + p := newParser([]byte(l.src)) + // p.enableTrace = true + item, err := p.objectItem() + if err != nil { + t.Error(err) + continue + } + + // we know that the ObjectKey name is foo for all cases, what matters + // is the object + obj, ok := item.Val.(*ast.ObjectType) + if !ok { + t.Errorf("node should be of type LiteralType, got: %T", item.Val) + continue + } + + // check if the total length of items are correct + equals(t, l.itemLen, len(obj.List.Items)) + + // check if the types are correct + for i, item := range obj.List.Items { + equals(t, reflect.TypeOf(l.nodeType[i]), reflect.TypeOf(item.Val)) + } + } +} + +func TestObjectKey(t *testing.T) { + keys := []struct { + exp []token.Type + src string + }{ + {[]token.Type{token.IDENT}, `foo {}`}, + {[]token.Type{token.IDENT}, `foo = {}`}, + {[]token.Type{token.IDENT}, `foo = bar`}, + {[]token.Type{token.IDENT}, `foo = 123`}, + {[]token.Type{token.IDENT}, `foo = "${var.bar}`}, + {[]token.Type{token.STRING}, `"foo" {}`}, + {[]token.Type{token.STRING}, `"foo" = {}`}, + {[]token.Type{token.STRING}, `"foo" = "${var.bar}`}, + {[]token.Type{token.IDENT, token.IDENT}, `foo bar {}`}, + {[]token.Type{token.IDENT, token.STRING}, `foo "bar" {}`}, + {[]token.Type{token.STRING, token.IDENT}, `"foo" bar {}`}, + {[]token.Type{token.IDENT, token.IDENT, token.IDENT}, `foo bar baz {}`}, + } + + for _, k := range keys { + p := newParser([]byte(k.src)) + keys, err := p.objectKey() + if err != nil { + t.Fatal(err) + } + + tokens := []token.Type{} + for _, o := range keys { + tokens = append(tokens, o.Token.Type) + } + + equals(t, k.exp, tokens) + } + + errKeys := []struct { + src string + }{ + {`foo 12 {}`}, + {`foo bar = {}`}, + {`foo []`}, + {`12 {}`}, + } + + for _, k := range errKeys { + p := newParser([]byte(k.src)) + _, err := p.objectKey() + if err == nil { + t.Errorf("case '%s' should give an error", k.src) + } + } +} + +func TestCommentGroup(t *testing.T) { + var cases = []struct { + src string + groups int + }{ + {"# Hello\n# World", 1}, + } + + for _, tc := range cases { + t.Run(tc.src, func(t *testing.T) { + p := newParser([]byte(tc.src)) + file, err := p.Parse() + if err != nil { + t.Fatalf("parse error: %s", err) + } + + if len(file.Comments) != tc.groups { + t.Fatalf("bad: %#v", file.Comments) + } + }) + } +} + +// Official HCL tests +func TestParse(t *testing.T) { + cases := []struct { + Name string + Err bool + }{ + { + "assign_colon.hcl", + true, + }, + { + "comment.hcl", + false, + }, + { + "comment_lastline.hcl", + false, + }, + { + "comment_single.hcl", + false, + }, + { + "empty.hcl", + false, + }, + { + "list_comma.hcl", + false, + }, + { + "multiple.hcl", + false, + }, + { + "object_list_comma.hcl", + false, + }, + { + "structure.hcl", + false, + }, + { + "structure_basic.hcl", + false, + }, + { + "structure_empty.hcl", + false, + }, + { + "complex.hcl", + false, + }, + { + "types.hcl", + false, + }, + { + "array_comment.hcl", + false, + }, + { + "array_comment_2.hcl", + true, + }, + { + "missing_braces.hcl", + true, + }, + { + "unterminated_object.hcl", + true, + }, + { + "unterminated_object_2.hcl", + true, + }, + { + "key_without_value.hcl", + true, + }, + { + "object_key_without_value.hcl", + true, + }, + { + "object_key_assign_without_value.hcl", + true, + }, + { + "object_key_assign_without_value2.hcl", + true, + }, + { + "object_key_assign_without_value3.hcl", + true, + }, + { + "git_crypt.hcl", + true, + }, + } + + const fixtureDir = "./test-fixtures" + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.Name)) + if err != nil { + t.Fatalf("err: %s", err) + } + + v, err := Parse(d) + if (err != nil) != tc.Err { + t.Fatalf("Input: %s\n\nError: %s\n\nAST: %#v", tc.Name, err, v) + } + }) + } +} + +func TestParse_inline(t *testing.T) { + cases := []struct { + Value string + Err bool + }{ + {"t t e{{}}", true}, + {"o{{}}", true}, + {"t t e d N{{}}", true}, + {"t t e d{{}}", true}, + {"N{}N{{}}", true}, + {"v\nN{{}}", true}, + {"v=/\n[,", true}, + {"v=10kb", true}, + {"v=/foo", true}, + } + + for _, tc := range cases { + t.Logf("Testing: %q", tc.Value) + ast, err := Parse([]byte(tc.Value)) + if (err != nil) != tc.Err { + t.Fatalf("Input: %q\n\nError: %s\n\nAST: %#v", tc.Value, err, ast) + } + } +} + +// 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() + } +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/array_comment.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/array_comment.hcl new file mode 100644 index 000000000..78c267582 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/array_comment.hcl @@ -0,0 +1,4 @@ +foo = [ + "1", + "2", # comment +] diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/array_comment_2.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/array_comment_2.hcl new file mode 100644 index 000000000..f91667738 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/array_comment_2.hcl @@ -0,0 +1,6 @@ +provisioner "remote-exec" { + scripts = [ + "${path.module}/scripts/install-consul.sh" // missing comma + "${path.module}/scripts/install-haproxy.sh" + ] +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/assign_colon.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/assign_colon.hcl new file mode 100644 index 000000000..eb5a99a69 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/assign_colon.hcl @@ -0,0 +1,6 @@ +resource = [{ + "foo": { + "bar": {}, + "baz": [1, 2, "foo"], + } +}] diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/assign_deep.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/assign_deep.hcl new file mode 100644 index 000000000..dd3151cb7 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/assign_deep.hcl @@ -0,0 +1,5 @@ +resource = [{
+ foo = [{
+ bar = {}
+ }]
+}]
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment.hcl new file mode 100644 index 000000000..1ff7f29fd --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment.hcl @@ -0,0 +1,15 @@ +// Foo
+
+/* Bar */
+
+/*
+/*
+Baz
+*/
+
+# Another
+
+# Multiple
+# Lines
+
+foo = "bar"
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment_lastline.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment_lastline.hcl new file mode 100644 index 000000000..5529b9b4c --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment_lastline.hcl @@ -0,0 +1 @@ +#foo
\ No newline at end of file diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment_single.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment_single.hcl new file mode 100644 index 000000000..fec56017d --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/comment_single.hcl @@ -0,0 +1 @@ +# Hello diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/complex.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/complex.hcl new file mode 100644 index 000000000..13b3c2726 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/complex.hcl @@ -0,0 +1,42 @@ +variable "foo" { + default = "bar" + description = "bar" +} + +variable "groups" { } + +provider "aws" { + access_key = "foo" + secret_key = "bar" +} + +provider "do" { + api_key = "${var.foo}" +} + +resource "aws_security_group" "firewall" { + count = 5 +} + +resource aws_instance "web" { + ami = "${var.foo}" + security_groups = [ + "foo", + "${aws_security_group.firewall.foo}", + "${element(split(\",\", var.groups)}", + ] + network_interface = { + device_index = 0 + description = "Main network interface" + } +} + +resource "aws_instance" "db" { + security_groups = "${aws_security_group.firewall.*.id}" + VPC = "foo" + depends_on = ["aws_instance.web"] +} + +output "web_ip" { + value = "${aws_instance.web.private_ip}" +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/complex_key.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/complex_key.hcl new file mode 100644 index 000000000..0007aaf5f --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/complex_key.hcl @@ -0,0 +1 @@ +foo.bar = "baz" diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/empty.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/empty.hcl new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/empty.hcl diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/git_crypt.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/git_crypt.hcl Binary files differnew file mode 100644 index 000000000..f691948e1 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/git_crypt.hcl diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/key_without_value.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/key_without_value.hcl new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/key_without_value.hcl @@ -0,0 +1 @@ +foo diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/list.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/list.hcl new file mode 100644 index 000000000..059d4ce65 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/list.hcl @@ -0,0 +1 @@ +foo = [1, 2, "foo"]
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/list_comma.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/list_comma.hcl new file mode 100644 index 000000000..50f4218ac --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/list_comma.hcl @@ -0,0 +1 @@ +foo = [1, 2, "foo",] diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/missing_braces.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/missing_braces.hcl new file mode 100644 index 000000000..68e7274e6 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/missing_braces.hcl @@ -0,0 +1,4 @@ +# should error, but not crash +resource "template_file" "cloud_config" { + template = "$file("${path.module}/some/path")" +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/multiple.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/multiple.hcl new file mode 100644 index 000000000..029c54b0c --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/multiple.hcl @@ -0,0 +1,2 @@ +foo = "bar"
+key = 7
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value.hcl new file mode 100644 index 000000000..37a2c7a06 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value.hcl @@ -0,0 +1,3 @@ +foo { + bar = +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value2.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value2.hcl new file mode 100644 index 000000000..83ec5e66e --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value2.hcl @@ -0,0 +1,4 @@ +foo { + baz = 7 + bar = +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value3.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value3.hcl new file mode 100644 index 000000000..21136d1d5 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_assign_without_value3.hcl @@ -0,0 +1,4 @@ +foo { + bar = + baz = 7 +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_without_value.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_without_value.hcl new file mode 100644 index 000000000..a9987318c --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_key_without_value.hcl @@ -0,0 +1,3 @@ +foo { + bar +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_list_comma.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_list_comma.hcl new file mode 100644 index 000000000..1921ec8f2 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/object_list_comma.hcl @@ -0,0 +1 @@ +foo = {one = 1, two = 2} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/old.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/old.hcl new file mode 100644 index 000000000..e9f77cae9 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/old.hcl @@ -0,0 +1,3 @@ +default = { + "eu-west-1": "ami-b1cf19c6", +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure.hcl new file mode 100644 index 000000000..92592fbb3 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure.hcl @@ -0,0 +1,5 @@ +// This is a test structure for the lexer +foo bar "baz" { + key = 7 + foo = "bar" +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure_basic.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure_basic.hcl new file mode 100644 index 000000000..7229a1f01 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure_basic.hcl @@ -0,0 +1,5 @@ +foo {
+ value = 7
+ "value" = 8
+ "complex::value" = 9
+}
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure_empty.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure_empty.hcl new file mode 100644 index 000000000..4d156ddea --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/structure_empty.hcl @@ -0,0 +1 @@ +resource "foo" "bar" {}
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/types.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/types.hcl new file mode 100644 index 000000000..cf2747ea1 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/types.hcl @@ -0,0 +1,7 @@ +foo = "bar"
+bar = 7
+baz = [1,2,3]
+foo = -12
+bar = 3.14159
+foo = true
+bar = false
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/unterminated_object.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/unterminated_object.hcl new file mode 100644 index 000000000..31b37c4f9 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/unterminated_object.hcl @@ -0,0 +1,2 @@ +foo "baz" { + bar = "baz" diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/unterminated_object_2.hcl b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/unterminated_object_2.hcl new file mode 100644 index 000000000..294e36d65 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/test-fixtures/unterminated_object_2.hcl @@ -0,0 +1,6 @@ +resource "aws_eip" "EIP1" { a { a { a { a { a { + count = "1" + +resource "aws_eip" "EIP2" { + count = "1" +} |