Loading...
Searching...
No Matches
string.f90
Go to the documentation of this file.
1!> @file
2!! @defgroup group_string String
3!! Minimal yet powerful variable-length string type with modern Fortran features.
4!! This module implements a lightweight `string` derived type that behaves like
5!! a true variable-length character string while remaining fully compatible with
6!! intrinsic Fortran character operations.
7!!
8!! Features:
9!! - Automatic memory management via `allocatable character(:)`
10!! - Overloaded assignment (`=`) between `string` and `character(*)`
11!! - Overloaded operators: `//` (concatenation), `==` (equality), `.contains.` (membership)
12!! - Generic interfaces for `len`, `len_trim`, `trim`
13!! - Full support for formatted I/O (`write`, `print`)
14!! - Helper routines for parsing Fortran source (line continuation, upper/lower case conversion, etc.)
15!!
16!! The design is intentionally minimal - it provides only what's necessary for
17!! robust string handling in scientific and preprocessing applications,
18!! avoiding the bloat of larger string libraries while remaining fast and standards-compliant.
19!! @note All procedures are `pure` or `elemental` when possible for maximum performance
20!! and usability in array contexts.
21!!
22!! @section string_examples Examples
23!!
24!! @par Basic Usage
25!! @code{.f90}
26!! type(string) :: s, t
27!! character(:), allocatable :: line
28!!
29!! s = 'Hello' ! Assignment from literal
30!! t = s // ' World!' ! Concatenation
31!! print *, t%chars ! Output: Hello World!
32!!
33!! if (s == 'Hello') then
34!! print *, 'Equal'
35!! else
36!! print *, 'Case sensitive'
37!! endif
38!!
39!! print *, len(t) ! -> 12
40!! print *, len_trim(t) ! -> 12
41!! ...
42!! @endcode
43!!
44!! @par Array and Container Support
45!! @code{.f90}
46!! type(string) :: words(3)
47!! logical :: found
48!!
49!! words = [string('apple'), string('banana'), string('cherry')]
50!! found = words .contains. 'banana' ! --> .true.
51!! found = words .contains. string('date') ! --> .false.
52!! ...
53!! @endcode
54!!
55!! @par Advanced: Source Code Processing
56!! @code{.f90}
57!! character(len=:), allocatable :: code_line
58!! code_line = uppercase('program hello_world ! comment') ! --> 'PROGRAM HELLO_WORLD ! comment'
59!! ...
60!! @endcode
61module fpx_string
62 use fpx_constants
63
64 implicit none; private
65
66 public :: len, &
67 len_trim, &
68 trim, &
69 operator(//), &
70 operator(.contains.), &
71 index
72
73 public :: starts_with, &
74 head, &
75 tail, &
76 previous, &
77 concat, &
78 writechk, &
79 uppercase, &
81
82 !> Represents text as a sequence of ASCII code units.
83 !! The derived type wraps an allocatable character array.
84 !!
85 !! @section string_type_examples Examples
86 !!
87 !! @code{.f90}
88 !! type(string) :: s
89 !! s = 'foo'
90 !! @endcode
91 !!
92 !! @section string_type_constructor Constructors
93 !! Initializes a new instance of the string class
94 !! <h3>string(character(:))</h3>
95 !! @verbatim type(string) function string(chars) @endverbatim
96 !!
97 !! @param[in] chars character(:)
98 !!
99 !! @b Examples
100 !! @code{.f90}
101 !! type(string) :: s
102 !! s = string('foo')
103 !! @endcode
104 !! @return The constructed string object.
105 !!
106 !! @section string_type_remarks Remarks
107 !! The string implementation proposed here is kept at the bare
108 !! minimum of what is required by the library. There are many
109 !! other implementations that can be found.
110 !!
111 !! @ingroup group_string
112 type, public :: string
113 character(:), allocatable :: chars !< Variable length character array
114 contains
115 !! @cond
116 procedure, pass(lhs), private :: character_assign_string
117 procedure, pass(rhs), private :: string_assign_character
118 procedure, pass(lhs), private :: string_eq_string !! Equal to string logical operator.
119 procedure, pass(lhs), private :: string_eq_character !! Equal to character logical operator.
120 procedure, pass(rhs), private :: character_eq_string !! Equal to character (inverted) logical operator.
121 procedure, pass(dtv), private :: write_formatted !! Formatted output.
122 !! @endcond
123 generic, public :: assignment(=) => character_assign_string, &
124 string_assign_character
125 generic, public :: operator(==) => string_eq_string, &
126 string_eq_character, &
127 character_eq_string
128 generic, public :: write(formatted) => write_formatted
129 end type
130
131 !> Return the length of a @ref string object.
132 !!
133 !! This generic interface extends the intrinsic Fortran `len` function
134 !! to support the fpx @ref string type.
135 !!
136 !! The returned value corresponds to the full length of the underlying
137 !! character storage, including trailing blanks.
138 !!
139 !! If the string is not allocated, the returned value is zero.
140 !!
141 !! @section len_examples Examples
142 !!
143 !! Basic usage:
144 !! @code{.f90}
145 !! type(string) :: s
146 !!
147 !! s = 'foo'
148 !! print *, len(s) ! 3
149 !!
150 !! s = 'foo '
151 !! print *, len(s) ! 4
152 !! ...
153 !! @endcode
154 !!
155 !! Unallocated strings:
156 !! @code{.f90}
157 !! type(string) :: s
158 !!
159 !! print *, len(s) ! 0
160 !! ...
161 !! @endcode
162 !!
163 !! @ingroup group_string
164 interface len
165 module procedure :: string_len
166 end interface
167
168 !> Return the trimmed length of a @ref string object.
169 !!
170 !! This generic interface extends the intrinsic Fortran `len_trim`
171 !! function to support the fpx @ref string type.
172 !!
173 !! The returned value corresponds to the number of characters after
174 !! removing trailing blanks.
175 !!
176 !! If the string is not allocated, the returned value is zero.
177 !!
178 !! @section len_trim_examples Examples
179 !!
180 !! Basic usage:
181 !! @code{.f90}
182 !! type(string) :: s
183 !!
184 !! s = 'foo'
185 !! print *, len_trim(s) ! 3
186 !!
187 !! s = 'foo '
188 !! print *, len_trim(s) ! 3
189 !! ...
190 !! @endcode
191 !!
192 !! Unallocated strings:
193 !! @code{.f90}
194 !! type(string) :: s
195 !!
196 !! print *, len_trim(s) ! 0
197 !! ...
198 !! @endcode
199 !!
200 !! @ingroup group_string
201 interface len_trim
202 module procedure :: string_len_trim
203 end interface
204
205 !> Remove trailing blanks from a @ref string object.
206 !!
207 !! This generic interface extends the intrinsic Fortran `trim`
208 !! function to support the fpx @ref string type.
209 !!
210 !! The result is returned as a deferred-length intrinsic character
211 !! expression with trailing blanks removed.
212 !!
213 !! If the string is not allocated, an empty character string is returned.
214 !!
215 !! @section trim_examples Examples
216 !!
217 !! Basic usage:
218 !! @code{.f90}
219 !! type(string) :: s
220 !! character(:), allocatable :: c
221 !!
222 !! s = 'hello '
223 !!
224 !! c = trim(s)
225 !! print *, '"' // c // '"' ! "hello"
226 !! ...
227 !! @endcode
228 !!
229 !! Unallocated strings:
230 !! @code{.f90}
231 !! type(string) :: s
232 !!
233 !! print *, len(trim(s)) ! 0
234 !! ...
235 !! @endcode
236 !!
237 !! @return Deferred-length character string without trailing blanks.
238 !!
239 !! @ingroup group_string
240 interface trim
241 module procedure :: string_trim
242 end interface
243
244 !> Concatenate string and character expressions.
245 !!
246 !! Supports all combinations of:
247 !! - string // string
248 !! - string // character(*)
249 !! - character(*) // string
250 !!
251 !! The result is returned as a deferred-length character expression.
252 !!
253 !! @b Examples
254 !! @code{.f90}
255 !! type(string) :: s
256 !!
257 !! s = 'foo'
258 !!
259 !! print *, s // 'bar'
260 !! print *, '>>' // s
261 !! ...
262 !! @endcode
263 !!
264 !! @ingroup group_string
265 interface operator(//)
266 module procedure :: string_concat_string
267 module procedure :: string_concat_character
268 module procedure :: character_concat_string
269 end interface
270
271 !> Test whether a value is present in an array.
272 !!
273 !! The `.contains.` operator provides convenient membership testing
274 !! between arrays of intrinsic characters and arrays of @ref string
275 !! objects.
276 !!
277 !! Supported combinations are:
278 !! - `string(:) .contains. string`
279 !! - `string(:) .contains. character(*)`
280 !! - `character(:) .contains. string`
281 !! - `character(:) .contains. character(*)`
282 !!
283 !! The comparison uses the overloaded equality operator (`==`)
284 !! associated with the involved types.
285 !!
286 !! @section contains_examples Examples
287 !!
288 !! Arrays of string:
289 !! @code{.f90}
290 !! type(string) :: fruits(3)
291 !!
292 !! fruits = [ string('apple'), &
293 !! string('banana'), &
294 !! string('cherry') ]
295 !!
296 !! print *, fruits .contains. 'banana' ! .true.
297 !! print *, fruits .contains. 'orange' ! .false.
298 !! ...
299 !! @endcode
300 !!
301 !! Mixed character/string usage:
302 !! @code{.f90}
303 !! character(10) :: names(2)
304 !!
305 !! names = ['foo ', 'bar ']
306 !!
307 !! print *, names .contains. string('foo') ! .true.
308 !! ...
309 !! @endcode
310 !!
311 !! Empty arrays:
312 !! @code{.f90}
313 !! type(string) :: values(0)
314 !!
315 !! print *, values .contains. 'x' ! .false.
316 !! ...
317 !! @endcode
318 !!
319 !! @return `.true.` if the searched value is present,
320 !! `.false.` otherwise.
321 !!
322 !! @ingroup group_string
323 interface operator(.contains.)
324 module procedure :: strings_contain_string
325 module procedure :: strings_contain_character
326 module procedure :: characters_contain_string
327 module procedure :: characters_contain_character
328 end interface
329
330 !> Locate the position of a substring.
331 !!
332 !! This generic interface extends the intrinsic Fortran `index`
333 !! function to support the fpx @ref string type.
334 !!
335 !! Supported combinations are:
336 !! - `index(string, string)`
337 !! - `index(string, character(*))`
338 !! - `index(character(*), string)`
339 !!
340 !! The optional argument `back` behaves exactly as in the intrinsic
341 !! Fortran procedure:
342 !! - if absent or `.false.`, the first occurrence is returned;
343 !! - if `.true.`, the last occurrence is returned.
344 !!
345 !! The function returns zero if the substring is not found.
346 !!
347 !! @section index_examples Examples
348 !!
349 !! String and character:
350 !! @code{.f90}
351 !! type(string) :: s
352 !!
353 !! s = 'banana'
354 !!
355 !! print *, index(s, 'na') ! 3
356 !! print *, index(s, 'xy') ! 0
357 !! ...
358 !! @endcode
359 !!
360 !! String and string:
361 !! @code{.f90}
362 !! type(string) :: text
363 !! type(string) :: sub
364 !!
365 !! text = 'banana'
366 !! sub = 'na'
367 !!
368 !! print *, index(text, sub) ! 3
369 !! ...
370 !! @endcode
371 !!
372 !! Search from the end:
373 !! @code{.f90}
374 !! type(string) :: s
375 !!
376 !! s = 'banana'
377 !!
378 !! print *, index(s, 'na', back=.true.) ! 5
379 !! ...
380 !! @endcode
381 !!
382 !! Mixed usage:
383 !! @code{.f90}
384 !! type(string) :: sub
385 !!
386 !! sub = 'ana'
387 !!
388 !! print *, index('banana', sub) ! 2
389 !! ...
390 !! @endcode
391 !!
392 !! @return Position of the matching substring, or zero if not found.
393 !!
394 !! @ingroup group_string
395 interface index
396 module procedure :: index_string_string
397 module procedure :: index_string_character
398 module procedure :: index_character_string
399 end interface
400
401contains
402
403 !> Assignment overloading. Assign a character array to a string.
404 !! @param[inout] lhs string
405 !! @param[in] rhs character(*)
406 !!
407 !! @b Examples
408 !! @code{.f90}
409 !! type(string) :: s
410 !!
411 !! s = 'foo'
412 !! @endcode
413 !!
414 !! @ingroup group_string
415 subroutine character_assign_string(lhs, rhs)
416 class(string), intent(inout) :: lhs
417 character(*), intent(in) :: rhs
418
419 if (allocated(lhs%chars)) deallocate(lhs%chars)
420 allocate(lhs%chars, source=rhs)
421 end subroutine
422
423 !> Assignment overloading. Assign a string to a character array.
424 !! @param[inout] lhs character(:), allocatable
425 !! @param[in] rhs string
426 !!
427 !! @b Examples
428 !! @code{.f90}
429 !! type(string) :: s
430 !! character(:), allocatable :: c
431 !!
432 !! s = 'foo'
433 !! c = s
434 !! ! The value of c is now 'foo'
435 !! @endcode
436 !!
437 !! @ingroup group_string
438 subroutine string_assign_character(lhs, rhs)
439 character(:), allocatable, intent(inout) :: lhs
440 class(string), intent(in) :: rhs
441
442 lhs = rhs%chars
443 end subroutine
444
445 !> Length of the string entity.
446 !! @param[in] this string
447 !!
448 !! @b Examples
449 !! @code{.f90}
450 !! type(string) :: s
451 !! integer :: l
452 !!
453 !! s = string('foo ')
454 !! l = len(s)
455 !! ! The value of l is 4
456 !! @endcode
457 !! @return An integer corresponding to the length of the string.
458 !!
459 !! @ingroup group_string
460 elemental integer function string_len(this) result(res)
461 class(string), intent(in) :: this
462
463 if (allocated(this%chars)) then
464 res = len(this%chars)
465 else
466 res = 0
467 end if
468 end function
469
470 !> Length of the string entity without trailing blanks (len_trim).
471 !! @param[in] this string
472 !!
473 !! @b Examples
474 !! @code{.f90}
475 !! type(string) :: s
476 !! integer :: l
477 !!
478 !! s = string('foo ')
479 !! l = len_trim(s)
480 !! ! The value of l is 3
481 !! @endcode
482 !! @return An integer corresponding to the trimmed length of the string.
483 !!
484 !! @ingroup group_string
485 pure integer function string_len_trim(this) result(res)
486 class(string), intent(in) :: this
487
488 if (allocated(this%chars)) then
489 res = len_trim(this%chars)
490 else
491 res = 0
492 end if
493 end function
494
495 !> Returns a copy of the string with trailing blanks removed.
496 !! @param[in] this string
497 !! @return Trimmed character string (deferred length).
498 !!
499 !! @ingroup group_string
500 pure function string_trim(this) result(res)
501 class(string), intent(in) :: this
502 character(:), allocatable :: res
503
504 if (allocated(this%chars)) then
505 res = trim(this%chars)
506 else
507 res = ''
508 end if
509 end function
510
511 !> Concatenation of two string objects.
512 !! @param[in] lhs left-hand side string
513 !! @param[in] rhs right-hand side string
514 !! @return New concatenated string.
515 !!
516 !! @ingroup group_string
517 pure function string_concat_string(lhs, rhs) result(res)
518 class(string), intent(in) :: lhs
519 class(string), intent(in) :: rhs
520 character(:), allocatable :: res
521
522 if (allocated(lhs%chars) .and. allocated(rhs%chars)) then
523 res = lhs%chars // rhs%chars
524 elseif (allocated(lhs%chars)) then
525 res = lhs%chars
526 elseif (allocated(rhs%chars)) then
527 res = rhs%chars
528 else
529 res = ''
530 end if
531 end function
532
533 !> Concatenation of string and character expression.
534 !! @param[in] lhs string
535 !! @param[in] rhs character expression
536 !! @return New concatenated string.
537 !!
538 !! @ingroup group_string
539 pure function string_concat_character(lhs, rhs) result(res)
540 class(string), intent(in) :: lhs
541 character(*), intent(in) :: rhs
542 character(:), allocatable :: res
543
544 if (allocated(lhs%chars)) then
545 res = lhs%chars // rhs
546 else
547 res = rhs
548 end if
549 end function
550
551 !> Concatenation of character expression and string.
552 !! @param[in] lhs character expression
553 !! @param[in] rhs string
554 !! @return New concatenated string.
555 !!
556 !! @ingroup group_string
557 pure function character_concat_string(lhs, rhs) result(res)
558 character(*), intent(in) :: lhs
559 class(string), intent(in) :: rhs
560 character(:), allocatable :: res
561
562 if (allocated(rhs%chars)) then
563 res = lhs // rhs%chars
564 else
565 res = lhs
566 end if
567 end function
568
569 !> Equality comparison between two string objects.
570 !! @param[in] lhs left-hand side
571 !! @param[in] rhs right-hand side
572 !! @return .true. if the strings are equal, .false. otherwise.
573 !!
574 !! @ingroup group_string
575 elemental function string_eq_string(lhs, rhs) result(res)
576 class(string), intent(in) :: lhs !! Left hand side.
577 class(string), intent(in) :: rhs !! Right hand side.
578 logical :: res
579
580 if (.not. allocated(lhs%chars)) then
581 res = .not. allocated(rhs%chars)
582 else
583 res = lhs%chars == rhs%chars
584 end if
585 end function
586
587 !> Equality comparison between string and character expression.
588 !! @param[in] lhs string
589 !! @param[in] rhs character expression
590 !! @return .true. if equal, .false. otherwise.
591 !!
592 !! @ingroup group_string
593 elemental function string_eq_character(lhs, rhs) result(res)
594 class(string), intent(in) :: lhs !! Left hand side.
595 character(*), intent(in) :: rhs !! Right hand side.
596 logical :: res !! Opreator test result.
597
598 if (.not. allocated(lhs%chars)) then
599 res = .false.
600 else
601 res = lhs%chars == rhs
602 end if
603 end function
604
605 !> Equality comparison (reversed) between character expression and string.
606 !! @param[in] lhs character expression
607 !! @param[in] rhs string
608 !! @return .true. if equal, .false. otherwise.
609 !!
610 !! @ingroup group_string
611 elemental function character_eq_string(lhs, rhs) result(res)
612 character(*), intent(in) :: lhs !! Left hand side.
613 class(string), intent(in) :: rhs !! Right hand side.
614 logical :: res !! Operator test result.
615
616 if (.not. allocated(rhs%chars)) then
617 res = .false.
618 else
619 res = rhs%chars == lhs
620 end if
621 end function
622
623 !> Formatted output procedure for user-defined type @ref string (UDTIO)
624 !! This procedure is called automatically when a formatted WRITE statement is used
625 !! with a variable of type `string` (when using the DT edit descriptor or default
626 !! formatted output for the type).
627 !!
628 !! It writes the content of the string component `dtv%chars` using a simple `A` format.
629 !! If the string is not allocated, an empty string is written.
630 !!
631 !! @param[in] dtv The @ref string object to be written (polymorphic dummy argument)
632 !! @param[in] unit Fortran logical unit number
633 !! @param[in] iotype String describing the edit descriptor ('DT' + optional string)
634 !! @param[in] v_list Integer array containing the values from the DT edit descriptor
635 !! (v_list is empty if no parentheses were used after DT)
636 !! @param[out] iostat I/O status code (0 = success, positive = error, negative = end-of-file/end-of-record)
637 !! @param[inout] iomsg Message describing the I/O error (if any)
638 !!
639 !! @b Note
640 !! - This implementation **ignores** `iotype` and `v_list` parameters
641 !! -> the same simple character output is always performed
642 !! - The procedure always uses format `(A)`
643 !! - Empty (not allocated) string is written as empty line (zero characters)
644 !!
645 !! @b Warning
646 !! This is a minimal implementation of UDTIO formatted output.
647 !! More sophisticated versions could:
648 !! - respect `iotype` (DT"..." or LISTDIRECTED)
649 !! - use `v_list` for width/precision control
650 !! - add quotation marks, escaping, etc.
651 !!
652 !! @b Examples
653 !! @code{.f90}
654 !! type(string) :: s
655 !! call s%set("Hello formatted world!")
656 !!
657 !! write(*, *) s ! may call write_formatted (depending on compiler)
658 !! write(*, '(DT)') s ! explicitly calls write_formatted
659 !! @endcode
660 !!
661 !! @ingroup group_string
662 ! allow(assumed-size-character-intent)
663 subroutine write_formatted(dtv, unit, iotype, v_list, iostat, iomsg)
664 class(string), intent(in) :: dtv
665 integer, intent(in) :: unit !! Logical unit.
666 character(*), intent(in) :: iotype !! Edit descriptor.
667 integer, intent(in) :: v_list(:) !! Edit descriptor list.
668 integer, intent(out) :: iostat !! IO status code.
669 character(*), intent(inout) :: iomsg !! IO status message.
670
671 if (allocated(dtv%chars)) then
672 write(unit, '(A)', iostat=iostat, iomsg=iomsg) dtv%chars
673 else
674 write(unit, '(A)', iostat=iostat, iomsg=iomsg) ''
675 end if
676 end subroutine
677
678 !> Checks if a string starts with a given prefix
679 !! Returns `.true.` if the string `str` (after trimming leading/trailing whitespace)
680 !! begins exactly with the substring `arg1`.
681 !! The function uses `index()` after trimming both strings with `trim(adjustl())`.
682 !!
683 !! @param[in] str The string to be tested
684 !! @param[in] arg1 The prefix to look for at the beginning of `str`
685 !! @param[out] idx (optional) If present, receives the starting position of `arg1` in the trimmed string
686 !! (will be 1 if the function returns `.true.`, otherwise >1 or 0)
687 !!
688 !! @return `.true.` if `str` starts with `arg1` (after trimming), `.false.` otherwise
689 !!
690 !! @b Note
691 !! - Leading and trailing whitespace of both `str` and `arg1` is ignored
692 !! - Comparison is case-sensitive
693 !! - Empty `arg1` will always return `.true.` (any string starts with empty string)
694 !!
695 !! @b Warning
696 !! The returned index (when requested) is the position **after trimming** of the input string,
697 !! not in the original untrimmed string.
698 !!
699 !! @b Examples
700 !! @code{.f90}
701 !! character(80) :: line = ' hello world '
702 !! logical :: ok
703 !! integer :: pos
704 !!
705 !! ok = starts_with(line, 'hello') ! -> .true.
706 !! ok = starts_with(line, 'hello', pos) ! -> .true. and pos = 1
707 !! ok = starts_with(line, 'world') ! -> .false.
708 !! ok = starts_with(' test123 ', 'test') ! -> .true.
709 !! ...
710 !! @endcode
711 !!
712 !! @ingroup group_string
713 !! @ingroup group_string
714 logical function starts_with(str, arg1, idx) result(res)
715 character(*), intent(in) :: str
716 character(*), intent(in) :: arg1
717 integer, intent(out), optional :: idx
718 !private
719 integer :: i
720
721 i = index(trim(adjustl(str)), trim(arg1))
722 res = (i == 1)
723 if (present(idx)) idx = i
724 end function
725
726 !> Returns the first character of the trimmed string.
727 !! @param[in] str input string
728 !! @return First character (space if empty)
729 !!
730 !! @ingroup group_string
731 character function head(str) result(res)
732 character(*), intent(in) :: str
733
734 res = ' '
735 if (len_trim(str) == 0) return
736
737 res = str(1:1)
738 end function
739
740 !> Returns the last non-blank character of a string.
741 !! @param[in] str input string
742 !! @return Last character (space if empty)
743 !!
744 !! @ingroup group_string
745 character function tail(str) result(res)
746 character(*), intent(in) :: str
747 !private
748 integer :: n
749
750 res = ' '; n = len_trim(str)
751 if (n == 0) return
752
753 res = str(n:n)
754 end function
755
756 !> Smart concatenation that removes continuation markers (&) and handles line-continuation rules.
757 !! @param[in] str1 first line
758 !! @param[in] str2 second line
759 !! @return Concatenated string with proper continuation handling
760 !!
761 !! @ingroup group_string
762 function concat(str1, str2) result(res)
763 character(*), intent(in) :: str1
764 character(*), intent(in) :: str2
765 character(:), allocatable :: res
766 !private
767 integer :: n1, n2
768
769 n1 = len(str1); n2 = 1
770 if (head(str1) == '!') then
771 n2 = 2
772 if (tail(str1) == '&') n1 = len_trim(str1) - 1
773 if (starts_with(str2, '!dir$') .or. starts_with(str2, '!DIR$') .or. &
774 starts_with(str2, '!dec$') .or. starts_with(str2, '!DEC$') .or. &
775 starts_with(str2, '!gcc$') .or. starts_with(str2, '!GCC$') .or. &
776 starts_with(str2, '!acc$') .or. starts_with(str2, '!ACC$') .or. &
777 starts_with(str2, '!$omp') .or. starts_with(str2, '!$OMP')) then
778 n2 = 6
779 end if
780 if (head(adjustl(str2(n2:))) == '&') then
781 n2 = index(str2, '&') + 1
782 end if
783 else
784 if (tail(str1) == '&') n1 = len_trim(str1) - 1
785 if (head(trim(str2)) == '&') n2 = index(str2, '&') + 1
786 if (tail(str1(:n1)) == '(') n1 = index(str1(:n1), '(', back=.true.)
787 end if
788
789 if (len(str1) > 0 .and. len(str2) >= n2) then
790 if (str1(n1:n1) == ' ' .and. str2(n2:n2) == ' ') n2 = n2 + 1
791 end if
792 res = str1(:n1) // str2(n2:)
793 end function
794
795 !> Convert string to upper case (respects contents of quotes).
796 !! @param[in] str input string
797 !! @return Upper-case version of the string
798 !!
799 !! @b Examples
800 !! @code
801 !! character(*), parameter :: input = 'test'
802 !! character(:), allocatable :: output
803 !! output = uppercase(input)
804 !! if (output == 'TEST') print*, 'OK'
805 !! @endcode
806 !!
807 !! @ingroup group_string
808 pure function uppercase(str) result(res)
809 character(*), intent(in) :: str
810 character(len_trim(str)) :: res
811 !private
812 integer :: ilen, ioffset, iquote, iqc, iav, i
813
814 ilen = len_trim(str)
815 ioffset = iachar('A') - iachar('a')
816 iquote = 0
817 res = str
818 do i = 1, ilen
819 iav = iachar(str(i:i))
820 if (iquote == 0 .and. (iav == 34 .or. iav == 39)) then
821 iquote = 1
822 iqc = iav
823 cycle
824 end if
825 if (iquote == 1 .and. iav == iqc) then
826 iquote = 0
827 cycle
828 end if
829 if (iquote == 1) cycle
830 if (iav >= iachar('a') .and. iav <= iachar('z')) then
831 res(i:i) = achar(iav + ioffset)
832 else
833 res(i:i) = str(i:i)
834 end if
835 end do
836 end function
837
838 !> Convert string to lower case (respects contents of quotes).
839 !! @param[in] str input string
840 !! @return Lower-case version of the string
841 !!
842 !! @b Examples
843 !! @code
844 !! character(*), parameter :: input = 'TEST'
845 !! character(:), allocatable :: output
846 !! output = lowercase(input)
847 !! if (output == 'test') print*, 'OK'
848 !! @endcode
849 !!
850 !! @ingroup group_string
851 pure function lowercase(str) result(res)
852 character(*), intent(in) :: str
853 character(len_trim(str)) :: res
854 !private
855 integer :: ilen, ioffset, iquote, iqc, iav, i
856
857 ilen = len_trim(str)
858 ioffset = iachar('A') - iachar('a')
859 iquote = 0
860 res = str
861 do i = 1, ilen
862 iav = iachar(str(i:i))
863 if (iquote == 0 .and. (iav == 34 .or. iav == 39)) then
864 iquote = 1
865 iqc = iav
866 cycle
867 end if
868 if (iquote == 1 .and. iav == iqc) then
869 iquote = 0
870 cycle
871 end if
872 if (iquote == 1) cycle
873 if (iav >= iachar('A') .and. iav <= iachar('Z')) then
874 res(i:i) = achar(iav - ioffset)
875 else
876 res(i:i) = str(i:i)
877 end if
878 end do
879 end function
880
881 !> Write a long line split into chunks of size CHKSIZE with continuation (&).
882 !! @param[in] unit logical unit
883 !! @param[in] str string to write
884 !!
885 !! @ingroup group_string
886 subroutine writechk(unit, str)
887 integer, intent(in) :: unit
888 character(*), intent(in) :: str
889 !private
890 integer :: i, n
891
892 n = 0
893 if (head(str) /= '!') then
894 n = floor(len(str) / real(chksize))
895 do i = 1, n
896 write(unit, '(A)') str((i - 1) * chksize + 1:i * chksize) // '&'
897 end do
898 end if
899 write(unit, '(A)') str(n * chksize + 1:)
900 end subroutine
901
902 !> Returns the previous non-blank character before position pos (updates pos).
903 !! @param[in] line input line
904 !! @param[inout] pos current position (moved backward)
905 !! @return Previous non-blank character
906 !!
907 !! @ingroup group_string
908 character(1) function previous(line, pos) result(res)
909 character(*), intent(in) :: line
910 integer, intent(inout) :: pos
911 !private
912
913 if (pos == 1) then
914 res = trim(line(pos:pos))
915 else
916 do while (line(pos:pos) == ' ')
917 pos = pos - 1
918 if (pos == 1) exit
919 end do
920 res = line(pos:pos)
921 end if
922 end function
923
924 !> Checks whether an array of string contains a given string.
925 !! @param[in] lhs array of string
926 !! @param[in] rhs string to search for
927 !! @return .true. if rhs is present in lhs
928 !!
929 !! @ingroup group_string
930 logical function strings_contain_string(lhs, rhs) result(res)
931 type(string), intent(in) :: lhs(:)
932 type(string), intent(in) :: rhs
933 !private
934 integer :: i
935
936 res = .false.
937 do i = 1, size(lhs)
938 if (lhs(i) == rhs) then
939 res = .true.
940 exit
941 end if
942 end do
943 end function
944
945 !> Checks whether an array of string contains a given character expression.
946 !! @param[in] lhs array of string
947 !! @param[in] rhs character expression to search for
948 !! @return .true. if rhs is present in lhs
949 !!
950 !! @ingroup group_string
951 logical function strings_contain_character(lhs, rhs) result(res)
952 type(string), intent(in) :: lhs(:)
953 character(*), intent(in) :: rhs
954 !private
955 integer :: i
956
957 res = .false.
958 do i = 1, size(lhs)
959 if (lhs(i) == rhs) then
960 res = .true.
961 exit
962 end if
963 end do
964 end function
965
966 !> Checks whether an array of character contains a given character expression.
967 !! @param[in] lhs array of character
968 !! @param[in] rhs character expression to search for
969 !! @return .true. if rhs is present in lhs
970 !!
971 !! @ingroup group_string
972 logical function characters_contain_character(lhs, rhs) result(res)
973 character(*), intent(in) :: lhs(:)
974 character(*), intent(in) :: rhs
975 !private
976 integer :: i
977
978 res = .false.
979 do i = 1, size(lhs)
980 if (lhs(i) == rhs) then
981 res = .true.
982 exit
983 end if
984 end do
985 end function
986
987 !> Checks whether an array of character contains a given string.
988 !! @param[in] lhs array of character
989 !! @param[in] rhs string to search for
990 !! @return .true. if rhs is present in lhs
991 !!
992 !! @ingroup group_string
993 logical function characters_contain_string(lhs, rhs) result(res)
994 character(*), intent(in) :: lhs(:)
995 type(string), intent(in) :: rhs
996 !private
997 integer :: i
998
999 res = .false.
1000 do i = 1, size(lhs)
1001 if (lhs(i) == rhs) then
1002 res = .true.
1003 exit
1004 end if
1005 end do
1006 end function
1007
1008 integer function index_string_string(str, substr, back) result(res)
1009 class(string), intent(in) :: str
1010 class(string), intent(in) :: substr
1011 logical, intent(in), optional :: back
1012
1013 res = index(str%chars, substr%chars, back=back)
1014 end function
1015
1016 integer function index_character_string(str, substr, back) result(res)
1017 character(*), intent(in) :: str
1018 class(string), intent(in) :: substr
1019 logical, intent(in), optional :: back
1020
1021 res = index(str, substr%chars, back=back)
1022 end function
1023
1024 integer function index_string_character(str, substr, back) result(res)
1025 class(string), intent(in) :: str
1026 character(*), intent(in) :: substr
1027 logical, intent(in), optional :: back
1028
1029 res = index(str%chars, substr, back=back)
1030 end function
1031
1032end module
integer, parameter, public chksize
Default chunk size used for internal buffering operations.
character function, public tail(str)
Returns the last non-blank character of a string.
Definition string.f90:746
pure character(len_trim(str)) function, public lowercase(str)
Convert string to lower case (respects contents of quotes).
Definition string.f90:852
subroutine, public writechk(unit, str)
Write a long line split into chunks of size CHKSIZE with continuation (&).
Definition string.f90:887
character(1) function, public previous(line, pos)
Returns the previous non-blank character before position pos (updates pos).
Definition string.f90:909
pure character(len_trim(str)) function, public uppercase(str)
Convert string to upper case (respects contents of quotes).
Definition string.f90:809
character(:) function, allocatable, public concat(str1, str2)
Smart concatenation that removes continuation markers (&) and handles line-continuation rules.
Definition string.f90:763
logical function, public starts_with(str, arg1, idx)
Checks if a string starts with a given prefix Returns .true. if the string str (after trimming leadin...
Definition string.f90:715
character function, public head(str)
Returns the first character of the trimmed string.
Definition string.f90:732
Locate the position of a substring.
Definition string.f90:395
Return the trimmed length of a string object.
Definition string.f90:201
Return the length of a string object.
Definition string.f90:164
Remove trailing blanks from a string object.
Definition string.f90:240
Represents text as a sequence of ASCII code units. The derived type wraps an allocatable character ar...
Definition string.f90:112