1
1
! SPDX-Identifier: MIT
2
2
module test_string_functions
3
+ use , intrinsic :: iso_fortran_env, only : error_unit
3
4
use stdlib_error, only : check
4
5
use stdlib_string_type, only : string_type, assignment (= ), operator (==), &
5
6
to_lower, to_upper, to_title, to_sentence, reverse
6
7
use stdlib_strings, only: slice, find
8
+ use stdlib_optval, only: optval
9
+ use stdlib_ascii, only : to_string
7
10
implicit none
8
11
9
12
contains
@@ -53,57 +56,111 @@ subroutine test_reverse_string
53
56
54
57
end subroutine test_reverse_string
55
58
56
- subroutine test_slice
59
+ subroutine test_slice_string
57
60
type (string_type) :: test_string
58
- character (len= :), allocatable :: test_char
59
61
test_string = " abcdefghijklmnopqrstuvwxyz"
60
- test_char = " abcdefghijklmnopqrstuvwxyz"
61
-
62
- call check(slice(test_string, 2 , 16 , 3 ) == " behkn" , &
63
- ' function slice failed' , warn= .false. )
64
- call check(slice(test_char, first= 15 , stride=- 1 ) == " onmlkjihgfedcba" , &
65
- ' function slice failed' , warn= .false. )
66
- call check(slice(test_string, last= 22 , stride=- 1 ) == " zyxwv" , &
67
- ' function slice failed' , warn= .false. )
68
- call check(slice(test_char, 7 , 2 ) == " gfedcb" , &
69
- ' function slice failed' , warn= .false. )
70
- call check(slice(test_string, 7 , 2 , 1 ) == " " , &
71
- ' function slice failed' , warn= .false. )
72
- call check(slice(test_char, 2 , 6 , - 1 ) == " " , &
73
- ' function slice failed' , warn= .false. )
74
- call check(slice(test_string, stride=- 1 ) == " zyxwvutsrqponmlkjihgfedcba" , &
75
- ' function slice failed' , warn= .false. )
76
- call check(slice(test_string, 7 , 7 , - 4 ) == " g" , &
77
- ' function slice failed' , warn= .false. )
78
- call check(slice(test_char, 7 , 7 , 3 ) == " g" , &
79
- ' function slice failed' , warn= .false. )
80
- call check(slice(test_string, 7 , 7 , 3 ) == " g" , &
81
- ' function slice failed' , warn= .false. )
82
- call check(slice(test_char, 7 , - 10 ) == " gfedcba" , &
83
- ' function slice failed' , warn= .false. )
84
- call check(slice(test_string, 500 , 22 ) == " zyxwv" , &
85
- ' function slice failed' , warn= .false. )
86
- call check(slice(test_char, 50 , 27 ) == " " , &
87
- ' function slice failed' , warn= .false. )
88
- call check(slice(test_string, - 20 , - 200 ) == " " , &
89
- ' function slice failed' , warn= .false. )
90
- call check(slice(test_char, first= 0 , stride=- 1 ) == " " , &
91
- ' function slice failed' , warn= .false. )
92
- call check(slice(test_string, last= 27 , stride=- 2 ) == " " , &
93
- ' function slice failed' , warn= .false. )
94
- call check(slice(test_char, first= 27 , stride= 2 ) == " " , &
95
- ' function slice failed' , warn= .false. )
96
- call check(slice(test_string, - 500 , 500 ) == " abcdefghijklmnopqrstuvwxyz" , &
97
- ' function slice failed' , warn= .false. )
98
62
63
+ ! Only one argument is given
64
+ ! Valid
65
+ call check(slice(test_string, first= 10 ) == " jklmnopqrstuvwxyz" , &
66
+ " Slice, Valid arguments: first=10" ) ! last=+inf
67
+ call check(slice(test_string, last= 10 ) == " abcdefghij" , &
68
+ " Slice, Valid arguments: last=10" ) ! first=-inf
69
+ call check(slice(test_string, stride= 3 ) == " adgjmpsvy" , &
70
+ " Slice, Valid arguments: stride=3" ) ! first=-inf, last=+inf
71
+ call check(slice(test_string, stride=- 3 ) == " zwtqnkheb" , &
72
+ " Slice, Valid arguments: stride=-3" ) ! first=+inf, last=-inf
73
+
74
+ ! Invalid
75
+ call check(slice(test_string, first= 27 ) == " " , &
76
+ " Slice, Invalid arguments: first=27" ) ! last=+inf
77
+ call check(slice(test_string, first=- 10 ) == " abcdefghijklmnopqrstuvwxyz" , &
78
+ " Slice, Invalid arguments: first=-10" ) ! last=+inf
79
+ call check(slice(test_string, last=- 2 ) == " " , &
80
+ " Slice, Invalid arguments: last=-2" ) ! first=-inf
81
+ call check(slice(test_string, last= 30 ) == " abcdefghijklmnopqrstuvwxyz" , &
82
+ " Slice, Invalid arguments: last=30" ) ! first=-inf
83
+ call check(slice(test_string, stride= 0 ) == " abcdefghijklmnopqrstuvwxyz" , &
84
+ " Slice, Invalid arguments: stride=0" ) ! stride=1
85
+
86
+ ! Only two arguments are given
87
+ ! Valid
88
+ call check(slice(test_string, first= 10 , last= 20 ) == " jklmnopqrst" , &
89
+ " Slice, Valid arguments: first=10, last=20" )
90
+ call check(slice(test_string, first= 7 , last= 2 ) == " gfedcb" , &
91
+ " Slice, Valid arguments: first=7, last=2" ) ! stride=-1
92
+ call check(slice(test_string, first= 10 , stride=- 2 ) == " jhfdb" , &
93
+ " Slice, Valid arguments: first=10, stride=-2" ) ! last=-inf
94
+ call check(slice(test_string, last= 21 , stride=- 2 ) == " zxv" , &
95
+ " Slice, Valid arguments: last=21, stride=-2" ) ! first=+inf
96
+
97
+ ! Atleast one argument is invalid
98
+ call check(slice(test_string, first= 30 , last=- 3 ) == " zyxwvutsrqponmlkjihgfedcba" , &
99
+ " Slice, Invalid arguments: first=30, last=-3" )
100
+ call check(slice(test_string, first= 1 , last=- 20 ) == " a" , &
101
+ " Slice, Invalid arguments: first=1, last=-20" )
102
+ call check(slice(test_string, first= 7 , last=- 10 ) == " gfedcba" , &
103
+ " Slice, Invalid arguments: first=7, last=-10" )
104
+ call check(slice(test_string, first= 500 , last= 22 ) == " zyxwv" , &
105
+ " Slice, Invalid arguments: first=500, last=22" )
106
+ call check(slice(test_string, first= 50 , last= 27 ) == " " , &
107
+ " Slice, Invalid arguments: first=50, last=27" )
108
+ call check(slice(test_string, first=- 20 , last= 0 ) == " " , &
109
+ " Slice, Invalid arguments: first=-20, last=0" )
110
+ call check(slice(test_string, last=- 3 , stride=- 2 ) == " zxvtrpnljhfdb" , &
111
+ " Slice, Invalid arguments: last=-3, stride=-2" ) ! first=+inf
112
+ call check(slice(test_string, last= 10 , stride= 0 ) == " abcdefghij" , &
113
+ " Slice, Invalid arguments: last=10, stride=0" ) ! stride=1
114
+ call check(slice(test_string, first=- 2 , stride=- 2 ) == " " , &
115
+ " Slice, Invalid arguments: first=-2, stride=-2" ) ! last=-inf
116
+ call check(slice(test_string, first= 27 , stride= 2 ) == " " , &
117
+ " Slice, Invalid arguments: first=27, stride=2" ) ! last=+inf
118
+ call check(slice(test_string, last= 27 , stride=- 1 ) == " " , &
119
+ " Slice, Invalid arguments: last=27, stride=-1" ) ! first=+inf
120
+
121
+ ! All three arguments are given
122
+ ! Valid
123
+ call check(slice(test_string, first= 2 , last= 16 , stride= 3 ) == " behkn" , &
124
+ " Slice, Valid arguments: first=2, last=16, stride=3" )
125
+ call check(slice(test_string, first= 16 , last= 2 , stride=- 3 ) == " pmjgd" , &
126
+ " Slice, Valid arguments: first=16, last=2, stride=-3" )
127
+ call check(slice(test_string, first= 7 , last= 7 , stride=- 4 ) == " g" , &
128
+ " Slice, Valid arguments: first=7, last=7, stride=-4" )
129
+ call check(slice(test_string, first= 7 , last= 7 , stride= 3 ) == " g" , &
130
+ " Slice, Valid arguments: first=7, last=7, stride=3" )
131
+ call check(slice(test_string, first= 2 , last= 6 , stride=- 1 ) == " " , &
132
+ " Slice, Valid arguments: first=2, last=6, stride=-1" )
133
+ call check(slice(test_string, first= 20 , last= 10 , stride= 2 ) == " " , &
134
+ " Slice, Valid arguments: first=20, last=10, stride=2" )
135
+
136
+ ! Atleast one argument is invalid
137
+ call check(slice(test_string, first= 20 , last= 30 , stride= 2 ) == " tvxz" , &
138
+ " Slice, Invalid arguments: first=20, last=30, stride=2" )
139
+ call check(slice(test_string, first=- 20 , last= 30 , stride= 2 ) == " acegikmoqsuwy" , &
140
+ " Slice, Invalid arguments: first=-20, last=30, stride=2" )
141
+ call check(slice(test_string, first= 26 , last= 30 , stride= 1 ) == " z" , &
142
+ " Slice, Invalid arguments: first=26, last=30, stride=1" )
143
+ call check(slice(test_string, first= 1 , last=- 20 , stride=- 1 ) == " a" , &
144
+ " Slice, Invalid arguments: first=1, last=-20, stride=-1" )
145
+ call check(slice(test_string, first= 26 , last= 20 , stride= 1 ) == " " , &
146
+ " Slice, Invalid arguments: first=26, last=20, stride=1" )
147
+ call check(slice(test_string, first= 1 , last= 20 , stride=- 1 ) == " " , &
148
+ " Slice, Invalid arguments: first=1, last=20, stride=-1" )
149
+
99
150
test_string = " "
100
- test_char = " "
101
- call check(slice(test_string, 2 , 16 , 3 ) == " " , &
102
- ' function slice failed' , warn= .false. )
103
- call check(slice(test_char, 2 , 16 , 3 ) == " " , &
104
- ' function slice failed' , warn= .false. )
151
+ ! Empty string input
152
+ call check(slice(test_string, first=- 2 , last= 6 ) == " " , &
153
+ " Slice, Empty string: first=-2, last=6" )
154
+ call check(slice(test_string, first= 6 , last=- 2 ) == " " , &
155
+ " Slice, Empty string: first=6, last=-2" )
156
+ call check(slice(test_string, first=- 10 ) == " " , &
157
+ " Slice, Empty string: first=-10" ) ! last=+inf
158
+ call check(slice(test_string, last= 10 ) == " " , &
159
+ " Slice, Empty string: last=10" ) ! first=-inf
160
+ call check(slice(test_string) == " " , &
161
+ " Slice, Empty string: no arguments provided" )
105
162
106
- end subroutine test_slice
163
+ end subroutine test_slice_string
107
164
108
165
subroutine test_find
109
166
type (string_type) :: test_string, test_pattern
@@ -118,6 +175,130 @@ subroutine test_find
118
175
119
176
end subroutine test_find
120
177
178
+ subroutine test_slice_gen
179
+ character (len=* ), parameter :: test = &
180
+ & " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
181
+ integer :: i, j, k
182
+ integer , parameter :: offset = 3
183
+
184
+ do i = 1 - offset, len (test) + offset
185
+ call check_slicer(test, first= i)
186
+ end do
187
+
188
+ do i = 1 - offset, len (test) + offset
189
+ call check_slicer(test, last= i)
190
+ end do
191
+
192
+ do i = - len (test) - offset, len (test) + offset
193
+ call check_slicer(test, stride= i)
194
+ end do
195
+
196
+ do i = 1 - offset, len (test) + offset
197
+ do j = 1 - offset, len (test) + offset
198
+ call check_slicer(test, first= i, last= j)
199
+ end do
200
+ end do
201
+
202
+ do i = 1 - offset, len (test) + offset
203
+ do j = - len (test) - offset, len (test) + offset
204
+ call check_slicer(test, first= i, stride= j)
205
+ end do
206
+ end do
207
+
208
+ do i = 1 - offset, len (test) + offset
209
+ do j = - len (test) - offset, len (test) + offset
210
+ call check_slicer(test, last= i, stride= j)
211
+ end do
212
+ end do
213
+
214
+ do i = 1 - offset, len (test) + offset
215
+ do j = 1 - offset, len (test) + offset
216
+ do k = - len (test) - offset, len (test) + offset
217
+ call check_slicer(test, first= i, last= j, stride= k)
218
+ end do
219
+ end do
220
+ end do
221
+ end subroutine test_slice_gen
222
+
223
+ subroutine check_slicer (string , first , last , stride )
224
+ character (len=* ), intent (in ) :: string
225
+ integer , intent (in ), optional :: first
226
+ integer , intent (in ), optional :: last
227
+ integer , intent (in ), optional :: stride
228
+
229
+ character (len= :), allocatable :: actual, expected, message
230
+ logical :: stat
231
+
232
+ actual = slice(string, first, last, stride)
233
+ expected = reference_slice(string, first, last, stride)
234
+
235
+ stat = actual == expected
236
+
237
+ if (.not. stat) then
238
+ message = " For input '" // string// " '" // new_line(' a' )
239
+
240
+ if (present (first)) then
241
+ message = message // " first: " // to_string(first)// new_line(' a' )
242
+ end if
243
+ if (present (last)) then
244
+ message = message // " last: " // to_string(last)// new_line(' a' )
245
+ end if
246
+ if (present (stride)) then
247
+ message = message // " stride: " // to_string(stride)// new_line(' a' )
248
+ end if
249
+ message = message // " Expected: '" // expected// " ' but got '" // actual// " '"
250
+ end if
251
+ call check(stat, message)
252
+
253
+ end subroutine check_slicer
254
+
255
+ pure function reference_slice (string , first , last , stride ) result(sliced_string)
256
+ character (len=* ), intent (in ) :: string
257
+ integer , intent (in ), optional :: first
258
+ integer , intent (in ), optional :: last
259
+ integer , intent (in ), optional :: stride
260
+ character (len= :), allocatable :: sliced_string
261
+ character (len= 1 ), allocatable :: carray(:)
262
+
263
+ integer :: first_, last_, stride_
264
+
265
+ stride_ = 1
266
+ if (present (stride)) then
267
+ stride_ = merge (stride_, stride, stride == 0 )
268
+ else
269
+ if (present (first) .and. present (last)) then
270
+ if (last < first) stride_ = - 1
271
+ end if
272
+ end if
273
+
274
+ if (stride_ < 0 ) then
275
+ last_ = min (max (optval(last, 1 ), 1 ), len (string)+ 1 )
276
+ first_ = min (max (optval(first, len (string)), 0 ), len (string))
277
+ else
278
+ first_ = min (max (optval(first, 1 ), 1 ), len (string)+ 1 )
279
+ last_ = min (max (optval(last, len (string)), 0 ), len (string))
280
+ end if
281
+
282
+ carray = string_to_carray(string)
283
+ carray = carray(first_:last_:stride_)
284
+ sliced_string = carray_to_string(carray)
285
+
286
+ end function reference_slice
287
+
288
+ pure function string_to_carray (string ) result(carray)
289
+ character (len=* ), intent (in ) :: string
290
+ character (len= 1 ) :: carray(len (string))
291
+
292
+ carray = transfer (string, carray)
293
+ end function string_to_carray
294
+
295
+ pure function carray_to_string (carray ) result(string)
296
+ character (len= 1 ), intent (in ) :: carray(:)
297
+ character (len= size (carray)) :: string
298
+
299
+ string = transfer (carray, string)
300
+ end function carray_to_string
301
+
121
302
end module test_string_functions
122
303
123
304
@@ -130,7 +311,8 @@ program tester
130
311
call test_to_title_string
131
312
call test_to_sentence_string
132
313
call test_reverse_string
133
- call test_slice
314
+ call test_slice_string
134
315
call test_find
316
+ call test_slice_gen
135
317
136
318
end program tester
0 commit comments