godog/gherkin/gherkin.go

137 строки
2,6 КиБ
Go

package gherkin
import (
"bufio"
"fmt"
"io"
"strings"
)
type Parser interface {
StopAtFirstError(b bool)
Parse(s Scanner, m Matcher) (err error)
}
/*
The scanner reads a gherkin doc (typically read from a .feature file) and creates a token for
each line. The tokens are passed to the parser, which outputs an AST (Abstract Syntax Tree).
If the scanner sees a # language header, it will reconfigure itself dynamically to look for
Gherkin keywords for the associated language. The keywords are defined in gherkin-languages.json.
*/
type Scanner interface {
Scan() (line *Line, atEof bool, err error)
}
type Builder interface {
Build(*Token) (bool, error)
StartRule(RuleType) (bool, error)
EndRule(RuleType) (bool, error)
Reset()
}
type Token struct {
Type TokenType
Keyword string
Text string
Items []*LineSpan
GherkinDialect string
Indent string
Location *Location
}
func (t *Token) IsEOF() bool {
return t.Type == TokenType_EOF
}
func (t *Token) String() string {
return fmt.Sprintf("%s: %s/%s", t.Type.Name(), t.Keyword, t.Text)
}
type LineSpan struct {
Column int
Text string
}
func (l *LineSpan) String() string {
return fmt.Sprintf("%d:%s", l.Column, l.Text)
}
type parser struct {
builder Builder
stopAtFirstError bool
}
func NewParser(b Builder) Parser {
return &parser{
builder: b,
}
}
func (p *parser) StopAtFirstError(b bool) {
p.stopAtFirstError = b
}
func NewScanner(r io.Reader) Scanner {
return &scanner{
s: bufio.NewScanner(r),
line: 0,
}
}
type scanner struct {
s *bufio.Scanner
line int
}
func (t *scanner) Scan() (line *Line, atEof bool, err error) {
scanning := t.s.Scan()
if !scanning {
err = t.s.Err()
if err == nil {
atEof = true
}
}
if err == nil {
t.line += 1
str := t.s.Text()
line = &Line{str, t.line, strings.TrimLeft(str, " \t"), atEof}
}
return
}
type Line struct {
LineText string
LineNumber int
TrimmedLineText string
AtEof bool
}
func (g *Line) Indent() int {
return len(g.LineText) - len(g.TrimmedLineText)
}
func (g *Line) IsEmpty() bool {
return len(g.TrimmedLineText) == 0
}
func (g *Line) IsEof() bool {
return g.AtEof
}
func (g *Line) StartsWith(prefix string) bool {
return strings.HasPrefix(g.TrimmedLineText, prefix)
}
func ParseFeature(in io.Reader) (feature *Feature, err error) {
builder := NewAstBuilder()
parser := NewParser(builder)
parser.StopAtFirstError(false)
matcher := NewMatcher(GherkinDialectsBuildin())
scanner := NewScanner(in)
err = parser.Parse(scanner, matcher)
return builder.GetFeature(), err
}