diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index faa30df06..20dc511d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRC + stdlib_experimental_ascii.f90 stdlib_experimental_io.f90 stdlib_experimental_error.f90 ) diff --git a/src/stdlib_experimental_ascii.f90 b/src/stdlib_experimental_ascii.f90 new file mode 100644 index 000000000..fd7910790 --- /dev/null +++ b/src/stdlib_experimental_ascii.f90 @@ -0,0 +1,196 @@ +module stdlib_experimental_ascii + + implicit none + private + + ! Character validation functions + public :: is_alpha, is_alphanum + public :: is_digit, is_hex_digit, is_octal_digit + public :: is_control, is_white, is_blank + public :: is_ascii, is_punctuation + public :: is_graphical, is_printable + public :: is_lower, is_upper + + ! Character conversion functions + public :: to_lower, to_upper + + ! All control characters in the ASCII table (see www.asciitable.com). + character(len=1), public, parameter :: NUL = achar(z'00') !! Null + character(len=1), public, parameter :: SOH = achar(z'01') !! Start of heading + character(len=1), public, parameter :: STX = achar(z'02') !! Start of text + character(len=1), public, parameter :: ETX = achar(z'03') !! End of text + character(len=1), public, parameter :: EOT = achar(z'04') !! End of transmission + character(len=1), public, parameter :: ENQ = achar(z'05') !! Enquiry + character(len=1), public, parameter :: ACK = achar(z'06') !! Acknowledge + character(len=1), public, parameter :: BEL = achar(z'07') !! Bell + character(len=1), public, parameter :: BS = achar(z'08') !! Backspace + character(len=1), public, parameter :: TAB = achar(z'09') !! Horizontal tab + character(len=1), public, parameter :: LF = achar(z'0A') !! NL line feed, new line + character(len=1), public, parameter :: VT = achar(z'0B') !! Vertical tab + character(len=1), public, parameter :: FF = achar(z'0C') !! NP form feed, new page + character(len=1), public, parameter :: CR = achar(z'0D') !! Carriage return + character(len=1), public, parameter :: SO = achar(z'0E') !! Shift out + character(len=1), public, parameter :: SI = achar(z'0F') !! Shift in + character(len=1), public, parameter :: DLE = achar(z'10') !! Data link escape + character(len=1), public, parameter :: DC1 = achar(z'11') !! Device control 1 + character(len=1), public, parameter :: DC2 = achar(z'12') !! Device control 2 + character(len=1), public, parameter :: DC3 = achar(z'13') !! Device control 3 + character(len=1), public, parameter :: DC4 = achar(z'14') !! Device control 4 + character(len=1), public, parameter :: NAK = achar(z'15') !! Negative acknowledge + character(len=1), public, parameter :: SYN = achar(z'16') !! Synchronous idle + character(len=1), public, parameter :: ETB = achar(z'17') !! End of transmission block + character(len=1), public, parameter :: CAN = achar(z'18') !! Cancel + character(len=1), public, parameter :: EM = achar(z'19') !! End of medium + character(len=1), public, parameter :: SUB = achar(z'1A') !! Substitute + character(len=1), public, parameter :: ESC = achar(z'1B') !! Escape + character(len=1), public, parameter :: FS = achar(z'1C') !! File separator + character(len=1), public, parameter :: GS = achar(z'1D') !! Group separator + character(len=1), public, parameter :: RS = achar(z'1E') !! Record separator + character(len=1), public, parameter :: US = achar(z'1F') !! Unit separator + character(len=1), public, parameter :: DEL = achar(z'7F') !! Delete + + ! Constant character sequences + character(len=*), public, parameter :: fullhex_digits = "0123456789ABCDEFabcdef" !! 0 .. 9A .. Fa .. f + character(len=*), public, parameter :: hex_digits = fullhex_digits(1:16) !! 0 .. 9A .. F + character(len=*), public, parameter :: lowerhex_digits = "0123456789abcdef" !! 0 .. 9a .. f + character(len=*), public, parameter :: digits = hex_digits(1:10) !! 0 .. 9 + character(len=*), public, parameter :: octal_digits = digits(1:8) !! 0 .. 7 + character(len=*), public, parameter :: letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" !! A .. Za .. z + character(len=*), public, parameter :: uppercase = letters(1:26) !! A .. Z + character(len=*), public, parameter :: lowercase = letters(27:) !! a .. z + character(len=*), public, parameter :: whitespace = " "//TAB//VT//CR//LF//FF !! ASCII _whitespace + +contains + + !> Checks whether `c` is an ASCII letter (A .. Z, a .. z). + pure logical function is_alpha(c) + character(len=1), intent(in) :: c !! The character to test. + is_alpha = (c >= 'A' .and. c <= 'Z') .or. (c >= 'a' .and. c <= 'z') + end function + + !> Checks whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z). + pure logical function is_alphanum(c) + character(len=1), intent(in) :: c !! The character to test. + is_alphanum = (c >= '0' .and. c <= '9') .or. (c >= 'a' .and. c <= 'z') & + .or. (c >= 'A' .and. c <= 'Z') + end function + + !> Checks whether or not `c` is in the ASCII character set - + ! i.e. in the range 0 .. 0x7F. + pure logical function is_ascii(c) + character(len=1), intent(in) :: c !! The character to test. + is_ascii = iachar(c) <= z'7F' + end function + + !> Checks whether `c` is a control character. + pure logical function is_control(c) + character(len=1), intent(in) :: c !! The character to test. + integer :: ic + ic = iachar(c) + is_control = ic < z'20' .or. ic == z'7F' + end function + + !> Checks whether `c` is a digit (0 .. 9). + pure logical function is_digit(c) + character(len=1), intent(in) :: c !! The character to test. + is_digit = ('0' <= c) .and. (c <= '9') + end function + + !> Checks whether `c` is a digit in base 8 (0 .. 7). + pure logical function is_octal_digit(c) + character(len=1), intent(in) :: c !! The character to test. + is_octal_digit = (c >= '0') .and. (c <= '7'); + end function + + !> Checks whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f). + pure logical function is_hex_digit(c) + character(len=1), intent(in) :: c !! The character to test. + is_hex_digit = (c >= '0' .and. c <= '9') .or. (c >= 'a' .and. c <= 'f') & + .or. (c >= 'A' .and. c <= 'F') + end function + + !> Checks whether or not `c` is a punctuation character. That includes + ! all ASCII characters which are not control characters, letters, + ! digits, or whitespace. + pure logical function is_punctuation(c) + character(len=1), intent(in) :: c !! The character to test. + integer :: ic + ic = iachar(c) ! '~' '!' + is_punctuation = (ic <= z'7E') .and. (ic >= z'21') .and. & + (.not. is_alphanum(c)) + end function + + !> Checks whether or not `c` is a printable character other than the + ! space character. + pure logical function is_graphical(c) + character(len=1), intent(in) :: c !! The character to test. + integer :: ic + ic = iachar(c) ! '!' '~' + is_graphical = (z'21' <= ic) .and. (ic <= z'7E') + end function + + !> Checks whether or not `c` is a printable character - including the + ! space character. + pure logical function is_printable(c) + character(len=1), intent(in) :: c !! The character to test. + integer :: ic + ic = iachar(c) ! '~' + is_printable = c >= ' ' .and. ic <= z'7E' + end function + + !> Checks whether `c` is a lowercase ASCII letter (a .. z). + pure logical function is_lower(c) + character(len=1), intent(in) :: c !! The character to test. + is_lower = (c >= 'a') .and. (c <= 'z') + end function + + !> Checks whether `c` is an uppercase ASCII letter (A .. Z). + pure logical function is_upper(c) + character(len=1), intent(in) :: c !! The character to test. + is_upper = (c >= 'A') .and. (c <= 'Z') + end function + + !> Checks whether or not `c` is a whitespace character. That includes the + ! space, tab, vertical tab, form feed, carriage return, and linefeed + ! characters. + pure logical function is_white(c) + character(len=1), intent(in) :: c !! The character to test. + integer :: ic + ic = iachar(c) ! TAB, LF, VT, FF, CR + is_white = (c == ' ') .or. (ic >= z'09' .and. ic <= z'0D'); + end function + + !> Checks whether or not `c` is a blank character. That includes the + ! only the space and tab characters + pure logical function is_blank(c) + character(len=1), intent(in) :: c !! The character to test. + integer :: ic + ic = iachar(c) ! TAB + is_blank = (c == ' ') .or. (ic == z'09'); + end function + + !> Returns the corresponding lowercase letter, if `c` is an uppercase + ! ASCII character, otherwise `c` itself. + pure function to_lower(c) result(t) + character(len=1), intent(in) :: c !! A character. + character(len=1) :: t + integer :: diff + diff = iachar('A')-iachar('a') + t = c + ! if uppercase, make lowercase + if (is_upper(t)) t = achar(iachar(t) - diff) + end function + + !> Returns the corresponding uppercase letter, if `c` is a lowercase + ! ASCII character, otherwise `c` itself. + pure function to_upper(c) result(t) + character(len=1), intent(in) :: c !! A character. + character(len=1) :: t + integer :: diff + diff = iachar('A')-iachar('a') + t = c + ! if lowercase, make uppercase + if (is_lower(t)) t = achar(iachar(t) + diff) + end function + +end module \ No newline at end of file diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c6f586e91..7ba8c4a4f 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1 +1,3 @@ +add_subdirectory(ascii) add_subdirectory(loadtxt) + diff --git a/src/tests/ascii/CMakeLists.txt b/src/tests/ascii/CMakeLists.txt new file mode 100644 index 000000000..41df5d7ac --- /dev/null +++ b/src/tests/ascii/CMakeLists.txt @@ -0,0 +1,9 @@ +include_directories(${PROJECT_BINARY_DIR}/src) + +project(ascii) + +add_executable(test_ascii test_ascii.f90) +target_link_libraries(test_ascii fortran_stdlib) + +add_test(test_ascii ${PROJECT_BINARY_DIR}/test_ascii) + diff --git a/src/tests/ascii/test_ascii.f90 b/src/tests/ascii/test_ascii.f90 new file mode 100644 index 000000000..5a535f2ce --- /dev/null +++ b/src/tests/ascii/test_ascii.f90 @@ -0,0 +1,542 @@ +program test_ascii + + use stdlib_experimental_error, only: assert + use stdlib_experimental_ascii, only: lowercase, uppercase, digits, & + octal_digits, fullhex_digits, hex_digits, lowerhex_digits, & + whitespace, letters, is_alphanum, is_alpha, is_lower, is_upper, & + is_digit, is_octal_digit, is_hex_digit, is_white, is_blank, & + is_control, is_punctuation, is_graphical, is_printable, is_ascii, & + to_lower, to_upper, LF, TAB, NUL, DEL + + write(*,*) "Lowercase letters: ", lowercase + write(*,*) "Uppercase letters: ", uppercase + write(*,*) "Digits: ", digits + write(*,*) "Octal digits: ", octal_digits + write(*,*) "Full hex digits: ", fullhex_digits + write(*,*) "Hex digits: ", hex_digits + write(*,*) "Lower hex digits: ", lowerhex_digits + + call test_is_alphanum_short + call test_is_alphanum_long + + call test_is_alpha_short + call test_is_alpha_long + + call test_is_lower_short + call test_is_lower_long + + call test_is_upper_short + call test_is_upper_long + + call test_is_digit_short + call test_is_digit_long + + call test_is_octal_digit_short + call test_is_octal_digit_long + + call test_is_hex_digit_short + call test_is_hex_digit_long + + call test_is_white_short + call test_is_white_long + + call test_is_blank_short + call test_is_blank_long + + call test_is_control_short + call test_is_control_long + + call test_is_punctuation_short + call test_is_punctuation_long + + call test_is_graphical_short + call test_is_graphical_long + + call test_is_printable_short + call test_is_printable_long + + call test_is_ascii_short + call test_is_ascii_long + + call test_to_lower_short + call test_to_lower_long + + call test_to_upper_short + call test_to_upper_long + + ! call test_ascii_table + +contains + + subroutine test_is_alphanum_short + write(*,*) "test_is_alphanum_short" + call assert(is_alphanum('A')) + call assert(is_alphanum('1')) + call assert(.not. is_alphanum('#')) + + ! N.B.: does not return true for non-ASCII Unicode alphanumerics + call assert(.not. is_alphanum('á')) + end subroutine + + subroutine test_is_alphanum_long + integer :: i + character(len=:), allocatable :: clist + + write(*,*) "test_is_alphanum_long" + + clist = digits//octal_digits//fullhex_digits//letters//lowercase//uppercase + do i = 1, len(clist) + call assert(is_alphanum(clist(i:i))) + end do + + clist = whitespace + do i = 1, len(clist) + call assert(.not. is_alphanum(clist(i:i))) + end do + end subroutine + + subroutine test_is_alpha_short + write(*,*) "test_is_alpha_short" + call assert(is_alpha('A')) + call assert(.not. is_alpha('1')) + call assert(.not. is_alpha('#')) + + ! N.B.: does not return true for non-ASCII Unicode alphabetic characters + call assert(.not. is_alpha('á')) + end subroutine + + subroutine test_is_alpha_long + integer :: i + character(len=:), allocatable :: clist + + write(*,*) "test_is_alpha_long" + + clist = letters//lowercase//uppercase + do i = 1, len(clist) + call assert(is_alpha(clist(i:i))) + end do + + clist = digits//octal_digits//whitespace + do i = 1, len(clist) + call assert(.not. is_alpha(clist(i:i))) + end do + end subroutine + + subroutine test_is_lower_short + write(*,*) "test_is_lower_short" + call assert(is_lower('a')) + call assert(.not. is_lower('A')) + call assert(.not. is_lower('#')) + + ! N.B.: does not return true for non-ASCII Unicode lowercase letters + call assert(.not. is_lower('á')) + call assert(.not. is_lower('Á')) + end subroutine + + subroutine test_is_lower_long + integer :: i + character(len=:), allocatable :: clist + + write(*,*) "test_is_lower_long" + do i = 1, len(lowercase) + call assert(is_lower(lowercase(i:i))) + end do + + clist = digits//uppercase//whitespace + do i = 1, len(clist) + call assert(.not. is_lower(clist(i:i))) + end do + end subroutine + + subroutine test_is_upper_short + write(*,*) "test_is_upper_short" + call assert(is_upper('A')) + call assert(.not. is_upper('a')) + call assert(.not. is_upper('#')) + + ! N.B.: does not return true for non-ASCII Unicode uppercase letters + call assert(.not. is_upper('á')) + call assert(.not. is_upper('Á')) + end subroutine + + subroutine test_is_upper_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_upper_long" + do i = 1, len(uppercase) + call assert(is_upper(uppercase(i:i))) + end do + + clist = digits//lowercase//whitespace + do i = 1, len(clist) + call assert(.not. is_upper(clist(i:i))) + end do + end subroutine + + + subroutine test_is_digit_short + write(*,*) "test_is_digit_short" + call assert(is_digit('3')) + call assert(is_digit('8')) + call assert(.not. is_digit('B')) + call assert(.not. is_digit('#')) + + ! N.B.: does not return true for non-ASCII Unicode numbers + call assert(.not. is_digit('0')) ! full-width digit zero (U+FF10) + call assert(.not. is_digit('4')) ! full-width digit four (U+FF14)) + end subroutine + + subroutine test_is_digit_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_digit_long" + do i = 1, len(digits) + call assert(is_digit(digits(i:i))) + end do + + clist = letters//whitespace + do i = 1, len(clist) + call assert(.not. is_digit(clist(i:i))) + end do + end subroutine + + subroutine test_is_octal_digit_short + write(*,*) "test_is_octal_digit_short" + call assert(is_octal_digit('0')) + call assert(is_octal_digit('7')) + call assert(.not. is_octal_digit('8')) + call assert(.not. is_octal_digit('A')) + call assert(.not. is_octal_digit('#')) + end subroutine + + subroutine test_is_octal_digit_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_octal_digit_long" + do i = 1, len(octal_digits) + call assert(is_octal_digit(octal_digits(i:i))) + end do + clist = letters//'89'//whitespace + do i = 1, len(clist) + call assert(.not. is_octal_digit(clist(i:i))) + end do + end subroutine + + subroutine test_is_hex_digit_short + write(*,*) "test_is_hex_digit_short" + call assert(is_hex_digit('0')) + call assert(is_hex_digit('A')) + call assert(is_hex_digit('f')) !! lowercase hex digits are accepted + call assert(.not. is_hex_digit('g')) + call assert(.not. is_hex_digit('G')) + call assert(.not. is_hex_digit('#')) + end subroutine + + subroutine test_is_hex_digit_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_hex_digit_long" + do i = 1, len(fullhex_digits) + call assert(is_hex_digit(fullhex_digits(i:i))) + end do + clist = lowercase(7:)//uppercase(7:)//whitespace + do i = 1, len(clist) + call assert(.not. is_hex_digit(clist(i:i))) + end do + end subroutine + + subroutine test_is_white_short + write(*,*) "test_is_white_short" + call assert(is_white(' ')) + call assert(is_white(TAB)) + call assert(is_white(LF)) + call assert(.not. is_white('1')) + call assert(.not. is_white('a')) + call assert(.not. is_white('#')) + end subroutine + + subroutine test_is_white_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_white_long" + do i = 1, len(whitespace) + call assert(is_white(whitespace(i:i))) + end do + clist = digits//letters + do i = 1, len(clist) + call assert(.not. is_white(clist(i:i))) + end do + end subroutine + + subroutine test_is_blank_short + write(*,*) "test_is_blank_short" + call assert(is_blank(' ')) + call assert(is_blank(TAB)) + call assert(.not. is_blank('1')) + call assert(.not. is_blank('a')) + call assert(.not. is_blank('#')) + end subroutine + + subroutine test_is_blank_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_blank_long" + do i = 1, len(whitespace) + if (whitespace(i:i) == ' ' .or. whitespace(i:i) == TAB) then + call assert(is_blank(whitespace(i:i))) + else + call assert(.not. is_blank(whitespace(i:i))) + end if + end do + clist = digits//letters + do i = 1, len(clist) + call assert(.not. is_blank(clist(i:i))) + end do + end subroutine + + subroutine test_is_control_short + write(*,*) "test_is_control_short" + ! write(*,*) is_control('\0') + ! write(*,*) is_control('\022') + call assert(is_control(new_line('a'))) ! newline is both whitespace and control + call assert(.not. is_control(' ')) + call assert(.not. is_control('1')) + call assert(.not. is_control('a')) + call assert(.not. is_control('#')) + + ! N.B.: non-ASCII Unicode control characters are not recognized: + ! write(*,*) .not. is_control('\u0080') + ! write(*,*) .not. is_control('\u2028') + ! write(*,*) .not. is_control('\u2029') + end subroutine + + subroutine test_is_control_long + integer :: i + character(len=:), allocatable :: clist + write(*,*) "test_is_control_long" + do i = 0, 31 + call assert(is_control(achar(i))) + end do + call assert(is_control(DEL)) + + clist = digits//letters//' ' + do i = 1, len(clist) + call assert(.not. is_control(clist(i:i))) + end do + end subroutine + + subroutine test_is_punctuation_short + write(*,*) "test_is_punctuation_short" + call assert(is_punctuation('.')) + call assert(is_punctuation(',')) + call assert(is_punctuation(':')) + call assert(is_punctuation('!')) + call assert(is_punctuation('#')) + call assert(is_punctuation('~')) + call assert(is_punctuation('+')) + call assert(is_punctuation('_')) + + call assert(.not. is_punctuation('1')) + call assert(.not. is_punctuation('a')) + call assert(.not. is_punctuation(' ')) + call assert(.not. is_punctuation(LF)) ! new line character + call assert(.not. is_punctuation(NUL)) + + ! N.B.: Non-ASCII Unicode punctuation characters are not recognized. + ! write(*,*) is_punctuation('\u2012') ! (U+2012 = en-dash) + end subroutine + + subroutine test_is_punctuation_long + integer :: i + character(len=1) :: c + write(*,*) "test_is_punctuation_long" + do i = 0, 127 + c = achar(i) + if (is_control(c) .or. is_alphanum(c) .or. c == ' ') then + call assert(.not. is_punctuation(c)) + else + call assert(is_punctuation(c)) + end if + end do + end subroutine + + subroutine test_is_graphical_short + write(*,*) "test_is_graphical" + call assert(is_graphical('1')) + call assert(is_graphical('a')) + call assert(is_graphical('#')) + call assert(.not. is_graphical(' ')) ! whitespace is not graphical + call assert(.not. is_graphical(LF)) + call assert(.not. is_graphical(NUL)) + + ! N.B.: Unicode graphical characters are not regarded as such. + call assert(.not. is_graphical('ä')) + end subroutine + + subroutine test_is_graphical_long + integer :: i + character(len=1) :: c + write(*,*) "test_is_graphical_long" + do i = 0, 127 + c = achar(i) + if (is_control(c) .or. c == ' ') then + call assert(.not. is_graphical(c)) + else + call assert(is_graphical(c)) + end if + end do + end subroutine + + subroutine test_is_printable_short + write(*,*) "test_is_printable_short" + call assert(is_printable(' ')) ! whitespace is printable + call assert(is_printable('1')) + call assert(is_printable('a')) + call assert(is_printable('#')) + call assert(.not. is_printable(NUL)) ! control characters are not printable + + ! N.B.: Printable non-ASCII Unicode characters are not recognized. + call assert(.not. is_printable('ä')) + end subroutine + + subroutine test_is_printable_long + integer :: i + character(len=1) :: c + write(*,*) "test_is_printable_long" + do i = 0, 127 + c = achar(i) + if (is_control(c)) then + call assert(.not. is_printable(c)) + else + call assert(is_printable(c)) + end if + end do + end subroutine + + subroutine test_is_ascii_short() + write(*,*) "test_is_ascii_short" + call assert(is_ascii('a')) + call assert(.not. is_ascii('ä')) + end subroutine + + subroutine test_is_ascii_long() + integer :: i + write(*,*) "test_is_ascii_long" + do i = 0, 127 + call assert(is_ascii(achar(i))) + end do + call assert(.not. is_ascii(achar(128))) ! raises compiler warning + + end subroutine + + subroutine test_to_lower_short() + write(*,*) "test_to_lower_short" + call assert(to_lower('a') == 'a') + call assert(to_lower('A') == 'a') + call assert(to_lower('#') == '#') + end subroutine + + subroutine test_to_lower_long() + integer :: i + character(len=1) :: c + write(*,*) "test_to_lower_long" + do i = 1, len(uppercase) + call assert(to_lower(uppercase(i:i)) == lowercase(i:i)) + end do + do i = 0, 127 + c = achar(i) + if (c < 'A' .or. c > 'Z') then + call assert(to_lower(c) == c) + else + call assert(to_lower(c) /= c) + end if + end do + end subroutine + + subroutine test_to_upper_short() + write(*,*) "test_to_upper_short" + call assert(to_upper('a') == 'A') + call assert(to_upper('A') == 'A') + call assert(to_upper('#') == '#') + end subroutine + + subroutine test_to_upper_long() + integer :: i + character(len=1) :: c + write(*,*) "test_to_upper_long" + do i = 1, len(lowercase) + call assert(to_upper(lowercase(i:i)) == uppercase(i:i)) + end do + + do i = 0, 127 + c = achar(i) + if (c < 'a' .or. c > 'z') then + call assert(to_upper(c) == c) + else + call assert(to_upper(c) /= c) + end if + end do + end subroutine + + ! + ! This test reproduces the true/false table found at + ! https://en.cppreference.com/w/cpp/string/byte + ! + subroutine test_ascii_table + integer :: i, j + character(len=1) :: c + logical :: table(15,12) + + abstract interface + pure logical function validation_func_interface(c) + character(len=1), intent(in) :: c + end function + end interface + + type :: proc_pointer_array + procedure(validation_func_interface), pointer, nopass :: pcf + end type proc_pointer_array + + type(proc_pointer_array) :: pcfs(12) + + pcfs(1)%pcf => is_control + pcfs(2)%pcf => is_printable + pcfs(3)%pcf => is_white + pcfs(4)%pcf => is_blank + pcfs(5)%pcf => is_graphical + pcfs(6)%pcf => is_punctuation + pcfs(7)%pcf => is_alphanum + pcfs(8)%pcf => is_alpha + pcfs(9)%pcf => is_upper + pcfs(10)%pcf => is_lower + pcfs(11)%pcf => is_digit + pcfs(12)%pcf => is_hex_digit + + ! loop through functions + do i = 1, 12 + table(1,i) = all([(pcfs(i)%pcf(achar(j)),j=0,8)]) ! control codes + table(2,i) = pcfs(i)%pcf(achar(9)) ! tab + table(3,i) = all([(pcfs(i)%pcf(achar(j)),j=10,13)]) ! whitespaces + table(4,i) = all([(pcfs(i)%pcf(achar(j)),j=14,31)]) ! control codes + table(5,i) = pcfs(i)%pcf(achar(32)) ! space + table(6,i) = all([(pcfs(i)%pcf(achar(j)),j=33,47)]) ! !"#$%&'()*+,-./ + table(7,i) = all([(pcfs(i)%pcf(achar(j)),j=48,57)]) ! 0123456789 + table(8,i) = all([(pcfs(i)%pcf(achar(j)),j=58,64)]) ! :;<=>?@ + table(9,i) = all([(pcfs(i)%pcf(achar(j)),j=65,70)]) ! ABCDEF + table(10,i) = all([(pcfs(i)%pcf(achar(j)),j=71,90)]) ! GHIJKLMNOPQRSTUVWXYZ + table(11,i) = all([(pcfs(i)%pcf(achar(j)),j=91,96)]) ! [\]^_` + table(12,i) = all([(pcfs(i)%pcf(achar(j)),j=97,102)]) ! abcdef + table(13,i) = all([(pcfs(i)%pcf(achar(j)),j=103,122)]) ! ghijklmnopqrstuvwxyz + table(14,i) = all([(pcfs(i)%pcf(achar(j)),j=123,126)]) ! {|}~ + table(15,i) = pcfs(i)%pcf(achar(127)) ! backspace character + end do + + ! output table for verification + write(*,'(5X,12(I4))') (i,i=1,12) + do j = 1, 15 + write(*,'(I3,2X,12(L4),2X,I3)') j, (table(j,i),i=1,12), count(table(j,:)) + end do + write(*,'(5X,12(I4))') (count(table(:,i)),i=1,12) + end subroutine + +end program \ No newline at end of file