parse.go (2854B)
1 package ini 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 func 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 17 type parser struct { 18 csection, ckey string // current state 19 path string // source file path 20 sections Sections // parse result 21 } 22 23 func (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 38 func (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 50 func (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 73 func (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 98 func (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 }