From 4e4bda4c14459c557e1f2cdc41964ccb2f5ebb8b Mon Sep 17 00:00:00 2001
From: arjenmarkus <arjen.markus895@gmail.com>
Date: Fri, 18 Dec 2020 17:41:02 +0100
Subject: [PATCH 1/3] Preliminary implementation of stringlist module

This commit introduces a very preliminary version of a module to handle lists of strings (that is: a structure containing strings of varying lengths)
---
 src/CMakeLists.txt                       |   1 +
 src/stdlib_stringlist.f90                | 148 +++++++++++++++++++++++
 src/tests/CMakeLists.txt                 |   1 +
 src/tests/stringlist/CMakeLists.txt      |   1 +
 src/tests/stringlist/test_stringlist.f90 |  63 ++++++++++
 5 files changed, 214 insertions(+)
 create mode 100644 src/stdlib_stringlist.f90
 create mode 100644 src/tests/stringlist/CMakeLists.txt
 create mode 100644 src/tests/stringlist/test_stringlist.f90

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 02604959e..6605a460d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,6 +37,7 @@ set(SRC
     stdlib_error.f90
     stdlib_kinds.f90
     stdlib_logger.f90
+    stdlib_stringlist.f90
     stdlib_system.F90
     ${outFiles}
 )
diff --git a/src/stdlib_stringlist.f90 b/src/stdlib_stringlist.f90
new file mode 100644
index 000000000..08600aa1f
--- /dev/null
+++ b/src/stdlib_stringlist.f90
@@ -0,0 +1,148 @@
+! stringlist.f90 --
+!     Module for storing and manipulating lists of strings
+!     The strings may have arbitrary lengths, not necessarily the same
+!
+!     Note: very preliminary
+!
+module stdlib_stringlists
+    implicit none
+
+    private
+    integer, parameter, public :: list_head = 0
+    integer, parameter, public :: list_end  = -1
+    public :: t_stringlist
+
+
+    integer, parameter :: initial_size  = 20
+
+    type t_string
+        character(len=:), allocatable :: value
+    end type t_string
+
+    type t_stringlist
+        private
+        integer :: size = 0
+        type(t_string), dimension(:), allocatable :: string
+    contains
+        procedure :: insert => insert_string
+        procedure :: get    => get_string
+        procedure :: length => length_list
+    end type t_stringlist
+
+contains
+
+! length_list --
+!     Return the size (length) of the list
+!
+! Arguments:
+!     list                   The list of strings to retrieve the string from
+!
+integer function length_list( list )
+    class(t_stringlist), intent(in) :: list
+
+    length_list = list%size
+end function length_list
+
+! insert_string --
+!     Insert a new string into the list
+!
+! Arguments:
+!     list                   The list of strings where the new string should be inserted
+!     idx                    Index after which to insert the string
+!     string                 The string in question
+!
+subroutine insert_string( list, idx, string )
+    class(t_stringlist), intent(inout)        :: list
+    integer, intent(in)                       :: idx
+    character(len=*), intent(in)              :: string
+
+    integer                                   :: i
+    integer                                   :: idxnew
+    type(t_string)                            :: new_element
+    type(t_string), dimension(:), allocatable :: empty_strings
+
+    !
+    ! Initialise the list if necessary
+    !
+    if ( .not. allocated(list%string) ) then
+        allocate( list%string(initial_size) )
+        do i = 1,size(list%string)
+            list%string(i)%value = ''
+        enddo
+    endif
+
+    !
+    ! Check the index:
+    ! - if the index is list_head, then shift the entire array
+    ! - if the index is list_end or negative in general, determine the absolute index
+    ! - if the index is large than the registered size, expand the list
+    ! - shift everything after the absolute index
+    !
+    new_element%value = string
+
+    if ( idx == list_head ) then
+        list%size   = list%size + 1
+        list%string = [new_element, list%string]
+    else
+        idxnew = idx
+        if ( idx <= list_end ) then
+            idxnew = list%size - (abs(idx) - 1)
+            if ( idxnew <= 0 ) then
+                idxnew = 0
+            endif
+        endif
+
+        if ( idxnew <= size(list%string) ) then
+            list%size   = max( idxnew+1, list%size + 1 )
+            list%string = [list%string(1:idxnew), new_element, list%string(idxnew+1:)]
+        else
+            allocate( empty_strings(idxnew-size(list%string)) )
+            do i = 1,size(empty_strings)
+                empty_strings(i)%value = ''
+            enddo
+            list%string            = [list%string, empty_strings, new_element]
+            list%size              = idxnew + 1
+        endif
+    endif
+end subroutine insert_string
+
+! get_string --
+!     Get the string at a particular index
+!
+! Arguments:
+!     list                   The list of strings to retrieve the string from
+!     idx                    Index after which to insert the string
+!
+function get_string( list, idx )
+    class(t_stringlist), intent(inout) :: list
+    integer, intent(in)                :: idx
+    character(len=:), allocatable      :: get_string
+
+    integer                            :: idxnew
+    type(t_string)                     :: new_element
+
+    !
+    ! Examine the actual index:
+    ! - if the index is larger than the size, return an empty string
+    ! - if the index is equal to list_head, interpret it as index 1
+    ! - if the index is negative, calculate the absolute index
+    !
+    if ( idx > list%size ) then
+        get_string = ''
+    else
+        idxnew = idx
+        if ( idx == list_head ) then
+            idxnew = 1
+        elseif ( idx <= list_end ) then
+            idxnew = list%size - (abs(idx) - 1)
+        endif
+
+        if ( idxnew < 1 ) then
+            get_string = ''
+        else
+            get_string = list%string(idxnew)%value
+        endif
+    endif
+end function get_string
+
+end module stdlib_stringlists
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index c3b09e34d..701db67e2 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -15,6 +15,7 @@ add_subdirectory(optval)
 add_subdirectory(stats)
 add_subdirectory(system)
 add_subdirectory(quadrature)
+add_subdirectory(stringlist)
 
 ADDTEST(always_skip)
 set_tests_properties(always_skip PROPERTIES SKIP_RETURN_CODE 77)
diff --git a/src/tests/stringlist/CMakeLists.txt b/src/tests/stringlist/CMakeLists.txt
new file mode 100644
index 000000000..a591ef543
--- /dev/null
+++ b/src/tests/stringlist/CMakeLists.txt
@@ -0,0 +1 @@
+ADDTEST(stringlist)
diff --git a/src/tests/stringlist/test_stringlist.f90 b/src/tests/stringlist/test_stringlist.f90
new file mode 100644
index 000000000..715661096
--- /dev/null
+++ b/src/tests/stringlist/test_stringlist.f90
@@ -0,0 +1,63 @@
+! test_stringlist.f90 --
+!     Test program for the stdlib_stringlist module
+!
+!     Straightforward test program: try to cover "all" special cases
+!
+program test_stringlists
+    use stdlib_stringlists
+
+    implicit none
+
+    type(t_stringlist) :: list
+    integer            :: i
+
+    !
+    ! The straightforward cases: insert a few strings and retrieve them
+    !
+    call list%insert( list_head, "C" )
+    call list%insert( list_head, "B" )
+    call list%insert( list_head, "A" )
+
+    !
+    ! Now insert a string at an arbitrary location - it will be at position 11
+    !
+    call list%insert( 10, "Number 10" )
+
+    !
+    ! Append a string to the end of the list - that will be position 12
+    !
+    call list%insert( list_end, "Appended" )
+
+    !
+    ! Insert a string near the end of the list - that will be position 12-2+1
+    !
+    call list%insert( list_end-2, "Appended 2" )
+
+    !
+    ! Insert a string beyond the beginning - that should appear at the start
+    !
+    call list%insert( list_end-30, "Effectively prepended" )
+
+    !
+    ! Print the result
+    !
+    do i = 1,list%length()
+        write(*,*) i, '>', list%get(i), '<'
+    enddo
+
+    !
+    ! And select two elements at the end
+    !
+    write(*,*) 'end-1' , '>', list%get(list_end-1), '<'
+    write(*,*) 'end'   , '>', list%get(list_end), '<'
+    write(*,*) 'end-30', '>', list%get(list_end-30), '<'
+
+    !
+    ! Okay, finally set an element really far and read it back
+    !
+    call list%insert( 40, "Really far away" )
+    write(*,*) '40', '>', list%get(40), '<'
+    write(*,*) '41', '>', list%get(41), '<'
+    write(*,*) list%length()
+
+end program test_stringlists

From 11650c3eb5b18ad1e17e86eaa5f11c9415f967b8 Mon Sep 17 00:00:00 2001
From: arjenmarkus <arjen.markus895@gmail.com>
Date: Sun, 20 Dec 2020 14:49:35 +0100
Subject: [PATCH 2/3] Add sorting function

Added a function to sort the list of strings (implementation inspired by the alternative set-up of a long string with indices)
---
 src/stdlib_stringlist.f90                | 103 ++++++++++++++++++++++-
 src/tests/stringlist/test_stringlist.f90 |   9 ++
 2 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/src/stdlib_stringlist.f90 b/src/stdlib_stringlist.f90
index 08600aa1f..650b581ea 100644
--- a/src/stdlib_stringlist.f90
+++ b/src/stdlib_stringlist.f90
@@ -27,10 +27,48 @@ module stdlib_stringlists
         procedure :: insert => insert_string
         procedure :: get    => get_string
         procedure :: length => length_list
+        procedure :: sort   => sort_list
     end type t_stringlist
 
+
+    interface operator(<)
+        module procedure string_lower
+    end interface
+
+    interface operator(>)
+        module procedure string_greater
+    end interface
+
+    interface operator(==)
+        module procedure string_equal
+    end interface
+
 contains
 
+! compare t_string derived types
+!     Required by sorting functions
+!
+elemental logical function string_lower( string1, string2 )
+     type(t_string), intent(in) :: string1
+     type(t_string), intent(in) :: string2
+
+     string_lower = string1%value < string2%value
+end function string_lower
+
+elemental logical function string_greater( string1, string2 )
+     type(t_string), intent(in) :: string1
+     type(t_string), intent(in) :: string2
+
+     string_greater = string1%value > string2%value
+end function string_greater
+
+elemental logical function string_equal( string1, string2 )
+     type(t_string), intent(in) :: string1
+     type(t_string), intent(in) :: string2
+
+     string_equal = string1%value == string2%value
+end function string_equal
+
 ! length_list --
 !     Return the size (length) of the list
 !
@@ -119,7 +157,6 @@ function get_string( list, idx )
     character(len=:), allocatable      :: get_string
 
     integer                            :: idxnew
-    type(t_string)                     :: new_element
 
     !
     ! Examine the actual index:
@@ -145,4 +182,68 @@ function get_string( list, idx )
     endif
 end function get_string
 
+! sort_list --
+!     Sort the list and return the result as a new list
+!
+! Arguments:
+!     list                   The list of strings to retrieve the string from
+!     ascending              Whether to sort as ascending (true) or not (false)
+!
+function sort_list( list, ascending )
+    class(t_stringlist), intent(in)    :: list
+    logical, intent(in)                :: ascending
+
+    integer                            :: i
+    integer, dimension(:), allocatable :: idx
+    class(t_stringlist), allocatable   :: sort_list
+
+    !
+    ! Allocate and fill the index array, then sort the indices
+    ! based on the strings
+    !
+    idx = [ (i ,i=1,list%size) ]
+
+    if ( ascending ) then
+        idx = sort_ascending( idx )
+    else
+        idx = sort_descending( idx )
+    endif
+
+    allocate( sort_list )
+    allocate( sort_list%string(list%size) )
+
+    do i = 1,list%size
+        sort_list%string(i) = list%string(idx(i))
+    enddo
+    sort_list%size = list%size
+
+contains
+recursive function sort_ascending( idx ) result(idxnew)
+    integer, dimension(:) :: idx
+    integer, dimension(size(idx)) :: idxnew
+
+    if ( size(idx) > 1 ) then
+        idxnew = [ sort_ascending( pack( idx, list%string(idx) < list%string(idx(1)) ) ), &
+                   pack( idx, list%string(idx) == list%string(idx(1)) )                 , &
+                   sort_ascending( pack( idx, list%string(idx) > list%string(idx(1)) ) ) ]
+    else
+        idxnew = idx
+    endif
+end function sort_ascending
+
+recursive function sort_descending( idx ) result(idxnew)
+    integer, dimension(:) :: idx
+    integer, dimension(size(idx)) :: idxnew
+
+    if ( size(idx) > 1 ) then
+        idxnew = [ sort_descending( pack( idx, list%string(idx) > list%string(idx(1)) ) ), &
+                   pack( idx, list%string(idx) == list%string(idx(1)) )                  , &
+                   sort_descending( pack( idx, list%string(idx) < list%string(idx(1)) ) ) ]
+    else
+        idxnew = idx
+    endif
+end function sort_descending
+
+end function sort_list
+
 end module stdlib_stringlists
diff --git a/src/tests/stringlist/test_stringlist.f90 b/src/tests/stringlist/test_stringlist.f90
index 715661096..af74c9016 100644
--- a/src/tests/stringlist/test_stringlist.f90
+++ b/src/tests/stringlist/test_stringlist.f90
@@ -9,6 +9,7 @@ program test_stringlists
     implicit none
 
     type(t_stringlist) :: list
+    type(t_stringlist) :: list_sorted
     integer            :: i
 
     !
@@ -60,4 +61,12 @@ program test_stringlists
     write(*,*) '41', '>', list%get(41), '<'
     write(*,*) list%length()
 
+    !
+    ! Sort the list and print the result
+    !
+    list_sorted = list%sort( .false. )
+    do i = 1,list_sorted%length()
+        write(*,*) i, '>', list_sorted%get(i), '<'
+    enddo
+
 end program test_stringlists

From ed8e43010582e278a23113ad730b26885706ee76 Mon Sep 17 00:00:00 2001
From: arjenmarkus <arjen.markus895@gmail.com>
Date: Thu, 31 Dec 2020 09:57:46 +0100
Subject: [PATCH 3/3] Extend the implementation of stdlib_stringlist

Add documentation of the interface for stdlib_stringlist.
Add several subroutines and functions. Note, however, that it is not quite complete (see the head of stdlib_stringliust.f90 for some details)
---
 doc/specs/index.md                       |   1 +
 doc/specs/stdlib_stringlist.md           | 467 +++++++++++++++++++++++
 src/stdlib_stringlist.f90                | 306 +++++++++++++--
 src/tests/stringlist/test_stringlist.f90 |  53 ++-
 4 files changed, 788 insertions(+), 39 deletions(-)
 create mode 100644 doc/specs/stdlib_stringlist.md

diff --git a/doc/specs/index.md b/doc/specs/index.md
index 6ea78b52e..0261e99d9 100644
--- a/doc/specs/index.md
+++ b/doc/specs/index.md
@@ -19,6 +19,7 @@ This is and index/directory of the specifications (specs) for each new module/fe
  - [optval](./stdlib_optval.html) - Fallback value for optional arguments
  - [quadrature](./stdlib_quadrature.html) - Numerical integration
  - [stats](./stdlib_stats.html) - Descriptive Statistics
+ - [stringlist](./stdlib_stringlist.html) - Handling lists of strings
 
 ## Missing specs
 
diff --git a/doc/specs/stdlib_stringlist.md b/doc/specs/stdlib_stringlist.md
new file mode 100644
index 000000000..41e081ca2
--- /dev/null
+++ b/doc/specs/stdlib_stringlist.md
@@ -0,0 +1,467 @@
+---
+title: stringlist
+---
+# Lists of strings
+
+[TOC]
+
+## Introduction
+
+Fortran has supported variable-length strings since the 2003 standard,
+but it does not have a native type to handle collections of strings of
+different lengths. Such collections are quite useful though and the
+language allows us to define a derived type that can handle such
+collections.
+
+The `stdlib_stringlist` module defines a derived type that is capable of
+storing a list of strings and of manipulating them.
+
+Methods include:
+
+* inserting strings at a given position
+* replacing strings at a given position
+* deleting a single string or a range of strings
+* retrieving a string or a range of strings at a given position
+* finding the position of a particular string or a string which contains some substring
+* sorting the list
+
+## Positions in a list of strings
+
+The module implements what are effectively infinitely long lists: a position is
+represented as a positive integer, but there is no "out-of-bound" index. That is,
+the following piece of code will simply work:
+
+```fortran
+type(stringlist_type) :: list
+
+! Add two strings ...
+call list%insert( list_head, "The first string" )
+call list%insert( 20, "The last string" )
+
+write(*,*) 'The last:    ', list%get(list_end)
+write(*,*) 'Beyond that: ', list%get(30)
+```
+The special position `list_head` represents the position *before* the first element. Likewise,
+the special position `list_end` represents the position of the *last* element. You can
+use these positions to insert a string before the first string that is already in the
+list or to insert after the last string that has been inserted.
+
+If you specify a position beyond the last, the `list%get()` method simply returns an empty
+string.
+
+You can also specify *negative* positions, but they are interpreted as going back from the
+last inserted string. If you need the last but one string, you can do so innthis way:
+
+```fortran
+write(*,*) 'The last but onw: ', list%get(list_end-1)
+```
+
+So, it is possible to do simple arithmetic.
+
+*Note:* this does not work for the head of the list.
+
+## The derived type: stringlist_type
+
+### Status
+
+Experimental
+
+### Description
+
+The type holds a small number of components and gives access to a number of procedures,
+some of which are implemented as subroutines, others as functions or as operations.
+
+
+### Public `stringlist_type` methods
+
+The following methods are defined:
+
+Method               | Class      | Description
+---------------------|------------|------------
+[`delete`](./stdlib_stringlist.html#delete-delete_one_or_more_strings)                                                 | Subroutine | Delete one or more strings from the list
+[`destroy`](./stdlib_stringlist.html#destroy_destroy_all_strings_in_the_list)                                          | Subroutine | Destroy the contents of the list
+[`get`](./stdlib_stringlist.html#get-get_a_single_string_from_a_list)                                                  | Function   | Get a string from a particular position
+[`index`](./stdlib_stringlist.html#index-find_the_index_of_a_particular_string_in_the_list)                            | Function   | Find the index of a string in a list
+[`index_sub`](./stdlib_stringlist.html#index_sub-find_the_index_of_a_particular_string_containing_the_given_substring) | Function   | Find the index of a string containing a partilcar substring
+[`insert`](./stdlib_stringlist.html#insert-insert_one_or_more_strings_after_a_given_position)                          | Subroutine | Insert a string or a list after a given position
+[`length`](./stdlib_stringlist.html#length-return_the_length_of_the_list)                                              | Function   | Return the index of the last set position
+[`range`](./stdlib_stringlist.html#range-retrieve_a_range_of_string_from_the_list)                                     | Function   | Retrieve a range of strings from the list
+[`replace`](./stdlib_stringlist.html#replace-replace_one_or_more_strings_between_two_given_positions)                  | Subroutine | Replace one or more stringa between two positions
+[`sort`](./stdlib_stringlist.html#sort-return_a_sorted_list)                                                           | Function   | Sort the list and return the result as a new list
+[`=`](./stdlib_stringlist.html#assign-copy_the_contents_of_a_list)                                                     | Assignment | Copy a list
+[`//`](./stdlib_stringlist.html#//-concatenate_a_list_with_one_or_more_strings)                                        | Operation  | Concatenate a list with a string or concatenate two lists
+
+
+## Details of the methods
+
+### `delete` - delete one or more strings
+
+#### Status
+
+Experimental
+
+#### Description
+
+Delete one or more strings from the list via a given position or positions.
+
+#### Syntax
+
+`call list % [[stringlist_type(type):delete(bound)]]( first [, last] )`
+
+#### Class
+
+Subroutine
+
+#### Arguments
+
+`list`: the stringlist variable from which to delete one or more strings
+
+`first`: the index of the first string to be deleted
+
+`last` (optional): the index of the last string to be deleted. If left out, only one string is deleted.
+If the value is lower than that of `first`, the range is considered to be empty and nothing is deleted.
+
+
+### `destroy` - destroy all strings in the list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Destroy the entire contents of the list. As the variable holding the list is simply a derived type, the variable
+itself is not destroyed.
+
+#### Syntax
+
+`call list % [[stringlist_type(type):destroy(bound)]]`
+
+#### Class
+
+Subroutine
+
+#### Arguments
+
+`list`: the stringlist variable from which to delete all strings
+
+
+### `get` - get a single string from the list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Get the string at the given position.
+
+#### Syntax
+
+`string = list % [[stringlist_type(type):get(bound) ( idx )]]`
+
+#### Class
+
+Function
+
+#### Arguments
+
+`list`: the stringlist variable to retrieve a string from
+
+`idx`: the index of the string to be retrieved (see [`the section on positions`](./stdlib_stringlist.html#position-in-a-list-of-strings)
+
+#### Result value
+
+A copy of the string stored at the indicated position.
+
+
+### `index` - find the index of a particular string in the list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Get the position of the first stored string that matches the given string, if `back` is not present or false. If `back` is
+false, return the position of the last stored string that matches. Note that trailing blanks are ignored.
+
+#### Syntax
+
+`idx = list % [[stringlist_type(type):index(bound) ( string, back )]]`
+
+#### Class
+
+Function
+
+#### Arguments
+
+`list`: the stringlist variable to retrieve a string from
+
+`string`: the string to be found in the list
+
+`back` (optional): logical argument indicating the first occurrence should be returned (`false`) or the last (`true`)
+
+#### Result value
+
+The result is either the index of the string in the list or -1 if the string was not found
+
+#### Example
+
+Because trailing blanks are ignored, the following calls will give the same result:
+
+```fortran
+    write(*,*) list%index( 'A' )
+    write(*,*) list%index( 'A    ' )
+```
+
+
+### `index_sub` - find the index of a string containing the given substring in the list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Get the position of the first stored string that contains the given substring, if `back` is not present or false. If `back` is
+false, return the position of the last stored string that contains it.
+
+#### Syntax
+
+`idx = list % [[stringlist_type(type):index_sub(bound) ( substring, back )]]`
+
+#### Class
+
+Function
+
+#### Arguments
+
+`list`: the stringlist variable to retrieve a string from
+
+`substring`: the substring in question
+
+`back` (optional): logical argument indicating the first occurrence should be returned (`false`) or the last (`true`)
+
+#### Result value
+
+The result is either the index of the string in the list or -1 if the string was not found
+
+
+### `insert` - insert one or more strings after a given position
+
+#### Status
+
+Experimental
+
+#### Description
+
+Insert one or more strings after a given position. The position may be anything as explained in the section on positions.
+A single string may be inserted, another list of strings or a plain array of strings. In all cases trailing blanks, if any,
+are retained.
+
+#### Syntax
+
+`idx = list % [[stringlist_type(type):insert(bound) ( idx, string )]]`
+
+#### Class
+
+Subroutine
+
+#### Arguments
+
+`list`: the stringlist variable to insert the string(s) into
+
+`idx`: the position after which the strings should be inserted
+
+`string`: the string to be inserted, a list of strings or a plain array of strings
+
+
+### `length` - return the length of the list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Return the length of the list, defined as the highest index for which a string has been assigned. You can place strings
+in any position without needing to fill in the intervening positions.
+
+#### Syntax
+
+`length = list % [[stringlist_type(type):length(bound) ()]]`
+
+#### Class
+
+Function
+
+#### Arguments
+
+`list`: the stringlist variable to retrieve the length from
+
+#### Result value
+
+Returns the highest index of a string that has been set.
+
+
+
+### `range` - retrieve a range of strings from the list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Retrieve the strings occurring between the given positions as a new list.
+
+#### Syntax
+
+`rangelist = list % [[stringlist_type(type):range(bound) ( first, last )]]`
+
+#### Class
+
+Function
+
+#### Arguments
+
+`list`: the stringlist variable to insert the string(s) into
+
+`first`: the position of the first string to be retrieved
+
+`last`: the position of the last string to be retrieved
+
+#### Result value
+
+The result is a new list containing all the strings that appear from the first to the last position, inclusively.
+
+
+
+### `replace` - replace one or more strings between two given positions
+
+#### Status
+
+Experimental
+
+#### Description
+
+Replace one or more strings between two given positions. The new strings may be given as a single string, a list of
+strings or a plain array.
+
+#### Syntax
+
+`call list % [[stringlist_type(type):replace(bound) ( first, last, string )]]`
+
+#### Class
+
+Subroutine
+
+#### Arguments
+
+`list`: the stringlist variable to replace the string(s) in
+
+
+`first`: the position of the first string to be retrieved
+
+`last`: the position of the last string to be retrieved
+
+`string`: the string to be inserted, a list of strings or a plain array of strings
+
+
+
+### `sort` - return a sorted list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Create a new list consisting of the sorted strings of the given list. The strings are sorted according to ASCII, either
+in ascending order or descending order.
+
+#### Syntax
+
+`sortedlist = list % [[stringlist_type(type):sort(bound) ( ascending )]]`
+
+#### Class
+
+Subroutine
+
+#### Arguments
+
+`list`: the stringlist variable of which the contents should be copied
+
+`ascending` (optional): if not present or true, sort the list in ascending order, otherwise descending
+
+#### Result value
+
+The contents of the given list is sorted and then stored in the new list.
+
+
+### `=` - copy the contents of a list
+
+#### Status
+
+Experimental
+
+#### Description
+
+Copy an existing list to a new one. The original list remains unchanged.
+
+#### Syntax
+
+`copylist = list`
+
+#### Class
+
+Assignment
+
+#### Operands
+
+`list`: the stringlist variable to be copied
+
+
+
+### `//` - concatenate a list with one or more strings
+
+#### Status
+
+Experimental
+
+#### Description
+
+Concatenate a list with a string, a list of strings or a plain array
+
+#### Syntax
+
+`concatenatedlist = list // string`
+
+`concatenatedlist = string // list`
+
+#### Class
+
+Assignment
+
+#### Operands
+
+`list`: the stringlist variable to be concatenated
+
+`string`: the string to be concatenated, a list of strings or a plain array of strings
+
+#### Result value
+
+A stringlist that contains the concatenation of the two operands.
+
+
+
+## TODO
+
+Additional methods:
+
+filter
+
+map
+
+Suggestions from the discussion
diff --git a/src/stdlib_stringlist.f90 b/src/stdlib_stringlist.f90
index 650b581ea..60fc9cdba 100644
--- a/src/stdlib_stringlist.f90
+++ b/src/stdlib_stringlist.f90
@@ -1,35 +1,48 @@
-! stringlist.f90 --
+! stdlib_stringlist.f90 --
 !     Module for storing and manipulating lists of strings
 !     The strings may have arbitrary lengths, not necessarily the same
 !
 !     Note: very preliminary
 !
-module stdlib_stringlists
+!     TODO:
+!     insert( list_end, ... ) in an empty list?
+!
+!     Not implemented yet:
+!     insert a list or an array of character strings
+!     replace a string, list or an array of character strings
+!     concatenate a list with another list or an array
+!
+module stdlib_stringlist
     implicit none
 
     private
     integer, parameter, public :: list_head = 0
     integer, parameter, public :: list_end  = -1
-    public :: t_stringlist
+    public :: stringlist_type
+    public :: operator(//)
 
 
     integer, parameter :: initial_size  = 20
 
-    type t_string
+    type string_type
         character(len=:), allocatable :: value
-    end type t_string
+    end type string_type
 
-    type t_stringlist
+    type stringlist_type
         private
         integer :: size = 0
-        type(t_string), dimension(:), allocatable :: string
+        type(string_type), dimension(:), allocatable :: string
     contains
-        procedure :: insert => insert_string
-        procedure :: get    => get_string
-        procedure :: length => length_list
-        procedure :: sort   => sort_list
-    end type t_stringlist
-
+        procedure :: destroy   => destroy_list
+        procedure :: insert    => insert_string
+        procedure :: get       => get_string
+        procedure :: length    => length_list
+        procedure :: sort      => sort_list
+        procedure :: index     => index_of_string
+        procedure :: index_sub => index_of_substring
+        procedure :: delete    => delete_strings
+        procedure :: range     => range_list
+    end type stringlist_type
 
     interface operator(<)
         module procedure string_lower
@@ -43,32 +56,69 @@ module stdlib_stringlists
         module procedure string_equal
     end interface
 
+    interface operator(//)
+        module procedure append_string
+        module procedure prepend_string
+    end interface
 contains
 
-! compare t_string derived types
+! compare string_type derived types
 !     Required by sorting functions
 !
 elemental logical function string_lower( string1, string2 )
-     type(t_string), intent(in) :: string1
-     type(t_string), intent(in) :: string2
+     type(string_type), intent(in) :: string1
+     type(string_type), intent(in) :: string2
 
      string_lower = string1%value < string2%value
 end function string_lower
 
 elemental logical function string_greater( string1, string2 )
-     type(t_string), intent(in) :: string1
-     type(t_string), intent(in) :: string2
+     type(string_type), intent(in) :: string1
+     type(string_type), intent(in) :: string2
 
      string_greater = string1%value > string2%value
 end function string_greater
 
 elemental logical function string_equal( string1, string2 )
-     type(t_string), intent(in) :: string1
-     type(t_string), intent(in) :: string2
+     type(string_type), intent(in) :: string1
+     type(string_type), intent(in) :: string2
 
      string_equal = string1%value == string2%value
 end function string_equal
 
+function append_string( list, string )
+     type(stringlist_type), intent(in) :: list
+     character(len=*), intent(in)      :: string
+     type(stringlist_type)             :: append_string
+
+     append_string = list
+     call append_string%insert( list_end, string )
+end function append_string
+
+function prepend_string( string, list )
+     character(len=*), intent(in)      :: string
+     type(stringlist_type), intent(in) :: list
+     type(stringlist_type)             :: prepend_string
+
+     prepend_string = list
+     call prepend_string%insert( list_head, string )
+end function prepend_string
+
+! TODO: concatenate two string lists
+
+! destroy_list --
+!     Destroy the contetns of the list
+!
+! Arguments:
+!     list                   The list of strings in question
+!
+subroutine destroy_list( list )
+    class(stringlist_type), intent(inout) :: list
+
+    list%size = 0
+    deallocate( list%string )
+end subroutine destroy_list
+
 ! length_list --
 !     Return the size (length) of the list
 !
@@ -76,12 +126,33 @@ end function string_equal
 !     list                   The list of strings to retrieve the string from
 !
 integer function length_list( list )
-    class(t_stringlist), intent(in) :: list
+    class(stringlist_type), intent(in) :: list
 
     length_list = list%size
 end function length_list
 
-! insert_string --
+! abspos --
+!     Return the absolute position in the list
+!
+! Arguments:
+!     list                   The list of strings to retrieve the string from
+!     pos                    Possibly relative position
+!
+! Note:
+!     This is an auxiliary function only intended for internal use
+!
+integer function abspos( list, pos )
+    class(stringlist_type), intent(in) :: list
+    integer, intent(in)                :: pos
+
+    if ( pos >= list_head .or. pos <= list%size ) then
+        abspos = pos
+    elseif ( pos <= list_end ) then
+         abspos = list%size - (abs(pos) - 1)
+    endif
+end function abspos
+
+! inserstring_type --
 !     Insert a new string into the list
 !
 ! Arguments:
@@ -90,14 +161,14 @@ end function length_list
 !     string                 The string in question
 !
 subroutine insert_string( list, idx, string )
-    class(t_stringlist), intent(inout)        :: list
+    class(stringlist_type), intent(inout)        :: list
     integer, intent(in)                       :: idx
     character(len=*), intent(in)              :: string
 
     integer                                   :: i
     integer                                   :: idxnew
-    type(t_string)                            :: new_element
-    type(t_string), dimension(:), allocatable :: empty_strings
+    type(string_type)                            :: new_element
+    type(string_type), dimension(:), allocatable :: empty_strings
 
     !
     ! Initialise the list if necessary
@@ -144,7 +215,7 @@ subroutine insert_string( list, idx, string )
     endif
 end subroutine insert_string
 
-! get_string --
+! get_string_type --
 !     Get the string at a particular index
 !
 ! Arguments:
@@ -152,7 +223,7 @@ end subroutine insert_string
 !     idx                    Index after which to insert the string
 !
 function get_string( list, idx )
-    class(t_stringlist), intent(inout) :: list
+    class(stringlist_type), intent(inout) :: list
     integer, intent(in)                :: idx
     character(len=:), allocatable      :: get_string
 
@@ -190,12 +261,13 @@ end function get_string
 !     ascending              Whether to sort as ascending (true) or not (false)
 !
 function sort_list( list, ascending )
-    class(t_stringlist), intent(in)    :: list
-    logical, intent(in)                :: ascending
+    class(stringlist_type), intent(in)  :: list
+    logical, intent(in), optional       :: ascending
 
-    integer                            :: i
-    integer, dimension(:), allocatable :: idx
-    class(t_stringlist), allocatable   :: sort_list
+    integer                             :: i
+    integer, dimension(:), allocatable  :: idx
+    class(stringlist_type), allocatable :: sort_list
+    logical                             :: ascending_order
 
     !
     ! Allocate and fill the index array, then sort the indices
@@ -203,7 +275,12 @@ function sort_list( list, ascending )
     !
     idx = [ (i ,i=1,list%size) ]
 
-    if ( ascending ) then
+    ascending_order = .true.
+    if ( present(ascending) ) then
+        ascending_order = ascending
+    endif
+
+    if ( ascending_order ) then
         idx = sort_ascending( idx )
     else
         idx = sort_descending( idx )
@@ -246,4 +323,165 @@ end function sort_descending
 
 end function sort_list
 
-end module stdlib_stringlists
+! index_of_string --
+!     Return the index in the list of a particular string
+!
+! Arguments:
+!     list                   The list of strings in which to search the string
+!     string                 The string to be found
+!     back                   Whether to search from the end (true) or not (false, default)
+!
+integer function index_of_string( list, string, back )
+    class(stringlist_type), intent(in)  :: list
+    character(len=*), intent(in)        :: string
+    logical, intent(in), optional       :: back
+
+    integer                             :: idx
+    integer                             :: i
+    logical                             :: start_backwards
+
+    start_backwards = .false.
+    if ( present(back) ) then
+        start_backwards = back
+    endif
+
+    idx = -1
+    if ( start_backwards) then
+        do i = list%size,1,-1
+            if ( list%string(i)%value == string ) then
+                idx = i
+                exit
+            endif
+        enddo
+    else
+        do i = 1,list%size
+            if ( list%string(i)%value == string ) then
+                idx = i
+                exit
+            endif
+        enddo
+    endif
+
+    index_of_string = idx
+end function index_of_string
+
+! index_of_substring --
+!     Return the index in the list of a string containing a particular substring
+!
+! Arguments:
+!     list                   The list of strings in which to search the string
+!     substring              The substring to be found
+!     back                   Whether to search from the end (true) or not (false, default)
+!
+integer function index_of_substring( list, substring, back )
+    class(stringlist_type), intent(in)  :: list
+    character(len=*), intent(in)        :: substring
+    logical, intent(in), optional       :: back
+
+    integer                             :: idx
+    integer                             :: i
+    logical                             :: start_backwards
+
+    start_backwards = .false.
+    if ( present(back) ) then
+        start_backwards = back
+    endif
+
+    idx = -1
+    if ( start_backwards) then
+        do i = list%size,1,-1
+            if ( index(list%string(i)%value, substring) > 0 ) then
+                idx = i
+                exit
+            endif
+        enddo
+    else
+        do i = 1,list%size
+            if ( index(list%string(i)%value, substring) > 0 ) then
+                idx = i
+                exit
+            endif
+        enddo
+    endif
+
+    index_of_substring = idx
+end function index_of_substring
+
+! delete_strings --
+!     Delete one or more strings from the list
+!
+! Arguments:
+!     list                   The list of strings in which to search the string
+!     first                  The position of the first string to be deleted
+!     last                   The position of the last string to be deleted
+!
+! Note:
+!     If the range defined by first and last has a zero length or first > last,
+!     then nothing happens.
+!
+subroutine delete_strings( list, first, last )
+    class(stringlist_type), intent(inout) :: list
+    integer, intent(in)                   :: first
+    integer, intent(in)                   :: last
+
+    integer                               :: firstpos
+    integer                               :: lastpos
+    integer                               :: i
+    integer                               :: j
+
+    firstpos = abspos( list, first )
+    lastpos  = min( abspos( list, last ), list%size )
+
+    if ( firstpos > lastpos ) then
+        return
+    else
+        do i = lastpos+1,list%size
+            j = firstpos + i - lastpos - 1
+            call move_alloc( list%string(i)%value, list%string(j)%value )
+        enddo
+        do i = list%size - (lastpos-firstpos), list%size
+            list%string(i)%value = ''
+        enddo
+
+        list%size = list%size - (lastpos-firstpos + 1)
+    endif
+end subroutine delete_strings
+
+! range_list --
+!     Return a sublist given by the first and last position
+!
+! Arguments:
+!     list                   The list of strings in which to search the string
+!     first                  The position of the first string to be deleted
+!     last                   The position of the last string to be deleted
+!
+! Note:
+!     If the range defined by first and last has a zero length or first > last,
+!     then return an empty list
+!
+function range_list( list, first, last )
+    class(stringlist_type), intent(inout) :: list
+    integer, intent(in)                   :: first
+    integer, intent(in)                   :: last
+    class(stringlist_type), allocatable   :: range_list
+
+    integer                               :: firstpos
+    integer                               :: lastpos
+    integer                               :: i
+    integer                               :: j
+
+    allocate( range_list )
+
+    firstpos = abspos( list, first )
+    lastpos  = min( abspos( list, last ), list%size )
+
+    if ( firstpos > lastpos ) then
+        allocate( range_list%string(0) )
+        return
+    else
+        range_list%size   = lastpos - firstpos + 1
+        range_list%string = list%string(firstpos:lastpos)
+    endif
+end function range_list
+
+end module stdlib_stringlist
diff --git a/src/tests/stringlist/test_stringlist.f90 b/src/tests/stringlist/test_stringlist.f90
index af74c9016..0c82cd53a 100644
--- a/src/tests/stringlist/test_stringlist.f90
+++ b/src/tests/stringlist/test_stringlist.f90
@@ -1,16 +1,17 @@
 ! test_stringlist.f90 --
-!     Test program for the stdlib_stringlist module
 !
+!
+! test_stringlists
 !     Straightforward test program: try to cover "all" special cases
 !
 program test_stringlists
-    use stdlib_stringlists
+    use stdlib_stringlist
 
     implicit none
 
-    type(t_stringlist) :: list
-    type(t_stringlist) :: list_sorted
-    integer            :: i
+    type(stringlist_type) :: list, list_sorted, list_appended, list_to_search
+    type(stringlist_type) :: list_to_delete, sublist
+    integer               :: i
 
     !
     ! The straightforward cases: insert a few strings and retrieve them
@@ -61,12 +62,54 @@ program test_stringlists
     write(*,*) '41', '>', list%get(41), '<'
     write(*,*) list%length()
 
+    list_appended = list // "Another string"
+
+    write(*,*) 'List with appended string:'
+    do i = 1,list_appended%length()
+        write(*,*) i, '>', list_appended%get(i), '<'
+    enddo
+
     !
     ! Sort the list and print the result
     !
+    write(*,*) 'Sorted list:'
     list_sorted = list%sort( .false. )
     do i = 1,list_sorted%length()
         write(*,*) i, '>', list_sorted%get(i), '<'
     enddo
 
+    !
+    ! Searching strings
+    !
+    call list_to_search%insert( 0, 'A'   )
+    call list_to_search%insert( 1, 'A  ' )
+    call list_to_search%insert( 2, 'BB'  )
+    call list_to_search%insert( 3, 'CB'  )
+
+    write(*,*) 'First "A": ', list_to_search%index( 'A' )
+    write(*,*) 'Last "A":  ', list_to_search%index( 'A', back =.true. )
+    write(*,*) 'First "B": ', list_to_search%index_sub( 'B' )
+    write(*,*) 'Last "B":  ', list_to_search%index_sub( 'B', back =.true. )
+
+    !
+    ! Deleting a string
+    !
+    write(*,*) 'Deleting a string: '
+    list_to_delete = list_to_search
+    call list_to_delete%delete( 1, 2 )
+
+    do i = 1,list_to_delete%length()
+        write(*,*) '>', list_to_delete%get(i), '<'
+    enddo
+
+    !
+    ! Get a range
+    !
+    write(*,*) 'Get a sublist: '
+    sublist = list_to_search%range( 2, 3 )
+
+    do i = 1,sublist%length()
+        write(*,*) '>', sublist%get(i), '<'
+    enddo
+
 end program test_stringlists