parse.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package ini
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. func parse(tokens []lineToken, path string) Sections {
  7. parser := &parser{
  8. path: path,
  9. sections: NewSections(),
  10. }
  11. parser.parse(tokens)
  12. return parser.sections
  13. }
  14. type parser struct {
  15. csection, ckey string // current state
  16. path string // source file path
  17. sections Sections // parse result
  18. }
  19. func (p *parser) parse(tokens []lineToken) {
  20. for _, otok := range tokens {
  21. switch tok := otok.(type) {
  22. case *lineTokenProfile:
  23. p.handleProfile(tok)
  24. case *lineTokenProperty:
  25. p.handleProperty(tok)
  26. case *lineTokenSubProperty:
  27. p.handleSubProperty(tok)
  28. case *lineTokenContinuation:
  29. p.handleContinuation(tok)
  30. }
  31. }
  32. }
  33. func (p *parser) handleProfile(tok *lineTokenProfile) {
  34. name := tok.Name
  35. if tok.Type != "" {
  36. name = fmt.Sprintf("%s %s", tok.Type, tok.Name)
  37. }
  38. p.ckey = ""
  39. p.csection = name
  40. if _, ok := p.sections.container[name]; !ok {
  41. p.sections.container[name] = NewSection(name)
  42. }
  43. }
  44. func (p *parser) handleProperty(tok *lineTokenProperty) {
  45. if p.csection == "" {
  46. return // LEGACY: don't error on "global" properties
  47. }
  48. p.ckey = tok.Key
  49. if _, ok := p.sections.container[p.csection].values[tok.Key]; ok {
  50. section := p.sections.container[p.csection]
  51. section.Logs = append(p.sections.container[p.csection].Logs,
  52. fmt.Sprintf(
  53. "For profile: %v, overriding %v value, with a %v value found in a duplicate profile defined later in the same file %v. \n",
  54. p.csection, tok.Key, tok.Key, p.path,
  55. ),
  56. )
  57. p.sections.container[p.csection] = section
  58. }
  59. p.sections.container[p.csection].values[tok.Key] = Value{
  60. str: tok.Value,
  61. }
  62. p.sections.container[p.csection].SourceFile[tok.Key] = p.path
  63. }
  64. func (p *parser) handleSubProperty(tok *lineTokenSubProperty) {
  65. if p.csection == "" {
  66. return // LEGACY: don't error on "global" properties
  67. }
  68. if p.ckey == "" || p.sections.container[p.csection].values[p.ckey].str != "" {
  69. // This is an "orphaned" subproperty, either because it's at
  70. // the beginning of a section or because the last property's
  71. // value isn't empty. Either way we're lenient here and
  72. // "promote" this to a normal property.
  73. p.handleProperty(&lineTokenProperty{
  74. Key: tok.Key,
  75. Value: strings.TrimSpace(trimPropertyComment(tok.Value)),
  76. })
  77. return
  78. }
  79. if p.sections.container[p.csection].values[p.ckey].mp == nil {
  80. p.sections.container[p.csection].values[p.ckey] = Value{
  81. mp: map[string]string{},
  82. }
  83. }
  84. p.sections.container[p.csection].values[p.ckey].mp[tok.Key] = tok.Value
  85. }
  86. func (p *parser) handleContinuation(tok *lineTokenContinuation) {
  87. if p.ckey == "" {
  88. return
  89. }
  90. value, _ := p.sections.container[p.csection].values[p.ckey]
  91. if value.str != "" && value.mp == nil {
  92. value.str = fmt.Sprintf("%s\n%s", value.str, tok.Value)
  93. }
  94. p.sections.container[p.csection].values[p.ckey] = value
  95. }