diff --git a/parser/ast.go b/parser/ast.go index ea80ba6..76c5db3 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -14,39 +14,88 @@ const ( Object ) -// Node is an element in the parse tree. +// Node is an element in the abstract syntax tree. type Node interface { + node() String() string - Type() NodeType Pos() scanner.Pos - End() scanner.Pos } +func (IdentStatement) node() {} +func (BlockStatement) node() {} +func (AssignStatement) node() {} +func (ListStatement) node() {} +func (ObjectStatement) node() {} + // IdentStatement represents an identifier. type IdentStatement struct { - Token scanner.Token - Pos scanner.Pos // position of the literal - Value string + pos scanner.Pos // position of the literal + token scanner.Token + value string +} + +func (i IdentStatement) String() string { + return i.value +} + +func (i IdentStatement) Pos() scanner.Pos { + return i.pos } type BlockStatement struct { - Lbrace scanner.Pos // position of "{" - Rbrace scanner.Pos // position of "}" - List []Node // the nodes in lexical order + 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 - Rhs Node // right hand side of the assignment - Assign scanner.Pos // position of "=" + lhs Node // left hand side of the assignment + rhs Node // right hand side of the assignment + assign scanner.Pos // position of "=" +} + +func (a AssignStatement) String() string { + return a.lhs.String() + " = " + a.rhs.String() +} + +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 + 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 @@ -54,3 +103,21 @@ type ObjectStatement struct { Idents []Node // the idents in elements in lexical order BlockStatement } + +func (o ObjectStatement) String() string { + s := "" + + for i, n := range o.Idents { + s += n.String() + if i != len(o.Idents) { + s += " " + } + } + + s += o.BlockStatement.String() + return s +} + +func (o ObjectStatement) Pos() scanner.Pos { + return o.Idents[0].Pos() +} diff --git a/parser/parser.go b/parser/parser.go index 46a1729..a3513f6 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -3,14 +3,46 @@ package parser import "github.com/fatih/hcl/scanner" type Parser struct { - sc *scanner.Scanner + sc *scanner.Scanner + buf struct { + tok scanner.Token // last read token + n int // buffer size (max = 1) + } } func NewParser(src []byte) *Parser { return &Parser{ - sc: scanner.NewScanner(src), + sc: scanner.New(src), } } -func (p *Parser) Parse() { +func (p *Parser) Parse() Node { + tok := p.scan() + + switch tok.Type() { + case scanner.IDENT: + // p.parseStatement() + case scanner.EOF: + } + + return nil } + +// scan returns the next token from the underlying scanner. +// If a token has been unscanned then read that instead. +func (p *Parser) scan() scanner.Token { + // If we have a token on the buffer, then return it. + if p.buf.n != 0 { + p.buf.n = 0 + return p.buf.tok + } + + // Otherwise read the next token from the scanner and Save it to the buffer + // in case we unscan later. + p.buf.tok = p.sc.Scan() + + return p.buf.tok +} + +// unscan pushes the previously read token back onto the buffer. +func (p *Parser) unread() { p.buf.n = 1 } diff --git a/scanner/scanner.go b/scanner/scanner.go index ebf91db..e127240 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -42,15 +42,9 @@ type Scanner struct { tokPos Pos } -// NewScannerstring creates and initializes a new instance of Scanner using -// string src as its source content. -func NewScannerString(src string) *Scanner { - return NewScanner([]byte(src)) -} - -// NewScanner creates and initializes a new instance of Scanner using src as +// New creates and initializes a new instance of Scanner using src as // its source content. -func NewScanner(src []byte) *Scanner { +func New(src []byte) *Scanner { // even though we accept a src, we read from a io.Reader compatible type // (*bytes.Buffer). So in the future we might easily change it to streaming // read. diff --git a/scanner/scanner_test.go b/scanner/scanner_test.go index 47b91e1..5918ef2 100644 --- a/scanner/scanner_test.go +++ b/scanner/scanner_test.go @@ -182,7 +182,7 @@ func TestPosition(t *testing.T) { } } - s := NewScanner(buf.Bytes()) + s := New(buf.Bytes()) pos := Pos{"", 4, 1, 5} s.Scan() @@ -329,7 +329,7 @@ func TestRealExample(t *testing.T) { {EOF, ``}, } - s := NewScanner([]byte(complexHCL)) + s := New([]byte(complexHCL)) for _, l := range literals { tok := s.Scan() if l.token != tok.Type() { @@ -366,7 +366,7 @@ func TestError(t *testing.T) { } func testError(t *testing.T, src, pos, msg string, tok TokenType) { - s := NewScanner([]byte(src)) + s := New([]byte(src)) errorCalled := false s.Error = func(p Pos, m string) { @@ -401,7 +401,7 @@ func testTokenList(t *testing.T, tokenList []tokenPair) { fmt.Fprintf(buf, "%s\n", ident.text) } - s := NewScanner(buf.Bytes()) + s := New(buf.Bytes()) for _, ident := range tokenList { tok := s.Scan() if tok.Type() != ident.tok {