// grammar of the config file // // Notes: We parse exactly three modules ("input", "filter", "output") // in our configuration. Each module consists of two name-object pairs // that are nested. We don't want to allow limitless recursion here. // // config = mainmodule mainmodule mainmodule // mainmodule = name '{' module+ '}' // module = name object // name = 'input' | 'filter' | 'output' | etc. // object = '{' keyvalue* '}' // keyvalue = statement '=>' value // statement = name | if // value = literal | '[' literal ']' // literal = '"' name '"' package conf import ( "fmt" "io" "os" "github.com/Shugyousha/stasher/filter" "github.com/Shugyousha/stasher/input" "github.com/Shugyousha/stasher/manager" "github.com/Shugyousha/stasher/output" "github.com/Shugyousha/stasher/registry" ) // Having a Config to Manager function could be nice? Or we could just // return a Manager from here which may be even better... type parser struct { s scanner last token cur token } type moduledesc struct { name string keyvalues map[string]string } func newParser(s *scanner) *parser { p := &parser{ s: *s, } return p } func NewManagerFromConfig(r io.Reader) *manager.Manager { p := newParser(newScanner(r)) return p.parse() } func (p *parser) parse() *manager.Manager { var ( in input.Input filt filter.Filter out output.Output ) // TODO: use a loop to parse all the main modules? inputmdescs := p.mainmodule("input") fmt.Fprintf(os.Stderr, "input moduledescs: %#v\n", inputmdescs) for _, idesc := range inputmdescs { inputnewfunc, ok := registry.Inputregistry[idesc.name] if !ok { fmt.Fprintf(os.Stderr, "input module is not known: %q\n", idesc.name) } in = inputnewfunc(idesc.keyvalues) } filtermdescs := p.mainmodule("filter") fmt.Fprintf(os.Stderr, "filter moduledescs: %#v\n", filtermdescs) for _, fdesc := range filtermdescs { filternewfunc, ok := registry.Filterregistry[fdesc.name] if !ok { fmt.Fprintf(os.Stderr, "filter module is not known: %q\n", fdesc.name) } filt = filternewfunc(fdesc.keyvalues) } outputmdescs := p.mainmodule("output") fmt.Fprintf(os.Stderr, "output moduledescs: %#v\n", outputmdescs) for _, odesc := range outputmdescs { outputnewfunc, ok := registry.Outputregistry[odesc.name] if !ok { fmt.Fprintf(os.Stderr, "output module is not known: %q\n", odesc.name) } out = outputnewfunc(odesc.keyvalues) } return &manager.Manager{Input: in, Filter: filt, Output: out} } func (p *parser) advanceOneToken(place string) { var err error p.last = p.cur p.cur, err = p.s.Scan() if err != nil { fmt.Fprintf(os.Stderr, "Error: tokentype: %v, token: %q, err: %v\n", p.cur.Type, p.cur.Lit, err) } fmt.Fprintf(os.Stderr, "tokentype: %v, token: %q\n", p.cur.Type, p.cur.Lit) } func (p *parser) mainmodule(firstname string) []moduledesc { var moduledescs []moduledesc md := moduledesc{keyvalues: make(map[string]string)} p.advanceOneToken("mainmodule") if p.cur.Lit != firstname { fmt.Fprintf(os.Stderr, "error when parsing module. We were expecting name %q but got %q at line %d offset %d.\n", firstname, p.cur.Lit, p.cur.LineNr, p.cur.Offset) } p.advanceOneToken("mainmodule") if p.cur.Type != ObjectOpen { fmt.Fprintf(os.Stderr, "error when parsing module. We were expecting an opening bracket but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } for { p.advanceOneToken("more modules") if p.cur.Type != Name { if p.cur.Lit[0] == '}' { break } fmt.Fprintf(os.Stderr, "error when parsing module. We were expecting either a name or the end of the module but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } md.name = p.cur.Lit md.keyvalues = p.object(md.keyvalues) moduledescs = append(moduledescs, md) md = moduledesc{keyvalues: make(map[string]string)} } return moduledescs } func (p *parser) object(kv map[string]string) map[string]string { var more bool p.advanceOneToken("object") if p.cur.Lit[0] != '{' { fmt.Fprintf(os.Stderr, "error when parsing object. Was expecting an opening bracket but got %s at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } // hack to deal with case where there is no white space between // the delimiters. if p.cur.Lit == "{}" { return kv } for { more, kv = p.keyvalue(kv) if !more { break } } return kv } func (p *parser) keyvalue(kv map[string]string) (bool, map[string]string) { p.advanceOneToken("keyvalue") fmt.Fprintf(os.Stderr, "keyvalue: tokentype: %v, token: %q\n", p.cur.Type, p.cur.Lit) if p.cur.Type != Literal && p.cur.Type != Name { fmt.Fprintf(os.Stderr, "error when parsing keyvalue. Was expecting a name or a literal but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } lhs := p.cur.Lit p.advanceOneToken("keyvalue") if p.cur.Lit != "=>" { fmt.Fprintf(os.Stderr, "error when parsing keyvalue. Was expecting a '=>' but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } p.advanceOneToken("keyvalue") if p.cur.Type != Literal && p.cur.Type != Name { fmt.Fprintf(os.Stderr, "error when parsing keyvalue. Was expecting a literal or a name but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } kv[lhs] = p.cur.Lit tok, err := p.s.Peek() if err != nil { fmt.Fprintf(os.Stderr, "error when parsing keyvalue. Got an error when checking if there are more keyvalues: %v.\n", err) return false, kv } fmt.Fprintf(os.Stderr, "peeked %q (type %v) at line %d offset %d.\n", tok.Lit, tok.Type, tok.LineNr, tok.Offset) if tok.Type == ObjectClose { p.advanceOneToken("keyvaluelast") return false, kv } return true, kv }