1// Copyright 2018 Frank Schroeder. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package properties
6
7import (
8	"fmt"
9	"runtime"
10)
11
12type parser struct {
13	lex *lexer
14}
15
16func parse(input string) (properties *Properties, err error) {
17	p := &parser{lex: lex(input)}
18	defer p.recover(&err)
19
20	properties = NewProperties()
21	key := ""
22	comments := []string{}
23
24	for {
25		token := p.expectOneOf(itemComment, itemKey, itemEOF)
26		switch token.typ {
27		case itemEOF:
28			goto done
29		case itemComment:
30			comments = append(comments, token.val)
31			continue
32		case itemKey:
33			key = token.val
34			if _, ok := properties.m[key]; !ok {
35				properties.k = append(properties.k, key)
36			}
37		}
38
39		token = p.expectOneOf(itemValue, itemEOF)
40		if len(comments) > 0 {
41			properties.c[key] = comments
42			comments = []string{}
43		}
44		switch token.typ {
45		case itemEOF:
46			properties.m[key] = ""
47			goto done
48		case itemValue:
49			properties.m[key] = token.val
50		}
51	}
52
53done:
54	return properties, nil
55}
56
57func (p *parser) errorf(format string, args ...interface{}) {
58	format = fmt.Sprintf("properties: Line %d: %s", p.lex.lineNumber(), format)
59	panic(fmt.Errorf(format, args...))
60}
61
62func (p *parser) expect(expected itemType) (token item) {
63	token = p.lex.nextItem()
64	if token.typ != expected {
65		p.unexpected(token)
66	}
67	return token
68}
69
70func (p *parser) expectOneOf(expected ...itemType) (token item) {
71	token = p.lex.nextItem()
72	for _, v := range expected {
73		if token.typ == v {
74			return token
75		}
76	}
77	p.unexpected(token)
78	panic("unexpected token")
79}
80
81func (p *parser) unexpected(token item) {
82	p.errorf(token.String())
83}
84
85// recover is the handler that turns panics into returns from the top level of Parse.
86func (p *parser) recover(errp *error) {
87	e := recover()
88	if e != nil {
89		if _, ok := e.(runtime.Error); ok {
90			panic(e)
91		}
92		*errp = e.(error)
93	}
94	return
95}
96