github.com/elves/elvish@v0.15.0/pkg/eval/vals/index_list.go (about) 1 package vals 2 3 import ( 4 "errors" 5 "strconv" 6 "strings" 7 8 "github.com/elves/elvish/pkg/eval/errs" 9 ) 10 11 var ( 12 errIndexMustBeInteger = errors.New("index must must be integer") 13 ) 14 15 func indexList(l List, rawIndex interface{}) (interface{}, error) { 16 index, err := ConvertListIndex(rawIndex, l.Len()) 17 if err != nil { 18 return nil, err 19 } 20 if index.Slice { 21 return l.SubVector(index.Lower, index.Upper), nil 22 } 23 // Bounds are already checked. 24 value, _ := l.Index(index.Lower) 25 return value, nil 26 } 27 28 // ListIndex represents a (converted) list index. 29 type ListIndex struct { 30 Slice bool 31 Lower int 32 Upper int 33 } 34 35 func adjustAndCheckIndex(i, n int, includeN bool) (int, error) { 36 if i < 0 { 37 if i < -n { 38 return 0, negIndexOutOfRange(strconv.Itoa(i), n) 39 } 40 return i + n, nil 41 } 42 if includeN { 43 if i > n { 44 return 0, posIndexOutOfRange(strconv.Itoa(i), n+1) 45 } 46 } else { 47 if i >= n { 48 return 0, posIndexOutOfRange(strconv.Itoa(i), n) 49 } 50 } 51 return i, nil 52 } 53 54 // ConvertListIndex parses a list index, check whether it is valid, and returns 55 // the converted structure. 56 func ConvertListIndex(rawIndex interface{}, n int) (*ListIndex, error) { 57 switch rawIndex := rawIndex.(type) { 58 case float64: 59 index := int(rawIndex) 60 if rawIndex != float64(index) { 61 return nil, errIndexMustBeInteger 62 } 63 index, err := adjustAndCheckIndex(index, n, false) 64 if err != nil { 65 return nil, err 66 } 67 return &ListIndex{false, index, 0}, nil 68 case string: 69 slice, i, j, err := parseIndexString(rawIndex, n) 70 if err != nil { 71 return nil, err 72 } 73 if !slice { 74 i, err = adjustAndCheckIndex(i, n, false) 75 if err != nil { 76 return nil, err 77 } 78 } else { 79 i, err = adjustAndCheckIndex(i, n, true) 80 if err != nil { 81 return nil, err 82 } 83 j0 := j 84 j, err = adjustAndCheckIndex(j, n, true) 85 if err != nil { 86 return nil, err 87 } 88 if j < i { 89 if j0 < 0 { 90 return nil, errs.OutOfRange{ 91 What: "negative slice upper index here", 92 ValidLow: strconv.Itoa(i - n), ValidHigh: "-1", 93 Actual: strconv.Itoa(j0)} 94 } 95 return nil, errs.OutOfRange{ 96 What: "slice upper index here", 97 ValidLow: strconv.Itoa(i), ValidHigh: strconv.Itoa(n), 98 Actual: strconv.Itoa(j0)} 99 } 100 } 101 return &ListIndex{slice, i, j}, nil 102 default: 103 return nil, errIndexMustBeInteger 104 } 105 } 106 107 // Index = Number | 108 // Number ( ':' | '..' | '..=' ) Number 109 func parseIndexString(s string, n int) (slice bool, i int, j int, err error) { 110 low, sep, high := splitIndexString(s) 111 if sep == "" { 112 // A single number 113 i, err := atoi(s, n) 114 if err != nil { 115 return false, 0, 0, err 116 } 117 return false, i, 0, nil 118 } 119 if low == "" { 120 i = 0 121 } else { 122 i, err = atoi(low, n) 123 if err != nil { 124 return false, 0, 0, err 125 } 126 } 127 if high == "" { 128 j = n 129 } else { 130 j, err = atoi(high, n) 131 if err != nil { 132 return false, 0, 0, err 133 } 134 if sep == "..=" { 135 j++ 136 } 137 } 138 // Two numbers 139 return true, i, j, nil 140 } 141 142 func splitIndexString(s string) (low, sep, high string) { 143 if i := strings.IndexRune(s, ':'); i >= 0 { 144 return s[:i], ":", s[i+1:] 145 } 146 if i := strings.Index(s, "..="); i >= 0 { 147 return s[:i], "..=", s[i+3:] 148 } 149 if i := strings.Index(s, ".."); i >= 0 { 150 return s[:i], "..", s[i+2:] 151 } 152 return s, "", "" 153 } 154 155 // atoi is a wrapper around strconv.Atoi, converting strconv.ErrRange to 156 // errs.OutOfRange. 157 func atoi(a string, n int) (int, error) { 158 i, err := strconv.Atoi(a) 159 if err != nil { 160 if err.(*strconv.NumError).Err == strconv.ErrRange { 161 if i < 0 { 162 return 0, negIndexOutOfRange(a, n) 163 } 164 return 0, posIndexOutOfRange(a, n) 165 } 166 return 0, errIndexMustBeInteger 167 } 168 return i, nil 169 } 170 171 func posIndexOutOfRange(index string, n int) errs.OutOfRange { 172 return errs.OutOfRange{ 173 What: "index here", 174 ValidLow: "0", ValidHigh: strconv.Itoa(n - 1), Actual: index} 175 } 176 177 func negIndexOutOfRange(index string, n int) errs.OutOfRange { 178 return errs.OutOfRange{ 179 What: "negative index here", 180 ValidLow: strconv.Itoa(-n), ValidHigh: "-1", Actual: index} 181 }