1package ini
2
3import (
4	"bytes"
5	"fmt"
6	"strconv"
7)
8
9const (
10	none = numberFormat(iota)
11	binary
12	octal
13	decimal
14	hex
15	exponent
16)
17
18type numberFormat int
19
20// numberHelper is used to dictate what format a number is in
21// and what to do for negative values. Since -1e-4 is a valid
22// number, we cannot just simply check for duplicate negatives.
23type numberHelper struct {
24	numberFormat numberFormat
25
26	negative         bool
27	negativeExponent bool
28}
29
30func (b numberHelper) Exists() bool {
31	return b.numberFormat != none
32}
33
34func (b numberHelper) IsNegative() bool {
35	return b.negative || b.negativeExponent
36}
37
38func (b *numberHelper) Determine(c rune) error {
39	if b.Exists() {
40		return NewParseError(fmt.Sprintf("multiple number formats: 0%v", string(c)))
41	}
42
43	switch c {
44	case 'b':
45		b.numberFormat = binary
46	case 'o':
47		b.numberFormat = octal
48	case 'x':
49		b.numberFormat = hex
50	case 'e', 'E':
51		b.numberFormat = exponent
52	case '-':
53		if b.numberFormat != exponent {
54			b.negative = true
55		} else {
56			b.negativeExponent = true
57		}
58	case '.':
59		b.numberFormat = decimal
60	default:
61		return NewParseError(fmt.Sprintf("invalid number character: %v", string(c)))
62	}
63
64	return nil
65}
66
67func (b numberHelper) CorrectByte(c rune) bool {
68	switch {
69	case b.numberFormat == binary:
70		if !isBinaryByte(c) {
71			return false
72		}
73	case b.numberFormat == octal:
74		if !isOctalByte(c) {
75			return false
76		}
77	case b.numberFormat == hex:
78		if !isHexByte(c) {
79			return false
80		}
81	case b.numberFormat == decimal:
82		if !isDigit(c) {
83			return false
84		}
85	case b.numberFormat == exponent:
86		if !isDigit(c) {
87			return false
88		}
89	case b.negativeExponent:
90		if !isDigit(c) {
91			return false
92		}
93	case b.negative:
94		if !isDigit(c) {
95			return false
96		}
97	default:
98		if !isDigit(c) {
99			return false
100		}
101	}
102
103	return true
104}
105
106func (b numberHelper) Base() int {
107	switch b.numberFormat {
108	case binary:
109		return 2
110	case octal:
111		return 8
112	case hex:
113		return 16
114	default:
115		return 10
116	}
117}
118
119func (b numberHelper) String() string {
120	buf := bytes.Buffer{}
121	i := 0
122
123	switch b.numberFormat {
124	case binary:
125		i++
126		buf.WriteString(strconv.Itoa(i) + ": binary format\n")
127	case octal:
128		i++
129		buf.WriteString(strconv.Itoa(i) + ": octal format\n")
130	case hex:
131		i++
132		buf.WriteString(strconv.Itoa(i) + ": hex format\n")
133	case exponent:
134		i++
135		buf.WriteString(strconv.Itoa(i) + ": exponent format\n")
136	default:
137		i++
138		buf.WriteString(strconv.Itoa(i) + ": integer format\n")
139	}
140
141	if b.negative {
142		i++
143		buf.WriteString(strconv.Itoa(i) + ": negative format\n")
144	}
145
146	if b.negativeExponent {
147		i++
148		buf.WriteString(strconv.Itoa(i) + ": negative exponent format\n")
149	}
150
151	return buf.String()
152}
153