From 87c53831a23d70389301056fda6dd7f6583bd66c Mon Sep 17 00:00:00 2001 From: Christopher Ramey Date: Thu, 20 Aug 2020 04:34:22 -0800 Subject: [PATCH] Code cleanup --- config.go | 231 +---------------------------------------------------- parser.go | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 229 deletions(-) create mode 100644 parser.go diff --git a/config.go b/config.go index b181d33..27ac166 100644 --- a/config.go +++ b/config.go @@ -1,13 +1,5 @@ package main -import ( - "bufio" - "fmt" - "os" - "strconv" - "strings" -) - type AlrmConfig struct { Groups []*AlrmGroup Interval int @@ -19,10 +11,6 @@ func (ac *AlrmConfig) NewGroup() *AlrmGroup { return group } -func (ac *AlrmConfig) LastGroup() *AlrmGroup { - return ac.Groups[len(ac.Groups)-1] -} - type AlrmGroup struct { Name string Hosts []*AlrmHost @@ -34,231 +22,16 @@ func (ag *AlrmGroup) NewHost() *AlrmHost { return host } -func (ag *AlrmGroup) LastHost() *AlrmHost { - return ag.Hosts[len(ag.Hosts)-1] -} - type AlrmHost struct { Name string Address string } -const ( - TK_NONE = iota - TK_SET - TK_MONITOR - TK_GROUP - TK_HOST -) - -func stateName(s int) string { - switch s { - case TK_NONE: - return "TK_NONE" - case TK_SET: - return "TK_SET" - case TK_MONITOR: - return "TK_MONTIOR" - case TK_GROUP: - return "TK_GROUP" - case TK_HOST: - return "TK_HOST" - default: - return "UNKNOWN" - } -} - func ReadConfig(fn string) (*AlrmConfig, error) { - file, err := os.Open(fn) - if err != nil { - return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error()) - } - defer file.Close() - - config := &AlrmConfig{} parser := &Parser{} - - scan := bufio.NewScanner(file) - scan.Split(parser.Split) - for scan.Scan() { - tk := scan.Text() - - stateswitch: - switch parser.GetState() { - case TK_NONE: - switch strings.ToLower(tk) { - case "monitor": - parser.SetState(TK_MONITOR) - case "set": - parser.SetState(TK_SET) - default: - return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"", - fn, parser.Line+1, tk) - } - - case TK_SET: - key := strings.ToLower(tk) - if scan.Scan() { - value := scan.Text() - switch key { - case "interval": - config.Interval, err = strconv.Atoi(value) - if err != nil { - return nil, fmt.Errorf( - "invalid number for interval, line %d: \"%s\"", - parser.Line+1, value, - ) - } - default: - return nil, fmt.Errorf("invalid key for set, line %d: \"%s\"", - parser.Line+1, tk, - ) - } - } - parser.PrevState() - - case TK_MONITOR: - switch strings.ToLower(tk) { - case "host": - config.NewGroup().NewHost() - parser.SetState(TK_HOST) - - case "group": - config.NewGroup() - parser.SetState(TK_GROUP) - - default: - parser.PrevState() - goto stateswitch - } - - case TK_GROUP: - group := config.LastGroup() - - switch strings.ToLower(tk) { - case "host": - group.NewHost() - parser.SetState(TK_HOST) - continue - - default: - if group.Name == "" { - group.Name = tk - continue - } - - parser.PrevState() - goto stateswitch - } - - case TK_HOST: - host := config.LastGroup().LastHost() - if host.Name == "" { - host.Name = tk - continue - } - - switch strings.ToLower(tk) { - case "address": - if scan.Scan() { - host.Address = scan.Text() - } - - default: - parser.PrevState() - goto stateswitch - } - - default: - return nil, fmt.Errorf("unknown parser state: %d", parser.GetState()) - } - } - if err := scan.Err(); err != nil { + config, err := parser.Parse(fn) + if err != nil { return nil, err } return config, nil } - -type Parser struct { - Line int - states []int -} - -func (pr *Parser) GetState() int { - if len(pr.states) < 1 { - return TK_NONE - } - return pr.states[len(pr.states)-1] -} - -func (pr *Parser) SetState(state int) { - //fmt.Printf("%s -> %s\n", stateName(pr.GetState()), stateName(state)) - pr.states = append(pr.states, state) -} - -func (pr *Parser) PrevState() int { - if len(pr.states) > 0 { - pr.states = pr.states[:len(pr.states)-1] - } - return pr.GetState() -} - -func (pr *Parser) Split(data []byte, atEOF bool) (int, []byte, error) { - if atEOF && len(data) == 0 { - return 0, nil, nil - } - - var ignoreline bool - var started bool - var startidx int - var quote byte - - for i := 0; i < len(data); i++ { - c := data[i] - switch c { - case '\f', '\n', '\r': - pr.Line++ - if ignoreline { - ignoreline = false - continue - } - fallthrough - - case ' ', '\t', '\v': - if started && quote == 0 { - return i + 1, data[startidx:i], nil - } - - case '\'', '"', '`': - if started && quote == c { - return i + 1, data[startidx:i], nil - } - - if quote == 0 { - quote = c - } - - case '#': - if !started { - ignoreline = true - } - - default: - if !ignoreline && !started { - started = true - startidx = i - } - } - } - - if atEOF { - if ignoreline { - return len(data), nil, nil - } - if started { - return len(data), data[startidx:], nil - } - } - - return 0, nil, nil -} diff --git a/parser.go b/parser.go new file mode 100644 index 0000000..144cb4b --- /dev/null +++ b/parser.go @@ -0,0 +1,235 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +const ( + TK_NONE = iota + TK_SET + TK_MONITOR + TK_GROUP + TK_HOST +) + +type Parser struct { + Line int + states []int +} + +func (p *Parser) Parse(fn string) (*AlrmConfig, error) { + file, err := os.Open(fn) + if err != nil { + return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error()) + } + defer file.Close() + + config := &AlrmConfig{} + var group *AlrmGroup + var host *AlrmHost + + scan := bufio.NewScanner(file) + scan.Split(p.Split) + for scan.Scan() { + tk := scan.Text() + stateswitch: + switch p.state() { + case TK_NONE: + switch strings.ToLower(tk) { + case "monitor": + p.setState(TK_MONITOR) + case "set": + p.setState(TK_SET) + default: + return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"", + fn, p.Line+1, tk) + } + + case TK_SET: + key := strings.ToLower(tk) + if scan.Scan() { + value := scan.Text() + switch key { + case "interval": + config.Interval, err = strconv.Atoi(value) + if err != nil { + return nil, fmt.Errorf( + "invalid number for interval, line %d: \"%s\"", + p.Line+1, value, + ) + } + default: + return nil, fmt.Errorf("invalid key for set, line %d: \"%s\"", + p.Line+1, tk, + ) + } + } + p.prevState() + + case TK_MONITOR: + switch strings.ToLower(tk) { + case "host": + host = config.NewGroup().NewHost() + p.setState(TK_HOST) + + case "group": + group = config.NewGroup() + p.setState(TK_GROUP) + + default: + p.prevState() + goto stateswitch + } + + case TK_GROUP: + if group == nil { + return nil, fmt.Errorf("Group token without initialization") + } + + switch strings.ToLower(tk) { + case "host": + host = group.NewHost() + p.setState(TK_HOST) + continue + + default: + if group.Name == "" { + group.Name = tk + continue + } + + p.prevState() + goto stateswitch + } + + case TK_HOST: + if host == nil { + return nil, fmt.Errorf("Host token without initialization") + } + + if host.Name == "" { + host.Name = tk + continue + } + + switch strings.ToLower(tk) { + case "address": + if scan.Scan() { + host.Address = scan.Text() + } + + default: + p.prevState() + goto stateswitch + } + + default: + return nil, fmt.Errorf("unknown parser state: %d", p.state()) + } + } + if err := scan.Err(); err != nil { + return nil, err + } + return config, nil +} + +func (p *Parser) state() int { + if len(p.states) < 1 { + return TK_NONE + } + return p.states[len(p.states)-1] +} + +func (p *Parser) setState(state int) { + // fmt.Printf("%s", p.stateName()) + p.states = append(p.states, state) + // fmt.Printf(" -> %s\n", p.stateName()) +} + +func (p *Parser) prevState() int { + if len(p.states) > 0 { + p.states = p.states[:len(p.states)-1] + } + return p.state() +} + +func (p *Parser) stateName() string { + switch p.state() { + case TK_NONE: + return "TK_NONE" + case TK_SET: + return "TK_SET" + case TK_MONITOR: + return "TK_MONTIOR" + case TK_GROUP: + return "TK_GROUP" + case TK_HOST: + return "TK_HOST" + default: + return "UNKNOWN" + } +} + +func (p *Parser) Split(data []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + var ignoreline bool + var started bool + var startidx int + var quote byte + + for i := 0; i < len(data); i++ { + c := data[i] + switch c { + case '\f', '\n', '\r': + p.Line++ + if ignoreline { + ignoreline = false + continue + } + fallthrough + + case ' ', '\t', '\v': + if started && quote == 0 { + return i + 1, data[startidx:i], nil + } + + case '\'', '"', '`': + if started && quote == c { + return i + 1, data[startidx:i], nil + } + + if quote == 0 { + quote = c + } + + case '#': + if !started { + ignoreline = true + } + + default: + if !ignoreline && !started { + started = true + startidx = i + } + } + } + + if atEOF { + if ignoreline { + return len(data), nil, nil + } + if started { + return len(data), data[startidx:], nil + } + } + + return 0, nil, nil +}