From e3829b453ba8a09614b4a145ea32466a0d238749 Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Fri, 25 Jun 2021 21:17:51 +0800 Subject: [PATCH 01/19] add: 1. src/stdlib_strings_format_string.fypp 2. src/tests/string/test_strings_format_string.f90 3. stdlib_strings.fypp%format_string interface modify: 1. doc/spec/stdlib_strings.md%format_string(doc) 2. stdlib_strings.f90 -> stdlib_strings.fypp(rename) 3. src/Makefile.manual%format_string(make) 4. src/tests/string/Makefile.manual%format_string(make) 5. src/CMakelists.txt%format_string(cmake) 6. src/CMakelists.txt%format_string(cmake) note: make test passed. cmake test passed --- doc/specs/stdlib_strings.md | 60 +++++++++++++++++++ src/CMakeLists.txt | 3 +- src/Makefile.manual | 9 ++- ...stdlib_strings.f90 => stdlib_strings.fypp} | 19 +++++- src/stdlib_strings_format_string.fypp | 52 ++++++++++++++++ src/tests/string/CMakeLists.txt | 1 + src/tests/string/Makefile.manual | 3 +- .../string/test_strings_format_string.f90 | 24 ++++++++ 8 files changed, 165 insertions(+), 6 deletions(-) rename src/{stdlib_strings.f90 => stdlib_strings.fypp} (95%) create mode 100644 src/stdlib_strings_format_string.fypp create mode 100644 src/tests/string/test_strings_format_string.f90 diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 19bbf4a59..c7683ef4a 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -328,3 +328,63 @@ program demo_find end program demo_find ``` + + +### `format_string` + +#### Description + +Format or transfer a integer/real/complex/logical variable as a character sequence. + + +#### Syntax + +`format_string = [[stdlib_strings(module):format_string(interface)]] (value, [, format])` + +#### Status + +Experimental + +#### Class + +Pure function + +#### Argument + +- `value`: Integer/real/complex/logical scalar. + This argument is intent(in). +- `format`: Character scalar like `'(F6.2)'`. + This argument is intent(in) and optional. + +#### Result value + +The result is a allocatable length Character scalar. + +#### Example + +```fortran +program demo_strings_format_string + use, non_intrinsic :: stdlib_strings, only: format_string + implicit none + print *, 'format_string(complex) : ' + print *, format_string((1, 1)) ! (1.00000000,1.00000000) + print *, format_string((1, 1), '(F6.2)') ! (1.00,1.00) + print *, format_string((1, 1), '(F6.2)'), format_string((2, 2), '(F7.3)') ! (1.00,1.00)(2.000,2.000) + print *, 'format_string(integer) : ' + print *, format_string(100) ! 100 + print *, format_string(100, '(I6)') ! 100 + print *, format_string(100, '(I6)'), format_string(1000, '(I7)') ! 1001000 + print *, 'format_string(real) : ' + print *, format_string(100.) ! 100.000000 + print *, format_string(100., '(F6.2)') ! 100.00 + print *, format_string(100., '(F6.2)'), & + format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') ! 100.00******** + !! Wrong demonstration + print *, 'format_string(logical) : ' + print *, format_string(.true.) ! T + print *, format_string(.true., '(L2)') ! T + print *, format_string(.false., '(L2)'), format_string(.true., '(L5)'), & + format_string(.false., '(I5)') ! FT* + !! Wrong demonstration +end program demo_strings_format_string +``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a059c4a5e..88bf89c56 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,8 @@ set(fppFiles stdlib_stats_distribution_PRNG.fypp stdlib_math.fypp stdlib_string_type.fypp + stdlib_strings_format_string.fypp + stdlib_strings.fypp ) @@ -47,7 +49,6 @@ set(SRC stdlib_error.f90 stdlib_kinds.f90 stdlib_logger.f90 - stdlib_strings.f90 stdlib_system.F90 ${outFiles} ) diff --git a/src/Makefile.manual b/src/Makefile.manual index 06a99a472..c10eddccb 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -25,13 +25,14 @@ SRCFYPP =\ stdlib_stats_var.fypp \ stdlib_math.fypp \ stdlib_stats_distribution_PRNG.fypp \ - stdlib_string_type.fypp + stdlib_string_type.fypp \ + stdlib_strings.fypp \ + stdlib_strings_format_string.fypp SRC = f18estop.f90 \ stdlib_error.f90 \ stdlib_kinds.f90 \ stdlib_logger.f90 \ - stdlib_strings.f90 \ $(SRCGEN) LIB = libstdlib.a @@ -129,5 +130,7 @@ stdlib_string_type.o: stdlib_ascii.o \ stdlib_kinds.o stdlib_strings.o: stdlib_ascii.o \ stdlib_string_type.o \ - stdlib_optval.o + stdlib_optval.o \ + stdlib_kinds.o stdlib_math.o: stdlib_kinds.o +stdlib_strings_format_string.o: stdlib_strings.o diff --git a/src/stdlib_strings.f90 b/src/stdlib_strings.fypp similarity index 95% rename from src/stdlib_strings.f90 rename to src/stdlib_strings.fypp index 89d5ba020..45609f509 100644 --- a/src/stdlib_strings.f90 +++ b/src/stdlib_strings.fypp @@ -1,5 +1,7 @@ ! SPDX-Identifier: MIT - +#:include "common.fypp" +#:set KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES & + & + CMPLX_KINDS_TYPES !> This module implements basic string handling routines. !> !> The specification of this module is available [here](../page/specs/stdlib_strings.html). @@ -7,13 +9,28 @@ module stdlib_strings use stdlib_ascii, only: whitespace use stdlib_string_type, only: string_type, char, verify use stdlib_optval, only: optval + use stdlib_kinds, only: sp, dp, qp, int8, int16, int32, int64, lk, c_bool implicit none private + public :: format_string public :: strip, chomp public :: starts_with, ends_with public :: slice, find + interface format_string + !! version: experimental + !! + !! Format ${type}$ variable as character sequence + !! ([Specification](../page/specs/stdlib_string.html#description)) + #:for kind, type in KINDS_TYPES + pure module function format_string_${type[0]}$${kind}$(val, fmt) result(string) + character(len=:), allocatable :: string + ${type}$, intent(in) :: val + character(len=*), intent(in), optional :: fmt + end function format_string_${type[0]}$${kind}$ + #:endfor + end interface format_string !> Remove leading and trailing whitespace characters. !> diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_strings_format_string.fypp new file mode 100644 index 000000000..b3b2f0e18 --- /dev/null +++ b/src/stdlib_strings_format_string.fypp @@ -0,0 +1,52 @@ +#:include "common.fypp" +#:set RIL_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES +submodule (stdlib_strings) stdlib_strings_format_string + + implicit none + integer, parameter :: buffer_len = 512 + +contains + + #:for kind, type in RIL_KINDS_TYPES + module procedure format_string_${type[0]}$${kind}$ + !! Format ${type}$ variable as character sequence + character(len=buffer_len) :: buffer + integer :: stat + if(present(fmt)) then + write(buffer, fmt, iostat=stat) val + if(stat == 0) then + string = trim(adjustl(buffer)) + else + string = '*' + !!\TODO: *? + end if + else + write(buffer, *, iostat=stat) val + if(stat == 0) then + string = trim(adjustl(buffer)) + else + string = '*' + !!\TODO: *? + end if + end if + end procedure format_string_${type[0]}$${kind}$ + #:endfor + + #:for kind, type in CMPLX_KINDS_TYPES + module procedure format_string_${type[0]}$${kind}$ + !! Format ${type}$ variable as character sequence + character(len=buffer_len) :: buffer + if(present(fmt)) then + write(buffer, *) '('//& + format_string_r${kind}$(real(val), fmt)//','// & + format_string_r${kind}$(aimag(val), fmt)//')' + else + write(buffer, *) '('//& + format_string_r${kind}$(real(val))//','// & + format_string_r${kind}$(aimag(val))//')' + end if + string = trim(adjustl(buffer)) + end procedure format_string_${type[0]}$${kind}$ + #:endfor + +end submodule stdlib_strings_format_string \ No newline at end of file diff --git a/src/tests/string/CMakeLists.txt b/src/tests/string/CMakeLists.txt index e0d1d9710..4e5db15e2 100644 --- a/src/tests/string/CMakeLists.txt +++ b/src/tests/string/CMakeLists.txt @@ -5,3 +5,4 @@ ADDTEST(string_match) ADDTEST(string_derivedtype_io) ADDTEST(string_functions) ADDTEST(string_strip_chomp) +ADDTEST(strings_format_string) diff --git a/src/tests/string/Makefile.manual b/src/tests/string/Makefile.manual index 2e91044a1..7ab5e5765 100644 --- a/src/tests/string/Makefile.manual +++ b/src/tests/string/Makefile.manual @@ -4,7 +4,8 @@ PROGS_SRC = test_string_assignment.f90 \ test_string_intrinsic.f90 \ test_string_match.f90 \ test_string_operator.f90 \ - test_string_strip_chomp.f90 + test_string_strip_chomp.f90 \ + test_strings_format_string.f90 include ../Makefile.manual.test.mk diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 new file mode 100644 index 000000000..ca2fe1248 --- /dev/null +++ b/src/tests/string/test_strings_format_string.f90 @@ -0,0 +1,24 @@ +program test_strings_format_string + use, non_intrinsic :: stdlib_strings, only: format_string + implicit none + print *, 'format_string(complex) : ' + print *, format_string((1, 1)) + print *, format_string((1, 1), '(F6.2)') + print *, format_string((1, 1), '(F6.2)'), format_string((2, 2), '(F7.3)') + print *, 'format_string(integer) : ' + print *, format_string(100) + print *, format_string(100, '(I6)') + print *, format_string(100, '(I6)'), format_string(1000, '(I7)') + print *, 'format_string(real) : ' + print *, format_string(100.) + print *, format_string(100., '(F6.2)') + print *, format_string(100., '(F6.2)'), & + format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') + !! Wrong demonstration + print *, 'format_string(logical) : ' + print *, format_string(.true.) + print *, format_string(.true., '(L2)') + print *, format_string(.false., '(L2)'), format_string(.true., '(L5)'), & + format_string(.false., '(I5)') + !! Wrong demonstration +end program test_strings_format_string \ No newline at end of file From da2881cdce8c89e4e5c471cd03a8c54bf1bca783 Mon Sep 17 00:00:00 2001 From: St Maxwell Date: Sat, 26 Jun 2021 13:41:06 +0800 Subject: [PATCH 02/19] remove a redundant comma in doc clean the implementation of format_string --- doc/specs/stdlib_strings.md | 2 +- src/stdlib_strings_format_string.fypp | 40 ++++++++++----------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index c7683ef4a..933fc2d4d 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -339,7 +339,7 @@ Format or transfer a integer/real/complex/logical variable as a character sequen #### Syntax -`format_string = [[stdlib_strings(module):format_string(interface)]] (value, [, format])` +`format_string = [[stdlib_strings(module):format_string(interface)]] (value[, format])` #### Status diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_strings_format_string.fypp index b3b2f0e18..2418a253c 100644 --- a/src/stdlib_strings_format_string.fypp +++ b/src/stdlib_strings_format_string.fypp @@ -2,6 +2,7 @@ #:set RIL_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES submodule (stdlib_strings) stdlib_strings_format_string + use stdlib_optval, only: optval implicit none integer, parameter :: buffer_len = 512 @@ -12,23 +13,15 @@ contains !! Format ${type}$ variable as character sequence character(len=buffer_len) :: buffer integer :: stat - if(present(fmt)) then - write(buffer, fmt, iostat=stat) val - if(stat == 0) then - string = trim(adjustl(buffer)) - else - string = '*' - !!\TODO: *? - end if + + write(buffer, optval(fmt, "g0"), iostat=stat) val + if (stat == 0) then + string = trim(buffer) else - write(buffer, *, iostat=stat) val - if(stat == 0) then - string = trim(adjustl(buffer)) - else - string = '*' - !!\TODO: *? - end if + string = '*' + !!\TODO: *? end if + end procedure format_string_${type[0]}$${kind}$ #:endfor @@ -36,16 +29,13 @@ contains module procedure format_string_${type[0]}$${kind}$ !! Format ${type}$ variable as character sequence character(len=buffer_len) :: buffer - if(present(fmt)) then - write(buffer, *) '('//& - format_string_r${kind}$(real(val), fmt)//','// & - format_string_r${kind}$(aimag(val), fmt)//')' - else - write(buffer, *) '('//& - format_string_r${kind}$(real(val))//','// & - format_string_r${kind}$(aimag(val))//')' - end if - string = trim(adjustl(buffer)) + + write(buffer, *) '('//& + format_string_r${kind}$(val%re, fmt)//','// & + format_string_r${kind}$(val%im, fmt)//')' + + string = trim(buffer) + end procedure format_string_${type[0]}$${kind}$ #:endfor From c622a5bbd1fe8f4a54a992b7fbd10fca5c510b0c Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Sat, 26 Jun 2021 14:48:27 +0800 Subject: [PATCH 03/19] fix format_string routines update its doc. fix: 1. src/stdlib_strings.fypp%format_string interface comments 2. src/stdlib_strings_format_string.fypp update: 1. src/tests/string/test_strings_format_string.f90 2. doc/specs/stdlib_strings.md note: make passed cmake passed --- doc/specs/stdlib_strings.md | 45 +++++++++++++------ src/stdlib_strings.fypp | 4 +- src/stdlib_strings_format_string.fypp | 3 +- .../string/test_strings_format_string.f90 | 2 +- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 933fc2d4d..c46599ea1 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -339,7 +339,7 @@ Format or transfer a integer/real/complex/logical variable as a character sequen #### Syntax -`format_string = [[stdlib_strings(module):format_string(interface)]] (value[, format])` +`format_string = [[stdlib_strings(module):format_string(interface)]] (value [, format])` #### Status @@ -367,24 +367,43 @@ program demo_strings_format_string use, non_intrinsic :: stdlib_strings, only: format_string implicit none print *, 'format_string(complex) : ' - print *, format_string((1, 1)) ! (1.00000000,1.00000000) - print *, format_string((1, 1), '(F6.2)') ! (1.00,1.00) - print *, format_string((1, 1), '(F6.2)'), format_string((2, 2), '(F7.3)') ! (1.00,1.00)(2.000,2.000) + print *, format_string((1, 1)) + print *, format_string((1, 1), '(F6.2)') + print *, format_string((1, 1), '(F6.2)'), format_string((2, 2), '(F7.3)') print *, 'format_string(integer) : ' - print *, format_string(100) ! 100 - print *, format_string(100, '(I6)') ! 100 - print *, format_string(100, '(I6)'), format_string(1000, '(I7)') ! 1001000 + print *, format_string(100) + print *, format_string(100, '(I6)') + print *, format_string(100, '(I6)'), format_string(1000, '(I7)') print *, 'format_string(real) : ' - print *, format_string(100.) ! 100.000000 - print *, format_string(100., '(F6.2)') ! 100.00 + print *, format_string(100.) + print *, format_string(100., '(F12.2)') print *, format_string(100., '(F6.2)'), & - format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') ! 100.00******** + format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') !! Wrong demonstration print *, 'format_string(logical) : ' - print *, format_string(.true.) ! T - print *, format_string(.true., '(L2)') ! T + print *, format_string(.true.) + print *, format_string(.true., '(L2)') print *, format_string(.false., '(L2)'), format_string(.true., '(L5)'), & - format_string(.false., '(I5)') ! FT* + format_string(.false., '(I5)') !! Wrong demonstration end program demo_strings_format_string ``` +**Results** +```fortran + format_string(complex) : + (1.00000000,1.00000000) + ( 1.00, 1.00) + ( 1.00, 1.00) ( 2.000, 2.000) + format_string(integer) : + 100 + 100 + 100 1000 + format_string(real) : + 100.000000 + 100.00 + 100.00******** + format_string(logical) : + T + T + F T* +``` \ No newline at end of file diff --git a/src/stdlib_strings.fypp b/src/stdlib_strings.fypp index 45609f509..6432d45df 100644 --- a/src/stdlib_strings.fypp +++ b/src/stdlib_strings.fypp @@ -21,8 +21,8 @@ module stdlib_strings interface format_string !! version: experimental !! - !! Format ${type}$ variable as character sequence - !! ([Specification](../page/specs/stdlib_string.html#description)) + !! Format other types as character sequence. + !! ([Specification](../page/specs/stdlib_strings.html#description)) #:for kind, type in KINDS_TYPES pure module function format_string_${type[0]}$${kind}$(val, fmt) result(string) character(len=:), allocatable :: string diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_strings_format_string.fypp index 2418a253c..29cda718d 100644 --- a/src/stdlib_strings_format_string.fypp +++ b/src/stdlib_strings_format_string.fypp @@ -2,7 +2,6 @@ #:set RIL_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES submodule (stdlib_strings) stdlib_strings_format_string - use stdlib_optval, only: optval implicit none integer, parameter :: buffer_len = 512 @@ -14,7 +13,7 @@ contains character(len=buffer_len) :: buffer integer :: stat - write(buffer, optval(fmt, "g0"), iostat=stat) val + write(buffer, optval(fmt, "(g0)"), iostat=stat) val if (stat == 0) then string = trim(buffer) else diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 index ca2fe1248..0362b4258 100644 --- a/src/tests/string/test_strings_format_string.f90 +++ b/src/tests/string/test_strings_format_string.f90 @@ -11,7 +11,7 @@ program test_strings_format_string print *, format_string(100, '(I6)'), format_string(1000, '(I7)') print *, 'format_string(real) : ' print *, format_string(100.) - print *, format_string(100., '(F6.2)') + print *, format_string(100., '(F12.2)') print *, format_string(100., '(F6.2)'), & format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') !! Wrong demonstration From 193f07fe001946302a3ae6bf48f68293db72005b Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Sat, 26 Jun 2021 09:11:39 +0200 Subject: [PATCH 04/19] Expand unit testing for more cases --- src/stdlib_strings_format_string.fypp | 9 +- .../string/test_strings_format_string.f90 | 94 +++++++++++++++---- 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_strings_format_string.fypp index 29cda718d..708c4f6a0 100644 --- a/src/stdlib_strings_format_string.fypp +++ b/src/stdlib_strings_format_string.fypp @@ -29,13 +29,10 @@ contains !! Format ${type}$ variable as character sequence character(len=buffer_len) :: buffer - write(buffer, *) '('//& - format_string_r${kind}$(val%re, fmt)//','// & - format_string_r${kind}$(val%im, fmt)//')' - - string = trim(buffer) + string = '('//format_string_r${kind}$(val%re, fmt)//','// & + format_string_r${kind}$(val%im, fmt)//')' end procedure format_string_${type[0]}$${kind}$ #:endfor -end submodule stdlib_strings_format_string \ No newline at end of file +end submodule stdlib_strings_format_string diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 index 0362b4258..0cfadeb9f 100644 --- a/src/tests/string/test_strings_format_string.f90 +++ b/src/tests/string/test_strings_format_string.f90 @@ -1,24 +1,82 @@ program test_strings_format_string - use, non_intrinsic :: stdlib_strings, only: format_string + use stdlib_strings, only: format_string, starts_with + use stdlib_error, only: check + use stdlib_optval, only: optval implicit none print *, 'format_string(complex) : ' - print *, format_string((1, 1)) - print *, format_string((1, 1), '(F6.2)') - print *, format_string((1, 1), '(F6.2)'), format_string((2, 2), '(F7.3)') + call check_formatter(format_string((1, 1)), "(1.00000000,1.00000000)", & + & "Default formatter for complex number") + call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & + & "Formatter for complex number") + call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & + & "Formatter for negative complex number") + call check_formatter(format_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & + & "Formatter with sign control descriptor for complex number") + call check_formatter(format_string((1, 1), '(F6.2)')//format_string((2, 2), '(F7.3)'), & + & "( 1.00, 1.00)( 2.000, 2.000)", & + & "Multiple formatters for complex numbers") print *, 'format_string(integer) : ' - print *, format_string(100) - print *, format_string(100, '(I6)') - print *, format_string(100, '(I6)'), format_string(1000, '(I7)') + call check_formatter(format_string(100), "100", & + & "Default formatter for integer number") + call check_formatter(format_string(100, '(I6)'), " 100", & + & "Formatter for integer number") + call check_formatter(format_string(100, '(I0.6)'), "000100", & + & "Formatter with zero padding for integer number") + call check_formatter(format_string(100, '(I6)')//format_string(1000, '(I7)'), & + & " 100 1000", & + & "Multiple formatters for integers") + call check_formatter(format_string(34, '(B8)'), " 100010", & + & "Binary formatter for integer number") + call check_formatter(format_string(34, '(O0.3)'), "042", & + & "Octal formatter with zero padding for integer number") + call check_formatter(format_string(34, '(Z3)'), " 22", & + & "Hexadecimal formatter for integer number") print *, 'format_string(real) : ' - print *, format_string(100.) - print *, format_string(100., '(F12.2)') - print *, format_string(100., '(F6.2)'), & - format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') - !! Wrong demonstration + call check_formatter(format_string(100.), "100.000000", & + & "Default formatter for real number") + call check_formatter(format_string(100., '(F6.2)'), "100.00", & + & "Formatter for real number") + call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & + & "Exponential formatter with rounding for real number") + call check_formatter(format_string(128., '(ES8.2)'), "1.28E+02", & + & "Exponential formatter for real number") + ! Wrong demonstration + call check_formatter(format_string(-100., '(F6.2)'), "*", & + & "Too narrow formatter for signed real number", partial=.true.) + call check_formatter(format_string(1000., '(F6.3)'), "*", & + & "Too narrow formatter for real number", partial=.true.) + call check_formatter(format_string(1000, '(F7.3)'), "*", & + & "Real formatter for integer number", partial=.true.) print *, 'format_string(logical) : ' - print *, format_string(.true.) - print *, format_string(.true., '(L2)') - print *, format_string(.false., '(L2)'), format_string(.true., '(L5)'), & - format_string(.false., '(I5)') - !! Wrong demonstration -end program test_strings_format_string \ No newline at end of file + call check_formatter(format_string(.true.), "T", & + & "Default formatter for logcal value") + call check_formatter(format_string(.true., '(L2)'), " T", & + & "Formatter for logical value") + call check_formatter(format_string(.false., '(L2)')//format_string(.true., '(L5)'), & + & " F T", & + & "Multiple formatters for logical values") + ! Wrong demonstration + call check_formatter(format_string(.false., '(I5)'), "*", & + & "Integer formatter for logical value", partial=.true.) + +contains + subroutine check_formatter(actual, expected, description, partial) + character(len=*), intent(in) :: actual, expected, description + logical, intent(in), optional :: partial + logical :: stat + character(len=:), allocatable :: msg + + if (optval(partial, .false.)) then + stat = starts_with(actual, expected) + else + stat = actual == expected + end if + if (.not.stat) then + msg = description // new_line("a") // & + & "Expected: '"//expected//"' but got '"//actual//"'" + else + print '(" - ", a, /, " Result: ''", a, "''")', description, actual + end if + call check(stat, msg) + end subroutine check_formatter +end program test_strings_format_string From 41c578393077b896812caa3affc62fa78a8d1389 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Sat, 26 Jun 2021 10:52:37 +0200 Subject: [PATCH 05/19] Try to make default formatter tests compiler independent --- src/tests/string/test_strings_format_string.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 index 0cfadeb9f..1d0d5015f 100644 --- a/src/tests/string/test_strings_format_string.f90 +++ b/src/tests/string/test_strings_format_string.f90 @@ -4,8 +4,8 @@ program test_strings_format_string use stdlib_optval, only: optval implicit none print *, 'format_string(complex) : ' - call check_formatter(format_string((1, 1)), "(1.00000000,1.00000000)", & - & "Default formatter for complex number") + call check_formatter(format_string((1, 1)), "(1.0", & + & "Default formatter for complex number", partial=.true.) call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & & "Formatter for complex number") call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & @@ -32,8 +32,8 @@ program test_strings_format_string call check_formatter(format_string(34, '(Z3)'), " 22", & & "Hexadecimal formatter for integer number") print *, 'format_string(real) : ' - call check_formatter(format_string(100.), "100.000000", & - & "Default formatter for real number") + call check_formatter(format_string(100.), "100.0", & + & "Default formatter for real number", partial=.true.) call check_formatter(format_string(100., '(F6.2)'), "100.00", & & "Formatter for real number") call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & From 785902c069ec47f9590273fb159385d8c2480f87 Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Sat, 26 Jun 2021 17:27:05 +0800 Subject: [PATCH 06/19] fix test_strings_format_string.f90 for [this problem](https://github.com/fortran-lang/stdlib/pull/444#issuecomment-868965643) fix: 1. src/tests/string/test_strings_format_string.f90 2. romove the `buffer` variable in `format_string_c${kind}$` update: 1. doc/specs/stdlib_strings.md%format_string note: make passed cmake passed --- doc/specs/stdlib_strings.md | 157 ++++++++++++++---- src/stdlib_strings_format_string.fypp | 1 - .../string/test_strings_format_string.f90 | 8 +- 3 files changed, 129 insertions(+), 37 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index c46599ea1..a2ca020ab 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -363,47 +363,140 @@ The result is a allocatable length Character scalar. #### Example ```fortran -program demo_strings_format_string - use, non_intrinsic :: stdlib_strings, only: format_string +program test_strings_format_string + use stdlib_strings, only: format_string, starts_with + use stdlib_error, only: check + use stdlib_optval, only: optval implicit none print *, 'format_string(complex) : ' - print *, format_string((1, 1)) - print *, format_string((1, 1), '(F6.2)') - print *, format_string((1, 1), '(F6.2)'), format_string((2, 2), '(F7.3)') + call check_formatter(format_string((1, 1)), "(1.0", & + & "Default formatter for complex number", partial=.true.) + call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & + & "Formatter for complex number") + call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & + & "Formatter for negative complex number") + call check_formatter(format_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & + & "Formatter with sign control descriptor for complex number") + call check_formatter(format_string((1, 1), '(F6.2)')//format_string((2, 2), '(F7.3)'), & + & "( 1.00, 1.00)( 2.000, 2.000)", & + & "Multiple formatters for complex numbers") print *, 'format_string(integer) : ' - print *, format_string(100) - print *, format_string(100, '(I6)') - print *, format_string(100, '(I6)'), format_string(1000, '(I7)') + call check_formatter(format_string(100), "100", & + & "Default formatter for integer number") + call check_formatter(format_string(100, '(I6)'), " 100", & + & "Formatter for integer number") + call check_formatter(format_string(100, '(I0.6)'), "000100", & + & "Formatter with zero padding for integer number") + call check_formatter(format_string(100, '(I6)')//format_string(1000, '(I7)'), & + & " 100 1000", & + & "Multiple formatters for integers") + call check_formatter(format_string(34, '(B8)'), " 100010", & + & "Binary formatter for integer number") + call check_formatter(format_string(34, '(O0.3)'), "042", & + & "Octal formatter with zero padding for integer number") + call check_formatter(format_string(34, '(Z3)'), " 22", & + & "Hexadecimal formatter for integer number") print *, 'format_string(real) : ' - print *, format_string(100.) - print *, format_string(100., '(F12.2)') - print *, format_string(100., '(F6.2)'), & - format_string(1000., '(F7.3)'), format_string(1000, '(F7.3)') - !! Wrong demonstration + call check_formatter(format_string(100.), "100.0", & + & "Default formatter for real number", partial=.true.) + call check_formatter(format_string(100., '(F6.2)'), "100.00", & + & "Formatter for real number") + call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & + & "Exponential formatter with rounding for real number") + call check_formatter(format_string(128., '(ES8.2)'), "1.28E+02", & + & "Exponential formatter for real number") + ! Wrong demonstration + call check_formatter(format_string(-100., '(F6.2)'), "*", & + & "Too narrow formatter for signed real number", partial=.true.) + call check_formatter(format_string(1000., '(F6.3)'), "*", & + & "Too narrow formatter for real number", partial=.true.) + call check_formatter(format_string(1000, '(F7.3)'), "*", & + & "Real formatter for integer number", partial=.true.) print *, 'format_string(logical) : ' - print *, format_string(.true.) - print *, format_string(.true., '(L2)') - print *, format_string(.false., '(L2)'), format_string(.true., '(L5)'), & - format_string(.false., '(I5)') - !! Wrong demonstration -end program demo_strings_format_string + call check_formatter(format_string(.true.), "T", & + & "Default formatter for logcal value") + call check_formatter(format_string(.true., '(L2)'), " T", & + & "Formatter for logical value") + call check_formatter(format_string(.false., '(L2)')//format_string(.true., '(L5)'), & + & " F T", & + & "Multiple formatters for logical values") + ! Wrong demonstration + call check_formatter(format_string(.false., '(I5)'), "*", & + & "Integer formatter for logical value", partial=.true.) + +contains + subroutine check_formatter(actual, expected, description, partial) + character(len=*), intent(in) :: actual, expected, description + logical, intent(in), optional :: partial + logical :: stat + character(len=:), allocatable :: msg + + if (optval(partial, .false.)) then + stat = starts_with(actual, expected) + else + stat = actual == expected + end if + if (.not.stat) then + msg = description // new_line("a") // & + & "Expected: '"//expected//"' but got '"//actual//"'" + else + print '(" - ", a, /, " Result: ''", a, "''")', description, actual + end if + call check(stat, msg) + end subroutine check_formatter +end program test_strings_format_string ``` **Results** ```fortran - format_string(complex) : - (1.00000000,1.00000000) - ( 1.00, 1.00) - ( 1.00, 1.00) ( 2.000, 2.000) + format_string(complex) : + - Default formatter for complex number + Result: '(1.00000000,1.00000000)' !! Different compilers have different widths here. + !! [link](https://github.com/fortran-lang/stdlib/pull/444#issuecomment-868965643) + - Formatter for complex number + Result: '( 1.00, 1.00)' + - Formatter for negative complex number + Result: '( -1.00, -1.00)' + - Formatter with sign control descriptor for complex number + Result: '( +1.00, +1.00)' + - Multiple formatters for complex numbers + Result: '( 1.00, 1.00)( 2.000, 2.000)' format_string(integer) : - 100 - 100 - 100 1000 + - Default formatter for integer number + Result: '100' + - Formatter for integer number + Result: ' 100' + - Formatter with zero padding for integer number + Result: '000100' + - Multiple formatters for integers + Result: ' 100 1000' + - Binary formatter for integer number + Result: ' 100010' + - Octal formatter with zero padding for integer number + Result: '042' + - Hexadecimal formatter for integer number + Result: ' 22' format_string(real) : - 100.000000 - 100.00 - 100.00******** + - Default formatter for real number + Result: '100.000000' !! Ditto + - Formatter for real number + Result: '100.00' + - Exponential formatter with rounding for real number + Result: '.29E+03' + - Exponential formatter for real number + Result: '1.28E+02' + - Too narrow formatter for signed real number + Result: '******' + - Too narrow formatter for real number + Result: '******' + - Real formatter for integer number + Result: '*' format_string(logical) : - T - T - F T* + - Default formatter for logcal value + Result: 'T' + - Formatter for logical value + Result: ' T' + - Multiple formatters for logical values + Result: ' F T' + - Integer formatter for logical value + Result: '*' ``` \ No newline at end of file diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_strings_format_string.fypp index 708c4f6a0..5486acfbe 100644 --- a/src/stdlib_strings_format_string.fypp +++ b/src/stdlib_strings_format_string.fypp @@ -27,7 +27,6 @@ contains #:for kind, type in CMPLX_KINDS_TYPES module procedure format_string_${type[0]}$${kind}$ !! Format ${type}$ variable as character sequence - character(len=buffer_len) :: buffer string = '('//format_string_r${kind}$(val%re, fmt)//','// & format_string_r${kind}$(val%im, fmt)//')' diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 index 0cfadeb9f..1d0d5015f 100644 --- a/src/tests/string/test_strings_format_string.f90 +++ b/src/tests/string/test_strings_format_string.f90 @@ -4,8 +4,8 @@ program test_strings_format_string use stdlib_optval, only: optval implicit none print *, 'format_string(complex) : ' - call check_formatter(format_string((1, 1)), "(1.00000000,1.00000000)", & - & "Default formatter for complex number") + call check_formatter(format_string((1, 1)), "(1.0", & + & "Default formatter for complex number", partial=.true.) call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & & "Formatter for complex number") call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & @@ -32,8 +32,8 @@ program test_strings_format_string call check_formatter(format_string(34, '(Z3)'), " 22", & & "Hexadecimal formatter for integer number") print *, 'format_string(real) : ' - call check_formatter(format_string(100.), "100.000000", & - & "Default formatter for real number") + call check_formatter(format_string(100.), "100.0", & + & "Default formatter for real number", partial=.true.) call check_formatter(format_string(100., '(F6.2)'), "100.00", & & "Formatter for real number") call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & From 99954bb43e71da2ff2913df8b4ae3dee95b74f44 Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Sat, 26 Jun 2021 17:46:08 +0800 Subject: [PATCH 07/19] update test_string_format_string.f90 --- doc/specs/stdlib_strings.md | 6 +++--- src/tests/string/test_strings_format_string.f90 | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index a2ca020ab..7dd154be1 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -410,8 +410,8 @@ program test_strings_format_string & "Too narrow formatter for signed real number", partial=.true.) call check_formatter(format_string(1000., '(F6.3)'), "*", & & "Too narrow formatter for real number", partial=.true.) - call check_formatter(format_string(1000, '(F7.3)'), "*", & - & "Real formatter for integer number", partial=.true.) + call check_formatter(format_string(1000., '(7.3)'), "*", & + & "Invalid formatter for real number", partial=.true.) print *, 'format_string(logical) : ' call check_formatter(format_string(.true.), "T", & & "Default formatter for logcal value") @@ -488,7 +488,7 @@ end program test_strings_format_string Result: '******' - Too narrow formatter for real number Result: '******' - - Real formatter for integer number + - Invalid formatter for real number Result: '*' format_string(logical) : - Default formatter for logcal value diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 index 1d0d5015f..6f935748a 100644 --- a/src/tests/string/test_strings_format_string.f90 +++ b/src/tests/string/test_strings_format_string.f90 @@ -45,8 +45,8 @@ program test_strings_format_string & "Too narrow formatter for signed real number", partial=.true.) call check_formatter(format_string(1000., '(F6.3)'), "*", & & "Too narrow formatter for real number", partial=.true.) - call check_formatter(format_string(1000, '(F7.3)'), "*", & - & "Real formatter for integer number", partial=.true.) + call check_formatter(format_string(1000., '(7.3)'), "*", & + & "Invalid formatter for real number", partial=.true.) print *, 'format_string(logical) : ' call check_formatter(format_string(.true.), "T", & & "Default formatter for logcal value") From f9f7755901534ca0d496e7023e7ed2cf8a4fd927 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Sat, 26 Jun 2021 15:15:49 +0200 Subject: [PATCH 08/19] Make invalid logical example more compiler agnostic --- src/tests/string/test_strings_format_string.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 index 6f935748a..9f978f45a 100644 --- a/src/tests/string/test_strings_format_string.f90 +++ b/src/tests/string/test_strings_format_string.f90 @@ -56,8 +56,8 @@ program test_strings_format_string & " F T", & & "Multiple formatters for logical values") ! Wrong demonstration - call check_formatter(format_string(.false., '(I5)'), "*", & - & "Integer formatter for logical value", partial=.true.) + call check_formatter(format_string(.false., '(1x)'), "*", & + & "Invalid formatter for logical value", partial=.true.) contains subroutine check_formatter(actual, expected, description, partial) From e7ce1e77487452af62e8cfd569b6a96f8b0e267e Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Sun, 27 Jun 2021 11:29:56 +0800 Subject: [PATCH 09/19] update format_string example in stdlib_string.md(doc) --- doc/specs/stdlib_strings.md | 150 +++++------------------------------- 1 file changed, 19 insertions(+), 131 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 7dd154be1..5021aef15 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -363,140 +363,28 @@ The result is a allocatable length Character scalar. #### Example ```fortran -program test_strings_format_string - use stdlib_strings, only: format_string, starts_with - use stdlib_error, only: check - use stdlib_optval, only: optval +program demo_strings_format_string + use, non_intrinsic :: stdlib_strings, only: format_string implicit none print *, 'format_string(complex) : ' - call check_formatter(format_string((1, 1)), "(1.0", & - & "Default formatter for complex number", partial=.true.) - call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & - & "Formatter for complex number") - call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & - & "Formatter for negative complex number") - call check_formatter(format_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & - & "Formatter with sign control descriptor for complex number") - call check_formatter(format_string((1, 1), '(F6.2)')//format_string((2, 2), '(F7.3)'), & - & "( 1.00, 1.00)( 2.000, 2.000)", & - & "Multiple formatters for complex numbers") + print *, format_string((1, 1)) ! (1.00000000,1.00000000) + print *, format_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) + print *, format_string((1000, 1), '(ES0.2)'), format_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) + !! Too narrow formatter for real number + !! Normal demonstration(`******` from Fortran Standard) print *, 'format_string(integer) : ' - call check_formatter(format_string(100), "100", & - & "Default formatter for integer number") - call check_formatter(format_string(100, '(I6)'), " 100", & - & "Formatter for integer number") - call check_formatter(format_string(100, '(I0.6)'), "000100", & - & "Formatter with zero padding for integer number") - call check_formatter(format_string(100, '(I6)')//format_string(1000, '(I7)'), & - & " 100 1000", & - & "Multiple formatters for integers") - call check_formatter(format_string(34, '(B8)'), " 100010", & - & "Binary formatter for integer number") - call check_formatter(format_string(34, '(O0.3)'), "042", & - & "Octal formatter with zero padding for integer number") - call check_formatter(format_string(34, '(Z3)'), " 22", & - & "Hexadecimal formatter for integer number") + print *, format_string(1) ! 1 + print *, format_string(1, '(I4)') ! 1 + print *, format_string(1, '(I0.4)'), format_string(2, '(B4)') ! 0001 10 print *, 'format_string(real) : ' - call check_formatter(format_string(100.), "100.0", & - & "Default formatter for real number", partial=.true.) - call check_formatter(format_string(100., '(F6.2)'), "100.00", & - & "Formatter for real number") - call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & - & "Exponential formatter with rounding for real number") - call check_formatter(format_string(128., '(ES8.2)'), "1.28E+02", & - & "Exponential formatter for real number") - ! Wrong demonstration - call check_formatter(format_string(-100., '(F6.2)'), "*", & - & "Too narrow formatter for signed real number", partial=.true.) - call check_formatter(format_string(1000., '(F6.3)'), "*", & - & "Too narrow formatter for real number", partial=.true.) - call check_formatter(format_string(1000., '(7.3)'), "*", & - & "Invalid formatter for real number", partial=.true.) + print *, format_string(1.) ! 1.00000000 + print *, format_string(1., '(F6.2)') ! 1.00 + print *, format_string(1., '(SP,ES9.2)'), format_string(1, '(F7.3)') ! +1.00E+00* + !! 1 wrong demonstration(`*` from `format_string`) print *, 'format_string(logical) : ' - call check_formatter(format_string(.true.), "T", & - & "Default formatter for logcal value") - call check_formatter(format_string(.true., '(L2)'), " T", & - & "Formatter for logical value") - call check_formatter(format_string(.false., '(L2)')//format_string(.true., '(L5)'), & - & " F T", & - & "Multiple formatters for logical values") - ! Wrong demonstration - call check_formatter(format_string(.false., '(I5)'), "*", & - & "Integer formatter for logical value", partial=.true.) - -contains - subroutine check_formatter(actual, expected, description, partial) - character(len=*), intent(in) :: actual, expected, description - logical, intent(in), optional :: partial - logical :: stat - character(len=:), allocatable :: msg - - if (optval(partial, .false.)) then - stat = starts_with(actual, expected) - else - stat = actual == expected - end if - if (.not.stat) then - msg = description // new_line("a") // & - & "Expected: '"//expected//"' but got '"//actual//"'" - else - print '(" - ", a, /, " Result: ''", a, "''")', description, actual - end if - call check(stat, msg) - end subroutine check_formatter -end program test_strings_format_string -``` -**Results** -```fortran - format_string(complex) : - - Default formatter for complex number - Result: '(1.00000000,1.00000000)' !! Different compilers have different widths here. - !! [link](https://github.com/fortran-lang/stdlib/pull/444#issuecomment-868965643) - - Formatter for complex number - Result: '( 1.00, 1.00)' - - Formatter for negative complex number - Result: '( -1.00, -1.00)' - - Formatter with sign control descriptor for complex number - Result: '( +1.00, +1.00)' - - Multiple formatters for complex numbers - Result: '( 1.00, 1.00)( 2.000, 2.000)' - format_string(integer) : - - Default formatter for integer number - Result: '100' - - Formatter for integer number - Result: ' 100' - - Formatter with zero padding for integer number - Result: '000100' - - Multiple formatters for integers - Result: ' 100 1000' - - Binary formatter for integer number - Result: ' 100010' - - Octal formatter with zero padding for integer number - Result: '042' - - Hexadecimal formatter for integer number - Result: ' 22' - format_string(real) : - - Default formatter for real number - Result: '100.000000' !! Ditto - - Formatter for real number - Result: '100.00' - - Exponential formatter with rounding for real number - Result: '.29E+03' - - Exponential formatter for real number - Result: '1.28E+02' - - Too narrow formatter for signed real number - Result: '******' - - Too narrow formatter for real number - Result: '******' - - Invalid formatter for real number - Result: '*' - format_string(logical) : - - Default formatter for logcal value - Result: 'T' - - Formatter for logical value - Result: ' T' - - Multiple formatters for logical values - Result: ' F T' - - Integer formatter for logical value - Result: '*' + print *, format_string(.true.) ! T + print *, format_string(.true., '(L2)') ! T + print *, format_string(.true., 'L2'), format_string(.false., '(I5)') ! ** + !! 2 wrong demonstrations(`*` from `format_string`) +end program demo_strings_format_string ``` \ No newline at end of file From 65ab05d7ffb476996e44a2a0d264c53f10930294 Mon Sep 17 00:00:00 2001 From: Aman-Godara Date: Sun, 27 Jun 2021 16:58:53 +0530 Subject: [PATCH 10/19] improved aesthetics to make code consistent with stdlib's format --- src/stdlib_strings.fypp | 15 +++++++------- src/stdlib_strings_format_string.fypp | 28 +++++++++++++++------------ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/stdlib_strings.fypp b/src/stdlib_strings.fypp index 6432d45df..e9bd7a1db 100644 --- a/src/stdlib_strings.fypp +++ b/src/stdlib_strings.fypp @@ -18,17 +18,18 @@ module stdlib_strings public :: starts_with, ends_with public :: slice, find + !> Format other types as character sequence. + !> ([Specification](../page/specs/stdlib_strings.html#description)) + !> version: experimental interface format_string - !! version: experimental - !! - !! Format other types as character sequence. - !! ([Specification](../page/specs/stdlib_strings.html#description)) #:for kind, type in KINDS_TYPES - pure module function format_string_${type[0]}$${kind}$(val, fmt) result(string) - character(len=:), allocatable :: string + !> Format ${type}$ variable as character sequence + pure module function format_string_${type[0]}$_${kind}$(val, fmt) result(string) ${type}$, intent(in) :: val character(len=*), intent(in), optional :: fmt - end function format_string_${type[0]}$${kind}$ + character(len=:), allocatable :: string + end function format_string_${type[0]}$_${kind}$ + #:endfor end interface format_string diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_strings_format_string.fypp index 5486acfbe..702ad9fe5 100644 --- a/src/stdlib_strings_format_string.fypp +++ b/src/stdlib_strings_format_string.fypp @@ -7,9 +7,11 @@ submodule (stdlib_strings) stdlib_strings_format_string contains - #:for kind, type in RIL_KINDS_TYPES - module procedure format_string_${type[0]}$${kind}$ - !! Format ${type}$ variable as character sequence + + #:for kind, type in RIL_KINDS_TYPES + !> Format ${type}$ variable as character sequence + module procedure format_string_${type[0]}$_${kind}$ + character(len=buffer_len) :: buffer integer :: stat @@ -21,17 +23,19 @@ contains !!\TODO: *? end if - end procedure format_string_${type[0]}$${kind}$ - #:endfor + end procedure format_string_${type[0]}$_${kind}$ + + #:endfor + + #:for kind, type in CMPLX_KINDS_TYPES + !> Format ${type}$ variable as character sequence + module procedure format_string_${type[0]}$_${kind}$ - #:for kind, type in CMPLX_KINDS_TYPES - module procedure format_string_${type[0]}$${kind}$ - !! Format ${type}$ variable as character sequence + string = '(' // format_string_r_${kind}$(val%re, fmt) // ',' // & + & format_string_r_${kind}$(val%im, fmt) // ')' - string = '('//format_string_r${kind}$(val%re, fmt)//','// & - format_string_r${kind}$(val%im, fmt)//')' + end procedure format_string_${type[0]}$_${kind}$ - end procedure format_string_${type[0]}$${kind}$ - #:endfor + #:endfor end submodule stdlib_strings_format_string From 34fa3cd9b0d0cb6398fa9251f0409a56d180e5e1 Mon Sep 17 00:00:00 2001 From: Aman-Godara Date: Mon, 28 Jun 2021 19:11:10 +0530 Subject: [PATCH 11/19] improved aesthetics of test file and documentation --- doc/specs/stdlib_strings.md | 15 ++- src/stdlib_strings.fypp | 2 +- src/tests/string/CMakeLists.txt | 2 +- src/tests/string/Makefile.manual | 2 +- .../string/test_string_format_string.f90 | 113 ++++++++++++++++++ .../string/test_strings_format_string.f90 | 82 ------------- 6 files changed, 125 insertions(+), 91 deletions(-) create mode 100644 src/tests/string/test_string_format_string.f90 delete mode 100644 src/tests/string/test_strings_format_string.f90 diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 5021aef15..ae348c222 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -283,7 +283,6 @@ Default value of `occurrence` is set to `1`. If `consider_overlapping` is not provided or is set to `.true.` the function counts two overlapping occurrences of substring as two different occurrences. If `occurrence`th occurrence is not found, function returns `0`. - #### Syntax `string = [[stdlib_strings(module):find(interface)]] (string, pattern [, occurrence, consider_overlapping])` @@ -336,10 +335,9 @@ end program demo_find Format or transfer a integer/real/complex/logical variable as a character sequence. - #### Syntax -`format_string = [[stdlib_strings(module):format_string(interface)]] (value [, format])` +`format_string = [[stdlib_strings(module):format_string(interface)]] (val [, fmt])` #### Status @@ -358,33 +356,38 @@ Pure function #### Result value -The result is a allocatable length Character scalar. +The result is an allocatable length Character scalar. #### Example ```fortran -program demo_strings_format_string +program demo_format_string use, non_intrinsic :: stdlib_strings, only: format_string implicit none + print *, 'format_string(complex) : ' print *, format_string((1, 1)) ! (1.00000000,1.00000000) print *, format_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) print *, format_string((1000, 1), '(ES0.2)'), format_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) !! Too narrow formatter for real number !! Normal demonstration(`******` from Fortran Standard) + print *, 'format_string(integer) : ' print *, format_string(1) ! 1 print *, format_string(1, '(I4)') ! 1 print *, format_string(1, '(I0.4)'), format_string(2, '(B4)') ! 0001 10 + print *, 'format_string(real) : ' print *, format_string(1.) ! 1.00000000 print *, format_string(1., '(F6.2)') ! 1.00 print *, format_string(1., '(SP,ES9.2)'), format_string(1, '(F7.3)') ! +1.00E+00* !! 1 wrong demonstration(`*` from `format_string`) + print *, 'format_string(logical) : ' print *, format_string(.true.) ! T print *, format_string(.true., '(L2)') ! T print *, format_string(.true., 'L2'), format_string(.false., '(I5)') ! ** !! 2 wrong demonstrations(`*` from `format_string`) -end program demo_strings_format_string + +end program demo_format_string ``` \ No newline at end of file diff --git a/src/stdlib_strings.fypp b/src/stdlib_strings.fypp index e9bd7a1db..893a8a224 100644 --- a/src/stdlib_strings.fypp +++ b/src/stdlib_strings.fypp @@ -20,7 +20,7 @@ module stdlib_strings !> Format other types as character sequence. !> ([Specification](../page/specs/stdlib_strings.html#description)) - !> version: experimental + !> Version: experimental interface format_string #:for kind, type in KINDS_TYPES !> Format ${type}$ variable as character sequence diff --git a/src/tests/string/CMakeLists.txt b/src/tests/string/CMakeLists.txt index 4e5db15e2..13ae0baab 100644 --- a/src/tests/string/CMakeLists.txt +++ b/src/tests/string/CMakeLists.txt @@ -5,4 +5,4 @@ ADDTEST(string_match) ADDTEST(string_derivedtype_io) ADDTEST(string_functions) ADDTEST(string_strip_chomp) -ADDTEST(strings_format_string) +ADDTEST(string_format_string) diff --git a/src/tests/string/Makefile.manual b/src/tests/string/Makefile.manual index 7ab5e5765..80dacbd0b 100644 --- a/src/tests/string/Makefile.manual +++ b/src/tests/string/Makefile.manual @@ -5,7 +5,7 @@ PROGS_SRC = test_string_assignment.f90 \ test_string_match.f90 \ test_string_operator.f90 \ test_string_strip_chomp.f90 \ - test_strings_format_string.f90 + test_string_format_string.f90 include ../Makefile.manual.test.mk diff --git a/src/tests/string/test_string_format_string.f90 b/src/tests/string/test_string_format_string.f90 new file mode 100644 index 000000000..d5d43b313 --- /dev/null +++ b/src/tests/string/test_string_format_string.f90 @@ -0,0 +1,113 @@ +! SPDX-Identifier: MIT +module test_string_format_string + use stdlib_strings, only: format_string, starts_with + use stdlib_error, only: check + use stdlib_optval, only: optval + implicit none + +contains + + + subroutine check_formatter(actual, expected, description, partial) + character(len=*), intent(in) :: actual, expected, description + logical, intent(in), optional :: partial + logical :: stat + character(len=:), allocatable :: msg + + if (optval(partial, .false.)) then + stat = starts_with(actual, expected) + else + stat = actual == expected + end if + + if (.not. stat) then + msg = description // new_line("a") // & + & "Expected: '" // expected // "' but got '" // actual // "'" + else + print '(" - ", a, /, " Result: ''", a, "''")', description, actual + end if + + call check(stat, msg) + + end subroutine check_formatter + + subroutine test_format_string_complex + call check_formatter(format_string((1, 1)), "(1.0", & + & "Default formatter for complex number", partial=.true.) + call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & + & "Formatter for complex number") + call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & + & "Formatter for negative complex number") + call check_formatter(format_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & + & "Formatter with sign control descriptor for complex number") + call check_formatter(format_string((1, 1), '(F6.2)') // format_string((2, 2), '(F7.3)'), & + & "( 1.00, 1.00)( 2.000, 2.000)", & + & "Multiple formatters for complex numbers") + + end subroutine test_format_string_complex + + subroutine test_format_string_integer + call check_formatter(format_string(100), "100", & + & "Default formatter for integer number") + call check_formatter(format_string(100, '(I6)'), " 100", & + & "Formatter for integer number") + call check_formatter(format_string(100, '(I0.6)'), "000100", & + & "Formatter with zero padding for integer number") + call check_formatter(format_string(100, '(I6)') // format_string(1000, '(I7)'), & + & " 100 1000", "Multiple formatters for integers") + call check_formatter(format_string(34, '(B8)'), " 100010", & + & "Binary formatter for integer number") + call check_formatter(format_string(34, '(O0.3)'), "042", & + & "Octal formatter with zero padding for integer number") + call check_formatter(format_string(34, '(Z3)'), " 22", & + & "Hexadecimal formatter for integer number") + + end subroutine test_format_string_integer + + subroutine test_format_string_real + call check_formatter(format_string(100.), "100.0", & + & "Default formatter for real number", partial=.true.) + call check_formatter(format_string(100., '(F6.2)'), "100.00", & + & "Formatter for real number") + call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & + & "Exponential formatter with rounding for real number") + call check_formatter(format_string(128., '(ES8.2)'), "1.28E+02", & + & "Exponential formatter for real number") + + ! Wrong demonstration + call check_formatter(format_string(-100., '(F6.2)'), "*", & + & "Too narrow formatter for signed real number", partial=.true.) + call check_formatter(format_string(1000., '(F6.3)'), "*", & + & "Too narrow formatter for real number", partial=.true.) + call check_formatter(format_string(1000., '(7.3)'), "*", & + & "Invalid formatter for real number", partial=.true.) + + end subroutine test_format_string_real + + subroutine test_format_string_logical + call check_formatter(format_string(.true.), "T", & + & "Default formatter for logcal value") + call check_formatter(format_string(.true., '(L2)'), " T", & + & "Formatter for logical value") + call check_formatter(format_string(.false., '(L2)') // format_string(.true., '(L5)'), & + & " F T", "Multiple formatters for logical values") + + ! Wrong demonstration + call check_formatter(format_string(.false., '(1x)'), "*", & + & "Invalid formatter for logical value", partial=.true.) + + end subroutine test_format_string_logical + + +end module test_string_format_string + +program tester + use test_string_format_string + implicit none + + call test_format_string_complex + call test_format_string_integer + call test_format_string_logical + call test_format_string_real + +end program tester diff --git a/src/tests/string/test_strings_format_string.f90 b/src/tests/string/test_strings_format_string.f90 deleted file mode 100644 index 9f978f45a..000000000 --- a/src/tests/string/test_strings_format_string.f90 +++ /dev/null @@ -1,82 +0,0 @@ -program test_strings_format_string - use stdlib_strings, only: format_string, starts_with - use stdlib_error, only: check - use stdlib_optval, only: optval - implicit none - print *, 'format_string(complex) : ' - call check_formatter(format_string((1, 1)), "(1.0", & - & "Default formatter for complex number", partial=.true.) - call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & - & "Formatter for complex number") - call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & - & "Formatter for negative complex number") - call check_formatter(format_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & - & "Formatter with sign control descriptor for complex number") - call check_formatter(format_string((1, 1), '(F6.2)')//format_string((2, 2), '(F7.3)'), & - & "( 1.00, 1.00)( 2.000, 2.000)", & - & "Multiple formatters for complex numbers") - print *, 'format_string(integer) : ' - call check_formatter(format_string(100), "100", & - & "Default formatter for integer number") - call check_formatter(format_string(100, '(I6)'), " 100", & - & "Formatter for integer number") - call check_formatter(format_string(100, '(I0.6)'), "000100", & - & "Formatter with zero padding for integer number") - call check_formatter(format_string(100, '(I6)')//format_string(1000, '(I7)'), & - & " 100 1000", & - & "Multiple formatters for integers") - call check_formatter(format_string(34, '(B8)'), " 100010", & - & "Binary formatter for integer number") - call check_formatter(format_string(34, '(O0.3)'), "042", & - & "Octal formatter with zero padding for integer number") - call check_formatter(format_string(34, '(Z3)'), " 22", & - & "Hexadecimal formatter for integer number") - print *, 'format_string(real) : ' - call check_formatter(format_string(100.), "100.0", & - & "Default formatter for real number", partial=.true.) - call check_formatter(format_string(100., '(F6.2)'), "100.00", & - & "Formatter for real number") - call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & - & "Exponential formatter with rounding for real number") - call check_formatter(format_string(128., '(ES8.2)'), "1.28E+02", & - & "Exponential formatter for real number") - ! Wrong demonstration - call check_formatter(format_string(-100., '(F6.2)'), "*", & - & "Too narrow formatter for signed real number", partial=.true.) - call check_formatter(format_string(1000., '(F6.3)'), "*", & - & "Too narrow formatter for real number", partial=.true.) - call check_formatter(format_string(1000., '(7.3)'), "*", & - & "Invalid formatter for real number", partial=.true.) - print *, 'format_string(logical) : ' - call check_formatter(format_string(.true.), "T", & - & "Default formatter for logcal value") - call check_formatter(format_string(.true., '(L2)'), " T", & - & "Formatter for logical value") - call check_formatter(format_string(.false., '(L2)')//format_string(.true., '(L5)'), & - & " F T", & - & "Multiple formatters for logical values") - ! Wrong demonstration - call check_formatter(format_string(.false., '(1x)'), "*", & - & "Invalid formatter for logical value", partial=.true.) - -contains - subroutine check_formatter(actual, expected, description, partial) - character(len=*), intent(in) :: actual, expected, description - logical, intent(in), optional :: partial - logical :: stat - character(len=:), allocatable :: msg - - if (optval(partial, .false.)) then - stat = starts_with(actual, expected) - else - stat = actual == expected - end if - if (.not.stat) then - msg = description // new_line("a") // & - & "Expected: '"//expected//"' but got '"//actual//"'" - else - print '(" - ", a, /, " Result: ''", a, "''")', description, actual - end if - call check(stat, msg) - end subroutine check_formatter -end program test_strings_format_string From 1fe6a07ffd2bf450ec7e6e3570ebf203f5a8a7e3 Mon Sep 17 00:00:00 2001 From: Aman-Godara Date: Thu, 1 Jul 2021 12:56:46 +0530 Subject: [PATCH 12/19] renamed strings_format_string to string_format_string --- src/CMakeLists.txt | 2 +- src/Makefile.manual | 4 ++-- ...gs_format_string.fypp => stdlib_string_format_string.fypp} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename src/{stdlib_strings_format_string.fypp => stdlib_string_format_string.fypp} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 88bf89c56..c7843e8c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,7 @@ set(fppFiles stdlib_stats_distribution_PRNG.fypp stdlib_math.fypp stdlib_string_type.fypp - stdlib_strings_format_string.fypp + stdlib_string_format_string.fypp stdlib_strings.fypp ) diff --git a/src/Makefile.manual b/src/Makefile.manual index c10eddccb..2c4109acc 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -27,7 +27,7 @@ SRCFYPP =\ stdlib_stats_distribution_PRNG.fypp \ stdlib_string_type.fypp \ stdlib_strings.fypp \ - stdlib_strings_format_string.fypp + stdlib_string_format_string.fypp SRC = f18estop.f90 \ stdlib_error.f90 \ @@ -133,4 +133,4 @@ stdlib_strings.o: stdlib_ascii.o \ stdlib_optval.o \ stdlib_kinds.o stdlib_math.o: stdlib_kinds.o -stdlib_strings_format_string.o: stdlib_strings.o +stdlib_string_format_string.o: stdlib_strings.o diff --git a/src/stdlib_strings_format_string.fypp b/src/stdlib_string_format_string.fypp similarity index 100% rename from src/stdlib_strings_format_string.fypp rename to src/stdlib_string_format_string.fypp From 835de22a1435958039a708d3f1f713cf45f36848 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Sat, 3 Jul 2021 14:59:54 +0200 Subject: [PATCH 13/19] Fix manual Makefile build --- src/Makefile.manual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.manual b/src/Makefile.manual index 6a00706d5..810154e21 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -37,7 +37,7 @@ SRC = f18estop.f90 \ stdlib_io.f90 \ stdlib_kinds.f90 \ stdlib_logger.f90 \ - stdlib_quadrature_gauss.f90 + stdlib_quadrature_gauss.f90 \ $(SRCGEN) LIB = libstdlib.a From b05cbae3096f484e0d1416403c93b2c089a3552c Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Mon, 5 Jul 2021 10:36:34 +0800 Subject: [PATCH 14/19] rename `format_string` to `format_to_string`; rename `format_to_string` input arg: val -> value; fmt -> format. modify `format_to_string` failure output string as `[*]`; update `format_to_string` doc. --- doc/specs/stdlib_strings.md | 59 +++++++------- src/CMakeLists.txt | 2 +- src/Makefile.manual | 7 +- src/stdlib_string_format_string.fypp | 41 ---------- src/stdlib_string_format_to_string.fypp | 41 ++++++++++ src/stdlib_strings.fypp | 19 ++--- src/tests/string/CMakeLists.txt | 2 +- src/tests/string/Makefile.manual | 2 +- ...g.f90 => test_string_format_to_string.f90} | 78 +++++++++---------- 9 files changed, 127 insertions(+), 124 deletions(-) delete mode 100644 src/stdlib_string_format_string.fypp create mode 100644 src/stdlib_string_format_to_string.fypp rename src/tests/string/{test_string_format_string.f90 => test_string_format_to_string.f90} (50%) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index ae348c222..4b096fd82 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -329,15 +329,16 @@ end program demo_find ``` -### `format_string` +### `format_to_string` #### Description -Format or transfer a integer/real/complex/logical variable as a character sequence. +Format or transfer a `integer/real/complex/logical` scalar as a string. +Input a wrong `format` that cause the internal-IO to fail, the result value is a string of `[*]`. #### Syntax -`format_string = [[stdlib_strings(module):format_string(interface)]] (val [, fmt])` +`format_to_string = [[stdlib_strings(module):format_to_string(interface)]] (value [, format])` #### Status @@ -349,45 +350,45 @@ Pure function #### Argument -- `value`: Integer/real/complex/logical scalar. - This argument is intent(in). -- `format`: Character scalar like `'(F6.2)'`. - This argument is intent(in) and optional. +- `value`: Shall be an `integer/real/complex/logical` scalar. + This is an `intent(in)` argument. +- `format`: Shall be a `character` scalar like `'(F6.2)'`. + This is an `intent(in)` and `optional` argument. #### Result value -The result is an allocatable length Character scalar. +The result is an allocatable length `character` scalar with up to 512 `character` length. #### Example ```fortran -program demo_format_string - use, non_intrinsic :: stdlib_strings, only: format_string +program demo_format_to_string + use :: stdlib_strings, only: format_to_string implicit none - print *, 'format_string(complex) : ' - print *, format_string((1, 1)) ! (1.00000000,1.00000000) - print *, format_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) - print *, format_string((1000, 1), '(ES0.2)'), format_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) + print *, 'format_to_string(complex) : ' + print *, format_to_string((1, 1)) ! (1.00000000,1.00000000) + print *, format_to_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) + print *, format_to_string((1000, 1), '(ES0.2)'), format_to_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) !! Too narrow formatter for real number !! Normal demonstration(`******` from Fortran Standard) - print *, 'format_string(integer) : ' - print *, format_string(1) ! 1 - print *, format_string(1, '(I4)') ! 1 - print *, format_string(1, '(I0.4)'), format_string(2, '(B4)') ! 0001 10 + print *, 'format_to_string(integer) : ' + print *, format_to_string(1) ! 1 + print *, format_to_string(1, '(I4)') ! 1 + print *, format_to_string(1, '(I0.4)'), format_to_string(2, '(B4)') ! 0001 10 - print *, 'format_string(real) : ' - print *, format_string(1.) ! 1.00000000 - print *, format_string(1., '(F6.2)') ! 1.00 - print *, format_string(1., '(SP,ES9.2)'), format_string(1, '(F7.3)') ! +1.00E+00* - !! 1 wrong demonstration(`*` from `format_string`) + print *, 'format_to_string(real) : ' + print *, format_to_string(1.) ! 1.00000000 + print *, format_to_string(1., '(F6.2)') ! 1.00 + print *, format_to_string(1., '(SP,ES9.2)'), format_to_string(1, '(F7.3)') ! +1.00E+00[*] + !! 1 wrong demonstration(`*` from `format_to_string`) - print *, 'format_string(logical) : ' - print *, format_string(.true.) ! T - print *, format_string(.true., '(L2)') ! T - print *, format_string(.true., 'L2'), format_string(.false., '(I5)') ! ** - !! 2 wrong demonstrations(`*` from `format_string`) + print *, 'format_to_string(logical) : ' + print *, format_to_string(.true.) ! T + print *, format_to_string(.true., '(L2)') ! T + print *, format_to_string(.true., 'L2'), format_to_string(.false., '(I5)') ! [*][*] + !! 2 wrong demonstrations(`*` from `format_to_string`) -end program demo_format_string +end program demo_format_to_string ``` \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ccb312a4..b9ef06c10 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,7 +30,7 @@ set(fppFiles stdlib_stats_distribution_PRNG.fypp stdlib_math.fypp stdlib_string_type.fypp - stdlib_string_format_string.fypp + stdlib_string_format_to_string.fypp stdlib_strings.fypp ) diff --git a/src/Makefile.manual b/src/Makefile.manual index 810154e21..01522faa1 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -28,7 +28,7 @@ SRCFYPP =\ stdlib_stats_distribution_PRNG.fypp \ stdlib_string_type.fypp \ stdlib_strings.fypp \ - stdlib_string_format_string.fypp + stdlib_string_format_to_string.fypp SRC = f18estop.f90 \ stdlib_error.f90 \ @@ -76,7 +76,8 @@ stdlib_specialfunctions_legendre.o: stdlib_kinds.o stdlib_specialfunctions.o stdlib_io.o: \ stdlib_error.o \ stdlib_optval.o \ - stdlib_kinds.o + stdlib_kinds.o \ + stdlib_ascii.o stdlib_linalg.o: \ stdlib_kinds.o stdlib_linalg_diag.o: \ @@ -143,5 +144,5 @@ stdlib_strings.o: stdlib_ascii.o \ stdlib_optval.o \ stdlib_kinds.o stdlib_math.o: stdlib_kinds.o -stdlib_string_format_string.o: stdlib_strings.o +stdlib_string_format_to_string.o: stdlib_strings.o stdlib_linalg_outer_product.o: stdlib_linalg.o diff --git a/src/stdlib_string_format_string.fypp b/src/stdlib_string_format_string.fypp deleted file mode 100644 index 702ad9fe5..000000000 --- a/src/stdlib_string_format_string.fypp +++ /dev/null @@ -1,41 +0,0 @@ -#:include "common.fypp" -#:set RIL_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES -submodule (stdlib_strings) stdlib_strings_format_string - - implicit none - integer, parameter :: buffer_len = 512 - -contains - - - #:for kind, type in RIL_KINDS_TYPES - !> Format ${type}$ variable as character sequence - module procedure format_string_${type[0]}$_${kind}$ - - character(len=buffer_len) :: buffer - integer :: stat - - write(buffer, optval(fmt, "(g0)"), iostat=stat) val - if (stat == 0) then - string = trim(buffer) - else - string = '*' - !!\TODO: *? - end if - - end procedure format_string_${type[0]}$_${kind}$ - - #:endfor - - #:for kind, type in CMPLX_KINDS_TYPES - !> Format ${type}$ variable as character sequence - module procedure format_string_${type[0]}$_${kind}$ - - string = '(' // format_string_r_${kind}$(val%re, fmt) // ',' // & - & format_string_r_${kind}$(val%im, fmt) // ')' - - end procedure format_string_${type[0]}$_${kind}$ - - #:endfor - -end submodule stdlib_strings_format_string diff --git a/src/stdlib_string_format_to_string.fypp b/src/stdlib_string_format_to_string.fypp new file mode 100644 index 000000000..c9a656501 --- /dev/null +++ b/src/stdlib_string_format_to_string.fypp @@ -0,0 +1,41 @@ +#:include "common.fypp" +#:set RIL_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES +submodule (stdlib_strings) stdlib_strings_format_to_string + + implicit none + integer, parameter :: buffer_len = 512 + +contains + + + #:for kind, type in RIL_KINDS_TYPES + !> Format or transfer a ${type}$ scalar as a string. + module procedure format_to_string_${type[0]}$_${kind}$ + + character(len=buffer_len) :: buffer + integer :: stat + + write(buffer, optval(format, "(g0)"), iostat=stat) value + if (stat == 0) then + string = trim(buffer) + else + string = '[*]' + !!\TODO: [*]? + end if + + end procedure format_to_string_${type[0]}$_${kind}$ + + #:endfor + + #:for kind, type in CMPLX_KINDS_TYPES + !> Format or transfer a ${type}$ scalar as a string. + module procedure format_to_string_${type[0]}$_${kind}$ + + string = '(' // format_to_string_r_${kind}$(value%re, format) // ',' // & + & format_to_string_r_${kind}$(value%im, format) // ')' + + end procedure format_to_string_${type[0]}$_${kind}$ + + #:endfor + +end submodule stdlib_strings_format_to_string diff --git a/src/stdlib_strings.fypp b/src/stdlib_strings.fypp index 893a8a224..feb00bc26 100644 --- a/src/stdlib_strings.fypp +++ b/src/stdlib_strings.fypp @@ -13,25 +13,26 @@ module stdlib_strings implicit none private - public :: format_string + public :: format_to_string public :: strip, chomp public :: starts_with, ends_with public :: slice, find - !> Format other types as character sequence. + !> Format or transfer other types as a string. !> ([Specification](../page/specs/stdlib_strings.html#description)) + !> !> Version: experimental - interface format_string + interface format_to_string #:for kind, type in KINDS_TYPES - !> Format ${type}$ variable as character sequence - pure module function format_string_${type[0]}$_${kind}$(val, fmt) result(string) - ${type}$, intent(in) :: val - character(len=*), intent(in), optional :: fmt + !> Format or transfer a ${type}$ scalar as a string. + pure module function format_to_string_${type[0]}$_${kind}$(value, format) result(string) + ${type}$, intent(in) :: value + character(len=*), intent(in), optional :: format character(len=:), allocatable :: string - end function format_string_${type[0]}$_${kind}$ + end function format_to_string_${type[0]}$_${kind}$ #:endfor - end interface format_string + end interface format_to_string !> Remove leading and trailing whitespace characters. !> diff --git a/src/tests/string/CMakeLists.txt b/src/tests/string/CMakeLists.txt index 13ae0baab..e767a8216 100644 --- a/src/tests/string/CMakeLists.txt +++ b/src/tests/string/CMakeLists.txt @@ -5,4 +5,4 @@ ADDTEST(string_match) ADDTEST(string_derivedtype_io) ADDTEST(string_functions) ADDTEST(string_strip_chomp) -ADDTEST(string_format_string) +ADDTEST(string_format_to_string) diff --git a/src/tests/string/Makefile.manual b/src/tests/string/Makefile.manual index 80dacbd0b..9288d9328 100644 --- a/src/tests/string/Makefile.manual +++ b/src/tests/string/Makefile.manual @@ -5,7 +5,7 @@ PROGS_SRC = test_string_assignment.f90 \ test_string_match.f90 \ test_string_operator.f90 \ test_string_strip_chomp.f90 \ - test_string_format_string.f90 + test_string_format_to_string.f90 include ../Makefile.manual.test.mk diff --git a/src/tests/string/test_string_format_string.f90 b/src/tests/string/test_string_format_to_string.f90 similarity index 50% rename from src/tests/string/test_string_format_string.f90 rename to src/tests/string/test_string_format_to_string.f90 index d5d43b313..d61af4ae7 100644 --- a/src/tests/string/test_string_format_string.f90 +++ b/src/tests/string/test_string_format_to_string.f90 @@ -1,6 +1,6 @@ ! SPDX-Identifier: MIT -module test_string_format_string - use stdlib_strings, only: format_string, starts_with +module test_string_format_to_string + use stdlib_strings, only: format_to_string, starts_with use stdlib_error, only: check use stdlib_optval, only: optval implicit none @@ -31,83 +31,83 @@ subroutine check_formatter(actual, expected, description, partial) end subroutine check_formatter - subroutine test_format_string_complex - call check_formatter(format_string((1, 1)), "(1.0", & + subroutine test_format_to_string_complex + call check_formatter(format_to_string((1, 1)), "(1.0", & & "Default formatter for complex number", partial=.true.) - call check_formatter(format_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & + call check_formatter(format_to_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & & "Formatter for complex number") - call check_formatter(format_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & + call check_formatter(format_to_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & & "Formatter for negative complex number") - call check_formatter(format_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & + call check_formatter(format_to_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & & "Formatter with sign control descriptor for complex number") - call check_formatter(format_string((1, 1), '(F6.2)') // format_string((2, 2), '(F7.3)'), & + call check_formatter(format_to_string((1, 1), '(F6.2)') // format_to_string((2, 2), '(F7.3)'), & & "( 1.00, 1.00)( 2.000, 2.000)", & & "Multiple formatters for complex numbers") - end subroutine test_format_string_complex + end subroutine test_format_to_string_complex - subroutine test_format_string_integer - call check_formatter(format_string(100), "100", & + subroutine test_format_to_string_integer + call check_formatter(format_to_string(100), "100", & & "Default formatter for integer number") - call check_formatter(format_string(100, '(I6)'), " 100", & + call check_formatter(format_to_string(100, '(I6)'), " 100", & & "Formatter for integer number") - call check_formatter(format_string(100, '(I0.6)'), "000100", & + call check_formatter(format_to_string(100, '(I0.6)'), "000100", & & "Formatter with zero padding for integer number") - call check_formatter(format_string(100, '(I6)') // format_string(1000, '(I7)'), & + call check_formatter(format_to_string(100, '(I6)') // format_to_string(1000, '(I7)'), & & " 100 1000", "Multiple formatters for integers") - call check_formatter(format_string(34, '(B8)'), " 100010", & + call check_formatter(format_to_string(34, '(B8)'), " 100010", & & "Binary formatter for integer number") - call check_formatter(format_string(34, '(O0.3)'), "042", & + call check_formatter(format_to_string(34, '(O0.3)'), "042", & & "Octal formatter with zero padding for integer number") - call check_formatter(format_string(34, '(Z3)'), " 22", & + call check_formatter(format_to_string(34, '(Z3)'), " 22", & & "Hexadecimal formatter for integer number") - end subroutine test_format_string_integer + end subroutine test_format_to_string_integer - subroutine test_format_string_real - call check_formatter(format_string(100.), "100.0", & + subroutine test_format_to_string_real + call check_formatter(format_to_string(100.), "100.0", & & "Default formatter for real number", partial=.true.) - call check_formatter(format_string(100., '(F6.2)'), "100.00", & + call check_formatter(format_to_string(100., '(F6.2)'), "100.00", & & "Formatter for real number") - call check_formatter(format_string(289., '(E7.2)'), ".29E+03", & + call check_formatter(format_to_string(289., '(E7.2)'), ".29E+03", & & "Exponential formatter with rounding for real number") - call check_formatter(format_string(128., '(ES8.2)'), "1.28E+02", & + call check_formatter(format_to_string(128., '(ES8.2)'), "1.28E+02", & & "Exponential formatter for real number") ! Wrong demonstration - call check_formatter(format_string(-100., '(F6.2)'), "*", & + call check_formatter(format_to_string(-100., '(F6.2)'), "*", & & "Too narrow formatter for signed real number", partial=.true.) - call check_formatter(format_string(1000., '(F6.3)'), "*", & + call check_formatter(format_to_string(1000., '(F6.3)'), "*", & & "Too narrow formatter for real number", partial=.true.) - call check_formatter(format_string(1000., '(7.3)'), "*", & + call check_formatter(format_to_string(1000., '(7.3)'), "[*]", & & "Invalid formatter for real number", partial=.true.) - end subroutine test_format_string_real + end subroutine test_format_to_string_real - subroutine test_format_string_logical - call check_formatter(format_string(.true.), "T", & + subroutine test_format_to_string_logical + call check_formatter(format_to_string(.true.), "T", & & "Default formatter for logcal value") - call check_formatter(format_string(.true., '(L2)'), " T", & + call check_formatter(format_to_string(.true., '(L2)'), " T", & & "Formatter for logical value") - call check_formatter(format_string(.false., '(L2)') // format_string(.true., '(L5)'), & + call check_formatter(format_to_string(.false., '(L2)') // format_to_string(.true., '(L5)'), & & " F T", "Multiple formatters for logical values") ! Wrong demonstration - call check_formatter(format_string(.false., '(1x)'), "*", & + call check_formatter(format_to_string(.false., '(1x)'), "[*]", & & "Invalid formatter for logical value", partial=.true.) - end subroutine test_format_string_logical + end subroutine test_format_to_string_logical -end module test_string_format_string +end module test_string_format_to_string program tester - use test_string_format_string + use test_string_format_to_string implicit none - call test_format_string_complex - call test_format_string_integer - call test_format_string_logical - call test_format_string_real + call test_format_to_string_complex + call test_format_to_string_integer + call test_format_to_string_logical + call test_format_to_string_real end program tester From c71772488b8d78518a338322c62dc5d67109d8e2 Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Tue, 3 Aug 2021 22:53:40 +0800 Subject: [PATCH 15/19] Merge `stdlib_ascii(module):to_string(interface)` to `stdlib_strings(module):format_to_string(interface)`. #### Works 1. merge `to_string` and `format_to_string` to `stdlib_strings`, make the name `to_string` 2. redrect the used keyword `to_string` in `stdlib_string_type` 3. add `stdlib_string_type_constructor.fypp` to avoid the ring dependence in `stdlib_strings` and `stdlib_string_type`. 4. other clean works. --- doc/specs/stdlib_ascii.md | 40 +----- doc/specs/stdlib_strings.md | 50 +++---- src/CMakeLists.txt | 3 +- src/Makefile.manual | 8 +- src/stdlib_ascii.fypp | 60 --------- src/stdlib_string_format_to_string.fypp | 41 ------ src/stdlib_string_to_string.fypp | 124 ++++++++++++++++++ src/stdlib_string_type.fypp | 63 +++------ src/stdlib_string_type_constructor.fypp | 35 +++++ src/stdlib_strings.fypp | 37 ++++-- src/tests/ascii/test_ascii.f90 | 48 +------ src/tests/string/CMakeLists.txt | 2 +- src/tests/string/Makefile.manual | 2 +- .../string/test_string_format_to_string.f90 | 113 ---------------- src/tests/string/test_string_functions.f90 | 2 +- src/tests/string/test_string_to_string.f90 | 112 ++++++++++++++++ 16 files changed, 353 insertions(+), 387 deletions(-) delete mode 100644 src/stdlib_string_format_to_string.fypp create mode 100644 src/stdlib_string_to_string.fypp create mode 100644 src/stdlib_string_type_constructor.fypp delete mode 100644 src/tests/string/test_string_format_to_string.f90 create mode 100644 src/tests/string/test_string_to_string.f90 diff --git a/doc/specs/stdlib_ascii.md b/doc/specs/stdlib_ascii.md index d80ae62ea..518a9eb7e 100644 --- a/doc/specs/stdlib_ascii.md +++ b/doc/specs/stdlib_ascii.md @@ -212,42 +212,4 @@ program demo_reverse implicit none print'(a)', reverse("Hello, World!") ! returns "!dlroW ,olleH" end program demo_reverse -``` - -### `to_string` - -#### Status - -Experimental - -#### Description - -Create a character string representing the value of the provided variable. - -#### Syntax - -`res = [[stdlib_ascii(module):to_string(interface)]] (string)` - -#### Class - -Pure function. - -#### Argument - -`val`: shall be an intrinsic integer or logical type. It is an `intent(in)` argument. - -#### Result value - -The result is an intrinsic character type. - -#### Example - -```fortran -program demo_string_value - use stdlib_ascii, only : to_string - implicit none - print'(a)', to_string(-3) ! returns "-3" - print'(a)', to_string(.true.) ! returns "T" - print'(a)', to_string(42) ! returns "42" -end program demo_string_value -``` +``` \ No newline at end of file diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 4a3e30dec..691f25fb8 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -436,7 +436,7 @@ end program demo_count ``` -### `format_to_string` +### `to_string` #### Description @@ -445,7 +445,7 @@ Input a wrong `format` that cause the internal-IO to fail, the result value is a #### Syntax -`format_to_string = [[stdlib_strings(module):format_to_string(interface)]] (value [, format])` +`string = [[stdlib_strings(module):to_string(interface)]] (value [, format])` #### Status @@ -453,7 +453,7 @@ Experimental #### Class -Pure function +Pure function. #### Argument @@ -464,38 +464,38 @@ Pure function #### Result value -The result is an allocatable length `character` scalar with up to 512 `character` length. +The result is an allocatable length `character` scalar with up to 128 cached `character` length. #### Example ```fortran -program demo_format_to_string - use :: stdlib_strings, only: format_to_string +program demo_to_string + use :: stdlib_strings, only: to_string implicit none - print *, 'format_to_string(complex) : ' - print *, format_to_string((1, 1)) ! (1.00000000,1.00000000) - print *, format_to_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) - print *, format_to_string((1000, 1), '(ES0.2)'), format_to_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) + print *, 'to_string(complex) : ' + print *, to_string((1, 1)) ! (1.00000000,1.00000000) + print *, to_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) + print *, to_string((1000, 1), '(ES0.2)'), to_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) !! Too narrow formatter for real number !! Normal demonstration(`******` from Fortran Standard) - print *, 'format_to_string(integer) : ' - print *, format_to_string(1) ! 1 - print *, format_to_string(1, '(I4)') ! 1 - print *, format_to_string(1, '(I0.4)'), format_to_string(2, '(B4)') ! 0001 10 + print *, 'to_string(integer) : ' + print *, to_string(1) ! 1 + print *, to_string(1, '(I4)') ! 1 + print *, to_string(1, '(I0.4)'), to_string(2, '(B4)') ! 0001 10 - print *, 'format_to_string(real) : ' - print *, format_to_string(1.) ! 1.00000000 - print *, format_to_string(1., '(F6.2)') ! 1.00 - print *, format_to_string(1., '(SP,ES9.2)'), format_to_string(1, '(F7.3)') ! +1.00E+00[*] - !! 1 wrong demonstration(`*` from `format_to_string`) + print *, 'to_string(real) : ' + print *, to_string(1.) ! 1.00000000 + print *, to_string(1., '(F6.2)') ! 1.00 + print *, to_string(1., '(SP,ES9.2)'), to_string(1, '(F7.3)') ! +1.00E+00[*] + !! 1 wrong demonstration(`*` from `to_string`) - print *, 'format_to_string(logical) : ' - print *, format_to_string(.true.) ! T - print *, format_to_string(.true., '(L2)') ! T - print *, format_to_string(.true., 'L2'), format_to_string(.false., '(I5)') ! [*][*] - !! 2 wrong demonstrations(`*` from `format_to_string`) + print *, 'to_string(logical) : ' + print *, to_string(.true.) ! T + print *, to_string(.true., '(L2)') ! T + print *, to_string(.true., 'L2'), to_string(.false., '(I5)') ! [*][*] + !! 2 wrong demonstrations(`*` from `to_string`) -end program demo_format_to_string +end program demo_to_string ``` \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9ef06c10..9d5ac7cf6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,7 +30,8 @@ set(fppFiles stdlib_stats_distribution_PRNG.fypp stdlib_math.fypp stdlib_string_type.fypp - stdlib_string_format_to_string.fypp + stdlib_string_type_constructor.fypp + stdlib_string_to_string.fypp stdlib_strings.fypp ) diff --git a/src/Makefile.manual b/src/Makefile.manual index 01522faa1..f245025da 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -27,8 +27,9 @@ SRCFYPP =\ stdlib_math.fypp \ stdlib_stats_distribution_PRNG.fypp \ stdlib_string_type.fypp \ + stdlib_string_type_constructor.fypp \ stdlib_strings.fypp \ - stdlib_string_format_to_string.fypp + stdlib_string_to_string.fypp SRC = f18estop.f90 \ stdlib_error.f90 \ @@ -139,10 +140,13 @@ stdlib_stats_distribution_PRNG.o: \ stdlib_error.o stdlib_string_type.o: stdlib_ascii.o \ stdlib_kinds.o +stdlib_string_type_constructor.o: stdlib_string_type.o \ + stdlib_string_to_string.o \ + stdlib_strings.o stdlib_strings.o: stdlib_ascii.o \ stdlib_string_type.o \ stdlib_optval.o \ stdlib_kinds.o stdlib_math.o: stdlib_kinds.o -stdlib_string_format_to_string.o: stdlib_strings.o +stdlib_string_to_string.o: stdlib_strings.o stdlib_linalg_outer_product.o: stdlib_linalg.o diff --git a/src/stdlib_ascii.fypp b/src/stdlib_ascii.fypp index 6415fa604..8dc47388a 100644 --- a/src/stdlib_ascii.fypp +++ b/src/stdlib_ascii.fypp @@ -20,19 +20,6 @@ module stdlib_ascii ! Character conversion functions public :: to_lower, to_upper, to_title, to_sentence, reverse - public :: to_string - - !> Version: experimental - !> - !> Create a character string representing the value of the provided variable. - interface to_string - #:for kind in INT_KINDS - module procedure :: to_string_integer_${kind}$ - #:endfor - #:for kind in LOG_KINDS - module procedure :: to_string_logical_${kind}$ - #:endfor - end interface to_string ! All control characters in the ASCII table (see www.asciitable.com). character(len=1), public, parameter :: NUL = achar(int(z'00')) !! Null @@ -362,51 +349,4 @@ contains end function reverse - #:for kind in INT_KINDS - !> Represent an integer of kind ${kind}$ as character sequence - pure function to_string_integer_${kind}$(val) result(string) - integer, parameter :: ik = ${kind}$ - integer(ik), intent(in) :: val - character(len=:), allocatable :: string - integer, parameter :: buffer_len = range(val)+2 - character(len=buffer_len) :: buffer - integer :: pos - integer(ik) :: n - character(len=1), parameter :: numbers(0:9) = & - ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] - - if (val == 0_ik) then - string = numbers(0) - return - end if - - n = abs(val) - buffer = "" - - pos = buffer_len + 1 - do while (n > 0_ik) - pos = pos - 1 - buffer(pos:pos) = numbers(mod(n, 10_ik)) - n = n/10_ik - end do - if (val < 0_ik) then - pos = pos - 1 - buffer(pos:pos) = '-' - end if - - string = buffer(pos:) - end function to_string_integer_${kind}$ - #:endfor - - #:for kind in LOG_KINDS - !> Represent an logical of kind ${kind}$ as character sequence - pure function to_string_logical_${kind}$(val) result(string) - integer, parameter :: ik = ${kind}$ - logical(ik), intent(in) :: val - character(len=1) :: string - - string = merge("T", "F", val) - end function to_string_logical_${kind}$ - #:endfor - end module stdlib_ascii diff --git a/src/stdlib_string_format_to_string.fypp b/src/stdlib_string_format_to_string.fypp deleted file mode 100644 index c9a656501..000000000 --- a/src/stdlib_string_format_to_string.fypp +++ /dev/null @@ -1,41 +0,0 @@ -#:include "common.fypp" -#:set RIL_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES -submodule (stdlib_strings) stdlib_strings_format_to_string - - implicit none - integer, parameter :: buffer_len = 512 - -contains - - - #:for kind, type in RIL_KINDS_TYPES - !> Format or transfer a ${type}$ scalar as a string. - module procedure format_to_string_${type[0]}$_${kind}$ - - character(len=buffer_len) :: buffer - integer :: stat - - write(buffer, optval(format, "(g0)"), iostat=stat) value - if (stat == 0) then - string = trim(buffer) - else - string = '[*]' - !!\TODO: [*]? - end if - - end procedure format_to_string_${type[0]}$_${kind}$ - - #:endfor - - #:for kind, type in CMPLX_KINDS_TYPES - !> Format or transfer a ${type}$ scalar as a string. - module procedure format_to_string_${type[0]}$_${kind}$ - - string = '(' // format_to_string_r_${kind}$(value%re, format) // ',' // & - & format_to_string_r_${kind}$(value%im, format) // ')' - - end procedure format_to_string_${type[0]}$_${kind}$ - - #:endfor - -end submodule stdlib_strings_format_to_string diff --git a/src/stdlib_string_to_string.fypp b/src/stdlib_string_to_string.fypp new file mode 100644 index 000000000..1c92c004e --- /dev/null +++ b/src/stdlib_string_to_string.fypp @@ -0,0 +1,124 @@ +#:include "common.fypp" +submodule(stdlib_strings) stdlib_strings_to_string + + integer, parameter :: buffer_len = 128 + +contains + + #:for k1, t1 in REAL_KINDS_TYPES + !> Format or transfer a ${t1}$ scalar as a string. + pure module function to_string_${t1[0]}$_${k1}$(value, format) result(string) + ${t1}$, intent(in) :: value + character(len=*), intent(in), optional :: format + character(len=:), allocatable :: string + + character(len=buffer_len) :: buffer + integer :: stat + + write(buffer, optval(format, "(g0)"), iostat=stat) value + if (stat == 0) then + string = trim(buffer) + else + string = '[*]' + !!\TODO: [*]? + end if + + end function to_string_${t1[0]}$_${k1}$ + #:endfor + + #:for k1, t1 in CMPLX_KINDS_TYPES + !> Format or transfer a ${t1}$ scalar as a string. + pure module function to_string_${t1[0]}$_${k1}$(value, format) result(string) + ${t1}$, intent(in) :: value + character(len=*), intent(in), optional :: format + character(len=:), allocatable :: string + + string = '(' // to_string_r_${k1}$(value%re, format) // ',' // & + & to_string_r_${k1}$(value%im, format) // ')' + + end function to_string_${t1[0]}$_${k1}$ + #:endfor + + #:for k1, t1 in INT_KINDS_TYPES + !> Represent an integer of kind ${k1}$ as character sequence + pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string) + ${t1}$, intent(in) :: value + character(len=:), allocatable :: string + integer, parameter :: buffer_len = range(value)+2 + character(len=buffer_len) :: buffer + integer :: pos + ${t1}$ :: n + character(len=1), parameter :: numbers(0:9) = & + ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + + if (value == 0_${k1}$) then + string = numbers(0) + return + end if + + n = abs(value) + buffer = "" + + pos = buffer_len + 1 + do while (n > 0_${k1}$) + pos = pos - 1 + buffer(pos:pos) = numbers(mod(n, 10_${k1}$)) + n = n/10_${k1}$ + end do + if (value < 0_${k1}$) then + pos = pos - 1 + buffer(pos:pos) = '-' + end if + + string = buffer(pos:) + end function to_string_1_${t1[0]}$_${k1}$ + + pure module function to_string_2_${t1[0]}$_${k1}$(value, format) result(string) + ${t1}$, intent(in) :: value + character(len=*), intent(in) :: format + character(len=:), allocatable :: string + + character(len=buffer_len) :: buffer + integer :: stat + + write(buffer, format, iostat=stat) value + if (stat == 0) then + string = trim(buffer) + else + string = '[*]' + !!\TODO: [*]? + end if + + end function to_string_2_${t1[0]}$_${k1}$ + #:endfor + + #:for k1, t1 in LOG_KINDS_TYPES + !> Represent an logical of kind ${k1}$ as character sequence + pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string) + ${t1}$, intent(in) :: value + character(len=1) :: string + + string = merge("T", "F", value) + + end function to_string_1_${t1[0]}$_${k1}$ + + pure module function to_string_2_${t1[0]}$_${k1}$(value, format) result(string) + ${t1}$, intent(in) :: value + character(len=*), intent(in) :: format + character(len=:), allocatable :: string + + character(len=buffer_len) :: buffer + integer :: stat + + write(buffer, format, iostat=stat) value + if (stat == 0) then + string = trim(buffer) + else + string = '[*]' + !!\TODO: [*]? + end if + + end function to_string_2_${t1[0]}$_${k1}$ + #:endfor + +end submodule stdlib_strings_to_string diff --git a/src/stdlib_string_type.fypp b/src/stdlib_string_type.fypp index a802830b2..ee853f719 100644 --- a/src/stdlib_string_type.fypp +++ b/src/stdlib_string_type.fypp @@ -14,7 +14,7 @@ !> The specification of this module is available [here](../page/specs/stdlib_string_type.html). module stdlib_string_type use stdlib_ascii, only: to_lower_ => to_lower, to_upper_ => to_upper, & - & to_title_ => to_title, to_sentence_ => to_sentence, reverse_ => reverse, to_string + & to_title_ => to_title, to_sentence_ => to_sentence, reverse_ => reverse use stdlib_kinds, only : int8, int16, int32, int64, lk, c_bool implicit none private @@ -42,18 +42,6 @@ module stdlib_string_type character(len=:), allocatable :: raw end type string_type - !> Constructor for new string instances - interface string_type - module procedure :: new_string - #:for kind in INT_KINDS - module procedure :: new_string_from_integer_${kind}$ - #:endfor - #:for kind in LOG_KINDS - module procedure :: new_string_from_logical_${kind}$ - #:endfor - end interface string_type - - !> Returns the length of the character sequence represented by the string. !> !> This method is elemental and returns a default integer scalar value. @@ -61,6 +49,26 @@ module stdlib_string_type module procedure :: len_string end interface len + !> Constructor for new string instances + interface string_type + pure elemental module function new_string(string) result(new) + character(len=*), intent(in), optional :: string + type(string_type) :: new + end function new_string + #:for kind in INT_KINDS + pure elemental module function new_string_from_integer_${kind}$(val) result(new) + integer(${kind}$), intent(in) :: val + type(string_type) :: new + end function new_string_from_integer_${kind}$ + #:endfor + #:for kind in LOG_KINDS + pure elemental module function new_string_from_logical_${kind}$(val) result(new) + logical(${kind}$), intent(in) :: val + type(string_type) :: new + end function new_string_from_logical_${kind}$ + #:endfor + end interface string_type + !> Returns the length of the character sequence without trailing spaces !> represented by the string. !> @@ -356,35 +364,6 @@ module stdlib_string_type contains - - !> Constructor for new string instances from a scalar character value. - elemental function new_string(string) result(new) - character(len=*), intent(in), optional :: string - type(string_type) :: new - if (present(string)) then - new%raw = string - end if - end function new_string - - #:for kind in INT_KINDS - !> Constructor for new string instances from an integer of kind ${kind}$. - elemental function new_string_from_integer_${kind}$(val) result(new) - integer(${kind}$), intent(in) :: val - type(string_type) :: new - new%raw = to_string(val) - end function new_string_from_integer_${kind}$ - #:endfor - - #:for kind in LOG_KINDS - !> Constructor for new string instances from a logical of kind ${kind}$. - elemental function new_string_from_logical_${kind}$(val) result(new) - logical(${kind}$), intent(in) :: val - type(string_type) :: new - new%raw = to_string(val) - end function new_string_from_logical_${kind}$ - #:endfor - - !> Assign a character sequence to a string. elemental subroutine assign_string_char(lhs, rhs) type(string_type), intent(inout) :: lhs diff --git a/src/stdlib_string_type_constructor.fypp b/src/stdlib_string_type_constructor.fypp new file mode 100644 index 000000000..859eaf103 --- /dev/null +++ b/src/stdlib_string_type_constructor.fypp @@ -0,0 +1,35 @@ +#:include "common.fypp" +submodule(stdlib_string_type) stdlib_string_type_constructor + + use stdlib_strings, only: to_string + +contains + + !> Constructor for new string instances from a scalar character value. + elemental module function new_string(string) result(new) + character(len=*), intent(in), optional :: string + type(string_type) :: new + if (present(string)) then + new%raw = string + end if + end function new_string + + #:for kind in INT_KINDS + !> Constructor for new string instances from an integer of kind ${kind}$. + elemental module function new_string_from_integer_${kind}$(val) result(new) + integer(${kind}$), intent(in) :: val + type(string_type) :: new + new%raw = to_string(val) + end function new_string_from_integer_${kind}$ + #:endfor + + #:for kind in LOG_KINDS + !> Constructor for new string instances from a logical of kind ${kind}$. + elemental module function new_string_from_logical_${kind}$(val) result(new) + logical(${kind}$), intent(in) :: val + type(string_type) :: new + new%raw = to_string(val) + end function new_string_from_logical_${kind}$ + #:endfor + +end submodule stdlib_string_type_constructor \ No newline at end of file diff --git a/src/stdlib_strings.fypp b/src/stdlib_strings.fypp index e2a12e482..caba30e5c 100644 --- a/src/stdlib_strings.fypp +++ b/src/stdlib_strings.fypp @@ -1,7 +1,5 @@ ! SPDX-Identifier: MIT #:include "common.fypp" -#:set KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + LOG_KINDS_TYPES & - & + CMPLX_KINDS_TYPES !> This module implements basic string handling routines. !> !> The specification of this module is available [here](../page/specs/stdlib_strings.html). @@ -13,26 +11,37 @@ module stdlib_strings implicit none private - public :: format_to_string + public :: to_string public :: strip, chomp public :: starts_with, ends_with public :: slice, find, replace_all, count - !> Format or transfer other types as a string. - !> ([Specification](../page/specs/stdlib_strings.html#description)) - !> !> Version: experimental - interface format_to_string - #:for kind, type in KINDS_TYPES - !> Format or transfer a ${type}$ scalar as a string. - pure module function format_to_string_${type[0]}$_${kind}$(value, format) result(string) - ${type}$, intent(in) :: value + !> + !> Format or transfer other types as a string. + !> ([Specification](../page/specs/stdlib_strings.html#to_string)) + interface to_string + #:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES + #:set IL_KINDS_TYPES = INT_KINDS_TYPES + LOG_KINDS_TYPES + #:for k1, t1 in RC_KINDS_TYPES + pure module function to_string_${t1[0]}$_${k1}$(value, format) result(string) + ${t1}$, intent(in) :: value character(len=*), intent(in), optional :: format character(len=:), allocatable :: string - end function format_to_string_${type[0]}$_${kind}$ - + end function to_string_${t1[0]}$_${k1}$ + #:endfor + #:for k1, t1 in IL_KINDS_TYPES + pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string) + ${t1}$, intent(in) :: value + character(len=#{if t1[0]=="l"}#1)#{else}#:), allocatable#{endif}# :: string + end function to_string_1_${t1[0]}$_${k1}$ + pure module function to_string_2_${t1[0]}$_${k1}$(value, format) result(string) + ${t1}$, intent(in) :: value + character(len=*), intent(in) :: format + character(len=:), allocatable :: string + end function to_string_2_${t1[0]}$_${k1}$ #:endfor - end interface format_to_string + end interface to_string !> Remove leading and trailing whitespace characters. !> diff --git a/src/tests/ascii/test_ascii.f90 b/src/tests/ascii/test_ascii.f90 index 9ea29d5f7..c93a1f6e6 100644 --- a/src/tests/ascii/test_ascii.f90 +++ b/src/tests/ascii/test_ascii.f90 @@ -6,8 +6,7 @@ program test_ascii whitespace, letters, is_alphanum, is_alpha, is_lower, is_upper, & is_digit, is_octal_digit, is_hex_digit, is_white, is_blank, & is_control, is_punctuation, is_graphical, is_printable, is_ascii, & - to_lower, to_upper, to_title, to_sentence, reverse, LF, TAB, NUL, DEL, & - to_string + to_lower, to_upper, to_title, to_sentence, reverse, LF, TAB, NUL, DEL use stdlib_kinds, only : int8, int16, int32, int64, lk, c_bool implicit none @@ -76,8 +75,6 @@ program test_ascii call test_to_sentence_string call test_reverse_string - call test_to_string - contains subroutine test_is_alphanum_short @@ -640,47 +637,4 @@ subroutine test_reverse_string call check(trim(adjustl(dlc)) == "desrever") end subroutine test_reverse_string - subroutine test_to_string - character(len=128) :: flc - - write(flc, '(g0)') 1026192 - call check(to_string(1026192) == trim(flc)) - - write(flc, '(g0)') -124784 - call check(to_string(-124784) == trim(flc)) - - write(flc, '(g0)') 1_int8 - call check(to_string(1_int8) == trim(flc)) - - write(flc, '(g0)') -3_int8 - call check(to_string(-3_int8) == trim(flc)) - - write(flc, '(g0)') 80_int16 - call check(to_string(80_int16) == trim(flc)) - - write(flc, '(g0)') 8924890_int32 - call check(to_string(8924890_int32) == trim(flc)) - - write(flc, '(g0)') -2378401_int32 - call check(to_string(-2378401_int32) == trim(flc)) - - write(flc, '(g0)') -921092378401_int64 - call check(to_string(-921092378401_int64) == trim(flc)) - - write(flc, '(g0)') 1272835771_int64 - call check(to_string(1272835771_int64) == trim(flc)) - - write(flc, '(g0)') .true. - call check(to_string(.true.) == trim(flc)) - - write(flc, '(g0)') .false. - call check(to_string(.false.) == trim(flc)) - - write(flc, '(g0)') .true._c_bool - call check(to_string(.true._c_bool) == trim(flc)) - - write(flc, '(g0)') .false._lk - call check(to_string(.false._lk) == trim(flc)) - end subroutine test_to_string - end program test_ascii diff --git a/src/tests/string/CMakeLists.txt b/src/tests/string/CMakeLists.txt index e767a8216..59ee5cf86 100644 --- a/src/tests/string/CMakeLists.txt +++ b/src/tests/string/CMakeLists.txt @@ -5,4 +5,4 @@ ADDTEST(string_match) ADDTEST(string_derivedtype_io) ADDTEST(string_functions) ADDTEST(string_strip_chomp) -ADDTEST(string_format_to_string) +ADDTEST(string_to_string) diff --git a/src/tests/string/Makefile.manual b/src/tests/string/Makefile.manual index 9288d9328..8b93f625b 100644 --- a/src/tests/string/Makefile.manual +++ b/src/tests/string/Makefile.manual @@ -5,7 +5,7 @@ PROGS_SRC = test_string_assignment.f90 \ test_string_match.f90 \ test_string_operator.f90 \ test_string_strip_chomp.f90 \ - test_string_format_to_string.f90 + test_string_to_string.f90 include ../Makefile.manual.test.mk diff --git a/src/tests/string/test_string_format_to_string.f90 b/src/tests/string/test_string_format_to_string.f90 deleted file mode 100644 index d61af4ae7..000000000 --- a/src/tests/string/test_string_format_to_string.f90 +++ /dev/null @@ -1,113 +0,0 @@ -! SPDX-Identifier: MIT -module test_string_format_to_string - use stdlib_strings, only: format_to_string, starts_with - use stdlib_error, only: check - use stdlib_optval, only: optval - implicit none - -contains - - - subroutine check_formatter(actual, expected, description, partial) - character(len=*), intent(in) :: actual, expected, description - logical, intent(in), optional :: partial - logical :: stat - character(len=:), allocatable :: msg - - if (optval(partial, .false.)) then - stat = starts_with(actual, expected) - else - stat = actual == expected - end if - - if (.not. stat) then - msg = description // new_line("a") // & - & "Expected: '" // expected // "' but got '" // actual // "'" - else - print '(" - ", a, /, " Result: ''", a, "''")', description, actual - end if - - call check(stat, msg) - - end subroutine check_formatter - - subroutine test_format_to_string_complex - call check_formatter(format_to_string((1, 1)), "(1.0", & - & "Default formatter for complex number", partial=.true.) - call check_formatter(format_to_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & - & "Formatter for complex number") - call check_formatter(format_to_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & - & "Formatter for negative complex number") - call check_formatter(format_to_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & - & "Formatter with sign control descriptor for complex number") - call check_formatter(format_to_string((1, 1), '(F6.2)') // format_to_string((2, 2), '(F7.3)'), & - & "( 1.00, 1.00)( 2.000, 2.000)", & - & "Multiple formatters for complex numbers") - - end subroutine test_format_to_string_complex - - subroutine test_format_to_string_integer - call check_formatter(format_to_string(100), "100", & - & "Default formatter for integer number") - call check_formatter(format_to_string(100, '(I6)'), " 100", & - & "Formatter for integer number") - call check_formatter(format_to_string(100, '(I0.6)'), "000100", & - & "Formatter with zero padding for integer number") - call check_formatter(format_to_string(100, '(I6)') // format_to_string(1000, '(I7)'), & - & " 100 1000", "Multiple formatters for integers") - call check_formatter(format_to_string(34, '(B8)'), " 100010", & - & "Binary formatter for integer number") - call check_formatter(format_to_string(34, '(O0.3)'), "042", & - & "Octal formatter with zero padding for integer number") - call check_formatter(format_to_string(34, '(Z3)'), " 22", & - & "Hexadecimal formatter for integer number") - - end subroutine test_format_to_string_integer - - subroutine test_format_to_string_real - call check_formatter(format_to_string(100.), "100.0", & - & "Default formatter for real number", partial=.true.) - call check_formatter(format_to_string(100., '(F6.2)'), "100.00", & - & "Formatter for real number") - call check_formatter(format_to_string(289., '(E7.2)'), ".29E+03", & - & "Exponential formatter with rounding for real number") - call check_formatter(format_to_string(128., '(ES8.2)'), "1.28E+02", & - & "Exponential formatter for real number") - - ! Wrong demonstration - call check_formatter(format_to_string(-100., '(F6.2)'), "*", & - & "Too narrow formatter for signed real number", partial=.true.) - call check_formatter(format_to_string(1000., '(F6.3)'), "*", & - & "Too narrow formatter for real number", partial=.true.) - call check_formatter(format_to_string(1000., '(7.3)'), "[*]", & - & "Invalid formatter for real number", partial=.true.) - - end subroutine test_format_to_string_real - - subroutine test_format_to_string_logical - call check_formatter(format_to_string(.true.), "T", & - & "Default formatter for logcal value") - call check_formatter(format_to_string(.true., '(L2)'), " T", & - & "Formatter for logical value") - call check_formatter(format_to_string(.false., '(L2)') // format_to_string(.true., '(L5)'), & - & " F T", "Multiple formatters for logical values") - - ! Wrong demonstration - call check_formatter(format_to_string(.false., '(1x)'), "[*]", & - & "Invalid formatter for logical value", partial=.true.) - - end subroutine test_format_to_string_logical - - -end module test_string_format_to_string - -program tester - use test_string_format_to_string - implicit none - - call test_format_to_string_complex - call test_format_to_string_integer - call test_format_to_string_logical - call test_format_to_string_real - -end program tester diff --git a/src/tests/string/test_string_functions.f90 b/src/tests/string/test_string_functions.f90 index ca44d956d..fc9a92635 100644 --- a/src/tests/string/test_string_functions.f90 +++ b/src/tests/string/test_string_functions.f90 @@ -6,7 +6,7 @@ module test_string_functions to_lower, to_upper, to_title, to_sentence, reverse use stdlib_strings, only: slice, find, replace_all, count use stdlib_optval, only: optval - use stdlib_ascii, only : to_string + use stdlib_strings, only : to_string implicit none contains diff --git a/src/tests/string/test_string_to_string.f90 b/src/tests/string/test_string_to_string.f90 new file mode 100644 index 000000000..ade0f3e3e --- /dev/null +++ b/src/tests/string/test_string_to_string.f90 @@ -0,0 +1,112 @@ +! SPDX-Identifier: MIT +module test_string_to_string + + use stdlib_strings, only: to_string, starts_with + use stdlib_error, only: check + implicit none + +contains + + subroutine check_formatter(actual, expected, description, partial) + character(len=*), intent(in) :: actual, expected, description + logical, intent(in), optional :: partial + logical :: stat + character(len=:), allocatable :: msg + + if (merge(partial, .false., present(partial))) then + stat = starts_with(actual, expected) + else + stat = actual == expected + end if + + if (.not. stat) then + msg = description // new_line("a") // & + & "Expected: '" // expected // "' but got '" // actual // "'" + else + print '(" - ", a, /, " Result: ''", a, "''")', description, actual + end if + + call check(stat, msg) + + end subroutine check_formatter + + subroutine test_to_string_complex + call check_formatter(to_string((1, 1)), "(1.0", & + & "Default formatter for complex number", partial=.true.) + call check_formatter(to_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & + & "Formatter for complex number") + call check_formatter(to_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & + & "Formatter for negative complex number") + call check_formatter(to_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & + & "Formatter with sign control descriptor for complex number") + call check_formatter(to_string((1, 1), '(F6.2)') // to_string((2, 2), '(F7.3)'), & + & "( 1.00, 1.00)( 2.000, 2.000)", & + & "Multiple formatters for complex numbers") + + end subroutine test_to_string_complex + + subroutine test_to_string_integer + call check_formatter(to_string(100), "100", & + & "Default formatter for integer number") + call check_formatter(to_string(100, '(I6)'), " 100", & + & "Formatter for integer number") + call check_formatter(to_string(100, '(I0.6)'), "000100", & + & "Formatter with zero padding for integer number") + call check_formatter(to_string(100, '(I6)') // to_string(1000, '(I7)'), & + & " 100 1000", "Multiple formatters for integers") + call check_formatter(to_string(34, '(B8)'), " 100010", & + & "Binary formatter for integer number") + call check_formatter(to_string(34, '(O0.3)'), "042", & + & "Octal formatter with zero padding for integer number") + call check_formatter(to_string(34, '(Z3)'), " 22", & + & "Hexadecimal formatter for integer number") + + end subroutine test_to_string_integer + + subroutine test_to_string_real + call check_formatter(to_string(100.), "100.0", & + & "Default formatter for real number", partial=.true.) + call check_formatter(to_string(100., '(F6.2)'), "100.00", & + & "Formatter for real number") + call check_formatter(to_string(289., '(E7.2)'), ".29E+03", & + & "Exponential formatter with rounding for real number") + call check_formatter(to_string(128., '(ES8.2)'), "1.28E+02", & + & "Exponential formatter for real number") + + ! Wrong demonstration + call check_formatter(to_string(-100., '(F6.2)'), "*", & + & "Too narrow formatter for signed real number", partial=.true.) + call check_formatter(to_string(1000., '(F6.3)'), "*", & + & "Too narrow formatter for real number", partial=.true.) + call check_formatter(to_string(1000., '(7.3)'), "[*]", & + & "Invalid formatter for real number", partial=.true.) + + end subroutine test_to_string_real + + subroutine test_to_string_logical + call check_formatter(to_string(.true.), "T", & + & "Default formatter for logcal value") + call check_formatter(to_string(.true., '(L2)'), " T", & + & "Formatter for logical value") + call check_formatter(to_string(.false., '(L2)') // to_string(.true., '(L5)'), & + & " F T", "Multiple formatters for logical values") + + ! Wrong demonstration + call check_formatter(to_string(.false., '(1x)'), "[*]", & + & "Invalid formatter for logical value", partial=.true.) + + end subroutine test_to_string_logical + + +end module test_string_to_string + +program tester + use test_string_to_string + implicit none + + call test_to_string_complex + call test_to_string_integer + call test_to_string_logical + call test_to_string_real + +end program tester From bdc33f55c967fac04ec69b1d4ffd647e64cc23df Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Wed, 4 Aug 2021 10:37:17 +0800 Subject: [PATCH 16/19] Some clean works for `to_string` func. --- doc/specs/stdlib_strings.md | 53 ++++++++++++++++---------------- src/stdlib_string_to_string.fypp | 15 +++++---- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 3a11109f1..7a8e23e0e 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -563,43 +563,44 @@ Pure function. - `value`: Shall be an `integer/real/complex/logical` scalar. This is an `intent(in)` argument. -- `format`: Shall be a `character` scalar like `'(F6.2)'`. +- `format`: Shall be a `character(len=*)` scalar like `'(F6.2)'`. This is an `intent(in)` and `optional` argument. #### Result value -The result is an allocatable length `character` scalar with up to 128 cached `character` length. +The result is an `allocatable` length `character` scalar with up to `128` cached `character` length. #### Example ```fortran program demo_to_string - use :: stdlib_strings, only: to_string + use stdlib_strings, only: to_string implicit none - print *, 'to_string(complex) : ' - print *, to_string((1, 1)) ! (1.00000000,1.00000000) - print *, to_string((1, 1), '(F6.2)') ! ( 1.00, 1.00) - print *, to_string((1000, 1), '(ES0.2)'), to_string((1000, 1), '(SP,F6.3)') ! (1.00E+3,1.00)(******,+1.000) - !! Too narrow formatter for real number - !! Normal demonstration(`******` from Fortran Standard) - - print *, 'to_string(integer) : ' - print *, to_string(1) ! 1 - print *, to_string(1, '(I4)') ! 1 - print *, to_string(1, '(I0.4)'), to_string(2, '(B4)') ! 0001 10 - - print *, 'to_string(real) : ' - print *, to_string(1.) ! 1.00000000 - print *, to_string(1., '(F6.2)') ! 1.00 - print *, to_string(1., '(SP,ES9.2)'), to_string(1, '(F7.3)') ! +1.00E+00[*] - !! 1 wrong demonstration(`*` from `to_string`) - - print *, 'to_string(logical) : ' - print *, to_string(.true.) ! T - print *, to_string(.true., '(L2)') ! T - print *, to_string(.true., 'L2'), to_string(.false., '(I5)') ! [*][*] - !! 2 wrong demonstrations(`*` from `to_string`) + !> Example for `complex` type + print *, to_string((1, 1)) !! "(1.00000000,1.00000000)" + print *, to_string((1, 1), '(F6.2)') !! "( 1.00, 1.00)" + print *, to_string((1000, 1), '(ES0.2)'), to_string((1000, 1), '(SP,F6.3)') + !! "(1.00E+3,1.00)""(******,+1.000)" + !! Too narrow formatter for real number + !! Normal demonstration(`******` from Fortran Standard) + + !> Example for `integer` type + print *, to_string(-3) !! "-3" + print *, to_string(42, '(I4)') !! " 42" + print *, to_string(1, '(I0.4)'), to_string(2, '(B4)') !! "0001"" 10" + + !> Example for `real` type + print *, to_string(1.) !! "1.00000000" + print *, to_string(1., '(F6.2)') !! " 1.00" + print *, to_string(1., '(SP,ES9.2)'), to_string(1, '(F7.3)') !! "+1.00E+00""[*]" + !! 1 wrong demonstration (`[*]` from `to_string`) + + !> Example for `logical` type + print *, to_string(.true.) !! "T" + print *, to_string(.true., '(L2)') !! " T" + print *, to_string(.true., 'L2'), to_string(.false., '(I5)') !! "[*]""[*]" + !! 2 wrong demonstrations(`[*]` from `to_string`) end program demo_to_string ``` \ No newline at end of file diff --git a/src/stdlib_string_to_string.fypp b/src/stdlib_string_to_string.fypp index 1c92c004e..85431bad9 100644 --- a/src/stdlib_string_to_string.fypp +++ b/src/stdlib_string_to_string.fypp @@ -2,6 +2,8 @@ submodule(stdlib_strings) stdlib_strings_to_string integer, parameter :: buffer_len = 128 + character(len=*), parameter :: err_sym = "[*]" + !!TODO: [*]? contains @@ -19,8 +21,7 @@ contains if (stat == 0) then string = trim(buffer) else - string = '[*]' - !!\TODO: [*]? + string = err_sym end if end function to_string_${t1[0]}$_${k1}$ @@ -40,7 +41,7 @@ contains #:endfor #:for k1, t1 in INT_KINDS_TYPES - !> Represent an integer of kind ${k1}$ as character sequence + !> Represent an integer of kind ${k1}$ as character sequence. pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string) ${t1}$, intent(in) :: value character(len=:), allocatable :: string @@ -85,15 +86,14 @@ contains if (stat == 0) then string = trim(buffer) else - string = '[*]' - !!\TODO: [*]? + string = err_sym end if end function to_string_2_${t1[0]}$_${k1}$ #:endfor #:for k1, t1 in LOG_KINDS_TYPES - !> Represent an logical of kind ${k1}$ as character sequence + !> Represent an logical of kind ${k1}$ as character sequence. pure module function to_string_1_${t1[0]}$_${k1}$(value) result(string) ${t1}$, intent(in) :: value character(len=1) :: string @@ -114,8 +114,7 @@ contains if (stat == 0) then string = trim(buffer) else - string = '[*]' - !!\TODO: [*]? + string = err_sym end if end function to_string_2_${t1[0]}$_${k1}$ From f3c17a0e0bc04de562a1230936de2bed70d68c0c Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Wed, 11 Aug 2021 10:30:06 +0800 Subject: [PATCH 17/19] Fix `test_string_to_string.f90`: `merge` -> `optval` --- src/CMakeLists.txt | 2 +- src/Makefile.manual | 6 +++--- ..._string_to_string.fypp => stdlib_strings_to_string.fypp} | 0 src/tests/string/test_string_to_string.f90 | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) rename src/{stdlib_string_to_string.fypp => stdlib_strings_to_string.fypp} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5474f6949..56ba3ba23 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,7 @@ set(fppFiles stdlib_math_logspace.fypp stdlib_string_type.fypp stdlib_string_type_constructor.fypp - stdlib_string_to_string.fypp + stdlib_strings_to_string.fypp stdlib_strings.fypp ) diff --git a/src/Makefile.manual b/src/Makefile.manual index d7f11a0f9..208b08454 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -32,7 +32,7 @@ SRCFYPP =\ stdlib_string_type.fypp \ stdlib_string_type_constructor.fypp \ stdlib_strings.fypp \ - stdlib_string_to_string.fypp + stdlib_strings_to_string.fypp SRC = f18estop.f90 \ stdlib_error.f90 \ @@ -150,14 +150,14 @@ stdlib_stats_distribution_PRNG.o: \ stdlib_string_type.o: stdlib_ascii.o \ stdlib_kinds.o stdlib_string_type_constructor.o: stdlib_string_type.o \ - stdlib_string_to_string.o \ + stdlib_strings_to_string.o \ stdlib_strings.o stdlib_strings.o: stdlib_ascii.o \ stdlib_string_type.o \ stdlib_optval.o \ stdlib_kinds.o stdlib_math.o: stdlib_kinds.o -stdlib_string_to_string.o: stdlib_strings.o +stdlib_strings_to_string.o: stdlib_strings.o stdlib_math_linspace.o: \ stdlib_math.o stdlib_math_logspace.o: \ diff --git a/src/stdlib_string_to_string.fypp b/src/stdlib_strings_to_string.fypp similarity index 100% rename from src/stdlib_string_to_string.fypp rename to src/stdlib_strings_to_string.fypp diff --git a/src/tests/string/test_string_to_string.f90 b/src/tests/string/test_string_to_string.f90 index ade0f3e3e..7274aa8d2 100644 --- a/src/tests/string/test_string_to_string.f90 +++ b/src/tests/string/test_string_to_string.f90 @@ -3,6 +3,7 @@ module test_string_to_string use stdlib_strings, only: to_string, starts_with use stdlib_error, only: check + use stdlib_optval, only: optval implicit none contains @@ -13,7 +14,7 @@ subroutine check_formatter(actual, expected, description, partial) logical :: stat character(len=:), allocatable :: msg - if (merge(partial, .false., present(partial))) then + if (optval(partial, .false.)) then stat = starts_with(actual, expected) else stat = actual == expected From 527daed20f67eabc8d887e0a2131f1efea428285 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Thu, 19 Aug 2021 13:15:22 -0400 Subject: [PATCH 18/19] Consistent indentation in Makefile --- src/Makefile.manual | 100 ++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/src/Makefile.manual b/src/Makefile.manual index 410e8129a..5c1d2ba8d 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -1,4 +1,4 @@ -SRCFYPP =\ +SRCFYPP = \ stdlib_ascii.fypp \ stdlib_bitsets_64.fypp \ stdlib_bitsets_large.fypp \ @@ -27,8 +27,8 @@ SRCFYPP =\ stdlib_stats_moment_scalar.fypp \ stdlib_stats_var.fypp \ stdlib_math.fypp \ - stdlib_math_linspace.fypp \ - stdlib_math_logspace.fypp \ + stdlib_math_linspace.fypp \ + stdlib_math_logspace.fypp \ stdlib_stats_distribution_PRNG.fypp \ stdlib_string_type.fypp \ stdlib_string_type_constructor.fypp \ @@ -79,75 +79,73 @@ stdlib_error.o: stdlib_optval.o stdlib_specialfunctions.o: stdlib_kinds.o stdlib_specialfunctions_legendre.o: stdlib_kinds.o stdlib_specialfunctions.o stdlib_io.o: \ - stdlib_ascii.o \ - stdlib_error.o \ - stdlib_optval.o \ - stdlib_kinds.o \ + stdlib_ascii.o \ + stdlib_error.o \ + stdlib_optval.o \ + stdlib_kinds.o \ stdlib_ascii.o stdlib_linalg.o: \ - stdlib_kinds.o + stdlib_kinds.o stdlib_linalg_diag.o: \ - stdlib_linalg.o \ - stdlib_kinds.o + stdlib_linalg.o \ + stdlib_kinds.o stdlib_logger.o: stdlib_ascii.o stdlib_optval.o stdlib_optval.o: stdlib_kinds.o stdlib_quadrature.o: stdlib_kinds.o - stdlib_quadrature_gauss.o: stdlib_kinds.o stdlib_quadrature.o - stdlib_quadrature_simps.o: \ - stdlib_quadrature.o \ - stdlib_error.o \ - stdlib_kinds.o + stdlib_quadrature.o \ + stdlib_error.o \ + stdlib_kinds.o stdlib_quadrature_trapz.o: \ - stdlib_quadrature.o \ - stdlib_error.o \ - stdlib_kinds.o + stdlib_quadrature.o \ + stdlib_error.o \ + stdlib_kinds.o stdlib_sorting.o: \ - stdlib_kinds.o \ - stdlib_string_type.o + stdlib_kinds.o \ + stdlib_string_type.o stdlib_sorting_ord_sort.o: \ - stdlib_sorting.o + stdlib_sorting.o stdlib_sorting_sort.o: \ - stdlib_sorting.o + stdlib_sorting.o stdlib_sorting_sort_index.o: \ - stdlib_sorting.o + stdlib_sorting.o stdlib_stats.o: \ - stdlib_kinds.o + stdlib_kinds.o stdlib_stats_corr.o: \ - stdlib_optval.o \ - stdlib_kinds.o \ - stdlib_stats.o + stdlib_optval.o \ + stdlib_kinds.o \ + stdlib_stats.o stdlib_stats_cov.o: \ - stdlib_optval.o \ - stdlib_kinds.o \ - stdlib_stats.o + stdlib_optval.o \ + stdlib_kinds.o \ + stdlib_stats.o stdlib_stats_mean.o: \ - stdlib_optval.o \ - stdlib_kinds.o \ - stdlib_stats.o + stdlib_optval.o \ + stdlib_kinds.o \ + stdlib_stats.o stdlib_stats_median.o: \ - stdlib_optval.o \ - stdlib_kinds.o \ - stdlib_sorting.o \ - stdlib_stats.o + stdlib_optval.o \ + stdlib_kinds.o \ + stdlib_sorting.o \ + stdlib_stats.o stdlib_stats_moment.o: \ - stdlib_optval.o \ - stdlib_kinds.o \ - stdlib_stats.o + stdlib_optval.o \ + stdlib_kinds.o \ + stdlib_stats.o stdlib_stats_moment_all.o: \ - stdlib_stats_moment.o + stdlib_stats_moment.o stdlib_stats_moment_mask.o: \ - stdlib_stats_moment.o + stdlib_stats_moment.o stdlib_stats_moment_scalar.o: \ - stdlib_stats_moment.o + stdlib_stats_moment.o stdlib_stats_var.o: \ - stdlib_optval.o \ - stdlib_kinds.o \ - stdlib_stats.o + stdlib_optval.o \ + stdlib_kinds.o \ + stdlib_stats.o stdlib_stats_distribution_PRNG.o: \ - stdlib_kinds.o \ - stdlib_error.o + stdlib_kinds.o \ + stdlib_error.o stdlib_string_type.o: stdlib_ascii.o \ stdlib_kinds.o stdlib_string_type_constructor.o: stdlib_string_type.o \ @@ -155,15 +153,15 @@ stdlib_string_type_constructor.o: stdlib_string_type.o \ stdlib_strings.o stdlib_strings.o: stdlib_ascii.o \ stdlib_string_type.o \ - stdlib_optval.o \ + stdlib_optval.o \ stdlib_kinds.o stdlib_strings_to_string.o: stdlib_strings.o stdlib_math.o: stdlib_kinds.o \ stdlib_optval.o stdlib_math_linspace.o: \ - stdlib_math.o + stdlib_math.o stdlib_math_logspace.o: \ - stdlib_math_linspace.o + stdlib_math_linspace.o stdlib_math_arange.o: \ stdlib_math.o stdlib_linalg_outer_product.o: stdlib_linalg.o From 32f983787c013d1d21347d81d5fd23768f7a841a Mon Sep 17 00:00:00 2001 From: zoziha13 <1325686572@qq.com> Date: Sun, 22 Aug 2021 11:07:07 +0800 Subject: [PATCH 19/19] Improve `to_string`: add support for `format` like `'f6.2'`. --- doc/specs/stdlib_strings.md | 15 +++++---- src/stdlib_strings_to_string.fypp | 6 ++-- src/tests/string/test_string_to_string.f90 | 36 +++++++++++----------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 7a8e23e0e..f16995b48 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -563,9 +563,11 @@ Pure function. - `value`: Shall be an `integer/real/complex/logical` scalar. This is an `intent(in)` argument. -- `format`: Shall be a `character(len=*)` scalar like `'(F6.2)'`. - This is an `intent(in)` and `optional` argument. - +- `format`: Shall be a `character(len=*)` scalar like `'(F6.2)'` or just `'F6.2'`. + This is an `intent(in)` and `optional` argument. + Contains the edit descriptor to format `value` into a string, for example `'(F6.2)'` or `'(f6.2)'`. + `to_string` will automatically enclose `format` in a set of parentheses, so passing `F6.2` or `f6.2` as `format` is possible as well. + #### Result value The result is an `allocatable` length `character` scalar with up to `128` cached `character` length. @@ -575,7 +577,6 @@ The result is an `allocatable` length `character` scalar with up to `128` cached ```fortran program demo_to_string use stdlib_strings, only: to_string - implicit none !> Example for `complex` type print *, to_string((1, 1)) !! "(1.00000000,1.00000000)" @@ -593,14 +594,16 @@ program demo_to_string !> Example for `real` type print *, to_string(1.) !! "1.00000000" print *, to_string(1., '(F6.2)') !! " 1.00" + print *, to_string(1., 'F6.2') !! " 1.00" print *, to_string(1., '(SP,ES9.2)'), to_string(1, '(F7.3)') !! "+1.00E+00""[*]" !! 1 wrong demonstration (`[*]` from `to_string`) !> Example for `logical` type print *, to_string(.true.) !! "T" print *, to_string(.true., '(L2)') !! " T" - print *, to_string(.true., 'L2'), to_string(.false., '(I5)') !! "[*]""[*]" - !! 2 wrong demonstrations(`[*]` from `to_string`) + print *, to_string(.true., 'L2') !! " T" + print *, to_string(.false., '(I5)') !! "[*]" + !! 1 wrong demonstrations(`[*]` from `to_string`) end program demo_to_string ``` \ No newline at end of file diff --git a/src/stdlib_strings_to_string.fypp b/src/stdlib_strings_to_string.fypp index 85431bad9..c7d4d013d 100644 --- a/src/stdlib_strings_to_string.fypp +++ b/src/stdlib_strings_to_string.fypp @@ -17,7 +17,7 @@ contains character(len=buffer_len) :: buffer integer :: stat - write(buffer, optval(format, "(g0)"), iostat=stat) value + write(buffer, '(' // optval(format, "g0") // ')', iostat=stat) value if (stat == 0) then string = trim(buffer) else @@ -82,7 +82,7 @@ contains character(len=buffer_len) :: buffer integer :: stat - write(buffer, format, iostat=stat) value + write(buffer, "(" // format // ")", iostat=stat) value if (stat == 0) then string = trim(buffer) else @@ -110,7 +110,7 @@ contains character(len=buffer_len) :: buffer integer :: stat - write(buffer, format, iostat=stat) value + write(buffer, "(" // format // ")", iostat=stat) value if (stat == 0) then string = trim(buffer) else diff --git a/src/tests/string/test_string_to_string.f90 b/src/tests/string/test_string_to_string.f90 index 7274aa8d2..15bfb7531 100644 --- a/src/tests/string/test_string_to_string.f90 +++ b/src/tests/string/test_string_to_string.f90 @@ -36,11 +36,11 @@ subroutine test_to_string_complex & "Default formatter for complex number", partial=.true.) call check_formatter(to_string((1, 1), '(F6.2)'), "( 1.00, 1.00)", & & "Formatter for complex number") - call check_formatter(to_string((-1, -1), '(F6.2)'), "( -1.00, -1.00)", & + call check_formatter(to_string((-1, -1), 'F6.2'), "( -1.00, -1.00)", & & "Formatter for negative complex number") - call check_formatter(to_string((1, 1), '(SP,F6.2)'), "( +1.00, +1.00)", & + call check_formatter(to_string((1, 1), 'SP,F6.2'), "( +1.00, +1.00)", & & "Formatter with sign control descriptor for complex number") - call check_formatter(to_string((1, 1), '(F6.2)') // to_string((2, 2), '(F7.3)'), & + call check_formatter(to_string((1, 1), 'F6.2') // to_string((2, 2), '(F7.3)'), & & "( 1.00, 1.00)( 2.000, 2.000)", & & "Multiple formatters for complex numbers") @@ -49,17 +49,17 @@ end subroutine test_to_string_complex subroutine test_to_string_integer call check_formatter(to_string(100), "100", & & "Default formatter for integer number") - call check_formatter(to_string(100, '(I6)'), " 100", & + call check_formatter(to_string(100, 'I6'), " 100", & & "Formatter for integer number") - call check_formatter(to_string(100, '(I0.6)'), "000100", & + call check_formatter(to_string(100, 'I0.6'), "000100", & & "Formatter with zero padding for integer number") - call check_formatter(to_string(100, '(I6)') // to_string(1000, '(I7)'), & + call check_formatter(to_string(100, 'I6') // to_string(1000, '(I7)'), & & " 100 1000", "Multiple formatters for integers") - call check_formatter(to_string(34, '(B8)'), " 100010", & + call check_formatter(to_string(34, 'B8'), " 100010", & & "Binary formatter for integer number") - call check_formatter(to_string(34, '(O0.3)'), "042", & + call check_formatter(to_string(34, 'O0.3'), "042", & & "Octal formatter with zero padding for integer number") - call check_formatter(to_string(34, '(Z3)'), " 22", & + call check_formatter(to_string(34, 'Z3'), " 22", & & "Hexadecimal formatter for integer number") end subroutine test_to_string_integer @@ -67,19 +67,19 @@ end subroutine test_to_string_integer subroutine test_to_string_real call check_formatter(to_string(100.), "100.0", & & "Default formatter for real number", partial=.true.) - call check_formatter(to_string(100., '(F6.2)'), "100.00", & + call check_formatter(to_string(100., 'F6.2'), "100.00", & & "Formatter for real number") - call check_formatter(to_string(289., '(E7.2)'), ".29E+03", & + call check_formatter(to_string(289., 'E7.2'), ".29E+03", & & "Exponential formatter with rounding for real number") - call check_formatter(to_string(128., '(ES8.2)'), "1.28E+02", & + call check_formatter(to_string(128., 'ES8.2'), "1.28E+02", & & "Exponential formatter for real number") ! Wrong demonstration - call check_formatter(to_string(-100., '(F6.2)'), "*", & + call check_formatter(to_string(-100., 'F6.2'), "*", & & "Too narrow formatter for signed real number", partial=.true.) - call check_formatter(to_string(1000., '(F6.3)'), "*", & + call check_formatter(to_string(1000., 'F6.3'), "*", & & "Too narrow formatter for real number", partial=.true.) - call check_formatter(to_string(1000., '(7.3)'), "[*]", & + call check_formatter(to_string(1000., '7.3'), "[*]", & & "Invalid formatter for real number", partial=.true.) end subroutine test_to_string_real @@ -87,13 +87,13 @@ end subroutine test_to_string_real subroutine test_to_string_logical call check_formatter(to_string(.true.), "T", & & "Default formatter for logcal value") - call check_formatter(to_string(.true., '(L2)'), " T", & + call check_formatter(to_string(.true., 'L2'), " T", & & "Formatter for logical value") - call check_formatter(to_string(.false., '(L2)') // to_string(.true., '(L5)'), & + call check_formatter(to_string(.false., 'L2') // to_string(.true., '(L5)'), & & " F T", "Multiple formatters for logical values") ! Wrong demonstration - call check_formatter(to_string(.false., '(1x)'), "[*]", & + call check_formatter(to_string(.false., '1x'), "[*]", & & "Invalid formatter for logical value", partial=.true.) end subroutine test_to_string_logical