github.com/maps90/godog@v0.7.5-0.20170923143419-0093943021d4/gherkin/gherkin.go (about) 1 package gherkin 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "strings" 8 ) 9 10 type Parser interface { 11 StopAtFirstError(b bool) 12 Parse(s Scanner, m Matcher) (err error) 13 } 14 15 /* 16 The scanner reads a gherkin doc (typically read from a .feature file) and creates a token for 17 each line. The tokens are passed to the parser, which outputs an AST (Abstract Syntax Tree). 18 19 If the scanner sees a # language header, it will reconfigure itself dynamically to look for 20 Gherkin keywords for the associated language. The keywords are defined in gherkin-languages.json. 21 */ 22 type Scanner interface { 23 Scan() (line *Line, atEof bool, err error) 24 } 25 26 type Builder interface { 27 Build(*Token) (bool, error) 28 StartRule(RuleType) (bool, error) 29 EndRule(RuleType) (bool, error) 30 Reset() 31 } 32 33 type Token struct { 34 Type TokenType 35 Keyword string 36 Text string 37 Items []*LineSpan 38 GherkinDialect string 39 Indent string 40 Location *Location 41 } 42 43 func (t *Token) IsEOF() bool { 44 return t.Type == TokenType_EOF 45 } 46 func (t *Token) String() string { 47 return fmt.Sprintf("%s: %s/%s", t.Type.Name(), t.Keyword, t.Text) 48 } 49 50 type LineSpan struct { 51 Column int 52 Text string 53 } 54 55 func (l *LineSpan) String() string { 56 return fmt.Sprintf("%d:%s", l.Column, l.Text) 57 } 58 59 type parser struct { 60 builder Builder 61 stopAtFirstError bool 62 } 63 64 func NewParser(b Builder) Parser { 65 return &parser{ 66 builder: b, 67 } 68 } 69 70 func (p *parser) StopAtFirstError(b bool) { 71 p.stopAtFirstError = b 72 } 73 74 func NewScanner(r io.Reader) Scanner { 75 return &scanner{ 76 s: bufio.NewScanner(r), 77 line: 0, 78 } 79 } 80 81 type scanner struct { 82 s *bufio.Scanner 83 line int 84 } 85 86 func (t *scanner) Scan() (line *Line, atEof bool, err error) { 87 scanning := t.s.Scan() 88 if !scanning { 89 err = t.s.Err() 90 if err == nil { 91 atEof = true 92 } 93 } 94 if err == nil { 95 t.line += 1 96 str := t.s.Text() 97 line = &Line{str, t.line, strings.TrimLeft(str, " \t"), atEof} 98 } 99 return 100 } 101 102 type Line struct { 103 LineText string 104 LineNumber int 105 TrimmedLineText string 106 AtEof bool 107 } 108 109 func (g *Line) Indent() int { 110 return len(g.LineText) - len(g.TrimmedLineText) 111 } 112 113 func (g *Line) IsEmpty() bool { 114 return len(g.TrimmedLineText) == 0 115 } 116 117 func (g *Line) IsEof() bool { 118 return g.AtEof 119 } 120 121 func (g *Line) StartsWith(prefix string) bool { 122 return strings.HasPrefix(g.TrimmedLineText, prefix) 123 } 124 125 func ParseFeature(in io.Reader) (feature *Feature, err error) { 126 127 builder := NewAstBuilder() 128 parser := NewParser(builder) 129 parser.StopAtFirstError(false) 130 matcher := NewMatcher(GherkinDialectsBuildin()) 131 132 scanner := NewScanner(in) 133 134 err = parser.Parse(scanner, matcher) 135 136 return builder.GetFeature(), err 137 }