|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932 |
- !*****************************************************************************************
- !> author: Jacob Williams
- ! license: BSD
- !
- ! JSON-Fortran support module for string manipulation.
- !
- !### License
- ! * JSON-Fortran is released under a BSD-style license.
- ! See the [LICENSE](https://github.com/jacobwilliams/json-fortran/blob/master/LICENSE)
- ! file for details.
-
- module json_string_utilities
-
- use,intrinsic :: ieee_arithmetic
- use json_kinds
- use json_parameters
-
- implicit none
-
- private
-
- !******************************************************
- !>
- ! Convert a 'DEFAULT' kind character input to
- ! 'ISO_10646' kind and return it
- interface to_unicode
- module procedure to_uni, to_uni_vec
- end interface
- !******************************************************
-
- #ifdef USE_UCS4
- !******************************************************
- !>
- ! Provide a means to convert to UCS4 while
- ! concatenating UCS4 and default strings
- interface operator(//)
- module procedure ucs4_join_default, default_join_ucs4
- end interface
- public :: operator(//)
- !******************************************************
-
- !******************************************************
- !>
- ! Provide a string `==` operator that works
- ! with mixed kinds
- interface operator(==)
- module procedure ucs4_comp_default, default_comp_ucs4
- end interface
- public :: operator(==)
- !******************************************************
-
- !******************************************************
- !>
- ! Provide a string `/=` operator that works
- ! with mixed kinds
- interface operator(/=)
- module procedure ucs4_neq_default, default_neq_ucs4
- end interface
- public :: operator(/=)
- !******************************************************
- #endif
-
- public :: integer_to_string
- public :: real_to_string
- public :: string_to_integer
- public :: string_to_real
- public :: valid_json_hex
- public :: to_unicode
- public :: escape_string
- public :: unescape_string
- public :: lowercase_string
- public :: replace_string
- public :: decode_rfc6901
- public :: encode_rfc6901
-
- contains
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- ! date: 12/4/2013
- !
- ! Convert an integer to a string.
-
- pure subroutine integer_to_string(ival,int_fmt,str)
-
- implicit none
-
- integer(IK),intent(in) :: ival !! integer value.
- character(kind=CDK,len=*),intent(in) :: int_fmt !! format for integers
- character(kind=CK,len=*),intent(out) :: str !! `ival` converted to a string.
-
- integer(IK) :: istat
-
- write(str,fmt=int_fmt,iostat=istat) ival
-
- if (istat==0) then
- str = adjustl(str)
- else
- str = repeat(star,len(str))
- end if
-
- end subroutine integer_to_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !>
- ! Convert a string into an integer.
- !
- !# History
- ! * Jacob Williams : 12/10/2013 : Rewrote original `parse_integer` routine.
- ! Added error checking.
- ! * Modified by Izaak Beekman
- ! * Jacob Williams : 2/4/2017 : moved core logic to this routine.
-
- subroutine string_to_integer(str,ival,status_ok)
-
- implicit none
-
- character(kind=CK,len=*),intent(in) :: str !! the string to convert to an integer
- integer(IK),intent(out) :: ival !! the integer value
- logical(LK),intent(out) :: status_ok !! true if there were no errors
-
- character(kind=CDK,len=:),allocatable :: digits
- integer(IK) :: ndigits_digits,ndigits,ierr
-
- ! Compute how many digits we need to read
- ndigits = 2*len_trim(str)
- if (ndigits/=0) then
- ndigits_digits = floor(log10(real(ndigits)))+1
- allocate(character(kind=CDK,len=ndigits_digits) :: digits)
- write(digits,'(I0)') ndigits !gfortran will have a runtime error with * edit descriptor here
- ! gfortran bug: '*' edit descriptor for ISO_10646 strings does bad stuff.
- read(str,'(I'//trim(digits)//')',iostat=ierr) ival !string to integer
- ! error check:
- status_ok = (ierr==0)
- else
- status_ok = .false.
- end if
- if (.not. status_ok) ival = 0_IK
-
- end subroutine string_to_integer
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- ! date: 12/4/2013
- !
- ! Convert a real value to a string.
- !
- !### Modified
- ! * Izaak Beekman : 02/24/2015 : added the compact option.
- ! * Jacob Williams : 10/27/2015 : added the star option.
- ! * Jacob Williams : 07/07/2019 : added null and ieee options.
-
- subroutine real_to_string(rval,real_fmt,compact_real,non_normals_to_null,str)
-
- implicit none
-
- real(RK),intent(in) :: rval !! real value.
- character(kind=CDK,len=*),intent(in) :: real_fmt !! format for real numbers
- logical(LK),intent(in) :: compact_real !! compact the string so that it is
- !! displayed with fewer characters
- logical(LK),intent(in) :: non_normals_to_null !! If True, NaN, Infinity, or -Infinity are returned as `null`.
- !! If False, the string value will be returned in quotes
- !! (e.g., "NaN", "Infinity", or "-Infinity" )
- character(kind=CK,len=*),intent(out) :: str !! `rval` converted to a string.
-
- integer(IK) :: istat !! write `iostat` flag
-
- if (ieee_is_finite(rval) .and. .not. ieee_is_nan(rval)) then
-
- ! normal real numbers
-
- if (real_fmt==star) then
- write(str,fmt=*,iostat=istat) rval
- else
- write(str,fmt=real_fmt,iostat=istat) rval
- end if
-
- if (istat==0) then
- !in this case, the default string will be compacted,
- ! so that the same value is displayed with fewer characters.
- if (compact_real) call compact_real_string(str)
- else
- str = repeat(star,len(str)) ! error
- end if
-
- else
- ! special cases for NaN, Infinity, and -Infinity
-
- if (non_normals_to_null) then
- ! return it as a JSON null value
- str = null_str
- else
- ! Let the compiler do the real to string conversion
- ! like before, but put the result in quotes so it
- ! gets printed as a string
- write(str,fmt=*,iostat=istat) rval
- if (istat==0) then
- str = quotation_mark//trim(adjustl(str))//quotation_mark
- else
- str = repeat(star,len(str)) ! error
- end if
- end if
-
- end if
-
- end subroutine real_to_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- ! date: 1/19/2014
- !
- ! Convert a string into a `real(RK)`.
- !
- !# History
- ! * Jacob Williams, 10/27/2015 : Now using `fmt=*`, rather than
- ! `fmt=real_fmt`, since it doesn't work for some unusual cases
- ! (e.g., when `str='1E-5'`).
- ! * Jacob Williams : 2/6/2017 : moved core logic to this routine.
-
- subroutine string_to_real(str,use_quiet_nan,rval,status_ok)
-
- implicit none
-
- character(kind=CK,len=*),intent(in) :: str !! the string to convert to a real
- logical(LK),intent(in) :: use_quiet_nan !! if true, return NaN's as `ieee_quiet_nan`.
- !! otherwise, use `ieee_signaling_nan`.
- real(RK),intent(out) :: rval !! `str` converted to a real value
- logical(LK),intent(out) :: status_ok !! true if there were no errors
-
- integer(IK) :: ierr !! read iostat error code
-
- read(str,fmt=*,iostat=ierr) rval
- status_ok = (ierr==0)
- if (.not. status_ok) then
- rval = 0.0_RK
- else
- if (ieee_support_nan(rval)) then
- if (ieee_is_nan(rval)) then
- ! make sure to return the correct NaN
- if (use_quiet_nan) then
- rval = ieee_value(rval,ieee_quiet_nan)
- else
- rval = ieee_value(rval,ieee_signaling_nan)
- end if
- end if
- end if
- end if
-
- end subroutine string_to_real
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- ! date: 02/24/2015
- !
- ! Compact a string representing a real number, so that
- ! the same value is displayed with fewer characters.
- !
- !# See also
- ! * [[real_to_string]]
-
- subroutine compact_real_string(str)
-
- implicit none
-
- character(kind=CK,len=*),intent(inout) :: str !! string representation of a real number.
-
- character(kind=CK,len=len(str)) :: significand
- character(kind=CK,len=len(str)) :: expnt
- character(kind=CK,len=2) :: separator
- integer(IK) :: exp_start
- integer(IK) :: decimal_pos
- integer(IK) :: sig_trim
- integer(IK) :: exp_trim
- integer(IK) :: i !! counter
-
- str = adjustl(str)
- exp_start = scan(str,CK_'eEdD')
- if (exp_start == 0) exp_start = scan(str,CK_'-+',back=.true.)
- decimal_pos = scan(str,CK_'.')
- if (exp_start /= 0) separator = str(exp_start:exp_start)
-
- if ( exp_start < decimal_pos ) then !possibly signed, exponent-less float
-
- significand = str
- sig_trim = len(trim(significand))
- do i = len(trim(significand)),decimal_pos+2,-1 !look from right to left at 0s
- !but save one after the decimal place
- if (significand(i:i) == '0') then
- sig_trim = i-1
- else
- exit
- end if
- end do
- str = trim(significand(1:sig_trim))
-
- else if (exp_start > decimal_pos) then !float has exponent
-
- significand = str(1:exp_start-1)
- sig_trim = len(trim(significand))
- do i = len(trim(significand)),decimal_pos+2,-1 !look from right to left at 0s
- if (significand(i:i) == '0') then
- sig_trim = i-1
- else
- exit
- end if
- end do
- expnt = adjustl(str(exp_start+1:))
- if (expnt(1:1) == '+' .or. expnt(1:1) == '-') then
- separator = trim(adjustl(separator))//expnt(1:1)
- exp_start = exp_start + 1
- expnt = adjustl(str(exp_start+1:))
- end if
- exp_trim = 1
- do i = 1,(len(trim(expnt))-1) !look at exponent leading zeros saving last
- if (expnt(i:i) == '0') then
- exp_trim = i+1
- else
- exit
- end if
- end do
- str = trim(adjustl(significand(1:sig_trim)))// &
- trim(adjustl(separator))// &
- trim(adjustl(expnt(exp_trim:)))
-
- !else ! mal-formed real, BUT this code should be unreachable
-
- end if
-
- end subroutine compact_real_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- ! date: 1/21/2014
- !
- ! Add the escape characters to a string for adding to JSON.
-
- subroutine escape_string(str_in, str_out, escape_solidus)
-
- implicit none
-
- character(kind=CK,len=*),intent(in) :: str_in
- character(kind=CK,len=:),allocatable,intent(out) :: str_out
- logical(LK),intent(in) :: escape_solidus !! if the solidus (forward slash)
- !! is also to be escaped
-
- integer(IK) :: i !! counter
- integer(IK) :: ipos !! accumulated string size
- !! (so we can allocate it in chunks for
- !! greater runtime efficiency)
- character(kind=CK,len=1) :: c !! for reading `str_in` one character at a time.
- #if defined __GFORTRAN__
- character(kind=CK,len=:),allocatable :: tmp !! workaround for bug in gfortran 6.1
- #endif
- logical :: to_be_escaped !! if there are characters to be escaped
-
- character(kind=CK,len=*),parameter :: specials_no_slash = quotation_mark//&
- backslash//&
- bspace//&
- formfeed//&
- newline//&
- carriage_return//&
- horizontal_tab
-
- character(kind=CK,len=*),parameter :: specials = specials_no_slash//slash
-
- !Do a quick scan for the special characters,
- ! if any are present, then process the string,
- ! otherwise, return the string as is.
- if (escape_solidus) then
- to_be_escaped = scan(str_in,specials)>0
- else
- to_be_escaped = scan(str_in,specials_no_slash)>0
- end if
-
- if (to_be_escaped) then
-
- str_out = repeat(space,chunk_size)
- ipos = 1
-
- !go through the string and look for special characters:
- do i=1,len(str_in)
-
- c = str_in(i:i) !get next character in the input string
-
- !if the string is not big enough, then add another chunk:
- if (ipos+3>len(str_out)) str_out = str_out // blank_chunk
-
- select case(c)
- case(backslash)
-
- !test for unicode sequence: '\uXXXX'
- ![don't add an extra '\' for those]
- if (i+5<=len(str_in)) then
- if (str_in(i+1:i+1)==CK_'u' .and. &
- valid_json_hex(str_in(i+2:i+5))) then
- str_out(ipos:ipos) = c
- ipos = ipos + 1
- cycle
- end if
- end if
-
- str_out(ipos:ipos+1) = backslash//c
- ipos = ipos + 2
-
- case(quotation_mark)
- str_out(ipos:ipos+1) = backslash//c
- ipos = ipos + 2
- case(slash)
- if (escape_solidus) then
- str_out(ipos:ipos+1) = backslash//c
- ipos = ipos + 2
- else
- str_out(ipos:ipos) = c
- ipos = ipos + 1
- end if
- case(bspace)
- str_out(ipos:ipos+1) = '\b'
- ipos = ipos + 2
- case(formfeed)
- str_out(ipos:ipos+1) = '\f'
- ipos = ipos + 2
- case(newline)
- str_out(ipos:ipos+1) = '\n'
- ipos = ipos + 2
- case(carriage_return)
- str_out(ipos:ipos+1) = '\r'
- ipos = ipos + 2
- case(horizontal_tab)
- str_out(ipos:ipos+1) = '\t'
- ipos = ipos + 2
- case default
- str_out(ipos:ipos) = c
- ipos = ipos + 1
- end select
-
- end do
-
- !trim the string if necessary:
- if (ipos<len(str_out)+1) then
- if (ipos==1) then
- str_out = CK_''
- else
- #if defined __GFORTRAN__
- tmp = str_out(1:ipos-1) !workaround for bug in gfortran 6.1
- str_out = tmp
- #else
- str_out = str_out(1:ipos-1) !original
- #endif
- end if
- end if
-
- else
-
- str_out = str_in
-
- end if
-
- end subroutine escape_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !>
- ! Remove the escape characters from a JSON string and return it.
- !
- ! The escaped characters are denoted by the `\` character:
- !
- ! * `\"` - quotation mark
- ! * `\\` - reverse solidus
- ! * `\/` - solidus
- ! * `\b` - backspace
- ! * `\f` - formfeed
- ! * `\n` - newline (LF)
- ! * `\r` - carriage return (CR)
- ! * `\t` - horizontal tab
- ! * `\uXXXX` - 4 hexadecimal digits
-
- subroutine unescape_string(str, error_message)
-
- implicit none
-
- character(kind=CK,len=:),allocatable,intent(inout) :: str !! in: string as stored
- !! in a [[json_value]].
- !! out: decoded string.
- character(kind=CK,len=:),allocatable,intent(out) :: error_message !! will be allocated if
- !! there was an error
-
- integer :: i !! counter
- integer :: n !! length of `str`
- integer :: m !! length of `str_tmp`
- character(kind=CK,len=1) :: c !! for scanning each character in string
- character(kind=CK,len=:),allocatable :: str_tmp !! temp decoded string (if the input
- !! string contains an escape character
- !! and needs to be decoded).
-
- if (scan(str,backslash)>0) then
-
- !there is at least one escape character, so process this string:
-
- n = len(str)
- str_tmp = repeat(space,n) !size the output string (will be trimmed later)
- m = 0 !counter in str_tmp
- i = 0 !counter in str
-
- do
-
- i = i + 1
- if (i>n) exit ! finished
- c = str(i:i) ! get next character in the string
-
- if (c == backslash) then
-
- if (i<n) then
-
- i = i + 1
- c = str(i:i) !character after the escape
-
- select case(c)
- case (quotation_mark,backslash,slash)
- !use d as is
- m = m + 1
- str_tmp(m:m) = c
- case (CK_'b')
- c = bspace
- m = m + 1
- str_tmp(m:m) = c
- case (CK_'f')
- c = formfeed
- m = m + 1
- str_tmp(m:m) = c
- case (CK_'n')
- c = newline
- m = m + 1
- str_tmp(m:m) = c
- case (CK_'r')
- c = carriage_return
- m = m + 1
- str_tmp(m:m) = c
- case (CK_'t')
- c = horizontal_tab
- m = m + 1
- str_tmp(m:m) = c
-
- case (CK_'u') ! expecting 4 hexadecimal digits after
- ! the escape character [\uXXXX]
-
- !for now, we are just returning them as is
- ![not checking to see if it is a valid hex value]
- !
- ! Example:
- ! 123456
- ! \uXXXX
-
- if (i+4<=n) then
-
- ! validate the hex string:
- if (valid_json_hex(str(i+1:i+4))) then
- m = m + 1
- str_tmp(m:m+5) = str(i-1:i+4)
- i = i + 4
- m = m + 5
- else
- error_message = 'Error in unescape_string:'//&
- ' Invalid hexadecimal sequence in string "'//&
- trim(str)//'" ['//str(i-1:i+4)//']'
- if (allocated(str_tmp)) deallocate(str_tmp)
- return
- end if
- else
- error_message = 'Error in unescape_string:'//&
- ' Invalid hexadecimal sequence in string "'//&
- trim(str)//'" ['//str(i-1:)//']'
- if (allocated(str_tmp)) deallocate(str_tmp)
- return
- end if
-
- case default
-
- !unknown escape character
- error_message = 'Error in unescape_string:'//&
- ' unknown escape sequence in string "'//&
- trim(str)//'" ['//backslash//c//']'
- if (allocated(str_tmp)) deallocate(str_tmp)
- return
-
- end select
-
- else
- ! an escape character is the last character in
- ! the string. This is an error.
- error_message = 'Error in unescape_string:'//&
- ' invalid escape character in string "'//&
- trim(str)//'"'
- if (allocated(str_tmp)) deallocate(str_tmp)
- return
- end if
-
- else
- m = m + 1
- str_tmp(m:m) = c
- end if
-
- end do
-
- !trim trailing space:
- str = str_tmp(1:m)
-
- end if
-
- end subroutine unescape_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- ! date:6/14/2014
- !
- ! Returns true if the string is a valid 4-digit hex string.
- !
- !# Examples
- !```fortran
- ! valid_json_hex('0000') !returns true
- ! valid_json_hex('ABC4') !returns true
- ! valid_json_hex('AB') !returns false (< 4 characters)
- ! valid_json_hex('WXYZ') !returns false (invalid characters)
- !```
-
- pure function valid_json_hex(str) result(valid)
-
- implicit none
-
- logical(LK) :: valid !! is str a value 4-digit hex string
- character(kind=CK,len=*),intent(in) :: str !! the string to check.
-
- integer(IK) :: n !! length of `str`
- integer(IK) :: i !! counter
-
- !> an array of the valid hex characters
- character(kind=CK,len=1),dimension(22),parameter :: valid_chars = &
- [ (achar(i),i=48,57), & ! decimal digits
- (achar(i),i=65,70), & ! capital A-F
- (achar(i),i=97,102) ] ! lowercase a-f
-
- !initialize
- valid = .false.
-
- !check all the characters in the string:
- n = len(str)
- if (n==4) then
- do i=1,n
- if (.not. any(str(i:i)==valid_chars)) return
- end do
- valid = .true. !all are in the set, so it is OK
- end if
-
- end function valid_json_hex
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- !
- ! Convert string to unicode (CDK to CK).
-
- pure function to_uni(str)
-
- implicit none
-
- character(kind=CDK,len=*), intent(in) :: str
- character(kind=CK,len=len(str)) :: to_uni
-
- to_uni = str
-
- end function to_uni
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- !
- ! Convert array of strings to unicode (CDK to CK).
- !
- !@note JW: may be able to remove this by making [[to_uni]] PURE ELEMENTAL ?
-
- pure function to_uni_vec(str)
-
- implicit none
-
- character(kind=CDK,len=*), dimension(:), intent(in) :: str
- character(kind=CK,len=len(str)), dimension(size(str)) :: to_uni_vec
-
- to_uni_vec = str
-
- end function to_uni_vec
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- !
- ! `CK`//`CDK` operator.
-
- pure function ucs4_join_default(ucs4_str,def_str) result(res)
-
- implicit none
-
- character(kind=CK, len=*), intent(in) :: ucs4_str
- character(kind=CDK,len=*), intent(in) :: def_str
- character(kind=CK,len=(len(ucs4_str)+len(def_str))) :: res
-
- res = ucs4_str//to_unicode(def_str)
-
- end function ucs4_join_default
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- !
- ! `CDK`//`CK` operator.
-
- pure function default_join_ucs4(def_str,ucs4_str) result(res)
-
- implicit none
-
- character(kind=CDK,len=*), intent(in) :: def_str
- character(kind=CK, len=*), intent(in) :: ucs4_str
- character(kind=CK,len=(len(def_str)+len(ucs4_str))) :: res
-
- res = to_unicode(def_str)//ucs4_str
-
- end function default_join_ucs4
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- !
- ! `CK`==`CDK` operator.
-
- pure elemental function ucs4_comp_default(ucs4_str,def_str) result(res)
-
- implicit none
-
- character(kind=CK, len=*), intent(in) :: ucs4_str
- character(kind=CDK,len=*), intent(in) :: def_str
- logical(LK) :: res
-
- res = ( ucs4_str == to_unicode(def_str) )
-
- end function ucs4_comp_default
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Izaak Beekman
- !
- ! `CDK`==`CK` operator.
-
- pure elemental function default_comp_ucs4(def_str,ucs4_str) result(res)
-
- implicit none
-
- character(kind=CDK,len=*), intent(in) :: def_str
- character(kind=CK, len=*), intent(in) :: ucs4_str
- logical(LK) :: res
-
- res = (to_unicode(def_str) == ucs4_str)
-
- end function default_comp_ucs4
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- !
- ! `CK`/=`CDK` operator.
-
- pure elemental function ucs4_neq_default(ucs4_str,def_str) result(res)
-
- implicit none
-
- character(kind=CK, len=*), intent(in) :: ucs4_str
- character(kind=CDK,len=*), intent(in) :: def_str
- logical(LK) :: res
-
- res = ( ucs4_str /= to_unicode(def_str) )
-
- end function ucs4_neq_default
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- !
- ! `CDK`/=`CK` operator.
-
- pure elemental function default_neq_ucs4(def_str,ucs4_str) result(res)
-
- implicit none
-
- character(kind=CDK,len=*), intent(in) :: def_str
- character(kind=CK, len=*), intent(in) :: ucs4_str
- logical(LK) :: res
-
- res = (to_unicode(def_str) /= ucs4_str)
-
- end function default_neq_ucs4
- !*****************************************************************************************
-
- !*****************************************************************************************
- !> author: Jacob Williams
- !
- ! Returns lowercase version of the `CK` string.
-
- pure function lowercase_string(str) result(s_lower)
-
- implicit none
-
- character(kind=CK,len=*),intent(in) :: str !! input string
- character(kind=CK,len=(len(str))) :: s_lower !! lowercase version of the string
-
- integer :: i !! counter
- integer :: j !! index of uppercase character
-
- s_lower = str
-
- do i = 1, len_trim(str)
- j = index(upper,s_lower(i:i))
- if (j>0) s_lower(i:i) = lower(j:j)
- end do
-
- end function lowercase_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !>
- ! Replace all occurrences of `s1` in `str` with `s2`.
- !
- ! A case-sensitive match is used.
- !
- !@note `str` must be allocated.
-
- pure subroutine replace_string(str,s1,s2)
-
- implicit none
-
- character(kind=CK,len=:),allocatable,intent(inout) :: str
- character(kind=CK,len=*),intent(in) :: s1
- character(kind=CK,len=*),intent(in) :: s2
-
- character(kind=CK,len=:),allocatable :: tmp !! temporary string for accumulating result
- integer(IK) :: i !! counter
- integer(IK) :: n !! for accumulating the string
- integer(IK) :: ilen !! length of `str` string
- integer(IK) :: ilen1 !! length of `s1` string
-
- if (len(str)>0) then
-
- tmp = CK_'' ! initialize
- ilen1 = len(s1)
-
- ! .
- ! '123ab789'
-
- do
- ilen = len(str)
- i = index(str,s1)
- if (i>0) then
- if (i>1) tmp = tmp//str(1:i-1)
- tmp = tmp//s2 ! replace s1 with s2 in new string
- n = i+ilen1 ! start of remainder of str to keep
- if (n<=ilen) then
- str = str(n:ilen)
- else
- ! done
- exit
- end if
- else
- ! done: get remainder of string
- tmp = tmp//str
- exit
- end if
- end do
-
- str = tmp
-
- end if
-
- end subroutine replace_string
- !*****************************************************************************************
-
- !*****************************************************************************************
- !>
- ! Decode a string from the "JSON Pointer" RFC 6901 format.
- !
- ! It replaces `~1` with `/` and `~0` with `~`.
-
- pure function decode_rfc6901(str) result(str_out)
-
- implicit none
-
- character(kind=CK,len=*),intent(in) :: str
- character(kind=CK,len=:),allocatable :: str_out
-
- str_out = str
-
- call replace_string(str_out,tilde//CK_'1',slash)
- call replace_string(str_out,tilde//CK_'0',tilde)
-
- end function decode_rfc6901
- !*****************************************************************************************
-
- !*****************************************************************************************
- !>
- ! Encode a string into the "JSON Pointer" RFC 6901 format.
- !
- ! It replaces `~` with `~0` and `/` with `~1`.
-
- pure function encode_rfc6901(str) result(str_out)
-
- implicit none
-
- character(kind=CK,len=*),intent(in) :: str
- character(kind=CK,len=:),allocatable :: str_out
-
- str_out = str
-
- call replace_string(str_out,tilde,tilde//CK_'0')
- call replace_string(str_out,slash,tilde//CK_'1')
-
- end function encode_rfc6901
- !*****************************************************************************************
-
- end module json_string_utilities
- !*****************************************************************************************
|