Simulation Core
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

933 regels
31 KiB

  1. !*****************************************************************************************
  2. !> author: Jacob Williams
  3. ! license: BSD
  4. !
  5. ! JSON-Fortran support module for string manipulation.
  6. !
  7. !### License
  8. ! * JSON-Fortran is released under a BSD-style license.
  9. ! See the [LICENSE](https://github.com/jacobwilliams/json-fortran/blob/master/LICENSE)
  10. ! file for details.
  11. module json_string_utilities
  12. use,intrinsic :: ieee_arithmetic
  13. use json_kinds
  14. use json_parameters
  15. implicit none
  16. private
  17. !******************************************************
  18. !>
  19. ! Convert a 'DEFAULT' kind character input to
  20. ! 'ISO_10646' kind and return it
  21. interface to_unicode
  22. module procedure to_uni, to_uni_vec
  23. end interface
  24. !******************************************************
  25. #ifdef USE_UCS4
  26. !******************************************************
  27. !>
  28. ! Provide a means to convert to UCS4 while
  29. ! concatenating UCS4 and default strings
  30. interface operator(//)
  31. module procedure ucs4_join_default, default_join_ucs4
  32. end interface
  33. public :: operator(//)
  34. !******************************************************
  35. !******************************************************
  36. !>
  37. ! Provide a string `==` operator that works
  38. ! with mixed kinds
  39. interface operator(==)
  40. module procedure ucs4_comp_default, default_comp_ucs4
  41. end interface
  42. public :: operator(==)
  43. !******************************************************
  44. !******************************************************
  45. !>
  46. ! Provide a string `/=` operator that works
  47. ! with mixed kinds
  48. interface operator(/=)
  49. module procedure ucs4_neq_default, default_neq_ucs4
  50. end interface
  51. public :: operator(/=)
  52. !******************************************************
  53. #endif
  54. public :: integer_to_string
  55. public :: real_to_string
  56. public :: string_to_integer
  57. public :: string_to_real
  58. public :: valid_json_hex
  59. public :: to_unicode
  60. public :: escape_string
  61. public :: unescape_string
  62. public :: lowercase_string
  63. public :: replace_string
  64. public :: decode_rfc6901
  65. public :: encode_rfc6901
  66. contains
  67. !*****************************************************************************************
  68. !*****************************************************************************************
  69. !> author: Jacob Williams
  70. ! date: 12/4/2013
  71. !
  72. ! Convert an integer to a string.
  73. pure subroutine integer_to_string(ival,int_fmt,str)
  74. implicit none
  75. integer(IK),intent(in) :: ival !! integer value.
  76. character(kind=CDK,len=*),intent(in) :: int_fmt !! format for integers
  77. character(kind=CK,len=*),intent(out) :: str !! `ival` converted to a string.
  78. integer(IK) :: istat
  79. write(str,fmt=int_fmt,iostat=istat) ival
  80. if (istat==0) then
  81. str = adjustl(str)
  82. else
  83. str = repeat(star,len(str))
  84. end if
  85. end subroutine integer_to_string
  86. !*****************************************************************************************
  87. !*****************************************************************************************
  88. !>
  89. ! Convert a string into an integer.
  90. !
  91. !# History
  92. ! * Jacob Williams : 12/10/2013 : Rewrote original `parse_integer` routine.
  93. ! Added error checking.
  94. ! * Modified by Izaak Beekman
  95. ! * Jacob Williams : 2/4/2017 : moved core logic to this routine.
  96. subroutine string_to_integer(str,ival,status_ok)
  97. implicit none
  98. character(kind=CK,len=*),intent(in) :: str !! the string to convert to an integer
  99. integer(IK),intent(out) :: ival !! the integer value
  100. logical(LK),intent(out) :: status_ok !! true if there were no errors
  101. character(kind=CDK,len=:),allocatable :: digits
  102. integer(IK) :: ndigits_digits,ndigits,ierr
  103. ! Compute how many digits we need to read
  104. ndigits = 2*len_trim(str)
  105. if (ndigits/=0) then
  106. ndigits_digits = floor(log10(real(ndigits)))+1
  107. allocate(character(kind=CDK,len=ndigits_digits) :: digits)
  108. write(digits,'(I0)') ndigits !gfortran will have a runtime error with * edit descriptor here
  109. ! gfortran bug: '*' edit descriptor for ISO_10646 strings does bad stuff.
  110. read(str,'(I'//trim(digits)//')',iostat=ierr) ival !string to integer
  111. ! error check:
  112. status_ok = (ierr==0)
  113. else
  114. status_ok = .false.
  115. end if
  116. if (.not. status_ok) ival = 0_IK
  117. end subroutine string_to_integer
  118. !*****************************************************************************************
  119. !*****************************************************************************************
  120. !> author: Jacob Williams
  121. ! date: 12/4/2013
  122. !
  123. ! Convert a real value to a string.
  124. !
  125. !### Modified
  126. ! * Izaak Beekman : 02/24/2015 : added the compact option.
  127. ! * Jacob Williams : 10/27/2015 : added the star option.
  128. ! * Jacob Williams : 07/07/2019 : added null and ieee options.
  129. subroutine real_to_string(rval,real_fmt,compact_real,non_normals_to_null,str)
  130. implicit none
  131. real(RK),intent(in) :: rval !! real value.
  132. character(kind=CDK,len=*),intent(in) :: real_fmt !! format for real numbers
  133. logical(LK),intent(in) :: compact_real !! compact the string so that it is
  134. !! displayed with fewer characters
  135. logical(LK),intent(in) :: non_normals_to_null !! If True, NaN, Infinity, or -Infinity are returned as `null`.
  136. !! If False, the string value will be returned in quotes
  137. !! (e.g., "NaN", "Infinity", or "-Infinity" )
  138. character(kind=CK,len=*),intent(out) :: str !! `rval` converted to a string.
  139. integer(IK) :: istat !! write `iostat` flag
  140. if (ieee_is_finite(rval) .and. .not. ieee_is_nan(rval)) then
  141. ! normal real numbers
  142. if (real_fmt==star) then
  143. write(str,fmt=*,iostat=istat) rval
  144. else
  145. write(str,fmt=real_fmt,iostat=istat) rval
  146. end if
  147. if (istat==0) then
  148. !in this case, the default string will be compacted,
  149. ! so that the same value is displayed with fewer characters.
  150. if (compact_real) call compact_real_string(str)
  151. else
  152. str = repeat(star,len(str)) ! error
  153. end if
  154. else
  155. ! special cases for NaN, Infinity, and -Infinity
  156. if (non_normals_to_null) then
  157. ! return it as a JSON null value
  158. str = null_str
  159. else
  160. ! Let the compiler do the real to string conversion
  161. ! like before, but put the result in quotes so it
  162. ! gets printed as a string
  163. write(str,fmt=*,iostat=istat) rval
  164. if (istat==0) then
  165. str = quotation_mark//trim(adjustl(str))//quotation_mark
  166. else
  167. str = repeat(star,len(str)) ! error
  168. end if
  169. end if
  170. end if
  171. end subroutine real_to_string
  172. !*****************************************************************************************
  173. !*****************************************************************************************
  174. !> author: Jacob Williams
  175. ! date: 1/19/2014
  176. !
  177. ! Convert a string into a `real(RK)`.
  178. !
  179. !# History
  180. ! * Jacob Williams, 10/27/2015 : Now using `fmt=*`, rather than
  181. ! `fmt=real_fmt`, since it doesn't work for some unusual cases
  182. ! (e.g., when `str='1E-5'`).
  183. ! * Jacob Williams : 2/6/2017 : moved core logic to this routine.
  184. subroutine string_to_real(str,use_quiet_nan,rval,status_ok)
  185. implicit none
  186. character(kind=CK,len=*),intent(in) :: str !! the string to convert to a real
  187. logical(LK),intent(in) :: use_quiet_nan !! if true, return NaN's as `ieee_quiet_nan`.
  188. !! otherwise, use `ieee_signaling_nan`.
  189. real(RK),intent(out) :: rval !! `str` converted to a real value
  190. logical(LK),intent(out) :: status_ok !! true if there were no errors
  191. integer(IK) :: ierr !! read iostat error code
  192. read(str,fmt=*,iostat=ierr) rval
  193. status_ok = (ierr==0)
  194. if (.not. status_ok) then
  195. rval = 0.0_RK
  196. else
  197. if (ieee_support_nan(rval)) then
  198. if (ieee_is_nan(rval)) then
  199. ! make sure to return the correct NaN
  200. if (use_quiet_nan) then
  201. rval = ieee_value(rval,ieee_quiet_nan)
  202. else
  203. rval = ieee_value(rval,ieee_signaling_nan)
  204. end if
  205. end if
  206. end if
  207. end if
  208. end subroutine string_to_real
  209. !*****************************************************************************************
  210. !*****************************************************************************************
  211. !> author: Izaak Beekman
  212. ! date: 02/24/2015
  213. !
  214. ! Compact a string representing a real number, so that
  215. ! the same value is displayed with fewer characters.
  216. !
  217. !# See also
  218. ! * [[real_to_string]]
  219. subroutine compact_real_string(str)
  220. implicit none
  221. character(kind=CK,len=*),intent(inout) :: str !! string representation of a real number.
  222. character(kind=CK,len=len(str)) :: significand
  223. character(kind=CK,len=len(str)) :: expnt
  224. character(kind=CK,len=2) :: separator
  225. integer(IK) :: exp_start
  226. integer(IK) :: decimal_pos
  227. integer(IK) :: sig_trim
  228. integer(IK) :: exp_trim
  229. integer(IK) :: i !! counter
  230. str = adjustl(str)
  231. exp_start = scan(str,CK_'eEdD')
  232. if (exp_start == 0) exp_start = scan(str,CK_'-+',back=.true.)
  233. decimal_pos = scan(str,CK_'.')
  234. if (exp_start /= 0) separator = str(exp_start:exp_start)
  235. if ( exp_start < decimal_pos ) then !possibly signed, exponent-less float
  236. significand = str
  237. sig_trim = len(trim(significand))
  238. do i = len(trim(significand)),decimal_pos+2,-1 !look from right to left at 0s
  239. !but save one after the decimal place
  240. if (significand(i:i) == '0') then
  241. sig_trim = i-1
  242. else
  243. exit
  244. end if
  245. end do
  246. str = trim(significand(1:sig_trim))
  247. else if (exp_start > decimal_pos) then !float has exponent
  248. significand = str(1:exp_start-1)
  249. sig_trim = len(trim(significand))
  250. do i = len(trim(significand)),decimal_pos+2,-1 !look from right to left at 0s
  251. if (significand(i:i) == '0') then
  252. sig_trim = i-1
  253. else
  254. exit
  255. end if
  256. end do
  257. expnt = adjustl(str(exp_start+1:))
  258. if (expnt(1:1) == '+' .or. expnt(1:1) == '-') then
  259. separator = trim(adjustl(separator))//expnt(1:1)
  260. exp_start = exp_start + 1
  261. expnt = adjustl(str(exp_start+1:))
  262. end if
  263. exp_trim = 1
  264. do i = 1,(len(trim(expnt))-1) !look at exponent leading zeros saving last
  265. if (expnt(i:i) == '0') then
  266. exp_trim = i+1
  267. else
  268. exit
  269. end if
  270. end do
  271. str = trim(adjustl(significand(1:sig_trim)))// &
  272. trim(adjustl(separator))// &
  273. trim(adjustl(expnt(exp_trim:)))
  274. !else ! mal-formed real, BUT this code should be unreachable
  275. end if
  276. end subroutine compact_real_string
  277. !*****************************************************************************************
  278. !*****************************************************************************************
  279. !> author: Jacob Williams
  280. ! date: 1/21/2014
  281. !
  282. ! Add the escape characters to a string for adding to JSON.
  283. subroutine escape_string(str_in, str_out, escape_solidus)
  284. implicit none
  285. character(kind=CK,len=*),intent(in) :: str_in
  286. character(kind=CK,len=:),allocatable,intent(out) :: str_out
  287. logical(LK),intent(in) :: escape_solidus !! if the solidus (forward slash)
  288. !! is also to be escaped
  289. integer(IK) :: i !! counter
  290. integer(IK) :: ipos !! accumulated string size
  291. !! (so we can allocate it in chunks for
  292. !! greater runtime efficiency)
  293. character(kind=CK,len=1) :: c !! for reading `str_in` one character at a time.
  294. #if defined __GFORTRAN__
  295. character(kind=CK,len=:),allocatable :: tmp !! workaround for bug in gfortran 6.1
  296. #endif
  297. logical :: to_be_escaped !! if there are characters to be escaped
  298. character(kind=CK,len=*),parameter :: specials_no_slash = quotation_mark//&
  299. backslash//&
  300. bspace//&
  301. formfeed//&
  302. newline//&
  303. carriage_return//&
  304. horizontal_tab
  305. character(kind=CK,len=*),parameter :: specials = specials_no_slash//slash
  306. !Do a quick scan for the special characters,
  307. ! if any are present, then process the string,
  308. ! otherwise, return the string as is.
  309. if (escape_solidus) then
  310. to_be_escaped = scan(str_in,specials)>0
  311. else
  312. to_be_escaped = scan(str_in,specials_no_slash)>0
  313. end if
  314. if (to_be_escaped) then
  315. str_out = repeat(space,chunk_size)
  316. ipos = 1
  317. !go through the string and look for special characters:
  318. do i=1,len(str_in)
  319. c = str_in(i:i) !get next character in the input string
  320. !if the string is not big enough, then add another chunk:
  321. if (ipos+3>len(str_out)) str_out = str_out // blank_chunk
  322. select case(c)
  323. case(backslash)
  324. !test for unicode sequence: '\uXXXX'
  325. ![don't add an extra '\' for those]
  326. if (i+5<=len(str_in)) then
  327. if (str_in(i+1:i+1)==CK_'u' .and. &
  328. valid_json_hex(str_in(i+2:i+5))) then
  329. str_out(ipos:ipos) = c
  330. ipos = ipos + 1
  331. cycle
  332. end if
  333. end if
  334. str_out(ipos:ipos+1) = backslash//c
  335. ipos = ipos + 2
  336. case(quotation_mark)
  337. str_out(ipos:ipos+1) = backslash//c
  338. ipos = ipos + 2
  339. case(slash)
  340. if (escape_solidus) then
  341. str_out(ipos:ipos+1) = backslash//c
  342. ipos = ipos + 2
  343. else
  344. str_out(ipos:ipos) = c
  345. ipos = ipos + 1
  346. end if
  347. case(bspace)
  348. str_out(ipos:ipos+1) = '\b'
  349. ipos = ipos + 2
  350. case(formfeed)
  351. str_out(ipos:ipos+1) = '\f'
  352. ipos = ipos + 2
  353. case(newline)
  354. str_out(ipos:ipos+1) = '\n'
  355. ipos = ipos + 2
  356. case(carriage_return)
  357. str_out(ipos:ipos+1) = '\r'
  358. ipos = ipos + 2
  359. case(horizontal_tab)
  360. str_out(ipos:ipos+1) = '\t'
  361. ipos = ipos + 2
  362. case default
  363. str_out(ipos:ipos) = c
  364. ipos = ipos + 1
  365. end select
  366. end do
  367. !trim the string if necessary:
  368. if (ipos<len(str_out)+1) then
  369. if (ipos==1) then
  370. str_out = CK_''
  371. else
  372. #if defined __GFORTRAN__
  373. tmp = str_out(1:ipos-1) !workaround for bug in gfortran 6.1
  374. str_out = tmp
  375. #else
  376. str_out = str_out(1:ipos-1) !original
  377. #endif
  378. end if
  379. end if
  380. else
  381. str_out = str_in
  382. end if
  383. end subroutine escape_string
  384. !*****************************************************************************************
  385. !*****************************************************************************************
  386. !>
  387. ! Remove the escape characters from a JSON string and return it.
  388. !
  389. ! The escaped characters are denoted by the `\` character:
  390. !
  391. ! * `\"` - quotation mark
  392. ! * `\\` - reverse solidus
  393. ! * `\/` - solidus
  394. ! * `\b` - backspace
  395. ! * `\f` - formfeed
  396. ! * `\n` - newline (LF)
  397. ! * `\r` - carriage return (CR)
  398. ! * `\t` - horizontal tab
  399. ! * `\uXXXX` - 4 hexadecimal digits
  400. subroutine unescape_string(str, error_message)
  401. implicit none
  402. character(kind=CK,len=:),allocatable,intent(inout) :: str !! in: string as stored
  403. !! in a [[json_value]].
  404. !! out: decoded string.
  405. character(kind=CK,len=:),allocatable,intent(out) :: error_message !! will be allocated if
  406. !! there was an error
  407. integer :: i !! counter
  408. integer :: n !! length of `str`
  409. integer :: m !! length of `str_tmp`
  410. character(kind=CK,len=1) :: c !! for scanning each character in string
  411. character(kind=CK,len=:),allocatable :: str_tmp !! temp decoded string (if the input
  412. !! string contains an escape character
  413. !! and needs to be decoded).
  414. if (scan(str,backslash)>0) then
  415. !there is at least one escape character, so process this string:
  416. n = len(str)
  417. str_tmp = repeat(space,n) !size the output string (will be trimmed later)
  418. m = 0 !counter in str_tmp
  419. i = 0 !counter in str
  420. do
  421. i = i + 1
  422. if (i>n) exit ! finished
  423. c = str(i:i) ! get next character in the string
  424. if (c == backslash) then
  425. if (i<n) then
  426. i = i + 1
  427. c = str(i:i) !character after the escape
  428. select case(c)
  429. case (quotation_mark,backslash,slash)
  430. !use d as is
  431. m = m + 1
  432. str_tmp(m:m) = c
  433. case (CK_'b')
  434. c = bspace
  435. m = m + 1
  436. str_tmp(m:m) = c
  437. case (CK_'f')
  438. c = formfeed
  439. m = m + 1
  440. str_tmp(m:m) = c
  441. case (CK_'n')
  442. c = newline
  443. m = m + 1
  444. str_tmp(m:m) = c
  445. case (CK_'r')
  446. c = carriage_return
  447. m = m + 1
  448. str_tmp(m:m) = c
  449. case (CK_'t')
  450. c = horizontal_tab
  451. m = m + 1
  452. str_tmp(m:m) = c
  453. case (CK_'u') ! expecting 4 hexadecimal digits after
  454. ! the escape character [\uXXXX]
  455. !for now, we are just returning them as is
  456. ![not checking to see if it is a valid hex value]
  457. !
  458. ! Example:
  459. ! 123456
  460. ! \uXXXX
  461. if (i+4<=n) then
  462. ! validate the hex string:
  463. if (valid_json_hex(str(i+1:i+4))) then
  464. m = m + 1
  465. str_tmp(m:m+5) = str(i-1:i+4)
  466. i = i + 4
  467. m = m + 5
  468. else
  469. error_message = 'Error in unescape_string:'//&
  470. ' Invalid hexadecimal sequence in string "'//&
  471. trim(str)//'" ['//str(i-1:i+4)//']'
  472. if (allocated(str_tmp)) deallocate(str_tmp)
  473. return
  474. end if
  475. else
  476. error_message = 'Error in unescape_string:'//&
  477. ' Invalid hexadecimal sequence in string "'//&
  478. trim(str)//'" ['//str(i-1:)//']'
  479. if (allocated(str_tmp)) deallocate(str_tmp)
  480. return
  481. end if
  482. case default
  483. !unknown escape character
  484. error_message = 'Error in unescape_string:'//&
  485. ' unknown escape sequence in string "'//&
  486. trim(str)//'" ['//backslash//c//']'
  487. if (allocated(str_tmp)) deallocate(str_tmp)
  488. return
  489. end select
  490. else
  491. ! an escape character is the last character in
  492. ! the string. This is an error.
  493. error_message = 'Error in unescape_string:'//&
  494. ' invalid escape character in string "'//&
  495. trim(str)//'"'
  496. if (allocated(str_tmp)) deallocate(str_tmp)
  497. return
  498. end if
  499. else
  500. m = m + 1
  501. str_tmp(m:m) = c
  502. end if
  503. end do
  504. !trim trailing space:
  505. str = str_tmp(1:m)
  506. end if
  507. end subroutine unescape_string
  508. !*****************************************************************************************
  509. !*****************************************************************************************
  510. !> author: Jacob Williams
  511. ! date:6/14/2014
  512. !
  513. ! Returns true if the string is a valid 4-digit hex string.
  514. !
  515. !# Examples
  516. !```fortran
  517. ! valid_json_hex('0000') !returns true
  518. ! valid_json_hex('ABC4') !returns true
  519. ! valid_json_hex('AB') !returns false (< 4 characters)
  520. ! valid_json_hex('WXYZ') !returns false (invalid characters)
  521. !```
  522. pure function valid_json_hex(str) result(valid)
  523. implicit none
  524. logical(LK) :: valid !! is str a value 4-digit hex string
  525. character(kind=CK,len=*),intent(in) :: str !! the string to check.
  526. integer(IK) :: n !! length of `str`
  527. integer(IK) :: i !! counter
  528. !> an array of the valid hex characters
  529. character(kind=CK,len=1),dimension(22),parameter :: valid_chars = &
  530. [ (achar(i),i=48,57), & ! decimal digits
  531. (achar(i),i=65,70), & ! capital A-F
  532. (achar(i),i=97,102) ] ! lowercase a-f
  533. !initialize
  534. valid = .false.
  535. !check all the characters in the string:
  536. n = len(str)
  537. if (n==4) then
  538. do i=1,n
  539. if (.not. any(str(i:i)==valid_chars)) return
  540. end do
  541. valid = .true. !all are in the set, so it is OK
  542. end if
  543. end function valid_json_hex
  544. !*****************************************************************************************
  545. !*****************************************************************************************
  546. !> author: Izaak Beekman
  547. !
  548. ! Convert string to unicode (CDK to CK).
  549. pure function to_uni(str)
  550. implicit none
  551. character(kind=CDK,len=*), intent(in) :: str
  552. character(kind=CK,len=len(str)) :: to_uni
  553. to_uni = str
  554. end function to_uni
  555. !*****************************************************************************************
  556. !*****************************************************************************************
  557. !> author: Izaak Beekman
  558. !
  559. ! Convert array of strings to unicode (CDK to CK).
  560. !
  561. !@note JW: may be able to remove this by making [[to_uni]] PURE ELEMENTAL ?
  562. pure function to_uni_vec(str)
  563. implicit none
  564. character(kind=CDK,len=*), dimension(:), intent(in) :: str
  565. character(kind=CK,len=len(str)), dimension(size(str)) :: to_uni_vec
  566. to_uni_vec = str
  567. end function to_uni_vec
  568. !*****************************************************************************************
  569. !*****************************************************************************************
  570. !> author: Izaak Beekman
  571. !
  572. ! `CK`//`CDK` operator.
  573. pure function ucs4_join_default(ucs4_str,def_str) result(res)
  574. implicit none
  575. character(kind=CK, len=*), intent(in) :: ucs4_str
  576. character(kind=CDK,len=*), intent(in) :: def_str
  577. character(kind=CK,len=(len(ucs4_str)+len(def_str))) :: res
  578. res = ucs4_str//to_unicode(def_str)
  579. end function ucs4_join_default
  580. !*****************************************************************************************
  581. !*****************************************************************************************
  582. !> author: Izaak Beekman
  583. !
  584. ! `CDK`//`CK` operator.
  585. pure function default_join_ucs4(def_str,ucs4_str) result(res)
  586. implicit none
  587. character(kind=CDK,len=*), intent(in) :: def_str
  588. character(kind=CK, len=*), intent(in) :: ucs4_str
  589. character(kind=CK,len=(len(def_str)+len(ucs4_str))) :: res
  590. res = to_unicode(def_str)//ucs4_str
  591. end function default_join_ucs4
  592. !*****************************************************************************************
  593. !*****************************************************************************************
  594. !> author: Izaak Beekman
  595. !
  596. ! `CK`==`CDK` operator.
  597. pure elemental function ucs4_comp_default(ucs4_str,def_str) result(res)
  598. implicit none
  599. character(kind=CK, len=*), intent(in) :: ucs4_str
  600. character(kind=CDK,len=*), intent(in) :: def_str
  601. logical(LK) :: res
  602. res = ( ucs4_str == to_unicode(def_str) )
  603. end function ucs4_comp_default
  604. !*****************************************************************************************
  605. !*****************************************************************************************
  606. !> author: Izaak Beekman
  607. !
  608. ! `CDK`==`CK` operator.
  609. pure elemental function default_comp_ucs4(def_str,ucs4_str) result(res)
  610. implicit none
  611. character(kind=CDK,len=*), intent(in) :: def_str
  612. character(kind=CK, len=*), intent(in) :: ucs4_str
  613. logical(LK) :: res
  614. res = (to_unicode(def_str) == ucs4_str)
  615. end function default_comp_ucs4
  616. !*****************************************************************************************
  617. !*****************************************************************************************
  618. !> author: Jacob Williams
  619. !
  620. ! `CK`/=`CDK` operator.
  621. pure elemental function ucs4_neq_default(ucs4_str,def_str) result(res)
  622. implicit none
  623. character(kind=CK, len=*), intent(in) :: ucs4_str
  624. character(kind=CDK,len=*), intent(in) :: def_str
  625. logical(LK) :: res
  626. res = ( ucs4_str /= to_unicode(def_str) )
  627. end function ucs4_neq_default
  628. !*****************************************************************************************
  629. !*****************************************************************************************
  630. !> author: Jacob Williams
  631. !
  632. ! `CDK`/=`CK` operator.
  633. pure elemental function default_neq_ucs4(def_str,ucs4_str) result(res)
  634. implicit none
  635. character(kind=CDK,len=*), intent(in) :: def_str
  636. character(kind=CK, len=*), intent(in) :: ucs4_str
  637. logical(LK) :: res
  638. res = (to_unicode(def_str) /= ucs4_str)
  639. end function default_neq_ucs4
  640. !*****************************************************************************************
  641. !*****************************************************************************************
  642. !> author: Jacob Williams
  643. !
  644. ! Returns lowercase version of the `CK` string.
  645. pure function lowercase_string(str) result(s_lower)
  646. implicit none
  647. character(kind=CK,len=*),intent(in) :: str !! input string
  648. character(kind=CK,len=(len(str))) :: s_lower !! lowercase version of the string
  649. integer :: i !! counter
  650. integer :: j !! index of uppercase character
  651. s_lower = str
  652. do i = 1, len_trim(str)
  653. j = index(upper,s_lower(i:i))
  654. if (j>0) s_lower(i:i) = lower(j:j)
  655. end do
  656. end function lowercase_string
  657. !*****************************************************************************************
  658. !*****************************************************************************************
  659. !>
  660. ! Replace all occurrences of `s1` in `str` with `s2`.
  661. !
  662. ! A case-sensitive match is used.
  663. !
  664. !@note `str` must be allocated.
  665. pure subroutine replace_string(str,s1,s2)
  666. implicit none
  667. character(kind=CK,len=:),allocatable,intent(inout) :: str
  668. character(kind=CK,len=*),intent(in) :: s1
  669. character(kind=CK,len=*),intent(in) :: s2
  670. character(kind=CK,len=:),allocatable :: tmp !! temporary string for accumulating result
  671. integer(IK) :: i !! counter
  672. integer(IK) :: n !! for accumulating the string
  673. integer(IK) :: ilen !! length of `str` string
  674. integer(IK) :: ilen1 !! length of `s1` string
  675. if (len(str)>0) then
  676. tmp = CK_'' ! initialize
  677. ilen1 = len(s1)
  678. ! .
  679. ! '123ab789'
  680. do
  681. ilen = len(str)
  682. i = index(str,s1)
  683. if (i>0) then
  684. if (i>1) tmp = tmp//str(1:i-1)
  685. tmp = tmp//s2 ! replace s1 with s2 in new string
  686. n = i+ilen1 ! start of remainder of str to keep
  687. if (n<=ilen) then
  688. str = str(n:ilen)
  689. else
  690. ! done
  691. exit
  692. end if
  693. else
  694. ! done: get remainder of string
  695. tmp = tmp//str
  696. exit
  697. end if
  698. end do
  699. str = tmp
  700. end if
  701. end subroutine replace_string
  702. !*****************************************************************************************
  703. !*****************************************************************************************
  704. !>
  705. ! Decode a string from the "JSON Pointer" RFC 6901 format.
  706. !
  707. ! It replaces `~1` with `/` and `~0` with `~`.
  708. pure function decode_rfc6901(str) result(str_out)
  709. implicit none
  710. character(kind=CK,len=*),intent(in) :: str
  711. character(kind=CK,len=:),allocatable :: str_out
  712. str_out = str
  713. call replace_string(str_out,tilde//CK_'1',slash)
  714. call replace_string(str_out,tilde//CK_'0',tilde)
  715. end function decode_rfc6901
  716. !*****************************************************************************************
  717. !*****************************************************************************************
  718. !>
  719. ! Encode a string into the "JSON Pointer" RFC 6901 format.
  720. !
  721. ! It replaces `~` with `~0` and `/` with `~1`.
  722. pure function encode_rfc6901(str) result(str_out)
  723. implicit none
  724. character(kind=CK,len=*),intent(in) :: str
  725. character(kind=CK,len=:),allocatable :: str_out
  726. str_out = str
  727. call replace_string(str_out,tilde,tilde//CK_'0')
  728. call replace_string(str_out,slash,tilde//CK_'1')
  729. end function encode_rfc6901
  730. !*****************************************************************************************
  731. end module json_string_utilities
  732. !*****************************************************************************************