2015-10-03 11:32:19 +00:00
|
|
|
package parser
|
2015-10-07 22:38:39 +00:00
|
|
|
|
2015-10-11 23:27:43 +00:00
|
|
|
import (
|
2015-10-12 19:53:40 +00:00
|
|
|
"errors"
|
2015-10-11 23:27:43 +00:00
|
|
|
"fmt"
|
|
|
|
|
2015-10-16 20:12:26 +00:00
|
|
|
"github.com/fatih/hcl/ast"
|
2015-10-11 23:27:43 +00:00
|
|
|
"github.com/fatih/hcl/scanner"
|
2015-10-16 20:12:26 +00:00
|
|
|
"github.com/fatih/hcl/token"
|
2015-10-11 23:27:43 +00:00
|
|
|
)
|
2015-10-07 22:38:39 +00:00
|
|
|
|
|
|
|
type Parser struct {
|
2015-10-11 22:38:59 +00:00
|
|
|
sc *scanner.Scanner
|
|
|
|
|
2015-10-16 20:12:26 +00:00
|
|
|
tok token.Token // last read token
|
|
|
|
prevTok token.Token // previous read token
|
2015-10-11 22:38:59 +00:00
|
|
|
|
2015-10-11 23:27:43 +00:00
|
|
|
enableTrace bool
|
|
|
|
indent int
|
|
|
|
n int // buffer size (max = 1)
|
2015-10-07 22:38:39 +00:00
|
|
|
}
|
|
|
|
|
2015-10-11 23:27:43 +00:00
|
|
|
func New(src []byte) *Parser {
|
2015-10-07 22:38:39 +00:00
|
|
|
return &Parser{
|
2015-10-11 21:20:17 +00:00
|
|
|
sc: scanner.New(src),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 22:27:35 +00:00
|
|
|
var errEofToken = errors.New("EOF token found")
|
|
|
|
|
2015-10-11 22:38:59 +00:00
|
|
|
// Parse returns the fully parsed source and returns the abstract syntax tree.
|
2015-10-16 20:12:26 +00:00
|
|
|
func (p *Parser) Parse() (ast.Node, error) {
|
2015-10-16 22:14:40 +00:00
|
|
|
return p.parseObjectList()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) parseObjectList() (*ast.ObjectList, error) {
|
2015-10-16 19:57:56 +00:00
|
|
|
defer un(trace(p, "ParseObjectList"))
|
2015-10-16 20:12:26 +00:00
|
|
|
node := &ast.ObjectList{}
|
2015-10-11 23:27:43 +00:00
|
|
|
|
|
|
|
for {
|
2015-10-15 21:57:57 +00:00
|
|
|
n, err := p.parseObjectItem()
|
2015-10-14 22:27:35 +00:00
|
|
|
if err == errEofToken {
|
|
|
|
break // we are finished
|
|
|
|
}
|
2015-10-16 22:39:49 +00:00
|
|
|
|
2015-10-16 22:48:38 +00:00
|
|
|
// we don't return a nil, because might want to use already collected
|
|
|
|
// items.
|
2015-10-12 19:53:40 +00:00
|
|
|
if err != nil {
|
2015-10-16 22:48:38 +00:00
|
|
|
return node, err
|
2015-10-11 23:49:07 +00:00
|
|
|
}
|
|
|
|
|
2015-10-14 22:27:35 +00:00
|
|
|
// we successfully parsed a node, add it to the final source node
|
2015-10-16 20:12:26 +00:00
|
|
|
node.Add(n)
|
2015-10-07 22:38:39 +00:00
|
|
|
}
|
2015-10-11 21:20:17 +00:00
|
|
|
|
2015-10-12 07:37:37 +00:00
|
|
|
return node, nil
|
2015-10-11 22:38:59 +00:00
|
|
|
}
|
|
|
|
|
2015-10-15 22:11:54 +00:00
|
|
|
// parseObjectItem parses a single object item
|
2015-10-16 20:12:26 +00:00
|
|
|
func (p *Parser) parseObjectItem() (*ast.ObjectItem, error) {
|
2015-10-15 21:57:57 +00:00
|
|
|
defer un(trace(p, "ParseObjectItem"))
|
2015-10-11 23:27:43 +00:00
|
|
|
|
2015-10-15 23:00:02 +00:00
|
|
|
keys, err := p.parseObjectKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-10-16 11:16:12 +00:00
|
|
|
switch p.tok.Type {
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.ASSIGN:
|
2015-10-16 22:39:49 +00:00
|
|
|
// assignments
|
2015-10-16 20:12:26 +00:00
|
|
|
o := &ast.ObjectItem{
|
|
|
|
Keys: keys,
|
|
|
|
Assign: p.tok.Pos,
|
2015-10-16 11:44:11 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 20:12:26 +00:00
|
|
|
o.Val, err = p.parseType()
|
2015-10-16 11:44:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return o, nil
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.LBRACE:
|
2015-10-16 22:39:49 +00:00
|
|
|
// object or nested objects
|
|
|
|
o := &ast.ObjectItem{
|
|
|
|
Keys: keys,
|
2015-10-16 11:16:12 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 22:39:49 +00:00
|
|
|
o.Val, err = p.parseObjectType()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return o, nil
|
2015-10-16 11:16:12 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 19:57:56 +00:00
|
|
|
return nil, fmt.Errorf("not yet implemented: %s", p.tok.Type)
|
|
|
|
}
|
2015-10-15 23:00:02 +00:00
|
|
|
|
2015-10-16 19:57:56 +00:00
|
|
|
// parseType parses any type of Type, such as number, bool, string, object or
|
|
|
|
// list.
|
2015-10-16 20:12:26 +00:00
|
|
|
func (p *Parser) parseType() (ast.Node, error) {
|
2015-10-16 19:57:56 +00:00
|
|
|
defer un(trace(p, "ParseType"))
|
2015-10-11 22:38:59 +00:00
|
|
|
tok := p.scan()
|
2015-10-12 19:53:40 +00:00
|
|
|
|
2015-10-14 22:27:35 +00:00
|
|
|
switch tok.Type {
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.NUMBER, token.FLOAT, token.BOOL, token.STRING:
|
2015-10-16 19:57:56 +00:00
|
|
|
return p.parseLiteralType()
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.LBRACE:
|
2015-10-16 19:57:56 +00:00
|
|
|
return p.parseObjectType()
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.LBRACK:
|
2015-10-16 19:57:56 +00:00
|
|
|
return p.parseListType()
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.COMMENT:
|
2015-10-14 22:27:35 +00:00
|
|
|
// implement comment
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.EOF:
|
2015-10-14 22:27:35 +00:00
|
|
|
return nil, errEofToken
|
|
|
|
}
|
|
|
|
|
2015-10-16 11:44:11 +00:00
|
|
|
return nil, errors.New("ParseType is not implemented yet")
|
|
|
|
}
|
|
|
|
|
2015-10-15 23:00:02 +00:00
|
|
|
// parseObjectKey parses an object key and returns a ObjectKey AST
|
2015-10-16 20:12:26 +00:00
|
|
|
func (p *Parser) parseObjectKey() ([]*ast.ObjectKey, error) {
|
2015-10-15 23:00:02 +00:00
|
|
|
tok := p.scan()
|
2015-10-16 20:12:26 +00:00
|
|
|
if tok.Type == token.EOF {
|
2015-10-16 19:57:56 +00:00
|
|
|
return nil, errEofToken
|
|
|
|
}
|
2015-10-16 11:16:12 +00:00
|
|
|
|
2015-10-16 20:12:26 +00:00
|
|
|
keys := make([]*ast.ObjectKey, 0)
|
2015-10-16 11:16:12 +00:00
|
|
|
|
2015-10-15 23:00:02 +00:00
|
|
|
switch tok.Type {
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.IDENT, token.STRING:
|
2015-10-15 23:00:02 +00:00
|
|
|
// add first found token
|
2015-10-16 20:12:26 +00:00
|
|
|
keys = append(keys, &ast.ObjectKey{Token: tok})
|
2015-10-15 23:00:02 +00:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("expected: IDENT | STRING got: %s", tok.Type)
|
|
|
|
}
|
2015-10-16 11:16:12 +00:00
|
|
|
|
|
|
|
nestedObj := false
|
|
|
|
|
|
|
|
// we have three casses
|
|
|
|
// 1. assignment: KEY = NODE
|
|
|
|
// 2. object: KEY { }
|
|
|
|
// 2. nested object: KEY KEY2 ... KEYN {}
|
|
|
|
for {
|
|
|
|
tok := p.scan()
|
|
|
|
switch tok.Type {
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.ASSIGN:
|
2015-10-16 11:44:11 +00:00
|
|
|
// assignment or object only, but not nested objects. this is not
|
|
|
|
// allowed: `foo bar = {}`
|
2015-10-16 11:16:12 +00:00
|
|
|
if nestedObj {
|
|
|
|
return nil, fmt.Errorf("nested object expected: LBRACE got: %s", tok.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
return keys, nil
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.LBRACE:
|
2015-10-16 11:16:12 +00:00
|
|
|
// object
|
|
|
|
return keys, nil
|
2015-10-16 20:12:26 +00:00
|
|
|
case token.IDENT, token.STRING:
|
2015-10-16 11:16:12 +00:00
|
|
|
// nested object
|
|
|
|
nestedObj = true
|
2015-10-16 20:12:26 +00:00
|
|
|
keys = append(keys, &ast.ObjectKey{Token: tok})
|
2015-10-16 11:16:12 +00:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("expected: IDENT | STRING | ASSIGN | LBRACE got: %s", tok.Type)
|
|
|
|
}
|
|
|
|
}
|
2015-10-15 23:00:02 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 22:14:40 +00:00
|
|
|
// parseObjectType parses an object type and returns a ObjectType AST
|
|
|
|
func (p *Parser) parseObjectType() (*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.parseObjectList()
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
o.List = l
|
|
|
|
o.Rbrace = p.tok.Pos // advanced via parseObjectList
|
|
|
|
return o, nil
|
|
|
|
}
|
|
|
|
|
2015-10-16 21:00:05 +00:00
|
|
|
// parseListType parses a list type and returns a ListType AST
|
|
|
|
func (p *Parser) parseListType() (*ast.ListType, error) {
|
|
|
|
defer un(trace(p, "ParseListType"))
|
|
|
|
|
2015-10-16 22:14:40 +00:00
|
|
|
// we assume that the currently scanned token is a LBRACK
|
2015-10-16 21:00:05 +00:00
|
|
|
l := &ast.ListType{
|
|
|
|
Lbrack: p.tok.Pos,
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
tok := p.scan()
|
|
|
|
switch tok.Type {
|
|
|
|
case token.NUMBER, token.FLOAT, token.STRING:
|
|
|
|
node, err := p.parseLiteralType()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
l.Add(node)
|
|
|
|
case token.COMMA:
|
|
|
|
// get next list item or we are at the end
|
|
|
|
continue
|
|
|
|
case token.BOOL:
|
|
|
|
// TODO(arslan) should we support? not supported by HCL yet
|
|
|
|
case token.LBRACK:
|
2015-10-16 22:14:40 +00:00
|
|
|
// TODO(arslan) should we support nested lists? Even though it's
|
2015-10-18 20:19:56 +00:00
|
|
|
// written in README of HCL, it's not a part of the grammar
|
|
|
|
// (not defined in parse.y)
|
2015-10-16 21:00:05 +00:00
|
|
|
case token.RBRACK:
|
|
|
|
// finished
|
|
|
|
l.Rbrack = p.tok.Pos
|
|
|
|
return l, nil
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unexpected token while parsing list: %s", tok.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-12 20:44:53 +00:00
|
|
|
// parseLiteralType parses a literal type and returns a LiteralType AST
|
2015-10-16 20:12:26 +00:00
|
|
|
func (p *Parser) parseLiteralType() (*ast.LiteralType, error) {
|
2015-10-14 22:27:35 +00:00
|
|
|
defer un(trace(p, "ParseLiteral"))
|
2015-10-12 20:44:53 +00:00
|
|
|
|
2015-10-16 20:12:26 +00:00
|
|
|
return &ast.LiteralType{
|
|
|
|
Token: p.tok,
|
2015-10-14 22:27:35 +00:00
|
|
|
}, nil
|
2015-10-12 20:44:53 +00:00
|
|
|
}
|
|
|
|
|
2015-10-11 21:20:17 +00:00
|
|
|
// scan returns the next token from the underlying scanner.
|
|
|
|
// If a token has been unscanned then read that instead.
|
2015-10-16 20:12:26 +00:00
|
|
|
func (p *Parser) scan() token.Token {
|
2015-10-11 21:20:17 +00:00
|
|
|
// If we have a token on the buffer, then return it.
|
2015-10-11 22:38:59 +00:00
|
|
|
if p.n != 0 {
|
|
|
|
p.n = 0
|
|
|
|
return p.tok
|
2015-10-11 21:20:17 +00:00
|
|
|
}
|
|
|
|
|
2015-10-11 22:38:59 +00:00
|
|
|
// store previous token
|
|
|
|
p.prevTok = p.tok
|
|
|
|
|
2015-10-11 21:20:17 +00:00
|
|
|
// Otherwise read the next token from the scanner and Save it to the buffer
|
|
|
|
// in case we unscan later.
|
2015-10-11 22:38:59 +00:00
|
|
|
p.tok = p.sc.Scan()
|
|
|
|
return p.tok
|
2015-10-07 22:38:39 +00:00
|
|
|
}
|
2015-10-11 21:20:17 +00:00
|
|
|
|
|
|
|
// unscan pushes the previously read token back onto the buffer.
|
2015-10-11 23:49:07 +00:00
|
|
|
func (p *Parser) unscan() {
|
|
|
|
p.n = 1
|
|
|
|
p.tok = p.prevTok
|
|
|
|
}
|
2015-10-11 23:27:43 +00:00
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// 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(")")
|
|
|
|
}
|