A very simple and stupid preprocessor for modern Fortran projects.
PreForM.py supports the most used cpp
pre-processing directives and provides advanced features typical of templating systems. Even if PreForM.py is currently Fortran-agnostic (it being usable within any programming languages) it is focused on Fortran programming language.
- Team Members
- Why?
- Main features
- Todos
- Requirements
- Install
- Getting Help
- Copyrights
- Usage
- Examples
- Tips for non pythonic users
- Version History
- Stefano Zaghi, aka szaghi https://github.com/szaghi
- Tomas Bylund, aka Tobychev https://github.com/Tobychev
The Fortran programming language has not its own pre-processor neither it has a standard pre-processing syntax. Consequently, Fortran developers must rely on external pre-processor tools. However, the pre-processors focused on Fortran language are very fews: PreForM.py is just anther Pythonic pre-processor designed with only main target, the Fortran poor-men.
It is designed to be very simple, but flexible enough to constitute a template system for Fortran developers. Moreover, to facilitate the migration from other pre-processors, PreForM.py supports the most used cpp pre-processing directives.
It is worth noting that PreForM.py even if it is designed for Fortran poor-men is presently Fortran-agnostic, as a consequence it can be used within any programming language.
As a matter of fact, many Fortran developers use cpp, the C pre-processor, being one of the most diffused and standardised pre-processor. cpp is a great pre-processor, but it is basically a macro processor, meaning that it is quite focused on macro expansion/substitution/evaluation. cpp has some limitations that makes complex using it as a template system. Let us suppose we want to write a generic interface as the following:
...
interface foo
module procedure foo1
module procedure foo2
module procedure foo3
endinterface
contains
function foo1(in) result(out)
type(first), intent(IN):: in
logical:: out
out = in%logical_test()
endfunction foo1
function foo2(in) result(out)
type(second), intent(IN):: in
logical:: out
out = in%logical_test()
endfunction foo2
function foo3(in) result(out)
type(third), intent(IN):: in
logical:: out
out = in%logical_test()
endfunction foo3
...
Writing a macro in cpp syntax to generalize such a generic interface implementation is quite impossible. On the contrary, using PreForM.py as a template system the implementation becomes very simple and elegant:
...
interface foo
#PFM for i in [1,2,3]:
module procedure foo$i
#PFM endfor
endinterface
contains
#PFM for i in [1,2,3] and t in [first,second,third]:
function foo$i(in) result(out)
type($t), intent(IN):: in
logical:: out
out = in%logical_test()
endfunction foo$i
#PFM endfor
PreForM.py is just a pre-processor for Fortran poor-men supporting the most used cpp directives, but overtaking some of the cpp limitations in order to make PreForM.py similar to a template system.
- Easy-extensible: PreForM.py is just a less-than 500 lines of Python statements... no bad for a poor-cpp-preprocessor improvement;
- well integrated with a powerful yet simple automatic Building System for Fortran poor-men, namely FoBiS.py;
- simple command line interface;
- support the most used
cpp
pre-processing directives:- conditionals:
- operators (also nested):
-
defined MACRO
ordefined(MACRO)
; -
EXPRESSION || EXPRESSION
(logic or); -
EXPRESSION && EXPRESSION
(logic and);
-
-
#if EXPRESSION
; -
#elif EXPRESSION
; -
#ifdef MACRO
; -
#ifndef MACRO
; -
#else
; -
#endif
;
- operators (also nested):
- macros:
- standard predefined macros:
-
__FILE__
; -
__LINE__
; -
__DATE__
; -
__TIME__
;
-
- expansion;
- stringification;
- concatenation;
- variadic macros;
- object-like macros:
-
#define MACRO [VALUE]
, VALUE is optional;
-
- function-like macros:
-
#define FUNCTION FUNCTION_DEFINITION
;
-
-
#undef
;
- standard predefined macros:
-
#include
;
- conditionals:
- pythonic Template System:
-
#PFM for EXPRESSION
-#PFM endfor
pairs loop control (only for one iteration counter at time;
-
- ...
Note that in general the cpp
pre-processing directives should start at the first column, the symbol #
being the first one. PreForM.py relaxes this rule allowing any number of leading white spaces before #
.
- Pythonic Template System;
-
#PFM for EXPRESSION
-#PFM endfor
pairs loop control for complex EXPRESSION;
-
- any feature request is welcome.
- Python 2.7+ or Python 3.x;
- required modules:
- sys;
- os;
- argparse;
- configparser;
- re;
- optional modules:
- datetime;
- multiprocessing;
- required modules:
- a lot of patience with the author.
PreForM.py is developed on a GNU/Linux architecture. For Windows architecture there is no support, however it should work out-of-the-box.
PreForM.py is a one-file-script, consequently it does not need a real installation: simply download the script and placed into your PATH. See the requirements section.
However, note that the script placed into the root of PreForM.py project is just a wrapper to the real script. As a matter of fact, the tree structure of the PreForM.py project is the following:
├── CONTRIBUTING.md
├── examples
│ ├── cpp-directives
│ │ ├── cpp-directives.F90
│ │ ├── foo.inc
│ │ └── README.md
│ └── template-system
│ ├── README.md
│ └── simple-for-loop.f90
├── LICENSE.gpl3.md
├── PreForM
│ ├── __init__.py
│ ├── __main__.py
│ └── PreForM.py
├── PreForM.py
├── README.md
└── setup.py
Therefore, the actual script that you need to download is PreForM/PreForM.py
. This cumbersome files tree is necessary to create a valid PyPI egg
, see PyPI install procedure.
It can be convenient to clone the project:
git clone https://github.com/szaghi/PreForM
and then make a link to the script where your environment can find it.
PreForM.py can be installed by means of pip
, the python installer that search into the PyPI (Python Package Index) for packages and automatically install them. Just type:
pip install PreForM.py
Note that you need root permissions if you are not using your virtualenv or you are trying to install PreForM.py into your system space.
It is worth noting that the pip
installation will create a command line tool named PreForM
and not PreForM.py
: take this into account when using PreForM.py.
It is also worth noting that the pip
installation will allow you to directly import PreForM.py code into your Python application by means of module import, e.g.
from PreForM.PreForM import preprocess_file
will import the preprocess_file
function into your python application.
You are reading the main documentation of PreForM.py that should be comprehensive. For more help contact directly the author.
PreForM.py is an open source project, it is distributed under the GPL v3 license. A copy of the license should be distributed within PreForM.py. Anyone interested to use, develop or to contribute to PreForM.py is welcome. Take a look at the contributing guidelines for starting to contribute to the project.
Printing the main help message:
PreForM.py -h
This will echo:
usage: PreForM.py [-h] [-v] [-o OUTPUT] [-D D [D ...]] [-lm] input
PreForM.py, Preprocessor for Fortran poor Men
positional arguments:
input Input file name of source to be preprocessed
optional arguments:
-h, --help show this help message and exit
-v, --version Show version
-o OUTPUT, --output OUTPUT
Output file name of preprocessed source
-D D [D ...] Define a list of macros in the form NAME1=VALUE1
NAME2=VALUE2...
-lm, --list-macros Print the list of macros state as the last parsed line
left it
PreForM.py my_file_to_be_preprocessed.my_extension
This will print to stdout the pre-processed file.
PreForM.py my_file_to_be_preprocessed.my_extension -o my_result_file
This will save into my_result_file
the pre-processed file.
It is possible to define macros on-the-fly using the CLI switch -D
. As an example
PreForM.py my_source.f90 -D first=1 second=sec third=.true.
pre-process the source file my_source.f90
defining on-the-fly 3 macros, first,second,third
, having the values 1,'sec',.true.
, respectively. The syntax is macro_name=macro_value
. In case you want just define a macro name (without take into account for its value) you must always insert the symbol =
, e.g.
PreForM.py my_source.f90 -D first= second=2
This defines 2 macros, first,second
, but second
only has a true value (2
), whereas first
is only defined, but it has not a value.
Into the directory examples there are some KISS examples, just read their provided REAMDE.md.
In the examples above PreForM.py is supposed to have the executable permissions, thus it is used without an explicit invocation of the Python interpreter. In general, if PreForM.py is not set to have executable permissions, it must be executed as:
python PreForM.py ...
In the following the changelog of most important releases is reported.
Partial Code Lint. Fully backward compatible.
Add PyPI installation procedure. Strongly modify the project tree. Fully backward compatible.
First STABLE release. Implement #PFM for EXPRESSION
-#PFM endfor
pairs loop control for only simple expressions (i.e. expression having only one iteration counter). Fully backward compatible.
CPP
Support almost complete. The most used cpp
pre-processing directives are now supported. Fully backward compatible.
Implement function-like macros substitution. Fully backward compatible.
Quasi stable API, with many cpp directives supported. Fully backward compatible.
Very first, totally UNSTABLE release. Implement only few cpp directives.