// 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 = module module module // module = name object name object // name = 'input' | 'filter' | 'output' | etc. // object = '{' keyvalue | keyvalue | ... '}' // keyvalue = statement '=>' value // statement = name | if // value = literal | '[' literal ']' // literal = '"' name '"' package conf import ( "fmt" "io" "os" ) // Having a Config to Manager function could be nice? Or we could just // return a Manager from here which may be even better... type Config struct { } type parser struct { s scanner last token cur token } func newParser(s *scanner) *parser { p := &parser{ s: *s, } return p } func NewConfig(r io.Reader) *Config { p := newParser(newScanner(r)) p.startparsing() return &Config{} } func (p *parser) startparsing() { p.module("input") p.module("filter") } 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 when parsing %s. Scanner returned an error %v.\n", place, err) os.Exit(1) } } func (p *parser) module(firstname string) { p.advanceOneToken("module") fmt.Fprintf(os.Stderr, "tokentype: %d, token: %q\n", p.cur.Type, p.cur.Lit) 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("module") if p.cur.Type != ObjectDelimiter { 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) } p.advanceOneToken("module") if p.cur.Type != Name { fmt.Fprintf(os.Stderr, "error when parsing module. We were expecting another name but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } p.object() p.advanceOneToken("module") if p.cur.Lit[0] != '}' { fmt.Fprintf(os.Stderr, "error when parsing module. Was expecting a closing bracket but got %s at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } } func (p *parser) object() { 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 } for { more := p.keyvalue() if !more { break } } } func (p *parser) keyvalue() bool { p.advanceOneToken("keyvalue") fmt.Fprintf(os.Stderr, "keyvalue: tokentype: %d, token: %q\n", p.cur.Type, p.cur.Lit) if p.cur.Type != Literal { fmt.Fprintf(os.Stderr, "error when parsing keyvalue. Was expecting a name but got %q at line %d offset %d.\n", p.cur.Lit, p.cur.LineNr, p.cur.Offset) } 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) } 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 } fmt.Fprintf(os.Stderr, "peeked %q (type %d) at line %d offset %d.\n", tok.Lit, tok.Type, tok.LineNr, tok.Offset) if tok.Type == ObjectDelimiter { p.advanceOneToken("keyvaluelast") return false } return true }