package nginx import ( "bufio" "io" "regexp" "strings" ) // Decoder implements parser.Decoder type Decoder struct{ reader io.Reader } // Decode is the main function of the parser.Decoder interface func (d *Decoder) Decode(v interface{}) error { // check 'v' if v == nil { return ErrNullReceiver } vcast, ok := v.(*Line) if !ok { return ErrInvalidReceiver } vcast.Lines = make([]*Line, 0) r := bufio.NewReader(d.reader) n := -1 // line number // regexes reSection := regexp.MustCompile(`(?m)^([a-z0-9_-]+)\s*([^\{]*)\s*\{$`) reAssign := regexp.MustCompile(`(?m)^([a-z0-9_-]+)\s+([^;#]+);$`) reInclude := regexp.MustCompile(`(?m)^include\s+([^;#]+);$`) // actual section stack stack := make([]*Line, 0) eof := false for { n++ if eof { break } l := &Line{Type: NONE} // 1. read line notrim, err := r.ReadString('\n') if err == io.EOF { if len(notrim) > 0 { eof = true } else { break } } else if err != nil { return &LineError{n, err} } notrim = strings.Trim(notrim, "\r\n") // 2. ignore empty line := strings.Trim(notrim, " \t\r\n") if len(line) < 1 { continue } // 2. comment if line[0] == '#' { l.Type = COMMENT l.Components = []string{line[1:]} } else if line[0] == ';' { l.Type = COLONCOMMENT l.Components = []string{line[1:]} } else if line[0] == '}' { l.Type = SECTIONEND if len(stack) < 1 { return &LineError{n, ErrUnexpectedSectionClose} } } // 3. section if l.Type == NONE { match := reSection.FindStringSubmatch(line) if match != nil { l.Type = SECTION l.Components = make([]string, 0) l.Components = append(l.Components, match[1]) if len(match[2]) > 0 { l.Components = append(l.Components, strings.Trim(match[2], " \t")) } l.Lines = make([]*Line, 0) stack = append(stack, l) } } // 4. include if l.Type == NONE { match := reInclude.FindStringSubmatch(line) if match != nil { l.Type = INCLUDE l.Components = []string{match[1]} } } // 5. assignment if l.Type == NONE { match := reAssign.FindStringSubmatch(line) if match != nil { l.Type = ASSIGNMENT l.Components = match[1:] } } // 6. invalid type if l.Type == NONE { return &LineError{n, ErrInvalidSyntax} } // 7. pop section if l.Type == SECTIONEND { cur := stack[len(stack)-1] // pop stack = stack[0 : len(stack)-1] // pop var last []*Line if len(stack) > 0 { last = stack[len(stack)-1].Lines } else { last = vcast.Lines } last = append(last, cur) continue } // 8. add to section / or receiver stacklen := len(stack) if stacklen > 0 && l.Type == SECTION { stacklen-- } if stacklen > 0 { tail := stack[stacklen-1] tail.Lines = append(tail.Lines, l) } else { vcast.Lines = append(vcast.Lines, l) } } // fail on unclosed section if len(stack) > 0 { return ErrUnclosedSection } return nil }