# a bag is a combination of a stack, array and dict
# ## like a stack, you can
# - **push** a value onto the top
# - **pop** a value off of the top
# - **read** the topmost value
# - get the **size** of the stack
# ## like an array, you can
# - **filter** for values matching a pattern
# - **find** the first value matching a pattern
# - get the **index** of the first value matching a pattern
# ## like a dict, you can
# - **set** a key to a value.  This is stored as "$key=$value".  It will
#   overwrite any previous value for $key.
# - **get** the value for a key.
# - **print** outputs the contents of the bag, one line at a time.
bag () {
  action=$1
  varname=$2
  shift 2
  if [ "$action" != "init" ]; then
    length=$(eval "echo \${#$varname[*]}")
    last=$(( length - 1 ))
  fi
  case "$action" in
    init) eval "$varname=( )" ;;
    push) eval "$varname[$length]=\"$1\"" ;;
    pop) eval "unset $varname[$last]" ;;
    read)
      [ "$length" -gt 0 ] && echo $(eval "echo \${$varname[$last]}") ;;
    size) echo $length ;;
    filter)
      index=0
      (( limit=$2 ))
      [ "$limit" -eq 0 ] && limit=-1
      while [ "$index" -lt $length ]; do
        line=$(eval "echo \${$varname[$index]}")
        if str_matches "$line" "$1"; then
          [ -n "$3" ] && echo $index || echo $line
          [ "$limit" -ge $index ] && return
        fi
        (( index++ ))
      done ;;
    find) echo $(bag filter $varname $1 1) ;;
    index) echo $(bag filter $varname $1 1 1) ;;
    set)
      idx=$(bag index $varname "^$1=")
      [ -z "$idx" ] && idx=$length
      eval "$varname[$idx]=\"$1=$2\""
      ;;
    get)
      line=$(bag filter $varname "^$1=" 1)
      echo "${line##*=}" ;;
    print)
      index=0
      while [ "$index" -lt $length ]; do
        eval "echo \"\${$varname[$index]}\""
        (( index++ ))
      done
      ;;
    *) return 1 ;;
  esac
}
