1package ini 2 3import ( 4 "fmt" 5 "strconv" 6 "strings" 7) 8 9var ( 10 runesTrue = []rune("true") 11 runesFalse = []rune("false") 12) 13 14var literalValues = [][]rune{ 15 runesTrue, 16 runesFalse, 17} 18 19func isBoolValue(b []rune) bool { 20 for _, lv := range literalValues { 21 if isLitValue(lv, b) { 22 return true 23 } 24 } 25 return false 26} 27 28func isLitValue(want, have []rune) bool { 29 if len(have) < len(want) { 30 return false 31 } 32 33 for i := 0; i < len(want); i++ { 34 if want[i] != have[i] { 35 return false 36 } 37 } 38 39 return true 40} 41 42// isNumberValue will return whether not the leading characters in 43// a byte slice is a number. A number is delimited by whitespace or 44// the newline token. 45// 46// A number is defined to be in a binary, octal, decimal (int | float), hex format, 47// or in scientific notation. 48func isNumberValue(b []rune) bool { 49 negativeIndex := 0 50 helper := numberHelper{} 51 needDigit := false 52 53 for i := 0; i < len(b); i++ { 54 negativeIndex++ 55 56 switch b[i] { 57 case '-': 58 if helper.IsNegative() || negativeIndex != 1 { 59 return false 60 } 61 helper.Determine(b[i]) 62 needDigit = true 63 continue 64 case 'e', 'E': 65 if err := helper.Determine(b[i]); err != nil { 66 return false 67 } 68 negativeIndex = 0 69 needDigit = true 70 continue 71 case 'b': 72 if helper.numberFormat == hex { 73 break 74 } 75 fallthrough 76 case 'o', 'x': 77 needDigit = true 78 if i == 0 { 79 return false 80 } 81 82 fallthrough 83 case '.': 84 if err := helper.Determine(b[i]); err != nil { 85 return false 86 } 87 needDigit = true 88 continue 89 } 90 91 if i > 0 && (isNewline(b[i:]) || isWhitespace(b[i])) { 92 return !needDigit 93 } 94 95 if !helper.CorrectByte(b[i]) { 96 return false 97 } 98 needDigit = false 99 } 100 101 return !needDigit 102} 103 104func isValid(b []rune) (bool, int, error) { 105 if len(b) == 0 { 106 // TODO: should probably return an error 107 return false, 0, nil 108 } 109 110 return isValidRune(b[0]), 1, nil 111} 112 113func isValidRune(r rune) bool { 114 return r != ':' && r != '=' && r != '[' && r != ']' && r != ' ' && r != '\n' 115} 116 117// ValueType is an enum that will signify what type 118// the Value is 119type ValueType int 120 121func (v ValueType) String() string { 122 switch v { 123 case NoneType: 124 return "NONE" 125 case DecimalType: 126 return "FLOAT" 127 case IntegerType: 128 return "INT" 129 case StringType: 130 return "STRING" 131 case BoolType: 132 return "BOOL" 133 } 134 135 return "" 136} 137 138// ValueType enums 139const ( 140 NoneType = ValueType(iota) 141 DecimalType 142 IntegerType 143 StringType 144 QuotedStringType 145 BoolType 146) 147 148// Value is a union container 149type Value struct { 150 Type ValueType 151 raw []rune 152 153 integer int64 154 decimal float64 155 boolean bool 156 str string 157} 158 159func newValue(t ValueType, base int, raw []rune) (Value, error) { 160 v := Value{ 161 Type: t, 162 raw: raw, 163 } 164 var err error 165 166 switch t { 167 case DecimalType: 168 v.decimal, err = strconv.ParseFloat(string(raw), 64) 169 case IntegerType: 170 if base != 10 { 171 raw = raw[2:] 172 } 173 174 v.integer, err = strconv.ParseInt(string(raw), base, 64) 175 case StringType: 176 v.str = string(raw) 177 case QuotedStringType: 178 v.str = string(raw[1 : len(raw)-1]) 179 case BoolType: 180 v.boolean = runeCompare(v.raw, runesTrue) 181 } 182 183 // issue 2253 184 // 185 // if the value trying to be parsed is too large, then we will use 186 // the 'StringType' and raw value instead. 187 if nerr, ok := err.(*strconv.NumError); ok && nerr.Err == strconv.ErrRange { 188 v.Type = StringType 189 v.str = string(raw) 190 err = nil 191 } 192 193 return v, err 194} 195 196// Append will append values and change the type to a string 197// type. 198func (v *Value) Append(tok Token) { 199 r := tok.Raw() 200 if v.Type != QuotedStringType { 201 v.Type = StringType 202 r = tok.raw[1 : len(tok.raw)-1] 203 } 204 if tok.Type() != TokenLit { 205 v.raw = append(v.raw, tok.Raw()...) 206 } else { 207 v.raw = append(v.raw, r...) 208 } 209} 210 211func (v Value) String() string { 212 switch v.Type { 213 case DecimalType: 214 return fmt.Sprintf("decimal: %f", v.decimal) 215 case IntegerType: 216 return fmt.Sprintf("integer: %d", v.integer) 217 case StringType: 218 return fmt.Sprintf("string: %s", string(v.raw)) 219 case QuotedStringType: 220 return fmt.Sprintf("quoted string: %s", string(v.raw)) 221 case BoolType: 222 return fmt.Sprintf("bool: %t", v.boolean) 223 default: 224 return "union not set" 225 } 226} 227 228func newLitToken(b []rune) (Token, int, error) { 229 n := 0 230 var err error 231 232 token := Token{} 233 if b[0] == '"' { 234 n, err = getStringValue(b) 235 if err != nil { 236 return token, n, err 237 } 238 239 token = newToken(TokenLit, b[:n], QuotedStringType) 240 } else if isNumberValue(b) { 241 var base int 242 base, n, err = getNumericalValue(b) 243 if err != nil { 244 return token, 0, err 245 } 246 247 value := b[:n] 248 vType := IntegerType 249 if contains(value, '.') || hasExponent(value) { 250 vType = DecimalType 251 } 252 token = newToken(TokenLit, value, vType) 253 token.base = base 254 } else if isBoolValue(b) { 255 n, err = getBoolValue(b) 256 257 token = newToken(TokenLit, b[:n], BoolType) 258 } else { 259 n, err = getValue(b) 260 token = newToken(TokenLit, b[:n], StringType) 261 } 262 263 return token, n, err 264} 265 266// IntValue returns an integer value 267func (v Value) IntValue() int64 { 268 return v.integer 269} 270 271// FloatValue returns a float value 272func (v Value) FloatValue() float64 { 273 return v.decimal 274} 275 276// BoolValue returns a bool value 277func (v Value) BoolValue() bool { 278 return v.boolean 279} 280 281func isTrimmable(r rune) bool { 282 switch r { 283 case '\n', ' ': 284 return true 285 } 286 return false 287} 288 289// StringValue returns the string value 290func (v Value) StringValue() string { 291 switch v.Type { 292 case StringType: 293 return strings.TrimFunc(string(v.raw), isTrimmable) 294 case QuotedStringType: 295 // preserve all characters in the quotes 296 return string(removeEscapedCharacters(v.raw[1 : len(v.raw)-1])) 297 default: 298 return strings.TrimFunc(string(v.raw), isTrimmable) 299 } 300} 301 302func contains(runes []rune, c rune) bool { 303 for i := 0; i < len(runes); i++ { 304 if runes[i] == c { 305 return true 306 } 307 } 308 309 return false 310} 311 312func runeCompare(v1 []rune, v2 []rune) bool { 313 if len(v1) != len(v2) { 314 return false 315 } 316 317 for i := 0; i < len(v1); i++ { 318 if v1[i] != v2[i] { 319 return false 320 } 321 } 322 323 return true 324} 325