Skip to content

Commit 328d6b9

Browse files
committed
Added error message if json_initialize is not called first. Fixes #142
Added support for changing the real printing format on subsequent calls to son_initialize. Fixes #143 Added new units test and some formatting updates.
1 parent 0f1b290 commit 328d6b9

File tree

3 files changed

+156
-70
lines changed

3 files changed

+156
-70
lines changed

src/json_module.F90

+61-55
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,10 @@ end subroutine array_callback_func
867867
!for indenting (Note: jsonlint.com uses 4 spaces)
868868
integer(IK),parameter :: spaces_per_tab = 2
869869

870+
!Variables for real string printing:
871+
872+
logical(LK) :: compact_real = .true. !! to use the "compact" form of real numbers for output
873+
870874
!find out the precision of the floating point number system
871875
!and set safety factors
872876
integer(IK),parameter :: rp_safety_factor = 1
@@ -881,25 +885,21 @@ end subroutine array_callback_func
881885
real(max(maxexp,abs(maxexp)),&
882886
kind=RK) ) )
883887

884-
!6 = sign + leading 0 + decimal + 'E' + exponent sign + 1 extra
885-
integer(IK),parameter :: max_numeric_str_len = real_precision + real_exponent_digits + 6
886-
! real format set by library initialization
887-
character(kind=CDK,len=*),parameter :: int_fmt = '(ss,I0)' !minimum width format for integers
888-
character(kind=CK, len=*),parameter :: star = '*' !for invalid numbers
889-
890-
!real string printing:
891-
character(kind=CDK,len=:),allocatable :: real_fmt !the format string to use for real numbers
892-
! [set in json_initialize]
893-
logical(LK) :: compact_real = .true. !to use the "compact" form of real numbers for output
888+
integer(IK),parameter :: max_numeric_str_len = real_precision + real_exponent_digits + 6
889+
!! 6 = sign + leading 0 + decimal + 'E' + exponent sign + 1 extra
890+
character(kind=CDK,len=*),parameter :: int_fmt = '(ss,I0)' !! minimum width format for integers
891+
character(kind=CK, len=*),parameter :: star = '*' !! for invalid numbers
892+
character(kind=CDK,len=:),allocatable :: real_fmt !! the format string to use for real numbers
893+
!! it is set in [[json_initialize]]
894894

895895
!
896896
! Note: the following global variables make this module non thread safe.
897897
!
898898

899899
!exception handling [private variables]
900-
logical(LK) :: is_verbose = .false. !if true, all exceptions are immediately printed to console
901-
logical(LK) :: exception_thrown = .false. !the error flag
902-
character(kind=CK,len=:),allocatable :: err_message !the error message
900+
logical(LK) :: is_verbose = .false. !! if true, all exceptions are immediately printed to console
901+
logical(LK) :: exception_thrown = .true. !! the error flag (by default, this is true to make sure that [[json_initialize]] is called.
902+
character(kind=CK,len=:),allocatable :: err_message !! the error message
903903

904904
!temp vars used when parsing lines in file [private variables]
905905
integer(IK) :: char_count = 0 !character position in the current line
@@ -1642,59 +1642,71 @@ subroutine json_initialize(verbose,compact_reals,print_signs,real_format)
16421642

16431643
implicit none
16441644

1645-
logical(LK),intent(in),optional :: verbose !! mainly useful for debugging (default is false)
1646-
logical(LK),intent(in),optional :: compact_reals !! to compact the real number strings for output
1647-
logical(LK),intent(in),optional :: print_signs !! always print numeric sign (default is false)
1648-
character(len=*,kind=CDK),intent(in),optional :: real_format
1649-
!! exponential (default), scientific, engineering or general
1645+
logical(LK),intent(in),optional :: verbose !! mainly useful for debugging (default is false)
1646+
logical(LK),intent(in),optional :: compact_reals !! to compact the real number strings for output (default is true)
1647+
logical(LK),intent(in),optional :: print_signs !! always print numeric sign (default is false)
1648+
character(len=*,kind=CDK),intent(in),optional :: real_format !! exponential (default), scientific, engineering or general
16501649

16511650
character(kind=CDK,len=10) :: w,d,e
16521651
character(kind=CDK,len=2) :: sgn, rl_edit_desc
16531652
integer(IK) :: istat
16541653
logical(LK) :: sgn_prnt
16551654

1656-
16571655
!clear any errors from previous runs:
16581656
call json_clear_exceptions()
16591657

1660-
!set defaults
1661-
sgn_prnt = .false.
1662-
if ( present( print_signs) ) sgn_prnt = print_signs
1663-
if ( sgn_prnt ) then
1664-
sgn = 'sp'
1665-
else
1666-
sgn = 'ss'
1667-
end if
1658+
!Ensure gfortran bug work around "parameters" are set properly
1659+
null_str = 'null'
1660+
true_str = 'true'
1661+
false_str = 'false'
16681662

1669-
rl_edit_desc = 'E'
1670-
if ( present( real_format ) ) then
1671-
select case ( real_format )
1672-
case ('g','G','e','E','en','EN','es','ES')
1673-
rl_edit_desc = real_format
1674-
case default
1675-
call throw_exception('Invalid real format, "' // trim(real_format) // '", passed to json_initialize.'// &
1676-
new_line('a') // 'Acceptable formats are: "G", "E", "EN", and "ES".' )
1677-
end select
1678-
end if
1663+
!Just in case, clear these global variables also:
1664+
pushed_index = 0
1665+
pushed_char = ''
1666+
char_count = 0
1667+
line_count = 1
1668+
ipos = 1
16791669

16801670
# ifdef USE_UCS4
16811671
! reopen stdout and stderr with utf-8 encoding
16821672
open(output_unit,encoding='utf-8')
16831673
open(error_unit, encoding='utf-8')
16841674
# endif
16851675

1686-
!Ensure gfortran bug work around "parameters" are set properly
1687-
null_str = 'null'
1688-
true_str = 'true'
1689-
false_str = 'false'
1676+
!verbose error printing:
1677+
if (present(verbose)) is_verbose = verbose
1678+
1679+
!Set the format for real numbers:
1680+
! [if not changing it, then it remains the same]
1681+
1682+
if ( (.not. allocated(real_fmt)) .or. & ! if this hasn't been done yet
1683+
present(compact_reals) .or. &
1684+
present(print_signs) .or. &
1685+
present(real_format) ) then
1686+
1687+
if (present(compact_reals)) compact_real = compact_reals
1688+
1689+
!set defaults
1690+
sgn_prnt = .false.
1691+
if ( present( print_signs) ) sgn_prnt = print_signs
1692+
if ( sgn_prnt ) then
1693+
sgn = 'sp'
1694+
else
1695+
sgn = 'ss'
1696+
end if
16901697

1691-
!optional inputs (if not present, values remains unchanged):
1692-
if (present(verbose)) is_verbose = verbose
1693-
if (present(compact_reals)) compact_real = compact_reals
1698+
rl_edit_desc = 'E'
1699+
if ( present( real_format ) ) then
1700+
select case ( real_format )
1701+
case ('g','G','e','E','en','EN','es','ES')
1702+
rl_edit_desc = real_format
1703+
case default
1704+
call throw_exception('Invalid real format, "' // trim(real_format) // '", passed to json_initialize.'// &
1705+
new_line('a') // 'Acceptable formats are: "G", "E", "EN", and "ES".' )
1706+
end select
1707+
end if
16941708

1695-
! set the default output/input format for reals:
1696-
! [this only needs to be done once, since it can't change]
1697-
if (.not. allocated(real_fmt)) then
1709+
! set the default output/input format for reals:
16981710
write(w,'(ss,I0)',iostat=istat) max_numeric_str_len
16991711
if (istat==0) write(d,'(ss,I0)',iostat=istat) real_precision
17001712
if (istat==0) write(e,'(ss,I0)',iostat=istat) real_exponent_digits
@@ -1703,14 +1715,8 @@ subroutine json_initialize(verbose,compact_reals,print_signs,real_format)
17031715
else
17041716
real_fmt = '(' // sgn // ',' // trim(rl_edit_desc) // '30.16E3)' !just use this one (should never happen)
17051717
end if
1706-
end if
17071718

1708-
!Just in case, clear these global variables also:
1709-
pushed_index = 0
1710-
pushed_char = ''
1711-
char_count = 0
1712-
line_count = 1
1713-
ipos = 1
1719+
end if
17141720

17151721
end subroutine json_initialize
17161722
!*****************************************************************************************
@@ -1815,7 +1821,7 @@ subroutine json_check_for_errors(status_ok, error_msg)
18151821
if (allocated(err_message)) then
18161822
error_msg = err_message
18171823
else
1818-
error_msg = 'Unknown Error'
1824+
error_msg = 'Error: json_initialize() must be called first to initialize the module.'
18191825
end if
18201826
else
18211827
error_msg = ''

src/tests/jf_test_12.f90

+19-15
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,26 @@ module jf_test_12_mod
1818
contains
1919

2020
subroutine test_12(error_cnt)
21+
22+
implicit none
2123

2224
integer,intent(out) :: error_cnt !! report number of errors to caller
2325

2426
integer,parameter :: imx = 5, jmx = 3, kmx = 4 !! dimensions for raw work array of primitive type
25-
integer :: shape(3) !! shape of work array
26-
integer, allocatable :: fetched_shape(:) !! retrieved shape
27-
type(json_value), pointer :: root, meta_array !! json nodes to work with
28-
type(json_value), pointer :: tmp_json_ptr
29-
type(json_file) :: my_file
30-
real(wp) :: raw_array(imx,jmx,kmx) !! raw work array
31-
real(wp) :: array_element
32-
real(wp), allocatable :: fetched_array(:)
27+
28+
integer,dimension(3) :: shape !! shape of work array
29+
integer, dimension(:), allocatable :: fetched_shape !! retrieved shape
30+
type(json_value), pointer :: root, meta_array !! json nodes to work with
31+
type(json_value), pointer :: tmp_json_ptr
32+
type(json_file) :: my_file
33+
real(wp),dimension(imx,jmx,kmx) :: raw_array !! raw work array
34+
real(wp) :: array_element
35+
real(wp), dimension(:), allocatable :: fetched_array
3336
character(kind=CK,len=:), allocatable :: description
34-
integer :: i,j,k !! loop indices
35-
integer :: array_length, lun
36-
logical :: existed
37-
logical, allocatable :: SOS(:)
37+
integer :: i,j,k !! loop indices
38+
integer :: array_length, lun
39+
logical :: existed
40+
logical, dimension(:), allocatable :: SOS
3841

3942
error_cnt = 0
4043
call json_initialize(verbose=.true.,real_format='G')
@@ -178,7 +181,7 @@ subroutine check_errors(assertion)
178181
if (present (assertion)) then
179182
if (.not. assertion) error_cnt = error_cnt + 1
180183
end if
181-
end subroutine
184+
end subroutine check_errors
182185

183186
subroutine get_3D_from_array(element, i, count)
184187
type(json_value), pointer , intent(in) :: element
@@ -192,9 +195,9 @@ subroutine get_3D_from_array(element, i, count)
192195
mod((i-1)/imx,jmx) + 1, & ! j index
193196
mod((i-1)/imx/jmx,kmx) + 1 ) ) ! k inded
194197
useless = count
195-
end subroutine
198+
end subroutine get_3D_from_array
196199

197-
end subroutine
200+
end subroutine test_12
198201

199202
end module jf_test_12_mod
200203
!*****************************************************************************************
@@ -210,5 +213,6 @@ program jf_test_12
210213
n_errors = 0
211214
call test_12(n_errors)
212215
if ( n_errors /= 0) stop 1
216+
213217
end program jf_test_12
214218
!*****************************************************************************************

src/tests/jf_test_13.f90

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
!*****************************************************************************************
2+
!> author: Jacob Williams
3+
! date: 09/01/2015
4+
!
5+
! Module for the 13th unit test.
6+
7+
module jf_test_13_mod
8+
9+
use json_module
10+
use, intrinsic :: iso_fortran_env , only: error_unit, output_unit
11+
12+
implicit none
13+
14+
contains
15+
16+
subroutine test_13(error_cnt)
17+
18+
!! Tests different real format strings using repeated calls to [[json_initialize]].
19+
20+
implicit none
21+
22+
integer,intent(out) :: error_cnt !! report number of errors to caller
23+
24+
type(json_file) :: my_file
25+
character(kind=CK,len=:),allocatable :: str
26+
integer :: i
27+
28+
character(len=2),dimension(4),parameter :: fmts=['g ','e ','en','es'] !! format statements to test
29+
30+
write(error_unit,'(A)') ''
31+
write(error_unit,'(A)') '================================='
32+
write(error_unit,'(A)') ' TEST 13'
33+
write(error_unit,'(A)') '================================='
34+
write(error_unit,'(A)') ''
35+
36+
error_cnt = 0
37+
38+
do i=1,size(fmts)
39+
40+
call json_initialize(real_format=trim(fmts(i)))
41+
42+
call my_file%load_from_string('{ "value": 1234.56789 }')
43+
if (json_failed()) then
44+
call json_print_error_message(error_unit)
45+
error_cnt = error_cnt + 1
46+
end if
47+
call my_file%print_to_string(str)
48+
if (json_failed()) then
49+
call json_print_error_message(error_unit)
50+
error_cnt = error_cnt + 1
51+
else
52+
write(output_unit,'(A)') str
53+
end if
54+
55+
call my_file%destroy()
56+
57+
end do
58+
59+
end subroutine test_13
60+
61+
end module jf_test_13_mod
62+
!*****************************************************************************************
63+
64+
!*****************************************************************************************
65+
program jf_test_13
66+
67+
!! 13th unit test.
68+
69+
use jf_test_13_mod, only: test_13
70+
implicit none
71+
integer :: n_errors
72+
call test_13(n_errors)
73+
if ( n_errors /= 0) stop 1
74+
75+
end program jf_test_13
76+
!*****************************************************************************************

0 commit comments

Comments
 (0)