|
|
@@ -0,0 +1,104 @@
|
|
|
+package goyaml
|
|
|
+
|
|
|
+import (
|
|
|
+ "reflect"
|
|
|
+ "unicode"
|
|
|
+)
|
|
|
+
|
|
|
+type keyList []reflect.Value
|
|
|
+
|
|
|
+func (l keyList) Len() int { return len(l) }
|
|
|
+func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
|
|
+func (l keyList) Less(i, j int) bool {
|
|
|
+ a := l[i]
|
|
|
+ b := l[j]
|
|
|
+ ak := a.Kind()
|
|
|
+ bk := b.Kind()
|
|
|
+ for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
|
|
|
+ a = a.Elem()
|
|
|
+ ak = a.Kind()
|
|
|
+ }
|
|
|
+ for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
|
|
|
+ b = b.Elem()
|
|
|
+ bk = b.Kind()
|
|
|
+ }
|
|
|
+ af, aok := keyFloat(a)
|
|
|
+ bf, bok := keyFloat(b)
|
|
|
+ if aok && bok {
|
|
|
+ if af != bf {
|
|
|
+ return af < bf
|
|
|
+ }
|
|
|
+ if ak != bk {
|
|
|
+ return ak < bk
|
|
|
+ }
|
|
|
+ return numLess(a, b)
|
|
|
+ }
|
|
|
+ if ak != reflect.String || bk != reflect.String {
|
|
|
+ return ak < bk
|
|
|
+ }
|
|
|
+ ar, br := []rune(a.String()), []rune(b.String())
|
|
|
+ for i := 0; i < len(ar) && i < len(br); i++ {
|
|
|
+ if ar[i] == br[i] {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ al := unicode.IsLetter(ar[i])
|
|
|
+ bl := unicode.IsLetter(br[i])
|
|
|
+ if al && bl {
|
|
|
+ return ar[i] < br[i]
|
|
|
+ }
|
|
|
+ if al || bl {
|
|
|
+ return bl
|
|
|
+ }
|
|
|
+ var ai, bi int
|
|
|
+ var an, bn int64
|
|
|
+ for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
|
|
|
+ an = an*10 + int64(ar[ai]-'0')
|
|
|
+ }
|
|
|
+ for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
|
|
|
+ bn = bn*10 + int64(br[bi]-'0')
|
|
|
+ }
|
|
|
+ if an != bn {
|
|
|
+ return an < bn
|
|
|
+ }
|
|
|
+ if ai != bi {
|
|
|
+ return ai < bi
|
|
|
+ }
|
|
|
+ return ar[i] < br[i]
|
|
|
+ }
|
|
|
+ return len(ar) < len(br)
|
|
|
+}
|
|
|
+
|
|
|
+// keyFloat returns a float value for v if it is a number/bool
|
|
|
+// and whether it is a number/bool or not.
|
|
|
+func keyFloat(v reflect.Value) (f float64, ok bool) {
|
|
|
+ switch v.Kind() {
|
|
|
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
+ return float64(v.Int()), true
|
|
|
+ case reflect.Float32, reflect.Float64:
|
|
|
+ return v.Float(), true
|
|
|
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
+ return float64(v.Uint()), true
|
|
|
+ case reflect.Bool:
|
|
|
+ if v.Bool() {
|
|
|
+ return 1, true
|
|
|
+ }
|
|
|
+ return 0, true
|
|
|
+ }
|
|
|
+ return 0, false
|
|
|
+}
|
|
|
+
|
|
|
+// numLess returns whether a < b.
|
|
|
+// a and b must necessarily have the same kind.
|
|
|
+func numLess(a, b reflect.Value) bool {
|
|
|
+ switch a.Kind() {
|
|
|
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
+ return a.Int() < b.Int()
|
|
|
+ case reflect.Float32, reflect.Float64:
|
|
|
+ return a.Float() < b.Float()
|
|
|
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
+ return a.Uint() < b.Uint()
|
|
|
+ case reflect.Bool:
|
|
|
+ return !a.Bool() && b.Bool()
|
|
|
+ }
|
|
|
+ panic("not a number")
|
|
|
+}
|