github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/vals/index_list.go (about) 1 package vals 2 3 import ( 4 "errors" 5 "strconv" 6 "strings" 7 8 "github.com/xiaq/persistent/vector" 9 ) 10 11 var ( 12 errIndexMustBeString = errors.New("index must be string") 13 errIndexMustBeNumber = errors.New("index or slice component must be number") 14 errIndexOutOfRange = errors.New("index out of range") 15 ) 16 17 type listIndexable interface { 18 Lener 19 Index(int) (interface{}, bool) 20 SubVector(int, int) vector.Vector 21 } 22 23 var _ listIndexable = vector.Vector(nil) 24 25 func indexList(l listIndexable, rawIndex interface{}) (interface{}, error) { 26 index, err := ConvertListIndex(rawIndex, l.Len()) 27 if err != nil { 28 return nil, err 29 } 30 if index.Slice { 31 return l.SubVector(index.Lower, index.Upper), nil 32 } 33 // Bounds are already checked. 34 value, _ := l.Index(index.Lower) 35 return value, nil 36 } 37 38 // ListIndex represents a (converted) list index. 39 type ListIndex struct { 40 Slice bool 41 Lower int 42 Upper int 43 } 44 45 // ConvertListIndex parses a list index, check whether it is valid, and returns 46 // the converted structure. 47 func ConvertListIndex(rawIndex interface{}, n int) (*ListIndex, error) { 48 s, ok := rawIndex.(string) 49 if !ok { 50 return nil, errIndexMustBeString 51 } 52 slice, i, j, err := parseListIndex(s, n) 53 if err != nil { 54 return nil, err 55 } 56 if i < 0 { 57 i += n 58 } 59 if j < 0 { 60 j += n 61 } 62 if slice { 63 if !(0 <= i && i <= j && j <= n) { 64 return nil, errIndexOutOfRange 65 } 66 } else { 67 if !(0 <= i && i < n) { 68 return nil, errIndexOutOfRange 69 } 70 } 71 return &ListIndex{slice, i, j}, nil 72 } 73 74 // ListIndex = Number | 75 // Number ':' Number 76 func parseListIndex(s string, n int) (slice bool, i int, j int, err error) { 77 colon := strings.IndexRune(s, ':') 78 if colon == -1 { 79 // A single number 80 i, err := atoi(s) 81 if err != nil { 82 return false, 0, 0, err 83 } 84 return false, i, 0, nil 85 } 86 if s[:colon] == "" { 87 i = 0 88 } else { 89 i, err = atoi(s[:colon]) 90 if err != nil { 91 return false, 0, 0, err 92 } 93 } 94 if s[colon+1:] == "" { 95 j = n 96 } else { 97 j, err = atoi(s[colon+1:]) 98 if err != nil { 99 return false, 0, 0, err 100 } 101 } 102 // Two numbers 103 return true, i, j, nil 104 } 105 106 // atoi is a wrapper around strconv.Atoi, converting strconv.ErrRange to 107 // errIndexOutOfRange. 108 func atoi(a string) (int, error) { 109 i, err := strconv.Atoi(a) 110 if err != nil { 111 if err.(*strconv.NumError).Err == strconv.ErrRange { 112 return 0, errIndexOutOfRange 113 } 114 return 0, errIndexMustBeNumber 115 } 116 return i, nil 117 }