From e7ae30194771059523eb13638b72ce955364d16a Mon Sep 17 00:00:00 2001 From: gedi Date: Sun, 7 Jun 2015 11:18:46 +0300 Subject: [PATCH] begin with parser implementation --- gherkin/lexer/lexer.go | 13 +++++-- gherkin/parse.go | 86 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/gherkin/lexer/lexer.go b/gherkin/lexer/lexer.go index 48c6e17..88a8260 100644 --- a/gherkin/lexer/lexer.go +++ b/gherkin/lexer/lexer.go @@ -19,13 +19,20 @@ func New(r io.Reader) *Lexer { } } -func (l *Lexer) Next() (t *Token) { +func (l *Lexer) Next(skip ...TokenType) (t *Token) { if l.peek != nil { t = l.peek l.peek = nil - return + } else { + t = l.read() } - return l.read() + + for _, typ := range skip { + if t.Type == typ { + return l.Next(skip...) + } + } + return } func (l *Lexer) Peek() *Token { diff --git a/gherkin/parse.go b/gherkin/parse.go index 5cb41e3..b189a49 100644 --- a/gherkin/parse.go +++ b/gherkin/parse.go @@ -1,16 +1,22 @@ package gherkin +import ( + "errors" + "io" + "strings" + + "github.com/l3pp4rd/behat/gherkin/lexer" +) + type Tag string type Scenario struct { Steps []*Step Tags []Tag - Line string } type Background struct { Steps []*Step - Line string } type StepType string @@ -22,7 +28,6 @@ const ( ) type Step struct { - Line string Text string Type StepType } @@ -30,17 +35,74 @@ type Step struct { type Feature struct { Tags []Tag Description string - Line string Title string - Filename string Background *Background Scenarios []*Scenario } -// func Parse(r io.Reader) (*Feature, error) { -// in := bufio.NewReader(r) -// for line, err := in.ReadString(byte('\n')); err != nil; line, err = in.ReadString(byte('\n')) { -// ln := strings.TrimFunc(string(line), unicode.IsSpace) -// } -// return nil, nil -// } +var ErrNotFeature = errors.New("expected a file to begin with a feature definition") +var ErrEmpty = errors.New("the feature file is empty") +var ErrTagsNextToFeature = errors.New("tags must be a single line next to a feature definition") +var ErrSingleBackground = errors.New("there can only be a single background section") + +type parser struct { + lx *lexer.Lexer +} + +func Parse(r io.Reader) (*Feature, error) { + return (parser{lx: lexer.New(r)}).parseFeature() +} + +func (p parser) parseFeature() (*Feature, error) { + var tok *lexer.Token = p.lx.Next(lexer.COMMENT, lexer.NEW_LINE) + if tok.Type == lexer.EOF { + return nil, ErrEmpty + } + + ft := &Feature{} + if tok.Type == lexer.TAGS { + if p.lx.Peek().Type != lexer.FEATURE { + return ft, ErrTagsNextToFeature + } + ft.Tags = p.parseTags(tok.Value) + tok = p.lx.Next() + } + + if tok.Type != lexer.FEATURE { + return ft, ErrNotFeature + } + + ft.Title = tok.Value + var desc []string + for ; p.lx.Peek().Type == lexer.TEXT; tok = p.lx.Next() { + desc = append(desc, tok.Value) + } + ft.Description = strings.Join(desc, "\n") + + tok = p.lx.Next(lexer.COMMENT, lexer.NEW_LINE) + for ; tok.Type != lexer.EOF; p.lx.Next(lexer.COMMENT, lexer.NEW_LINE) { + if tok.Type == lexer.BACKGROUND { + if ft.Background != nil { + return ft, ErrSingleBackground + } + ft.Background = p.parseBackground() + continue + } + } + + return ft, nil +} + +func (p parser) parseBackground() *Background { + return nil +} + +func (p parser) parseTags(s string) (tags []Tag) { + for _, tag := range strings.Split(s, " ") { + t := strings.Trim(tag, "@ ") + if len(t) > 0 { + tags = append(tags, Tag(t)) + } + } + return +}