diff --git a/parser/ast.go b/parser/ast.go index e54a5ba..8b0dc0a 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -21,13 +21,16 @@ type Node interface { Pos() scanner.Pos } -func (Source) node() {} -func (Ident) node() {} -func (BlockStatement) node() {} +func (Source) node() {} +func (Ident) node() {} + func (AssignStatement) node() {} -func (ListStatement) node() {} func (ObjectStatement) node() {} +func (LiteralType) node() {} +func (ObjectType) node() {} +func (ListType) node() {} + // Source represents a single HCL source file type Source struct { nodes []Node @@ -57,33 +60,13 @@ type Ident struct { } func (i *Ident) String() string { - return i.token.String() + return i.token.Text } func (i *Ident) Pos() scanner.Pos { return i.token.Pos } -type BlockStatement struct { - lbrace scanner.Pos // position of "{" - rbrace scanner.Pos // position of "}" - list []Node // the nodes in lexical order -} - -func (b *BlockStatement) String() string { - s := "{\n" - for _, n := range b.list { - s += n.String() + "\n" - } - - s += "}" - return s -} - -func (b *BlockStatement) Pos() scanner.Pos { - return b.lbrace -} - // AssignStatement represents an assignment type AssignStatement struct { lhs Node // left hand side of the assignment @@ -99,31 +82,10 @@ func (a *AssignStatement) Pos() scanner.Pos { return a.lhs.Pos() } -// ListStatement represents a list -type ListStatement struct { - lbrack scanner.Pos // position of "[" - rbrack scanner.Pos // position of "]" - list []Node // the elements in lexical order -} - -func (l *ListStatement) String() string { - s := "[\n" - for _, n := range l.list { - s += n.String() + ",\n" - } - - s += "]" - return s -} - -func (l *ListStatement) Pos() scanner.Pos { - return l.lbrack -} - -// ObjectStatment represents an object +// ObjectStatment represents an object statement type ObjectStatement struct { Idents []Node // the idents in elements in lexical order - BlockStatement + ObjectType } func (o *ObjectStatement) String() string { @@ -136,10 +98,66 @@ func (o *ObjectStatement) String() string { } } - s += o.BlockStatement.String() + s += o.ObjectType.String() return s } func (o *ObjectStatement) Pos() scanner.Pos { return o.Idents[0].Pos() } + +// LiteralType represents a literal of basic type. Valid types are: +// scanner.NUMBER, scanner.FLOAT, scanner.BOOL and scanner.STRING +type LiteralType struct { + token scanner.Token +} + +func (l *LiteralType) String() string { + return l.token.Text +} + +func (l *LiteralType) Pos() scanner.Pos { + return l.token.Pos +} + +// ListStatement represents a HCL List type +type ListType struct { + lbrack scanner.Pos // position of "[" + rbrack scanner.Pos // position of "]" + list []Node // the elements in lexical order +} + +func (l *ListType) String() string { + s := "[\n" + for _, n := range l.list { + s += n.String() + ",\n" + } + + s += "]" + return s +} + +func (l *ListType) Pos() scanner.Pos { + return l.lbrack +} + +// ObjectType represents a HCL Object Type +type ObjectType struct { + lbrace scanner.Pos // position of "{" + rbrace scanner.Pos // position of "}" + list []Node // the nodes in lexical order +} + +func (b *ObjectType) String() string { + s := "{\n" + for _, n := range b.list { + s += n.String() + "\n" + } + + s += "}" + return s +} + +func (b *ObjectType) Pos() scanner.Pos { + return b.lbrace +} diff --git a/parser/parser.go b/parser/parser.go index 3466af9..4d47810 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1,6 +1,7 @@ package parser import ( + "errors" "fmt" "github.com/fatih/hcl/scanner" @@ -29,10 +30,13 @@ func (p *Parser) Parse() (Node, error) { node := &Source{} for { - if n := p.parseStatement(); n != nil { - node.add(n) + n, err := p.parseNode() + if err != nil { + return nil, err } + node.add(n) + // break if we hit the end if p.tok.Type == scanner.EOF { break @@ -42,17 +46,20 @@ func (p *Parser) Parse() (Node, error) { return node, nil } -func (p *Parser) parseStatement() Node { - defer un(trace(p, "ParseStatement")) +func (p *Parser) parseNode() (Node, error) { + defer un(trace(p, "ParseNode")) tok := p.scan() + fmt.Println(tok) // debug + if tok.Type.IsLiteral() { if p.prevTok.Type.IsLiteral() { - return p.parseObject() + return p.parseObjectType() } - if tok := p.scan(); tok.Type == scanner.ASSIGN { + tok := p.scan() + if tok.Type == scanner.ASSIGN { return p.parseAssignment() } @@ -60,34 +67,45 @@ func (p *Parser) parseStatement() Node { return p.parseIdent() } - return nil + return nil, errors.New("not yet implemented") } -func (p *Parser) parseAssignment() Node { +func (p *Parser) parseAssignment() (*AssignStatement, error) { defer un(trace(p, "ParseAssignment")) - return &AssignStatement{ + a := &AssignStatement{ lhs: &Ident{ token: p.prevTok, }, assign: p.tok.Pos, - rhs: p.parseStatement(), } + + n, err := p.parseNode() + if err != nil { + return nil, err + } + + a.rhs = n + return a, nil } -func (p *Parser) parseIdent() Node { +func (p *Parser) parseIdent() (*Ident, error) { defer un(trace(p, "ParseIdent")) + if !p.tok.Type.IsLiteral() { + return nil, errors.New("can't parse non literal token") + } + return &Ident{ token: p.tok, - } + }, nil } -func (p *Parser) parseObject() Node { - return nil +func (p *Parser) parseObjectType() (*ObjectStatement, error) { + return nil, errors.New("ObjectStatement is not implemented yet") } -func (p *Parser) parseList() Node { - return nil +func (p *Parser) parseListType() (*ListType, error) { + return nil, errors.New("ListStatement is not implemented yet") } // scan returns the next token from the underlying scanner. diff --git a/parser/parser_test.go b/parser/parser_test.go index 9d1820a..054473a 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -2,7 +2,7 @@ package parser import "testing" -func TestAssignStatment(t *testing.T) { +func TestAssignStatement(t *testing.T) { src := `ami = "${var.foo}"` p := New([]byte(src)) p.enableTrace = true diff --git a/scanner/token.go b/scanner/token.go index 30b215d..6c62fa8 100644 --- a/scanner/token.go +++ b/scanner/token.go @@ -1,6 +1,9 @@ package scanner -import "strconv" +import ( + "fmt" + "strconv" +) // Token defines a single HCL token which can be obtained via the Scanner type Token struct { @@ -90,5 +93,5 @@ func (t TokenType) IsOperator() bool { return operator_beg < t && t < operator_e // applicable for certain token types, such as token.IDENT, // token.STRING, etc.. func (t Token) String() string { - return t.Text + return fmt.Sprintf("%s %s %s", t.Pos.String(), t.Type.String(), t.Text) }