code.dwrz.net

Go monorepo.
Log | Files | Refs

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 }