Code Monkey home page Code Monkey logo

fypp's Introduction

Fypp — Python powered Fortran metaprogramming

image

Fypp is a Python powered preprocessor. It can be used for any programming languages but its primary aim is to offer a Fortran preprocessor, which helps to extend Fortran with condititional compiling and template metaprogramming capabilities. Instead of introducing its own expression syntax, it uses Python expressions in its preprocessor directives, offering the consistency and versatility of Python when formulating metaprogramming tasks. It puts strong emphasis on robustness and on neat integration into developing toolchains.

The project is hosted on github.

Detailed DOCUMENTATION is available on readthedocs.org.

Fypp is released under the BSD 2-clause license.

Main features

  • Definition, evaluation and removal of variables:

    #:if DEBUG > 0
      print *, "Some debug information"
    #:endif
    
    #:set LOGLEVEL = 2
    print *, "LOGLEVEL: ${LOGLEVEL}$"
    
    #:del LOGLEVEL
  • Macro definitions and macro calls:

    #:def ASSERT(cond)
      #:if DEBUG > 0
        if (.not. ${cond}$) then
          print *, "Assert failed in file ${_FILE_}$, line ${_LINE_}$"
          error stop
        end if
      #:endif
    #:enddef ASSERT
    
    ! Invoked via direct call (argument needs no quotation)
    @:ASSERT(size(myArray) > 0)
    
    ! Invoked as Python expression (argument needs quotation)
    $:ASSERT('size(myArray) > 0')
  • Conditional output:

    program test
    #:if defined('WITH_MPI')
      use mpi
    #:elif defined('WITH_OPENMP')
      use openmp
    #:else
      use serial
    #:endif
  • Iterated output (e.g. for generating Fortran templates):

    interface myfunc
    #:for dtype in ['real', 'dreal', 'complex', 'dcomplex']
      module procedure myfunc_${dtype}$
    #:endfor
    end interface myfunc
  • Inline directives:

    logical, parameter :: hasMpi = #{if defined('MPI')}# .true. #{else}# .false. #{endif}#
  • Insertion of arbitrary Python expressions:

    character(*), parameter :: comp_date = "${time.strftime('%Y-%m-%d')}$"
  • Inclusion of files during preprocessing:

    #:include "macrodefs.fypp"
  • Using Fortran-style continutation lines in preprocessor directives:

    #:if var1 > var2 &
        & or var2 > var4
      print *, "Doing something here"
    #:endif
  • Passing (unquoted) multiline string arguments to callables:

    #! Callable needs only string argument
    #:def DEBUG_CODE(code)
      #:if DEBUG > 0
        $:code
      #:endif
    #:enddef DEBUG_CODE
    
    #! Pass code block as first positional argument
    #:block DEBUG_CODE
      if (size(array) > 100) then
        print *, "DEBUG: spuriously large array"
      end if
    #:endblock DEBUG_CODE
    
    #! Callable needs also non-string argument types
    #:def REPEAT_CODE(code, repeat)
      #:for ind in range(repeat)
        $:code
      #:endfor
    #:enddef REPEAT_CODE
    
    #! Pass code block as positional argument and 3 as keyword argument "repeat"
    #:block REPEAT_CODE(repeat=3)
    this will be repeated 3 times
    #:endblock REPEAT_CODE
  • Preprocessor comments:

    #! This will not show up in the output
    #! Also the newline characters at the end of the lines will be suppressed
  • Suppressing the preprocessor output in selected regions:

    #! Definitions are read, but no output (e.g. newlines) will be produced
    #:mute
    #:include "macrodefs.fypp"
    #:endmute
  • Explicit request for stopping the preprocessor:

    #:if DEBUGLEVEL < 0
      #:stop 'Negative debug level not allowed!'
    #:endif
  • Easy check for macro parameter sanity:

    #:def mymacro(RANK)
      #! Macro only works for RANK 1 and above
      #:assert RANK > 0
      :
    #:enddef mymacro
  • Line numbering directives in output:

    program test
    #:if defined('MPI')
    use mpi
    #:endif
    :

    transformed to :

    # 1 "test.fypp" 1
    program test
    # 3 "test.fypp"
    use mpi
    # 5 "test.fypp"
    :

    when variable MPI is defined and Fypp was instructed to generate line markers.

  • Automatic folding of generated lines exceeding line length limit

Installing

Fypp needs a working Python 3 interpreter (Python 3.5 or above).

When you install Fypp, you obtain the command line tool fypp and the Python module fypp.py. Latter you can import if you want to access the functionality of Fypp directly from within your Python scripts.

Installing via conda

The last stable release of Fypp can be easily installed as conda package by issuing :

conda install -c conda-forge fypp

Installing via pip

You can also use Pythons command line installer pip in order to download the stable release from the Fypp page on PyPI and install it on your system.

If you want to install Fypp into the module system of the active Python 3 interpreter (typically the case when you are using a Python virtual environment), issue :

pip3 install fypp

Alternatively, you can install Fypp into the user space (under ~/.local) with :

pip3 install --user fypp

Installing via MSYS2 pacman

On Windows you can use the MSYS2 toolchain to install Fypp in a MinGW terminal. To install Fypp use:

pacman -S mingw-w64-x86_64-python-fypp

Make sure the selected architecture is matching your current MinGW terminal. For all supporting MinGW architectures visit check the package index here.

Manual install

For a manual install, you can download the source code of the stable releases from the Fypp project website.

If you wish to obtain the latest development version, clone the projects repository:

git clone https://github.com/aradi/fypp.git

and check out the master branch.

The command line tool is a single stand-alone script. You can run it directly from the source folder :

FYPP_SOURCE_FOLDER/bin/fypp

or after copying it from the bin folder to any location listed in your PATH environment variable, by just issuing :

fypp

The python module fypp.py can be found in FYP_SOURCE_FOLDER/src.

Running

The Fypp command line tool reads a file, preprocesses it and writes it to another file, so you would typically invoke it like:

fypp source.fpp source.f90

which would process source.fpp and write the result to source.f90. If input and output files are not specified, information is read from stdin and written to stdout.

The behavior of Fypp can be influenced with various command line options. A summary of all command line options can be obtained by:

fypp -h

fypp's People

Contributors

aradi avatar awvwgk avatar baradi09 avatar dependabot[bot] avatar dev-zero avatar e-kwsm avatar haraldkl avatar oschuett avatar rhdtownsend avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fypp's Issues

Feature: Print result directly to an output file in the *same directory* as the input file

Hello,

I'm trying to use Fypp with CMake build system, and faced an issue.

Fypp currently can be used as $ fypp src/Core/test.fpp src/Core/test.F90 which is nice, and simple.

But in a build system like CMake, I'm finding it difficult to extract the directory path to where the output file will be written.

This is mainly because I'm not very experienced in CMake's own weird language syntax.

But I'm thinking, shouldn't it be possible for fypp to just output the result in the same directory?

Adding such a feature will probably not be too complicated, and it will be helpful to everyone.

In best case scenario, it would be as easy to use as $fypp --output-in-source-directory --extension F90 src/Core/test.fpp

Where --extension F90 would be the user specified extension used to write the output file src/Core/test.F90

And --output-in-source-directory is a long form example of a command we can give to instruct fypp to write in the same directory as the source input file.

The commands are little bit verbose to show what I mean, and for better user experience, it might be better to shorten and simply them, as per your own wish.

Thanks.

Dependency generation for fypp

Is there a straightfoward way for fypp to generate dependency information? That is, process a file and print out a list of other files that get in included by #:include directives.

It looks like this can be done by subclassing Parser and overriding handle_include(), but how do I then get the Fypp class to use my Parser subclass rather than the default class?

Thanks!

Rich

Logical test behaves different in a def'ed macro compares to outside of macro

Hi,

Thanks for a great tool! I have met a strange behaviour of a defined macro in a fypp file. If you set correct_behaviour = True in the snippet below, it evaluates a logical flag and writes "elemental" or "impure elemental" depending on the logical test outside of a macro, and writes (correctly) "elemental". If you set correct_behaviour = False it evaluates the same logical test, but this time inside of a macro. The result differs!

Is this a bug or something I am missing?

#:def mac(is_pure, test_coverage)
    #:set pure_flag = is_pure and not test_coverage
    I am in the macro
    ${is_pure}$
    ${test_coverage}$
    ${pure_flag}$
    #:if pure_flag
        #:set elemental_purity = "elemental"
    #:else
        #:set elemental_purity = "impure elemental"
    #:endif
    ${elemental_purity}$

#:enddef


#:set correct_behaviour = True

#:set is_pure = True
#:set test_coverage = False

#:if correct_behaviour

    #:set pure_flag = is_pure and not test_coverage
    ${is_pure}$
    ${test_coverage}$
    ${pure_flag}$
    #:if pure_flag
        #:set elemental_purity = "elemental"
    #:else
        #:set elemental_purity = "impure elemental"
    #:endif
    ${elemental_purity}$

#:else

    @:mac(${is_pure}$, ${test_coverage}$)

#:endif

Best regards,
Øyvind

Accessing Python dictionaries within fypp

Is it possible to assign and retrieve Python dictionary key-value pairs from within fypp without writing a dedicated Python class?
If yes, what is the correct syntax to do so?

Append method for lists

I was wondering how to use the .append() method of lists instead of the summation operator. The .pop() method works fine.

Minimal working example:

#:set a = [0]
#:set b = [1,2,3]
$:set a = a + [1]
#:set a = a + [2]
#:for i, ra in enumerate(a)
    print *, ${ra}$,${b.pop()}$
#:endfor
end

The output from fypp is:

    print *, 0,3
    print *, 1,2
    print *, 2,1

Horner's algorithm with fypp

Hello,

I am trying to create a macro to efficiently inline polynomial expressions. This is related to issue fortran-lang/stdlib#179. The authors of Julia reckon, that meta-programming is one of the reasons they can beat Fortran:

The reason Julia can beat the Fortran code is that metaprogramming makes it easy to apply performance optimizations that are awkward in Fortran. We have metaprogramming macros (@evalpoly) that can easily inline polynomial evaluations, whereas the Fortran code makes function calls that loop over look-up tables of polynomial coefficients. Even greater speedups are possible for evaluating polynomials of complex arguments, where there is a fancy recurrence from Knuth that is almost impossible to use effectively without code generation. In principle, the Fortran authors could have done the same inlining and gotten similar performance, but the code would have been much more painful to write by hand. (They could even have written a program to generate Fortran code, but that is even more painful.)

In principle, this is exactly what we have fypp for, and it should not be that painful at all.

The canonical way to implement Horner's algorithm in Fortran that loops over a table of coefficients is:

function evalpoly(x,p) result(res)
    real, intent(in) :: x
    real, intent(in) :: p(:)
    real :: res
    integer :: i, n

    n = size(p)
    res = p(n)
    do i = n-1,1,-1
        res = p(i) + res*x
    end do 
end function

We can write a very similar function to generate the symbolic expression in Python:

def horner(x,coeffs):
    n = len(coeffs)
    cfs = [str(coeff) for coeff in coeffs]
    res = cfs[n-1]
    for i in range(n-2,-1,-1):
        res = "({} + {}*{})".format(cfs[i],x,res)
    return res

Calling this function as horner('x',(1,2,3,4) will return the string "(1 + x*(2 + x*(3 + x*4)))". Assuming the function is in the file "horner.py" I can import this function using fypp -m horner <source.fypp> <output.f90>. To call the function I have to use now

program main
implicit none
real :: x, res
real :: p(4) = [1,2,3,4]
real, external :: evalpoly

x = 0.5

#:set coeffs = (1,2,3,4) 
res = ${horner.horner('x',coeffs)}$

print *, res, evalpoly(x,p)
end program

Is it possible to have Horner's algorithm live exclusively inside of a ".fypp" file? I have the feeling that with some clever preprocessing constructs, this should be possible (and preferable to the module import syntax).

Thanks for creating this tool!

`pip install fypp` breaks on Python 2.7, using `pip3 install fypp` works.

After pip install fypp, I try out fypp and I get some errors below (using python 2.7). No errors and everything works if I use pip3 instead.

The docs at https://fypp.readthedocs.io/en/stable/fypp.html#getting-started says:

The behavior of Fypp can be influenced with various command line options. A summary of all command line options can be obtained by:

fypp -h

When I try it I get an error from this section:

def _process_direct_call(self, callexpr, span):
        match = _DIRECT_CALL_REGEXP.match(callexpr)
        if not match:
            msg = "invalid direct call expression"
            raise FyppFatalError(msg, self._curfile, span)
        callname = match.group('callname')
        self.handle_call(span, callname, None, False)
        callparams = match.group('callparams')
        if callparams is None or not callparams.strip():
            args = []
        else:
            try:
                args = [arg.strip() for arg in _argsplit_fortran(callparams)]
            except Exception as exc:
                msg = 'unable to parse direct call argument'
                raise FyppFatalError(msg, self._curfile, span) from exc`

block/call macros: Is there a way to define a fypp custom macro in a procedural manner?

Is more of a question: I don't know if i'm doing this right, but I want to define a fypp macro pass as a argument inside the block/call macro as a argument and then call it there.
context: I want to create a modern interface to BLAS and LAPACK (here it is the repo) in the same fashion as blas95 and lapack95 from MKL and netlib's lapack95, using fypp macros.

Is there a way to do that or a better way to achieve the same results?
I made a sample code here

PS: I've tried using Python's replace but ain't a good solution

Linemarkers with Intel oneAPI compilers

I'm trying to debug a segfault for a new library in DFTB+ using Intel oneAPI. To get a meaningful traceback I would like to emit line markers with fypp but ifort likes non of the available options.

Using the cpp format the line markers are ignored due to bad format

ninja: Entering directory `_build'
[465/748] Building Fortran object prog/dftb+/CMakeFiles/dftbplus.dir/lib_common/status.f90.o
prog/dftb+/lib_common//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/include/common.fypp(1): warning #5117: Bad # preprocessor line
# 9 "prog/dftb+/lib_common//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_common/status.F90" 2 
-----------------------------------------------------------------------------------------------------^
[468/748] Building Fortran object prog/dftb+/CMakeFiles/dftbplus.dir/lib_common/globalenv.f90.o
prog/dftb+/lib_common//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/include/common.fypp(1): warning #5117: Bad # preprocessor line
# 9 "prog/dftb+/lib_common//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_common/globalenv.F90" 2 
--------------------------------------------------------------------------------------------------------^
[473/748] Building Fortran object prog/dftb+/CMakeFiles/dftbplus.dir/lib_common/assert.f90.o
prog/dftb+/lib_common//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/include/common.fypp(1): warning #5117: Bad # preprocessor line
# 9 "prog/dftb+/lib_common//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_common/assert.F90" 2 
-----------------------------------------------------------------------------------------------------^

While I get the an error with std formatted line markers

[502/764] Building Fortran object prog/dftb+/CMakeFiles/dftbplus.dir/lib_io/indexselection.f90.o
FAILED: prog/dftb+/CMakeFiles/dftbplus.dir/lib_io/indexselection.f90.o prog/dftb+/include/dftbp_io_indexselection.mod 
/opt/intel/oneapi/compiler/2021.1.1/linux/bin/intel64/ifort  -Iprog/dftb+/lib_io -Iprog/dftb+/include -Iexternal/xmlf90/include -Iexternal/ddcosmo/include -I../prog/dftb+/../../external/dftd4refs -Iexternal/tblite/origin/include -I_deps/mctc-lib-build/include -I_deps/multicharge-build/include -I_deps/dftd4-build/include -I_deps/s-dftd3-build/include -traceback  -g -O2 -ip -module prog/dftb+/include -qopenmp -c prog/dftb+/CMakeFiles/dftbplus.dir/lib_io/indexselection.f90-pp.f90 -o prog/dftb+/CMakeFiles/dftbplus.dir/lib_io/indexselection.f90.o
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(229): error #5120: Unterminated character constant
#line 228 "/home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90"
--------------------------------------------------------------------------------------^
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(229): error #5144: Invalid character_kind_parameter. No underscore
#line 228 "/home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90"
---------------------------------------------------------------------------------------^
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(229): error #5082: Syntax error, found '/' when expecting one of: ( <IDENTIFIER> <CHAR_CON_KIND_PARAM> <CHAR_NAM_KIND_PARAM> <CHARACTER_CONSTANT> <INTEGER_CONSTANT> ...
#line 228 "/home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90"
------------------------------------------------------------^
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(229): error #5082: Syntax error, found CHARACTER_CONSTANT '' when expecting one of: :: ) ( , : * <END-OF-STATEMENT> ; . (/ + - [ ] /) . ' ** / // ...
#line 228 "/home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90"
--------------------------------------------------------------------------------------^
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(230): error #5082: Syntax error, found '&' when expecting one of: <LABEL> <END-OF-STATEMENT> ; <IDENTIFIER> TYPE MODULE ELEMENTAL IMPURE NON_RECURSIVE ...
      &F90", 228)
-------^
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(230): error #5120: Unterminated character constant
      &F90", 228)
----------^
prog/dftb+/lib_io//home/awvwgk/projects/src/git/dftbplus/prog/dftb+/lib_io/indexselection.F90(230): error #5144: Invalid character_kind_parameter. No underscore
      &F90", 228)
-----------------^

Not sure what is going on here, but it seems to that fypp adding linebreaks in the line markers?

Multiple tuple returns for #:for

Great package ;)

Code explains all:

! This works great
#:set real_kinds = [('32', 'real32'), ('64', 'real64'), ('128', 'real128')]
#:for rname, rkind in real_kinds
...
#:endfor

! This does not work
#:for (lname, lkind), (hname, hkind) in zip(real_kinds[:-1], real_kinds[1:])
...
#:endfor

! This does not work
#:set low_high = list(zip(real_kinds[:-1], real_kinds[1:]))
#:for (lname, lkind), (hname, hkind) in low_high
...
#:endfor

! This WORKS:
#:set low_high = list(map(lambda a, b: a + b, real_kinds[:-1], real_kinds[1:]))
#:for lname, lkind, hname, hkind in low_high
...
#:endfor

It has to do with the parenthesis is some way.

Kernel launcher

A colleague of mine has used the variadic templates of C++ to mimic a kernel launcher:

template<typename F, typename... Ts>
void launch2D(const dim3 & numBlocks, const dim3 & blockDim, F & f, Ts&&... ts)
{
	for (int bx=0;bx<numBlocks.x;++bx)
	for (int by=0;by<numBlocks.y;++by)
	{
		#pragma omp parallel num_threads(blockDim.x*blockDim.y)
		{
			const int tn = omp_get_thread_num();
			const int tx = tn % blockDim.y;
			const int ty = tn / blockDim.y;
			f(numBlocks, blockDim, {bx,by}, {tx,ty}, ts...);
		}
	}
}

// ...

	const dim3 threadsperBlock {BlockSize,BlockSize};
	const dim3 numBlocks{N/threadsperBlock.x,N/threadsperBlock.y};
	launch2D(numBlocks, threadsperBlock, matrix_multiplication_kernel<BlockSize>, a.data(), b.data(), c.data(), N);

This is kind of like the CUDA triple chevron

launch2d<<<numBlocks,threadsperBlock>>>(matrix_multiplication_kernel<BlockSize>, a.data(), b.data(), c.data(), N)

I suppose it's possible to do something similar with Fypp, Fortran and OpenMP/OpenACC/CUDA. I came up with the following solution, but it lacks encapsulation:

#:def LAUNCH1D(kernel, n)
block
integer :: i
    !$omp parallel for simd
    do i = 1, ${n}$
        $:kernel
    end do
    !$omp end parallel for simd
end block
#:enddef

#:call LAUNCH1D
y(i) = a*x(i) + y(i)
#:nextarg
n
#:endcall

Fedora packaging

It would be nice to add Fedora packaging automation via packit. This would:

  • push PRs to Fedora downstream whenever a new release is published
  • test packaging and isolated pytest/ctests on each push to main or PR commits
  • enable more native tests using testing-farm, e.g. using cmakes find_package as if the Fedora package was installed natively

Variadic templates with fypp

I attended a C++ course recently, where I learned about variadic templates. Here's an example of using C++17 to compute the maximum value:

template< typename T1, typename T2, typename... Ts >
constexpr auto max( T1 const& a, T2 const& b, Ts const&... args )
{
   const auto result = ( a < b ) ? b : a;

   if constexpr( sizeof...(Ts) > 0 ) {
      return max( result, args... );
   }
   else {
      return result;
   }
}

Today, I finally had time to try this with fypp:

#:def base_max(a,b)
max(${a}$,${b}$)
#:enddef

#:def varmax(a,b,*pos)
  #:set res = base_max(a,b)
  #:if len(pos) > 0
      #:set res = varmax(res,*pos)
      $:res
  #:else
      $:res
  #:endif
#:enddef

The input

@:varmax(a,b)
@:varmax(a,b,c)
@:varmax(a,b,c,d)

gets expanded as

max(a,b)
max(max(a,b),c)
max(max(max(a,b),c),d)

I was really impressed when this just worked straight out the box! Now of course the Fortran max intrinsic is already variadic and can be used directly as max(a,b,c,d), so this example makes only little sense.

The website fypp.readthedocs.io doesn't appear to contain the keywords recursive or variadic anywhere, so I thought I'd open an issue.

C/C++ compatibility?

I wonder whether fypp can be used for C/C++ if it is invoked with --no-folding. The reason I'm asking this is not because I think it's a good idea to use fypp in C/C++ projects, but it may come in handy to write C bindings to Fortran code that is written using Fypp macros.

parsing cmake variables values of bool

Using fypp to parse cmake options are really great, but I generally have a problem parsing WITH_* arguments.

I would rather not do:

#:if defined("WITH_OPT")
.. option ON
#:endif

but rather something like:

#:if bool("WITH_OPT")
... option ON
#:endif

I can of course define a function that parses this, but I think this could be very generally useful? The truth/false values could optionally be set by some global FYPP_BOOL_TRUE/FALSE variables?

expanding macro in include directive

I wish to expand a macro in an include directive (to determine the file name). I have something like

#:set FILENAMES = ['file', 'anotherfile']
#:for FILE in FILENAMES
    ! some code
    ! now include a file
    #:include "${FILE}$.fypp"
    ! more code
#:endfor

This does not work, and fypp returns "include file '${FILE}$.fypp' not found". Is it possible to accomplish this? Thanks.

Enhancement request : detect repeated #:def with the same signature

If there is an inadvertent copy-paste mistake in the code to be pre-processed, it would be useful to get a warning/error for repeated definitions of the same macro, either within the same file or the same scope.

A follow up, would be a new keyword to redefine an existing macro (again with warning/error., if the macro has not been previously defined).

Using fypp through the Intel Fortran compilers

The Intel compilers provide a flag -fpp-name=<name> which can be used to specify an alternative preprocessor of the form:

alt_fpp [ [–D<define>]..] [[-I<include directory>]..] inputfile 

which prints output to STDOUT that will get captured by the compiler for further processing.

I've tried to use this with fypp, but I run into errors:

$ ifx -fpp-name=`which fypp` -DNAME=\"Ivan\" fypp_example.F90 
error: Failed to open file '@/tmp/ifxargBf8BjW' for read [FyppFatalError]
error: [Errno 2] No such file or directory: '@/tmp/ifxargBf8BjW' [FileNotFoundError]
$ ifx -fpp-name=fypp -Qlocation,fpp,/home/ivan/.local/bin -DNAME=\"Ivan\" fypp_example.F90 
error: Failed to open file '@/tmp/ifxarg6DL4Me' for read [FyppFatalError]
error: [Errno 2] No such file or directory: '@/tmp/ifxarg6DL4Me' [FileNotFoundError]

It seems like the compiler prepends @ to the path, but otherwise it seems it could work. If you'd like to experiment, here is the toy code:

! fypp_example.F90
#:def hello(name)
print *, "Hello ${name}$"
#:enddef

#:if defined('NAME')
$:hello(NAME)
#:else
print *, "Hello World"
#:endif
end

Do you have any previous experience with such usage, or should I raise an issue at the Intel Fortran forum?

Constructing string of list with proper ending

Hi,
I am trying to use the fypp to generate following code:

.. code-block:: bash

  procedure, pass :: get_real_vector_copy
  procedure, pass :: get_real_vector_ref
  procedure, pass :: get_integer_vector_copy
  procedure, pass :: get_integer_vector_ref

  generic         :: get  => &
                     get_real_vector_copy, &
                     get_real_vector_ref, &
                     get_integer_vector_copy, &
                     get_integer_vector_ref  , &

Here is my code with fypp directives:

 #:set TYPE_KINDS = [ ( 'real', 'RP'),               ( 'integer', 'IP')]
 #:set VERSIONS   = [ ( 'copy', 'allocatable', '='), ( 'ref', 'pointer', '=>')]

 #:for TYPE, KIND in TYPE_KINDS
 #:for VER, _, _ in VERSIONS
     procedure, pass :: get_${TYPE}$_vector_${VER}$
 #:endfor
 #:endfor

      generic         :: get  => &
  #:for TYPE, KIND in TYPE_KINDS
  #:for VER, _, _ in VERSIONS
                     get_${TYPE}$_vector_${VER}$, &
  #:endfor
  #:endfor

my issue is how to remove the last , & in generic statement.

Thanks,

fyyp 1.0 to 2.0.1

Hi Balint, I'm trying to migrate to the version 2.0.1 of fypp, but I'm having troubles in adapting the following expression, which was working with fypp 1.0, to the new version of fypp

#:for dim, shape, var in shapes
  #:set dims = ''.join([ 'dims(' + str(i+1) + '),' for i in range(int(dim)) ])[0:-1]
#:endfor

any suggestion?

Parsing the code -- choice

Hi Balint,

As for the implementation of fypp I see the parsing is very much done manually (reg-exp etc.).

Did you consider alternatives to this way, i.e. using ast?

I would think that changing the parsing of the "python" part to ast would leverage much complexity since you anyways want to adhere to python linguistics, no?

Macro changes

I have a code:

#:if defined("SINGLE_PRECISION")
#:def MACRO(A) 
   call bcast_doubles(dminfo,size($A$),$A$)
#:enddef MACRO
#:else
#:def MACRO(A) 
   call bcast_reals(dminfo,size($A$),$A$)
#:enddef MACRO
#:endif

module init

contains

subroutine tst()

   real :: ah2onw(2)
   real :: dminfo

   ah2onw =2.0
   dminfo = 1.0

   MACRO(ah2onw) !!! <<<------ in this line !!!!

end subroutine tst

subroutine bcast_reals(dminfo,n,a)
   real, INTENT(IN) :: dminfo,a(:)
   integer, intent(in) :: n

   print *,'real'

end subroutine bcast_reals

subroutine bcast_doubles(dminfo,n,a)
   real, INTENT(IN) :: dminfo,a(:)
   integer, intent(in) :: n

   PRINT *,'doubles'

end subroutine bcast_doubles

end module init

And my goal is that the FYPP makes the change of "DM_BCAST_MACRO(ah2onw)" by the macro defined at beginning of code. How can I make it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.