From 500af457c12a8c11c4b32301f6ab324248c4ccaa Mon Sep 17 00:00:00 2001 From: Kevin Chabowski Date: Thu, 1 May 2014 14:22:15 +0200 Subject: Introduced new config file format --- conflang.go | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 conflang.go (limited to 'conflang.go') diff --git a/conflang.go b/conflang.go new file mode 100644 index 0000000..0b4a856 --- /dev/null +++ b/conflang.go @@ -0,0 +1,160 @@ +package main + +import ( + "bufio" + "fmt" + "io" +) + +type toktype int + +const ( + tText toktype = iota + tNextCmd +) + +type token struct { + Type toktype + Data string + Line int +} + +func scan(r io.Reader, tokens chan<- token, errch chan<- error) { + emit := func(t toktype, d []byte, line int) { + if t == tText && len(d) == 0 { + return + } + tokens <- token{t, string(d), line} + } + + err := func() error { + br := bufio.NewReader(r) + + escaped := false + data := []byte{} + line := 1 + + for { + b, err := br.ReadByte() + + switch err { + case nil: + case io.EOF: + return nil + default: + return ErrorAtLine{line, err} + } + + if b == '\n' { + line++ + } + + if escaped { + data = append(data, b) + escaped = false + continue + } + + switch b { + case '\\': + escaped = true + case ' ', '\t': + emit(tText, data, line) + data = data[:0] + case '\n': + emit(tText, data, line) + data = data[:0] + emit(tNextCmd, nil, line) + default: + data = append(data, b) + } + } + + emit(tText, data, line) + emit(tNextCmd, nil, line) + return nil + }() + + close(tokens) + errch <- err +} + +type command struct { + Name string + Params []string + Line int +} + +func parse(tokens <-chan token, cmds chan<- command) { + defer close(cmds) + + startcmd := true + cmd := command{"", make([]string, 0), 0} + + for tok := range tokens { + switch tok.Type { + case tText: + if startcmd { + cmd.Name = tok.Data + cmd.Line = tok.Line + startcmd = false + } else { + cmd.Params = append(cmd.Params, tok.Data) + } + case tNextCmd: + if !startcmd { + cmds <- cmd + cmd.Name = "" + cmd.Params = make([]string, 0) + startcmd = true + } + } + } +} + +type cmdfunc func(params []string) error + +var commands = map[string]cmdfunc{ + "nop": func(_ []string) error { return nil }, +} + +func RegisterCommand(name string, f cmdfunc) { + commands[name] = f +} + +type ErrorAtLine struct { + Line int + Err error +} + +func (err ErrorAtLine) Error() string { + return fmt.Sprintf("%s (at line %d)", err.Err, err.Line) +} + +type CommandNotFound string + +func (c CommandNotFound) Error() string { + return fmt.Sprintf("Command \"%s\" not found", c) +} + +func RunCommands(r io.Reader) error { + errch := make(chan error) + tokens := make(chan token) + cmds := make(chan command) + + go scan(r, tokens, errch) + go parse(tokens, cmds) + + for cmd := range cmds { + f, ok := commands[cmd.Name] + if !ok { + return ErrorAtLine{cmd.Line, CommandNotFound(cmd.Name)} + } + + if err := f(cmd.Params); err != nil { + return ErrorAtLine{cmd.Line, err} + } + } + + return <-errch +} -- cgit v1.2.3-54-g00ecf