├── .codeclimate.yml ├── .github ├── dependabot.yml └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .pre-commit-hooks.yaml ├── .travis.yml ├── .travis ├── prep_cp2k.sh ├── prep_cron.sh ├── prep_flap.sh ├── prep_regular.sh ├── prep_rosetta.sh └── prep_wannier90.sh ├── LICENSE ├── MANIFEST.in ├── README.md ├── environment.yml ├── examples ├── example_after.f90 └── example_before.f90 ├── fortran_tests ├── after │ ├── example.f90 │ ├── example_swapcase.f90 │ ├── example_swapcase.f90-enabled │ ├── test_fypp.f90 │ ├── test_invalid.f90 │ ├── test_namelist_block_select.f90 │ └── where_forall.f90 ├── before │ ├── example.f90 │ ├── example_swapcase.f90 │ ├── test_fypp.f90 │ ├── test_invalid.f90 │ ├── test_namelist_block_select.f90 │ └── where_forall.f90 └── test_results │ └── expected_results ├── fprettify.py ├── fprettify ├── __init__.py ├── fparse_utils.py ├── tests │ └── __init__.py └── version.py ├── hooks.yml ├── pyproject.toml ├── requirements.txt ├── run_tests.py ├── setup.cfg └── setup.py /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | duplication: 3 | enabled: true 4 | exclude_fingerprints: 5 | - 8f0fa17586b70a9f8b584173ab0c400f 6 | - 2520b12215b2d5a47e145adbeddea955 7 | - 1dfb6bf58fa716f94c029daf456c4fe1 8 | config: 9 | languages: 10 | - python 11 | fixme: 12 | enabled: true 13 | radon: 14 | enabled: true 15 | ratings: 16 | paths: 17 | - "**.py" 18 | exclude_paths: [] 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Setup Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.x" 19 | 20 | - name: Build package 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install build 24 | python -m build 25 | 26 | - name: Publish to Test PyPi 27 | if: ${{ startsWith(github.ref, 'refs/tags') }} 28 | uses: pypa/gh-action-pypi-publish@master 29 | with: 30 | user: __token__ 31 | password: ${{ secrets.TEST_PYPI_API_TOKEN }} 32 | repository_url: https://test.pypi.org/legacy/ 33 | 34 | - name: Publish to PyPi 35 | if: ${{ startsWith(github.ref, 'refs/tags') }} 36 | uses: pypa/gh-action-pypi-publish@master 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * 3" 8 | workflow_dispatch: 9 | 10 | jobs: 11 | resources: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4 16 | 17 | - name: Create resource cache 18 | id: cache 19 | uses: actions/cache@v4 20 | with: 21 | path: ./fortran_tests/before/*/ 22 | key: resources-${{ github.event_name }} 23 | 24 | - name: Prepare tests (default) 25 | if: ${{ steps.cache.outputs.cache-hit != 'true' }} 26 | run: | 27 | .travis/prep_regular.sh 28 | 29 | - name: Prepare tests (schedule) 30 | if: ${{ steps.cache.outputs.cache-hit != 'true' && github.event_name == 'schedule' }} 31 | run: | 32 | .travis/prep_cron.sh 33 | 34 | pip: 35 | needs: 36 | - resources 37 | runs-on: ${{ matrix.os }} 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | os: [ubuntu-latest] 42 | python: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"] 43 | 44 | steps: 45 | - name: Checkout code 46 | uses: actions/checkout@v4 47 | 48 | - name: Load resources 49 | uses: actions/cache@v4 50 | with: 51 | path: ./fortran_tests/before/*/ 52 | key: resources-${{ github.event_name }} 53 | 54 | - uses: actions/setup-python@v5 55 | with: 56 | python-version: ${{ matrix.python }} 57 | 58 | - name: Install project & dependencies 59 | run: pip install .[dev] 60 | 61 | - name: Run tests 62 | run: | 63 | coverage run --source=fprettify setup.py test 64 | 65 | - name: Coverage upload 66 | run: coveralls --service=github 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | COVERALLS_FLAG_NAME: ${{ matrix.python }} 70 | COVERALLS_PARALLEL: true 71 | 72 | coverage: 73 | needs: 74 | - pip 75 | runs-on: ubuntu-latest 76 | 77 | steps: 78 | - uses: actions/setup-python@v5 79 | with: 80 | python-version: "3.x" 81 | 82 | - name: Install dependencies 83 | run: pip install coveralls 84 | 85 | - name: Coverage upload 86 | run: coveralls --service=github --finish 87 | env: 88 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | .venv/ 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # ctags 93 | tags 94 | 95 | # setuptools_scm autogeneated version 96 | _version.py 97 | 98 | # fortran temporary test files 99 | fortran_tests/ 100 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: fprettify 2 | name: auto-formatter for modern fortran source code 3 | description: imposes strict whitespace formatting for modern (F90+) Fortran code 4 | entry: fprettify 5 | language: python 6 | files: \.[fF]\d*$ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial # required for Python >= 3.7 2 | language: python 3 | python: 4 | - 3.5 5 | - 3.9 6 | before_install: 7 | - if [[ $TRAVIS_PYTHON_VERSION == "3.9" ]]; then export DO_COVERAGE=1; fi 8 | - if [[ "$TRAVIS_EVENT_TYPE" = "cron" ]]; then unset DO_COVERAGE; fi 9 | install: 10 | - pip install -U -r requirements.txt 11 | - if [[ $DO_COVERAGE ]]; then pip install coveralls; fi 12 | before_script: 13 | - ".travis/prep_regular.sh" 14 | - if [[ "$TRAVIS_EVENT_TYPE" = "cron" ]]; then .travis/prep_cron.sh; fi 15 | script: 16 | - if ! [[ $DO_COVERAGE ]]; then python setup.py test; fi 17 | - if [[ $DO_COVERAGE ]]; then coverage run --source=fprettify setup.py test; fi 18 | after_success: 19 | - if [[ $DO_COVERAGE ]]; then coveralls; fi 20 | deploy: 21 | provider: pypi 22 | user: pseewald 23 | password: 24 | secure: eI+t+10Tzjrkt7gjiBjgTS0xyOAkw5FlUuksHiUVnbI9JDGTxKuA3J9p9JjJKK9gRnljVaqCRhv4UYTxSCTtPlxpCKEFVMHGnjr2nT6IwETTi3R5tvzZzwuReh+jO5CSb9A1hF/iys4cKl+782Bgy6YH4j2jIq60FWFLBCMQiYGza62+jF6U37C5Cmw11DxqJABW4zA1g3y1Y4VQxOXnawPH7Zp6Ag5lk/R/fpthCZo/gnYkQKbkyl7mBng5xn4t6heIGiUnYf8Tqyd46UYYHCBzo2CtR+qkzFd07dtw5Ia3uCOGxfrdaFN+Fpf8gVlMAFq/83eRDa0qSHTO1hY5mbguN1UeVB/LrQNVd82fcBfHncjsSL6/GDiu/VzCW4i7Reh4KQmalBmwReKdC/4yQ+T05OZVtu1W7FyQHJW3Z0jSvpLVttkCSD5kSWvG+pyiEpbUtUsNjr9QosbiA1JhQ+XjhdkqiJU1jZ+oP330EwDnWlD74DgJNQRIQeA2mBMeuJxG87jHwudRetXTK2GW1Idh8YWqbm7pEQ5dGLbGHm1hl0XZ2HEGbYt//fXz47wo3aWX/1wE5e4a88OS8XQ4zMN6PBGAEqRuwQo8WfhfrWGQ8NYFMRwJ0Byo0AeBBj3pYNoGj4Y45+6ETnqkFaxMGbALK+iVr2jEBTbpeOcHJYQ= 25 | distributions: sdist bdist_wheel 26 | on: 27 | tags: true 28 | python: 3.9 29 | -------------------------------------------------------------------------------- /.travis/prep_cp2k.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | wget https://github.com/pseewald/cp2k/archive/fprettify-test.tar.gz 3 | mkdir fortran_tests/before/cp2k 4 | tar -xf fprettify-test.tar.gz -C fortran_tests/before/cp2k --strip-components=1 cp2k-fprettify-test/src 5 | rm fprettify-test.tar.gz 6 | -------------------------------------------------------------------------------- /.travis/prep_cron.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | .travis/prep_cp2k.sh 3 | -------------------------------------------------------------------------------- /.travis/prep_flap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | wget https://github.com/pseewald/FLAP/archive/fprettify-test.tar.gz 3 | mkdir fortran_tests/before/FLAP && tar -xf fprettify-test.tar.gz -C fortran_tests/before/FLAP --strip-components=1 4 | rm fprettify-test.tar.gz 5 | -------------------------------------------------------------------------------- /.travis/prep_regular.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | .travis/prep_rosetta.sh 3 | .travis/prep_flap.sh 4 | .travis/prep_wannier90.sh 5 | -------------------------------------------------------------------------------- /.travis/prep_rosetta.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | wget https://github.com/pseewald/RosettaCodeData/archive/fprettify-test.tar.gz 3 | mkdir fortran_tests/before/RosettaCodeData && tar -xf fprettify-test.tar.gz -C fortran_tests/before/RosettaCodeData --strip-components=1 4 | rm fprettify-test.tar.gz 5 | -------------------------------------------------------------------------------- /.travis/prep_wannier90.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | wget https://github.com/pseewald/wannier90/archive/fprettify-test.tar.gz 3 | mkdir fortran_tests/before/wannier90 && tar -xf fprettify-test.tar.gz -C fortran_tests/before/wannier90 --strip-components=1 4 | rm fprettify-test.tar.gz 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016-2019 Patrick Seewald, CP2K developers group 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fprettify 2 | 3 | [![CI](https://github.com/pseewald/fprettify/actions/workflows/test.yml/badge.svg)](https://github.com/pseewald/fprettify/actions/workflows/test.yml) 4 | [![Coverage Status](https://coveralls.io/repos/github/pseewald/fprettify/badge.svg?branch=master)](https://coveralls.io/github/pseewald/fprettify?branch=master) 5 | ![PyPI - License](https://img.shields.io/pypi/l/fprettify) 6 | ![PyPI](https://img.shields.io/pypi/v/fprettify) 7 | [![Code Climate](https://codeclimate.com/github/pseewald/fprettify/badges/gpa.svg)](https://codeclimate.com/github/pseewald/fprettify) 8 | 9 | fprettify is an auto-formatter for modern Fortran code that imposes strict whitespace formatting, written in Python. 10 | 11 | **NOTE:** I'm looking for help to maintain this repository, see [#127](https://github.com/pseewald/fprettify/issues/127). 12 | 13 | ## Features 14 | 15 | - Auto-indentation. 16 | - Line continuations are aligned with the previous opening delimiter `(`, `[` or `(/` or with an assignment operator `=` or `=>`. If none of the above is present, a default hanging indent is applied. 17 | - Consistent amount of whitespace around operators and delimiters. 18 | - Removal of extraneous whitespace and consecutive blank lines. 19 | - Change letter case (upper case / lower case conventions) of intrinsics 20 | - Tested for editor integration. 21 | - By default, fprettify causes whitespace changes only and thus preserves revision history. 22 | - fprettify can handle cpp and [fypp](https://github.com/aradi/fypp) preprocessor directives. 23 | 24 | ## Limitations 25 | 26 | - Works only for modern Fortran (Fortran 90 upwards). 27 | - Feature missing? Please create an issue. 28 | 29 | ## Requirements 30 | 31 | - Python 3 (Python 2.7 no longer supported) 32 | - [ConfigArgParse](https://pypi.org/project/ConfigArgParse): optional, enables use of config file 33 | 34 | ## Examples 35 | 36 | Compare `examples/*before.f90` (original Fortran files) with `examples/*after.f90` (reformatted Fortran files) to see what fprettify does. A quick demonstration: 37 | 38 | ```Fortran 39 | program demo 40 | integer :: endif,if,elseif 41 | integer,DIMENSION(2) :: function 42 | endif=3;if=2 43 | if(endif==2)then 44 | endif=5 45 | elseif=if+4*(endif+& 46 | 2**10) 47 | elseif(endif==3)then 48 | function(if)=endif/elseif 49 | print*,endif 50 | endif 51 | end program 52 | ``` 53 | 54 | ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩ `fprettify` ⇩⇩⇩⇩⇩⇩⇩⇩⇩⇩ 55 | 56 | ```Fortran 57 | program demo 58 | integer :: endif, if, elseif 59 | integer, DIMENSION(2) :: function 60 | endif = 3; if = 2 61 | if (endif == 2) then 62 | endif = 5 63 | elseif = if + 4*(endif + & 64 | 2**10) 65 | elseif (endif == 3) then 66 | function(if) = endif/elseif 67 | print *, endif 68 | endif 69 | end program 70 | ``` 71 | 72 | ## Installation 73 | 74 | The latest release can be installed using pip: 75 | 76 | ```sh 77 | pip install --upgrade fprettify 78 | ``` 79 | 80 | Installation from source requires Python Setuptools: 81 | 82 | ```sh 83 | pip install . 84 | ``` 85 | 86 | For local installation, use `--user` option. 87 | 88 | If you use the [Conda](https://docs.conda.io/) package manager, fprettify is available from the [conda-forge](https://conda-forge.org/) channel: 89 | 90 | ```sh 91 | conda install -c conda-forge fprettify 92 | ``` 93 | 94 | ## Command line tool 95 | 96 | Autoformat file1, file2, ... inplace by 97 | 98 | ```sh 99 | fprettify file1, file2, ... 100 | ``` 101 | 102 | The default indent is 3. If you prefer something else, use `--indent n` argument. 103 | 104 | In order to apply fprettify recursively to an entire Fortran project instead of a single file, use the `-r` option. 105 | 106 | For more options, read 107 | 108 | ```sh 109 | fprettify -h 110 | ``` 111 | 112 | ## Editor integration 113 | 114 | For editor integration, use 115 | 116 | ```sh 117 | fprettify --silent 118 | ``` 119 | 120 | For instance, with Vim, use fprettify with `gq` by putting the following commands in your `.vimrc`: 121 | 122 | ```vim 123 | autocmd Filetype fortran setlocal formatprg=fprettify\ --silent 124 | ``` 125 | 126 | ## Deactivation and manual formatting (experimental feature) 127 | 128 | fprettify can be deactivated for selected lines: a single line followed by an inline comment starting with `!&` is not auto-formatted and consecutive lines that are enclosed between two comment lines `!&<` and `!&>` are not auto-formatted. This is useful for cases where manual alignment is preferred over auto-formatting. Furthermore, deactivation is necessary when non-standard Fortran syntax (such as advanced usage of preprocessor directives) prevents proper formatting. As an example, consider the following snippet of fprettify formatted code: 129 | 130 | ```fortran 131 | A = [-1, 10, 0, & 132 | 0, 1000, 0, & 133 | 0, -1, 1] 134 | ``` 135 | 136 | In order to manually align the columns, fprettify needs to be deactivated by 137 | 138 | ```fortran 139 | A = [-1, 10, 0, & !& 140 | 0, 1000, 0, & !& 141 | 0, -1, 1] !& 142 | ``` 143 | 144 | or, equivalently by 145 | 146 | ```fortran 147 | !&< 148 | A = [-1, 10, 0, & 149 | 0, 1000, 0, & 150 | 0, -1, 1] 151 | !&> 152 | ``` 153 | 154 | ## Contributing / Testing 155 | 156 | The testing mechanism allows you to easily test fprettify with any Fortran project of your choice. Simply clone or copy your entire project into `fortran_tests/before` and run `python setup.py test`. The directory `fortran_tests/after` contains the test output (reformatted Fortran files). If testing fails, please submit an issue! 157 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: devel 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - configargparse 6 | - importlib-metadata 7 | -------------------------------------------------------------------------------- /examples/example_after.f90: -------------------------------------------------------------------------------- 1 | ../fortran_tests/after/example.f90 -------------------------------------------------------------------------------- /examples/example_before.f90: -------------------------------------------------------------------------------- 1 | ../fortran_tests/before/example.f90 -------------------------------------------------------------------------------- /fortran_tests/after/example.f90: -------------------------------------------------------------------------------- 1 | module example 2 | implicit none 3 | private 4 | public :: dp, test_routine, & 5 | test_function, test_type, str_function 6 | integer, parameter :: dp = selected_real_kind(15, 307) 7 | type test_type 8 | real(kind=dp) :: r = 1.0d-3 9 | integer :: i 10 | end type test_type 11 | 12 | contains 13 | 14 | subroutine test_routine( & 15 | r, i, j, k, l) 16 | integer, intent(in) :: r, i, j, k 17 | integer, intent(out) :: l 18 | 19 | l = test_function(r, i, j, k) 20 | end & 21 | subroutine 22 | 23 | pure function test_function(r, i, j, & 24 | k) & 25 | result(l) 26 | integer, intent(in) :: r, i, j, k 27 | integer :: l 28 | 29 | l = r + i + j + k 30 | end function 31 | function & 32 | str_function(a) result(l) 33 | character(len=*) :: a 34 | integer :: l 35 | 36 | if (len(a) < 5) then 37 | l = 0 38 | else 39 | l = 1 40 | end if 41 | end function 42 | 43 | end module 44 | 45 | program example_prog 46 | use example, only: dp, test_routine, test_function, test_type, str_function 47 | 48 | implicit none 49 | integer :: r, i, j, k, l, my_integer, m 50 | integer, dimension(5) :: arr 51 | integer, dimension(20) :: big_arr 52 | integer :: endif 53 | type(test_type) :: t 54 | real(kind=dp) :: r1, r2, r3, r4, r5, r6 55 | integer, pointer :: point 56 | 57 | point => null() 58 | 59 | ! 1) white space formatting ! 60 | !***************************! 61 | ! example 1.1 62 | r = 1; i = -2; j = 3; k = 4; l = 5 63 | r2 = 0.0_dp; r3 = 1.0_dp; r4 = 2.0_dp; r5 = 3.0_dp; r6 = 4.0_dp 64 | r1 = -(r2**i*(r3 + r5*(-r4) - r6)) - 2.e+2 65 | if (r .eq. 2 .and. r <= 5) i = 3 66 | write (*, *) (merge(3, 1, i <= 2)) 67 | write (*, *) test_function(r, i, j, k) 68 | t%r = 4.0_dp 69 | t%i = str_function("t % i = ") 70 | 71 | ! example 1.2 72 | my_integer = 2 73 | i = 3 74 | j = 5 75 | 76 | big_arr = [1, 2, 3, 4, 5, & 77 | 6, 7, 8, 9, 10, & 78 | 11, 12, 13, 14, 15, & 79 | 16, 17, 18, 19, 20] 80 | 81 | ! example 1.3: disabling auto-formatter: 82 | my_integer = 2 !& 83 | i = 3 !& 84 | j = 5 !& 85 | 86 | !&< 87 | my_integer = 2 88 | i = 3 89 | j = 5 90 | !&> 91 | 92 | big_arr = [ 1, 2, 3, 4, 5, & !& 93 | 6, 7, 8, 9, 10, & !& 94 | 11, 12, 13, 14, 15, & !& 95 | 16, 17, 18, 19, 20] !& 96 | 97 | ! example 1.4: 98 | 99 | big_arr = [1, 2, 3, 4, 5,& 100 | & 6, 7, 8, 9, 10, & 101 | & 11, 12, 13, 14, 15,& 102 | &16, 17, 18, 19, 20] 103 | 104 | ! 2) auto indentation for loops ! 105 | !*******************************! 106 | 107 | ! example 2.1 108 | l = 0 109 | do r = 1, 10 110 | select case (r) 111 | case (1) 112 | do_label: do i = 1, 100 113 | if (i <= 2) then 114 | m = 0 115 | do while (m < 4) 116 | m = m + 1 117 | do k = 1, 3 118 | if (k == 1) l = l + 1 119 | end do 120 | end do 121 | end if 122 | end do do_label 123 | case (2) 124 | l = i + j + k 125 | end select 126 | end do 127 | 128 | ! example 2.2 129 | do m = 1, 2 130 | do r = 1, 3 131 | write (*, *) r 132 | do k = 1, 4 133 | do l = 1, 3 134 | do i = 4, 5 135 | do my_integer = 1, 1 136 | do j = 1, 2 137 | write (*, *) test_function(m, r, k, l) + i 138 | end do 139 | end do 140 | end do 141 | end do 142 | end do 143 | end do 144 | end do 145 | 146 | ! 3) auto alignment for linebreaks ! 147 | !************************************! 148 | 149 | ! example 3.1 150 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + 3*(2 + 1) 151 | 152 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + & 153 | 3*(2 + 1) 154 | 155 | l = test_function(1, 2, & 156 | test_function(1, 2, 3, 4), 4) + & 157 | 3*(2 + 1) 158 | 159 | l = test_function(1, 2, & 160 | test_function(1, 2, 3, & 161 | 4), 4) + & 162 | 3*(2 + 1) 163 | 164 | ! example 3.2 165 | arr = [1, (/3, 4, 5/), 6] + [1, 2, 3, 4, 5] 166 | 167 | arr = [1, (/3, 4, 5/), & 168 | 6] + [1, 2, 3, 4, 5] 169 | 170 | arr = [1, (/3, 4, 5/), & 171 | 6] + & 172 | [1, 2, 3, 4, 5] 173 | 174 | arr = [1, (/3, 4, & 175 | 5/), & 176 | 6] + & 177 | [1, 2, 3, 4, 5] 178 | 179 | ! example 3.3 180 | l = test_function(1, 2, & 181 | 3, 4) 182 | 183 | l = test_function( & 184 | 1, 2, 3, 4) 185 | 186 | arr = [1, 2, & 187 | 3, 4, 5] 188 | arr = [ & 189 | 1, 2, 3, 4, 5] 190 | 191 | ! 4) more complex formatting and tricky test cases ! 192 | !**************************************************! 193 | 194 | ! example 4.1 195 | l = 0 196 | do r = 1, 10 197 | select case (r) 198 | case (1) 199 | do i = 1, 100; if (i <= 2) then! comment 200 | do j = 1, 5 201 | do k = 1, 3 202 | l = l + 1 203 | ! unindented comment 204 | ! indented comment 205 | end do; end do 206 | elseif (.not. j == 4) then 207 | my_integer = 4 208 | else 209 | write (*, *) " hello" 210 | end if 211 | end do 212 | case (2) 213 | l = i + j + k 214 | end select 215 | end do 216 | 217 | ! example 4.2 218 | if ( & 219 | l == & 220 | 111) & 221 | then 222 | do k = 1, 2 223 | if (k == 1) & 224 | l = test_function(1, & 225 | test_function(r=4, i=5, & 226 | j=6, k=test_function(1, 2*(3*(1 + 1)), str_function(")a!(b['(;=dfe"), & 227 | 9) + & 228 | test_function(1, 2, 3, 4)), 9, 10) & 229 | ! test_function(1,2,3,4)),9,10) & 230 | ! +13*str_function('') + str_function('"') 231 | + 13*str_function('') + str_function('"') 232 | end & ! comment 233 | ! comment 234 | do 235 | end if 236 | 237 | ! example 4.3 238 | arr = [1, (/3, 4, & 239 | 5/), & 240 | 6] + & 241 | [1, 2, 3, 4, 5]; arr = [1, 2, & 242 | 3, 4, 5] 243 | 244 | ! example 4.4 245 | endif = 3 246 | if (endif == 2) then 247 | endif = 5 248 | else if (endif == 3) then 249 | write (*, *) endif 250 | end if 251 | 252 | ! example 4.5 253 | do i = 1, 2; if (.true.) then 254 | write (*, *) "hello" 255 | end if; end do 256 | 257 | end program 258 | -------------------------------------------------------------------------------- /fortran_tests/after/example_swapcase.f90: -------------------------------------------------------------------------------- 1 | 2 | MODULE exAmple 3 | IMPLICIT NONE 4 | PRIVATE 5 | PUBLIC :: dp, test_routine, & 6 | test_function, test_type, str_function 7 | 8 | ! Comment, should not change case nor spaces 9 | !!$ INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND ( 15 , 307) 10 | !!$ TYPE test_type 11 | !!$ REAL (kind =dp ) :: r = 1.0d-3 12 | !!$ INTEGER :: i 13 | !!$ END TYPE test_type 14 | !!$ 15 | !!$ 16 | !!$CONTAINS 17 | !!$ 18 | !!$ 19 | !!$ SUBROUTINE test_routine( & 20 | !!$ r, i, j, k, l) 21 | !!$ INTEGER, INTENT(in) :: r, i, j, k 22 | !!$ INTEGER, INTENT (out) :: l 23 | !!$ 24 | !!$ l = test_function(r,i,j,k) 25 | !!$ END & 26 | !!$SUBROUTINE 27 | 28 | INTEGER, PARAMETER :: SELECTED_REAL_KIND = 1*2 29 | INTEGER, PARAMETER :: dp1 = SELECTED_REAL_KIND(15, 307) ! SELECTED_REAL_KIND ( 15 , 307) !should not change case in comment 30 | 31 | character(len=*), parameter :: a = 'INTEGER, PARAMETER'//'b'!should not change case in string 32 | character(len=*), parameter :: b = "INTEGER, PARAMETER" !should not change case in string 33 | character(len=*), parameter :: c = 'INTEGER, "PARAMETER"' !should not change case in string 34 | character(len=*), parameter :: d = "INTEGER, 'PARAMETER" !should not change case in string 35 | 36 | INTEGER(kind=int64), parameter :: l64 = 2_int64 37 | REAL(kind=real64), parameter :: r64a = 2._real64 38 | REAL(kind=real64), parameter :: r64b = 2.0_real64 39 | REAL(kind=real64), parameter :: r64c = .0_real64 40 | REAL(kind=real64), parameter :: r64a = 2.e3_real64 41 | REAL(kind=real64), parameter :: r64b = 2.0e3_real64 42 | REAL(kind=real64), parameter :: r64c = .0e3_real64 43 | 44 | INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15, 307) 45 | TYPE test_type 46 | REAL(kind=dp) :: r = 1.0d-3 47 | INTEGER :: i 48 | END TYPE test_type 49 | 50 | CONTAINS 51 | 52 | SUBROUTINE test_routine( & 53 | r, i, j, k, l) 54 | USE iso_fortran_env, only: int64 55 | INTEGER, INTENT(in) :: r, i, j, k 56 | INTEGER, INTENT(out) :: l 57 | 58 | INTEGER(kind=int64) :: l64 59 | 60 | l = test_function(r, i, j, k) 61 | 62 | l64 = 2_int64 63 | IF (l .EQ. 2) l = max(l64, 2_int64) 64 | IF (l .EQ. 2) l = max(l64, 2_int64) 65 | IF (l .EQ. 2) l = max 66 | 67 | END & 68 | SUBROUTINE 69 | 70 | PURE FUNCTION test_function(r, i, j, & 71 | k) & 72 | RESULT(l) 73 | INTEGER, INTENT(in) :: r, i, j, k 74 | INTEGER :: l 75 | 76 | l = r + i + j + k 77 | END FUNCTION 78 | FUNCTION & 79 | str_function(a) RESULT(l) 80 | CHARACTER(len=*) :: a 81 | INTEGER :: l 82 | 83 | IF (LEN(a) < 5) THEN 84 | l = 0 85 | ELSE 86 | l = 1 87 | END IF 88 | END FUNCTION 89 | 90 | END MODULE 91 | 92 | PROGRAM example_prog 93 | USE example, ONLY: dp, test_routine, test_function, test_type, str_function 94 | 95 | IMPLICIT NONE 96 | INTEGER :: r, i, j, k, l, my_integer, m 97 | INTEGER, DIMENSION(5) :: arr 98 | INTEGER, DIMENSION(20) :: big_arr 99 | INTEGER :: ENDIF 100 | TYPE(test_type) :: t 101 | REAL(kind=dp) :: r1, r2, r3, r4, r5, r6 102 | INTEGER, POINTER :: point 103 | 104 | point => NULL() 105 | 106 | ! 1) white space formatting ! 107 | !***************************! 108 | ! example 1.1 109 | r = 1; i = -2; j = 3; k = 4; l = 5 110 | r2 = 0.0_dp; r3 = 1.0_dp; r4 = 2.0_dp; r5 = 3.0_dp; r6 = 4.0_dp 111 | r1 = -(r2**i*(r3 + r5*(-r4) - r6)) - 2.e+2 112 | IF (r .EQ. 2 .AND. r <= 5) i = 3 113 | WRITE (*, *) (MERGE(3, 1, i <= 2)) 114 | WRITE (*, *) test_function(r, i, j, k) 115 | t%r = 4.0_dp 116 | t%i = str_function("t % i = ") 117 | 118 | ! example 1.2 119 | my_integer = 2 120 | i = 3 121 | j = 5 122 | 123 | big_arr = [1, 2, 3, 4, 5, & 124 | 6, 7, 8, 9, 10, & 125 | 11, 12, 13, 14, 15, & 126 | 16, 17, 18, 19, 20] 127 | 128 | ! example 1.3: disabling auto-formatter: 129 | my_integer = 2 !& 130 | i = 3 !& 131 | j = 5 !& 132 | 133 | !&< 134 | my_integer = 2 135 | i = 3 136 | j = 5 137 | !&> 138 | 139 | big_arr = [ 1, 2, 3, 4, 5, & !& 140 | 6, 7, 8, 9, 10, & !& 141 | 11, 12, 13, 14, 15, & !& 142 | 16, 17, 18, 19, 20] !& 143 | 144 | ! example 1.4: 145 | 146 | big_arr = [1, 2, 3, 4, 5,& 147 | & 6, 7, 8, 9, 10, & 148 | & 11, 12, 13, 14, 15,& 149 | &16, 17, 18, 19, 20] 150 | 151 | ! 2) auto indentation for loops ! 152 | !*******************************! 153 | 154 | ! example 2.1 155 | l = 0 156 | DO r = 1, 10 157 | SELECT CASE (r) 158 | CASE (1) 159 | do_label: DO i = 1, 100 160 | IF (i <= 2) THEN 161 | m = 0 162 | DO WHILE (m < 4) 163 | m = m + 1 164 | DO k = 1, 3 165 | IF (k == 1) l = l + 1 166 | END DO 167 | END DO 168 | END IF 169 | END DO do_label 170 | CASE (2) 171 | l = i + j + k 172 | END SELECT 173 | END DO 174 | 175 | ! example 2.2 176 | DO m = 1, 2 177 | DO r = 1, 3 178 | WRITE (*, *) r 179 | DO k = 1, 4 180 | DO l = 1, 3 181 | DO i = 4, 5 182 | DO my_integer = 1, 1 183 | DO j = 1, 2 184 | WRITE (*, *) test_function(m, r, k, l) + i 185 | END DO 186 | END DO 187 | END DO 188 | END DO 189 | END DO 190 | END DO 191 | END DO 192 | 193 | ! 3) auto alignment for linebreaks ! 194 | !************************************! 195 | 196 | ! example 3.1 197 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + 3*(2 + 1) 198 | 199 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + & 200 | 3*(2 + 1) 201 | 202 | l = test_function(1, 2, & 203 | test_function(1, 2, 3, 4), 4) + & 204 | 3*(2 + 1) 205 | 206 | l = test_function(1, 2, & 207 | test_function(1, 2, 3, & 208 | 4), 4) + & 209 | 3*(2 + 1) 210 | 211 | ! example 3.2 212 | arr = [1, (/3, 4, 5/), 6] + [1, 2, 3, 4, 5] 213 | 214 | arr = [1, (/3, 4, 5/), & 215 | 6] + [1, 2, 3, 4, 5] 216 | 217 | arr = [1, (/3, 4, 5/), & 218 | 6] + & 219 | [1, 2, 3, 4, 5] 220 | 221 | arr = [1, (/3, 4, & 222 | 5/), & 223 | 6] + & 224 | [1, 2, 3, 4, 5] 225 | 226 | ! example 3.3 227 | l = test_function(1, 2, & 228 | 3, 4) 229 | 230 | l = test_function( & 231 | 1, 2, 3, 4) 232 | 233 | arr = [1, 2, & 234 | 3, 4, 5] 235 | arr = [ & 236 | 1, 2, 3, 4, 5] 237 | 238 | ! 4) more complex formatting and tricky test cases ! 239 | !**************************************************! 240 | 241 | ! example 4.1 242 | l = 0 243 | DO r = 1, 10 244 | SELECT CASE (r) 245 | CASE (1) 246 | DO i = 1, 100; IF (i <= 2) THEN! comment 247 | DO j = 1, 5 248 | DO k = 1, 3 249 | l = l + 1 250 | ! unindented comment 251 | ! indented comment 252 | END DO; END DO 253 | ELSEIF (.NOT. j == 4) THEN 254 | my_integer = 4 255 | ELSE 256 | WRITE (*, *) " hello" 257 | END IF 258 | END DO 259 | CASE (2) 260 | l = i + j + k 261 | END SELECT 262 | END DO 263 | 264 | ! example 4.2 265 | IF ( & 266 | l == & 267 | 111) & 268 | THEN 269 | DO k = 1, 2 270 | IF (k == 1) & 271 | l = test_function(1, & 272 | test_function(r=4, i=5, & 273 | j=6, k=test_function(1, 2*(3*(1 + 1)), str_function(")a!(b['(;=dfe"), & 274 | 9) + & 275 | test_function(1, 2, 3, 4)), 9, 10) & 276 | ! test_function(1,2,3,4)),9,10) & 277 | ! +13*str_function('') + str_function('"') 278 | + 13*str_function('') + str_function('"') 279 | END & ! comment 280 | ! comment 281 | DO 282 | END IF 283 | 284 | ! example 4.3 285 | arr = [1, (/3, 4, & 286 | 5/), & 287 | 6] + & 288 | [1, 2, 3, 4, 5]; arr = [1, 2, & 289 | 3, 4, 5] 290 | 291 | ! example 4.4 292 | ENDIF = 3 293 | IF (ENDIF == 2) THEN 294 | ENDIF = 5 295 | ELSE IF (ENDIF == 3) THEN 296 | WRITE (*, *) ENDIF 297 | END IF 298 | 299 | ! example 4.5 300 | DO i = 1, 2; IF (.TRUE.) THEN 301 | WRITE (*, *) "hello" 302 | END IF; END DO 303 | 304 | END PROGRAM 305 | -------------------------------------------------------------------------------- /fortran_tests/after/example_swapcase.f90-enabled: -------------------------------------------------------------------------------- 1 | 2 | module exAmple 3 | implicit none 4 | private 5 | public :: dp, test_routine, & 6 | test_function, test_type, str_function 7 | 8 | ! Comment, should not change case nor spaces 9 | !!$ INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND ( 15 , 307) 10 | !!$ TYPE test_type 11 | !!$ REAL (kind =dp ) :: r = 1.0d-3 12 | !!$ INTEGER :: i 13 | !!$ END TYPE test_type 14 | !!$ 15 | !!$ 16 | !!$CONTAINS 17 | !!$ 18 | !!$ 19 | !!$ SUBROUTINE test_routine( & 20 | !!$ r, i, j, k, l) 21 | !!$ INTEGER, INTENT(in) :: r, i, j, k 22 | !!$ INTEGER, INTENT (out) :: l 23 | !!$ 24 | !!$ l = test_function(r,i,j,k) 25 | !!$ END & 26 | !!$SUBROUTINE 27 | 28 | integer, parameter :: SELECTED_REAL_KIND = 1*2 29 | integer, parameter :: dp1 = selected_real_kind(15, 307) ! SELECTED_REAL_KIND ( 15 , 307) !should not change case in comment 30 | 31 | character(len=*), parameter :: a = 'INTEGER, PARAMETER'//'b'!should not change case in string 32 | character(len=*), parameter :: b = "INTEGER, PARAMETER" !should not change case in string 33 | character(len=*), parameter :: c = 'INTEGER, "PARAMETER"' !should not change case in string 34 | character(len=*), parameter :: d = "INTEGER, 'PARAMETER" !should not change case in string 35 | 36 | integer(kind=INT64), parameter :: l64 = 2_INT64 37 | real(kind=REAL64), parameter :: r64a = 2._REAL64 38 | real(kind=REAL64), parameter :: r64b = 2.0_REAL64 39 | real(kind=REAL64), parameter :: r64c = .0_REAL64 40 | real(kind=REAL64), parameter :: r64a = 2.E3_REAL64 41 | real(kind=REAL64), parameter :: r64b = 2.0E3_REAL64 42 | real(kind=REAL64), parameter :: r64c = .0E3_REAL64 43 | 44 | integer, parameter :: dp = selected_real_kind(15, 307) 45 | type test_type 46 | real(kind=dp) :: r = 1.0D-3 47 | integer :: i 48 | end type test_type 49 | 50 | contains 51 | 52 | subroutine test_routine( & 53 | r, i, j, k, l) 54 | use ISO_FORTRAN_ENV, only: INT64 55 | integer, intent(in) :: r, i, j, k 56 | integer, intent(out) :: l 57 | 58 | integer(kind=INT64) :: l64 59 | 60 | l = test_function(r, i, j, k) 61 | 62 | l64 = 2_INT64 63 | if (l .eq. 2) l = max(l64, 2_INT64) 64 | if (l .eq. 2) l = max(l64, 2_INT64) 65 | if (l .eq. 2) l = max 66 | 67 | end & 68 | subroutine 69 | 70 | pure function test_function(r, i, j, & 71 | k) & 72 | result(l) 73 | integer, intent(in) :: r, i, j, k 74 | integer :: l 75 | 76 | l = r + i + j + k 77 | end function 78 | function & 79 | str_function(a) result(l) 80 | character(len=*) :: a 81 | integer :: l 82 | 83 | if (len(a) < 5) then 84 | l = 0 85 | else 86 | l = 1 87 | endif 88 | end function 89 | 90 | end module 91 | 92 | program example_prog 93 | use example, only: dp, test_routine, test_function, test_type, str_function 94 | 95 | implicit none 96 | integer :: r, i, j, k, l, my_integer, m 97 | integer, dimension(5) :: arr 98 | integer, dimension(20) :: big_arr 99 | integer :: endif 100 | type(test_type) :: t 101 | real(kind=dp) :: r1, r2, r3, r4, r5, r6 102 | integer, pointer :: point 103 | 104 | point => null() 105 | 106 | ! 1) white space formatting ! 107 | !***************************! 108 | ! example 1.1 109 | r = 1; i = -2; j = 3; k = 4; l = 5 110 | r2 = 0.0_DP; r3 = 1.0_DP; r4 = 2.0_DP; r5 = 3.0_DP; r6 = 4.0_DP 111 | r1 = -(r2**i*(r3 + r5*(-r4) - r6)) - 2.E+2 112 | if (r .eq. 2 .and. r <= 5) i = 3 113 | write (*, *) (merge(3, 1, i <= 2)) 114 | write (*, *) test_function(r, i, j, k) 115 | t%r = 4.0_DP 116 | t%i = str_function("t % i = ") 117 | 118 | ! example 1.2 119 | my_integer = 2 120 | i = 3 121 | j = 5 122 | 123 | big_arr = [1, 2, 3, 4, 5, & 124 | 6, 7, 8, 9, 10, & 125 | 11, 12, 13, 14, 15, & 126 | 16, 17, 18, 19, 20] 127 | 128 | ! example 1.3: disabling auto-formatter: 129 | my_integer = 2 !& 130 | i = 3 !& 131 | j = 5 !& 132 | 133 | !&< 134 | my_integer = 2 135 | i = 3 136 | j = 5 137 | !&> 138 | 139 | big_arr = [ 1, 2, 3, 4, 5, & !& 140 | 6, 7, 8, 9, 10, & !& 141 | 11, 12, 13, 14, 15, & !& 142 | 16, 17, 18, 19, 20] !& 143 | 144 | ! example 1.4: 145 | 146 | big_arr = [1, 2, 3, 4, 5,& 147 | & 6, 7, 8, 9, 10, & 148 | & 11, 12, 13, 14, 15,& 149 | &16, 17, 18, 19, 20] 150 | 151 | ! 2) auto indentation for loops ! 152 | !*******************************! 153 | 154 | ! example 2.1 155 | l = 0 156 | do r = 1, 10 157 | select case (r) 158 | case (1) 159 | do_label: do i = 1, 100 160 | if (i <= 2) then 161 | m = 0 162 | do while (m < 4) 163 | m = m + 1 164 | do k = 1, 3 165 | if (k == 1) l = l + 1 166 | end do 167 | enddo 168 | endif 169 | enddo do_label 170 | case (2) 171 | l = i + j + k 172 | end select 173 | enddo 174 | 175 | ! example 2.2 176 | do m = 1, 2 177 | do r = 1, 3 178 | write (*, *) r 179 | do k = 1, 4 180 | do l = 1, 3 181 | do i = 4, 5 182 | do my_integer = 1, 1 183 | do j = 1, 2 184 | write (*, *) test_function(m, r, k, l) + i 185 | enddo 186 | enddo 187 | enddo 188 | enddo 189 | enddo 190 | enddo 191 | enddo 192 | 193 | ! 3) auto alignment for linebreaks ! 194 | !************************************! 195 | 196 | ! example 3.1 197 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + 3*(2 + 1) 198 | 199 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + & 200 | 3*(2 + 1) 201 | 202 | l = test_function(1, 2, & 203 | test_function(1, 2, 3, 4), 4) + & 204 | 3*(2 + 1) 205 | 206 | l = test_function(1, 2, & 207 | test_function(1, 2, 3, & 208 | 4), 4) + & 209 | 3*(2 + 1) 210 | 211 | ! example 3.2 212 | arr = [1, (/3, 4, 5/), 6] + [1, 2, 3, 4, 5] 213 | 214 | arr = [1, (/3, 4, 5/), & 215 | 6] + [1, 2, 3, 4, 5] 216 | 217 | arr = [1, (/3, 4, 5/), & 218 | 6] + & 219 | [1, 2, 3, 4, 5] 220 | 221 | arr = [1, (/3, 4, & 222 | 5/), & 223 | 6] + & 224 | [1, 2, 3, 4, 5] 225 | 226 | ! example 3.3 227 | l = test_function(1, 2, & 228 | 3, 4) 229 | 230 | l = test_function( & 231 | 1, 2, 3, 4) 232 | 233 | arr = [1, 2, & 234 | 3, 4, 5] 235 | arr = [ & 236 | 1, 2, 3, 4, 5] 237 | 238 | ! 4) more complex formatting and tricky test cases ! 239 | !**************************************************! 240 | 241 | ! example 4.1 242 | l = 0 243 | do r = 1, 10 244 | select case (r) 245 | case (1) 246 | do i = 1, 100; if (i <= 2) then! comment 247 | do j = 1, 5 248 | do k = 1, 3 249 | l = l + 1 250 | ! unindented comment 251 | ! indented comment 252 | end do; enddo 253 | elseif (.not. j == 4) then 254 | my_integer = 4 255 | else 256 | write (*, *) " hello" 257 | endif 258 | enddo 259 | case (2) 260 | l = i + j + k 261 | end select 262 | enddo 263 | 264 | ! example 4.2 265 | if ( & 266 | l == & 267 | 111) & 268 | then 269 | do k = 1, 2 270 | if (k == 1) & 271 | l = test_function(1, & 272 | test_function(r=4, i=5, & 273 | j=6, k=test_function(1, 2*(3*(1 + 1)), str_function(")a!(b['(;=dfe"), & 274 | 9) + & 275 | test_function(1, 2, 3, 4)), 9, 10) & 276 | ! test_function(1,2,3,4)),9,10) & 277 | ! +13*str_function('') + str_function('"') 278 | + 13*str_function('') + str_function('"') 279 | end & ! comment 280 | ! comment 281 | do 282 | endif 283 | 284 | ! example 4.3 285 | arr = [1, (/3, 4, & 286 | 5/), & 287 | 6] + & 288 | [1, 2, 3, 4, 5]; arr = [1, 2, & 289 | 3, 4, 5] 290 | 291 | ! example 4.4 292 | endif = 3 293 | if (endif == 2) then 294 | endif = 5 295 | else if (endif == 3) then 296 | write (*, *) endif 297 | endif 298 | 299 | ! example 4.5 300 | do i = 1, 2; if (.true.) then 301 | write (*, *) "hello" 302 | endif; enddo 303 | 304 | end program 305 | -------------------------------------------------------------------------------- /fortran_tests/after/test_fypp.f90: -------------------------------------------------------------------------------- 1 | #:if DEBUG > 0 2 | print *, "Some debug information" 3 | #:endif 4 | 5 | #:set LOGLEVEL = 2 6 | print *, "LOGLEVEL: ${LOGLEVEL}$" 7 | 8 | #:del LOGLEVEL 9 | 10 | #:def assertTrue(cond) 11 | #:if DEBUG > 0 12 | if (.not. ${cond}$) then 13 | print *, "Assert failed in file ${_FILE_}$, line ${_LINE_}$" 14 | error stop 15 | end if 16 | #:endif 17 | #:enddef assertTrue 18 | 19 | ! Invoked via direct call (argument needs no quotation) 20 | @:assertTrue(size(myArray) > 0) 21 | 22 | ! Invoked as Python expression (argument needs quotation) 23 | $:assertTrue('size(myArray) > 0') 24 | 25 | program test 26 | #:if defined('WITH_MPI') 27 | use mpi 28 | #:elif defined('WITH_OPENMP') 29 | use openmp 30 | #:else 31 | use serial 32 | #:endif 33 | end program 34 | 35 | interface myfunc 36 | #:for dtype in ['real', 'dreal', 'complex', 'dcomplex'] 37 | module procedure myfunc_${dtype}$ 38 | #:endfor 39 | end interface myfunc 40 | 41 | logical, parameter :: hasMpi = #{if defined('MPI')}# .true. #{else}# .false. #{endif}# 42 | 43 | character(*), parameter :: comp_date = "${time.strftime('%Y-%m-%d')}$" 44 | 45 | #:include "macrodefs.fypp" 46 | 47 | #:if var1 > var2 & 48 | & or var2> var4 49 | print *, "Doing something here" 50 | #:endif 51 | 52 | #! Callable needs only string argument 53 | #:def debug_code(code) 54 | #:if DEBUG > 0 55 | $:code 56 | #:endif 57 | #:enddef debug_code 58 | 59 | #! Pass code block as first positional argument 60 | #:call debug_code 61 | if (size(array) > 100) then 62 | print *, "DEBUG: spuriously large array" 63 | end if 64 | #:endcall debug_code 65 | 66 | #! Callable needs also non-string argument types 67 | #:def repeat_code(code, repeat) 68 | #:for ind in range(repeat) 69 | $:code 70 | #:endfor 71 | #:enddef repeat_code 72 | 73 | #! Pass code block as positional argument and 3 as keyword argument "repeat" 74 | #:call repeat_code(repeat=3) 75 | this will be repeated 3 times 76 | #:endcall repeat_code 77 | 78 | #! This will not show up in the output 79 | #! Also the newline characters at the end of the lines will be suppressed 80 | 81 | #! Definitions are read, but no output (e.g. newlines) will be produced 82 | #:mute 83 | #:include "macrodefs.fypp" 84 | #:endmute 85 | 86 | #:if DEBUGLEVEL < 0 87 | #:stop 'Negative debug level not allowed!' 88 | #:endif 89 | 90 | #:def mymacro(RANK) 91 | #! Macro only works for RANK 1 and above 92 | #:assert RANK > 0 93 | #:enddef mymacro 94 | 95 | program test 96 | #:if defined('MPI') 97 | use mpi 98 | #:endif 99 | end program 100 | 101 | #{if 1 > 2}#Some code#{endif}# 102 | 103 | @:mymacro(a 0 164 | if (.not. (${cond}$)) then 165 | print *, "Assert failed!" 166 | error stop 167 | end if 168 | #:endif 169 | #:enddef 170 | 171 | #:def macro(X, *VARARGS) 172 | X = ${X}$, VARARGS = #{for ARG in VARARGS}#${ARG}$#{endfor}# 173 | #:enddef macro 174 | 175 | $:macro(1,2, 3) #! Returns "X=1, VARARGS=23" 176 | 177 | ! Rather ugly 178 | print *, #{call choose_code}# a(:) #{nextarg}# size(a) #{endcall}# 179 | 180 | ! This form is more readable 181 | print *, ${choose_code('a(:)', 'size(a)')}$ 182 | 183 | ! Alternatively, you may use a direct call (see next section) 184 | print *, @{choose_code(a(:), size(a))}@ 185 | 186 | @:assertEqual(size(coords, dim=2), & 187 | & size( atomtypes)) 188 | 189 | #! Using choose_code() macro defined in previous section 190 | print *, @{choose_code(a(:),size(a))}@ 191 | 192 | #:if a > b & 193 | & or b > c & 194 | & or c>d 195 | $:somePythonFunction( param1, & 196 | ¶m2) 197 | 198 | #:mute 199 | 200 | #! Enable debug feature if the preprocessor variable DEBUG has been defined 201 | #:set DEBUG = defined('DEBUG') 202 | 203 | #! Stops the code, if the condition passed to it is not fulfilled 204 | #! Only included in debug mode. 205 | #:def ensure(cond, msg=None) 206 | #:if DEBUG 207 | if (.not. (${cond}$)) then 208 | write (*, *) 'Run-time check failed' 209 | write (*, *) 'Condition: ${cond.replace("'", "''")}$' 210 | #:if msg is not None 211 | write (*, *) 'Message: ', ${msg}$ 212 | #:endif 213 | write (*, *) 'File: ${_FILE_}$' 214 | write (*, *) 'Line: ', ${_LINE_}$ 215 | stop 216 | end if 217 | #:endif 218 | #:enddef ensure 219 | 220 | #! Includes code if in debug mode. 221 | #:def debug_code(code) 222 | #:if DEBUG 223 | $:code 224 | #:endif 225 | #:enddef debug_code 226 | 227 | #:endmute 228 | 229 | #:include 'checks.fypp' 230 | 231 | module testmod 232 | implicit none 233 | 234 | contains 235 | 236 | subroutine someFunction(ind, uplo) 237 | integer, intent(in) :: ind 238 | character, intent(in) :: uplo 239 | 240 | @:ensure(ind > 0, msg="Index must be positive") 241 | @:ensure(uplo == 'U' .or. uplo == 'L') 242 | 243 | ! Do something useful here 244 | 245 | #:call debug_code 246 | print *, 'We are in debug mode' 247 | print *, 'The value of ind is', ind 248 | #:endcall debug_code 249 | 250 | end subroutine someFunction 251 | 252 | end module testmod 253 | 254 | #:def ranksuffix(RANK) 255 | $:'' if RANK == 0 else '(' + ':' + ',:' * (RANK - 1) + ')' 256 | #:enddef ranksuffix 257 | 258 | #:set PRECISIONS = ['sp', 'dp'] 259 | #:set RANKS = range(0, 8) 260 | 261 | module errorcalc 262 | implicit none 263 | 264 | integer, parameter :: sp = kind(1.0) 265 | integer, parameter :: dp = kind(1.0d0) 266 | 267 | interface maxRelError 268 | #:for PREC in PRECISIONS 269 | #:for RANK in RANKS 270 | module procedure maxRelError_${RANK}$_${PREC}$ 271 | #:endfor 272 | #:endfor 273 | end interface maxRelError 274 | 275 | contains 276 | 277 | #:for PREC in PRECISIONS 278 | #:for RANK in RANKS 279 | 280 | function maxRelError_${RANK}$_${PREC}$ (obtained, reference) result(res) 281 | real(${PREC}$), intent(in) :: obtained${ranksuffix(RANK)}$ 282 | real(${PREC}$), intent(in) :: reference${ranksuffix(RANK)}$ 283 | real(${PREC}$) :: res 284 | 285 | #:if RANK == 0 286 | res = abs((obtained - reference)/reference) 287 | #:else 288 | res = maxval(abs((obtained - reference)/reference)) 289 | #:endif 290 | 291 | end function maxRelError_${RANK}$_${PREC}$ 292 | 293 | #:endfor 294 | #:endfor 295 | 296 | end module errorcalc 297 | 298 | #:def maxRelError_template(RANK, PREC) 299 | function maxRelError_${RANK}$_${PREC}$ (obtained, reference) result(res) 300 | real(${PREC}$), intent(in) :: obtained${ranksuffix(RANK)}$ 301 | real(${PREC}$), intent(in) :: reference${ranksuffix(RANK)}$ 302 | real(${PREC}$) :: res 303 | 304 | #:if RANK == 0 305 | res = abs((obtained - reference)/reference) 306 | #:else 307 | res = maxval(abs((obtained - reference)/reference)) 308 | #:endif 309 | 310 | end function maxRelError_${RANK}$_${PREC}$ 311 | #:enddef maxRelError_template 312 | 313 | #:for PREC in PRECISIONS 314 | #:for RANK in RANKS 315 | $:maxRelError_template(RANK, PREC) 316 | #:endfor 317 | #:endfor 318 | 319 | end module errorcalc 320 | 321 | ! tests for fypp directives inside Fortran continuation lines 322 | call test(arg1, & 323 | ${a if a > b else b}$, arg3, & 324 | #:if c>d 325 | c, & 326 | #:else 327 | d, & 328 | #:endif 329 | arg4) 330 | 331 | ! test for nested fypp / fortran constructs 332 | ! this is globally consistent even though the logical scopes can not be matched correctly 333 | 334 | #:if do 335 | do x = 1, 3 336 | ax = x*0.1 337 | #:else 338 | ax = 0.1 339 | #:endif 340 | r = ax 341 | #:if do 342 | end do 343 | #:endif 344 | 345 | r2 = r**2 346 | -------------------------------------------------------------------------------- /fortran_tests/after/test_invalid.f90: -------------------------------------------------------------------------------- 1 | implicit none 2 | private 3 | public :: dp, test_routine, & 4 | test_function, test_type, str_function 5 | integer, parameter :: dp = selected_real_kind(15, 307) 6 | type test_type 7 | real(kind=dp) :: r = 1.0d-3 8 | integer :: i 9 | end type test_type 10 | 11 | contains 12 | 13 | subroutine test_routine( & 14 | r, i, j, k, l) 15 | integer, intent(in) :: r, i, j, k 16 | integer, intent(out) :: l 17 | 18 | l = test_function(r, i, j, k) 19 | 20 | pure function test_function(r, i, j, & 21 | k & 22 | result(l) 23 | integer, intent(in) :: r, i, j, k 24 | integer :: l 25 | 26 | l = r + i + j + k 27 | end function 28 | function & 29 | str_function(a) result(l) 30 | character(len=*) :: a 31 | integer :: l 32 | 33 | if (len(a) < 5) then 34 | l = 0 35 | else 36 | l = 1 37 | end function 38 | 39 | end module 40 | 41 | program example_prog 42 | use example, only: dp, test_routine, test_function, test_type, str_function 43 | 44 | implicit none 45 | integer :: r, i, j, k, l, my_integer, m 46 | integer, dimension(5) :: arr 47 | integer, dimension(20) :: big_arr 48 | integer :: endif 49 | type(test_type) :: t 50 | real(kind=dp) :: r1, r2, r3, r4, r5, r6 51 | integer, pointer :: point 52 | 53 | point => null() 54 | 55 | r1 = -(r2**i*r3 + r5*(-r4 & 56 | - 3) - r6)) - 2.e+2 57 | r1 = -(r2**i*r3 + (r5*(-r4 & 58 | - 3) - r6 - 2.e+2 59 | if (r .eq. 2 .and. r <= 5) i = 3 60 | write (*, *) ( & 61 | merge(3, 1, i <= 2) 62 | write (*, *) test_function(r, i, j, k) 63 | END MODULE 64 | END PROGRAM 65 | END IF 66 | END DO 67 | FUNCTION a(b) 68 | integer :: a 69 | END FUNCTION 70 | END SUBROUTINE 71 | 72 | -------------------------------------------------------------------------------- /fortran_tests/after/test_namelist_block_select.f90: -------------------------------------------------------------------------------- 1 | module a_mod 2 | integer :: a_variable, another_variable 3 | 4 | private :: a_variable, & 5 | another_variable 6 | contains 7 | 8 | subroutine test_type(bohoo) 9 | class(*), intent(in) :: bohoo 10 | 11 | select type (bohoo) 12 | type is (real) 13 | write (*, *) 'T' 14 | type is (integer) 15 | write (*, *) 'F' 16 | class default 17 | end select 18 | 19 | return 20 | 21 | end subroutine test_type 22 | 23 | end module a_mod 24 | 25 | program test 26 | use a_mod 27 | 28 | integer :: block_test = 2, block = 2 29 | real :: res, factor = 2.81 30 | 31 | namelist /test_nml/ block, block_test, res, factor 32 | 33 | block = 5 34 | 35 | block 36 | real :: another_real 37 | another_real = 4.5 38 | end block 39 | 40 | call test_type(block) 41 | 42 | block ! have more vars 43 | real :: block 44 | call test_type(block) 45 | end block 46 | 47 | block = block*5/block_test + 1 48 | ! whitespace 2 49 | ! res = factor*5/block_test + 1 50 | res = factor*5/block_test + 1 51 | ! whitespace 3 52 | ! res = factor * 5 / block_test + 1 53 | res = factor*5/block_test + 1 54 | 55 | stop 56 | 57 | end program test 58 | -------------------------------------------------------------------------------- /fortran_tests/after/where_forall.f90: -------------------------------------------------------------------------------- 1 | ! Forall-construct 2 | 3 | ! Example 1 4 | 5 | forall (I=3:N + 1, J=3:N + 1) 6 | C(I, J) = C(I, J + 2) + C(I, J - 2) + C(I + 2, J) + C(I - 2, J) 7 | D(I, J) = C(I, J) 8 | end forall 9 | 10 | ! Example 2 11 | 12 | forall (I=3:N + 1, J=3:N + 1) 13 | C(I, J) = C(I, J + 2) + C(I, J - 2) + C(I + 2, J) + C(I - 2, J) 14 | D(I, J) = C(I, J) 15 | end forall 16 | 17 | ! Example 3 18 | 19 | forall (I=3:N + 1, J=3:N + 1) 20 | C(I, J) = C(I, J + 2) + C(I, J - 2) + C(I + 2, J) + C(I - 2, J) 21 | D(I, J) = C(I, J) 22 | end forall 23 | 24 | ! Where-construct 25 | 26 | ! Example 1 27 | 28 | where (C /= 0) 29 | A = B/C 30 | elsewhere 31 | A = 0.0 32 | end where 33 | 34 | ! Example 2 35 | 36 | where (C /= 0) 37 | A = B/C 38 | elsewhere 39 | A = 0.0 40 | end where 41 | 42 | ! Example 3 43 | 44 | where (C /= 0) 45 | A = B/C 46 | elsewhere 47 | A = 0.0 48 | end where 49 | -------------------------------------------------------------------------------- /fortran_tests/before/example.f90: -------------------------------------------------------------------------------- 1 | module example 2 | implicit none 3 | private 4 | public :: dp, test_routine, & 5 | test_function, test_type, str_function 6 | integer, parameter :: dp = selected_real_kind ( 15 , 307) 7 | type test_type 8 | real (kind =dp ) :: r = 1.0d-3 9 | integer :: i 10 | end type test_type 11 | 12 | contains 13 | 14 | 15 | subroutine test_routine( & 16 | r, i, j, k, l) 17 | integer, intent(in) :: r, i, j, k 18 | integer, intent (out) :: l 19 | 20 | l = test_function(r,i,j,k) 21 | end & 22 | subroutine 23 | 24 | pure function test_function(r, i, j, & 25 | k) & 26 | result(l) 27 | integer, intent(in) :: r, i, j, k 28 | integer :: l 29 | 30 | l=r + i +j +k 31 | end function 32 | function & 33 | str_function(a) result(l) 34 | character(len=*) :: a 35 | integer :: l 36 | 37 | if(len(a)<5)then 38 | l=0 39 | else 40 | l=1 41 | endif 42 | end function 43 | 44 | end module 45 | 46 | program example_prog 47 | use example, only: dp, test_routine, test_function, test_type,str_function 48 | 49 | implicit none 50 | integer :: r,i,j,k,l,my_integer,m 51 | integer, dimension(5) :: arr 52 | integer, dimension(20) :: big_arr 53 | integer :: endif 54 | type(test_type) :: t 55 | real(kind=dp) :: r1, r2, r3, r4, r5, r6 56 | integer, pointer :: point 57 | 58 | point=> null( ) 59 | 60 | ! 1) white space formatting ! 61 | !***************************! 62 | ! example 1.1 63 | r=1;i=-2;j=3;k=4;l=5 64 | r2 = 0.0_dp; r3= 1.0_dp; r4 =2.0_dp; r5=3.0_dp; r6 = 4.0_dp 65 | r1=-(r2**i*(r3+r5*(-r4)-r6))-2.e+2 66 | if( r.eq.2.and.r<=5) i=3 67 | write(*, *)(merge(3, 1, i<=2)) 68 | write(*, *)test_function(r,i,j , k) 69 | t % r = 4.0_dp 70 | t%i = str_function( "t % i = " ) 71 | 72 | ! example 1.2 73 | my_integer=2 74 | i=3 75 | j=5 76 | 77 | big_arr = [1, 2, 3, 4, 5, & 78 | 6, 7, 8, 9, 10, & 79 | 11, 12, 13, 14, 15, & 80 | 16, 17, 18, 19, 20] 81 | 82 | ! example 1.3: disabling auto-formatter: 83 | my_integer = 2 !& 84 | i = 3 !& 85 | j = 5 !& 86 | 87 | !&< 88 | my_integer = 2 89 | i = 3 90 | j = 5 91 | !&> 92 | 93 | big_arr = [ 1, 2, 3, 4, 5, & !& 94 | 6, 7, 8, 9, 10, & !& 95 | 11, 12, 13, 14, 15, & !& 96 | 16, 17, 18, 19, 20] !& 97 | 98 | ! example 1.4: 99 | 100 | big_arr = [1, 2, 3, 4, 5,& 101 | & 6, 7, 8, 9, 10, & 102 | & 11, 12, 13, 14, 15,& 103 | &16, 17, 18, 19, 20] 104 | 105 | ! 2) auto indentation for loops ! 106 | !*******************************! 107 | 108 | ! example 2.1 109 | l = 0 110 | do r= 1 , 10 111 | select case (r) 112 | case(1) 113 | do_label: do i = 1,100 114 | if (i<=2) then 115 | m =0 116 | do while(m <4) 117 | m =m+1 118 | do k=1,3 119 | if (k==1) l =l +1 120 | end do 121 | enddo 122 | endif 123 | enddo do_label 124 | case ( 2 ) 125 | l=i + j + k 126 | end select 127 | enddo 128 | 129 | ! example 2.2 130 | do m = 1, 2 131 | do r = 1, 3 132 | write (*, *) r 133 | do k = 1, 4 134 | do l = 1, 3 135 | do i = 4, 5 136 | do my_integer = 1, 1 137 | do j = 1, 2 138 | write (*, *) test_function(m, r, k, l) + i 139 | enddo 140 | enddo 141 | enddo 142 | enddo 143 | enddo 144 | enddo 145 | enddo 146 | 147 | ! 3) auto alignment for linebreaks ! 148 | !************************************! 149 | 150 | ! example 3.1 151 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + 3 *(2+1) 152 | 153 | l = test_function (1, 2, test_function(1,2, 3, 4),4) +& 154 | 3*(2+ 1 ) 155 | 156 | l = test_function(1, 2, & 157 | test_function(1, 2, 3, 4), 4)+ & 158 | 3 * (2+1) 159 | 160 | l = test_function(1, 2, & 161 | test_function(1, 2, 3, & 162 | 4), 4) + & 163 | 3*(2 + 1) 164 | 165 | ! example 3.2 166 | arr = [1, (/3,4, 5/), 6] + [ 1, 2,3, 4,5 ] 167 | 168 | arr = [1,(/ 3, 4, 5 /) , & 169 | 6] +[1,2, 3, 4, 5 ] 170 | 171 | arr = [1,(/3,4,5/), & 172 | 6]+ & 173 | [1, 2, 3, 4, 5] 174 | 175 | arr = [1, (/3, 4, & 176 | 5/), & 177 | 6] + & 178 | [1, 2,3, 4, 5 ] 179 | 180 | ! example 3.3 181 | l = test_function(1, 2, & 182 | 3, 4) 183 | 184 | l = test_function( & 185 | 1, 2, 3, 4) 186 | 187 | arr = [1, 2, & 188 | 3, 4, 5] 189 | arr = [ & 190 | 1, 2, 3, 4, 5] 191 | 192 | ! 4) more complex formatting and tricky test cases ! 193 | !**************************************************! 194 | 195 | ! example 4.1 196 | l = 0 197 | do r = 1, 10 198 | select case ( r ) 199 | case( 1) 200 | do i=1,100;if (i<=2) then! comment 201 | do j = 1,5 202 | do k= 1, 3 203 | l = l + 1 204 | ! unindented comment 205 | ! indented comment 206 | end do; enddo 207 | elseif ( .not. j ==4 ) then 208 | my_integer = 4 209 | else 210 | write (*,*) " hello" 211 | endif 212 | enddo 213 | case(2 ) 214 | l = i+ j + k 215 | end select 216 | enddo 217 | 218 | ! example 4.2 219 | if ( & 220 | l == & 221 | 111) & 222 | then 223 | do k = 1, 2 224 | if (k == 1) & 225 | l = test_function(1, & 226 | test_function(r=4, i=5, & 227 | j=6, k=test_function(1,2*(3*(1 +1)), str_function ( ")a!(b['(;=dfe"), & 228 | 9) + & 229 | test_function(1, 2, 3, 4)), 9, 10) & 230 | ! test_function(1,2,3,4)),9,10) & 231 | ! +13*str_function('') + str_function('"') 232 | + 13*str_function('') + str_function('"') 233 | end & ! comment 234 | ! comment 235 | do 236 | endif 237 | 238 | ! example 4.3 239 | arr = [1,(/3,4, & 240 | 5 /),& 241 | 6 ]+ & 242 | [1,2, 3, 4,5] ; arr = [1, 2,& 243 | 3, 4, 5] 244 | 245 | ! example 4.4 246 | endif = 3 247 | if(endif==2)then 248 | endif=5 249 | else if(endif==3)then 250 | write(*,*)endif 251 | endif 252 | 253 | ! example 4.5 254 | do i=1,2;if(.true.)then 255 | write(*, *)"hello" 256 | endif; enddo 257 | 258 | end program 259 | -------------------------------------------------------------------------------- /fortran_tests/before/example_swapcase.f90: -------------------------------------------------------------------------------- 1 | 2 | MODULE exAmple 3 | IMPLICIT NONE 4 | PRIVATE 5 | PUBLIC :: dp, test_routine, & 6 | test_function, test_type, str_function 7 | 8 | ! Comment, should not change case nor spaces 9 | !!$ INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND ( 15 , 307) 10 | !!$ TYPE test_type 11 | !!$ REAL (kind =dp ) :: r = 1.0d-3 12 | !!$ INTEGER :: i 13 | !!$ END TYPE test_type 14 | !!$ 15 | !!$ 16 | !!$CONTAINS 17 | !!$ 18 | !!$ 19 | !!$ SUBROUTINE test_routine( & 20 | !!$ r, i, j, k, l) 21 | !!$ INTEGER, INTENT(in) :: r, i, j, k 22 | !!$ INTEGER, INTENT (out) :: l 23 | !!$ 24 | !!$ l = test_function(r,i,j,k) 25 | !!$ END & 26 | !!$SUBROUTINE 27 | 28 | INTEGER, PARAMETER :: SELECTED_REAL_KIND = 1*2 29 | INTEGER, PARAMETER :: dp1 = SELECTED_REAL_KIND ( 15 , 307) ! SELECTED_REAL_KIND ( 15 , 307) !should not change case in comment 30 | 31 | character(len=*), parameter :: a = 'INTEGER, PARAMETER' // 'b'!should not change case in string 32 | character(len=*), parameter :: b = "INTEGER, PARAMETER" !should not change case in string 33 | character(len=*), parameter :: c = 'INTEGER, "PARAMETER"' !should not change case in string 34 | character(len=*), parameter :: d = "INTEGER, 'PARAMETER" !should not change case in string 35 | 36 | 37 | INTEGER(kind=int64), parameter :: l64 = 2_int64 38 | REAL(kind=real64), parameter :: r64a = 2._real64 39 | REAL(kind=real64), parameter :: r64b = 2.0_real64 40 | REAL(kind=real64), parameter :: r64c = .0_real64 41 | REAL(kind=real64), parameter :: r64a = 2.e3_real64 42 | REAL(kind=real64), parameter :: r64b = 2.0e3_real64 43 | REAL(kind=real64), parameter :: r64c = .0e3_real64 44 | 45 | INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND ( 15 , 307) 46 | TYPE test_type 47 | REAL (kind =dp ) :: r = 1.0d-3 48 | INTEGER :: i 49 | END TYPE test_type 50 | 51 | 52 | CONTAINS 53 | 54 | 55 | SUBROUTINE test_routine( & 56 | r, i, j, k, l) 57 | USE iso_fortran_env, only: int64 58 | INTEGER, INTENT(in) :: r, i, j, k 59 | INTEGER, INTENT (out) :: l 60 | 61 | INTEGER(kind=int64) :: l64 62 | 63 | l = test_function(r,i,j,k) 64 | 65 | l64 = 2_int64 66 | IF (l.EQ.2) l=max(l64, 2_int64) 67 | IF (l.EQ.2) l=max (l64, 2_int64) 68 | IF (l.EQ.2) l=max 69 | 70 | END & 71 | SUBROUTINE 72 | 73 | PURE FUNCTION test_function(r, i, j, & 74 | k) & 75 | RESULT(l) 76 | INTEGER, INTENT(in) :: r, i, j, k 77 | INTEGER :: l 78 | 79 | l=r + i +j +k 80 | END FUNCTION 81 | FUNCTION & 82 | str_function(a) RESULT(l) 83 | CHARACTER(len=*) :: a 84 | INTEGER :: l 85 | 86 | IF(LEN(a)<5)THEN 87 | l=0 88 | ELSE 89 | l=1 90 | ENDIF 91 | END FUNCTION 92 | 93 | END MODULE 94 | 95 | PROGRAM example_prog 96 | USE example, ONLY: dp, test_routine, test_function, test_type,str_function 97 | 98 | IMPLICIT NONE 99 | INTEGER :: r,i,j,k,l,my_integer,m 100 | INTEGER, DIMENSION(5) :: arr 101 | INTEGER, DIMENSION(20) :: big_arr 102 | INTEGER :: ENDIF 103 | TYPE(test_type) :: t 104 | REAL(kind=dp) :: r1, r2, r3, r4, r5, r6 105 | INTEGER, POINTER :: point 106 | 107 | point=> NULL( ) 108 | 109 | ! 1) white space formatting ! 110 | !***************************! 111 | ! example 1.1 112 | r=1;i=-2;j=3;k=4;l=5 113 | r2 = 0.0_dp; r3= 1.0_dp; r4 =2.0_dp; r5=3.0_dp; r6 = 4.0_dp 114 | r1=-(r2**i*(r3+r5*(-r4)-r6))-2.e+2 115 | IF( r.EQ.2.AND.r<=5) i=3 116 | WRITE(*, *)(MERGE(3, 1, i<=2)) 117 | WRITE(*, *)test_function(r,i,j , k) 118 | t % r = 4.0_dp 119 | t%i = str_function( "t % i = " ) 120 | 121 | ! example 1.2 122 | my_integer=2 123 | i=3 124 | j=5 125 | 126 | big_arr = [1, 2, 3, 4, 5, & 127 | 6, 7, 8, 9, 10, & 128 | 11, 12, 13, 14, 15, & 129 | 16, 17, 18, 19, 20] 130 | 131 | ! example 1.3: disabling auto-formatter: 132 | my_integer = 2 !& 133 | i = 3 !& 134 | j = 5 !& 135 | 136 | !&< 137 | my_integer = 2 138 | i = 3 139 | j = 5 140 | !&> 141 | 142 | big_arr = [ 1, 2, 3, 4, 5, & !& 143 | 6, 7, 8, 9, 10, & !& 144 | 11, 12, 13, 14, 15, & !& 145 | 16, 17, 18, 19, 20] !& 146 | 147 | ! example 1.4: 148 | 149 | big_arr = [1, 2, 3, 4, 5,& 150 | & 6, 7, 8, 9, 10, & 151 | & 11, 12, 13, 14, 15,& 152 | &16, 17, 18, 19, 20] 153 | 154 | ! 2) auto indentation for loops ! 155 | !*******************************! 156 | 157 | ! example 2.1 158 | l = 0 159 | DO r= 1 , 10 160 | SELECT CASE (r) 161 | CASE(1) 162 | do_label: DO i = 1,100 163 | IF (i<=2) THEN 164 | m =0 165 | DO WHILE(m <4) 166 | m =m+1 167 | DO k=1,3 168 | IF (k==1) l =l +1 169 | END DO 170 | ENDDO 171 | ENDIF 172 | ENDDO do_label 173 | CASE ( 2 ) 174 | l=i + j + k 175 | END SELECT 176 | ENDDO 177 | 178 | ! example 2.2 179 | DO m = 1, 2 180 | DO r = 1, 3 181 | WRITE (*, *) r 182 | DO k = 1, 4 183 | DO l = 1, 3 184 | DO i = 4, 5 185 | DO my_integer = 1, 1 186 | DO j = 1, 2 187 | WRITE (*, *) test_function(m, r, k, l) + i 188 | ENDDO 189 | ENDDO 190 | ENDDO 191 | ENDDO 192 | ENDDO 193 | ENDDO 194 | ENDDO 195 | 196 | ! 3) auto alignment for linebreaks ! 197 | !************************************! 198 | 199 | ! example 3.1 200 | l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + 3 *(2+1) 201 | 202 | l = test_function (1, 2, test_function(1,2, 3, 4),4) +& 203 | 3*(2+ 1 ) 204 | 205 | l = test_function(1, 2, & 206 | test_function(1, 2, 3, 4), 4)+ & 207 | 3 * (2+1) 208 | 209 | l = test_function(1, 2, & 210 | test_function(1, 2, 3, & 211 | 4), 4) + & 212 | 3*(2 + 1) 213 | 214 | ! example 3.2 215 | arr = [1, (/3,4, 5/), 6] + [ 1, 2,3, 4,5 ] 216 | 217 | arr = [1,(/ 3, 4, 5 /) , & 218 | 6] +[1,2, 3, 4, 5 ] 219 | 220 | arr = [1,(/3,4,5/), & 221 | 6]+ & 222 | [1, 2, 3, 4, 5] 223 | 224 | arr = [1, (/3, 4, & 225 | 5/), & 226 | 6] + & 227 | [1, 2,3, 4, 5 ] 228 | 229 | ! example 3.3 230 | l = test_function(1, 2, & 231 | 3, 4) 232 | 233 | l = test_function( & 234 | 1, 2, 3, 4) 235 | 236 | arr = [1, 2, & 237 | 3, 4, 5] 238 | arr = [ & 239 | 1, 2, 3, 4, 5] 240 | 241 | ! 4) more complex formatting and tricky test cases ! 242 | !**************************************************! 243 | 244 | ! example 4.1 245 | l = 0 246 | DO r = 1, 10 247 | SELECT CASE ( r ) 248 | CASE( 1) 249 | DO i=1,100;IF (i<=2) THEN! comment 250 | DO j = 1,5 251 | DO k= 1, 3 252 | l = l + 1 253 | ! unindented comment 254 | ! indented comment 255 | END DO; ENDDO 256 | ELSEIF ( .NOT. j ==4 ) THEN 257 | my_integer = 4 258 | ELSE 259 | WRITE (*,*) " hello" 260 | ENDIF 261 | ENDDO 262 | CASE(2 ) 263 | l = i+ j + k 264 | END SELECT 265 | ENDDO 266 | 267 | ! example 4.2 268 | IF ( & 269 | l == & 270 | 111) & 271 | THEN 272 | DO k = 1, 2 273 | IF (k == 1) & 274 | l = test_function(1, & 275 | test_function(r=4, i=5, & 276 | j=6, k=test_function(1,2*(3*(1 +1)), str_function ( ")a!(b['(;=dfe"), & 277 | 9) + & 278 | test_function(1, 2, 3, 4)), 9, 10) & 279 | ! test_function(1,2,3,4)),9,10) & 280 | ! +13*str_function('') + str_function('"') 281 | + 13*str_function('') + str_function('"') 282 | END & ! comment 283 | ! comment 284 | DO 285 | ENDIF 286 | 287 | ! example 4.3 288 | arr = [1,(/3,4, & 289 | 5 /),& 290 | 6 ]+ & 291 | [1,2, 3, 4,5] ; arr = [1, 2,& 292 | 3, 4, 5] 293 | 294 | ! example 4.4 295 | ENDIF = 3 296 | IF(ENDIF==2)THEN 297 | ENDIF=5 298 | ELSE IF(ENDIF==3)THEN 299 | WRITE(*,*)ENDIF 300 | ENDIF 301 | 302 | ! example 4.5 303 | DO i=1,2;IF(.TRUE.)THEN 304 | WRITE(*, *)"hello" 305 | ENDIF; ENDDO 306 | 307 | END PROGRAM 308 | -------------------------------------------------------------------------------- /fortran_tests/before/test_fypp.f90: -------------------------------------------------------------------------------- 1 | #:if DEBUG > 0 2 | print *, "Some debug information" 3 | #:endif 4 | 5 | #:set LOGLEVEL = 2 6 | print *, "LOGLEVEL: ${LOGLEVEL}$" 7 | 8 | #:del LOGLEVEL 9 | 10 | #:def assertTrue(cond) 11 | #:if DEBUG > 0 12 | if (.not. ${cond}$ ) then 13 | print*,"Assert failed in file ${_FILE_}$, line ${_LINE_}$" 14 | error stop 15 | end if 16 | #:endif 17 | #:enddef assertTrue 18 | 19 | ! Invoked via direct call (argument needs no quotation) 20 | @:assertTrue(size(myArray) > 0) 21 | 22 | ! Invoked as Python expression (argument needs quotation) 23 | $:assertTrue('size(myArray) > 0') 24 | 25 | program test 26 | #:if defined('WITH_MPI') 27 | use mpi 28 | #:elif defined('WITH_OPENMP') 29 | use openmp 30 | #:else 31 | use serial 32 | #:endif 33 | end program 34 | 35 | interface myfunc 36 | #:for dtype in ['real', 'dreal', 'complex', 'dcomplex'] 37 | module procedure myfunc_${dtype}$ 38 | #:endfor 39 | end interface myfunc 40 | 41 | logical,parameter :: hasMpi = #{if defined('MPI')}# .true. #{else}# .false. #{endif}# 42 | 43 | character(*), parameter :: comp_date ="${time.strftime('%Y-%m-%d')}$" 44 | 45 | #:include "macrodefs.fypp" 46 | 47 | #:if var1 > var2 & 48 | & or var2> var4 49 | print *,"Doing something here" 50 | #:endif 51 | 52 | #! Callable needs only string argument 53 | #:def debug_code(code) 54 | #:if DEBUG > 0 55 | $:code 56 | #:endif 57 | #:enddef debug_code 58 | 59 | #! Pass code block as first positional argument 60 | #:call debug_code 61 | if (size(array) > 100) then 62 | print *,"DEBUG: spuriously large array" 63 | end if 64 | #:endcall debug_code 65 | 66 | #! Callable needs also non-string argument types 67 | #:def repeat_code(code, repeat) 68 | #:for ind in range(repeat) 69 | $:code 70 | #:endfor 71 | #:enddef repeat_code 72 | 73 | #! Pass code block as positional argument and 3 as keyword argument "repeat" 74 | #:call repeat_code(repeat=3) 75 | this will be repeated 3 times 76 | #:endcall repeat_code 77 | 78 | #! This will not show up in the output 79 | #! Also the newline characters at the end of the lines will be suppressed 80 | 81 | #! Definitions are read, but no output (e.g. newlines) will be produced 82 | #:mute 83 | #:include "macrodefs.fypp" 84 | #:endmute 85 | 86 | #:if DEBUGLEVEL < 0 87 | #:stop 'Negative debug level not allowed!' 88 | #:endif 89 | 90 | #:def mymacro(RANK) 91 | #! Macro only works for RANK 1 and above 92 | #:assert RANK > 0 93 | #:enddef mymacro 94 | 95 | program test 96 | #:if defined('MPI') 97 | use mpi 98 | #:endif 99 | end program 100 | 101 | #{if 1 > 2}#Some code#{endif}# 102 | 103 | @:mymacro(a 0 164 | if (.not. (${cond}$)) then 165 | print *,"Assert failed!" 166 | error stop 167 | end if 168 | #:endif 169 | #:enddef 170 | 171 | #:def macro(X, *VARARGS) 172 | X=${X}$, VARARGS=#{for ARG in VARARGS}#${ARG}$#{endfor}# 173 | #:enddef macro 174 | 175 | $:macro(1,2, 3) #! Returns "X=1, VARARGS=23" 176 | 177 | ! Rather ugly 178 | print *, #{call choose_code}# a(:) #{nextarg}# size(a) #{endcall}# 179 | 180 | ! This form is more readable 181 | print *, ${choose_code('a(:)', 'size(a)')}$ 182 | 183 | ! Alternatively, you may use a direct call (see next section) 184 | print *, @{choose_code(a(:), size(a))}@ 185 | 186 | @:assertEqual(size(coords, dim=2), & 187 | & size( atomtypes)) 188 | 189 | #! Using choose_code() macro defined in previous section 190 | print *, @{choose_code(a(:),size(a))}@ 191 | 192 | #:if a > b & 193 | & or b > c & 194 | & or c>d 195 | $:somePythonFunction( param1, & 196 | ¶m2) 197 | 198 | #:mute 199 | 200 | #! Enable debug feature if the preprocessor variable DEBUG has been defined 201 | #:set DEBUG = defined('DEBUG') 202 | 203 | 204 | #! Stops the code, if the condition passed to it is not fulfilled 205 | #! Only included in debug mode. 206 | #:def ensure(cond, msg=None) 207 | #:if DEBUG 208 | if (.not. (${cond}$)) then 209 | write(*,*) 'Run-time check failed' 210 | write(*,*) 'Condition: ${cond.replace("'", "''")}$' 211 | #:if msg is not None 212 | write(*,*) 'Message: ', ${msg}$ 213 | #:endif 214 | write(*,*)'File: ${_FILE_}$' 215 | write(*,*) 'Line: ', ${_LINE_}$ 216 | stop 217 | end if 218 | #:endif 219 | #:enddef ensure 220 | 221 | 222 | #! Includes code if in debug mode. 223 | #:def debug_code(code) 224 | #:if DEBUG 225 | $:code 226 | #:endif 227 | #:enddef debug_code 228 | 229 | #:endmute 230 | 231 | #:include 'checks.fypp' 232 | 233 | module testmod 234 | implicit none 235 | 236 | contains 237 | 238 | subroutine someFunction(ind, uplo) 239 | integer, intent(in) :: ind 240 | character, intent(in) :: uplo 241 | 242 | @:ensure(ind > 0, msg="Index must be positive") 243 | @:ensure(uplo == 'U' .or. uplo == 'L') 244 | 245 | ! Do something useful here 246 | 247 | #:call debug_code 248 | print *, 'We are in debug mode' 249 | print *, 'The value of ind is', ind 250 | #:endcall debug_code 251 | 252 | end subroutine someFunction 253 | 254 | end module testmod 255 | 256 | 257 | #:def ranksuffix(RANK) 258 | $:'' if RANK == 0 else '(' + ':' + ',:' * (RANK - 1) + ')' 259 | #:enddef ranksuffix 260 | 261 | #:set PRECISIONS = ['sp', 'dp'] 262 | #:set RANKS = range(0, 8) 263 | 264 | module errorcalc 265 | implicit none 266 | 267 | integer, parameter :: sp = kind(1.0) 268 | integer, parameter :: dp = kind(1.0d0) 269 | 270 | interface maxRelError 271 | #:for PREC in PRECISIONS 272 | #:for RANK in RANKS 273 | module procedure maxRelError_${RANK}$_${PREC}$ 274 | #:endfor 275 | #:endfor 276 | end interface maxRelError 277 | 278 | contains 279 | 280 | #:for PREC in PRECISIONS 281 | #:for RANK in RANKS 282 | 283 | function maxRelError_${RANK}$_${PREC}$(obtained, reference) result(res) 284 | real(${PREC}$), intent(in) :: obtained${ranksuffix(RANK)}$ 285 | real(${PREC}$), intent(in) :: reference${ranksuffix(RANK)}$ 286 | real(${PREC}$) :: res 287 | 288 | #:if RANK == 0 289 | res = abs((obtained - reference) / reference) 290 | #:else 291 | res = maxval(abs((obtained - reference) / reference)) 292 | #:endif 293 | 294 | end function maxRelError_${RANK}$_${PREC}$ 295 | 296 | #:endfor 297 | #:endfor 298 | 299 | end module errorcalc 300 | 301 | #:def maxRelError_template(RANK, PREC) 302 | function maxRelError_${RANK}$_${PREC}$(obtained, reference) result(res) 303 | real(${PREC}$), intent(in) :: obtained${ranksuffix(RANK)}$ 304 | real(${PREC}$), intent(in) :: reference${ranksuffix(RANK)}$ 305 | real(${PREC}$) :: res 306 | 307 | #:if RANK == 0 308 | res = abs((obtained - reference) / reference) 309 | #:else 310 | res = maxval(abs((obtained - reference) / reference)) 311 | #:endif 312 | 313 | end function maxRelError_${RANK}$_${PREC}$ 314 | #:enddef maxRelError_template 315 | 316 | #:for PREC in PRECISIONS 317 | #:for RANK in RANKS 318 | $:maxRelError_template(RANK, PREC) 319 | #:endfor 320 | #:endfor 321 | 322 | end module errorcalc 323 | 324 | ! tests for fypp directives inside Fortran continuation lines 325 | call test(arg1,& 326 | ${a if a > b else b}$, arg3, & 327 | #:if c>d 328 | c,& 329 | #:else 330 | d,& 331 | #:endif 332 | arg4) 333 | 334 | ! test for nested fypp / fortran constructs 335 | ! this is globally consistent even though the logical scopes can not be matched correctly 336 | 337 | #:if do 338 | do x=1,3 339 | ax=x*0.1 340 | #:else 341 | ax=0.1 342 | #:endif 343 | r=ax 344 | #:if do 345 | enddo 346 | #:endif 347 | 348 | r2 = r**2 349 | -------------------------------------------------------------------------------- /fortran_tests/before/test_invalid.f90: -------------------------------------------------------------------------------- 1 | implicit none 2 | private 3 | public :: dp, test_routine, & 4 | test_function, test_type, str_function 5 | integer, parameter :: dp = selected_real_kind ( 15 , 307) 6 | type test_type 7 | real (kind =dp ) :: r = 1.0d-3 8 | integer :: i 9 | end type test_type 10 | 11 | contains 12 | 13 | 14 | subroutine test_routine( & 15 | r, i, j, k, l) 16 | integer, intent(in) :: r, i, j, k 17 | integer, intent (out) :: l 18 | 19 | l = test_function(r,i,j,k) 20 | 21 | pure function test_function(r, i, j, & 22 | k & 23 | result(l) 24 | integer, intent(in) :: r, i, j, k 25 | integer :: l 26 | 27 | l=r + i +j +k 28 | end function 29 | function & 30 | str_function(a) result(l) 31 | character(len=*) :: a 32 | integer :: l 33 | 34 | if(len(a)<5)then 35 | l=0 36 | else 37 | l=1 38 | end function 39 | 40 | end module 41 | 42 | program example_prog 43 | use example, only: dp, test_routine, test_function, test_type,str_function 44 | 45 | implicit none 46 | integer :: r,i,j,k,l,my_integer,m 47 | integer, dimension(5) :: arr 48 | integer, dimension(20) :: big_arr 49 | integer :: endif 50 | type(test_type) :: t 51 | real(kind=dp) :: r1, r2, r3, r4, r5, r6 52 | integer, pointer :: point 53 | 54 | point=> null( ) 55 | 56 | r1=-(r2**i*r3+r5*(-r4 & 57 | - 3)-r6))-2.e+2 58 | r1=-(r2**i*r3+(r5*(-r4 & 59 | - 3)-r6-2.e+2 60 | if( r.eq.2.and.r<=5) i=3 61 | write(*, *)(& 62 | merge(3, 1, i<=2) 63 | write(*, *) test_function(r,i,j , k) 64 | END MODULE 65 | ENDPROGRAM 66 | ENDIF 67 | ENDDO 68 | FUNCTION a(b) 69 | integer :: a 70 | ENDFUNCTION 71 | END SUBROUTINE 72 | 73 | -------------------------------------------------------------------------------- /fortran_tests/before/test_namelist_block_select.f90: -------------------------------------------------------------------------------- 1 | module a_mod 2 | integer :: a_variable, another_variable 3 | 4 | private :: a_variable, & 5 | another_variable 6 | contains 7 | 8 | subroutine test_type(bohoo) 9 | class(*), intent(in) :: bohoo 10 | 11 | select type(bohoo) 12 | type is(real) 13 | write(*,*) 'T' 14 | type is(integer) 15 | write(*,*) 'F' 16 | class default 17 | end select 18 | 19 | return 20 | 21 | end subroutine test_type 22 | 23 | end module a_mod 24 | 25 | program test 26 | use a_mod 27 | 28 | integer :: block_test=2, block = 2 29 | real :: res, factor = 2.81 30 | 31 | namelist/test_nml/block, block_test, res, factor 32 | 33 | block = 5 34 | 35 | block 36 | real :: another_real 37 | another_real = 4.5 38 | end block 39 | 40 | call test_type(block) 41 | 42 | block ! have more vars 43 | real :: block 44 | call test_type(block) 45 | end block 46 | 47 | block = block*5/block_test+1 48 | ! whitespace 2 49 | ! res = factor*5/block_test + 1 50 | res = factor*5/block_test + 1 51 | ! whitespace 3 52 | ! res = factor * 5 / block_test + 1 53 | res = factor * 5 / block_test + 1 54 | 55 | stop 56 | 57 | end program test 58 | -------------------------------------------------------------------------------- /fortran_tests/before/where_forall.f90: -------------------------------------------------------------------------------- 1 | ! Forall-construct 2 | 3 | ! Example 1 4 | 5 | forall(I = 3:N + 1, J = 3:N + 1) 6 | C(I, J) = C(I, J + 2) + C(I, J - 2) + C(I + 2, J) + C(I - 2, J) 7 | D(I, J) = C(I, J) 8 | end forall 9 | 10 | ! Example 2 11 | 12 | forall(I = 3:N + 1, J = 3:N + 1) 13 | C(I, J) = C(I, J + 2) + C(I, J - 2) + C(I + 2, J) + C(I - 2, J) 14 | D(I, J) = C(I, J) 15 | end forall 16 | 17 | ! Example 3 18 | 19 | forall(I = 3:N + 1, J = 3:N + 1) 20 | C(I, J) = C(I, J + 2) + C(I, J - 2) + C(I + 2, J) + C(I - 2, J) 21 | D(I, J) = C(I, J) 22 | end forall 23 | 24 | ! Where-construct 25 | 26 | ! Example 1 27 | 28 | where (C/=0) 29 | A=B/C 30 | elsewhere 31 | A=0.0 32 | end where 33 | 34 | ! Example 2 35 | 36 | where (C/=0) 37 | A=B/C 38 | elsewhere 39 | A=0.0 40 | end where 41 | 42 | ! Example 3 43 | 44 | where (C/=0) 45 | A=B/C 46 | elsewhere 47 | A=0.0 48 | end where 49 | -------------------------------------------------------------------------------- /fprettify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ############################################################################### 4 | # This file is part of fprettify. 5 | # Copyright (C) 2016-2019 Patrick Seewald, CP2K developers group 6 | # 7 | # fprettify is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # fprettify is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with fprettify. If not, see . 19 | ############################################################################### 20 | 21 | """wrapper script to run fprettify""" 22 | 23 | from fprettify import run # pragma: no cover 24 | 25 | if __name__ == '__main__': # pragma: no cover 26 | run() 27 | -------------------------------------------------------------------------------- /fprettify/fparse_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # This file is part of fprettify. 4 | # Copyright (C) 2016-2019 Patrick Seewald, CP2K developers group 5 | # 6 | # fprettify is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # fprettify is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with fprettify. If not, see . 18 | ############################################################################### 19 | 20 | """This is a collection of Fortran parsing utilities.""" 21 | 22 | import re 23 | from collections import deque 24 | 25 | RE_FLAGS = re.IGNORECASE | re.UNICODE 26 | 27 | # FIXME bad ass regex! 28 | VAR_DECL_RE = re.compile( 29 | r"^ *(?Pinteger(?: *\* *[0-9]+)?|logical|character(?: *\* *[0-9]+)?|real(?: *\* *[0-9]+)?|complex(?: *\* *[0-9]+)?|type) *(?P\((?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*\))? *(?P(?: *, *[a-zA-Z_0-9]+(?: *\((?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*\))?)+)? *(?P::)?(?P[^\n]+)\n?", RE_FLAGS) 30 | 31 | OMP_COND_RE = re.compile(r"^\s*(!\$ )", RE_FLAGS) 32 | OMP_DIR_RE = re.compile(r"^\s*(!\$OMP)", RE_FLAGS) 33 | 34 | # supported preprocessors 35 | FYPP_LINE_STR = r"^(#!|#:|\$:|@:)" 36 | FYPP_WITHOUT_PREPRO_STR = r"^(#!|\$:|@:)" 37 | CPP_STR = r"^#[^!:{}]" 38 | COMMENT_LINE_STR = r"^!" 39 | FYPP_OPEN_STR = r"(#{|\${|@{)" 40 | FYPP_CLOSE_STR = r"(}#|}\$|}@)" 41 | NOTFORTRAN_LINE_RE = re.compile(r"("+FYPP_LINE_STR+r"|"+CPP_STR+r"|"+COMMENT_LINE_STR+r")", RE_FLAGS) 42 | NOTFORTRAN_FYPP_LINE_RE = re.compile(r"("+CPP_STR+r"|"+COMMENT_LINE_STR+r")", RE_FLAGS) 43 | FYPP_LINE_RE = re.compile(FYPP_LINE_STR, RE_FLAGS) 44 | FYPP_WITHOUT_PREPRO_RE = re.compile(FYPP_WITHOUT_PREPRO_STR, RE_FLAGS) 45 | FYPP_OPEN_RE = re.compile(FYPP_OPEN_STR, RE_FLAGS) 46 | FYPP_CLOSE_RE = re.compile(FYPP_CLOSE_STR, RE_FLAGS) 47 | 48 | STR_OPEN_RE = re.compile(r"("+FYPP_OPEN_STR+r"|"+r"'|\"|!)", RE_FLAGS) 49 | CPP_RE = re.compile(CPP_STR, RE_FLAGS) 50 | 51 | class fline_parser(object): 52 | def __init__(self): 53 | pass 54 | def search(self, line): 55 | pass 56 | 57 | class parser_re(fline_parser): 58 | def __init__(self, regex, spec=True): 59 | self._re = regex 60 | self.spec = spec 61 | 62 | def search(self, line): 63 | return self._re.search(line) 64 | 65 | def split(self, line): 66 | return self._re.split(line) 67 | 68 | class FprettifyException(Exception): 69 | """Base class for all custom exceptions""" 70 | 71 | def __init__(self, msg, filename, line_nr): 72 | super(FprettifyException, self).__init__(msg) 73 | self.filename = filename 74 | self.line_nr = line_nr 75 | 76 | 77 | class FprettifyParseException(FprettifyException): 78 | """Exception for unparseable Fortran code (user's fault).""" 79 | 80 | pass 81 | 82 | 83 | class FprettifyInternalException(FprettifyException): 84 | """Exception for potential internal errors (fixme's).""" 85 | 86 | pass 87 | 88 | 89 | class CharFilter(object): 90 | """ 91 | An iterator to wrap the iterator returned by `enumerate(string)` 92 | and ignore comments and characters inside strings 93 | """ 94 | 95 | def __init__(self, string, filter_comments=True, filter_strings=True, 96 | filter_fypp=True): 97 | self._content = string 98 | self._it = enumerate(self._content) 99 | self._instring = '' 100 | self._infypp = False 101 | self._incomment = '' 102 | self._instring = '' 103 | self._filter_comments = filter_comments 104 | self._filter_strings = filter_strings 105 | if filter_fypp: 106 | self._notfortran_re = NOTFORTRAN_LINE_RE 107 | else: 108 | self._notfortran_re = NOTFORTRAN_FYPP_LINE_RE 109 | 110 | 111 | def update(self, string, filter_comments=True, filter_strings=True, 112 | filter_fypp=True): 113 | self._content = string 114 | self._it = enumerate(self._content) 115 | self._filter_comments = filter_comments 116 | self._filter_strings = filter_strings 117 | if filter_fypp: 118 | self._notfortran_re = NOTFORTRAN_LINE_RE 119 | else: 120 | self._notfortran_re = NOTFORTRAN_FYPP_LINE_RE 121 | 122 | def __iter__(self): 123 | return self 124 | 125 | def __next__(self): 126 | 127 | pos, char = next(self._it) 128 | 129 | char2 = self._content[pos:pos+2] 130 | 131 | if not self._instring: 132 | if not self._incomment: 133 | if FYPP_OPEN_RE.search(char2): 134 | self._instring = char2 135 | self._infypp = True 136 | elif (self._notfortran_re.search(char2)): 137 | self._incomment = char 138 | elif char in ['"', "'"]: 139 | self._instring = char 140 | else: 141 | if self._infypp: 142 | if FYPP_CLOSE_RE.search(char2): 143 | self._instring = '' 144 | self._infypp = False 145 | if self._filter_strings: 146 | self.__next__() 147 | return self.__next__() 148 | 149 | elif char in ['"', "'"]: 150 | if self._instring == char: 151 | self._instring = '' 152 | if self._filter_strings: 153 | return self.__next__() 154 | 155 | if self._filter_comments: 156 | if self._incomment: 157 | raise StopIteration 158 | 159 | if self._filter_strings: 160 | if self._instring: 161 | return self.__next__() 162 | 163 | return (pos, char) 164 | 165 | def filter_all(self): 166 | filtered_str = '' 167 | for pos, char in self: 168 | filtered_str += char 169 | return filtered_str 170 | 171 | def instring(self): 172 | return self._instring 173 | 174 | class InputStream(object): 175 | """Class to read logical Fortran lines from a Fortran file.""" 176 | 177 | def __init__(self, infile, filter_fypp=True, orig_filename=None): 178 | if not orig_filename: 179 | orig_filename = infile.name 180 | self.line_buffer = deque([]) 181 | self.infile = infile 182 | self.line_nr = 0 183 | self.filename = orig_filename 184 | self.endpos = deque([]) 185 | self.what_omp = deque([]) 186 | if filter_fypp: 187 | self.notfortran_re = NOTFORTRAN_LINE_RE 188 | else: 189 | self.notfortran_re = NOTFORTRAN_FYPP_LINE_RE 190 | 191 | def next_fortran_line(self): 192 | """Reads a group of connected lines (connected with &, separated by newline or semicolon) 193 | returns a touple with the joined line, and a list with the original lines. 194 | Doesn't support multiline character constants! 195 | """ 196 | joined_line = "" 197 | comments = [] 198 | lines = [] 199 | continuation = 0 200 | fypp_cont = 0 201 | instring = '' 202 | 203 | string_iter = CharFilter('') 204 | fypp_cont = 0 205 | while 1: 206 | if not self.line_buffer: 207 | line = self.infile.readline().replace("\t", 8 * " ") 208 | self.line_nr += 1 209 | # convert OMP-conditional fortran statements into normal fortran statements 210 | # but remember to convert them back 211 | 212 | what_omp = OMP_COND_RE.search(line) 213 | 214 | if what_omp: 215 | what_omp = what_omp.group(1) 216 | else: 217 | what_omp = '' 218 | 219 | if what_omp: 220 | line = line.replace(what_omp, '', 1) 221 | line_start = 0 222 | 223 | pos = -1 224 | 225 | # multiline string: prepend line continuation with '&' 226 | if string_iter.instring() and not line.lstrip().startswith('&'): 227 | line = '&' + line 228 | 229 | # update instead of CharFilter(line) to account for multiline strings 230 | string_iter.update(line) 231 | for pos, char in string_iter: 232 | if char == ';' or pos + 1 == len(line): 233 | self.endpos.append(pos - line_start) 234 | self.line_buffer.append(line[line_start:pos + 1]) 235 | self.what_omp.append(what_omp) 236 | what_omp = '' 237 | line_start = pos + 1 238 | 239 | if pos + 1 < len(line): 240 | if fypp_cont: 241 | self.endpos.append(-1) 242 | self.line_buffer.append(line) 243 | self.what_omp.append(what_omp) 244 | else: 245 | for pos_add, char in CharFilter(line[pos+1:], filter_comments=False): 246 | char2 = line[pos+1+pos_add:pos+3+pos_add] 247 | if self.notfortran_re.search(char2): 248 | self.endpos.append(pos + pos_add - line_start) 249 | self.line_buffer.append(line[line_start:]) 250 | self.what_omp.append(what_omp) 251 | break 252 | 253 | if not self.line_buffer: 254 | self.endpos.append(len(line)) 255 | self.line_buffer.append(line) 256 | self.what_omp.append('') 257 | 258 | 259 | line = self.line_buffer.popleft() 260 | endpos = self.endpos.popleft() 261 | what_omp = self.what_omp.popleft() 262 | 263 | if not line: 264 | break 265 | 266 | lines.append(what_omp + line) 267 | 268 | line_core = line[:endpos + 1] 269 | 270 | if self.notfortran_re.search(line[endpos+1:endpos+3]) or fypp_cont: 271 | line_comments = line[endpos + 1:] 272 | else: 273 | line_comments = '' 274 | 275 | if line_core: 276 | newline = (line_core[-1] == '\n') 277 | else: 278 | newline = False 279 | 280 | line_core = line_core.strip() 281 | 282 | if line_core and not NOTFORTRAN_LINE_RE.search(line_core): 283 | continuation = 0 284 | if line_core.endswith('&'): 285 | continuation = 1 286 | 287 | if line_comments: 288 | if (FYPP_LINE_RE.search(line[endpos+1:endpos+3]) or fypp_cont) and line_comments.strip()[-1] == '&': 289 | fypp_cont = 1 290 | else: 291 | fypp_cont = 0 292 | 293 | line_core = line_core.strip('&') 294 | 295 | comments.append(line_comments.rstrip('\n')) 296 | if joined_line.strip(): 297 | joined_line = joined_line.rstrip( 298 | '\n') + line_core + '\n' * newline 299 | else: 300 | joined_line = what_omp + line_core + '\n' * newline 301 | 302 | if not (continuation or fypp_cont): 303 | break 304 | 305 | return (joined_line, comments, lines) 306 | -------------------------------------------------------------------------------- /fprettify/tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ############################################################################### 4 | # This file is part of fprettify. 5 | # Copyright (C) 2016-2019 Patrick Seewald, CP2K developers group 6 | # 7 | # fprettify is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # fprettify is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with fprettify. If not, see . 19 | ############################################################################### 20 | 21 | """Dynamically create tests based on examples in examples/before.""" 22 | from __future__ import (absolute_import, division, 23 | print_function, unicode_literals) 24 | 25 | import sys 26 | import os 27 | import unittest 28 | import hashlib 29 | import logging 30 | import io 31 | import re 32 | import difflib 33 | import subprocess 34 | import inspect 35 | 36 | sys.stderr = io.TextIOWrapper( 37 | sys.stderr.detach(), encoding='UTF-8', line_buffering=True) 38 | 39 | import fprettify 40 | from fprettify.fparse_utils import FprettifyParseException, FprettifyInternalException 41 | 42 | 43 | def joinpath(path1, path2): 44 | return os.path.normpath(os.path.join(path1, path2)) 45 | 46 | MYPATH = os.path.dirname(os.path.abspath( 47 | inspect.getfile(inspect.currentframe()))) 48 | 49 | BEFORE_DIR = joinpath(MYPATH, r'../../fortran_tests/before/') 50 | AFTER_DIR = joinpath(MYPATH, r'../../fortran_tests/after/') 51 | RESULT_DIR = joinpath(MYPATH, r'../../fortran_tests/test_results/') 52 | RESULT_FILE = joinpath(RESULT_DIR, r'expected_results') 53 | FAILED_FILE = joinpath(RESULT_DIR, r'failed_results') 54 | 55 | RUNSCRIPT = joinpath(MYPATH, r"../../fprettify.py") 56 | 57 | fprettify.set_fprettify_logger(logging.ERROR) 58 | 59 | 60 | class AlienInvasion(Exception): 61 | """Should not happen""" 62 | pass 63 | 64 | 65 | def eprint(*args, **kwargs): 66 | """ 67 | Print to stderr - to print output compatible with default unittest output. 68 | """ 69 | 70 | print(*args, file=sys.stderr, flush=True, **kwargs) 71 | 72 | class FPrettifyTestCase(unittest.TestCase): 73 | """ 74 | test class to be recognized by unittest. 75 | """ 76 | 77 | def shortDescription(self): 78 | """don't print doc string of testmethod""" 79 | return None 80 | 81 | def setUp(self): 82 | """ 83 | setUp to be recognized by unittest. 84 | We have large files to compare, raise the limit 85 | """ 86 | self.maxDiff = None 87 | 88 | @classmethod 89 | def setUpClass(cls): 90 | """ 91 | setUpClass to be recognized by unittest. 92 | """ 93 | 94 | cls.n_success = 0 95 | cls.n_parsefail = 0 96 | cls.n_internalfail = 0 97 | cls.n_unexpectedfail = 0 98 | 99 | eprint("-" * 70) 100 | eprint("recognized Fortran files") 101 | eprint(", ".join(fprettify.FORTRAN_EXTENSIONS)) 102 | eprint("-" * 70) 103 | eprint("Testing with Fortran files in " + BEFORE_DIR) 104 | eprint("Writing formatted Fortran files to " + AFTER_DIR) 105 | eprint("Storing expected results in " + RESULT_FILE) 106 | eprint("Storing failed results in " + FAILED_FILE) 107 | eprint("-" * 70) 108 | 109 | @classmethod 110 | def tearDownClass(cls): 111 | """ 112 | tearDownClass to be recognized by unittest. Used for test summary 113 | output. 114 | """ 115 | if cls.n_parsefail + cls.n_internalfail > 0: 116 | format = "{:<20}{:<6}" 117 | eprint('\n' + "=" * 70) 118 | eprint("IGNORED errors: invalid or old Fortran") 119 | eprint("-" * 70) 120 | eprint(format.format("parse errors: ", cls.n_parsefail)) 121 | eprint(format.format("internal errors: ", cls.n_internalfail)) 122 | 123 | @staticmethod 124 | def write_result(filename, content, sep_str): # pragma: no cover 125 | with io.open(filename, 'a', encoding='utf-8') as outfile: 126 | outfile.write(sep_str.join(content) + '\n') 127 | 128 | def test_whitespace(self): 129 | """simple test for whitespace formatting options -w in [0, 1, 2]""" 130 | instring = "(/-a-b-(a+b-c)/(-c)*d**e,f[1]%v/)" 131 | outstring_exp = ["(/-a-b-(a+b-c)/(-c)*d**e,f[1]%v/)", 132 | "(/-a-b-(a+b-c)/(-c)*d**e, f[1]%v/)", 133 | "(/-a - b - (a + b - c)/(-c)*d**e, f[1]%v/)", 134 | "(/-a - b - (a + b - c) / (-c) * d**e, f[1]%v/)"] 135 | 136 | outstring = [] 137 | for w, out in zip(range(0, 4), outstring_exp): 138 | args = ['-w', str(w)] 139 | self.assert_fprettify_result(args, instring, out) 140 | 141 | def test_type_selector(self): 142 | """test for whitespace formatting option -w 4""" 143 | instring = "A%component=func(mytype%a,mytype%abc+mytype%abcd)" 144 | outstring_exp = "A % component = func(mytype % a, mytype % abc + mytype % abcd)" 145 | 146 | self.assert_fprettify_result(['-w 4'], instring, outstring_exp) 147 | 148 | def test_indent(self): 149 | """simple test for indent options -i in [0, 3, 4]""" 150 | 151 | indents = [0, 3, 4] 152 | 153 | instring = "iF(teSt)ThEn\nCaLl subr(a,b,&\nc,(/d,&\ne,f/))\nEnD iF" 154 | outstring_exp = [ 155 | "iF (teSt) ThEn\n" + 156 | " " * ind + "CaLl subr(a, b, &\n" + 157 | " " * (10 + ind) + "c, (/d, &\n" + 158 | " " * (15 + ind) + "e, f/))\nEnD iF" 159 | for ind in indents 160 | ] 161 | 162 | for ind, out in zip(indents, outstring_exp): 163 | args = ['-i', str(ind)] 164 | self.assert_fprettify_result(args, instring, out) 165 | 166 | def test_nested(self): 167 | """test correct indentation of nested loops""" 168 | instring = ("integer :: i,j\ndo i=1,2\ndo j=1,3\n" 169 | "print*,i,j,i*j\nend do\nend do") 170 | outstring_exp_default = ("integer :: i, j\ndo i = 1, 2\ndo j = 1, 3\n" 171 | " print *, i, j, i*j\nend do\nend do") 172 | outstring_exp_strict = ("integer :: i, j\ndo i = 1, 2\n do j = 1, 3\n" 173 | " print *, i, j, i*j\n end do\nend do") 174 | 175 | self.assert_fprettify_result([], instring, outstring_exp_default) 176 | self.assert_fprettify_result(['--strict-indent'], instring, outstring_exp_strict) 177 | 178 | def test_reset_indent(self): 179 | """test of reset indentation at file start""" 180 | instring = ("integer :: i,j\ndo i=1,2\ndo j=1,3\n" 181 | "print*,i,j,i*j\nend do\nend do", 182 | " module a\ninteger :: 1\n") 183 | outstring = ("integer :: i, j\ndo i = 1, 2\ndo j = 1, 3\n" 184 | " print *, i, j, i*j\nend do\nend do", 185 | "module a\n integer :: 1") 186 | 187 | for ind, out in zip(instring, outstring): 188 | self.assert_fprettify_result([],ind, out) 189 | 190 | def test_disable(self): 191 | """test disabling indentation and/or whitespace formatting""" 192 | instring = ("if(&\nl==111)&\n then\n do m =1, 2\n A=&\nB+C\n end do; endif") 193 | outstring_exp_default = ("if ( &\n l == 111) &\n then\n do m = 1, 2\n" 194 | " A = &\n B + C\n end do; end if") 195 | outstring_exp_nowhitespace = ("if(&\n l==111)&\n then\n do m =1, 2\n" 196 | " A=&\n B+C\n end do; endif") 197 | outstring_exp_noindent = ("if ( &\nl == 111) &\n then\n do m = 1, 2\n" 198 | " A = &\nB + C\n end do; end if") 199 | 200 | self.assert_fprettify_result([], instring, outstring_exp_default) 201 | self.assert_fprettify_result(['--disable-whitespace'], instring, outstring_exp_nowhitespace) 202 | self.assert_fprettify_result(['--disable-indent'], instring, outstring_exp_noindent) 203 | self.assert_fprettify_result(['--disable-indent', '--disable-whitespace'], instring, instring) 204 | 205 | def test_comments(self): 206 | """test options related to comments""" 207 | instring = ("TYPE mytype\n! c1\n !c2\n INTEGER :: a ! c3\n" 208 | " REAL :: b, & ! c4\n! c5\n ! c6\n" 209 | " d ! c7\nEND TYPE ! c8") 210 | outstring_exp_default = ("TYPE mytype\n! c1\n !c2\n INTEGER :: a ! c3\n" 211 | " REAL :: b, & ! c4\n ! c5\n ! c6\n" 212 | " d ! c7\nEND TYPE ! c8") 213 | outstring_exp_strip = ("TYPE mytype\n! c1\n !c2\n INTEGER :: a ! c3\n" 214 | " REAL :: b, & ! c4\n ! c5\n ! c6\n" 215 | " d ! c7\nEND TYPE ! c8") 216 | 217 | self.assert_fprettify_result([], instring, outstring_exp_default) 218 | self.assert_fprettify_result(['--strip-comments'], instring, outstring_exp_strip) 219 | 220 | def test_directive(self): 221 | """ 222 | test deactivate directives '!&' (inline) and '!&<', '!&>' (block) 223 | and manual alignment (continuation line prefixed with '&') 224 | """ 225 | 226 | # manual alignment 227 | instring = "align_me = [ -1, 10,0, &\n & 0,1000 , 0,&\n &0 , -1, 1]" 228 | outstring_exp = "align_me = [-1, 10, 0, &\n & 0, 1000, 0,&\n &0, -1, 1]" 229 | self.assert_fprettify_result([], instring, outstring_exp) 230 | 231 | # inline deactivate 232 | instring2 = '\n'.join(_ + ' !&' for _ in instring.splitlines()) 233 | outstring_exp = instring2 234 | self.assert_fprettify_result([], instring2, outstring_exp) 235 | 236 | # block deactivate 237 | instring3 = '!&<\n' + instring + '\n!&>' 238 | outstring_exp = instring3 239 | self.assert_fprettify_result([], instring3, outstring_exp) 240 | 241 | def assert_fprettify_result(self, args, instring, outstring_exp): 242 | """ 243 | assert that result of calling fprettify with args on instring gives 244 | outstring_exp 245 | """ 246 | args.insert(0, RUNSCRIPT) 247 | p1 = subprocess.Popen( 248 | args, stdout=subprocess.PIPE, stdin=subprocess.PIPE) 249 | outstring = p1.communicate(instring.encode( 250 | 'UTF-8'))[0].decode('UTF-8').rstrip() 251 | self.assertEqual(outstring_exp.rstrip(), outstring) 252 | 253 | def test_io(self): 254 | """simple test for io (file inplace, stdin & stdout)""" 255 | 256 | # io and unicode 257 | outstring = [] 258 | instring = "CALL alien_invasion( 👽 )" 259 | outstring_exp = "CALL alien_invasion(👽)" 260 | 261 | alien_file = "alien_invasion.f90" 262 | if os.path.isfile(alien_file): 263 | raise AlienInvasion( 264 | "remove file alien_invasion.f90") # pragma: no cover 265 | 266 | try: 267 | with io.open(alien_file, 'w', encoding='utf-8') as infile: 268 | infile.write(instring) 269 | 270 | # testing stdin --> stdout 271 | p1 = subprocess.Popen(RUNSCRIPT, 272 | stdout=subprocess.PIPE, stdin=subprocess.PIPE) 273 | outstring.append(p1.communicate( 274 | instring.encode('UTF-8'))[0].decode('UTF-8')) 275 | 276 | # testing file --> stdout 277 | p1 = subprocess.Popen([RUNSCRIPT, alien_file, '--stdout'], 278 | stdout=subprocess.PIPE) 279 | outstring.append(p1.communicate( 280 | instring.encode('UTF-8')[0])[0].decode('UTF-8')) 281 | 282 | # testing file --> file (inplace) 283 | p1 = subprocess.Popen([RUNSCRIPT, alien_file]) 284 | p1.wait() 285 | 286 | with io.open(alien_file, 'r', encoding='utf-8') as infile: 287 | outstring.append(infile.read()) 288 | 289 | for outstr in outstring: 290 | self.assertEqual(outstring_exp, outstr.strip()) 291 | except: # pragma: no cover 292 | if os.path.isfile(alien_file): 293 | os.remove(alien_file) 294 | raise 295 | else: 296 | os.remove(alien_file) 297 | 298 | def test_multi_alias(self): 299 | """test for issue #11 (multiple alias and alignment)""" 300 | instring="use A,only:B=>C,&\nD=>E" 301 | outstring="use A, only: B => C, &\n D => E" 302 | self.assert_fprettify_result([], instring, outstring) 303 | 304 | def test_use(self): 305 | """test for alignment of use statements""" 306 | instring1="use A,only:B,C,&\nD,E" 307 | instring2="use A,only:&\nB,C,D,E" 308 | outstring1="use A, only: B, C, &\n D, E" 309 | outstring2="use A, only: &\n B, C, D, E" 310 | self.assert_fprettify_result([], instring1, outstring1) 311 | self.assert_fprettify_result([], instring2, outstring2) 312 | 313 | def test_wrongkind(self): 314 | """test whitespacing of deprecated kind definition""" 315 | instring = ["REAL*8 :: r, f ! some reals", 316 | "REAL * 8 :: r, f ! some reals", 317 | "INTEGER * 4 :: c, i ! some integers", 318 | "INTEGER*4 :: c, i ! some integers"] 319 | outstring = ["REAL*8 :: r, f ! some reals", 320 | "REAL*8 :: r, f ! some reals", 321 | "INTEGER*4 :: c, i ! some integers", 322 | "INTEGER*4 :: c, i ! some integers"] 323 | 324 | for i in range(0, len(instring)): 325 | self.assert_fprettify_result([], instring[i], outstring[i]) 326 | 327 | def test_new_intrinsics(self): 328 | """test new I/O intrinsics""" 329 | instring = ["REWIND(12)", 330 | "BACKSPACE(13)", 331 | "INQUIRE(14)"] 332 | outstring = ["REWIND (12)", 333 | "BACKSPACE (13)", 334 | "INQUIRE (14)"] 335 | 336 | for i in range(0, len(instring)): 337 | self.assert_fprettify_result([], instring[i], outstring[i]) 338 | 339 | def test_associate(self): 340 | """test correct formatting of associate construct""" 341 | instring = ("associate(a=>b , c =>d ,e=> f )\n" 342 | "e=a+c\n" 343 | "end associate") 344 | outstring = ("associate (a => b, c => d, e => f)\n" 345 | " e = a + c\n" 346 | "end associate") 347 | 348 | self.assert_fprettify_result([], instring, outstring) 349 | 350 | def test_line_length(self): 351 | """test line length option""" 352 | instring = ["REAL(KIND=4) :: r,f ! some reals", 353 | "if( min == max.and.min .eq. thres )", 354 | "INQUIRE(14)"] 355 | instring_ = "if( min == max.and.min .eq. thres ) one_really_long_function_call_to_hit_the_line_limit(parameter1, parameter2,parameter3,parameter4,parameter5,err) ! this line would be too long" 356 | outstring = ["REAL(KIND=4) :: r, f ! some reals", 357 | "REAL(KIND=4) :: r,f ! some reals", 358 | "if (min == max .and. min .eq. thres)", 359 | "if( min == max.and.min .eq. thres )", 360 | "INQUIRE (14)", 361 | "INQUIRE (14)"] 362 | outstring_ = ["if( min == max.and.min .eq. thres ) one_really_long_function_call_to_hit_the_line_limit(parameter1, parameter2,parameter3,parameter4,parameter5,err) ! this line would be too long", 363 | "if (min == max .and. min .eq. thres) one_really_long_function_call_to_hit_the_line_limit(parameter1, parameter2, parameter3, parameter4, parameter5, err) ! this line would be too long"] 364 | 365 | # test shorter lines first, after all the actual length doesn't matter 366 | for i in range(0, len(instring)): 367 | self.assert_fprettify_result(['-S'], instring[i], outstring[2*i]) 368 | self.assert_fprettify_result(['-S', '-l 20'], instring[i], outstring[2*i + 1]) 369 | # now test a long line 370 | self.assert_fprettify_result(['-S'], instring_, outstring_[0]) 371 | self.assert_fprettify_result(['-S', '-l 0'], instring_, outstring_[1]) 372 | 373 | def test_relation_replacement(self): 374 | """test replacement of relational statements""" 375 | instring = ["if ( min < max .and. min .lt. thres)", 376 | "if (min > max .and. min .gt. thres )", 377 | "if ( min == max .and. min .eq. thres )", 378 | "if(min /= max .and. min .ne. thres)", 379 | "if(min >= max .and. min .ge. thres )", 380 | "if( min <= max .and. min .le. thres)", 381 | "'==== heading", 382 | "if (vtk%my_rank .eq. 0) write (vtk%filehandle_par, '(\"\",", 384 | "if (abc(1) .lt. -bca .or. &\n qwe .gt. ewq) then"] 385 | f_outstring = ["if (min .lt. max .and. min .lt. thres)", 386 | "if (min .gt. max .and. min .gt. thres)", 387 | "if (min .eq. max .and. min .eq. thres)", 388 | "if (min .ne. max .and. min .ne. thres)", 389 | "if (min .ge. max .and. min .ge. thres)", 390 | "if (min .le. max .and. min .le. thres)", 391 | "'==== heading", 392 | "if (vtk%my_rank .eq. 0) write (vtk%filehandle_par, '(\"\",", 394 | "if (abc(1) .lt. -bca .or. &\n qwe .gt. ewq) then"] 395 | c_outstring = ["if (min < max .and. min < thres)", 396 | "if (min > max .and. min > thres)", 397 | "if (min == max .and. min == thres)", 398 | "if (min /= max .and. min /= thres)", 399 | "if (min >= max .and. min >= thres)", 400 | "if (min <= max .and. min <= thres)", 401 | "'==== heading", 402 | "if (vtk%my_rank == 0) write (vtk%filehandle_par, '(\"\",", 404 | "if (abc(1) < -bca .or. &\n qwe > ewq) then"] 405 | for i in range(0, len(instring)): 406 | self.assert_fprettify_result(['--enable-replacements', '--c-relations'], instring[i], c_outstring[i]) 407 | self.assert_fprettify_result(['--enable-replacements'], instring[i], f_outstring[i]) 408 | 409 | def test_swap_case(self): 410 | """test replacement of keyword character case""" 411 | instring = ( 412 | "MODULE exAmple", 413 | "INTEGER, PARAMETER :: SELECTED_REAL_KIND = 1*2", 414 | "INTEGER, PARAMETER :: dp1 = SELECTED_REAL_KIND ( 15 , 307)", 415 | 'CHARACTER(LEN=*), PARAMETER :: a = "INTEGER, PARAMETER" // "b"', 416 | "CHARACTER(LEN=*), PARAMETER :: a = 'INTEGER, PARAMETER' // 'b'", 417 | "INTEGER(kind=int64), PARAMETER :: l64 = 2_int64", 418 | "REAL(kind=real64), PARAMETER :: r64a = 2._real64", 419 | "REAL(kind=real64), PARAMETER :: r64b = 2.0_real64", 420 | "REAL(kind=real64), PARAMETER :: r64c = .0_real64", 421 | "REAL(kind=real64), PARAMETER :: r64a = 2.e3_real64", 422 | "REAL(kind=real64), PARAMETER :: r64b = 2.0e3_real64", 423 | "REAL(kind=real64), PARAMETER :: r64c = .0e3_real64", 424 | "REAL, PARAMETER :: r32 = 2.e3", 425 | "REAL, PARAMETER :: r32 = 2.0d3", 426 | "REAL, PARAMETER :: r32 = .2e3", 427 | "USE ISO_FORTRAN_ENV, ONLY: int64", 428 | "INTEGER, INTENT(IN) :: r, i, j, k", 429 | "IF (l.EQ.2) l=MAX (l64, 2_int64)", 430 | "PURE SUBROUTINE mypure()" 431 | ) 432 | outstring = ( 433 | "module exAmple", 434 | "integer, parameter :: SELECTED_REAL_KIND = 1*2", 435 | "integer, parameter :: dp1 = selected_real_kind(15, 307)", 436 | 'character(LEN=*), parameter :: a = "INTEGER, PARAMETER"//"b"', 437 | "character(LEN=*), parameter :: a = 'INTEGER, PARAMETER'//'b'", 438 | "integer(kind=INT64), parameter :: l64 = 2_INT64", 439 | "real(kind=REAL64), parameter :: r64a = 2._REAL64", 440 | "real(kind=REAL64), parameter :: r64b = 2.0_REAL64", 441 | "real(kind=REAL64), parameter :: r64c = .0_REAL64", 442 | "real(kind=REAL64), parameter :: r64a = 2.E3_REAL64", 443 | "real(kind=REAL64), parameter :: r64b = 2.0E3_REAL64", 444 | "real(kind=REAL64), parameter :: r64c = .0E3_REAL64", 445 | "real, parameter :: r32 = 2.E3", 446 | "real, parameter :: r32 = 2.0D3", 447 | "real, parameter :: r32 = .2E3", 448 | "use iso_fortran_env, only: INT64", 449 | "integer, intent(IN) :: r, i, j, k", 450 | "if (l .eq. 2) l = max(l64, 2_INT64)", 451 | "pure subroutine mypure()" 452 | ) 453 | for i in range(len(instring)): 454 | self.assert_fprettify_result(['--case', '1', '1', '1', '2'], 455 | instring[i], outstring[i]) 456 | 457 | def test_do(self): 458 | """test correct parsing of do statement""" 459 | instring = "do = 1\nb = 2" 460 | 461 | self.assert_fprettify_result([], instring, instring) 462 | 463 | def test_omp(self): 464 | """test formatting of omp directives""" 465 | instring = ("PROGRAM test_omp\n" 466 | " !$OMP PARALLEL DO\n" 467 | "b=4\n" 468 | "!$a=b\n" 469 | "!$ a=b\n" 470 | " !$ c=b\n" 471 | "!$acc parallel loop\n" 472 | "!$OMP END PARALLEL DO\n" 473 | "END PROGRAM") 474 | outstring = ("PROGRAM test_omp\n" 475 | "!$OMP PARALLEL DO\n" 476 | " b = 4\n" 477 | "!$a=b\n" 478 | "!$ a = b\n" 479 | "!$ c = b\n" 480 | "!$acc parallel loop\n" 481 | "!$OMP END PARALLEL DO\n" 482 | "END PROGRAM") 483 | 484 | self.assert_fprettify_result([], instring, outstring) 485 | 486 | def test_ford(self): 487 | """test formatting of ford comments""" 488 | instring = (" a = b\n" 489 | " !! ford docu\n" 490 | "b=c\n" 491 | " !! ford docu\n" 492 | "subroutine test(a,b,&\n" 493 | " !! ford docu\n" 494 | " c, d, e)" 495 | ) 496 | outstring = (" a = b\n" 497 | " !! ford docu\n" 498 | " b = c\n" 499 | " !! ford docu\n" 500 | " subroutine test(a, b, &\n" 501 | " !! ford docu\n" 502 | " c, d, e)" 503 | ) 504 | 505 | self.assert_fprettify_result([], instring, outstring) 506 | 507 | def test_plusminus(self): 508 | """test corner cases of +/-""" 509 | instring = "val_1d-1-1.0e-9-2.0d-08+.2e-1-val_2d-3.e-12+4" 510 | outstring = "val_1d - 1 - 1.0e-9 - 2.0d-08 + .2e-1 - val_2d - 3.e-12 + 4" 511 | self.assert_fprettify_result([], instring, outstring) 512 | 513 | def test_fypp(self): 514 | """test formatting of fypp statements""" 515 | 516 | instring = [] 517 | outstring = [] 518 | 519 | instring += [ 520 | """ 521 | #:if DEBUG> 0 522 | print *, "hola" 523 | if( .not. (${cond}$) ) then 524 | #:if ASSERT(cond) 525 | print *, "Assert failed!" 526 | #:endif 527 | error stop 528 | end if 529 | #:endif 530 | """ 531 | ] 532 | 533 | outstring += [ 534 | """ 535 | #:if DEBUG> 0 536 | print *, "hola" 537 | if (.not. (${cond}$)) then 538 | #:if ASSERT(cond) 539 | print *, "Assert failed!" 540 | #:endif 541 | error stop 542 | end if 543 | #:endif 544 | """ 545 | ] 546 | 547 | instring += [ 548 | """ 549 | if (.not. (${cond}$)) then 550 | #:for element in list 551 | print *, "Element is in list!" 552 | #:endfor 553 | error stop 554 | end if 555 | """ 556 | ] 557 | 558 | outstring += [ 559 | """ 560 | if (.not. (${cond}$)) then 561 | #:for element in list 562 | print *, "Element is in list!" 563 | #:endfor 564 | error stop 565 | end if 566 | """ 567 | ] 568 | 569 | instring += [ 570 | """ 571 | #:if aa > 1 572 | print *, "Number is more than 1" 573 | if (condition) then 574 | #:def something 575 | print *, "Added Definition!" 576 | #:enddef 577 | end if 578 | #:endif 579 | """ 580 | ] 581 | 582 | outstring += [ 583 | """ 584 | #:if aa > 1 585 | print *, "Number is more than 1" 586 | if (condition) then 587 | #:def something 588 | print *, "Added Definition!" 589 | #:enddef 590 | end if 591 | #:endif 592 | """ 593 | ] 594 | 595 | instring += [ 596 | """ 597 | #:def DEBUG_CODE( code) 598 | #:if DEBUG > 0 599 | $:code 600 | #:endif 601 | #:enddef DEBUG_CODE 602 | """ 603 | ] 604 | 605 | outstring += [ 606 | """ 607 | #:def DEBUG_CODE( code) 608 | #:if DEBUG > 0 609 | $:code 610 | #:endif 611 | #:enddef DEBUG_CODE 612 | """ 613 | ] 614 | 615 | 616 | instring += [ 617 | """ 618 | #:block DEBUG_CODE 619 | if (a 0 659 | print *, "hola" 660 | if (.not. (${cond}$)) then 661 | #:mute 662 | print *, "Muted" 663 | #:endmute 664 | error stop 665 | end if 666 | #:endif 667 | """ 668 | ] 669 | 670 | outstring += [ 671 | """ 672 | #:if DEBUG > 0 673 | print *, "hola" 674 | if (.not. (${cond}$)) then 675 | #:mute 676 | print *, "Muted" 677 | #:endmute 678 | error stop 679 | end if 680 | #:endif 681 | """ 682 | ] 683 | 684 | instring += [ 685 | """ 686 | program try 687 | #:def mydef 688 | a = & 689 | #:if dothat 690 | b + & 691 | #:else 692 | c + & 693 | #:endif 694 | d 695 | #:enddef 696 | end program 697 | """ 698 | ] 699 | 700 | outstring += [ 701 | """ 702 | program try 703 | #:def mydef 704 | a = & 705 | #:if dothat 706 | b + & 707 | #:else 708 | c + & 709 | #:endif 710 | d 711 | #:enddef 712 | end program 713 | """ 714 | ] 715 | 716 | instring += [ 717 | """ 718 | #:if worktype 719 | ${worktype}$, & 720 | #:else 721 | ${type}$, & 722 | #:endif 723 | DIMENSION(${arr_exp}$), & 724 | POINTER :: work 725 | """ 726 | ] 727 | 728 | outstring += [ 729 | """ 730 | #:if worktype 731 | ${worktype}$, & 732 | #:else 733 | ${type}$, & 734 | #:endif 735 | DIMENSION(${arr_exp}$), & 736 | POINTER :: work 737 | """ 738 | ] 739 | 740 | 741 | 742 | for instr, outstr in zip(instring, outstring): 743 | self.assert_fprettify_result([], instr, outstr) 744 | 745 | def test_mod(self): 746 | """test indentation of module / program""" 747 | instring_mod = "module my_module\nintrinsic none\ncontains\nfunction my_func()\nend\nend module" 748 | instring_prog = "program my_program\nintrinsic none\ncontains\nfunction my_func()\nend\nend program" 749 | 750 | outstring_mod = "module my_module\n intrinsic none\ncontains\n function my_func()\n end\nend module" 751 | outstring_mod_disable = "module my_module\nintrinsic none\ncontains\nfunction my_func()\nend\nend module" 752 | 753 | outstring_prog = "program my_program\n intrinsic none\ncontains\n function my_func()\n end\nend program" 754 | outstring_prog_disable = "program my_program\nintrinsic none\ncontains\nfunction my_func()\nend\nend program" 755 | 756 | self.assert_fprettify_result([], instring_mod, outstring_mod) 757 | self.assert_fprettify_result([], instring_prog, outstring_prog) 758 | 759 | self.assert_fprettify_result(['--disable-indent-mod'], instring_mod, outstring_mod_disable) 760 | self.assert_fprettify_result(['--disable-indent-mod'], instring_prog, outstring_prog_disable) 761 | 762 | def test_decl(self): 763 | """test formatting of declarations""" 764 | instring_1 = "integer :: a" 765 | instring_2 = "integer, dimension(:) :: a" 766 | outstring_1 = "integer :: a" 767 | outstring_2 = "integer, dimension(:) :: a" 768 | outstring_2_min = "integer, dimension(:)::a" 769 | 770 | self.assert_fprettify_result([], instring_1, instring_1) 771 | self.assert_fprettify_result([], instring_2, instring_2) 772 | self.assert_fprettify_result(['--enable-decl'], instring_1, outstring_1) 773 | self.assert_fprettify_result(['--enable-decl'], instring_2, outstring_2) 774 | self.assert_fprettify_result(['--enable-decl', '--whitespace-decl=0'], instring_2, outstring_2_min) 775 | 776 | def test_statement_label(self): 777 | instring = "1003 FORMAT(2(1x, i4), 5x, '-', 5x, '-', 3x, '-', 5x, '-', 5x, '-', 8x, '-', 3x, &\n 1p, 2(1x, d10.3))" 778 | outstring = "1003 FORMAT(2(1x, i4), 5x, '-', 5x, '-', 3x, '-', 5x, '-', 5x, '-', 8x, '-', 3x, &\n 1p, 2(1x, d10.3))" 779 | self.assert_fprettify_result([], instring, outstring) 780 | 781 | instring = "print *, 'hello'\n1003 FORMAT(2(1x, i4), 5x, '-', 5x, '-', 3x, '-', 5x, '-', 5x, '-', 8x, '-', 3x, &\n 1p, 2(1x, d10.3))" 782 | outstring = "print *, 'hello'\n1003 FORMAT(2(1x, i4), 5x, '-', 5x, '-', 3x, '-', 5x, '-', 5x, '-', 8x, '-', 3x, &\n 1p, 2(1x, d10.3))" 783 | self.assert_fprettify_result([], instring, outstring) 784 | 785 | def test_multiline_str(self): 786 | 787 | instring = [] 788 | outstring = [] 789 | 790 | instring += [ 791 | ''' 792 | CHARACTER(len=*), PARAMETER :: serialized_string = & 793 | "qtb_rng_gaussian 1 F T F 0.0000000000000000E+00& 794 | 12.0 12.0 12.0& 795 | 12.0 12.0 12.0& 796 | 12.0 12.0 12.0& 797 | 12.0 12.0 12.0& 798 | 12.0 12.0 12.0& 799 | 12.0 12.0 12.0" 800 | ''' 801 | ] 802 | 803 | outstring += [ 804 | ''' 805 | CHARACTER(len=*), PARAMETER :: serialized_string = & 806 | "qtb_rng_gaussian 1 F T F 0.0000000000000000E+00& 807 | & 12.0 12.0 12.0& 808 | & 12.0 12.0 12.0& 809 | & 12.0 12.0 12.0& 810 | & 12.0 12.0 12.0& 811 | & 12.0 12.0 12.0& 812 | & 12.0 12.0 12.0" 813 | ''' 814 | ] 815 | 816 | instring += [ 817 | ''' 818 | CHARACTER(len=*), PARAMETER :: serialized_string = & 819 | "qtb_rng_gaussian 1 F T F 0.0000000000000000E+00& 820 | & 12.0 12.0 12.0& 821 | & 12.0 12.0 12.0& 822 | & 12.0 12.0 12.0& 823 | & 12.0 12.0 12.0& 824 | & 12.0 12.0 12.0& 825 | & 12.0 12.0 12.0" 826 | ''' 827 | ] 828 | 829 | outstring += [ 830 | ''' 831 | CHARACTER(len=*), PARAMETER :: serialized_string = & 832 | "qtb_rng_gaussian 1 F T F 0.0000000000000000E+00& 833 | & 12.0 12.0 12.0& 834 | & 12.0 12.0 12.0& 835 | & 12.0 12.0 12.0& 836 | & 12.0 12.0 12.0& 837 | & 12.0 12.0 12.0& 838 | & 12.0 12.0 12.0" 839 | ''' 840 | ] 841 | 842 | for instr, outstr in zip(instring, outstring): 843 | self.assert_fprettify_result([], instr, outstr) 844 | 845 | def test_label(self): 846 | instring = \ 847 | """ 848 | MODULE cp_lbfgs 849 | CONTAINS 850 | 20000 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 851 | & 'it = iteration number', /, & 852 | & 'Machine precision =', 1p, d10.3) 853 | 2 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 854 | & 'it = iteration number', /, & 855 | & 'Machine precision =', 1p, d10.3) 856 | 20000 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 857 | 'it = iteration number', /, & 858 | 'Machine precision =', 1p, d10.3) 859 | 2 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 860 | 'it = iteration number', /, & 861 | 'Machine precision =', 1p, d10.3) 862 | END MODULE 863 | """ 864 | 865 | outstring = \ 866 | """ 867 | MODULE cp_lbfgs 868 | CONTAINS 869 | 20000 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 870 | & 'it = iteration number', /, & 871 | & 'Machine precision =', 1p, d10.3) 872 | 2 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 873 | & 'it = iteration number', /, & 874 | & 'Machine precision =', 1p, d10.3) 875 | 20000 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 876 | 'it = iteration number', /, & 877 | 'Machine precision =', 1p, d10.3) 878 | 2 FORMAT('RUNNING THE L-BFGS-B CODE', /, /, & 879 | 'it = iteration number', /, & 880 | 'Machine precision =', 1p, d10.3) 881 | END MODULE 882 | """ 883 | 884 | self.assert_fprettify_result([], instring, outstring) 885 | 886 | 887 | 888 | def addtestmethod(testcase, fpath, ffile): 889 | """add a test method for each example.""" 890 | 891 | def testmethod(testcase): 892 | """this is the test method invoked for each example.""" 893 | 894 | dirpath_before = joinpath(BEFORE_DIR, fpath) 895 | dirpath_after = joinpath(AFTER_DIR, fpath) 896 | if not os.path.exists(dirpath_after): 897 | os.makedirs(dirpath_after) 898 | 899 | example_before = joinpath(dirpath_before, ffile) 900 | example_after = joinpath(dirpath_after, ffile) 901 | 902 | if os.path.isfile(example_after): 903 | os.remove(example_after) 904 | 905 | def test_result(path, info): 906 | return [os.path.relpath(path, BEFORE_DIR), info] 907 | 908 | with io.open(example_before, 'r', encoding='utf-8') as infile: 909 | 910 | outstring = io.StringIO() 911 | 912 | try: 913 | fprettify.reformat_ffile(infile, outstring) 914 | m = hashlib.sha256() 915 | m.update(outstring.getvalue().encode('utf-8')) 916 | 917 | test_info = "checksum" 918 | test_content = test_result(example_before, m.hexdigest()) 919 | 920 | with io.open(example_after, 'w', encoding='utf-8') as outfile: 921 | outfile.write(outstring.getvalue()) 922 | FPrettifyTestCase.n_success += 1 923 | except FprettifyParseException as e: 924 | test_info = "parse error" 925 | fprettify.log_exception(e, test_info) 926 | test_content = test_result(example_before, test_info) 927 | FPrettifyTestCase.n_parsefail += 1 928 | except FprettifyInternalException as e: 929 | test_info = "internal error" 930 | fprettify.log_exception(e, test_info) 931 | test_content = test_result(example_before, test_info) 932 | FPrettifyTestCase.n_internalfail += 1 933 | except: # pragma: no cover 934 | FPrettifyTestCase.n_unexpectedfail += 1 935 | raise 936 | 937 | after_exists = os.path.isfile(example_after) 938 | if after_exists: 939 | with io.open(example_before, 'r', encoding='utf-8') as infile: 940 | before_content = infile.read() 941 | before_nosp = re.sub( 942 | r'\n{3,}', r'\n\n', before_content.lower().replace(' ', '').replace('\t', '')) 943 | 944 | with io.open(example_after, 'r', encoding='utf-8') as outfile: 945 | after_content = outfile.read() 946 | after_nosp = after_content.lower().replace(' ', '') 947 | 948 | testcase.assertMultiLineEqual(before_nosp, after_nosp) 949 | 950 | sep_str = ' : ' 951 | with io.open(RESULT_FILE, 'r', encoding='utf-8') as infile: 952 | found = False 953 | for line in infile: 954 | line_content = line.strip().split(sep_str) 955 | if line_content[0] == test_content[0]: 956 | found = True 957 | eprint(test_info, end=" ") 958 | msg = '{} (old) != {} (new)'.format( 959 | line_content[1], test_content[1]) 960 | if test_info == "checksum" and after_exists and after_content.count('\n') < 10000: 961 | # difflib can not handle large files 962 | result = list(difflib.unified_diff(before_content.splitlines( 963 | True), after_content.splitlines(True), fromfile=test_content[0], tofile=line_content[0])) 964 | msg += '\n' + ''.join(result) 965 | try: 966 | testcase.assertEqual( 967 | line_content[1], test_content[1], msg) 968 | except AssertionError: # pragma: no cover 969 | FPrettifyTestCase.write_result( 970 | FAILED_FILE, test_content, sep_str) 971 | raise 972 | break 973 | 974 | if not found: # pragma: no cover 975 | eprint(test_info + " new", end=" ") 976 | FPrettifyTestCase.write_result(RESULT_FILE, test_content, sep_str) 977 | 978 | # not sure why this even works, using "test something" (with a space) as function name... 979 | # however it gives optimal test output 980 | testmethod.__name__ = ("test " + joinpath(fpath, ffile)) 981 | 982 | setattr(testcase, testmethod.__name__, testmethod) 983 | 984 | # make sure all directories exist 985 | if not os.path.exists(BEFORE_DIR): # pragma: no cover 986 | os.makedirs(BEFORE_DIR) 987 | if not os.path.exists(AFTER_DIR): # pragma: no cover 988 | os.makedirs(AFTER_DIR) 989 | if not os.path.exists(RESULT_DIR): # pragma: no cover 990 | os.makedirs(RESULT_DIR) 991 | if not os.path.exists(RESULT_FILE): # pragma: no cover 992 | io.open(RESULT_FILE, 'w', encoding='utf-8').close() 993 | if os.path.exists(FAILED_FILE): # pragma: no cover 994 | # erase failures from previous testers 995 | io.open(FAILED_FILE, 'w', encoding='utf-8').close() 996 | 997 | # this prepares FPrettifyTestCase class when module is loaded by unittest 998 | for dirpath, _, filenames in os.walk(BEFORE_DIR): 999 | for example in [f for f in filenames if any(f.endswith(_) for _ in fprettify.FORTRAN_EXTENSIONS)]: 1000 | rel_dirpath = os.path.relpath(dirpath, start=BEFORE_DIR) 1001 | addtestmethod(FPrettifyTestCase, rel_dirpath, example) 1002 | -------------------------------------------------------------------------------- /fprettify/version.py: -------------------------------------------------------------------------------- 1 | try: 2 | from importlib.metadata import PackageNotFoundError, version 3 | except ModuleNotFoundError: 4 | from importlib_metadata import PackageNotFoundError, version 5 | try: 6 | __version__ = version(__package__) 7 | except PackageNotFoundError: 8 | from setuptools_scm import get_version 9 | 10 | __version__ = get_version(root="..", relative_to=__file__) 11 | -------------------------------------------------------------------------------- /hooks.yml: -------------------------------------------------------------------------------- 1 | .pre-commit-hooks.yaml -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools >= 45", 4 | "wheel", 5 | "setuptools_scm[toml] >= 6.2", 6 | "setuptools_scm_git_archive", 7 | ] 8 | build-backend = "setuptools.build_meta" 9 | 10 | [tool.setuptools_scm] 11 | write_to = "fprettify/_version.py" 12 | 13 | [tool.isort] 14 | profile = "black" 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | configargparse 3 | -------------------------------------------------------------------------------- /run_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ############################################################################### 4 | # This file is part of fprettify. 5 | # Copyright (C) 2016-2019 Patrick Seewald, CP2K developers group 6 | # 7 | # fprettify is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # fprettify is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with fprettify. If not, see . 19 | ############################################################################### 20 | 21 | import unittest 22 | from fprettify.tests import FPrettifyTestCase, FAILED_FILE, RESULT_FILE 23 | import fileinput 24 | import io 25 | import os 26 | import sys 27 | import argparse 28 | 29 | if __name__ == '__main__': 30 | parser = argparse.ArgumentParser( 31 | description='Run tests', formatter_class=argparse.ArgumentDefaultsHelpFormatter) 32 | parser.add_argument("-r", "--reset", action='store_true', default=False, 33 | help="Reset test results to new results of failed tests") 34 | 35 | args = parser.parse_args() 36 | 37 | suite = unittest.TestLoader().loadTestsFromTestCase(FPrettifyTestCase) 38 | unittest.TextTestRunner(verbosity=2).run(suite) 39 | 40 | if args.reset and os.path.isfile(FAILED_FILE): 41 | sep_str = ' : ' 42 | with io.open(FAILED_FILE, 'r', encoding='utf-8') as infile: 43 | for failed_line in infile: 44 | failed_content = failed_line.strip().split(sep_str) 45 | for result_line in fileinput.input(RESULT_FILE, inplace=True): 46 | result_content = result_line.strip().split(sep_str) 47 | if result_content[0] == failed_content[0]: 48 | sys.stdout.write(failed_line) 49 | else: 50 | sys.stdout.write(result_line) 51 | 52 | os.remove(FAILED_FILE) 53 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = fprettify 3 | url = https://github.com/pseewald/fprettify 4 | author = Patrick Seewald 5 | author_email = patrick.seewald@gmail.com 6 | description = auto-formatter for modern fortran source code 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | license = GPLv3 10 | classifiers = 11 | Development Status :: 5 - Production/Stable 12 | Intended Audience :: Developers 13 | Topic :: Software Development :: Quality Assurance 14 | License :: OSI Approved :: GNU General Public License v3 (GPLv3) 15 | Programming Language :: Python :: 3 16 | Programming Language :: Python :: 3.6 17 | Programming Language :: Python :: 3.7 18 | Programming Language :: Python :: 3.8 19 | Programming Language :: Python :: 3.9 20 | Environment :: Console 21 | Operating System :: OS Independent 22 | keywords = 23 | fortran 24 | formatter 25 | project_urls = 26 | Tracker = https://github.com/pseewald/fprettify/issues 27 | Source Code = https://github.com/pseewald/fprettify 28 | 29 | [options] 30 | packages = find: 31 | python_requires = >= 3.6 32 | install_requires = 33 | configargparse 34 | importlib-metadata; python_version < "3.8" 35 | 36 | [options.entry_points] 37 | console_scripts = 38 | fprettify = fprettify.__init__:run 39 | 40 | [options.extras_require] 41 | dev = 42 | black 43 | isort 44 | pre-commit 45 | coveralls 46 | 47 | [flake8] 48 | max-line-length = 88 49 | extend-ignore = E203, E722 50 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup() 5 | --------------------------------------------------------------------------------