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