~xenrox/go-scfg

e042ab15616e91f897729f861f0647cda1781114 — Simon Ser 11 months ago c2c7a15
Use own word splitting function

scfg word splitting is a bit different than shlex'. Let's just
implement our own parser.
4 files changed, 56 insertions(+), 9 deletions(-)

M go.mod
M go.sum
M reader.go
M reader_test.go
M go.mod => go.mod +1 -4
@@ 2,7 2,4 @@ module git.sr.ht/~emersion/go-scfg

go 1.15

require (
	github.com/davecgh/go-spew v1.1.1
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
)
require github.com/davecgh/go-spew v1.1.1

M go.sum => go.sum +0 -2
@@ 1,4 1,2 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=

M reader.go => reader.go +48 -3
@@ 6,8 6,7 @@ import (
	"fmt"
	"io"
	"os"

	"github.com/google/shlex"
	"strings"
)

// Block is a list of directives.


@@ 87,7 86,7 @@ func Read(r io.Reader) (Block, error) {
func readBlock(scanner *bufio.Scanner) (block Block, closingBrace bool, err error) {
	for scanner.Scan() {
		l := scanner.Text()
		words, err := shlex.Split(l)
		words, err := splitWords(l)
		if err != nil {
			return nil, false, fmt.Errorf("failed to parse configuration file: %v", err)
		} else if len(words) == 0 {


@@ 130,3 129,49 @@ func readBlock(scanner *bufio.Scanner) (block Block, closingBrace bool, err erro

	return block, closingBrace, nil
}

func splitWords(l string) ([]string, error) {
	var (
		words   []string
		sb      strings.Builder
		escape  bool
		quote   rune
		wantWSP bool
	)
	for _, ch := range l {
		switch {
		case escape:
			sb.WriteRune(ch)
			escape = false
		case wantWSP && (ch != ' ' && ch != '\t'):
			return words, fmt.Errorf("atom not allowed after quoted string")
		case ch == '\\':
			escape = true
		case quote != 0 && ch == quote:
			quote = 0
			wantWSP = true
		case quote == 0 && len(words) == 0 && sb.Len() == 0 && ch == '#':
			return nil, nil
		case quote == 0 && (ch == '\'' || ch == '"'):
			if sb.Len() > 0 {
				return words, fmt.Errorf("quoted string not allowed after atom")
			}
			quote = ch
		case quote == 0 && (ch == ' ' || ch == '\t'):
			if sb.Len() > 0 {
				words = append(words, sb.String())
			}
			sb.Reset()
			wantWSP = false
		default:
			sb.WriteRune(ch)
		}
	}
	if quote != 0 {
		return words, fmt.Errorf("unterminated quoted string")
	}
	if sb.Len() > 0 {
		words = append(words, sb.String())
	}
	return words, nil
}

M reader_test.go => reader_test.go +7 -0
@@ 128,6 128,13 @@ block4 {
			},
		},
	},
	{
		name: "quotes",
		src:  `"a \b ' \" c" 'd \e \' " f' a\"b`,
		want: Block{
			{Name: "a b ' \" c", Params: []string{"d e ' \" f", "a\"b"}},
		},
	},
}

func TestRead(t *testing.T) {