j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
178 stars 15 forks source link

Extend NAMELIST to support command line parsing per standard syntaxes #131

Open urbanjost opened 4 years ago

urbanjost commented 4 years ago

The desired extension would allow for a NAMELIST to be flagged as an argument list and a new procedure called GET_ARGUMENTS([standard_name]). The expected behavior would look like

     integer, a=1, b=2
     logical :: h=.false., v=.false.
     namelist,arguments /args/a,b,h,v
     call get_arguments()   # read command line as a NAMELIST input line
     call get_arguments('POSIX'|'getopts'|....

at a minimum only the NAMELIST and Sun CLIP standards would need supported, with developers free to support other command line styles.

The NAMELIST option can be simulated except for the ability to make the NAMELIST visible in a different scope (ie that the compiler can see the NAMELIST names).

program testit
implicit none
! declare and initialize a namelist
integer    :: i=1, j=2, k=3
real       :: s=111.1, t=222.2, r=333.3
character(len=255) :: c=' '
namelist /cmd/ i,j,k,s,t,r,c            ! just add a variable here and it is a new parameter !!
integer :: err
   err=get_arguments()  ! return command line arguments as NAMELIST input
   ! all done cracking the command line.  use the values in your program.
   write(*,nml=cmd)
   ! THE FOLLOWING WOULD NOT BE REQUIRED IF THE EXTENSION EXISTED
contains
function get_arguments()
character(len=255)           :: message ! use for I/O error messages
character(len=:),allocatable :: string  ! stores command line argument
integer                      :: get_arguments
integer :: command_line_length
   call get_command(length=command_line_length)   ! get length needed to hold command
   allocate(character(len=command_line_length) :: string)
   call get_command(string)
   ! trim off command name and get command line arguments
   string=adjustl(string)//' '                    ! assuming command verb does not have spaces in it
   string=string(index(string,' '):)
   string="&cmd "//string//" /"                   ! add namelist prefix and terminator
   read(string,nml=cmd,iostat=get_arguments,iomsg=message) ! internal read of namelist
   if(get_arguments.ne.0)then
      write(*,'("ERROR:",i0,1x,a)')get_arguments, trim(message)
      write(*,*)'COMMAND OPTIONS ARE'
      write(*,nml=cmd)
      !!stop 1
   endif
end function get_arguments
end program testit

The program can now be called using syntax like

   /testit  A=10 B=20

The syntax for parsing the argument values would remain the same as for NAMELIST. That is, if reading an array or user-defined type you could use ARR=10,20.3,40 and so on, per NAMELIST rules, no matter what the syntax.

Other than arguing what formats would be standard and how (if at all) case is handled; the advantages would be

Fortran would have a standard command-line argument parsing interface.

Extremely easy to use using existing NAMELIST definition; and requiring no additional code to convert arguments to correct types by user.

Leveraging existing NAMELIST features non-existent options would be detected, variable names could be equivalenced, ...

The https://github.com/urbanjost/M_commandline site is as close as I could get to what I would like to because of the limitation on the scope of the NAMELIST group. It looks at ways long and short names and case could be handled and shows that existing Fortran features can almost be used to provide the feature, indicating this should be a relatively if somewhat system-dependent feature.

Considering the number of requests for a command-line interface I have seen I believe this is very commonly desired feature and using NAMELIST in this way would provide an easy-to-use interface. Far easier than any I have seen, including the C getopts(3c) routine.

In addition, since the arguments are in a NAMELIST format it would be trivial for the user to add config files to set defaults for their applications or to record and/or display the command line selections.

This specifically suggests changing NAMELIST groups, but has a lot in common with #25.

The closest I got to what I wanted that is also useful as a production version is the module M_CLI; but I only have tried it in one programming environment. If anyone tries it, I would appreciate hearing what compilers were used and any errors as I think it could be useful in the interim.

urbanjost commented 4 years ago

PS; There is a more extensive M_args.f90 included in the "General Purpose Fortran" repository that explores other alternatives like using a prototype like call get_arguments('-i 10 --help .false. --version .false. -A "a string"')

I picture implementing it would be like adding the contained routine into a main program at compilation time. A more general solution would be that any procedure that can convert the command line into a table of keywords and values could be passed to the routine, which could convert that to the NAMELIST group values. This would allow users to customize the parser.

certik commented 4 years ago

@urbanjost you can use ``` to format your blocks of code and use other Markdown features to better format your post. See https://guides.github.com/features/mastering-markdown/ for how to do that.