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  }