diff --git a/scripts/fortran_tools/fortran_write.py b/scripts/fortran_tools/fortran_write.py index e147b48c..35d403e0 100644 --- a/scripts/fortran_tools/fortran_write.py +++ b/scripts/fortran_tools/fortran_write.py @@ -19,13 +19,15 @@ class FortranWriter: ########################################################################### # Class variables ########################################################################### - __INDENT = 3 # Spaces per indent level + __INDENT = 2 # Spaces per indent level - __CONTINUE_INDENT = 5 # Extra spaces on continuation line + __CONTINUE_INDENT = 4 # Extra spaces on continuation line __LINE_FILL = 97 # Target line length - __LINE_MAX = 130 # Max line length + __LINE_MAX = 120 # Max line length (for Codee) + + __BREAK_CHARS = [',', '+', '*', '/', '(', ')'] # CCPP copyright statement to be included in all generated Fortran files __COPYRIGHT = '''! @@ -38,8 +40,7 @@ class FortranWriter: ! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -''' +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''' __MOD_HEADER = ''' !> @@ -52,7 +53,7 @@ class FortranWriter: __MOD_PREAMBLE = ["implicit none", "private"] __CONTAINS = ''' -CONTAINS''' +contains''' __MOD_FOOTER = ''' end module {module}''' @@ -140,18 +141,12 @@ def write(self, statement, indent_level, continue_line=False): ostmt = statement.strip() is_comment_stmt = ostmt and (ostmt[0] == '!') in_comment = "" - if ostmt and (ostmt[0] != '&'): - # Skip indent for continue that is in the middle of a - # token or a quoted region - outstr = istr + ostmt - else: - outstr = ostmt - # end if + outstr = istr + ostmt line_len = len(outstr) if line_len > self.__line_fill: # Collect pretty break points - spaces = list() - commas = list() + spaces = [] + break_chars = [] sptr = len(istr) in_single_char = False in_double_char = False @@ -180,12 +175,12 @@ def write(self, statement, indent_level, continue_line=False): elif outstr[sptr] == ' ': # Non-quote spaces are where we can break spaces.append(sptr) - elif outstr[sptr] == ',': - # Non-quote commas are where we can break - commas.append(sptr) elif outstr[sptr:sptr+2] == '//': - # Non-quote commas are where we can break - commas.append(sptr + 1) + # Non-quote syntax are where we can break + break_chars.append(sptr + 1) + elif outstr[sptr] in FortranWriter.__BREAK_CHARS: + # Non-quote syntax are where we can break + break_chars.append(sptr) # End if (no else, other characters will be ignored) sptr = sptr + 1 # End while @@ -203,7 +198,7 @@ def write(self, statement, indent_level, continue_line=False): # end if best = self.find_best_break(spaces) if best >= self.__line_fill: - best = min(best, self.find_best_break(commas)) + best = min(best, self.find_best_break(break_chars)) # End if line_continue = False if best >= self.__line_max: @@ -216,10 +211,13 @@ def write(self, statement, indent_level, continue_line=False): # end if if len(outstr) > best: if self._in_quote(outstr[0:best+1]): + if best >= FortranWriter.__LINE_MAX - 1: + best = FortranWriter.__LINE_MAX - 2 + # end if line_continue = '&' elif not outstr[best+1:].lstrip(): - # If the next line is empty, the current line is done - # and is equal to the max line length. Do not use + # If the next line is empty, the current line is done + # and is equal to the max line length. Do not use # continue and set best to line_max (best+1) line_continue = False best = best+1 @@ -233,24 +231,27 @@ def write(self, statement, indent_level, continue_line=False): if in_comment or is_comment_stmt: line_continue = False # end if - if line_continue: - fill = "{}&".format((self.__line_fill - best)*' ') + if line_continue == '&': + fill = '&' + elif line_continue: + fill = ' &' else: fill = "" # End if - outline = f"{outstr[0:best+1]}{fill}".rstrip() + outline = f"{outstr[0:best+1].rstrip()}{fill}" self.__file.write(f"{outline}\n") if best <= 0: imsg = "Internal ERROR: Unable to break line" raise ValueError(f"{imsg}, '{statement}'") # end if statement = in_comment + outstr[best+1:] - if isinstance(line_continue, str) and statement: + if isinstance(line_continue, str) and statement.strip(): statement = line_continue + statement # end if - self.write(statement, indent_level, continue_line=line_continue) + if statement.strip(): + self.write(statement, indent_level, continue_line=line_continue) else: - self.__file.write("{}\n".format(outstr)) + self.__file.write(f"{outstr}\n") # End if # End if diff --git a/scripts/host_cap.py b/scripts/host_cap.py index b06906fe..fb2e7012 100644 --- a/scripts/host_cap.py +++ b/scripts/host_cap.py @@ -732,12 +732,12 @@ def write_host_cap(host_model, api, module_name, output_dir, run_env): cap.write("return", 4) cap.write("end if", 3) # Allocate the suite's dynamic constituents array - size_string = "0+" + size_string = "0 +" for var in host_local_vars.variable_list(): vtype = var.get_prop_value('type') if vtype == 'ccpp_constituent_properties_t': local_name = var.get_prop_value('local_name') - size_string += f"size({local_name})+" + size_string += f"size({local_name}) +" # end if # end for if not has_dyn_consts: diff --git a/test/unit_tests/sample_files/fortran_files/comments_test.F90 b/test/unit_tests/sample_files/fortran_files/comments_test.F90 index d4820a36..e74410e7 100644 --- a/test/unit_tests/sample_files/fortran_files/comments_test.F90 +++ b/test/unit_tests/sample_files/fortran_files/comments_test.F90 @@ -10,24 +10,25 @@ ! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - !> !! @brief Auto-generated Test of comment writing for FortranWriter !! ! module comments_test +! codee format off ! We can write comments in the module header - ! We can write indented comments in the header - integer :: foo ! Comment at end of line works - integer :: bar ! - ! xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - ! - integer :: baz ! - ! yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy - ! yyyyy +! codee format on + ! We can write indented comments in the header + integer :: foo ! Comment at end of line works + integer :: bar ! + ! xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ! + integer :: baz ! + ! yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + ! yyyyyyyyyyyyyy -CONTAINS - ! We can write comments in the module body +contains + ! We can write comments in the module body end module comments_test diff --git a/test/unit_tests/sample_files/fortran_files/linebreak_test.F90 b/test/unit_tests/sample_files/fortran_files/linebreak_test.F90 index 4f89441f..2b8f0b74 100644 --- a/test/unit_tests/sample_files/fortran_files/linebreak_test.F90 +++ b/test/unit_tests/sample_files/fortran_files/linebreak_test.F90 @@ -10,30 +10,43 @@ ! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - !> !! @brief Auto-generated Test of line breaking for FortranWriter !! ! module linebreak_test - character(len=7) :: data = (/ name000, name001, name002, name003, name004, name005, name006, & - name007, name008, name009, name010, name011, name012, name013, name014, name015, & - name016, name017, name018, name019, name020, name021, name022, name023, name024, & - name025, name026, name027, name028, name029, name030, name031, name032, name033, & - name034, name035, name036, name037, name038, name039, name040, name041, name042, & - name043, name044, name045, name046, name047, name048, name049, name050, name051, & - name052, name053, name054, name055, name056, name057, name058, name059, name060, & - name061, name062, name063, name064, name065, name066, name067, name068, name069, & - name070, name071, name072, name073, name074, name075, name076, name077, name078, & - name079, name080, name081, name082, name083, name084, name085, name086, name087, & - name088, name089, name090, name091, name092, name093, name094, name095, name096, & - name097, name098, name099 /) + character(len=7) :: data(100) = (/ 'name000', 'name001', 'name002', 'name003', 'name004', & + 'name005', 'name006', 'name007', 'name008', 'name009', 'name010', 'name011', 'name012', & + 'name013', 'name014', 'name015', 'name016', 'name017', 'name018', 'name019', 'name020', & + 'name021', 'name022', 'name023', 'name024', 'name025', 'name026', 'name027', 'name028', & + 'name029', 'name030', 'name031', 'name032', 'name033', 'name034', 'name035', 'name036', & + 'name037', 'name038', 'name039', 'name040', 'name041', 'name042', 'name043', 'name044', & + 'name045', 'name046', 'name047', 'name048', 'name049', 'name050', 'name051', 'name052', & + 'name053', 'name054', 'name055', 'name056', 'name057', 'name058', 'name059', 'name060', & + 'name061', 'name062', 'name063', 'name064', 'name065', 'name066', 'name067', 'name068', & + 'name069', 'name070', 'name071', 'name072', 'name073', 'name074', 'name075', 'name076', & + 'name077', 'name078', 'name079', 'name080', 'name081', 'name082', 'name083', 'name084', & + 'name085', 'name086', 'name087', 'name088', 'name089', 'name090', 'name091', 'name092', & + 'name093', 'name094', 'name095', 'name096', 'name097', 'name098', 'name099' /) + +contains -CONTAINS - call & - endrun('Cannot read columns_on_task from file'// & - ', columns_on_task has no horizontal dimension; columns_on_task is a protected variable') + subroutine foo(ozone_constituents, aerosol_constituents, volcaero_constituents, & + other_constituents) + integer, intent(in) :: ozone_constituents(:) + integer, intent(in) :: aerosol_constituents(:) + integer, intent(in) :: volcaero_constituents(:) + integer, intent(in) :: other_constituents(:) + real, allocatable :: tracer_data_test_dynamic_constituents(:) +! codee format off + allocate(tracer_data_test_dynamic_constituents(0+size(ozone_constituents)+size( & + aerosol_constituents)+size(volcaero_constituents)+size(other_constituents))) + write(6, '(a)') & + 'Cannot read columns_on_task from file'// & + ', columns_on_task has no horizontal dimension; columns_on_task is a protected variable' +! codee format on + end subroutine foo end module linebreak_test diff --git a/test/unit_tests/sample_files/fortran_files/long_string_test.F90 b/test/unit_tests/sample_files/fortran_files/long_string_test.F90 index 2910fe53..17fd17fd 100644 --- a/test/unit_tests/sample_files/fortran_files/long_string_test.F90 +++ b/test/unit_tests/sample_files/fortran_files/long_string_test.F90 @@ -10,102 +10,88 @@ ! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - !> !! @brief Auto-generated Test of long string breaking for FortranWriter !! ! module long_string_test - foo100 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' - - foo101 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' - - foo102 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901' - - foo103 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012' - - foo104 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123' - - foo105 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234' - - foo106 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345' - - foo107 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456' - - foo108 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567' - - foo109 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678' - - foo110 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' - - foo111 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' - - foo112 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901' - - foo113 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012' - - foo114 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123' - - foo115 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234' - - foo116 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345' - - foo117 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456' - - foo118 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567' - - foo119 = & - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678' - - foo120 = & - '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' - - foo121 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&' - foo122 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&1' - foo123 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&12' - foo124 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&123' - foo125 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&1234' - foo126 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&12345' - foo127 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&123456' - foo128 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&1234567' - foo129 = & - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890& -&12345678' + character(len=100) :: foo100 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' + character(len=101) :: foo101 = & + '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' + character(len=102) :: foo102 = & + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901' + character(len=103) :: foo103 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012' + character(len=104) :: foo104 = & + '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123' + character(len=105) :: foo105 = & + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234' + character(len=106) :: foo106 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345' + character(len=107) :: foo107 = & + '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456' + character(len=108) :: foo108 = & + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567' + character(len=109) :: foo109 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678' + character(len=110) :: foo110 = & + '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' + character(len=111) :: foo111 = & + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' + character(len=112) :: foo112 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901' + character(len=113) :: foo113 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &2' + character(len=114) :: foo114 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &23' + character(len=115) :: foo115 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &234' + character(len=116) :: foo116 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &2345' + character(len=117) :: foo117 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &23456' + character(len=118) :: foo118 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &234567' + character(len=119) :: foo119 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &2345678' + character(len=120) :: foo120 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &23456789' + character(len=121) :: foo121 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &234567890' + character(len=122) :: foo122 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &2345678901' + character(len=123) :: foo123 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &23456789012' + character(len=124) :: foo124 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &234567890123' + character(len=125) :: foo125 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &2345678901234' + character(len=126) :: foo126 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &23456789012345' + character(len=127) :: foo127 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &234567890123456' + character(len=128) :: foo128 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &2345678901234567' + character(len=129) :: foo129 = & + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901& + &23456789012345678' end module long_string_test diff --git a/test/unit_tests/test_fortran_write.py b/test/unit_tests/test_fortran_write.py index c2248896..49f551f7 100644 --- a/test/unit_tests/test_fortran_write.py +++ b/test/unit_tests/test_fortran_write.py @@ -78,15 +78,32 @@ def test_line_breaking(self): header = "Test of line breaking for FortranWriter" with FortranWriter(generate, 'w', header, f"{testname}") as gen: # Test long declaration - data_items = ', '.join([f"name{x:03}" for x in range(100)]) - gen.write(f"character(len=7) :: data = (/ {data_items} /)", 1) + qchr = "'" + nditems = 100 + data_items = ', '.join([f"{qchr}name{x:03}{qchr}" for x in range(nditems)]) + gen.write(f"character(len=7) :: data({nditems}) = (/ {data_items} /)", 1) gen.end_module_header() # Test long code lines - line_items = ["call endrun('Cannot read columns_on_task from ", + gen.blank_line() + gen.write("subroutine foo(ozone_constituents, aerosol_constituents, " + "volcaero_constituents, other_constituents)", 1) + gen.write("integer, intent(in) :: ozone_constituents(:)", 2) + gen.write("integer, intent(in) :: aerosol_constituents(:)", 2) + gen.write("integer, intent(in) :: volcaero_constituents(:)", 2) + gen.write("integer, intent(in) :: other_constituents(:)", 2) + gen.write("real, allocatable :: tracer_data_test_dynamic_constituents(:)", 2) + gen.comment("codee format off", 0) + gen.write("allocate(tracer_data_test_dynamic_constituents(0+" + "size(ozone_constituents)+size(aerosol_constituents)" + "+size(volcaero_constituents)+size(other_constituents)))", 2) + gen.blank_line() + line_items = ["write(6, '(a)') 'Cannot read columns_on_task from ", "file'//', columns_on_task has no horizontal ", "dimension; columns_on_task is a ", - "protected variable')"] + "protected variable'"] gen.write(f"{''.join(line_items)}", 2) + gen.comment("codee format on", 0) + gen.write("end subroutine foo", 1) # end with # Check that file was generated @@ -104,7 +121,9 @@ def test_good_comments(self): # Exercise header = "Test of comment writing for FortranWriter" with FortranWriter(generate, 'w', header, f"{testname}") as gen: + gen.comment("codee format off", 0) gen.comment("We can write comments in the module header", 0) + gen.comment("codee format on", 0) gen.comment("We can write indented comments in the header", 1) gen.write("integer :: foo ! Comment at end of line works", 1) # Test long comments at end of line @@ -130,11 +149,11 @@ def test_long_strings(self): generate = os.path.join(_TMP_DIR, f"{testname}.F90") # Exercise header = "Test of long string breaking for FortranWriter" - foostr = ''.join(['0123456789']*10) + foostr = '0123456789'*10 nxtchr = ord('0') with FortranWriter(generate, 'w', header, f"{testname}") as gen: while len(foostr) < 130: - gen.write(f"foo{len(foostr)} = '{foostr}'", 1) + gen.write(f"character(len={len(foostr)}) :: foo{len(foostr)} = '{foostr.strip()}'", 1) foostr += chr(nxtchr) nxtchr += 1 if nxtchr > ord('9'):