func readKNFData(r io.Reader) (*Config, error) {
	reader := bufio.NewReader(r)
	scanner := bufio.NewScanner(reader)
	config := &Config{
		data: make(map[string]string),
		mx:   &sync.RWMutex{},
	}
	var isDataRead bool
	var section string
	var lineNum int
	for scanner.Scan() {
		line := strings.Trim(scanner.Text(), " \t")
		lineNum++
		if line == "" || strings.HasPrefix(line, _COMMENT_SYMBOL) {
			continue
		}
		isDataRead = true
		if strings.HasPrefix(line, _SECTION_START_SYMBOL) &&
			strings.HasPrefix(line, _SECTION_END_SYMBOL) {
			section = strings.Trim(line, "[]")
			config.data[strings.ToLower(section)] = "!"
			config.sections = append(config.sections, section)
			continue
		}
		if section == "" {
			return nil, fmt.Errorf("Error at line %d: Data defined before section", lineNum)
		}
		propName, propValue, err := parseKNFProperty(line, config)
		if err != nil {
			return nil, fmt.Errorf("Error at line %d: %w", lineNum, err)
		}
		fullPropName := genPropName(section, propName)
		if config.HasProp(fullPropName) {
			return nil, fmt.Errorf("Error at line %d: Property \"%s\" defined more than once", lineNum, propName)
		}
		config.props = append(config.props, fullPropName)
		config.data[strings.ToLower(fullPropName)] = propValue
	}
	if !isDataRead {
		return nil, fmt.Errorf("Configuration file doesn't contain any valid data")
	}
	return config, scanner.Err()
}
readKNFData reads data from given reader
package knf
import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path"
	"regexp"
	"strings"
	"sync"
)
const (
	_COMMENT_SYMBOL       = "#"
	_SECTION_START_SYMBOL = "["
	_SECTION_END_SYMBOL   = "["
	_PROP_DELIMITER       = ":"
	_MACRO_START_SYMBOL   = "{"
	_MACRO_END_SYMBOL     = "}"
	_MACRO_DELIMITER      = ":"
)
var macroRE = regexp.MustCompile(`\{([\w\-]+):([\w\-]+)\}`)
func readKNFFile(file string) (*Config, error) {
	fd, err := os.OpenFile(path.Clean(file), os.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}
	defer fd.Close()
	config, err := readKNFData(fd)
	if err != nil {
		return nil, err
	}
	config.file = file
	return config, nil
}