parse.go raw

   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  }
 110