j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
175 stars 14 forks source link

Allow more readable integer assignment in the namelist input #257

Closed septcolor closed 2 years ago

septcolor commented 2 years ago

I have been using namelist input files extensively in my codes. While they are very useful, one weak point is that integer assignment allows only this kind of "raw" notation

...
par%nsteps = 1000000
par%nsteps2 = 100000
par%nsteps3 = 10000000
...

(here, par is some parameter object). Very often, the number of steps like above is rather large, which is often not very readable. I tried using the usual notation like

...
par%nsteps = 10**6
par%nsteps2 = 10**5
par%nsteps3 = 10**7
...

but it interferes with the special meaning of * in the namelist and results in a runtime error. So I wish some other syntax would be available, e.g.,

...
par%nsteps = 1_000_000
par%nsteps2 = 100_000
par%nsteps3 = 10_000_000
...

or

...
par%nsteps = 10^6
par%nsteps2 = 10^5
par%nsteps3 = 10^7
...

If the symbols like * or ^ are not acceptable, some kind of "math" string (e.g. m"...") may be useful for internal conversion (from math strings to integer in this case).

...
par%nsteps = m" 10**6 "
par%nsteps2 = m" 10**5 "
par%nsteps3 = m" 10**7 "
...

Apart from the specific syntax, I hope I can also write nsteps = 2 * 10^7 rather than nsteps = 20000000, because I need to count the number of zeros every time with mouse cursor.

(Edit) Because the math evaluation like sqrt() seems like too far as a request, I deleted it from the original post. My intention is not a "general" expression evaluator, but just the above kind of large integers (at the moment).

FortranFan commented 2 years ago

@septcolor , Toward your first item, how about using the scientific format instead which is already supported?

   real :: x
   namelist / dat / x
   character(len=:), allocatable :: s
   s = "&dat x=1e6 /"
   read( s, nml=dat )
   print *, "x = ", x
end

C:\temp>gfortran p.f90 -o p.exe

C:\temp>p.exe x = 1000000.00

As to your second suggestion, it appears a bridge too far. As you pointed out, you already have the problem with the repeat count (*) that makes the multiplication operation complicated. And that's just the beginning of complexity.

You might consider an expression evaluator library instead that you integrate with your codes and which then parse string "data" to evaluate the expressions.

septcolor commented 2 years ago

Hi @FortranFan ,

(Edit: I have just updated the original post and title, such that it focuses only on large integers (which is one of my "pain points" for namelist input.)

Thanks much for your suggestion. I've just tried your snippet, but noticed that x is real (and yes, in this case the scientific notation works!). However, if the namelist variable is integer, it results in a runtime error (at least for Gfortran-10), as follows...

   integer :: x
   namelist / dat / x
   character(len=:), allocatable :: s
   s = "&dat x=1e6 /"
   read( s, nml=dat )
   print *, "x = ", x
end

$ gfortran-10 test.f90 && ./a.out

Fortran runtime error: Cannot match namelist object name e6

And I agree that the namelist input has a lot of "weird" rules (maybe for historical reasons), so I wonder if there could be some way to entirely work around those limitations, while keeping the convenience of namelist input.

RE expression evaluators, that would be one approach if I really want math expressions in the input files. But one of the most appealing points of namelist (to me) is that it can fill values directly in the specified components of a given derived type. I usually set up "parameter type" objects and read all parameters, e.g., as

subroutine X_readpar( par )   !! where X is some tag/name/etc
type(Xpar_t) :: par
namelist / Xinp / par
...
read( file, nml = Xinp )
...

and the caller passes par as a component of a parent type

call  X_readpar( xobj % par )     !! where obj is an instance of type X, for example

Probably, this kind of "direct filling" of type components cannot be done via 3rd-party expression evaluators. Another approach is to use external input libraries like TOML, which may read in values into some dictionary object inside Fortran. However, in this case I have to copy necessary type components again from the dictionary to the derived-type object manually, which is just too tedious (to me) if the number of parameters is large... (One method may be to use such a dictionary directly inside the program, but I guess the code becomes more verbose everywhere, probably, as compared to just using the type components.)