Read a numerical value from the file (or string). The routine will determine if it is an integer or a real, and allocate the type accordingly.
Complete rewrite of the original FSON routine, which had some problems.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(json_core), | intent(inout) | :: | json | |||
integer(kind=IK), | intent(in) | :: | unit | file unit number (if parsing from a file) |
||
character(kind=CK,len=*), | intent(in) | :: | str | JSON string (if parsing from a string) |
||
type(json_value), | pointer | :: | value |
subroutine parse_number(json, unit, str, value)
implicit none
class(json_core),intent(inout) :: json
integer(IK),intent(in) :: unit !! file unit number (if parsing from a file)
character(kind=CK,len=*),intent(in) :: str !! JSON string (if parsing from a string)
type(json_value),pointer :: value
character(kind=CK,len=:),allocatable :: tmp !! temp string
character(kind=CK,len=:),allocatable :: saved_err_message !! temp error message for
!! string to int conversion
character(kind=CK,len=1) :: c !! character returned by [[pop_char]]
logical(LK) :: eof !! end of file flag
real(RK) :: rval !! real value
integer(IK) :: ival !! integer value
logical(LK) :: first !! first character
logical(LK) :: is_integer !! it is an integer
integer(IK) :: ip !! index to put next character
!! [to speed up by reducing the number
!! of character string reallocations]
if (.not. json%exception_thrown) then
tmp = blank_chunk
ip = 1
first = .true.
is_integer = .true. !assume it may be an integer, unless otherwise determined
!read one character at a time and accumulate the string:
do
!get the next character:
call json%pop_char(unit, str=str, eof=eof, skip_ws=.true., popped=c)
select case (c)
case(CK_'-',CK_'+') !note: allowing a '+' as the first character here.
if (is_integer .and. (.not. first)) is_integer = .false.
!add it to the string:
!tmp = tmp // c !...original
if (ip>len(tmp)) tmp = tmp // blank_chunk
tmp(ip:ip) = c
ip = ip + 1
case(CK_'.',CK_'E',CK_'e',CK_'D',CK_'d') !can be present in real numbers
if (is_integer) is_integer = .false.
!add it to the string:
!tmp = tmp // c !...original
if (ip>len(tmp)) tmp = tmp // blank_chunk
tmp(ip:ip) = c
ip = ip + 1
case(CK_'0':CK_'9') !valid characters for numbers
!add it to the string:
!tmp = tmp // c !...original
if (ip>len(tmp)) tmp = tmp // blank_chunk
tmp(ip:ip) = c
ip = ip + 1
case default
!push back the last character read:
call json%push_char(c)
!string to value:
if (is_integer) then
! it is an integer:
ival = json%string_to_int(tmp)
if (json%exception_thrown .and. .not. json%strict_integer_type_checking) then
! if it couldn't be converted to an integer,
! then try to convert it to a real value and see if that works
saved_err_message = json%err_message ! keep the original error message
call json%clear_exceptions() ! clear exceptions
rval = json%string_to_dble(tmp)
if (json%exception_thrown) then
! restore original error message and continue
json%err_message = saved_err_message
call json%to_integer(value,ival) ! just so we have something
else
! in this case, we return a real
call json%to_real(value,rval)
end if
else
call json%to_integer(value,ival)
end if
else
! it is a real:
rval = json%string_to_dble(tmp)
call json%to_real(value,rval)
end if
exit !finished
end select
if (first) first = .false.
end do
!cleanup:
if (allocated(tmp)) deallocate(tmp)
end if
end subroutine parse_number