@@ -1237,47 +1237,40 @@ def parse(
1237
1237
pp_skips = []
1238
1238
pp_defines = []
1239
1239
1240
- line_ind = 0
1241
- next_line_ind = 0
1242
- line_number = 1
1240
+ line_number = 0
1243
1241
block_id_stack = []
1244
- semi_split = []
1245
1242
doc_string : str = None
1246
1243
counters = Counter (
1247
- # line_no=1,
1248
- # line_idx=0,
1249
- # next_line_idx=0,
1250
1244
do = 0 ,
1251
1245
ifs = 0 ,
1252
1246
block = 0 ,
1253
1247
select = 0 ,
1254
1248
interface = 0 ,
1255
1249
)
1250
+ multi_lines = deque ()
1256
1251
self .COMMENT_LINE_MATCH , self .DOC_COMMENT_MATCH = self .get_comment_regexs ()
1257
- while (next_line_ind < self .nLines ) or ( len ( semi_split ) > 0 ) :
1252
+ while (line_number < self .nLines ) or multi_lines :
1258
1253
# Get next line
1259
- if len (semi_split ) > 0 :
1260
- line = semi_split [0 ]
1261
- semi_split = semi_split [1 :]
1262
- get_full = False
1263
- else :
1264
- line_ind = next_line_ind
1265
- line_number = line_ind + 1
1266
- line = self .get_line (line_ind , pp_content = True )
1267
- next_line_ind = line_ind + 1
1254
+ # Get a normal line, i.e. the stack is empty
1255
+ if not multi_lines :
1256
+ # get_line has a 0-based index
1257
+ line = self .get_line (line_number , pp_content = True )
1258
+ line_number += 1
1268
1259
get_full = True
1260
+ # Line is part of a multi-line construct, i.e. contained ';'
1261
+ else :
1262
+ line = multi_lines .pop ()
1263
+ get_full = False
1264
+
1269
1265
if line == "" :
1270
1266
continue # Skip empty lines
1271
1267
# Parse Documentation comments and skip all other comments
1272
1268
# this function should also nullify doc_string
1273
- idx = self ._parse_documentation (
1274
- line , line_number , file_ast , doc_string , next_line_ind
1275
- )
1269
+ idx = self ._parse_docs (line , line_number , file_ast , doc_string )
1276
1270
if idx :
1277
- next_line_ind = idx [0 ]
1271
+ line_number = idx [0 ]
1278
1272
doc_string = idx [1 ]
1279
1273
continue
1280
- # Handle trailing doc strings
1281
1274
if doc_string :
1282
1275
file_ast .add_doc ("!! " + doc_string )
1283
1276
self .parser_debug ("Doc" , doc_string , line_number )
@@ -1292,14 +1285,14 @@ def parse(
1292
1285
do_skip = True
1293
1286
if do_skip :
1294
1287
continue
1295
- # Get full line
1288
+ # Get full line, seek forward for code lines
1289
+ # @note line_number-1 refers to the array index for the current line
1296
1290
if get_full :
1297
1291
_ , line , post_lines = self .get_code_line (
1298
- line_ind , backward = False , pp_content = True
1292
+ line_number - 1 , backward = False , pp_content = True
1299
1293
)
1300
- next_line_ind += len (post_lines )
1294
+ line_number += len (post_lines )
1301
1295
line = "" .join ([line ] + post_lines )
1302
- # print(line)
1303
1296
line , line_label = strip_line_label (line )
1304
1297
line_stripped = strip_strings (line , maintain_len = True )
1305
1298
# Find trailing comments
@@ -1308,29 +1301,17 @@ def parse(
1308
1301
line_no_comment = line [:comm_ind ]
1309
1302
line_post_comment = line [comm_ind :]
1310
1303
line_stripped = line_stripped [:comm_ind ]
1304
+ # Look for trailing doc string
1305
+ doc_match = FRegex .FREE_DOC .match (line_post_comment )
1306
+ if doc_match :
1307
+ doc_string = line_post_comment [doc_match .end (0 ) :].strip ()
1311
1308
else :
1312
1309
line_no_comment = line
1313
- line_post_comment = None
1314
- # Split lines with semicolons
1315
- semi_colon_ind = line_stripped .find (";" )
1316
- if semi_colon_ind > 0 :
1317
- semi_inds = []
1318
- tmp_line = line_stripped
1319
- while semi_colon_ind >= 0 :
1320
- semi_inds .append (semi_colon_ind )
1321
- tmp_line = tmp_line [semi_colon_ind + 1 :]
1322
- semi_colon_ind = tmp_line .find (";" )
1323
- i0 = 0
1324
- for semi_colon_ind in semi_inds :
1325
- semi_split .append (line [i0 : i0 + semi_colon_ind ])
1326
- i0 += semi_colon_ind + 1
1327
- if len (semi_split ) > 0 :
1328
- semi_split .append (line [i0 :])
1329
- line = semi_split [0 ]
1330
- semi_split = semi_split [1 :]
1331
- line_stripped = strip_strings (line , maintain_len = True )
1332
- line_no_comment = line
1333
- line_post_comment = None
1310
+ # Split lines with semicolons, place the multiple lines into a stack
1311
+ if line_stripped .find (";" ) >= 0 :
1312
+ multi_lines .extendleft (line_stripped .split (";" ))
1313
+ line = multi_lines .pop ()
1314
+ line_stripped = line
1334
1315
self .line = line
1335
1316
# Test for scope end
1336
1317
if file_ast .END_SCOPE_REGEX is not None :
@@ -1355,11 +1336,6 @@ def parse(
1355
1336
# Mark contains statement
1356
1337
if self ._parse_contains (line_no_comment , line_number , file_ast ):
1357
1338
continue
1358
- # Look for trailing doc string
1359
- if line_post_comment :
1360
- doc_match = FRegex .FREE_DOC .match (line_post_comment )
1361
- if doc_match :
1362
- doc_string = line_post_comment [doc_match .end (0 ) :].strip ()
1363
1339
# Loop through tests
1364
1340
obj_read = self .get_fortran_definition (line )
1365
1341
# Move to next line if nothing in the definition tests matches
@@ -1754,21 +1730,20 @@ def _parse_contains(self, line: str, ln: int, file_ast: fortran_ast):
1754
1730
self .parser_debug ("CONTAINS" , self .line , ln )
1755
1731
return True
1756
1732
1757
- def _parse_documentation (
1733
+ def _parse_docs (
1758
1734
self ,
1759
1735
line : str ,
1760
1736
ln : int ,
1761
1737
file_ast : fortran_ast ,
1762
1738
doc_string : str ,
1763
- next_ln_idx : int ,
1764
1739
):
1765
1740
match = self .COMMENT_LINE_MATCH .match (line )
1766
1741
if not match :
1767
1742
return False
1768
1743
# Check for documentation
1769
1744
doc_match = self .DOC_COMMENT_MATCH .match (line )
1770
1745
if not doc_match :
1771
- return next_ln_idx , doc_string
1746
+ return ln , doc_string
1772
1747
doc_lines = [line [doc_match .end (0 ) :].strip ()]
1773
1748
if doc_match .group (1 ) == ">" :
1774
1749
doc_forward = True
@@ -1777,25 +1752,26 @@ def _parse_documentation(
1777
1752
doc_lines = [doc_string ] + doc_lines
1778
1753
doc_string = None
1779
1754
doc_forward = False
1780
- if next_ln_idx < self .nLines :
1781
- next_line = self .get_line (next_ln_idx , pp_content = True )
1782
- next_ln_idx += 1
1783
- doc_match = self .DOC_COMMENT_MATCH .match (next_line )
1784
- while doc_match and (next_ln_idx < self .nLines ):
1785
- doc_lines .append (next_line [doc_match .end (0 ) :].strip ())
1786
- next_line = self .get_line (next_ln_idx , pp_content = True )
1787
- next_ln_idx += 1
1788
- doc_match = self .DOC_COMMENT_MATCH .match (next_line )
1789
- next_ln_idx -= 1
1755
+ _ln = ln
1756
+ if ln < self .nLines :
1757
+ for i in range (ln , self .nLines ):
1758
+ # @note this gets the next line, index is 0-based
1759
+ next_line = self .get_line (i , pp_content = True )
1760
+ match = self .DOC_COMMENT_MATCH .match (next_line )
1761
+ if not match :
1762
+ ln = i # move the line number at the end of the docstring
1763
+ break
1764
+ doc_lines .append (next_line [match .end (0 ) :].strip ())
1765
+
1790
1766
# Count the total length of all the stings in doc_lines
1791
1767
# most efficient implementation, see: shorturl.at/dfmyV
1792
1768
if len ("" .join (doc_lines )) > 0 :
1793
1769
file_ast .add_doc ("!! " + "\n !! " .join (doc_lines ), forward = doc_forward )
1794
1770
# if debug:
1795
1771
for (i , doc_line ) in enumerate (doc_lines ):
1796
- log .debug (f"{ doc_line } !!! Doc string({ ln + i } )" )
1772
+ log .debug (f"{ doc_line } !!! Doc string({ _ln + i } )" )
1797
1773
# self.parser_debug("Doc", doc_line, line_number + i)
1798
- return next_ln_idx , doc_string
1774
+ return ln , doc_string
1799
1775
1800
1776
@staticmethod
1801
1777
def parser_debug (msg : str , line : str , ln : int , scope : bool = False ):
0 commit comments