blob: d66440d67ea6dc717efb0995bd5fa1d356f02b81 [file] [log] [blame]
!>
!! Definition of a string class for Fortran.
!!
!! Jeff Overbey (1/29/09)
!<
module string_module
implicit none
private
public :: new_string
interface new_string
module procedure &
new_string_string, &
new_string_integer, &
new_string_logical
end interface
type, public :: string
private
character, dimension(:), allocatable :: data
integer :: length = 0
contains
procedure :: initialize
generic :: operator(+) => concat1, concat2
generic :: concat => concat1, concat2 ! Photran doesn't parse - R1207
procedure :: substring
procedure :: char_at
procedure :: map
procedure :: to_uppercase
procedure :: to_lowercase
generic :: write(formatted) => write_formatted ! Photran doesn't parse
generic :: write(unformatted) => write_unformatted
procedure :: value
generic :: operator(==) => equals
generic :: operator(.eqic.) => equals_ignore_case
final :: destructor
procedure, private :: concat1
procedure, private :: concat2
procedure, private :: write_formatted
procedure, private :: write_unformatted
procedure, private :: all_satisfy
procedure, private :: equals
procedure, private :: equals_ignore_case
end type string
contains
type(string) function new_string_string(string) result(return)
character(len=*), intent(in) :: string
call return%initialize(string)
end function
type(string) function new_string_integer(integer) result(return)
integer, intent(in) :: integer
return = new_string_nat(abs(integer))
if (integer < 0) return = new_string("-") + return
end function
type(string) function new_string_logical(logical) result(return)
logical, intent(in) :: logical
if (logical) then
return = new_string('.true.')
else
return = new_string('.false.')
end if
end function
type(string) recursive function new_string_nat(absval) result(return)
integer, intent(in) :: absval
if (0 <= absval .and. absval <= 9) then
call return%initialize(digit_string(absval))
else
return = new_string_nat(absval / 10) + digit_string(mod(absval, 10))
end if
end function
character(len=1) function digit_string(digit)
integer, intent(in) :: digit
select case (digit)
case (0); digit_string = '0'
case (1); digit_string = '1'
case (2); digit_string = '2'
case (3); digit_string = '3'
case (4); digit_string = '4'
case (5); digit_string = '5'
case (6); digit_string = '6'
case (7); digit_string = '7'
case (8); digit_string = '8'
case (9); digit_string = '9'
end select
end function
subroutine initialize(self, data)
class(string), intent(inout) :: self
character(len=*), intent(in) :: data
integer :: i
self%length = len(data)
allocate(self%data(self%length))
do i = 1, len(data)
self%data(i) = data(i:i)
end do
end subroutine
! XLF parses class(string) but gives semantic error; Photran doesn't parse
type(string) function concat1(self, other) result(return)
class(string), intent(in) :: self
class(string), intent(in) :: other
return = concat2(self, other%value())
end function
type(string) function concat2(self, other) result(return)
class(string), intent(in) :: self
character(len=*), intent(in) :: other
integer :: i
return%length = self%length + len(other)
allocate(return%data(return%length))
return%data(1:self%length) = self%data
do i = 1, len(other); return%data(i + self%length) = other(i:i); end do
end function
type(string) function substring(self, start, thru) result(return)
class(string), intent(in) :: self
integer, intent(in) :: start, thru
if (thru < start) return
return%length = thru - start + 1
allocate(return%data(return%length))
return%data(1:self%length) = self%data(start:thru)
end function
character(len=1) function char_at(self, index) result(return)
class(string), intent(in) :: self
integer, intent(in) :: index
return = self%data(index)
end function
type(string) function map(self, function) result(return)
class(string), intent(in) :: self
interface
character(len=1) function function(string)
character(len=1), intent(in) :: string
end function
end interface
integer :: i
return%length = self%length
allocate(return%data(return%length))
do i = 1, self%length
return%data(i) = function(self%data(i))
end do
end function
type(string) function to_uppercase(self) result(return)
class(string), intent(in) :: self
return = self%map(to_upper)
!contains ! Causes XLF compiler bug -- see xlf-bug.f03
end function
character(len=1) function to_upper(ch) result(return)
character(len=1), intent(in) :: ch
integer, parameter :: diff = iachar('a') - iachar('A')
if (lle('a', ch) .and. lle(ch, 'z')) then
return = achar(iachar(ch) - diff)
else
return = ch
end if
end function
!end function
type(string) function to_lowercase(self) result(return)
class(string), intent(in) :: self
return = self%map(to_lower)
!contains ! Causes XLF compiler bug -- see xlf-bug.f03
end function
character(len=1) function to_lower(ch) result(return)
character(len=1), intent(in) :: ch
integer, parameter :: diff = iachar('a') - iachar('A')
if (lle('A', ch) .and. lle(ch, 'Z')) then
return = achar(iachar(ch) + diff)
else
return = ch
end if
end function
!end function
subroutine write_formatted(self, unit, iotype, v_list, iostat, iomsg)
class(string), intent(in) :: self
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, dimension(:), intent(in) :: v_list
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
! TODO Incomplete
write (unit=unit, fmt=*, iostat=iostat, iomsg=iomsg) self%value()
end subroutine
subroutine write_unformatted(self, unit, iostat, iomsg)
class(string), intent(in) :: self
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
! TODO Incomplete
write (unit=unit, fmt=*, iostat=iostat, iomsg=iomsg) self%value()
end subroutine
character(len=self%length) function value(self) result(result)
class(string), intent(in) :: self
integer :: i
do i = 1, ubound(self%data, 1)
result(i:i) = self%data(i)
end do
end function
logical function all_satisfy(self, function, other) result(return)
class(string), intent(in) :: self
interface
logical function function(ch1, ch2)
character(len=1), intent(in) :: ch1, ch2
end function
end interface
class(string), intent(in) :: other
integer :: i
if (self%length <> other%length) then
return = .false.
else
return = .true.
do i = 1, self%length
if (function(self%data(i), other%data(i)) .eqv. .false.) then
return = .false.
return
end if
end do
end if
end function
logical function equals(self, other) result(return)
class(string), intent(in) :: self
class(string), intent(in) :: other
return = self%all_satisfy(ch_eq, other)
end function
logical function ch_eq(ch1, ch2) result(return)
character(len=1), intent(in) :: ch1, ch2
return = ch1 .eq. ch2
end function
logical function equals_ignore_case(self, other) result(return)
class(string), intent(in) :: self
class(string), intent(in) :: other
return = self%all_satisfy(ch_eqic, other)
end function
logical function ch_eqic(ch1, ch2) result(return)
character(len=1), intent(in) :: ch1, ch2
return = to_upper(ch1) .eq. to_upper(ch2)
end function
subroutine destructor(self)
type(string), intent(inout) :: self
end subroutine
end module string_module
module test_util
implicit none
contains
character(len=1) function identity(string) result(return)
character(len=1), intent(in) :: string
return = string
end function
character(len=1) function always_a(string) result(return)
character(len=1), intent(in) :: string
return = 'a'
end function
end module test_util
program string_test
use string_module
use test_util
implicit none
type(string) :: s1, s2
print *, "Expected: "
print *, "Actual: ", s1%value()
call s1%initialize('Hello')
call s2%initialize('!')
print *, "Expected: Hello"
print *, "Actual: ", s1%value()
s1 = new_string("HeLlOoO!")
print *, "Expected: HeLlOoO!"
print *, "Actual: ", s1%value()
s1 = new_string("Hello")
s1 = s1%concat(', world')
print *, "Expected: Hello, world"
print *, "Actual: ", s1%value()
s1 = s1%concat(s2)
print *, "Expected: Hello, world!"
print *, "Actual: ", s1%value()
s1 = s1 + '!'
!print *, (s1 + '!')%value()
print *, "Expected: Hello, world!!"
print *, "Actual: ", s1%value()
print *, "Expected: -9876x"
print *, "Actual: ", new_string("-") + new_string("9876x")
print *, "Expected: ell"
print *, "Actual: ", "Hello, world!!"(2:4)
print *, "Expected: ell"
print *, "Actual: ", s1%substring(2, 4)
print *, "Expected: He"
print *, "Actual: ", s1%substring(0, 2)
print *, "Expected: "
print *, "Actual: ", "Hello, world!!"(5:1)
print *, "Expected: "
print *, "Actual: ", s1%substring(5, 1)
print *, "Expected: e"
print *, "Actual: ", s1%char_at(2)
print *, "Expected: ", "Hello, world!!"
print *, "Actual: ", s1%map(identity)
print *, "Expected: ", "aaaaaaaaaaaaaa"
print *, "Actual: ", s1%map(always_a)
print *, "Expected: HELLO, WORLD!!"
print *, "Actual: ", s1%to_uppercase()
print *, "Expected: hello, world!!"
print *, "Actual: ", s1%to_lowercase()
print *, "Expected: Hello, world!!"
print *, "Actual: ", s1
print *, "Expected: 0"
print *, "Actual: ", new_string(0)
print *, "Expected: 5"
print *, "Actual: ", new_string(5)
print *, "Expected: 12345"
print *, "Actual: ", new_string(12345)
print *, "Expected: -8"
print *, "Actual: ", new_string(-8)
print *, "Expected: -98765"
print *, "Actual: ", new_string(-98765)
print *, "Expected: .true."
print *, "Actual: ", new_string(.true.)
print *, "Expected: .false."
print *, "Actual: ", new_string(.false.)
print *, "Expected: T"
print *, "Actual: ", new_string(.false.) == new_string(".false.")
print *, "Expected: T"
print *, "Actual: ", new_string("") == new_string("")
print *, "Expected: F"
print *, "Actual: ", new_string("") == new_string("true")
print *, "Expected: F"
print *, "Actual: ", new_string("true") == new_string("")
print *, "Expected: F"
print *, "Actual: ", new_string("true") == new_string("truE")
print *, "Expected: T"
print *, "Actual: ", new_string(.false.) .eqic. new_string(".false.")
print *, "Expected: T"
print *, "Actual: ", new_string("") .eqic. new_string("")
print *, "Expected: F"
print *, "Actual: ", new_string("") .eqic. new_string("true")
print *, "Expected: F"
print *, "Actual: ", new_string("true") .eqic. new_string("")
print *, "Expected: T"
print *, "Actual: ", new_string("true") .eqic. new_string("truE")
print *, "Expected: T"
print *, "Actual: ", new_string("tRUe") .eqic. new_string("TrUe")
print *, "Done"
end program string_test