diff --git a/parser/ast.go b/parser/ast.go index 8170115..f9d5924 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -8,28 +8,48 @@ type Node interface { Pos() scanner.Pos } -func (Source) node() {} -func (Ident) node() {} - -func (AssignStatement) node() {} -func (ObjectStatement) node() {} - -func (LiteralType) node() {} +func (ObjectList) node() {} +func (ObjectItem) node() {} func (ObjectType) node() {} +func (LiteralType) node() {} func (ListType) node() {} +func (Ident) node() {} -// Source represents a single HCL source file -type Source struct { - nodes []Node +// ObjectList represents a list of ObjectItems. An HCL file itself is an +// ObjectList. +type ObjectList struct { + items []*ObjectItem } -func (s *Source) add(node Node) { - s.nodes = append(s.nodes, node) +func (o *ObjectList) add(item *ObjectItem) { + o.items = append(o.items, item) } -func (s *Source) Pos() scanner.Pos { +func (o *ObjectList) Pos() scanner.Pos { // always returns the uninitiliazed position - return s.nodes[0].Pos() + return o.items[0].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 { + // key is either an Identifier or a String. The slice is only one lenght + // long, however if it's a nested object it'll can be larger than one. In + // that case "assign" is invalid as there is no assignments for a nested + // object. + key []Ident + + // assign contains the position of "=", if any + 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 + // Object. + val Node +} + +func (o *ObjectItem) Pos() scanner.Pos { + return o.key[0].Pos() } // IdentStatement represents an identifier. @@ -41,27 +61,6 @@ func (i *Ident) Pos() scanner.Pos { return i.token.Pos } -// AssignStatement represents an assignment -type AssignStatement struct { - lhs Node // left hand side of the assignment - rhs Node // right hand side of the assignment - assign scanner.Pos // position of "=" -} - -func (a *AssignStatement) Pos() scanner.Pos { - return a.lhs.Pos() -} - -// ObjectStatment represents an object statement -type ObjectStatement struct { - Idents []Node // the idents in elements in lexical order - ObjectType -} - -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 { diff --git a/parser/parser.go b/parser/parser.go index e7fe9c1..8c062c2 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -29,10 +29,10 @@ var errEofToken = errors.New("EOF token found") // Parse returns the fully parsed source and returns the abstract syntax tree. func (p *Parser) Parse() (Node, error) { defer un(trace(p, "ParseSource")) - node := &Source{} + node := &ObjectList{} for { - n, err := p.parseNode() + n, err := p.parseObjectItem() if err == errEofToken { break // we are finished } @@ -47,15 +47,15 @@ func (p *Parser) Parse() (Node, error) { return node, nil } -func (p *Parser) parseNode() (Node, error) { - defer un(trace(p, "ParseNode")) +func (p *Parser) parseObjectItem() (*ObjectItem, error) { + defer un(trace(p, "ParseObjectItem")) tok := p.scan() fmt.Println(tok) // debug switch tok.Type { case scanner.ASSIGN: - return p.parseAssignment() + // return p.parseAssignment() case scanner.LBRACK: // return p.parseListType() case scanner.LBRACE: @@ -66,39 +66,9 @@ func (p *Parser) parseNode() (Node, error) { return nil, errEofToken } - if tok.Type.IsIdentifier() { - if p.prevTok.Type.IsIdentifier() { - return p.parseObjectStatement() - } - - if tok.Type.IsLiteral() { - return p.parseLiteralType() - } - return p.parseIdent() - } - return nil, fmt.Errorf("not yet implemented: %s", tok.Type) } -// parseAssignment parses an assignment and returns a AssignStatement AST -func (p *Parser) parseAssignment() (*AssignStatement, error) { - defer un(trace(p, "ParseAssignment")) - a := &AssignStatement{ - lhs: &Ident{ - token: p.prevTok, - }, - assign: p.tok.Pos, - } - - n, err := p.parseNode() - if err != nil { - return nil, err - } - - a.rhs = n - return a, nil -} - // parseIdent parses a generic identifier and returns a Ident AST func (p *Parser) parseIdent() (*Ident, error) { defer un(trace(p, "ParseIdent")) @@ -117,14 +87,6 @@ func (p *Parser) parseLiteralType() (*LiteralType, error) { }, nil } -// parseObjectStatement parses an object statement returns an ObjectStatement -// AST. ObjectsStatements represents both normal and nested objects statement -func (p *Parser) parseObjectStatement() (*ObjectStatement, error) { - defer un(trace(p, "ParseObjectStatement")) - - return nil, errors.New("ObjectStatement is not implemented yet") -} - // parseObjectType parses an object type and returns a ObjectType AST func (p *Parser) parseObjectType() (*ObjectType, error) { return nil, errors.New("ObjectType is not implemented yet") diff --git a/parser/parser_test.go b/parser/parser_test.go index 63004f5..4760e71 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1,6 +1,9 @@ package parser -import "testing" +import ( + "fmt" + "testing" +) func TestAssignStatement(t *testing.T) { src := `ami = "${var.foo}"` @@ -14,4 +17,13 @@ func TestAssignStatement(t *testing.T) { if n.Pos().Line != 1 { t.Errorf("AssignStatement position is wrong\n\twant: '%d'\n\tgot : '%d'", 1, n.Pos().Line) } + + n1, ok := n.(*ObjectList) + if !ok { + t.Fatal("First Node should be of type Source") + } + + for _, ns := range n1.nodes { + fmt.Printf("ns = %+v\n", ns) + } }