Joe's Lists Library

From Gwen Morse's Wiki
Jump to: navigation, search
;;===========================================================
;; This collection of list macros is the work of Joe Pelkey 
;; other than slight modifications by Worsel
;;
;; pop() and push() renamed to listpop() and listpush() by 
;; Gwen Morse to resolve issues with standard lib world-q.tf
;;===========================================================
;
; LISTS.TF - Version 0.4
;
; LISTS.TF - List functions, similar to zmud's, '|' is a separator between
;            list items. List items start on 1. No list functions do boundary
;            checks, if paramaters are out-of-bounds, least destructive result
;            is usually returned. Lists that have >0 items start with a '|' as
;            a placeholder to differentiate between lists with 0 items ("") and
;            lists with 1 item that's NULL ("|"). Items within list strings can
;            only contain the pipe ('|') and backslash ('\') characters if
;            escaped by a backslash. item() and listpop() return an item without
;            escaped | and \, additem(), replaceitem(), ismember(), and listpush()
;            are passed str args without escaped | and \, all other functions
;            that return or are passed an item require escaped | and \ chars.
;            These functions are optimized for speed, please report any bugs,
;            possible optimizations, or new features to me at
;            "pelkeyj@SoftHome.net".
;
; EXAMPLES: "|a|b|c d||e:f|" | ""       | "|"        | "|t"        | "||a"
;           -----------------+----------+------------+-------------+------------
;           6 items          | 0 items  | 1 item     | 1 item      | 2 items
;           -----------------+----------+------------+-------------+------------
;           item 1: "a"      |          | item 1: "" | item 1: "t" | item 1: ""
;           item 2: "b"      |          |            |             | item 2: "a"
;           item 3: "c d"    |          |            |             |
;           item 4: ""       |          |            |             |
;           item 5: "e:f"    |          |            |             |
;           item 6: ""       |          |            |             |
;
; item(list, n, listsize)   = n > 0 : return item 'n' in 'list'.
;                             n == 0: return number of elements in 'list'.
;                             n < 0 : return item abs('n') from last in 'list'.
;                             'listsize' is optional and used solely for
;                             efficiency, a "numitems" on the list if
;                             necessary is done in 'listsize's absence.
;
; itemrange(list, n, len, listsize) =
;                             n > 0 : return items 'n' through 'n' + 
;                                     (('n' > 0) ? ('len' - 1) : ('len' + 1))
;                                     in 'list'. If ('len' == ""), return
;                                     items 'n' through the last item, else if
;                                     ('len' == 0) then return NULL.
;                             n == 0: return NULL.
;                             n < 0 : return items abs('n') from last through
;                                     abs('n') - (('n' > 0) ? ('len' - 1)
;                                     : ('len' + 1)) from last in
;                                     'list'. If ('len' == ""), return items
;                                     abs('n') from last through the first
;                                     item else if ('len' == 0) then return
;                                     NULL.
;                             'listsize' is optional, used for efficiency in
;                             the same way "item" uses it.
;
; itemrangeold(list, n, len, listsize) =
;                             n > 0 : return items 'n' through 'n' + abs('len')
;                                     - 1 in 'list'. If ('len' == ""), return
;                                     items 'n' through the last item, else if
;                                     ('len' == 0) then return NULL.
;                             n == 0: return NULL.
;                             n < 0 : return items abs('n') from last through
;                                     (abs('n') + abs('len') - 1) from last in
;                                     'list'. If ('len' == ""), return items
;                                     abs('n') from last through the first
;                                     item else if ('len' == 0) then return
;                                     NULL.
;                             'listsize' is optional, used for efficiency in
;                             the same way "item" uses it.
;
; itemrangepos(list, n1, n2, listsize) =
;                            n1 > 0 : return items 'n1' through 'n2' (unless
;                                     'n2' is negative, then abs('n2') from last) 
;                                     in 'list'. If ('n2' == ""), return
;                                     items 'n1' through the last item.
;             (n1 == 0) | (n2 == 0) : return NULL.
;                            n1 < 0 : return items abs('n1') from last through
;                                     'n2' (unless 'n2' is negative, then
;                                     abs('n2') from last) in 'list'.
;                                     'list'. If ('n2' == ""), return items
;                                     abs('n1') from last through the first
;                                     item.
;                             'listsize' is optional, used for efficiency in
;                             the same way "item" uses it.
;
; additem(str, list, n, listsize) =
;                             n > 0 : return list after inserting 'str' before
;                                     item 'n' in 'list'.
;                             n == 0: return list after appending 'str' to
;                                     'list'. ('n' == 0) == ('n' == -1)
;                             n < 0 : return list after inserting 'str' after
;                                     item abs('n') from last in 'list'.
;
; additemrange(str, list, n, listsize) = ('str' is specified as an item range)
;                             n > 0 : return list after inserting 'str' before
;                                     item 'n' in 'list'.
;                             n == 0: return list after appending 'str' to
;                                     'list'. ('n' == 0) == ('n' == -1)
;                             n < 0 : return list after inserting 'str' after
;                                     item abs('n') from last in 'list'.
;
; delitem(list, n, listsize) =
;                             n > 0 : return list after deleting item 'n' in
;                                     'list'.
;                             n == 0: return 'list'.
;                             n < 0 : return list after deleting item abs('n')
;                                     from last in 'list'.
;                             'listsize' is optional, used for efficiency in
;                             the same way "item" uses it.
;
; delitemrange(list, n, len, listsize) = (works like itemrange())
;                             n > 0 : return list after deleting items 'n' 
;                                     through 'n' + (('n' > 0) ? ('len' - 1)
;                                     : ('len' + 1)) in 'list'. If 
;                                     ('len' == "") or ('len' == 0), then
;                                     return 'list'.
;              (n == 0) | (len == 0): return 'list'.
;                             n < 0 : return list after deleting items abs('n')
;                                     from last through item abs('n') -
;                                     (('n' > 0) ? ('len' - 1) : ('len' + 1))
;                                     from last in 'list'. If ('len' == "")
;                                     or ('len' == 0), then return 'list'.
;                             'listsize' is optional, used for efficiency in
;                             the same way "item" uses it.
;
; replaceitem(str, list, n, listsize) =
;                             n > 0 : return list after replacing item 'n' in
;                                     'list' with 'str'.
;                             n == 0: return 'list'.
;                             n < 0 : return list after replacing item abs('n')
;                                     from last in 'list' with 'str'.
;
; replaceitemrange(str, list, n, len, listsize) = (works like itemrange(),
;                                                  'str' is specified as an
;                                                  item range)
;                             n > 0 : return list after replacing items 'n'
;                                     through 'n' + (('n' > 0) ? ('len' - 1)
;                                     : ('len' + 1)) in 'list' with 'str'. If 
;                                     ('len' == "") or ('len' == 0), then
;                                     return 'list'.
;                             n == 0: return 'list'.
;                             n < 0 : return list after replacing items
;                                     abs('n') from last through item abs('n')
;                                     - (('n' > 0) ? ('len' - 1) : ('len' + 1))
;                                     from last in 'list' with 'str'. If
;                                     ('len' == "") or ('len' == 0), then
;                                     return 'list'.
;
; ismember(str, list, n)    = return the item number of 'n'th item match that
;                             exactly string matches 'str' in 'list'. If no
;                             'n'th item match, return 0.
;
; ismembers(str, list, n)    = return the item number of 'n'th item match that
;                              exactly string matches the item range 'str' in
;                              'list'. If no 'n'th item match, return 0.
;
; ciismember(str, list, n)    = return the item number of 'n'th item match that
;                               case insensitively exactly string matches 'str'
;                               in 'list'. If no 'n'th item match, return 0.
;
; ciismembers(str, list, n)    = return the item number of 'n'th item match
;                                that case insensitively exactly string matches
;                                the item range 'str' in 'list'. If no 'n'th
;                                item match, return 0.
;
; reismember(str, list, n)    = return the item number of 'n'th item match that
;                               PERL regexp matches 'str' in 'list'. If no
;                               'n'th item match, return 0.
;
; reismembers(str, list, n)    = return the item number of 'n'th item match
;                                that PERL regexp matches the item range 'str'
;                                in 'list'. If no 'n'th item match, return 0.
;
; numitems(list)            = return number of elements in 'list' (faster than
;                             "item(list, 0)").
;
; listpop(list)                 = returns the first item from 'list', and deletes
;                             the first item from 'list' if 'list' is settable.
;
; listpush(str, list)           = returns list after adding 'str' as a list item
;                             to the list before 'list', and adds 'str' as a
;                             list item before 'list' to 'list' if 'list' is
;                             settable.

;/set defcompile=1



/def list_list2func = \
  /return strchr({1}, "\\\\|") ? \
    (replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1})))) \
    : \
    ({1})
/def list_func2list = \
  




/def numitems = \
  /let _in=%; \
  /return (strstr({1}, "\\\\|") != -1) ? \
    ((_in := (replace("\\\\|", "", replace("\\\\\\\\", "", {1})))), \
    (strlen(_in) - strlen(replace("|", "", _in)))) \
    : \
    (strlen({1}) - strlen(replace("|", "", {1})))





/def item = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _listin=%; \
  /let _nitem=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /let _arg1=%{1}%;\
  /let _arg2=%{2}%;\
  /let _arg3=%{3}%;\
  /if /test (_nitem := trunc({2})) == 0%; /then \
    /return numitems({1})%; \
  /endif%; \
  /test _listin := (strstr({1}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
    : \
    ((_listdidsubst := 0), "%_arg1")%; \
  /test ({#} >= 3) & (_listsize := trunc({3}))%; \
  /if /test (_nitem < 0) & \
        (((_listsize < 0) & (_listsize := numitems(_listin))), \
        ((_nitem := (_listsize - abs(_nitem) + 1)), (_nitem < 1)))%; /then \
    /return ""%; \
  /endif%; \
  /return ((_listsize < 0) | (_nitem <= _listsize)) ? \
      (itemrange_skip(_nitem - 1), \
      (((_itemdiv := strchr(_listin, "|", 1)) != -1) ? \
        item_returnproper(substr(_listin, 1, _itemdiv - 1), _listdidsubst) \
      : \
        item_returnproper(substr(_listin, 1), _listdidsubst))) \
    : \
      ""






/def ismember = \
; ismember(str, list, n)    = return the item number of 'n'th item match that
  /let _str=%; \
  /let _listin=%; \
  /let _nmatch=1%; \
  /let _listleft=%; \
  /test _str := \
                replace("{", "\\\\{", \
                replace("+", "\\\\+", \
                replace("*", "\\\\*", \
                replace("?", "\\\\?", \
                replace(")", "\\\\)", \
                replace("(", "\\\\(", \
                replace("[", "\\\\[", \
                replace(".", "\\\\.", \
                replace("$$", "\\\\$$", \
                replace("^", "\\\\^", \
                replace("|", "<P", \
                replace("\\\\", "<B", \
                replace("<", "<L", {1})))))))))))))%; \
  /test _listin := \
                   replace("\\\\", "", \
                   replace("\\\\|", "<P", \
                   replace("\\\\\\\\", "<B", \
                   replace("<", "<L", {2}))))%; \
  /test ({#} >= 3) & (({3} > 0) & (_nmatch := trunc({3})))%; \
  /while /test _nmatch > 0%; /do \
    /if /test regmatch(strcat("(?-i)(\\\\|", _str, ")(\\\\||$$)"), _listin)%; /then \
      /test (_listleft := strcat(_listleft, {PL}, {P1})), \
        (_listin := strcat({P2}, {PR}))%; \
    /else \
      /return 0%; \
    /endif%; \
    /test _nmatch := (_nmatch - 1)%; \
  /done%; \
  /return numitems(_listleft)

/def ismembers = \
; ismember(str, list, n)    = return the item number of 'n'th item match that
  /let _str=%; \
  /let _listin=%; \
  /let _nmatch=1%; \
  /let _listleft=%; \
  /let _listcuritem=%; \
  /test _str := \
                replace("{", "\\\\{", \
                replace("+", "\\\\+", \
                replace("*", "\\\\*", \
                replace("?", "\\\\?", \
                replace(")", "\\\\)", \
                replace("(", "\\\\(", \
                replace("[", "\\\\[", \
                replace(".", "\\\\.", \
                replace("$$", "\\\\$$", \
                replace("^", "\\\\^", \
                replace("|", "\\\\|", \
                replace("\\\\", "", \
                replace("\\\\|", "<P", \
                replace("\\\\\\\\", "<B", \
                replace("<", "<L", {1})))))))))))))))%; \
  /test _listin := \
                   replace("\\\\", "", \
                   replace("\\\\|", "<P", \
                   replace("\\\\\\\\", "<B", \
                   replace("<", "<L", {2}))))%; \
  /test ({#} >= 3) & (({3} > 0) & (_nmatch := trunc({3})))%; \
  /while /test _nmatch > 0%; /do \
    /if /test regmatch(strcat("(?-i)(", _str, ")(\\\\||$$)"), _listin)%; /then \
      /test (_listleft := strcat(_listleft, {PL}, _listcuritem)), \
        (_listcuritem := {P1}), \
        (_listin := strcat({P2}, {PR}))%; \
    /else \
      /return 0%; \
    /endif%; \
    /test _nmatch := (_nmatch - 1)%; \
  /done%; \
  /return numitems(_listleft) + 1


/def ciismember = \
; ismember(str, list, n)    = return the item number of 'n'th item match that
  /let _str=%; \
  /let _listin=%; \
  /let _nmatch=1%; \
  /let _listleft=%; \
  /test _str := \
                replace("{", "\\\\{", \
                replace("+", "\\\\+", \
                replace("*", "\\\\*", \
                replace("?", "\\\\?", \
                replace(")", "\\\\)", \
                replace("(", "\\\\(", \
                replace("[", "\\\\[", \
                replace(".", "\\\\.", \
                replace("$$", "\\\\$$", \
                replace("^", "\\\\^", \
                replace("|", "<P", \
                replace("\\\\", "<B", \
                replace("<", "<L", {1})))))))))))))%; \
  /test _listin := \
                   replace("\\\\", "", \
                   replace("\\\\|", "<P", \
                   replace("\\\\\\\\", "<B", \
                   replace("<", "<L", {2}))))%; \
  /test ({#} >= 3) & (({3} > 0) & (_nmatch := trunc({3})))%; \
  /while /test _nmatch > 0%; /do \
    /if /test regmatch(strcat("(?i)(\\\\|", _str, ")(\\\\||$$)"), _listin)%; /then \
      /test (_listleft := strcat(_listleft, {PL}, {P1})), \
        (_listin := strcat({P2}, {PR}))%; \
    /else \
      /return 0%; \
    /endif%; \
    /test _nmatch := (_nmatch - 1)%; \
  /done%; \
  /return numitems(_listleft)

/def ciismembers = \
; ismember(str, list, n)    = return the item number of 'n'th item match that
  /let _str=%; \
  /let _listin=%; \
  /let _nmatch=1%; \
  /let _listleft=%; \
  /let _listcuritem=%; \
  /test _str := \
                replace("{", "\\\\{", \
                replace("+", "\\\\+", \
                replace("*", "\\\\*", \
                replace("?", "\\\\?", \
                replace(")", "\\\\)", \
                replace("(", "\\\\(", \
                replace("[", "\\\\[", \
                replace(".", "\\\\.", \
                replace("$$", "\\\\$$", \
                replace("^", "\\\\^", \
                replace("|", "\\\\|", \
                replace("\\\\", "", \
                replace("\\\\|", "<P", \
                replace("\\\\\\\\", "<B", \
                replace("<", "<L", {1})))))))))))))))%; \
  /test _listin := \
                   replace("\\\\", "", \
                   replace("\\\\|", "<P", \
                   replace("\\\\\\\\", "<B", \
                   replace("<", "<L", {2}))))%; \
  /test ({#} >= 3) & (({3} > 0) & (_nmatch := trunc({3})))%; \
  /while /test _nmatch > 0%; /do \
    /if /test regmatch(strcat("(?i)(", _str, ")(\\\\||$$)"), _listin)%; /then \
      /test (_listleft := strcat(_listleft, {PL}, _listcuritem)), \
        (_listcuritem := {P1}), \
        (_listin := strcat({P2}, {PR}))%; \
    /else \
      /return 0%; \
    /endif%; \
    /test _nmatch := (_nmatch - 1)%; \
  /done%; \
  /return numitems(_listleft) + 1


/def reismember = \
; ismember(str, list, n)    = return the item number of 'n'th item match that
  /let _str=%; \
  /let _listin=%; \
  /let _nmatch=1%; \
  /let _listleft=%; \
  /test _str := \
                replace("\\\\|", "<0", \
                replace("\\\\\\\\", "<1", \
                replace("<", "<2", {1})))%; \
  /test _listin := \
                   replace("\\\\", "", \
                   replace("\\\\|", "<0", \
                   replace("\\\\\\\\", "<1", \
                   replace("<", "<2", {2}))))%; \
  /test ({#} >= 3) & (({3} > 0) & (_nmatch := trunc({3})))%; \
  /while /test _nmatch > 0%; /do \
    /if /test regmatch(strcat("(?-i)(\\\\|(?:", _str, "))(\\\\||$$)"), _listin)%; /then \
      /test (_listleft := strcat(_listleft, {PL}, {P1})), \
        (_listin := strcat({P2}, {PR}))%; \
    /else \
      /return 0%; \
    /endif%; \
    /test _nmatch := (_nmatch - 1)%; \
  /done%; \
  /return numitems(_listleft)

/def reismembers = \
; ismember(str, list, n)    = return the item number of 'n'th item match that
  /let _str=%; \
  /let _listin=%; \
  /let _nmatch=1%; \
  /let _listleft=%; \
  /let _listcuritem=%; \
  /test _str := \
                replace("\\\\\\\\", "", \
                replace("\\\\\\\\|", "<0", \
                replace("\\\\\\\\\\\\\\\\", "<1", \
                replace("<", "<2", {1}))))%; \
  /test _listin := \
                   replace("\\\\", "", \
                   replace("\\\\|", "<0", \
                   replace("\\\\\\\\", "<1", \
                   replace("<", "<2", {2}))))%; \
  /test ({#} >= 3) & (({3} > 0) & (_nmatch := trunc({3})))%; \
  /while /test _nmatch > 0%; /do \
    /if /test regmatch(strcat("(?-i)((?:", _str, "))(\\\\||$$)"), _listin)%; /then \
      /test (_listleft := strcat(_listleft, {PL}, _listcuritem)), \
        (_listcuritem := {P1}), \
        (_listin := strcat({P2}, {PR}))%; \
    /else \
      /return 0%; \
    /endif%; \
    /test _nmatch := (_nmatch - 1)%; \
  /done%; \
  /return numitems(_listleft) + 1




;/def additem = \
; additem(str, list, n)     = n > 0 : return list after inserting 'str' before
;                                     item 'n' in 'list'.
;                             n == 0: return list after appending 'str' to
;                                     'list'. ('n' == 0) == ('n' == -1)
;                             n < 0 : return list after inserting 'str' after
;                                     item abs('n') from last in 'list'.
;  /let _str=%; \
;  /let _listin=%; \
;  /let _nplace=%; \
;  /test 

/def itemrangeold = \
  /let _listin=%; \
  /let _listout=%; \
  /let _nplace=%; \
  /let _nlen=%; \
  /let _nfirst=%; \
  /let _nlast=%; \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /test _listin := (strstr({1}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
    : \
    ((_listdidsubst := 0), {1})%; \
  /test _nplace := trunc({2})%; \
  /test _nlen := (({#} < 3) ? maxpri : (({3} =~ "") ? maxpri : trunc(abs({3}))))%; \
  /test ({#} >= 4) & (_listsize := trunc({4}))%; \
  /if /test (_nplace == 0) | (_nlen == 0)%; /then \
    /return ""%; \
  /endif%; \
  /if /test _nplace < 0%; /then \
    /test ((_listsize < 0) & (_listsize := numitems(_listin))), \
      (_nlast := (_listsize - abs(_nplace) + 1))%; \
    /if /test _nlast <= 0%; /then \
      /return ""%; \
    /endif%; \
    /test (_nfirst := (_nlast - _nlen + 1)), \
      ((_nfirst <= 0) & (_nfirst := 1))%; \
  /else \
    /test (_nfirst := _nplace), \
      (_nlast := (_nfirst + _nlen - 1)), \
      (((_listsize > -1) & (_nlast > _listsize)) & (_nlast := _listsize))%; \
  /endif%; \
  /return itemrange_skip(_nfirst - 1), \
    itemrange_len(_nlast - _nfirst + 1), \
    itemrange_returnproper(_listout, _listdidsubst)


/def itemrange = \
  /let _listin=%; \
  /let _listout=%; \
  /let _nplace=%; \
  /let _nlen=%; \
  /let _nfirst=%; \
  /let _nlast=%; \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _ntmp=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /test _listin := (strstr({1}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
    : \
    ((_listdidsubst := 0), {1})%; \
  /test _nplace := trunc({2})%; \
  /test _nlen := (({#} < 3) ? "" : (({3} != 0) ? trunc({3}) : {3}))%; \
  /test ({#} >= 4) & (_listsize := trunc({4}))%; \
  /if /test (_nplace == 0) | ((_nlen !~ "") & (_nlen == 0))%; /then \
    /return ""%; \
  /endif%; \
  /if /test _nplace < 0%; /then \
    /test ((_listsize < 0) & (_listsize := numitems(_listin))), \
      (_nlast := (_listsize + _nplace + 1))%; \
    /test ((_nlen !~ "") ? \
        ((_nfirst := (_nlast + _nlen + ((_nlen > 0) ? -1 : 1))), \
        ((_nfirst > _listsize) & (_nfirst := _listsize)), \
        ((_nfirst > _nlast) & \
          ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp)))) \
      : \
        (_nfirst := 1))%; \
    /if /test (_nlast < 1)%; /then \
      /return ""%; \
    /endif%; \
    /test (_nfirst < 1) & (_nfirst := 1)%; \
  /else \
    /test (_nfirst := _nplace), \
      ((_nlen !~ "") ? \
        ((_nlast := (_nfirst + _nlen + ((_nlen > 0) ? -1 : 1))), \
        ((_nlast < 1) & (_nlast := 1)), \
        ((_nfirst > _nlast) & \
          ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp)))) \
      : \
        (_nlast := ((_listsize > -1) ? _listsize : maxpri)))%; \
    /if /test ((_listsize > -1) & (_nfirst > _listsize))%; /then \
      /return ""%; \
    /endif%; \
    /test ((_listsize > -1) & (_nlast > _listsize)) & (_nlast := _listsize)%; \
  /endif%; \
  /return itemrange_skip(_nfirst - 1), \
    itemrange_len(_nlast - _nfirst + 1), \
    itemrange_returnproper(_listout, _listdidsubst)

/def itemrangepos = \
; itemrangepos(list, n1, n2, listsize) =
;                            n1 > 0 : return items 'n1' through 'n2' (unless
;                                     'n2' is negative, then abs('n2') from last) 
;                                     in 'list'. If ('n2' == ""), return
;                                     items 'n1' through the last item.
;             (n1 == 0) | (n2 == 0) : return NULL.
;                            n1 < 0 : return items abs('n1') from last through
;                                     'n2' (unless 'n2' is negative, then
;                                     abs('n2') from last) in 'list'.
;                                     'list'. If ('n2' == ""), return items
;                                     abs('n1') from last through the first
;                                     item.
;                             'listsize' is optional, used for efficiency in
;                             the same way "item" uses it.
  /let _listin=%; \
  /let _listout=%; \
  /let _nplace=%; \
  /let _nplace2=%; \
  /let _nfirst=%; \
  /let _nlast=%; \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _ntmp=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /test (_listin := ((strstr({1}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
    : \
    ((_listdidsubst := 0), {1}))), \
    (_nplace := trunc({2})), \
    (_nplace2 := (({#} < 3) ? "" : (({3} != 0) ? trunc({3}) : {3}))), \
    (({#} >= 4) & (_listsize := trunc({4})))%; \
  /if /test (_nplace == 0) | ((_nplace2 !~ "") & (_nplace2 == 0))%; /then \
    /return ""%; \
  /endif%; \
  /if /test _nplace < 0%; /then \
    /test (((_listsize < 0) & (_listsize := numitems(_listin))), \
      (_nfirst := (_listsize + _nplace + 1))), \
      (_nlast := ((_nplace2 !~ "") ? \
      ((_nplace2 > 0) ? \
        (_nplace2) : \
        (_listsize + _nplace2 + 1)) : \
      ((-maxpri)-1)))%; \
    /if /test ((_nfirst < 1) & (_nlast < 1))%; /then \
      /return ""%; \
    /endif%; \
  /else \
    /test (_nfirst := _nplace), \
      (_nlast := ((_nplace2 !~ "") ? \
      ((_nplace2 > 0) ? \
        (_nplace2) : \
        (((_listsize < 0) & (_listsize := numitems(_listin))), \
          (_listsize + _nplace2 + 1))) : \
      (maxpri)))%; \
    /if /test ((_listsize > -1) & (_nfirst > _listsize) & (_nlast > _listsize))%; /then \
      /return ""%; \
    /endif%; \
  /endif%; \
  /test ((_nfirst > _nlast) & \
      ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp))), \
    ((_nfirst < 1) & (_nfirst := 1)), \
    (((_listsize > -1) & (_nlast > _listsize)) & (_nlast := _listsize))%; \
  /return itemrange_skip(_nfirst - 1), \
    itemrange_len(_nlast - _nfirst + 1), \
    itemrange_returnproper(_listout, _listdidsubst)




; if {2} nonzero, already tokenized special sequences, otherwise didn't
/def item_returnproper = \
  /return {2} ? \
    replace("<L", "<", replace("<B", "\\\\", replace("<P", "|", replace("\\\\", "", {1})))) \
      : \
      replace("<L", "<", replace("<B", "\\\\", replace("\\\\", "", replace("\\\\\\\\", "<B", replace("<", "<L", {1})))))



; if {2} nonzero, already tokenized special sequences, otherwise didn't
/def itemrange_returnproper = \
  /return {2} ? \
    replace("<L", "<", replace("<B", "\\\\\\\\", replace("<P", "\\\\|", replace("\\\\", "", {1})))) \
      : \
      replace("<L", "<", replace("<B", "\\\\\\\\", replace("\\\\", "", replace("\\\\\\\\", "<B", replace("<", "<L", {1})))))


/def itemadded_returnproper = \
  /return replace("|", "\\\\|", replace("\\\\", "\\\\\\\\", {1}))


/set itemincrsize=1024

;ITEMRANGE_SKIP
; needs _listin (set to the list), _itemdiv, and _itemmod variables defined
; {1} == n
; DOES NOT TOKENIZE
; sets _listin to list after skipping over first n items
/def itemrange_skip = \
  /if /test {1} != 0%; /then \
    /test (_itemdiv := trunc({1} / itemincrsize)), \
      (_itemmod := mod({1}, itemincrsize))%; \
    /while /test _itemdiv & (_listin !~ "")%; /do \
      /test regmatch(strcat("^(?>(?>\\\\|[^\\\\|]*){", itemincrsize, "})"), _listin), \
        (_listin := {PR}), \
        (--_itemdiv)%; \
    /done%; \
    /test regmatch(strcat("^(?>(?>\\\\|[^\\\\|]*){", _itemmod, "})"), _listin), \
      (_listin := {PR})%; \
  /endif
  

;ITEMRANGE_LEN
; needs _listin (set to the list), _listout (empty), _itemdiv, and _itemmod variables defined
; {1} == n
; DOES NOT TOKENIZE
; sets _listout to first n items of _listin, leaves _listin in an
;  inconsistant state
/def itemrange_len = \
  /if /test {1} != 0%; /then \
    /test (_itemdiv := trunc({1} / itemincrsize)), \
      (_itemmod := mod({1}, itemincrsize))%; \
    /while /test _itemdiv & (_listin !~ "")%; /do \
      /test regmatch(strcat("^(?>(?>\\\\|[^\\\\|]*){1,", itemincrsize, "})"), _listin), \
        (_listout := strcat(_listout, {P0})), \
        (_listin := {PR}), \
        (--_itemdiv)%; \
    /done%; \
    /test regmatch(strcat("^(?>(?>\\\\|[^\\\\|]*){0,", _itemmod, "})"), _listin), \
      (_listout := strcat(_listout, {P0}))%; \
  /endif


;ITEMRANGE_SPLIT
; needs _listin (set to the list), _listout (empty), _itemdiv, and _itemmod variables defined
; {1} == n
; DOES NOT TOKENIZE
; sets _listout to first n items of list, sets _listin to remainder of
;  list
/def itemrange_split = \
  /if /test {1} != 0%; /then \
    /test (_itemdiv := trunc({1} / itemincrsize)), \
      (_itemmod := mod({1}, itemincrsize))%; \
    /while /test _itemdiv & (_listin !~ "")%; /do \
      /test regmatch(strcat("^(?>(?>\\\\|[^\\\\|]*){1,", itemincrsize, "})"), _listin), \
        (_listout := strcat(_listout, {P0})), \
        (_listin := {PR}), \
        (--_itemdiv)%; \
    /done%; \
    /test regmatch(strcat("^(?>(?>\\\\|[^\\\\|]*){0,", _itemmod, "})"), _listin), \
      (_listout := strcat(_listout, {P0})), \
      (_listin := {PR})%; \
  /endif


;ITEMRANGE_DELFIRST
; needs _listin (set to the list), and _itemdiv variables defined
; (misuses _itemdiv to record location within _listin of second | char)
; DOES NOT TOKENIZE
; sets _listin to everything but the first item of list
;/def itemrange_delfirst = \
;  /test ((_itemdiv := strchr(_listin, "|", 1)) !~ -1) & \
;    (_listin := substr(_listin, _itemdiv))
    

/def delitem = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _listin=%; \
  /let _listout=%; \
  /let _nitem=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test (_nitem := trunc({2})) == 0%; /then \
    /return {1}%; \
  /endif%; \
  /test _listin := (strstr({1}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
    : \
    ((_listdidsubst := 0), {1})%; \
  /test ({#} >= 3) & (_listsize := trunc({3}))%; \
  /if /test (_nitem < 0) & \
        (((_listsize < 0) & (_listsize := numitems(_listin))), \
        ((_nitem := (_listsize - abs(_nitem) + 1)) < 1))%; /then \
    /return {1}%; \
  /endif%; \
  /return ((_listsize < 0) | (_nitem <= _listsize)) ? \
      (itemrange_split(_nitem - 1), \
      (((_itemdiv := strchr(_listin, "|", 1)) != -1) ? \
        itemrange_returnproper(strcat(_listout, substr(_listin, _itemdiv)), _listdidsubst) \
      : \
        ((_listin =~ "") ? {1} : itemrange_returnproper(_listout, _listdidsubst)))) \
    : \
      {1}


/def delitemrange = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _listin=%; \
  /let _listout=%; \
  /let _nplace=%; \
  /let _nlen=%; \
  /let _nfirst=%; \
  /let _nlast=%; \
  /let _ntmp=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test ((_nplace := trunc({2})) == 0) | ((_nlen := trunc({3})) == 0)%; /then \
    /return {1}%; \
  /endif%; \
  /test _listin := (strstr({1}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
    : \
    ((_listdidsubst := 0), {1})%; \
  /test ({#} >= 4) & (_listsize := trunc({4}))%; \
  /if /test _nplace < 0%; /then \
    /test ((_listsize < 0) & (_listsize := numitems(_listin))), \
      (_nlast := (_listsize + _nplace + 1))%; \
    /test ((_nlen !~ "") ? \
        ((_nfirst := (_nlast + _nlen + ((_nlen > 0) ? -1 : 1))), \
        ((_nfirst > _listsize) & (_nfirst := _listsize)), \
        ((_nfirst > _nlast) & \
          ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp)))) \
      : \
        (_nfirst := 1))%; \
    /if /test (_nlast < 1)%; /then \
      /return {1}%; \
    /endif%; \
    /test (_nfirst < 1) & (_nfirst := 1)%; \
  /else \
    /test (_nfirst := _nplace), \
      ((_nlen !~ "") ? \
        ((_nlast := (_nfirst + _nlen + ((_nlen > 0) ? -1 : 1))), \
        ((_nlast < 1) & (_nlast := 1)), \
        ((_nfirst > _nlast) & \
          ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp)))) \
      : \
        (_nlast := ((_listsize > -1) ? _listsize : maxpri)))%; \
    /if /test ((_listsize > -1) & (_nfirst > _listsize))%; /then \
      /return {1}%; \
    /endif%; \
    /test ((_listsize > -1) & (_nlast > _listsize)) & (_nlast := _listsize)%; \
  /endif%; \
  /return itemrange_split(_nfirst - 1), \
    itemrange_skip(_nlast - _nfirst + 1), \
    itemrange_returnproper(strcat(_listout, _listin), _listdidsubst)




/def additem = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _str=%; \
  /let _listin=%; \
  /let _listout=%; \
  /let _nitem=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test ((_nitem := trunc({3})) == 0) | (_nitem == -1)%; /then \
    /return strcat({2}, "|", itemadded_returnproper({1}))%; \
  /elseif /test _nitem == 1%; /then \
    /return strcat("|", itemadded_returnproper({1}), {2})%; \
  /endif%; \
  /test _str := itemadded_returnproper({1})%; \
  /test _listin := (strstr({2}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {2}))) \
    : \
    ((_listdidsubst := 0), {2})%; \
  /test ({#} >= 4) & (_listsize := trunc({4}))%; \
  /if /test (_nitem < 0) & \
        (((_listsize < 0) & (_listsize := numitems(_listin))), \
        ((_nitem := (_listsize - abs(_nitem) + 2)) < 2))%; /then \
    /return strcat("|", _str, {2})%; \
  /endif%; \
  /return ((_listsize < 0) | (_nitem <= _listsize)) ? \
      (itemrange_split(_nitem - 1), \
      strcat(itemrange_returnproper(_listout, _listdidsubst), "|", _str, itemrange_returnproper(_listin, _listdidsubst))) \
    : \
      strcat({2}, "|", _str)


/def additemrange = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _listin=%; \
  /let _listout=%; \
  /let _nitem=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test ((_nitem := trunc({3})) == 0) | (_nitem == -1)%; /then \
    /return strcat({2}, {1})%; \
  /elseif /test _nitem == 1%; /then \
    /return strcat({1}, {2})%; \
  /endif%; \
  /test _listin := (strstr({2}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {2}))) \
    : \
    ((_listdidsubst := 0), {2})%; \
  /test ({#} >= 4) & (_listsize := trunc({4}))%; \
  /if /test (_nitem < 0) & \
        (((_listsize < 0) & (_listsize := numitems(_listin))), \
        ((_nitem := (_listsize - abs(_nitem) + 2)) < 2))%; /then \
    /return strcat({1}, {2})%; \
  /endif%; \
  /return ((_listsize < 0) | (_nitem <= _listsize)) ? \
      (itemrange_split(_nitem - 1), \
      strcat(itemrange_returnproper(_listout, _listdidsubst), {1}, itemrange_returnproper(_listin, _listdidsubst))) \
    : \
      strcat({2}, {1})


/def replaceitem = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _str=%; \
  /let _listin=%; \
  /let _listout=%; \
  /let _nitem=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test ((_nitem := trunc({3})) == 0)%; /then \
    /return {2}%; \
  /endif%; \
  /test _str := itemadded_returnproper({1})%; \
  /test _listin := (strstr({2}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {2}))) \
    : \
    ((_listdidsubst := 0), {2})%; \
  /test ({#} >= 4) & (_listsize := trunc({4}))%; \
  /if /test (_nitem < 0) & \
        (((_listsize < 0) & (_listsize := numitems(_listin))), \
        ((_nitem := (_listsize - abs(_nitem) + 1)) < 1))%; /then \
    /return {2}%; \
  /endif%; \
  /return ((_listsize < 0) | (_nitem <= _listsize)) ? \
    (itemrange_split(_nitem - 1), \
    (((_itemdiv := strchr(_listin, "|", 1)) != -1) ? \
      strcat(itemrange_returnproper(_listout, _listdidsubst), "|", _str, itemrange_returnproper(substr(_listin, _itemdiv), _listdidsubst)) : \
      ((_listin =~ "") ? {2} : strcat(itemrange_returnproper(_listout, _listdidsubst), "|", _str)))) \
    : \
      {2}


/def replaceitemrange = \
  /let _itemdiv=%; \
  /let _itemmod=%; \
  /let _listin=%; \
  /let _listout=%; \
  /let _nplace=%; \
  /let _nlen=%; \
  /let _nfirst=%; \
  /let _nlast=%; \
  /let _ntmp=%; \
  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test ((_nplace := trunc({3})) == 0) | ((_nlen := trunc({4})) == 0)%; /then \
    /return {2}%; \
  /endif%; \
  /test _listin := (strstr({2}, "\\\\|") != -1) ? \
    replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {2}))) \
    : \
    ((_listdidsubst := 0), {2})%; \
  /test ({#} >= 5) & (_listsize := trunc({5}))%; \
  /if /test _nplace < 0%; /then \
    /test ((_listsize < 0) & (_listsize := numitems(_listin))), \
      (_nlast := (_listsize + _nplace + 1))%; \
    /test ((_nlen !~ "") ? \
        ((_nfirst := (_nlast + _nlen + ((_nlen > 0) ? -1 : 1))), \
        ((_nfirst > _listsize) & (_nfirst := _listsize)), \
        ((_nfirst > _nlast) & \
          ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp)))) \
      : \
        (_nfirst := 1))%; \
    /if /test (_nlast < 1)%; /then \
      /return {2}%; \
    /endif%; \
    /test (_nfirst < 1) & (_nfirst := 1)%; \
  /else \
    /test (_nfirst := _nplace), \
      ((_nlen !~ "") ? \
        ((_nlast := (_nfirst + _nlen + ((_nlen > 0) ? -1 : 1))), \
        ((_nlast < 1) & (_nlast := 1)), \
        ((_nfirst > _nlast) & \
          ((_ntmp := _nfirst), (_nfirst := _nlast), (_nlast := _ntmp)))) \
      : \
        (_nlast := ((_listsize > -1) ? _listsize : maxpri)))%; \
    /if /test ((_listsize > -1) & (_nfirst > _listsize))%; /then \
      /return {2}%; \
    /endif%; \
    /test ((_listsize > -1) & (_nlast > _listsize)) & (_nlast := _listsize)%; \
  /endif%; \
  /return itemrange_split(_nfirst - 1), \
    ((_listin =~ "") ? {2} : \
    (itemrange_skip(_nlast - _nfirst + 1), \
    strcat(itemrange_returnproper(_listout, _listdidsubst), {1}, itemrange_returnproper(_listin, _listdidsubst))))


/def listpop = \
  /let _itemdiv=%; \
  /let _listin=%; \
  /let _listinvar=-%; \
;  /let _listsize=-1%; \
  /let _listdidsubst=1%; \
  /if /test regmatch("^(?>[0-9a-zA-Z_]+)$$", {1})%; /then \
    /test (_listinvar := {1}), \
      (_listin := (strstr(%{1}, "\\\\|") != -1) ? \
      replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", %{1}))) \
      : \
      ((_listdidsubst := 0), %{1}))%; \
    /eval /return \
      ((_itemdiv := strchr(_listin, "|", 1)) != -1) ? \
        ((%%{_listinvar} := itemrange_returnproper(substr(_listin, _itemdiv), _listdidsubst)), \
        (item_returnproper(substr(_listin, 1, _itemdiv - 1), _listdidsubst))) \
      : \
        ((%%{_listinvar} := ""), \
        (item_returnproper(substr(_listin, 1), _listdidsubst)))%; \
  /else \
    /test (_listin := (strstr({1}, "\\\\|") != -1) ? \
      replace("\\\\|", "<P", replace("\\\\\\\\", "<B", replace("<", "<L", {1}))) \
      : \
      ((_listdidsubst := 0), {1}))%; \
    /return \
      ((_itemdiv := strchr(_listin, "|", 1)) != -1) ? \
        item_returnproper(substr(_listin, 1, _itemdiv - 1), _listdidsubst) \
      : \
        item_returnproper(substr(_listin, 1), _listdidsubst)%; \
  /endif

/def listpush = \
;  /let _listin=%; \
;  /let _listinvar=-%; \
  /let _str=%; \
;  /let _listsize=-1%; \
;  /let _listdidsubst=1%; \
  /test _str := itemadded_returnproper({1})%; \
  /if /test regmatch("^(?>[0-9a-zA-Z_]+)$$", {2})%; /then \
    /eval /return \
      (%%{2} := strcat("|", _str, %%{2}))%; \
  /else \
    /return strcat("|", _str, {2})%; \
  /endif

  
/def array_init = \
   /if (!getopts("a:e#", "")) \
      /eval /echo -p % @{BCwhite}/array_init: inits array a to be a e size array.@{n}%;\
        /eval /echo -p % @{BCred}Usage: /array_add -a(string) -s(string) -i(integer)@{n}%;\
       /return -1%;\
    /endif%;\
   /let arrayname %{opt_a}%;\
   /if (!arrayname) \
      /eval /echo -p % @{BCwhite}/array_add: ERROR: array name unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /let elementstoinit %{opt_e}%;\
   /if (!elementstoinit) \
      /eval /echo -p % @{BCwhite}/array_add: ERROR: array size unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /if (elementstoinit==0) \
      /set %{arrayname} 0%;\
   /else \
        /for i 1 elementstoinit /set @{arrayname}_%{i} 0%;\
        /set %{arrayname} %{elementstoinit}%;\
    /endif


/def array_add = \
   /if (!getopts("a:s:i#", "")) \
      /eval /echo -p % @{BCwhite}/array_add: adds string s to array a with index i as the last element.@{n}%;\
        /eval /echo -p % @{BCred}Usage: /array_add -a(string) -s(string) -i(integer)@{n}%;\
       /return -1%;\
    /endif%;\
   /let arrayname %{opt_a}%;\
   /if (!arrayname) \
      /eval /echo -p % @{BCwhite}/array_add: ERROR: array name unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /let strtoadd %{opt_s}%;\
   /if (!strtoadd) \
      /eval /echo -p % @{BCwhite}/array_add: ERROR: element to add unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /let index %{opt_i}%;\
   /if (!index) \
      /eval /echo -p % @{BCwhite}/array_add: ERROR: array inde unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /eval /let newindex $[index+1]%;\
   /eval /set %{arrayname}_%{newindex} %{strtoadd}%;\
   /return %{newindex}


/def array_del = \
   /if (!getopts("a:e#i#", "")) \
      /eval /echo -p % @{BCwhite}/array_del: deletes the last e elements from array a with index i.@{n}%;\
        /eval /echo -p % @{BCred}Usage: /array_add -a(string) -e(integer) -i(integer)@{n}%;\
       /return -1%;\
   /endif%;\
   /let arrayname %{opt_a}%;\
   /if (!arrayname) \
      /eval /echo -p % @{BCwhite}/array_del: ERROR: array name unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /let numelements %{opt_e}%;\
   /if (!numelements) \
      /eval /echo -p % @{BCwhite}/array_del: ERROR: number of elements to delete unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /let index %{opt_i}%;\
   /if (!numelements) \
      /eval /echo -p % @{BCwhite}/array_del: ERROR: array index unspecified!@{n}%;\
      /return -1%;\
   /endf%;\
   /if (numelements > index) \
      /for i 1 index /unset %{arrayname}_%{i}%;\
      /unset %{arrayname}%;\
      /return 0%;\
   /else \
      /for i 1 numelements /unset %{arrayname}_%{i}%;\
      /eval /set %{arrayname} $[index-numelements]%;\
      /eval /return $[index-numelements]%;\
   /endif