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