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  }