diff --git a/.gitmodules b/.gitmodules
index c60caa6343..0204c8468f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,3 +2,7 @@
path = exts/dbcsr
url = https://github.com/cp2k/dbcsr
branch = master
+[submodule "tools/prettify/fprettify"]
+ path = tools/prettify/fprettify
+ url = https://github.com/pseewald/fprettify.git
+ branch = master
diff --git a/tools/prettify/fprettify b/tools/prettify/fprettify
new file mode 160000
index 0000000000..2b2801bf91
--- /dev/null
+++ b/tools/prettify/fprettify
@@ -0,0 +1 @@
+Subproject commit 2b2801bf91dd651c417c232bb891d407bf81085c
diff --git a/tools/prettify/fprettify/__init__.py b/tools/prettify/fprettify/__init__.py
deleted file mode 100755
index 3cdb69c2c2..0000000000
--- a/tools/prettify/fprettify/__init__.py
+++ /dev/null
@@ -1,1332 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-###############################################################################
-# This file is part of fprettify.
-# Copyright (C) 2016-2018 Patrick Seewald, CP2K developers group
-#
-# fprettify is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# fprettify is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with fprettify. If not, see .
-###############################################################################
-
-"""
-Impose white space conventions and indentation based on scopes / subunits
-
-normalization of white spaces supported for following operators:
-- relational operators:
- .EQ. .NE. .LT. .LE. .GT. .GE.
- == /= < <= > >=
-- logical operators:
- .AND. .OR. .EQV. .NEQV.
- .NOT.
-- bracket delimiters
-- commas and semicolons:
-- arithmetic operators:
- * / ** + -
-- other operators:
- % - (sign) = (function argument)
- = (assignment) => (pointer assignment)
-
-supported criteria for alignment / indentation:
- Fortran lines:
- - if, else, endif
- - do, enddo
- - select case, case, end select
- - subroutine, end subroutine
- - function, end function
- - module, end module
- - program, end program
- - interface, end interface
- - type, end type
- Actual lines (parts of Fortran lines separated by linebreaks):
- - bracket delimiters (.), (/./), and [.]
- - assignments by value = and pointer =>.
-
-LIMITATIONS
-- assumes that all subunits are explicitly ended within same file,
- no treatment of #include statements
-- can not deal with f77 constructs (files are ignored)
-
-FIXME's
-- internal errors should not happen
-- wrap regular expression parser. This allows to extend parser by constructs
- that are not regular expressions (and support e.g. forall construct).
-- strip whitespaces once and for all and then assume no trailing / leading
- whitespaces
-- open files only when needed
-"""
-from __future__ import (absolute_import, division,
- print_function, unicode_literals)
-import re
-import sys
-import logging
-import os
-import io
-import argparse
-
-# allow for unicode for stdin / stdout, it's a mess
-try:
- # python 3
- sys.stdin = io.TextIOWrapper(
- sys.stdin.detach(), encoding='UTF-8', line_buffering=True)
- sys.stdout = io.TextIOWrapper(
- sys.stdout.detach(), encoding='UTF-8', line_buffering=True)
-except AttributeError:
- # python 2
- import codecs
- utf8_reader = codecs.getreader('UTF-8')
- utf8_writer = codecs.getwriter('UTF-8')
- sys.stdin = utf8_reader(sys.stdin)
- sys.stdout = utf8_writer(sys.stdout)
-
-from .fparse_utils import (VAR_DECL_RE, OMP_RE, OMP_DIR_RE,
- InputStream, CharFilter,
- FprettifyException, FprettifyParseException, FprettifyInternalException, RE_FLAGS)
-
-# recognize fortran files by extension
-FORTRAN_EXTENSIONS = [".f", ".for", ".ftn",
- ".f90", ".f95", ".f03", ".fpp"]
-FORTRAN_EXTENSIONS += [_.upper() for _ in FORTRAN_EXTENSIONS]
-
-# constants, mostly regular expressions:
-FORMATTER_ERROR_MESSAGE = (" Wrong usage of formatting-specific directives"
- " '&', '!&', '!&<' or '!&>'.")
-LINESPLIT_MESSAGE = ("auto indentation failed due to 132 chars limit, "
- "line should be split")
-
-EOL_STR = r"\s*;?\s*$" # end of fortran line
-EOL_SC = r"\s*;\s*$" # whether line is ended with semicolon
-SOL_STR = r"^\s*" # start of fortran line
-
-F77_STYLE = re.compile(r"^\s*\d", RE_FLAGS)
-
-# regular expressions for parsing statements that start, continue or end a
-# subunit:
-IF_RE = re.compile(
- SOL_STR + r"(\w+\s*:)?\s*IF\s*\(.+\)\s*THEN" + EOL_STR, RE_FLAGS)
-ELSE_RE = re.compile(
- SOL_STR + r"ELSE(\s*IF\s*\(.+\)\s*THEN)?" + EOL_STR, RE_FLAGS)
-ENDIF_RE = re.compile(SOL_STR + r"END\s*IF(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-DO_RE = re.compile(SOL_STR + r"(\w+\s*:)?\s*DO(" + EOL_STR + r"|\s)", RE_FLAGS)
-ENDDO_RE = re.compile(SOL_STR + r"END\s*DO(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-SELCASE_RE = re.compile(
- SOL_STR + r"SELECT\s*(CASE|TYPE)\s*\(.+\)" + EOL_STR, RE_FLAGS)
-CASE_RE = re.compile(
- SOL_STR + r"((CASE|TYPE\s+IS|CLASS\s+IS)\s*(\(.+\)|DEFAULT)|CLASS\s+DEFAULT)" + EOL_STR, RE_FLAGS)
-ENDSEL_RE = re.compile(SOL_STR + r"END\s*SELECT" + EOL_STR, RE_FLAGS)
-
-ASSOCIATE_RE = re.compile(SOL_STR + r"ASSOCIATE\s*\(.+\)" + EOL_STR, RE_FLAGS)
-ENDASSOCIATE_RE = re.compile(SOL_STR + r"END\s*ASSOCIATE" + EOL_STR, RE_FLAGS)
-
-BLK_RE = re.compile(SOL_STR + r"(\w+\s*:)?\s*BLOCK" + EOL_STR, RE_FLAGS)
-ENDBLK_RE = re.compile(SOL_STR + r"END\s*BLOCK(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-SUBR_RE = re.compile(
- r"^([^\"'!]* )?SUBROUTINE\s+\w+\s*(\(.*\))?" + EOL_STR, RE_FLAGS)
-ENDSUBR_RE = re.compile(
- SOL_STR + r"END\s*SUBROUTINE(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-FCT_RE = re.compile(
- r"^([^\"'!]* )?FUNCTION\s+\w+\s*(\(.*\))?(\s*RESULT\s*\(\w+\))?" + EOL_STR,
- RE_FLAGS)
-ENDFCT_RE = re.compile(
- SOL_STR + r"END\s*FUNCTION(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-MOD_RE = re.compile(SOL_STR + r"MODULE\s+\w+" + EOL_STR, RE_FLAGS)
-ENDMOD_RE = re.compile(SOL_STR + r"END\s*MODULE(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-TYPE_RE = re.compile(
- SOL_STR +
- r"TYPE(\s*,\s*(BIND\s*\(\s*C\s*\)|EXTENDS\s*\(.*\)|ABSTRACT))?(\s*,\s*(PUBLIC|PRIVATE))?(\s*::\s*|\s+)\w+" + EOL_STR,
- RE_FLAGS)
-ENDTYPE_RE = re.compile(SOL_STR + r"END\s*TYPE(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-PROG_RE = re.compile(SOL_STR + r"PROGRAM\s+\w+" + EOL_STR, RE_FLAGS)
-ENDPROG_RE = re.compile(
- SOL_STR + r"END\s*PROGRAM(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-INTERFACE_RE = re.compile(
- r"^([^\"'!]* )?INTERFACE(\s+\w+|\s+(OPERATOR|ASSIGNMENT)\s*\(.*\))?" + EOL_STR, RE_FLAGS)
-ENDINTERFACE_RE = re.compile(
- SOL_STR + r"END\s*INTERFACE(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-CONTAINS_RE = re.compile(SOL_STR + r"CONTAINS" + EOL_STR, RE_FLAGS)
-
-ENUM_RE = re.compile(
- SOL_STR + r"ENUM(\s*,\s*(BIND\s*\(\s*C\s*\)))?(\s*::\s*|\s+)\w+" + EOL_STR,
- RE_FLAGS)
-ENDENUM_RE = re.compile(SOL_STR + r"END\s*ENUM(\s+\w+)?" + EOL_STR, RE_FLAGS)
-
-ENDANY_RE = re.compile(SOL_STR + r"END" + EOL_STR, RE_FLAGS)
-
-PRIVATE_RE = re.compile(SOL_STR + r"PRIVATE\s*::", RE_FLAGS)
-PUBLIC_RE = re.compile(SOL_STR + r"PUBLIC\s*::", RE_FLAGS)
-
-# intrinsic statements with parenthesis notation that are not functions
-INTR_STMTS_PAR = (r"(ALLOCATE|DEALLOCATE|REWIND|BACKSPACE|INQUIRE|"
- r"OPEN|CLOSE|READ|WRITE|"
- r"FORALL|WHERE|ASSOCIATE|NULLIFY)")
-
-# regular expressions for parsing linebreaks
-LINEBREAK_STR = r"(&)[\s]*(?:!.*)?$"
-
-# regular expressions for parsing operators
-# Note: +/- in real literals and sign operator is ignored
-PLUSMINUS_RE = re.compile(
- r"(?<=[\w\)\]])(?(?!=)|>=))\s*(?!\))",
- RE_FLAGS)
-LOG_OP_RE = re.compile(r"\s*(\.(?:AND|OR|EQV|NEQV)\.)\s*", RE_FLAGS)
-PRINT_RE = re.compile(r"(?<=\w)\s*(\*,)\s*", RE_FLAGS)
-
-# regular expressions for parsing delimiters
-DEL_OPEN_STR = r"(\(\/?|\[)"
-DEL_OPEN_RE = re.compile(r"^" + DEL_OPEN_STR, RE_FLAGS)
-DEL_CLOSE_STR = r"(\/?\)|\])"
-DEL_CLOSE_RE = re.compile(r"^" + DEL_CLOSE_STR, RE_FLAGS)
-
-# empty line regex
-EMPTY_RE = re.compile(SOL_STR + r"([!#].*)?$", RE_FLAGS)
-
-# two-sided operators
-LR_OPS_RE = [REL_OP_RE, LOG_OP_RE, PLUSMINUS_RE, MULTDIV_RE, PRINT_RE]
-
-USE_RE = re.compile(
- SOL_STR + "USE(\s+|(,.+?)?::\s*)\w+?((,.+?=>.+?)+|,\s*only\s*:.+?)?$" + EOL_STR, RE_FLAGS)
-
-# markups to deactivate formatter
-NO_ALIGN_RE = re.compile(SOL_STR + r"&\s*[^\s*]+")
-
-# combine regex that define subunits
-NEW_SCOPE_RE = [IF_RE, DO_RE, SELCASE_RE, SUBR_RE,
- FCT_RE, MOD_RE, PROG_RE, INTERFACE_RE, TYPE_RE, ENUM_RE, ASSOCIATE_RE, None, BLK_RE]
-CONTINUE_SCOPE_RE = [ELSE_RE, None, CASE_RE, CONTAINS_RE,
- CONTAINS_RE, CONTAINS_RE, CONTAINS_RE, None, CONTAINS_RE, None, None, None, None]
-END_SCOPE_RE = [ENDIF_RE, ENDDO_RE, ENDSEL_RE, ENDSUBR_RE,
- ENDFCT_RE, ENDMOD_RE, ENDPROG_RE, ENDINTERFACE_RE, ENDTYPE_RE, ENDENUM_RE, ENDASSOCIATE_RE, ENDANY_RE, ENDBLK_RE]
-
-# match namelist names
-NML_RE = re.compile(r"(/\w+/)", RE_FLAGS)
-# find namelists and data statements
-NML_STMT_RE = re.compile(SOL_STR + r"NAMELIST.*/.*/", RE_FLAGS)
-DATA_STMT_RE = re.compile(SOL_STR + r"DATA\s+\w", RE_FLAGS)
-
-class F90Indenter(object):
- """
- Parses encapsulation of subunits / scopes line by line
- and updates the indentation.
- """
-
- def __init__(self, first_indent, rel_indent, filename):
- # scopes / subunits:
- self._scope_storage = []
- # indents for all fortran lines:
- self._indent_storage = []
- # indents of actual lines of current fortran line
- self._line_indents = []
-
- self._filename = filename
- self._aligner = F90Aligner(filename)
-
- # no lines have been processed yet:
- self._initial = True
-
- # implicit scopes: we define implicit scopes, as many as match
- # first_indent and rel_indent. This allows for, e.g., a properly
- # indented "END FUNCTION" without matching "FUNCTION" statement:
- if rel_indent > 0:
- for n_impl in range(first_indent % rel_indent, first_indent + 1, rel_indent):
- self._indent_storage += [n_impl]
-
- if not self._indent_storage:
- self._indent_storage = [0]
-
- def process_lines_of_fline(self, f_line, lines, rel_ind, rel_ind_con,
- line_nr, manual_lines_indent=None):
- """
- Process all lines that belong to a Fortran line `f_line`.
-
- Impose a relative indent of `rel_ind` for current Fortran line,
- and `rel_ind_con` for line continuation.
- By default line continuations are auto-aligned by F90Aligner
- :param f_line: fortran line
- :param lines: actual lines belonging to f_line
- :param rel_ind: relative scope indent size for this line
- :rel_ind_con: relative continuation indent size for this line
- :line_nr: line number
- :manual_lines_indent: don't use F90Aligner but manually impose
- indents for continuations
- """
-
- self._line_indents = [0] * len(lines)
- br_indent_list = [0] * len(lines)
-
- # local variables to avoid self hassle:
- line_indents = self._line_indents
-
- scopes = self._scope_storage
- indents = self._indent_storage
- filename = self._filename
-
- # check statements that start new scope
- is_new = False
- valid_new = False
-
- for new_n, newre in enumerate(NEW_SCOPE_RE):
- if newre and newre.search(f_line) and not END_SCOPE_RE[new_n].search(f_line):
- what_new = new_n
- is_new = True
- valid_new = True
- scopes.append(what_new)
- log_message("{}: {}".format(what_new, f_line),
- "debug", filename, line_nr)
-
- # check statements that continue scope
- is_con = False
- valid_con = False
- for con_n, conre in enumerate(CONTINUE_SCOPE_RE):
- if conre and conre.search(f_line):
- what_con = con_n
- is_con = True
- if len(scopes) > 0:
- what = scopes[-1]
- if what == what_con:
- valid_con = True
- log_message("{}: {}".format(
- what_con, f_line), "debug", filename, line_nr)
-
- # check statements that end scope
- is_end = False
- valid_end = False
- for end_n, endre in enumerate(END_SCOPE_RE):
- if endre and endre.search(f_line):
- what_end = end_n
- is_end = True
- if len(scopes) > 0:
- what = scopes.pop()
- if what == what_end:
- valid_end = True
- log_message("{}: {}".format(
- what_end, f_line), "debug", filename, line_nr)
-
- # deal with line breaks
- if not manual_lines_indent:
- self._aligner.process_lines_of_fline(
- f_line, lines, rel_ind_con, line_nr)
- br_indent_list = self._aligner.get_lines_indent()
- else:
- br_indent_list = manual_lines_indent
-
- for pos in range(0, len(lines) - 1):
- line_indents[pos + 1] = br_indent_list[pos + 1]
-
- if is_new:
- if not valid_new:
- log_message('invalid scope opening statement',
- "info", filename, line_nr)
-
- line_indents = [ind + indents[-1] for ind in line_indents]
-
- indents.append(rel_ind + indents[-1])
-
- elif is_con or is_end:
- valid = valid_con if is_con else valid_end
-
- if not valid:
- log_message('invalid scope closing statement',
- "info", filename, line_nr)
- try:
- line_indents = [ind + indents[-2 + self._initial]
- for ind in line_indents]
- except IndexError:
- assert not valid
-
- if is_end:
- if len(indents) > 1:
- indents.pop()
- else:
- indents[-1] = 0
-
- else:
- line_indents = [ind + indents[-1] for ind in line_indents]
-
- # we have processed first line:
- self._initial = False
-
- # reassigning self.* to the updated variables
- self._line_indents = line_indents
- self._scope_storage = scopes
- self._indent_storage = indents
-
- def get_fline_indent(self):
- """after processing, retrieve the indentation of the full Fortran line."""
- return self._indent_storage[-1]
-
- def get_lines_indent(self):
- """after processing, retrieve the indents of all line parts."""
- return self._line_indents
-
-
-class F90Aligner(object):
- """
- Alignment of continuations of a broken line,
- based on the following heuristics:
-
- if line break in brackets
- We are parsing the level of nesting
- and align to most inner bracket delimiter.
-
- else if line is an assignment
- alignment to '=' or '=>'.
- note: assignment operator recognized as any '=' that is not
- part of another operator and that is not enclosed in bracket
-
- else if line is a declaration
- alignment to '::'
-
- else
- default indent
- """
-
- def __init__(self, filename):
- self._filename = filename
- self.__init_line(0)
-
- def __init_line(self, line_nr):
- """initialization before processing new line"""
- self._line_nr = line_nr
- self._line_indents = [0]
- self._level = 0
- self._br_indent_list = [0]
-
- def process_lines_of_fline(self, f_line, lines, rel_ind, line_nr):
- """
- process all lines that belong to a Fortran line `f_line`,
- `rel_ind` is the relative indentation size.
- """
-
- self.__init_line(line_nr)
-
- is_decl = VAR_DECL_RE.search(f_line) or PUBLIC_RE.search(f_line) or PRIVATE_RE.match(f_line)
- for pos, line in enumerate(lines):
- self.__align_line_continuations(
- line, is_decl, rel_ind, self._line_nr + pos)
- if pos + 1 < len(lines):
- self._line_indents.append(self._br_indent_list[-1])
-
- if len(self._br_indent_list) > 2 or self._level:
- log_message('unpaired bracket delimiters',
- "info", self._filename, self._line_nr)
-
- def get_lines_indent(self):
- """after processing, retrieve the indents of all line parts."""
- return self._line_indents
-
- def __align_line_continuations(self, line, is_decl, indent_size, line_nr):
- """align continuation lines."""
-
- indent_list = self._br_indent_list
- level = self._level
- filename = self._filename
-
- pos_eq = 0
- pos_ldelim = []
- pos_rdelim = []
- ldelim = []
- rdelim = []
-
- # find delimiters that are not ended on this line.
- # find proper alignment to most inner delimiter
- # or alignment to assignment operator
- rel_ind = indent_list[-1] # indentation of prev. line
-
- end_of_delim = -1
-
- for pos, char in CharFilter(enumerate(line)):
-
- what_del_open = None
- what_del_close = None
- if pos > end_of_delim:
- [what_del_open, what_del_close] = get_curr_delim(line, pos)
-
- if what_del_open:
- what_del_open = what_del_open.group()
- end_of_delim = pos + len(what_del_open) - 1
- level += 1
- indent_list.append(pos + len(what_del_open) + rel_ind)
- pos_ldelim.append(pos)
- ldelim.append(what_del_open)
- if what_del_close:
- what_del_close = what_del_close.group()
- end_of_delim = pos + len(what_del_close) - 1
- if level > 0:
- level += -1
- indent_list.pop()
- else:
- log_message('unpaired bracket delimiters',
- "info", filename, line_nr)
-
- if pos_ldelim:
- pos_ldelim.pop()
- what_del_open = ldelim.pop()
- valid = False
- if what_del_open == r"(":
- valid = what_del_close == r")"
- if what_del_open == r"(/":
- valid = what_del_close == r"/)"
- if what_del_open == r"[":
- valid = what_del_close == r"]"
- if not valid:
- log_message('unpaired bracket delimiters',
- "info", filename, line_nr)
-
- else:
- pos_rdelim.append(pos)
- rdelim.append(what_del_close)
- if char == ',' and not level and pos_eq > 0:
- # a top level comma removes previous alignment position.
- # (see issue #11)
- pos_eq = 0
- indent_list.pop()
- if not level and not is_decl and char == '=' and not REL_OP_RE.search(
- line[max(0, pos - 1):min(pos + 2, len(line))]):
- # should only have one assignment per line!
- if pos_eq > 0:
- raise FprettifyInternalException(
- "found more than one assignment in the same Fortran line", filename, line_nr)
- is_pointer = line[pos + 1] == '>'
- pos_eq = pos + 1
- # don't align if assignment operator directly before
- # line break
- if not re.search(r"=>?\s*" + LINEBREAK_STR, line,
- RE_FLAGS):
- indent_list.append(
- pos_eq + 1 + is_pointer + indent_list[-1])
- elif is_decl and line[pos:pos + 2] == '::' and not re.search(r"::\s*" + LINEBREAK_STR, line, RE_FLAGS):
- indent_list.append(pos + 3 + indent_list[-1])
-
- # Don't align if delimiter opening directly before line break
- if level and re.search(DEL_OPEN_STR + r"\s*" + LINEBREAK_STR, line,
- RE_FLAGS):
- if len(indent_list) > 1:
- indent_list[-1] = indent_list[-2]
- else:
- indent_list[-1] = 0
-
- if not indent_list[-1]:
- indent_list[-1] = indent_size
-
- self._level = level
-
-
-def inspect_ffile_format(infile, indent_size, orig_filename=None):
- """
- Determine indentation by inspecting original Fortran file.
-
- This is mainly for finding aligned blocks of DO/IF statements.
- Also check if it has f77 constructs.
- :param infile: open file
- :param indent_size: the default indent size, if <= 0,
- adopt original indents
- :orig_filename: filename used for messages
- :returns: [ target indent sizes for each line,
- indent of first line (offset),
- whether file is sufficiently modern fortran ]
- """
- if not orig_filename:
- orig_filename = infile.name
-
- adopt = indent_size <= 0
-
- num_labels = False
- indents = []
- stream = InputStream(infile, orig_filename)
- prev_offset = 0
- first_indent = -1
- f77_line_nr = 0
-
- while 1:
- f_line, _, lines = stream.next_fortran_line()
- if not lines:
- break
-
- offset = len(lines[0]) - len(lines[0].lstrip(' '))
- if f_line.strip() and first_indent == -1:
- first_indent = offset
- indents.append(offset - prev_offset)
-
- # do not adopt indentations but impose fixed rel. ind.
- # but don't impose indentation for blocked do/if constructs:
- if not adopt and (prev_offset != offset or (not IF_RE.search(f_line) and
- not DO_RE.search(f_line))):
- indents[-1] = indent_size
- prev_offset = offset
-
- if not num_labels and F77_STYLE.search(f_line):
- num_labels = True
- f77_line_nr = stream.line_nr
-
- modern_fortran = not num_labels
-
- return indents, first_indent, modern_fortran, f77_line_nr
-
-
-def format_single_fline(f_line, whitespace, linebreak_pos, ampersand_sep,
- filename, line_nr, auto_format=True):
- """
- format a single Fortran line - imposes white space formatting
- and inserts linebreaks.
- Takes a logical Fortran line `f_line` as input as well as the positions
- of the linebreaks (`linebreak_pos`), and the number of
- separating whitespace characters before ampersand (`ampersand_sep`).
- `filename` and `line_nr` just for error messages.
- The higher `whitespace`, the more white space characters inserted -
- whitespace = 0, 1, 2, 3 are currently supported.
- auto formatting can be turned off by setting `auto_format` to False.
- """
-
- # define whether to put whitespaces around operators:
- # 0: comma, semicolon
- # 1: assignment operators
- # 2: relational operators
- # 3: logical operators
- # 4: arithm. operators plus and minus
- # 5: arithm. operators multiply and divide
- # 6: print / read statements
-
- if whitespace == 0:
- spacey = [0, 0, 0, 0, 0, 0, 0]
- elif whitespace == 1:
- spacey = [1, 1, 1, 1, 0, 0, 1]
- elif whitespace == 2:
- spacey = [1, 1, 1, 1, 1, 0, 1]
- elif whitespace == 3:
- spacey = [1, 1, 1, 1, 1, 1, 1]
- else:
- raise NotImplementedError("unknown value for whitespace")
-
- line = f_line
- line_orig = line
-
- if auto_format:
- line = rm_extra_whitespace(line)
- line = add_whitespace_charwise(line, spacey, filename, line_nr)
- line = add_whitespace_context(line, spacey)
-
- lines_out = split_reformatted_line(
- line_orig, linebreak_pos, ampersand_sep, line, filename, line_nr)
- return lines_out
-
-
-def rm_extra_whitespace(line):
- """rm all unneeded whitespace chars, except for declarations"""
- line_ftd = ''
- pos_prev = -1
- for pos, char in CharFilter(enumerate(line)):
- is_decl = line[pos:].lstrip().startswith('::') or line[
- :pos].rstrip().endswith('::')
-
- if char == ' ':
- # remove double spaces:
- if line_ftd and (re.search(r'[\w"]', line_ftd[-1]) or is_decl):
- line_ftd = line_ftd + char
- else:
- if (line_ftd and line_ftd[-1] == ' ' and
- (not re.search(r"[\w\"']", char) and not is_decl)):
- line_ftd = line_ftd[:-1] # remove spaces except between words
- line_ftd = line_ftd + line[pos_prev + 1:pos + 1]
- pos_prev = pos
- return line_ftd
-
-
-def add_whitespace_charwise(line, spacey, filename, line_nr):
- """add whitespace character wise (no need for context aware parsing)"""
- line_ftd = line
- pos_eq = []
- end_of_delim = -1
- level = 0
- for pos, char in CharFilter(enumerate(line)):
- # offset w.r.t. unformatted line
- offset = len(line_ftd) - len(line)
-
- # format delimiters
- what_del_open = None
- what_del_close = None
- if pos > end_of_delim:
- [what_del_open, what_del_close] = get_curr_delim(line, pos)
-
- if what_del_open or what_del_close:
- sep1 = 0
- sep2 = 0
-
- if what_del_open:
- delim = what_del_open.group()
- else:
- delim = what_del_close.group()
-
- lhs = line_ftd[:pos + offset]
- rhs = line_ftd[pos + len(delim) + offset:]
-
- # format opening delimiters
- if what_del_open:
- level += 1 # new scope
- # add separating whitespace before opening delimiter
- # with some exceptions:
- # FIXME: duplication of regex, better to include them into
- # INTR_STMTS_PAR
- if ((not re.search((r"(" + DEL_OPEN_STR +
- r"|[\w\*/=\+\-:])\s*$"),
- line[:pos], RE_FLAGS) and
- not EMPTY_RE.search(line[:pos])) or
- re.search(SOL_STR + r"(\w+\s*:)?(ELSE)?\s*IF\s*$",
- line[:pos], RE_FLAGS) or
- re.search(SOL_STR + r"(\w+\s*:)?\s*DO\s+WHILE\s*$",
- line[:pos], RE_FLAGS) or
- re.search(SOL_STR + r"(SELECT)?\s*CASE\s*",
- line[:pos], RE_FLAGS) or
- re.search(SOL_STR + r"SELECT\s*TYPE\s*",
- line[:pos], RE_FLAGS) or
- re.search(SOL_STR + r"CLASS\s*DEFAULT\s*",
- line[:pos], RE_FLAGS) or
- re.search(SOL_STR + r"(TYPE|CLASS)\s+IS\s*",
- line[:pos], RE_FLAGS) or
- re.search(r"\b" + INTR_STMTS_PAR + r"\s*$",
- line[:pos], RE_FLAGS)):
- sep1 = 1
-
- # format closing delimiters
- else:
- if level > 0:
- level += -1 # close scope
- else:
- log_message('unpaired bracket delimiters',
- "info", filename, line_nr)
-
- # add separating whitespace after closing delimiter
- # with some exceptions:
- if not re.search(r"^\s*(" + DEL_CLOSE_STR + r"|[,%:/\*])",
- line[pos + 1:], RE_FLAGS):
- sep2 = 1
- elif re.search(r"^\s*::", line[pos + 1:], RE_FLAGS):
- sep2 = len(rhs) - len(rhs.lstrip(' '))
-
- # where delimiter token ends
- end_of_delim = pos + len(delim) - 1
-
- line_ftd = lhs.rstrip(' ') + ' ' * sep1 + \
- delim + ' ' * sep2 + rhs.lstrip(' ')
-
- # format commas and semicolons
- if char in [',', ';']:
- lhs = line_ftd[:pos + offset]
- rhs = line_ftd[pos + 1 + offset:]
- line_ftd = lhs.rstrip(' ') + char + ' ' * \
- spacey[0] + rhs.lstrip(' ')
- line_ftd = line_ftd.rstrip(' ')
-
- # format .NOT.
- if re.search(r"^\.NOT\.", line[pos:pos + 5], RE_FLAGS):
- lhs = line_ftd[:pos + offset]
- rhs = line_ftd[pos + 5 + offset:]
- line_ftd = lhs.rstrip(
- ' ') + line[pos:pos + 5] + ' ' * spacey[3] + rhs.lstrip(' ')
-
- # strip whitespaces from '=' and prepare assignment operator
- # formatting:
- if char == '=' and not REL_OP_RE.search(line[pos - 1:pos + 2]):
- lhs = line_ftd[:pos + offset]
- rhs = line_ftd[pos + 1 + offset:]
- line_ftd = lhs.rstrip(' ') + '=' + rhs.lstrip(' ')
- if not level: # remember position of assignment operator
- pos_eq.append(len(lhs.rstrip(' ')))
-
- line = line_ftd
-
- for pos in pos_eq:
- offset = len(line_ftd) - len(line)
- is_pointer = line[pos + 1] == '>'
- lhs = line_ftd[:pos + offset]
- rhs = line_ftd[pos + 1 + is_pointer + offset:]
- if is_pointer:
- assign_op = '=>' # pointer assignment
- else:
- assign_op = '=' # assignment
- line_ftd = (lhs.rstrip(' ') +
- ' ' * spacey[1] + assign_op +
- ' ' * spacey[1] + rhs.lstrip(' '))
- # offset w.r.t. unformatted line
-
- if level != 0:
- log_message('unpaired bracket delimiters', "info", filename, line_nr)
-
- return line_ftd
-
-
-def add_whitespace_context(line, spacey):
- """
- for context aware whitespace formatting we extract line parts that are
- not comments or strings in order to be able to apply a context aware regex.
- """
-
- line_parts = []
- str_end = -1
- instring = ''
- for pos, char in enumerate(line):
- if char in ['"', "'"]: # skip string
- if not instring:
- str_start = pos
- line_parts.append(line[str_end + 1:str_start])
- instring = char
- elif instring == char:
- str_end = pos
- line_parts.append(line[str_start:str_end + 1])
- instring = ''
- if pos == len(line) - 1:
- line_parts.append(line[str_end + 1:])
-
- # format namelists with spaces around /
- if NML_STMT_RE.match(line):
- for pos, part in enumerate(line_parts):
- # exclude comments, strings:
- if not re.match(r"['\"!]", part, RE_FLAGS):
- partsplit = NML_RE.split(part)
- line_parts[pos] = (' '.join(partsplit))
-
- # Two-sided operators
- for n_op, lr_re in enumerate(LR_OPS_RE):
- for pos, part in enumerate(line_parts):
- # exclude comments, strings:
- if not re.search(r"^['\"!]", part, RE_FLAGS):
- # also exclude / if we see a namelist and data statement
- if not ( NML_STMT_RE.match(line) or DATA_STMT_RE.match(line) ):
- partsplit = lr_re.split(part)
- line_parts[pos] = (' ' * spacey[n_op + 2]).join(partsplit)
-
- line = ''.join(line_parts)
-
- # format ':' for labels and use only statements
- for newre in NEW_SCOPE_RE[0:2]:
- if newre.search(line) and re.search(SOL_STR + r"\w+\s*:", line):
- line = ': '.join(_.strip() for _ in line.split(':', 1))
-
- if USE_RE.search(line):
- line = re.sub(r'(only)\s*:\s*', r'\g<1>:' + ' ' *
- spacey[0], line, flags=RE_FLAGS)
-
- return line
-
-
-def split_reformatted_line(line_orig, linebreak_pos_orig, ampersand_sep, line, filename, line_nr):
- """
- Infer linebreak positions of formatted line from linebreak positions in
- original line and split line.
- """
- # shift line break positions from original to reformatted line
- pos_new = 0
- pos_old = 0
- linebreak_pos_orig.sort(reverse=True)
- linebreak_pos_ftd = []
- while 1:
-
- if pos_new == len(line) or pos_old == len(line_orig):
- break
-
- if line[pos_new] != line_orig[pos_old]:
- raise FprettifyInternalException(
- "failed at finding line break position", filename, line_nr)
-
- if linebreak_pos_orig and pos_old > linebreak_pos_orig[-1]:
- linebreak_pos_orig.pop()
- linebreak_pos_ftd.append(pos_new)
- continue
-
- pos_new += 1
- while pos_new < len(line) and line[pos_new] == ' ':
- pos_new += 1
-
- pos_old += 1
- while pos_old < len(line_orig) and line_orig[pos_old] == ' ':
- pos_old += 1
-
- linebreak_pos_ftd.insert(0, 0)
-
- # We split line into parts and we insert ampersands at line end, but not
- # for empty lines and comment lines
- lines_split = [(line[l:r].rstrip(' ') +
- ' ' * ampersand_sep[pos] + '&' * min(1, r - l))
- for pos, (l, r) in enumerate(zip(linebreak_pos_ftd[0:-1],
- linebreak_pos_ftd[1:]))]
-
- lines_split.append(line[linebreak_pos_ftd[-1]:])
-
- return lines_split
-
-
-def reformat_inplace(filename, stdout=False, **kwargs): # pragma: no cover
- """reformat a file in place."""
- if filename == '-':
- infile = io.StringIO()
- infile.write(sys.stdin.read())
- else:
- infile = io.open(filename, 'r', encoding='utf-8')
-
- newfile = io.StringIO()
- reformat_ffile(infile=infile, outfile=newfile,
- orig_filename=filename, **kwargs)
-
- if stdout:
- sys.stdout.write(newfile.getvalue())
- else:
- outfile = io.open(filename, 'w', encoding='utf-8')
- outfile.write(newfile.getvalue())
-
-
-def reformat_ffile(infile, outfile, indent_size=3, whitespace=2,
- orig_filename=None):
- """main method to be invoked for formatting a Fortran file."""
-
- if not orig_filename:
- orig_filename = infile.name
-
- infile.seek(0)
- req_indents, first_indent, modern, f77_line_nr = inspect_ffile_format(
- infile, indent_size, orig_filename)
- infile.seek(0)
-
- if not modern:
- raise FprettifyParseException(
- "fprettify failed because of fixed format or f77 constructs.", orig_filename, f77_line_nr)
-
- # initialization
- indenter = F90Indenter(first_indent, indent_size, orig_filename)
- nfl = 0 # fortran line counter
- do_indent = True
- use_same_line = False
- stream = InputStream(infile, orig_filename)
- skip_blank = False
- in_format_off_block = False
-
- while 1:
- f_line, comments, lines = stream.next_fortran_line()
-
- if not lines:
- break
-
- indent = [0] * len(lines)
- nfl += 1
- orig_lines = lines
-
- comment_lines = format_comments(lines, comments)
- auto_align, auto_format, in_format_off_block = parse_fprettify_directives(
- lines, comment_lines, in_format_off_block, orig_filename, stream.line_nr)
- f_line, lines, is_omp, is_omp_conditional = preprocess_omp(
- f_line, lines)
-
- lines, do_format, use_indent, is_blank = preprocess_line(
- f_line, lines, comments, orig_filename, stream.line_nr)
- if is_blank and skip_blank:
- continue
- if not do_format and do_indent:
- if use_indent:
- assert len(indent) == 1
- # inherit indent from previous line
- indent[0] = indenter.get_fline_indent()
- else:
- do_indent = False
- else:
-
- if not auto_align:
- manual_lines_indent = get_manual_alignment(lines)
- else:
- manual_lines_indent = []
-
- lines, pre_ampersand, ampersand_sep = remove_pre_ampersands(
- lines, orig_filename, stream.line_nr)
- linebreak_pos = get_linebreak_pos(lines)
-
- f_line = f_line.strip(' ')
-
- lines = format_single_fline(
- f_line, whitespace, linebreak_pos, ampersand_sep,
- orig_filename, stream.line_nr, auto_format)
-
- lines = append_comments(lines, comment_lines)
-
- # target indent for next line
- rel_indent = req_indents[nfl] if nfl < len(req_indents) else 0
-
- indenter.process_lines_of_fline(
- f_line, lines, rel_indent, indent_size,
- stream.line_nr, manual_lines_indent)
- indent = indenter.get_lines_indent()
-
- lines, indent = prepend_ampersands(lines, indent, pre_ampersand)
-
- lines = remove_trailing_whitespace(lines)
-
- write_formatted_line(outfile, indent, lines, orig_lines, do_indent,
- use_same_line, is_omp_conditional, orig_filename, stream.line_nr)
-
- do_indent, use_same_line = pass_defaults_to_next_line(f_line)
-
- # rm subsequent blank lines
- skip_blank = EMPTY_RE.search(
- f_line) and not any(comments) and not is_omp
-
-
-def format_comments(lines, comments):
- comments_ftd = []
- for line, comment in zip(lines, comments):
- has_comment = bool(comment.strip())
- sep = has_comment and not comment.strip() == line.strip()
- if line.strip(): # empty lines between linebreaks are ignored
- comments_ftd.append(' ' * sep + comment.strip())
- return comments_ftd
-
-
-def parse_fprettify_directives(lines, comment_lines, in_format_off_block, filename, line_nr):
- """
- parse formatter directives '!&' and line continuations starting with an
- ampersand.
- """
- auto_align = not any(NO_ALIGN_RE.search(_) for _ in lines)
- auto_format = not (in_format_off_block or any(
- _.lstrip().startswith('!&') for _ in comment_lines))
- if not auto_format:
- auto_align = False
- if (len(lines)) == 1:
- valid_directive = True
- if lines[0].strip().startswith('!&<'):
- if in_format_off_block:
- valid_directive = False
- else:
- in_format_off_block = True
- if lines[0].strip().startswith('!&>'):
- if not in_format_off_block:
- valid_directive = False
- else:
- in_format_off_block = False
- if not valid_directive:
- raise FprettifyParseException(
- FORMATTER_ERROR_MESSAGE, filename, line_nr)
-
- return [auto_align, auto_format, in_format_off_block]
-
-
-def preprocess_omp(f_line, lines):
- """convert omp conditional to normal fortran"""
-
- is_omp = OMP_RE.search(f_line)
- is_omp_conditional = bool(is_omp and not OMP_DIR_RE.search(f_line))
- if is_omp_conditional:
- f_line = OMP_RE.sub(' ', f_line, count=1)
- lines = [OMP_RE.sub(' ', l, count=1) for l in lines]
-
- return [f_line, lines, is_omp, is_omp_conditional]
-
-
-def preprocess_line(f_line, lines, comments, filename, line_nr):
- """preprocess lines: identification and formatting of special cases"""
- is_blank = False
- use_indent = False
- do_format = False
-
- if OMP_DIR_RE.search(f_line):
- # move '!$OMP' to line start, otherwise don't format omp directives
- lines = ['!$OMP' + (len(l) - len(l.lstrip())) *
- ' ' + OMP_DIR_RE.sub('', l, count=1) for l in lines]
- elif lines[0].startswith('#'): # preprocessor macros
- if len(lines) != 1:
- raise FprettifyInternalException(
- "Continuation lines for preprocessor statement", filename, line_nr)
- elif EMPTY_RE.search(f_line): # empty lines including comment lines
- if len(lines) != 1:
- raise FprettifyInternalException(
- "Continuation lines for comment lines", filename, line_nr)
- if any(comments):
- if not lines[0].startswith('!'):
- # indent comment lines only if they were not indented before.
- use_indent = True
- else:
- is_blank = True
- lines = [l.strip(' ') for l in lines]
- else:
- do_format = True
-
- return [lines, do_format, use_indent, is_blank]
-
-
-def pass_defaults_to_next_line(f_line):
- """defaults to be transferred from f_line to next line"""
- if re.search(r";\s*$", f_line, RE_FLAGS):
- # if line ended with semicolon, don't indent next line
- do_indent = False
- use_same_line = True
- else:
- do_indent = True
- use_same_line = False
- return [do_indent, use_same_line]
-
-
-def remove_trailing_whitespace(lines):
- """remove trailing whitespaces from lines"""
- lines = [re.sub(r"\s+$", '\n', l, RE_FLAGS)
- for l in lines]
- return lines
-
-
-def prepend_ampersands(lines, indent, pre_ampersand):
- """prepend ampersands and correct indent"""
- for pos, line in enumerate(lines):
- amp_insert = pre_ampersand[pos]
- if amp_insert:
- indent[pos] += -1
- lines[pos] = amp_insert + line
-
- return [lines, indent]
-
-
-def append_comments(lines, comment_lines):
- """append comments to lines"""
- for pos, (line, comment) in enumerate(zip(lines, comment_lines)):
- if pos < len(lines) - 1:
- has_nl = True # has next line
- else:
- has_nl = not re.search(EOL_SC, line)
- lines[pos] = lines[pos].rstrip(' ') + comment + '\n' * has_nl
-
- return lines
-
-
-def get_linebreak_pos(lines):
- """extract linebreak positions in Fortran line from lines"""
- linebreak_pos = []
- for line in lines:
- found = None
- for char_pos, _ in CharFilter(enumerate(line)):
- if re.match(LINEBREAK_STR, line[char_pos:], RE_FLAGS):
- found = char_pos
- if found:
- linebreak_pos.append(found)
- elif line.lstrip(' ')[0] in ['!','#']:
- linebreak_pos.append(0)
-
- linebreak_pos = [sum(linebreak_pos[0:_ + 1]) -
- 1 for _ in range(0, len(linebreak_pos))]
-
- return linebreak_pos
-
-
-def remove_pre_ampersands(lines, filename, line_nr):
- """
- remove and return preceding ampersands ('pre_ampersand'). Also return
- number of whitespace characters before ampersand of previous line
- ('ampersand_sep').
-
- Note: Don't do any whitespace formatting on ampersands if next line starts
- with an ampersand but remember the original number of spaces
- (ampersand_sep). This "special rule" is necessary since ampersands starting
- a line can be used to break literals, so changing the number of whitespaces
- before the ampersand ending the previous line may lead to invalid syntax or
- may change the number of whitespace characters in a string.
- """
- pre_ampersand = []
- ampersand_sep = []
-
- for pos, line in enumerate(lines):
- match = re.search(SOL_STR + r'(&\s*)', line)
- if match:
- pre_ampersand.append(match.group(1))
- try:
- # amount of whitespace before ampersand of previous line:
- sep = len(re.search(r'(\s*)&[\s]*(?:!.*)?$',
- lines[pos - 1]).group(1))
- except AttributeError:
- raise FprettifyParseException(
- "Bad continuation line format", filename, line_nr)
-
- ampersand_sep.append(sep)
- else:
- pre_ampersand.append('')
- if pos > 0:
- # use default 1 whitespace character before ampersand
- ampersand_sep.append(1)
-
- lines = [l.strip(' ').strip('&') for l in lines]
-
- return [lines, pre_ampersand, ampersand_sep]
-
-
-def get_manual_alignment(lines):
- """extract manual indents for line continuations from line"""
- manual_lines_indent = [
- len(l) - len(l.lstrip(' ').lstrip('&')) for l in lines]
- manual_lines_indent = [ind - manual_lines_indent[0]
- for ind in manual_lines_indent]
- return manual_lines_indent
-
-
-def write_formatted_line(outfile, indent, lines, orig_lines, do_indent, use_same_line, is_omp_conditional, filename, line_nr):
- """Write reformatted line to file"""
- for ind, line, orig_line in zip(indent, lines, orig_lines):
-
- # get actual line length excluding comment:
- line_length = 0
- for line_length, _ in CharFilter(enumerate(line)):
- pass
- line_length += 1
-
- if do_indent:
- ind_use = ind
- else:
- if use_same_line:
- ind_use = 1
- else:
- ind_use = 0
-
- if line.lstrip().startswith('#'):
- ind_use = 0
-
- if ind_use + line_length <= 133: # 132 plus 1 newline char
- outfile.write('!$' * is_omp_conditional +
- ' ' * (ind_use - 2 * is_omp_conditional +
- len(line) - len(line.lstrip(' '))) +
- line.lstrip(' '))
- elif line_length <= 133:
- outfile.write('!$' * is_omp_conditional + ' ' *
- (133 - 2 * is_omp_conditional -
- len(line.lstrip(' '))) + line.lstrip(' '))
-
- log_message(LINESPLIT_MESSAGE, "warning",
- filename, line_nr)
- else:
- outfile.write(orig_line)
- log_message(LINESPLIT_MESSAGE, "warning",
- filename, line_nr)
-
-
-def get_curr_delim(line, pos):
- """get delimiter token in line starting at pos, if it exists"""
- what_del_open = DEL_OPEN_RE.search(line[pos:pos + 2])
- what_del_close = DEL_CLOSE_RE.search(line[pos:pos + 2])
- return [what_del_open, what_del_close]
-
-
-def set_fprettify_logger(level):
- """setup custom logger"""
- logger = logging.getLogger('fprettify-logger')
- logger.setLevel(level)
- stream_handler = logging.StreamHandler()
- stream_handler.setLevel(level)
- formatter = logging.Formatter(
- '%(levelname)s: File %(ffilename)s, line %(fline)s\n %(message)s')
- stream_handler.setFormatter(formatter)
- logger.addHandler(stream_handler)
-
-
-def log_exception(e, message):
- """log an exception and a message"""
- log_message(message, "exception", e.filename, e.line_nr)
-
-
-def log_message(message, level, filename, line_nr):
- """log a message"""
-
- logger = logging.getLogger('fprettify-logger')
- logger_d = {'ffilename': filename, 'fline': line_nr}
- logger_to_use = getattr(logger, level)
- logger_to_use(message, extra=logger_d)
-
-
-def run(argv=sys.argv): # pragma: no cover
- """Command line interface"""
-
- parser = argparse.ArgumentParser(prog=argv[0],
- description='Auto-format modern Fortran source files.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
- parser.add_argument("-i", "--indent", type=int, default=3,
- help="relative indentation width")
- parser.add_argument("-w", "--whitespace", type=int,
- choices=range(0, 4), default=2, help="Amount of whitespace")
- parser.add_argument("-s", "--stdout", action='store_true', default=False,
- help="Write to stdout instead of formatting inplace")
-
- group = parser.add_mutually_exclusive_group()
- group.add_argument("-S", "--silent", "--no-report-errors", action='store_true',
- default=False, help="Don't write any errors or warnings to stderr")
- group.add_argument("-D", "--debug", action='store_true',
- default=False, help=argparse.SUPPRESS)
- parser.add_argument("path", type=str, nargs='*',
- help="Paths to files to be formatted inplace. If no paths are given, stdin (-) is used by default. Path can be a directory if --recursive is used.", default=['-'])
- parser.add_argument('-r', '--recursive', action='store_true',
- default=False, help="Recursively auto-format all Fortran files in subdirectories of specified path; recognized filename extensions: {}". format(", ".join(FORTRAN_EXTENSIONS)))
- parser.add_argument('-f', '--fortran', type=str, action='append', default=[],
- help="Overrides default fortran extensions recognized by --recursive. Repeat this option to specify more than one extension.")
- parser.add_argument('--version', action='version',
- version='%(prog)s 0.3.2')
-
- args = parser.parse_args(argv[1:])
-
- # support legacy input:
- if 'stdin' in args.path and not os.path.isfile('stdin'):
- args.path = ['-' if _ == 'stdin' else _ for _ in args.path]
-
- for directory in args.path:
- if directory == '-':
- if args.recursive:
- sys.stderr.write("--recursive requires a directory.\n")
- sys.exit()
- else:
- if not os.path.exists(directory):
- sys.stderr.write("directory " + directory +
- " does not exist!\n")
- sys.exit()
- if not os.path.isfile(directory) and directory != '-' and not args.recursive:
- sys.stderr.write("file " + directory + " does not exist!\n")
- sys.exit()
-
- if not args.recursive:
- filenames = [directory]
- else:
- if args.fortran:
- ext = args.fortran
- else:
- ext = FORTRAN_EXTENSIONS
- filenames = []
- for dirpath, _, files in os.walk(directory):
- for ffile in [os.path.join(dirpath, f) for f in files if any(f.endswith(_) for _ in ext)]:
- filenames.append(ffile)
-
- for filename in filenames:
- stdout = args.stdout or directory == '-'
-
- if args.debug:
- level = logging.DEBUG
- elif args.silent:
- level = logging.CRITICAL
- else:
- level = logging.WARNING
-
- set_fprettify_logger(level)
-
- try:
- reformat_inplace(filename,
- stdout=stdout,
- indent_size=args.indent,
- whitespace=args.whitespace)
- except FprettifyException as e:
- log_exception(e, "Fatal error occured")
diff --git a/tools/prettify/fprettify/fparse_utils.py b/tools/prettify/fprettify/fparse_utils.py
deleted file mode 100644
index 0979c49138..0000000000
--- a/tools/prettify/fprettify/fparse_utils.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# -*- coding: utf-8 -*-
-###############################################################################
-# This file is part of fprettify.
-# Copyright (C) 2016-2018 Patrick Seewald, CP2K developers group
-#
-# fprettify is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# fprettify is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with fprettify. If not, see .
-###############################################################################
-
-"""This is a collection of Fortran parsing utilities."""
-
-from __future__ import (absolute_import, division,
- print_function, unicode_literals)
-import re
-from collections import deque
-
-RE_FLAGS = re.IGNORECASE | re.UNICODE
-
-# FIXME bad ass regex!
-VAR_DECL_RE = re.compile(
- 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)
-
-# FIXME: unify omp regular expressions
-OMP_DIR_RE = re.compile(r"^\s*(!\$omp)", RE_FLAGS)
-OMP_RE = re.compile(r"^\s*(!\$)", RE_FLAGS)
-OMP_SUBS_RE = re.compile(r"^\s*(!\$(omp)?)", RE_FLAGS)
-
-
-class FprettifyException(Exception):
- """Base class for all custom exceptions"""
-
- def __init__(self, msg, filename, line_nr):
- super(FprettifyException, self).__init__(msg)
- self.filename = filename
- self.line_nr = line_nr
-
-
-class FprettifyParseException(FprettifyException):
- """Exception for unparseable Fortran code (user's fault)."""
-
- pass
-
-
-class FprettifyInternalException(FprettifyException):
- """Exception for potential internal errors (fixme's)."""
-
- pass
-
-
-class CharFilter(object):
- """
- An iterator to wrap the iterator returned by `enumerate`
- and ignore comments and characters inside strings
- """
-
- def __init__(self, it):
- self._it = it
- self._instring = ''
-
- def __iter__(self):
- return self
-
- def next(self):
- """ Python 2 compatibility """
- return self.__next__()
-
- def __next__(self):
- pos, char = next(self._it)
- if not self._instring and char == '!':
- raise StopIteration
-
- # detect start/end of a string
- if char in ['"', "'"]:
- if self._instring == char:
- self._instring = ''
- elif not self._instring:
- self._instring = char
-
- if self._instring:
- return self.__next__()
-
- return (pos, char)
-
-
-class InputStream(object):
- """Class to read logical Fortran lines from a Fortran file."""
-
- def __init__(self, infile, orig_filename=None):
- if not orig_filename:
- orig_filename = infile.name
- self.line_buffer = deque([])
- self.infile = infile
- self.line_nr = 0
- self.filename = orig_filename
- self.endpos = deque([])
- self.what_omp = deque([])
-
- def next_fortran_line(self):
- """Reads a group of connected lines (connected with &, separated by newline or semicolon)
- returns a touple with the joined line, and a list with the original lines.
- Doesn't support multiline character constants!
- """
- joined_line = ""
- comments = []
- lines = []
- continuation = 0
- instring = ''
-
- while 1:
- if not self.line_buffer:
- line = self.infile.readline().replace("\t", 8 * " ")
- self.line_nr += 1
- # convert OMP-conditional fortran statements into normal fortran statements
- # but remember to convert them back
- what_omp = ''
- if OMP_SUBS_RE.search(line):
- what_omp = OMP_SUBS_RE.search(line).group(1)
- line = line.replace(what_omp, '', 1)
- line_start = 0
- for pos, char in enumerate(line):
- if not instring and char in ['!', '#']:
- self.endpos.append(pos - 1 - line_start)
- self.line_buffer.append(line[line_start:])
- self.what_omp.append(what_omp)
- break
- if char in ['"', "'"]:
- if instring == char:
- instring = ''
- elif not instring:
- instring = char
- if not instring:
- if char == ';' or pos + 1 == len(line):
- self.endpos.append(pos - line_start)
- self.line_buffer.append(line[line_start:pos + 1])
- self.what_omp.append(what_omp)
- what_omp = ''
- line_start = pos + 1
-
- if instring:
- raise FprettifyInternalException(
- "multline strings not supported", self.filename, self.line_nr)
-
- if self.line_buffer:
- line = self.line_buffer.popleft()
- endpos = self.endpos.popleft()
- what_omp = self.what_omp.popleft()
-
- if not line:
- break
-
- lines.append(what_omp + line)
-
- line_core = line[:endpos + 1]
-
- try:
- if line[endpos + 1] in ['!', '#']:
- line_comments = line[endpos + 1:]
- else:
- line_comments = ''
- except IndexError:
- line_comments = ''
-
- if line_core:
- newline = (line_core[-1] == '\n')
- else:
- newline = False
-
- line_core = line_core.strip()
-
- if line_core:
- continuation = 0
- if line_core.endswith('&'):
- continuation = 1
-
- line_core = line_core.strip('&')
-
- comments.append(line_comments.rstrip('\n'))
- if joined_line.strip():
- joined_line = joined_line.rstrip(
- '\n') + line_core + '\n' * newline
- else:
- joined_line = what_omp + line_core + '\n' * newline
-
- if not continuation:
- break
-
- return (joined_line, comments, lines)
diff --git a/tools/prettify/prettify.py b/tools/prettify/prettify.py
index f89a974026..ecf4799108 100755
--- a/tools/prettify/prettify.py
+++ b/tools/prettify/prettify.py
@@ -19,6 +19,8 @@ except ImportError:
from prettify_cp2k import normalizeFortranFile
from prettify_cp2k import replacer
+
+sys.path.append(path.join(path.dirname(path.abspath(__file__)), "fprettify"))
from fprettify import reformat_ffile, fparse_utils, log_exception
@@ -357,7 +359,7 @@ def main(argv):
)
parser.add_argument("--indent", type=int, default=3)
- parser.add_argument("--whitespace", type=int, default=1, choices=range(3))
+ parser.add_argument("--whitespace", type=int, default=2, choices=range(0, 5))
parser.add_argument("--decl-linelength", type=int, default=100)
parser.add_argument("--decl-offset", type=int, default=50)
parser.add_argument("--backup-dir", type=abspath, default=abspath("preprettify"))
diff --git a/tools/prettify/prettify_cp2k/selftest.py b/tools/prettify/prettify_cp2k/selftest.py
index 6e39280647..c7d263b85d 100644
--- a/tools/prettify/prettify_cp2k/selftest.py
+++ b/tools/prettify/prettify_cp2k/selftest.py
@@ -27,7 +27,7 @@ CONTAINS
INTEGER, INTENT(IN) :: r, i, j, k
INTEGER :: l
- l = r+i+j+k
+ l = r + i + j + k
END FUNCTION
FUNCTION &
str_function(a) RESULT(l)
@@ -65,7 +65,7 @@ PROGRAM example_prog
! example 1.1
r = 1; i = -2; j = 3; k = 4; l = 5
r2 = 0.0_dp; r3 = 1.0_dp; r4 = 2.0_dp; r5 = 3.0_dp; r6 = 4.0_dp
- r1 = -(r2**i*(r3+r5*(-r4)-r6))-2.e+2
+ r1 = -(r2**i*(r3 + r5*(-r4) - r6)) - 2.e+2
IF (r .EQ. 2 .AND. r <= 5) i = 3
WRITE (*, *) (MERGE(3, 1, i <= 2))
WRITE (*, *) test_function(r, i, j, k)
@@ -117,15 +117,15 @@ PROGRAM example_prog
IF (i <= 2) THEN
m = 0
DO WHILE (m < 4)
- m = m+1
+ m = m + 1
DO k = 1, 3
- IF (k == 1) l = l+1
+ IF (k == 1) l = l + 1
END DO
ENDDO
ENDIF
ENDDO do_label
CASE (2)
- l = i+j+k
+ l = i + j + k
END SELECT
ENDDO
@@ -138,7 +138,7 @@ PROGRAM example_prog
DO i = 4, 5
DO my_integer = 1, 1
DO j = 1, 2
- WRITE (*, *) test_function(m, r, k, l)+i
+ WRITE (*, *) test_function(m, r, k, l) + i
ENDDO
ENDDO
ENDDO
@@ -151,33 +151,33 @@ PROGRAM example_prog
!************************************!
! example 3.1
- l = test_function(1, 2, test_function(1, 2, 3, 4), 4)+3*(2+1)
+ l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + 3*(2 + 1)
- l = test_function(1, 2, test_function(1, 2, 3, 4), 4)+ &
- 3*(2+1)
+ l = test_function(1, 2, test_function(1, 2, 3, 4), 4) + &
+ 3*(2 + 1)
l = test_function(1, 2, &
- test_function(1, 2, 3, 4), 4)+ &
- 3*(2+1)
+ test_function(1, 2, 3, 4), 4) + &
+ 3*(2 + 1)
l = test_function(1, 2, &
test_function(1, 2, 3, &
- 4), 4)+ &
- 3*(2+1)
+ 4), 4) + &
+ 3*(2 + 1)
! example 3.2
- arr = [1, (/3, 4, 5/), 6]+[1, 2, 3, 4, 5]
+ arr = [1, (/3, 4, 5/), 6] + [1, 2, 3, 4, 5]
arr = [1, (/3, 4, 5/), &
- 6]+[1, 2, 3, 4, 5]
+ 6] + [1, 2, 3, 4, 5]
arr = [1, (/3, 4, 5/), &
- 6]+ &
+ 6] + &
[1, 2, 3, 4, 5]
arr = [1, (/3, 4, &
5/), &
- 6]+ &
+ 6] + &
[1, 2, 3, 4, 5]
! example 3.3
@@ -203,7 +203,7 @@ PROGRAM example_prog
DO i = 1, 100; IF (i <= 2) THEN ! comment
DO j = 1, 5
DO k = 1, 3
- l = l+1
+ l = l + 1
! unindented comment
! indented comment
END DO; ENDDO
@@ -214,7 +214,7 @@ PROGRAM example_prog
ENDIF
ENDDO
CASE (2)
- l = i+j+k
+ l = i + j + k
END SELECT
ENDDO
@@ -227,12 +227,12 @@ PROGRAM example_prog
IF (k == 1) &
l = test_function(1, &
test_function(r=4, i=5, &
- j=6, k=test_function(1, 2*(3*(1+1)), str_function(")a!(b['(;=dfe"), &
- 9)+ &
+ j=6, k=test_function(1, 2*(3*(1 + 1)), str_function(")a!(b['(;=dfe"), &
+ 9) + &
test_function(1, 2, 3, 4)), 9, 10) &
! test_function(1,2,3,4)),9,10) &
- ! +13*str_function('') + str_function('"')
- +13*str_function('')+str_function('"')
+ ! + 13*str_function('') + str_function('"')
+ + 13*str_function('') + str_function('"')
END & ! comment
! comment
DO
@@ -241,7 +241,7 @@ PROGRAM example_prog
! example 4.3
arr = [1, (/3, 4, &
5/), &
- 6]+ &
+ 6] + &
[1, 2, 3, 4, 5]; arr = [1, 2, &
3, 4, 5]