visitor.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. package ini
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. )
  7. // Visitor is an interface used by walkers that will
  8. // traverse an array of ASTs.
  9. type Visitor interface {
  10. VisitExpr(AST) error
  11. VisitStatement(AST) error
  12. }
  13. // DefaultVisitor is used to visit statements and expressions
  14. // and ensure that they are both of the correct format.
  15. // In addition, upon visiting this will build sections and populate
  16. // the Sections field which can be used to retrieve profile
  17. // configuration.
  18. type DefaultVisitor struct {
  19. // scope is the profile which is being visited
  20. scope string
  21. // path is the file path which the visitor is visiting
  22. path string
  23. // Sections defines list of the profile section
  24. Sections Sections
  25. }
  26. // NewDefaultVisitor returns a DefaultVisitor. It takes in a filepath
  27. // which points to the file it is visiting.
  28. func NewDefaultVisitor(filepath string) *DefaultVisitor {
  29. return &DefaultVisitor{
  30. Sections: Sections{
  31. container: map[string]Section{},
  32. },
  33. path: filepath,
  34. }
  35. }
  36. // VisitExpr visits expressions...
  37. func (v *DefaultVisitor) VisitExpr(expr AST) error {
  38. t := v.Sections.container[v.scope]
  39. if t.values == nil {
  40. t.values = values{}
  41. }
  42. if t.SourceFile == nil {
  43. t.SourceFile = make(map[string]string, 0)
  44. }
  45. switch expr.Kind {
  46. case ASTKindExprStatement:
  47. opExpr := expr.GetRoot()
  48. switch opExpr.Kind {
  49. case ASTKindEqualExpr:
  50. children := opExpr.GetChildren()
  51. if len(children) <= 1 {
  52. return NewParseError("unexpected token type")
  53. }
  54. rhs := children[1]
  55. // The right-hand value side the equality expression is allowed to contain '[', ']', ':', '=' in the values.
  56. // If the token is not either a literal or one of the token types that identifies those four additional
  57. // tokens then error.
  58. if !(rhs.Root.Type() == TokenLit || rhs.Root.Type() == TokenOp || rhs.Root.Type() == TokenSep) {
  59. return NewParseError("unexpected token type")
  60. }
  61. key := EqualExprKey(opExpr)
  62. val, err := newValue(rhs.Root.ValueType, rhs.Root.base, rhs.Root.Raw())
  63. if err != nil {
  64. return err
  65. }
  66. // lower case key to standardize
  67. k := strings.ToLower(key)
  68. // identify if the section already had this key, append log on section
  69. if t.Has(k) {
  70. t.Logs = append(t.Logs,
  71. fmt.Sprintf("For profile: %v, overriding %v value, "+
  72. "with a %v value found in a duplicate profile defined later in the same file %v. \n",
  73. t.Name, k, k, v.path))
  74. }
  75. // assign the value
  76. t.values[k] = val
  77. // update the source file path for region
  78. t.SourceFile[k] = v.path
  79. default:
  80. return NewParseError(fmt.Sprintf("unsupported expression %v", expr))
  81. }
  82. default:
  83. return NewParseError(fmt.Sprintf("unsupported expression %v", expr))
  84. }
  85. v.Sections.container[v.scope] = t
  86. return nil
  87. }
  88. // VisitStatement visits statements...
  89. func (v *DefaultVisitor) VisitStatement(stmt AST) error {
  90. switch stmt.Kind {
  91. case ASTKindCompletedSectionStatement:
  92. child := stmt.GetRoot()
  93. if child.Kind != ASTKindSectionStatement {
  94. return NewParseError(fmt.Sprintf("unsupported child statement: %T", child))
  95. }
  96. name := string(child.Root.Raw())
  97. // trim start and end space
  98. name = strings.TrimSpace(name)
  99. // if has prefix "profile " + [ws+] + "profile-name",
  100. // we standardize by removing the [ws+] between prefix and profile-name.
  101. if strings.HasPrefix(name, "profile ") {
  102. names := strings.SplitN(name, " ", 2)
  103. name = names[0] + " " + strings.TrimLeft(names[1], " ")
  104. }
  105. // attach profile name on section
  106. if !v.Sections.HasSection(name) {
  107. v.Sections.container[name] = NewSection(name)
  108. }
  109. v.scope = name
  110. default:
  111. return NewParseError(fmt.Sprintf("unsupported statement: %s", stmt.Kind))
  112. }
  113. return nil
  114. }
  115. // Sections is a map of Section structures that represent
  116. // a configuration.
  117. type Sections struct {
  118. container map[string]Section
  119. }
  120. // NewSections returns empty ini Sections
  121. func NewSections() Sections {
  122. return Sections{
  123. container: make(map[string]Section, 0),
  124. }
  125. }
  126. // GetSection will return section p. If section p does not exist,
  127. // false will be returned in the second parameter.
  128. func (t Sections) GetSection(p string) (Section, bool) {
  129. v, ok := t.container[p]
  130. return v, ok
  131. }
  132. // HasSection denotes if Sections consist of a section with
  133. // provided name.
  134. func (t Sections) HasSection(p string) bool {
  135. _, ok := t.container[p]
  136. return ok
  137. }
  138. // SetSection sets a section value for provided section name.
  139. func (t Sections) SetSection(p string, v Section) Sections {
  140. t.container[p] = v
  141. return t
  142. }
  143. // DeleteSection deletes a section entry/value for provided section name./
  144. func (t Sections) DeleteSection(p string) {
  145. delete(t.container, p)
  146. }
  147. // values represents a map of union values.
  148. type values map[string]Value
  149. // List will return a list of all sections that were successfully
  150. // parsed.
  151. func (t Sections) List() []string {
  152. keys := make([]string, len(t.container))
  153. i := 0
  154. for k := range t.container {
  155. keys[i] = k
  156. i++
  157. }
  158. sort.Strings(keys)
  159. return keys
  160. }
  161. // Section contains a name and values. This represent
  162. // a sectioned entry in a configuration file.
  163. type Section struct {
  164. // Name is the Section profile name
  165. Name string
  166. // values are the values within parsed profile
  167. values values
  168. // Errors is the list of errors
  169. Errors []error
  170. // Logs is the list of logs
  171. Logs []string
  172. // SourceFile is the INI Source file from where this section
  173. // was retrieved. They key is the property, value is the
  174. // source file the property was retrieved from.
  175. SourceFile map[string]string
  176. }
  177. // NewSection returns an initialize section for the name
  178. func NewSection(name string) Section {
  179. return Section{
  180. Name: name,
  181. values: values{},
  182. SourceFile: map[string]string{},
  183. }
  184. }
  185. // UpdateSourceFile updates source file for a property to provided filepath.
  186. func (t Section) UpdateSourceFile(property string, filepath string) {
  187. t.SourceFile[property] = filepath
  188. }
  189. // UpdateValue updates value for a provided key with provided value
  190. func (t Section) UpdateValue(k string, v Value) error {
  191. t.values[k] = v
  192. return nil
  193. }
  194. // Has will return whether or not an entry exists in a given section
  195. func (t Section) Has(k string) bool {
  196. _, ok := t.values[k]
  197. return ok
  198. }
  199. // ValueType will returned what type the union is set to. If
  200. // k was not found, the NoneType will be returned.
  201. func (t Section) ValueType(k string) (ValueType, bool) {
  202. v, ok := t.values[k]
  203. return v.Type, ok
  204. }
  205. // Bool returns a bool value at k
  206. func (t Section) Bool(k string) bool {
  207. return t.values[k].BoolValue()
  208. }
  209. // Int returns an integer value at k
  210. func (t Section) Int(k string) int64 {
  211. return t.values[k].IntValue()
  212. }
  213. // Float64 returns a float value at k
  214. func (t Section) Float64(k string) float64 {
  215. return t.values[k].FloatValue()
  216. }
  217. // String returns the string value at k
  218. func (t Section) String(k string) string {
  219. _, ok := t.values[k]
  220. if !ok {
  221. return ""
  222. }
  223. return t.values[k].StringValue()
  224. }