157 lines
2.9 KiB
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
|
|
}
|