Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5089a40

Browse files
authoredAug 22, 2021
Merge pull request #444 from St-Maxwell/zoziha/feature/format_string
Add format_string routine to format other types to strings
2 parents 590adbe + 3e31220 commit 5089a40

14 files changed

+458
-245
lines changed
 

‎doc/specs/stdlib_ascii.md

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -212,42 +212,4 @@ program demo_reverse
212212
implicit none
213213
print'(a)', reverse("Hello, World!") ! returns "!dlroW ,olleH"
214214
end program demo_reverse
215-
```
216-
217-
### `to_string`
218-
219-
#### Status
220-
221-
Experimental
222-
223-
#### Description
224-
225-
Create a character string representing the value of the provided variable.
226-
227-
#### Syntax
228-
229-
`res = [[stdlib_ascii(module):to_string(interface)]] (string)`
230-
231-
#### Class
232-
233-
Pure function.
234-
235-
#### Argument
236-
237-
`val`: shall be an intrinsic integer or logical type. It is an `intent(in)` argument.
238-
239-
#### Result value
240-
241-
The result is an intrinsic character type.
242-
243-
#### Example
244-
245-
```fortran
246-
program demo_string_value
247-
use stdlib_ascii, only : to_string
248-
implicit none
249-
print'(a)', to_string(-3) ! returns "-3"
250-
print'(a)', to_string(.true.) ! returns "T"
251-
print'(a)', to_string(42) ! returns "42"
252-
end program demo_string_value
253-
```
215+
```

‎doc/specs/stdlib_strings.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,3 +538,72 @@ program demo_count
538538
539539
end program demo_count
540540
```
541+
542+
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
543+
### `to_string`
544+
545+
#### Description
546+
547+
Format or transfer a `integer/real/complex/logical` scalar as a string.
548+
Input a wrong `format` that cause the internal-IO to fail, the result value is a string of `[*]`.
549+
550+
#### Syntax
551+
552+
`string = [[stdlib_strings(module):to_string(interface)]] (value [, format])`
553+
554+
#### Status
555+
556+
Experimental
557+
558+
#### Class
559+
560+
Pure function.
561+
562+
#### Argument
563+
564+
- `value`: Shall be an `integer/real/complex/logical` scalar.
565+
This is an `intent(in)` argument.
566+
- `format`: Shall be a `character(len=*)` scalar like `'(F6.2)'` or just `'F6.2'`.
567+
This is an `intent(in)` and `optional` argument.
568+
Contains the edit descriptor to format `value` into a string, for example `'(F6.2)'` or `'(f6.2)'`.
569+
`to_string` will automatically enclose `format` in a set of parentheses, so passing `F6.2` or `f6.2` as `format` is possible as well.
570+
571+
#### Result value
572+
573+
The result is an `allocatable` length `character` scalar with up to `128` cached `character` length.
574+
575+
#### Example
576+
577+
```fortran
578+
program demo_to_string
579+
use stdlib_strings, only: to_string
580+
581+
!> Example for `complex` type
582+
print *, to_string((1, 1)) !! "(1.00000000,1.00000000)"
583+
print *, to_string((1, 1), '(F6.2)') !! "( 1.00, 1.00)"
584+
print *, to_string((1000, 1), '(ES0.2)'), to_string((1000, 1), '(SP,F6.3)')
585+
!! "(1.00E+3,1.00)""(******,+1.000)"
586+
!! Too narrow formatter for real number
587+
!! Normal demonstration(`******` from Fortran Standard)
588+
589+
!> Example for `integer` type
590+
print *, to_string(-3) !! "-3"
591+
print *, to_string(42, '(I4)') !! " 42"
592+
print *, to_string(1, '(I0.4)'), to_string(2, '(B4)') !! "0001"" 10"
593+
594+
!> Example for `real` type
595+
print *, to_string(1.) !! "1.00000000"
596+
print *, to_string(1., '(F6.2)') !! " 1.00"
597+
print *, to_string(1., 'F6.2') !! " 1.00"
598+
print *, to_string(1., '(SP,ES9.2)'), to_string(1, '(F7.3)') !! "+1.00E+00""[*]"
599+
!! 1 wrong demonstration (`[*]` from `to_string`)
600+
601+
!> Example for `logical` type
602+
print *, to_string(.true.) !! "T"
603+
print *, to_string(.true., '(L2)') !! " T"
604+
print *, to_string(.true., 'L2') !! " T"
605+
print *, to_string(.false., '(I5)') !! "[*]"
606+
!! 1 wrong demonstrations(`[*]` from `to_string`)
607+
608+
end program demo_to_string
609+
```

‎src/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ set(fppFiles
3434
stdlib_math_logspace.fypp
3535
stdlib_math_arange.fypp
3636
stdlib_string_type.fypp
37+
stdlib_string_type_constructor.fypp
38+
stdlib_strings_to_string.fypp
39+
stdlib_strings.fypp
3740
)
3841

3942

@@ -52,7 +55,6 @@ set(SRC
5255
stdlib_error.f90
5356
stdlib_kinds.f90
5457
stdlib_logger.f90
55-
stdlib_strings.f90
5658
stdlib_system.F90
5759
stdlib_specialfunctions.f90
5860
stdlib_specialfunctions_legendre.f90

‎src/Makefile.manual

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SRCFYPP =\
1+
SRCFYPP = \
22
stdlib_ascii.fypp \
33
stdlib_bitsets_64.fypp \
44
stdlib_bitsets_large.fypp \
@@ -27,10 +27,13 @@ SRCFYPP =\
2727
stdlib_stats_moment_scalar.fypp \
2828
stdlib_stats_var.fypp \
2929
stdlib_math.fypp \
30-
stdlib_math_linspace.fypp \
31-
stdlib_math_logspace.fypp \
30+
stdlib_math_linspace.fypp \
31+
stdlib_math_logspace.fypp \
3232
stdlib_stats_distribution_PRNG.fypp \
33-
stdlib_string_type.fypp
33+
stdlib_string_type.fypp \
34+
stdlib_string_type_constructor.fypp \
35+
stdlib_strings.fypp \
36+
stdlib_strings_to_string.fypp
3437

3538
SRC = f18estop.f90 \
3639
stdlib_error.f90 \
@@ -40,7 +43,6 @@ SRC = f18estop.f90 \
4043
stdlib_kinds.f90 \
4144
stdlib_logger.f90 \
4245
stdlib_quadrature_gauss.f90 \
43-
stdlib_strings.f90 \
4446
$(SRCGEN)
4547

4648
LIB = libstdlib.a
@@ -77,85 +79,89 @@ stdlib_error.o: stdlib_optval.o
7779
stdlib_specialfunctions.o: stdlib_kinds.o
7880
stdlib_specialfunctions_legendre.o: stdlib_kinds.o stdlib_specialfunctions.o
7981
stdlib_io.o: \
80-
stdlib_ascii.o \
81-
stdlib_error.o \
82-
stdlib_optval.o \
83-
stdlib_kinds.o
82+
stdlib_ascii.o \
83+
stdlib_error.o \
84+
stdlib_optval.o \
85+
stdlib_kinds.o \
86+
stdlib_ascii.o
8487
stdlib_linalg.o: \
85-
stdlib_kinds.o
88+
stdlib_kinds.o
8689
stdlib_linalg_diag.o: \
87-
stdlib_linalg.o \
88-
stdlib_kinds.o
90+
stdlib_linalg.o \
91+
stdlib_kinds.o
8992
stdlib_logger.o: stdlib_ascii.o stdlib_optval.o
9093
stdlib_optval.o: stdlib_kinds.o
9194
stdlib_quadrature.o: stdlib_kinds.o
92-
9395
stdlib_quadrature_gauss.o: stdlib_kinds.o stdlib_quadrature.o
94-
9596
stdlib_quadrature_simps.o: \
96-
stdlib_quadrature.o \
97-
stdlib_error.o \
98-
stdlib_kinds.o
97+
stdlib_quadrature.o \
98+
stdlib_error.o \
99+
stdlib_kinds.o
99100
stdlib_quadrature_trapz.o: \
100-
stdlib_quadrature.o \
101-
stdlib_error.o \
102-
stdlib_kinds.o
101+
stdlib_quadrature.o \
102+
stdlib_error.o \
103+
stdlib_kinds.o
103104
stdlib_sorting.o: \
104-
stdlib_kinds.o \
105-
stdlib_string_type.o
105+
stdlib_kinds.o \
106+
stdlib_string_type.o
106107
stdlib_sorting_ord_sort.o: \
107-
stdlib_sorting.o
108+
stdlib_sorting.o
108109
stdlib_sorting_sort.o: \
109-
stdlib_sorting.o
110+
stdlib_sorting.o
110111
stdlib_sorting_sort_index.o: \
111-
stdlib_sorting.o
112+
stdlib_sorting.o
112113
stdlib_stats.o: \
113-
stdlib_kinds.o
114+
stdlib_kinds.o
114115
stdlib_stats_corr.o: \
115-
stdlib_optval.o \
116-
stdlib_kinds.o \
117-
stdlib_stats.o
116+
stdlib_optval.o \
117+
stdlib_kinds.o \
118+
stdlib_stats.o
118119
stdlib_stats_cov.o: \
119-
stdlib_optval.o \
120-
stdlib_kinds.o \
121-
stdlib_stats.o
120+
stdlib_optval.o \
121+
stdlib_kinds.o \
122+
stdlib_stats.o
122123
stdlib_stats_mean.o: \
123-
stdlib_optval.o \
124-
stdlib_kinds.o \
125-
stdlib_stats.o
124+
stdlib_optval.o \
125+
stdlib_kinds.o \
126+
stdlib_stats.o
126127
stdlib_stats_median.o: \
127-
stdlib_optval.o \
128-
stdlib_kinds.o \
129-
stdlib_sorting.o \
130-
stdlib_stats.o
128+
stdlib_optval.o \
129+
stdlib_kinds.o \
130+
stdlib_sorting.o \
131+
stdlib_stats.o
131132
stdlib_stats_moment.o: \
132-
stdlib_optval.o \
133-
stdlib_kinds.o \
134-
stdlib_stats.o
133+
stdlib_optval.o \
134+
stdlib_kinds.o \
135+
stdlib_stats.o
135136
stdlib_stats_moment_all.o: \
136-
stdlib_stats_moment.o
137+
stdlib_stats_moment.o
137138
stdlib_stats_moment_mask.o: \
138-
stdlib_stats_moment.o
139+
stdlib_stats_moment.o
139140
stdlib_stats_moment_scalar.o: \
140-
stdlib_stats_moment.o
141+
stdlib_stats_moment.o
141142
stdlib_stats_var.o: \
142-
stdlib_optval.o \
143-
stdlib_kinds.o \
144-
stdlib_stats.o
143+
stdlib_optval.o \
144+
stdlib_kinds.o \
145+
stdlib_stats.o
145146
stdlib_stats_distribution_PRNG.o: \
146-
stdlib_kinds.o \
147-
stdlib_error.o
147+
stdlib_kinds.o \
148+
stdlib_error.o
148149
stdlib_string_type.o: stdlib_ascii.o \
149150
stdlib_kinds.o
151+
stdlib_string_type_constructor.o: stdlib_string_type.o \
152+
stdlib_strings_to_string.o \
153+
stdlib_strings.o
150154
stdlib_strings.o: stdlib_ascii.o \
151155
stdlib_string_type.o \
152-
stdlib_optval.o
156+
stdlib_optval.o \
157+
stdlib_kinds.o
158+
stdlib_strings_to_string.o: stdlib_strings.o
153159
stdlib_math.o: stdlib_kinds.o \
154160
stdlib_optval.o
155161
stdlib_math_linspace.o: \
156-
stdlib_math.o
162+
stdlib_math.o
157163
stdlib_math_logspace.o: \
158-
stdlib_math_linspace.o
164+
stdlib_math_linspace.o
159165
stdlib_math_arange.o: \
160166
stdlib_math.o
161167
stdlib_linalg_outer_product.o: stdlib_linalg.o

‎src/stdlib_ascii.fypp

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,6 @@ module stdlib_ascii
2020

2121
! Character conversion functions
2222
public :: to_lower, to_upper, to_title, to_sentence, reverse
23-
public :: to_string
24-
25-
!> Version: experimental
26-
!>
27-
!> Create a character string representing the value of the provided variable.
28-
interface to_string
29-
#:for kind in INT_KINDS
30-
module procedure :: to_string_integer_${kind}$
31-
#:endfor
32-
#:for kind in LOG_KINDS
33-
module procedure :: to_string_logical_${kind}$
34-
#:endfor
35-
end interface to_string
3623

3724
! All control characters in the ASCII table (see www.asciitable.com).
3825
character(len=1), public, parameter :: NUL = achar(int(z'00')) !! Null
@@ -362,51 +349,4 @@ contains
362349

363350
end function reverse
364351

365-
#:for kind in INT_KINDS
366-
!> Represent an integer of kind ${kind}$ as character sequence
367-
pure function to_string_integer_${kind}$(val) result(string)
368-
integer, parameter :: ik = ${kind}$
369-
integer(ik), intent(in) :: val
370-
character(len=:), allocatable :: string
371-
integer, parameter :: buffer_len = range(val)+2
372-
character(len=buffer_len) :: buffer
373-
integer :: pos
374-
integer(ik) :: n
375-
character(len=1), parameter :: numbers(0:9) = &
376-
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
377-
378-
if (val == 0_ik) then
379-
string = numbers(0)
380-
return
381-
end if
382-
383-
n = abs(val)
384-
buffer = ""
385-
386-
pos = buffer_len + 1
387-
do while (n > 0_ik)
388-
pos = pos - 1
389-
buffer(pos:pos) = numbers(mod(n, 10_ik))
390-
n = n/10_ik
391-
end do
392-
if (val < 0_ik) then
393-
pos = pos - 1
394-
buffer(pos:pos) = '-'
395-
end if
396-
397-
string = buffer(pos:)
398-
end function to_string_integer_${kind}$
399-
#:endfor
400-
401-
#:for kind in LOG_KINDS
402-
!> Represent an logical of kind ${kind}$ as character sequence
403-
pure function to_string_logical_${kind}$(val) result(string)
404-
integer, parameter :: ik = ${kind}$
405-
logical(ik), intent(in) :: val
406-
character(len=1) :: string
407-
408-
string = merge("T", "F", val)
409-
end function to_string_logical_${kind}$
410-
#:endfor
411-
412352
end module stdlib_ascii

‎src/stdlib_string_type.fypp

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
!> The specification of this module is available [here](../page/specs/stdlib_string_type.html).
1515
module stdlib_string_type
1616
use stdlib_ascii, only: to_lower_ => to_lower, to_upper_ => to_upper, &
17-
& to_title_ => to_title, to_sentence_ => to_sentence, reverse_ => reverse, to_string
17+
& to_title_ => to_title, to_sentence_ => to_sentence, reverse_ => reverse
1818
use stdlib_kinds, only : int8, int16, int32, int64, lk, c_bool
1919
implicit none
2020
private
@@ -42,25 +42,33 @@ module stdlib_string_type
4242
character(len=:), allocatable :: raw
4343
end type string_type
4444

45-
!> Constructor for new string instances
46-
interface string_type
47-
module procedure :: new_string
48-
#:for kind in INT_KINDS
49-
module procedure :: new_string_from_integer_${kind}$
50-
#:endfor
51-
#:for kind in LOG_KINDS
52-
module procedure :: new_string_from_logical_${kind}$
53-
#:endfor
54-
end interface string_type
55-
56-
5745
!> Returns the length of the character sequence represented by the string.
5846
!>
5947
!> This method is elemental and returns a default integer scalar value.
6048
interface len
6149
module procedure :: len_string
6250
end interface len
6351

52+
!> Constructor for new string instances
53+
interface string_type
54+
pure elemental module function new_string(string) result(new)
55+
character(len=*), intent(in), optional :: string
56+
type(string_type) :: new
57+
end function new_string
58+
#:for kind in INT_KINDS
59+
pure elemental module function new_string_from_integer_${kind}$(val) result(new)
60+
integer(${kind}$), intent(in) :: val
61+
type(string_type) :: new
62+
end function new_string_from_integer_${kind}$
63+
#:endfor
64+
#:for kind in LOG_KINDS
65+
pure elemental module function new_string_from_logical_${kind}$(val) result(new)
66+
logical(${kind}$), intent(in) :: val
67+
type(string_type) :: new
68+
end function new_string_from_logical_${kind}$
69+
#:endfor
70+
end interface string_type
71+
6472
!> Returns the length of the character sequence without trailing spaces
6573
!> represented by the string.
6674
!>
@@ -356,35 +364,6 @@ module stdlib_string_type
356364

357365
contains
358366

359-
360-
!> Constructor for new string instances from a scalar character value.
361-
elemental function new_string(string) result(new)
362-
character(len=*), intent(in), optional :: string
363-
type(string_type) :: new
364-
if (present(string)) then
365-
new%raw = string
366-
end if
367-
end function new_string
368-
369-
#:for kind in INT_KINDS
370-
!> Constructor for new string instances from an integer of kind ${kind}$.
371-
elemental function new_string_from_integer_${kind}$(val) result(new)
372-
integer(${kind}$), intent(in) :: val
373-
type(string_type) :: new
374-
new%raw = to_string(val)
375-
end function new_string_from_integer_${kind}$
376-
#:endfor
377-
378-
#:for kind in LOG_KINDS
379-
!> Constructor for new string instances from a logical of kind ${kind}$.
380-
elemental function new_string_from_logical_${kind}$(val) result(new)
381-
logical(${kind}$), intent(in) :: val
382-
type(string_type) :: new
383-
new%raw = to_string(val)
384-
end function new_string_from_logical_${kind}$
385-
#:endfor
386-
387-
388367
!> Assign a character sequence to a string.
389368
elemental subroutine assign_string_char(lhs, rhs)
390369
type(string_type), intent(inout) :: lhs
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#:include "common.fypp"
2+
submodule(stdlib_string_type) stdlib_string_type_constructor
3+
4+
use stdlib_strings, only: to_string
5+
6+
contains
7+
8+
!> Constructor for new string instances from a scalar character value.
9+
elemental module function new_string(string) result(new)
10+
character(len=*), intent(in), optional :: string
11+
type(string_type) :: new
12+
if (present(string)) then
13+
new%raw = string
14+
end if
15+
end function new_string
16+
17+
#:for kind in INT_KINDS
18+
!> Constructor for new string instances from an integer of kind ${kind}$.
19+
elemental module function new_string_from_integer_${kind}$(val) result(new)
20+
integer(${kind}$), intent(in) :: val
21+
type(string_type) :: new
22+
new%raw = to_string(val)
23+
end function new_string_from_integer_${kind}$
24+
#:endfor
25+
26+
#:for kind in LOG_KINDS
27+
!> Constructor for new string instances from a logical of kind ${kind}$.
28+
elemental module function new_string_from_logical_${kind}$(val) result(new)
29+
logical(${kind}$), intent(in) :: val
30+
type(string_type) :: new
31+
new%raw = to_string(val)
32+
end function new_string_from_logical_${kind}$
33+
#:endfor
34+
35+
end submodule stdlib_string_type_constructor

‎src/stdlib_strings.f90 renamed to ‎src/stdlib_strings.fypp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
11
! SPDX-Identifier: MIT
2-
2+
#:include "common.fypp"
33
!> This module implements basic string handling routines.
44
!>
55
!> The specification of this module is available [here](../page/specs/stdlib_strings.html).
66
module stdlib_strings
77
use stdlib_ascii, only: whitespace
88
use stdlib_string_type, only: string_type, char, verify, repeat, len
99
use stdlib_optval, only: optval
10+
use stdlib_kinds, only: sp, dp, qp, int8, int16, int32, int64, lk, c_bool
1011
implicit none
1112
private
1213

14+
public :: to_string
1315
public :: strip, chomp
1416
public :: starts_with, ends_with
1517
public :: slice, find, replace_all, padl, padr, count
1618

19+
!> Version: experimental
20+
!>
21+
!> Format or transfer other types as a string.
22+
!> ([Specification](../page/specs/stdlib_strings.html#to_string))
23+
interface to_string
24+
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES
25+
#:set IL_KINDS_TYPES = INT_KINDS_TYPES + LOG_KINDS_TYPES
26+
#:for k1, t1 in RC_KINDS_TYPES
27+
pure module function to_string_${t1[0]}$_${k1}$(value, format) result(string)
28+
${t1}$, intent(in) :: value
29+
character(len=*), intent(in), optional :: format
30+
character(len=:), allocatable :: string
31+
end function to_string_${t1[0]}$_${k1}$
32+
#:endfor
33+
#:for k1, t1 in IL_KINDS_TYPES
34+
pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string)
35+
${t1}$, intent(in) :: value
36+
character(len=#{if t1[0]=="l"}#1)#{else}#:), allocatable#{endif}# :: string
37+
end function to_string_1_${t1[0]}$_${k1}$
38+
pure module function to_string_2_${t1[0]}$_${k1}$(value, format) result(string)
39+
${t1}$, intent(in) :: value
40+
character(len=*), intent(in) :: format
41+
character(len=:), allocatable :: string
42+
end function to_string_2_${t1[0]}$_${k1}$
43+
#:endfor
44+
end interface to_string
1745

1846
!> Remove leading and trailing whitespace characters.
1947
!>

‎src/stdlib_strings_to_string.fypp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#:include "common.fypp"
2+
submodule(stdlib_strings) stdlib_strings_to_string
3+
4+
integer, parameter :: buffer_len = 128
5+
character(len=*), parameter :: err_sym = "[*]"
6+
!!TODO: [*]?
7+
8+
contains
9+
10+
#:for k1, t1 in REAL_KINDS_TYPES
11+
!> Format or transfer a ${t1}$ scalar as a string.
12+
pure module function to_string_${t1[0]}$_${k1}$(value, format) result(string)
13+
${t1}$, intent(in) :: value
14+
character(len=*), intent(in), optional :: format
15+
character(len=:), allocatable :: string
16+
17+
character(len=buffer_len) :: buffer
18+
integer :: stat
19+
20+
write(buffer, '(' // optval(format, "g0") // ')', iostat=stat) value
21+
if (stat == 0) then
22+
string = trim(buffer)
23+
else
24+
string = err_sym
25+
end if
26+
27+
end function to_string_${t1[0]}$_${k1}$
28+
#:endfor
29+
30+
#:for k1, t1 in CMPLX_KINDS_TYPES
31+
!> Format or transfer a ${t1}$ scalar as a string.
32+
pure module function to_string_${t1[0]}$_${k1}$(value, format) result(string)
33+
${t1}$, intent(in) :: value
34+
character(len=*), intent(in), optional :: format
35+
character(len=:), allocatable :: string
36+
37+
string = '(' // to_string_r_${k1}$(value%re, format) // ',' // &
38+
& to_string_r_${k1}$(value%im, format) // ')'
39+
40+
end function to_string_${t1[0]}$_${k1}$
41+
#:endfor
42+
43+
#:for k1, t1 in INT_KINDS_TYPES
44+
!> Represent an integer of kind ${k1}$ as character sequence.
45+
pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string)
46+
${t1}$, intent(in) :: value
47+
character(len=:), allocatable :: string
48+
integer, parameter :: buffer_len = range(value)+2
49+
character(len=buffer_len) :: buffer
50+
integer :: pos
51+
${t1}$ :: n
52+
character(len=1), parameter :: numbers(0:9) = &
53+
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
54+
55+
if (value == 0_${k1}$) then
56+
string = numbers(0)
57+
return
58+
end if
59+
60+
n = abs(value)
61+
buffer = ""
62+
63+
pos = buffer_len + 1
64+
do while (n > 0_${k1}$)
65+
pos = pos - 1
66+
buffer(pos:pos) = numbers(mod(n, 10_${k1}$))
67+
n = n/10_${k1}$
68+
end do
69+
if (value < 0_${k1}$) then
70+
pos = pos - 1
71+
buffer(pos:pos) = '-'
72+
end if
73+
74+
string = buffer(pos:)
75+
end function to_string_1_${t1[0]}$_${k1}$
76+
77+
pure module function to_string_2_${t1[0]}$_${k1}$(value, format) result(string)
78+
${t1}$, intent(in) :: value
79+
character(len=*), intent(in) :: format
80+
character(len=:), allocatable :: string
81+
82+
character(len=buffer_len) :: buffer
83+
integer :: stat
84+
85+
write(buffer, "(" // format // ")", iostat=stat) value
86+
if (stat == 0) then
87+
string = trim(buffer)
88+
else
89+
string = err_sym
90+
end if
91+
92+
end function to_string_2_${t1[0]}$_${k1}$
93+
#:endfor
94+
95+
#:for k1, t1 in LOG_KINDS_TYPES
96+
!> Represent an logical of kind ${k1}$ as character sequence.
97+
pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string)
98+
${t1}$, intent(in) :: value
99+
character(len=1) :: string
100+
101+
string = merge("T", "F", value)
102+
103+
end function to_string_1_${t1[0]}$_${k1}$
104+
105+
pure module function to_string_2_${t1[0]}$_${k1}$(value, format) result(string)
106+
${t1}$, intent(in) :: value
107+
character(len=*), intent(in) :: format
108+
character(len=:), allocatable :: string
109+
110+
character(len=buffer_len) :: buffer
111+
integer :: stat
112+
113+
write(buffer, "(" // format // ")", iostat=stat) value
114+
if (stat == 0) then
115+
string = trim(buffer)
116+
else
117+
string = err_sym
118+
end if
119+
120+
end function to_string_2_${t1[0]}$_${k1}$
121+
#:endfor
122+
123+
end submodule stdlib_strings_to_string

‎src/tests/ascii/test_ascii.f90

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ program test_ascii
66
whitespace, letters, is_alphanum, is_alpha, is_lower, is_upper, &
77
is_digit, is_octal_digit, is_hex_digit, is_white, is_blank, &
88
is_control, is_punctuation, is_graphical, is_printable, is_ascii, &
9-
to_lower, to_upper, to_title, to_sentence, reverse, LF, TAB, NUL, DEL, &
10-
to_string
9+
to_lower, to_upper, to_title, to_sentence, reverse, LF, TAB, NUL, DEL
1110
use stdlib_kinds, only : int8, int16, int32, int64, lk, c_bool
1211

1312
implicit none
@@ -76,8 +75,6 @@ program test_ascii
7675
call test_to_sentence_string
7776
call test_reverse_string
7877

79-
call test_to_string
80-
8178
contains
8279

8380
subroutine test_is_alphanum_short
@@ -640,47 +637,4 @@ subroutine test_reverse_string
640637
call check(trim(adjustl(dlc)) == "desrever")
641638
end subroutine test_reverse_string
642639

643-
subroutine test_to_string
644-
character(len=128) :: flc
645-
646-
write(flc, '(g0)') 1026192
647-
call check(to_string(1026192) == trim(flc))
648-
649-
write(flc, '(g0)') -124784
650-
call check(to_string(-124784) == trim(flc))
651-
652-
write(flc, '(g0)') 1_int8
653-
call check(to_string(1_int8) == trim(flc))
654-
655-
write(flc, '(g0)') -3_int8
656-
call check(to_string(-3_int8) == trim(flc))
657-
658-
write(flc, '(g0)') 80_int16
659-
call check(to_string(80_int16) == trim(flc))
660-
661-
write(flc, '(g0)') 8924890_int32
662-
call check(to_string(8924890_int32) == trim(flc))
663-
664-
write(flc, '(g0)') -2378401_int32
665-
call check(to_string(-2378401_int32) == trim(flc))
666-
667-
write(flc, '(g0)') -921092378401_int64
668-
call check(to_string(-921092378401_int64) == trim(flc))
669-
670-
write(flc, '(g0)') 1272835771_int64
671-
call check(to_string(1272835771_int64) == trim(flc))
672-
673-
write(flc, '(g0)') .true.
674-
call check(to_string(.true.) == trim(flc))
675-
676-
write(flc, '(g0)') .false.
677-
call check(to_string(.false.) == trim(flc))
678-
679-
write(flc, '(g0)') .true._c_bool
680-
call check(to_string(.true._c_bool) == trim(flc))
681-
682-
write(flc, '(g0)') .false._lk
683-
call check(to_string(.false._lk) == trim(flc))
684-
end subroutine test_to_string
685-
686640
end program test_ascii

‎src/tests/string/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ ADDTEST(string_match)
55
ADDTEST(string_derivedtype_io)
66
ADDTEST(string_functions)
77
ADDTEST(string_strip_chomp)
8+
ADDTEST(string_to_string)

‎src/tests/string/Makefile.manual

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ PROGS_SRC = test_string_assignment.f90 \
44
test_string_intrinsic.f90 \
55
test_string_match.f90 \
66
test_string_operator.f90 \
7-
test_string_strip_chomp.f90
7+
test_string_strip_chomp.f90 \
8+
test_string_to_string.f90
89

910

1011
include ../Makefile.manual.test.mk

‎src/tests/string/test_string_functions.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module test_string_functions
66
to_lower, to_upper, to_title, to_sentence, reverse
77
use stdlib_strings, only: slice, find, replace_all, padl, padr, count
88
use stdlib_optval, only: optval
9-
use stdlib_ascii, only : to_string
9+
use stdlib_strings, only : to_string
1010
implicit none
1111

1212
contains
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
! SPDX-Identifier: MIT
2+
module test_string_to_string
3+
4+
use stdlib_strings, only: to_string, starts_with
5+
use stdlib_error, only: check
6+
use stdlib_optval, only: optval
7+
implicit none
8+
9+
contains
10+
11+
subroutine check_formatter(actual, expected, description, partial)
12+
character(len=*), intent(in) :: actual, expected, description
13+
logical, intent(in), optional :: partial
14+
logical :: stat
15+
character(len=:), allocatable :: msg
16+
17+
if (optval(partial, .false.)) then
18+
stat = starts_with(actual, expected)
19+
else
20+
stat = actual == expected
21+
end if
22+
23+
if (.not. stat) then
24+
msg = description // new_line("a") // &
25+
& "Expected: '" // expected // "' but got '" // actual // "'"
26+
else
27+
print '(" - ", a, /, " Result: ''", a, "''")', description, actual
28+
end if
29+
30+
call check(stat, msg)
31+
32+
end subroutine check_formatter
33+
34+
subroutine test_to_string_complex
35+
call check_formatter(to_string((1, 1)), "(1.0", &
36+
& "Default formatter for complex number", partial=.true.)
37+
call check_formatter(to_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", &
38+
& "Formatter for complex number")
39+
call check_formatter(to_string((-1, -1), 'F6.2'), "( -1.00, -1.00)", &
40+
& "Formatter for negative complex number")
41+
call check_formatter(to_string((1, 1), 'SP,F6.2'), "( +1.00, +1.00)", &
42+
& "Formatter with sign control descriptor for complex number")
43+
call check_formatter(to_string((1, 1), 'F6.2') // to_string((2, 2), '(F7.3)'), &
44+
& "( 1.00, 1.00)( 2.000, 2.000)", &
45+
& "Multiple formatters for complex numbers")
46+
47+
end subroutine test_to_string_complex
48+
49+
subroutine test_to_string_integer
50+
call check_formatter(to_string(100), "100", &
51+
& "Default formatter for integer number")
52+
call check_formatter(to_string(100, 'I6'), " 100", &
53+
& "Formatter for integer number")
54+
call check_formatter(to_string(100, 'I0.6'), "000100", &
55+
& "Formatter with zero padding for integer number")
56+
call check_formatter(to_string(100, 'I6') // to_string(1000, '(I7)'), &
57+
& " 100 1000", "Multiple formatters for integers")
58+
call check_formatter(to_string(34, 'B8'), " 100010", &
59+
& "Binary formatter for integer number")
60+
call check_formatter(to_string(34, 'O0.3'), "042", &
61+
& "Octal formatter with zero padding for integer number")
62+
call check_formatter(to_string(34, 'Z3'), " 22", &
63+
& "Hexadecimal formatter for integer number")
64+
65+
end subroutine test_to_string_integer
66+
67+
subroutine test_to_string_real
68+
call check_formatter(to_string(100.), "100.0", &
69+
& "Default formatter for real number", partial=.true.)
70+
call check_formatter(to_string(100., 'F6.2'), "100.00", &
71+
& "Formatter for real number")
72+
call check_formatter(to_string(289., 'E7.2'), ".29E+03", &
73+
& "Exponential formatter with rounding for real number")
74+
call check_formatter(to_string(128., 'ES8.2'), "1.28E+02", &
75+
& "Exponential formatter for real number")
76+
77+
! Wrong demonstration
78+
call check_formatter(to_string(-100., 'F6.2'), "*", &
79+
& "Too narrow formatter for signed real number", partial=.true.)
80+
call check_formatter(to_string(1000., 'F6.3'), "*", &
81+
& "Too narrow formatter for real number", partial=.true.)
82+
call check_formatter(to_string(1000., '7.3'), "[*]", &
83+
& "Invalid formatter for real number", partial=.true.)
84+
85+
end subroutine test_to_string_real
86+
87+
subroutine test_to_string_logical
88+
call check_formatter(to_string(.true.), "T", &
89+
& "Default formatter for logcal value")
90+
call check_formatter(to_string(.true., 'L2'), " T", &
91+
& "Formatter for logical value")
92+
call check_formatter(to_string(.false., 'L2') // to_string(.true., '(L5)'), &
93+
& " F T", "Multiple formatters for logical values")
94+
95+
! Wrong demonstration
96+
call check_formatter(to_string(.false., '1x'), "[*]", &
97+
& "Invalid formatter for logical value", partial=.true.)
98+
99+
end subroutine test_to_string_logical
100+
101+
102+
end module test_string_to_string
103+
104+
program tester
105+
use test_string_to_string
106+
implicit none
107+
108+
call test_to_string_complex
109+
call test_to_string_integer
110+
call test_to_string_logical
111+
call test_to_string_real
112+
113+
end program tester

0 commit comments

Comments
 (0)
Please sign in to comment.