nix-amer/internal/cnf/parser/nginx/decoder.go

157 lines
2.9 KiB
Go

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
}