parse/parser.go
func Parse(filename string, r io.Reader) ([]*Group, error) {

	n := 0
	groups := make([]*Group, 0)
	scanner := bufio.NewScanner(r)

	// whether we're at the point of expectations or
	// not.
	settingExpectations := false

	var currentGroup *Group
	var currentRequest *Request

	for scanner.Scan() {
		n++
		line, err := ParseLine(n, scanner.Bytes())
		if err != nil {
			return nil, err
		}
		switch line.Type {
		case LineTypeGroupHeading:
			// new group
			if currentGroup != nil {
				if currentRequest != nil {
					currentGroup.Requests = append(currentGroup.Requests, currentRequest)
					currentRequest = nil
				}
				groups = append(groups, currentGroup)
			}
			title, err := getok(line.Regexp.FindSubmatch(line.Bytes), 1)
			if err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
			currentGroup = &Group{
				Filename: filename,
				Title:    title,
			}
		case LineTypeRequest:
			// new request
			if currentGroup == nil {
				return nil, &ErrLine{N: n, Err: errMissingGroupHeader}
			}
			if currentRequest != nil {
				currentGroup.Requests = append(currentGroup.Requests, currentRequest)
			}
			settingExpectations = false
			var err error
			currentRequest = &Request{}
			matches := line.Regexp.FindSubmatch(line.Bytes)
			if currentRequest.Method, err = getok(matches, 1); err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
			if currentRequest.Path, err = getok(matches, 2); err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
		case LineTypeCodeBlock:

			if currentRequest == nil {
				return nil, &ErrLine{N: n, Err: errUnexpectedCodeblock}
			}

			var bodyType string
			if len(line.Bytes) > 3 {
				bodyType = string(line.Bytes[3:])
			}

			var lines Lines
			var err error
			n, lines, err = scancodeblock(n, scanner)
			if err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
			if settingExpectations {
				currentRequest.ExpectedBody = lines
				currentRequest.ExpectedBodyType = bodyType
			} else {
				currentRequest.Body = lines
				currentRequest.BodyType = bodyType
			}

		case LineTypeDetail:
			if currentRequest == nil && currentGroup == nil {
				return nil, &ErrLine{N: n, Err: errUnexpectedDetails}
			}
			if currentRequest == nil {
				currentGroup.Details = append(currentGroup.Details, line)
				continue
			}
			if settingExpectations {
				currentRequest.ExpectedDetails = append(currentRequest.ExpectedDetails, line)
			} else {
				currentRequest.Details = append(currentRequest.Details, line)
			}
		case LineTypeParam:
			if currentRequest == nil && currentGroup == nil {
				return nil, &ErrLine{N: n, Err: errUnexpectedParams}
			}
			if settingExpectations {
				return nil, &ErrLine{N: n, Err: errUnexpectedParams}
			}
			currentRequest.Params = append(currentRequest.Params, line)
		case LineTypeSeparator:
			settingExpectations = true
		}

	}

	if currentGroup == nil {
		return nil, &ErrLine{N: n, Err: errMissingGroupHeader}
	}
	if currentRequest != nil {
		currentGroup.Requests = append(currentGroup.Requests, currentRequest)
	}
	groups = append(groups, currentGroup)

	return groups, nil
}
func Parse(filename string, r io.Reader) ([]*Group, error) {

	n := 0
	groups := make([]*Group, 0)
	scanner := bufio.NewScanner(r)

	// whether we're at the point of expectations or
	// not.
	settingExpectations := false

	var currentGroup *Group
	var currentRequest *Request

	for scanner.Scan() {
		n++
		line, err := ParseLine(n, scanner.Bytes())
		if err != nil {
			return nil, err
		}
		switch line.Type {
		case LineTypeGroupHeading:
			// new group
			if currentGroup != nil {
				if currentRequest != nil {
					currentGroup.Requests = append(currentGroup.Requests, currentRequest)
					currentRequest = nil
				}
				groups = append(groups, currentGroup)
			}
			title, err := getok(line.Regexp.FindSubmatch(line.Bytes), 1)
			if err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
			currentGroup = &Group{
				Filename: filename,
				Title:    title,
			}
		case LineTypeRequest:
			// new request
			if currentGroup == nil {
				return nil, &ErrLine{N: n, Err: errMissingGroupHeader}
			}
			if currentRequest != nil {
				currentGroup.Requests = append(currentGroup.Requests, currentRequest)
			}
			settingExpectations = false
			var err error
			currentRequest = &Request{}
			matches := line.Regexp.FindSubmatch(line.Bytes)
			if currentRequest.Method, err = getok(matches, 1); err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
			if currentRequest.Path, err = getok(matches, 2); err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
		case LineTypeCodeBlock:

			if currentRequest == nil {
				return nil, &ErrLine{N: n, Err: errUnexpectedCodeblock}
			}

			var bodyType string
			if len(line.Bytes) > 3 {
				bodyType = string(line.Bytes[3:])
			}

			var lines Lines
			var err error
			n, lines, err = scancodeblock(n, scanner)
			if err != nil {
				return nil, &ErrLine{N: n, Err: err}
			}
			if settingExpectations {
				currentRequest.ExpectedBody = lines
				currentRequest.ExpectedBodyType = bodyType
			} else {
				currentRequest.Body = lines
				currentRequest.BodyType = bodyType
			}

		case LineTypeDetail:
			if currentRequest == nil && currentGroup == nil {
				return nil, &ErrLine{N: n, Err: errUnexpectedDetails}
			}
			if currentRequest == nil {
				currentGroup.Details = append(currentGroup.Details, line)
				continue
			}
			if settingExpectations {
				currentRequest.ExpectedDetails = append(currentRequest.ExpectedDetails, line)
			} else {
				currentRequest.Details = append(currentRequest.Details, line)
			}
		case LineTypeParam:
			if currentRequest == nil && currentGroup == nil {
				return nil, &ErrLine{N: n, Err: errUnexpectedParams}
			}
			if settingExpectations {
				return nil, &ErrLine{N: n, Err: errUnexpectedParams}
			}
			currentRequest.Params = append(currentRequest.Params, line)
		case LineTypeSeparator:
			settingExpectations = true
		}

	}

	if currentGroup == nil {
		return nil, &ErrLine{N: n, Err: errMissingGroupHeader}
	}
	if currentRequest != nil {
		currentGroup.Requests = append(currentGroup.Requests, currentRequest)
	}
	groups = append(groups, currentGroup)

	return groups, nil
}
// Parse parses a file.