7 "github.com/hashicorp/hcl/hcl/ast"
8 hcltoken "github.com/hashicorp/hcl/hcl/token"
9 "github.com/hashicorp/hcl/json/scanner"
10 "github.com/hashicorp/hcl/json/token"
22 n int // buffer size (max = 1)
25 func newParser(src []byte) *Parser {
31 // Parse returns the fully parsed source and returns the abstract syntax tree.
32 func Parse(src []byte) (*ast.File, error) {
37 var errEofToken = errors.New("EOF token found")
39 // Parse returns the fully parsed source and returns the abstract syntax tree.
40 func (p *Parser) Parse() (*ast.File, error) {
43 p.sc.Error = func(pos token.Pos, msg string) {
44 scerr = fmt.Errorf("%s: %s", pos, msg)
47 // The root must be an object in JSON
48 object, err := p.object()
56 // We make our final node an object list so it is more HCL compatible
59 // Flatten it, which finds patterns and turns them into more HCL-like
61 flattenObjects(f.Node)
66 func (p *Parser) objectList() (*ast.ObjectList, error) {
67 defer un(trace(p, "ParseObjectList"))
68 node := &ast.ObjectList{}
71 n, err := p.objectItem()
72 if err == errEofToken {
73 break // we are finished
76 // we don't return a nil node, because might want to use already
84 // Check for a followup comma. If it isn't a comma, then we're done
85 if tok := p.scan(); tok.Type != token.COMMA {
93 // objectItem parses a single object item
94 func (p *Parser) objectItem() (*ast.ObjectItem, error) {
95 defer un(trace(p, "ParseObjectItem"))
97 keys, err := p.objectKey()
102 o := &ast.ObjectItem{
109 o.Assign = hcltoken.Pos{
110 Filename: pos.Filename,
116 o.Val, err = p.objectValue()
125 // objectKey parses an object key and returns a ObjectKey AST
126 func (p *Parser) objectKey() ([]*ast.ObjectKey, error) {
128 keys := make([]*ast.ObjectKey, 0)
134 return nil, errEofToken
137 keys = append(keys, &ast.ObjectKey{
138 Token: p.tok.HCLToken(),
141 // If we have a zero keycount it means that we never got
142 // an object key, i.e. `{ :`. This is a syntax error.
144 return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type)
150 return nil, errors.New("illegal")
152 return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type)
157 // object parses any type of object, such as number, bool, string, object or
159 func (p *Parser) objectValue() (ast.Node, error) {
160 defer un(trace(p, "ParseObjectValue"))
164 case token.NUMBER, token.FLOAT, token.BOOL, token.NULL, token.STRING:
165 return p.literalType()
167 return p.objectType()
171 return nil, errEofToken
174 return nil, fmt.Errorf("Expected object value, got unknown token: %+v", tok)
177 // object parses any type of object, such as number, bool, string, object or
179 func (p *Parser) object() (*ast.ObjectType, error) {
180 defer un(trace(p, "ParseType"))
185 return p.objectType()
187 return nil, errEofToken
190 return nil, fmt.Errorf("Expected object, got unknown token: %+v", tok)
193 // objectType parses an object type and returns a ObjectType AST
194 func (p *Parser) objectType() (*ast.ObjectType, error) {
195 defer un(trace(p, "ParseObjectType"))
197 // we assume that the currently scanned token is a LBRACE
198 o := &ast.ObjectType{}
200 l, err := p.objectList()
202 // if we hit RBRACE, we are good to go (means we parsed all Items), if it's
203 // not a RBRACE, it's an syntax error and we just return it.
204 if err != nil && p.tok.Type != token.RBRACE {
212 // listType parses a list type and returns a ListType AST
213 func (p *Parser) listType() (*ast.ListType, error) {
214 defer un(trace(p, "ParseListType"))
216 // we assume that the currently scanned token is a LBRACK
222 case token.NUMBER, token.FLOAT, token.STRING:
223 node, err := p.literalType()
232 node, err := p.objectType()
239 // TODO(arslan) should we support? not supported by HCL yet
241 // TODO(arslan) should we support nested lists? Even though it's
242 // written in README of HCL, it's not a part of the grammar
243 // (not defined in parse.y)
248 return nil, fmt.Errorf("unexpected token while parsing list: %s", tok.Type)
254 // literalType parses a literal type and returns a LiteralType AST
255 func (p *Parser) literalType() (*ast.LiteralType, error) {
256 defer un(trace(p, "ParseLiteral"))
258 return &ast.LiteralType{
259 Token: p.tok.HCLToken(),
263 // scan returns the next token from the underlying scanner. If a token has
264 // been unscanned then read that instead.
265 func (p *Parser) scan() token.Token {
266 // If we have a token on the buffer, then return it.
276 // unscan pushes the previously read token back onto the buffer.
277 func (p *Parser) unscan() {
281 // ----------------------------------------------------------------------------
284 func (p *Parser) printTrace(a ...interface{}) {
289 const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
291 fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column)
303 func trace(p *Parser, msg string) *Parser {
304 p.printTrace(msg, "(")
309 // Usage pattern: defer un(trace(p, "..."))