2020-02-23 14:36:57 +00:00
|
|
|
package ini
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Visitor is an interface used by walkers that will
|
|
|
|
// traverse an array of ASTs.
|
|
|
|
type Visitor interface {
|
|
|
|
VisitExpr(AST) error
|
|
|
|
VisitStatement(AST) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultVisitor is used to visit statements and expressions
|
|
|
|
// and ensure that they are both of the correct format.
|
|
|
|
// In addition, upon visiting this will build sections and populate
|
|
|
|
// the Sections field which can be used to retrieve profile
|
|
|
|
// configuration.
|
|
|
|
type DefaultVisitor struct {
|
|
|
|
scope string
|
|
|
|
Sections Sections
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDefaultVisitor return a DefaultVisitor
|
|
|
|
func NewDefaultVisitor() *DefaultVisitor {
|
|
|
|
return &DefaultVisitor{
|
|
|
|
Sections: Sections{
|
|
|
|
container: map[string]Section{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// VisitExpr visits expressions...
|
|
|
|
func (v *DefaultVisitor) VisitExpr(expr AST) error {
|
|
|
|
t := v.Sections.container[v.scope]
|
|
|
|
if t.values == nil {
|
|
|
|
t.values = values{}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch expr.Kind {
|
|
|
|
case ASTKindExprStatement:
|
|
|
|
opExpr := expr.GetRoot()
|
|
|
|
switch opExpr.Kind {
|
|
|
|
case ASTKindEqualExpr:
|
|
|
|
children := opExpr.GetChildren()
|
|
|
|
if len(children) <= 1 {
|
|
|
|
return NewParseError("unexpected token type")
|
|
|
|
}
|
|
|
|
|
|
|
|
rhs := children[1]
|
|
|
|
|
2021-09-02 10:03:56 +00:00
|
|
|
// The right-hand value side the equality expression is allowed to contain '[', ']', ':', '=' in the values.
|
|
|
|
// If the token is not either a literal or one of the token types that identifies those four additional
|
|
|
|
// tokens then error.
|
|
|
|
if !(rhs.Root.Type() == TokenLit || rhs.Root.Type() == TokenOp || rhs.Root.Type() == TokenSep) {
|
2020-02-23 14:36:57 +00:00
|
|
|
return NewParseError("unexpected token type")
|
|
|
|
}
|
|
|
|
|
|
|
|
key := EqualExprKey(opExpr)
|
|
|
|
v, err := newValue(rhs.Root.ValueType, rhs.Root.base, rhs.Root.Raw())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.values[key] = v
|
|
|
|
default:
|
|
|
|
return NewParseError(fmt.Sprintf("unsupported expression %v", expr))
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return NewParseError(fmt.Sprintf("unsupported expression %v", expr))
|
|
|
|
}
|
|
|
|
|
|
|
|
v.Sections.container[v.scope] = t
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VisitStatement visits statements...
|
|
|
|
func (v *DefaultVisitor) VisitStatement(stmt AST) error {
|
|
|
|
switch stmt.Kind {
|
|
|
|
case ASTKindCompletedSectionStatement:
|
|
|
|
child := stmt.GetRoot()
|
|
|
|
if child.Kind != ASTKindSectionStatement {
|
|
|
|
return NewParseError(fmt.Sprintf("unsupported child statement: %T", child))
|
|
|
|
}
|
|
|
|
|
|
|
|
name := string(child.Root.Raw())
|
|
|
|
v.Sections.container[name] = Section{}
|
|
|
|
v.scope = name
|
|
|
|
default:
|
|
|
|
return NewParseError(fmt.Sprintf("unsupported statement: %s", stmt.Kind))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sections is a map of Section structures that represent
|
|
|
|
// a configuration.
|
|
|
|
type Sections struct {
|
|
|
|
container map[string]Section
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSection will return section p. If section p does not exist,
|
|
|
|
// false will be returned in the second parameter.
|
|
|
|
func (t Sections) GetSection(p string) (Section, bool) {
|
|
|
|
v, ok := t.container[p]
|
|
|
|
return v, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// values represents a map of union values.
|
|
|
|
type values map[string]Value
|
|
|
|
|
|
|
|
// List will return a list of all sections that were successfully
|
|
|
|
// parsed.
|
|
|
|
func (t Sections) List() []string {
|
|
|
|
keys := make([]string, len(t.container))
|
|
|
|
i := 0
|
|
|
|
for k := range t.container {
|
|
|
|
keys[i] = k
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
|
|
|
// Section contains a name and values. This represent
|
|
|
|
// a sectioned entry in a configuration file.
|
|
|
|
type Section struct {
|
|
|
|
Name string
|
|
|
|
values values
|
|
|
|
}
|
|
|
|
|
|
|
|
// Has will return whether or not an entry exists in a given section
|
|
|
|
func (t Section) Has(k string) bool {
|
|
|
|
_, ok := t.values[k]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValueType will returned what type the union is set to. If
|
|
|
|
// k was not found, the NoneType will be returned.
|
|
|
|
func (t Section) ValueType(k string) (ValueType, bool) {
|
|
|
|
v, ok := t.values[k]
|
|
|
|
return v.Type, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bool returns a bool value at k
|
|
|
|
func (t Section) Bool(k string) bool {
|
|
|
|
return t.values[k].BoolValue()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Int returns an integer value at k
|
|
|
|
func (t Section) Int(k string) int64 {
|
|
|
|
return t.values[k].IntValue()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Float64 returns a float value at k
|
|
|
|
func (t Section) Float64(k string) float64 {
|
|
|
|
return t.values[k].FloatValue()
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns the string value at k
|
|
|
|
func (t Section) String(k string) string {
|
|
|
|
_, ok := t.values[k]
|
|
|
|
if !ok {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return t.values[k].StringValue()
|
|
|
|
}
|