Nice.
Tried it. The results were mostly good. A few issues/questions …
o Does not appear to recognize “escape” characters and backslash. Would be nice, as
that allows easily looking for tab characters in particular.
which would let you try it interactively easily.
QA that produced traceback
program test_suite_M_match
use, intrinsic :: iso_fortran_env, only : ERROR_UNIT
use regex_module
!use M_match, only : getpat, match, regex_pattern
!use M_match, only : YES, ERR !, NO
implicit none
integer,parameter :: HT=9 ! horizontal tab
logical,allocatable :: TALLY(:)
allocate(tally(0))
! mymatch("regexp", "String", expected result )
call mymatch("Foo", "FooBar", .true. )
call mymatch("Poo", "FooBar", .false. )
call mymatch("Bar", "FooBar", .true. )
call mymatch("Par", "FooBar", .false. )
call mymatch("Foo", "Foo", .true. )
call mymatch("Fo", "Foo", .true. )
call mymatch("Foo", "Fo", .false. )
call mymatch("ooB", "FooBar", .true. )
call mymatch("ooP", "FooBar", .false. )
call mymatch(".", "FooBar", .true. )
call mymatch("P.", "FooBar", .false. )
call mymatch("^Foo", "FooBar", .true. )
call mymatch("^Bar", "FooBar", .false. )
call mymatch("Foo$", "FooBar", .false. )
call mymatch("Bar$", "FooBar", .true. )
call mymatch(".*o", "FooBar", .true. )
call mymatch("o*o", "FooBar", .true. )
call mymatch("P*o", "FooBar", .true. )
call mymatch("Fo*o", "FooBar", .true. )
call mymatch("Po*o", "FooBar", .false. )
call mymatch("F[po]o", "FooBar", .true. )
call mymatch("F[op]o", "FooBar", .true. )
call mymatch("F[qp]o", "FooBar", .false. )
call mymatch("F[^po]o", "FooBar", .false. )
call mymatch("F[^op]o", "FooBar", .false. )
call mymatch("F[^qp]o", "FooBar", .true. )
call mymatch("F[po]*o", "FooBar", .true. )
call mymatch("F[56]*o", "F5oBar", .true. )
call mymatch("F[46]*o", "F5oBar", .false. )
call mymatch("F[46]*5", "F5oBar", .true. )
call mymatch("F[46]*5o", "F5oBar", .true. )
call mymatch("F[op]*o", "FooBar", .true. )
call mymatch("F[qp]*o", "FooBar", .true. )
call mymatch("P[qp]*o", "FooBar", .false. )
call mymatch("F[^po]*o", "FooBar", .true. )
call mymatch("F[^op]*o", "FooBar", .true. )
call mymatch("F[^qp]*o", "FooBar", .true. )
call mymatch("P[^qp]*o", "FooBar", .false. )
call mymatch("[0-9][0-9]*$", "0123456789", .true. )
call mymatch("[0-9][0-9]*$", "A0123456789", .true. )
call mymatch("^[0-9][0-9]*$", "A0123456789", .false. )
call mymatch("^[0-9][0-9]*$", "", .false. )
call mymatch("^[0-9]$", "", .false. )
call mymatch("^[0-9]*$", "", .true. )
call mymatch("^$", "", .true. )
call mymatch("^$", " ", .false.)
call mymatch("^[A-Z ][A-Z ]*$", "", .false. )
call mymatch("^[ ]*[A-Z][A-Z ]*$", " THIS IS ALL UPPERCASE", .true. )
call mymatch("^[ ]*[a-z][a-z ]*$", " this is all lowercase", .true. )
call mymatch("^[ ]*[A-Z][A-Z ]*$", " THIS IS not ALL UPPERCASE", .false. )
call mymatch("^[ ]*[a-z][a-z ]*$", " this is NOT all lowercase", .false. )
! check dash in character class at beginning and end instead of in range
call mymatch("X[-+]Y", "X-Y", .true. )
call mymatch("X[-+]Y", "X+Y", .true. )
call mymatch("X[+-]Y", "X-Y", .true. )
call mymatch("X[+-]Y", "X+Y", .true. )
call mymatch("X[-+]Y", "Y-X", .false. )
call mymatch("X[-+]Y", "Y+X", .false. )
call mymatch("X[+-]Y", "Y-X", .false. )
call mymatch("X[+-]Y", "Y+X", .false. )
! tabs
call mymatch("X\tY", "X"//char(HT)//"Y", .true. )
call mymatch("X[\tab]Y", "X"//char(HT)//"Y", .true. )
call mymatch("X[\tab]Y", "XtY", .false. )
call mymatch("X[\tab]Y", "XaY", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "1.9", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "1.99", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "1.999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "1.9999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "1.99999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "11.99999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "111.99999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "1111.99999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "11111.99999", .true. )
call mymatch("[0-9][0-9]*\.[0-9]*", "123456.99999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "1.9", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "1.99", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "1.999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "1.9999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "1.99999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "11.99999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "111.99999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "1111.99999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "11111.99999", .true. )
call mymatch("^[0-9][0-9]*\.[0-9]*", "111111.99999", .true. )
call mymatch("a[0-9][0-9]*\.[0-9]*", "a1.9", .true. )
call mymatch("a[0-9][0-9]*\.", "a1.9", .true. )
call mymatch("a[0-9][0-9]*", "a1.9", .true. )
call mymatch("a", "a1.9", .true. )
call mymatch("\\", "\", .true. )
call mymatch("\.", "\", .false. )
call mymatch(".", "\", .true. )
! this version closes the set -- it really should complain
!!call mymatch("F[qpo", "FooBar", .false.) ! intentional bad regex
call mymatch("F[qpo", "FooBar", .false.) ! intentional bad regex
! write up total results and if anything failed exit with a non-zero status
write(*,'(*(g0))')'TALLY;',tally
if(all(tally))then
write(*,'(*(g0))')'PASSED: all ',count(tally),' tests passed '
else
write(*,*)'FAILED: PASSED=',count(tally),' FAILED=',count(.not.tally)
stop 4
endif
contains
subroutine mymatch(expression,string,expected)
character(len=*),intent(in) :: expression
character(len=*),intent(in) :: string
logical,intent(in) :: expected
integer :: indx, ln
logical :: answer
indx=regex(string,expression)
if(indx .lt. 0) then
write(*,'(*(g0,1x))').not.expected, 'illegal pattern for regex',expression, &
& 'and string',string,'expected',expected,'getpat=',indx
tally=[tally,.false.]
else
answer=merge(.true.,.false.,indx.gt.0)
write(*,'(*(g0,1x))') answer .eqv. expected, 'for regex',expression,'and string',string, &
& 'expected',expected,'got',answer,'getpat=',indx
tally=[tally,answer.eqv.expected]
endif
end subroutine mymatch
end program test_suite_M_match