index_list.go 3.7 KB

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