75 def indent(self, nodeToUpdate=None, indentProgramunit=0, indentBranch=2,
82 nodeToUpdate : Element, optional
83 Specific node to indent. If None, indents entire scope.
84 indentProgramunit : int, optional
85 Number of spaces for program unit contents. Default is 0.
86 indentBranch : int, optional
87 Number of spaces for nested constructs (do, if, etc.). Default is 2.
88 exclDirectives : list, optional
89 Lines to exclude from indentation:
90 - None: Exclude '!$OMP' lines (default).
91 - []: Include all lines.
92 - ['!$acc', '!$mnh']: Custom directive list.
97 Returns self for chaining.
101 >>> pft = PYFT('input.F90')
102 >>> pft.indent() # Default indentation
103 >>> pft.indent(indentProgramunit=0, indentBranch=4) # Custom
106 if nodeToUpdate
is None:
109 if exclDirectives
is None:
110 exclDirectives = [
'!$OMP']
112 def setLevel(elem, level, nextElem):
114 :param elem: element whose tail must be modifies
115 :param level: level of indentation
116 :param nextElem: next element
118 if elem.tail
is not None:
119 elem.tail = elem.tail.replace(
'\t',
' ')
120 excl = (nextElem
is not None and
121 (tag(nextElem) ==
'cpp' or
122 tag(nextElem) ==
'C' and
123 any(nextElem.text.startswith(d)
for d
in exclDirectives)))
125 elem.tail = re.sub(
'\n[ ]*',
'\n' +
' ' * level, elem.tail)
127 def indentRecur(elem, level, inConstruct):
129 :param elem: dom element
130 :param level: current level for elem
131 :param inConstruct: True if we are inside a construct
133 blocs = [
'file',
'program-unit',
'if-block',
'where-block',
'selectcase-block']
134 progstmt = [
'subroutine-stmt',
'program-stmt',
'module-stmt',
'function-stmt',
135 'submodule-stmt',
'procedure-stmt',
'interface-stmt']
136 endprogstmt = [
'end-' + s
for s
in progstmt]
137 interbranchstmt = [
'else-stmt',
'else-if-stmt',
'else-where-stmt']
138 branchstmt = [
'if-then-stmt',
'where-construct-stmt'] + interbranchstmt
139 endbranchstmt = [
'end-if-stmt',
'end-where-stmt']
143 firstnumselect =
True
144 for ie, sElem
in enumerate(elem):
147 if tag(sElem)
in progstmt:
148 currlevel += indentProgramunit
149 elif tag(sElem)
in branchstmt + [inConstruct +
'-stmt']:
150 currlevel += indentBranch
153 setLevel(sElem, currlevel, elem[ie + 1]
if ie + 1 < len(elem)
else None)
155 if tag(elem) ==
'selectcase-construct':
173 firstnumselect =
False
177 setLevel(laste[-1], level + indentBranch, sElem)
179 indentRecur(sElem, level + indentBranch * 2,
"")
180 if tag(sElem[-1]) ==
'end-select-case-stmt':
181 setLevel(sElem[-2], level, sElem[-1])
183 elif tag(sElem)
in blocs
or tag(sElem).endswith(
'-construct'):
185 if tag(sElem[0])
in interbranchstmt:
194 setLevel(laste[-1], level, sElem)
195 construct = tag(sElem)[:-10]
if tag(sElem).endswith(
'-construct')
else ""
196 indentRecur(sElem, currlevel, construct)
200 if tag(sElem)
in endprogstmt + endbranchstmt + [
'end-' + inConstruct +
'-stmt']:
201 setLevel(laste, level, sElem)
204 indentRecur(nodeToUpdate, 0,
"")
281 removeALL=False, addBegin=True, removeBegin=False):
283 :param nodeToUpdate: if None, the entire xml is updated
284 :param align: True to align begin of continued lines
285 :param removeALL: True to suppress all the continuation line characters ('&')
286 :param addBegin: True to add missing continuation line characters ('&')
287 at the begining of lines
288 :param removeBegin: True to suppress continuation line characters ('&')
289 at the begining of lines
291 When suppressed, the '&' are replaced by a space character
292 Comments after a '&' are lost
295 assert not (align
and removeALL),
"We cannot remove and align at the same time"
296 assert not (addBegin
and (removeALL
or removeBegin)), \
297 "We cannot remove and add, at the same time, continuation characters"
299 if nodeToUpdate
is None:
304 def recurReverse(elem, tail):
305 for ie
in range(len(elem))[::-1]:
307 parents[sElem] = elem
308 if tag(sElem) ==
'cnt':
312 while j < len(elem)
and tag(elem[j])
in (
'C',
'cpp'):
313 commentsAfter.append(elem[j])
315 nextNode = elem[j]
if j < len(elem)
else None
318 isend = ((sElem.tail
is not None and '\n' in sElem.tail)
or
319 len(commentsAfter) > 0)
322 if isend
and addBegin:
323 if sElem.tail
is not None and \
324 sElem.tail.replace(
'\n',
'').replace(
'\t',
'').lstrip(
' ') !=
'':
327 new = createElem(
'cnt', text=
'&')
330 while sElem.tail[i]
in (
' ',
'\n',
'\t'):
332 new.tail =
' ' + sElem.tail[i:]
333 sElem.tail = sElem.tail[:i]
334 elem.insert(ie + 1, new)
335 elif tag(nextNode) !=
'cnt':
337 new = createElem(
'cnt', text=
'&')
338 if len(commentsAfter) > 0:
342 while i < len(commentsAfter[-1].tail)
and \
343 commentsAfter[-1].tail[i]
in (
' ',
'\n',
'\t'):
345 new.tail =
' ' + commentsAfter[-1].tail[i:]
346 commentsAfter[-1].tail = commentsAfter[-1].tail[:i]
349 elem.insert(ie + 1 + len(commentsAfter), new)
352 if removeALL
or (removeBegin
and not isend):
354 for com
in commentsAfter[::-1]:
355 if tag(com) !=
'cpp':
362 if sElem.tail
is not None:
363 txt = sElem.tail.strip() +
' '
367 if elem[ie - 1].tail
is None:
368 elem[ie - 1].tail = txt
370 elem[ie - 1].tail += txt
374 recurReverse(sElem, tail)
376 def recurDirect(elem, ct, inCnt):
378 :param ct: current text
379 :param inCnt: -1 if we are not in a statement spanning several lines
380 elswhere contains the number of spaces to add
382 ignoreComment =
False
384 for ie, sElem
in enumerate(list(elem)):
386 isendcnt = tag(sElem) ==
'cnt' and \
387 ((sElem.tail
is not None and '\n' in sElem.tail)
or
388 (ie + 1 < len(elem)
and tag(elem[ie + 1]) ==
'C'))
389 ignoreComment = (ignoreComment
or
391 (ie + 1 < len(elem)
and tag(elem[ie + 1]) ==
'C')))
400 if isendcnt
or ignoreComment
or (inCnt != -1
and tag(sElem) ==
'cpp'):
403 if isendcnt
and inCnt == -1:
406 while not tag(topstmt).endswith(
'-stmt'):
407 topstmt = parents[topstmt]
410 if tag(topstmt) ==
'a-stmt':
411 patList = (
'=>',
'=',
r'\(')
412 elif tag(topstmt) ==
'call-stmt':
413 patList = (
r'\(',
r'call[ ]+\w',
'call ',
'call')
414 elif tag(topstmt) ==
'if-stmt':
415 patList = (
r'\(',
r'\)',
'if ',
'if')
416 elif tag(topstmt) ==
'where-stmt':
417 patList = (
r'\(',
r'\)',
'where ',
'where')
418 elif tag(topstmt) ==
'forall-stmt':
419 patList = (
r'\(',
r'\)',
'forall ',
'forall')
420 elif tag(topstmt) ==
'namelist-stmt':
421 patList = (
'/.*/',
'/',
'namelist')
422 elif tag(topstmt) ==
'subroutine-stmt':
423 patList = (
r'\(',
r'subroutine[ ]+\w',
'subroutine ',
'subroutine')
424 elif tag(topstmt) ==
'use-stmt':
425 patList = (
':',
r'use[ ]+\w',
'use ',
'use')
426 elif tag(topstmt) ==
'T-decl-stmt':
427 patList = (
'::',
r'\w,',
r'\w ',
r'\w')
428 elif tag(topstmt) ==
'print-stmt':
429 patList = (
'print', )
430 elif tag(topstmt) ==
'write-stmt':
431 patList = (
r'\)',
r'write[ ]*\(',
'write[ ]*',
'write')
432 elif tag(topstmt) ==
'procedure-stmt':
433 patList = (
'module[ ]+procedure[ ]*',
'module[ ]*',
'module')
435 patList = (
'::',
':',
r'\(',
'=>',
'=',
'[',
':',
'/')
441 mat = re.search(pat, ct, flags=re.IGNORECASE)
443 if ie + 1 < len(elem)
and tag(elem[ie + 1]) !=
'cnt':
449 inCnt = mat.end() - 1
454 if not (ie + 1 < len(elem)
and tag(elem[ie + 1]) ==
'cpp'):
455 if sElem.tail
is not None:
456 sElem.tail = re.sub(
'\n[ ]*',
'\n' +
' ' * inCnt, sElem.tail)
458 sElem.tail =
'\n' +
' ' * inCnt
460 if tag(sElem)
not in (
'C',
'cnt'):
461 ct += (sElem.text
if sElem.text
is not None else '')
462 ignoreComment =
False
466 ct, inCnt = recurDirect(sElem, ct, inCnt)
469 ct += (sElem.tail
if sElem.tail
is not None else '')
471 ct = ct.split(
'\n')[-1]
472 if tag(sElem)
not in (
'cnt',
'C',
'cpp'):
477 recurReverse(nodeToUpdate, 0)
478 recurDirect(nodeToUpdate,
"", -1)
485 beforeComma=0, afterComma=1,
486 beforeParenthesis=0, afterParenthesis=0,
487 beforeAffectation=1, afterAffectation=1, inAffectation=True,
488 beforeRangeDelim=0, afterRangeDelim=0,
489 beforeUseDelim=0, afterUseDelim=1,
490 beforeDeclDelim=1, afterDeclDelim=1,
491 inDeclDelim=True, afterTypeDecl=1,
492 beforeEqDo=0, afterEqDo=0,
493 beforeEqCall=0, afterEqCall=0,
494 beforeEqInit=0, afterEqInit=0,
495 beforeEndcnt=1, afterBegincnt=1,
496 afterIfwherecase=1, beforeThen=1, beforeIfaction=1,
498 endOfLine=True, afterName=0, inName=True,
499 beforeCmdsep=0, afterCmdsep=1,
500 adjacentKeywords=__NO_VALUE__, afterKeywords=__NO_VALUE__):
502 :param beforeOp, afterOp: number of spaces before and after operators
503 :param inOperator: True to suppress spaces in operators
504 :param beforeComma, afterComma: number of spaces before and after commas
505 :param beforeParenthesis, afterParenthesis: number of spaces before and after parenthesis
506 :param beforeAffectation, afterAffectation: number of spaces before and after
507 affectations or associations
508 :param inAffectation: True to suppress spaces in affectations and in association ('= >')
509 :param beforeRangeDelim, afterRangeDelim: number of spaces before and after range delimiters
510 :param beforeUseDelim, afterUseDelim: number of spaces before and after use delimiters (':')
511 :param beforeDeclDelim, afterDeclDelim: number of spaces before and after declaration and
512 enumerator delimiter ('::')
513 :param inDeclDelim: True to suppress spaces in declaration and enumerator delimiter (': :')
514 :param afterTypeDecl: number of spaces after the type in a declaration w/o '::'
515 (e.g. 'INTEGER I'); also for enumerators (minimum 1)
516 :param beforeEqDo, afterEqDo: number of spaces before and after '=' sign in DO and
518 :param beforeEqCall, afterEqCall: number of spaces before and after '=' sign
520 :param beforeEqInit, afterEqInit: number of spaces before and after '=' sign for init values
521 :param beforeEndcnt, afterBegincnt: number of spaces before a continuation chararcter at the
522 end of the line and after a continuation character
523 at the begining of a line
524 :param afterIfwherecase: number of spaces after the IF, ELSEIF, WHERE, ELSEWHERE,
525 SELECTCASE, CASE and FORALL keywords
526 :param beforeThen: number of spaces before the THEN keyword
527 :param beforeIfaction: number of spaces
528 between IF condition and action in one-line IF statement and
529 between FORALL specification and affectation in one-line FORALL
531 between WHERE mask and action in one-line WHERE statement
532 :param afterProgunit: between the program unit type (e.g. SUBROUTINE) and its name
533 :param endOfLine: True to suppress spaces at the end of the line
534 :param afterName: number of spaces after an indentifier, type or attribute name
535 :param inName: True to suppress spaces in identifier names
536 :param beforeCmdsep, afterCmdsep: number of spaces before and after command separator (';')
537 :param adjacentKeywords: describes the number of spaces to introduce between adjancent
538 keywords when this is legal (the list comes from the table
539 "6.2 Adjacent keywords where separating blanks are optional" of the
540 F2008 norm and has been complemented by "end select",
541 "implicit none" and "module procedure"; for the last two,
542 a minimum of 1 is required).
543 The allowed dictionnary keys are:
575 For example, use {'end_do':1} to write 'END DO' or
576 {'end_do':0} to write 'ENDDO' or
577 {'end_do':None} to not update the writting
578 or use adjacentKeywords=None to disable everything
579 :param afterKeywords: describes the number of spaces to introduce after keywords.
580 Some keywords need a more sophisticated treatment and are controled
581 by specific keys (e.g. CASE).
582 The keys are the keyword in lowercase, some names can be tricky
583 to guess (e.g. the key for ENDFILE is 'end-file'). By default
584 only a few are defined.
585 Use afterKeywords=None to disable everything.
587 To not update spaces, put None instead of an integer and False in booleans.
588 For example, to not change number of spaces after a comma, use afterComma=None
590 Updates are done in the following order:
594 'block_data': (1,
'.//{*}block-data-stmt'),
595 'double_precision': (1,
'.//{*}intrinsic-T-spec/{*}T-N'),
596 'else_if': (1,
'.//{*}else-if-stmt'),
597 'else_where': (0,
'.//{*}else-where-stmt'),
598 'end_associate': (1,
'.//{*}end-associate-stmt'),
599 'end_block': (1,
'.//{*}end-block-stmt'),
600 'end_block_data': (1,
'.//{*}end-block-data-stmt'),
601 'end_critical': (1,
'.//{*}end-critical-stmt'),
602 'end_do': (1,
'.//{*}end-do-stmt'),
603 'end_enum': (1,
'.//{*}end-enum-stmt'),
604 'end_file': (1,
'.//{*}end-file-stmt'),
605 'end_forall': (1,
'.//{*}end-forall-stmt'),
606 'end_function': (1,
'.//{*}end-function-stmt'),
607 'end_if': (1,
'.//{*}end-if-stmt'),
608 'end_interface': (1,
'.//{*}end-interface-stmt'),
609 'end_module': (1,
'.//{*}end-module-stmt'),
610 'end_procedure': (1,
'.//{*}end-procedure-stmt'),
611 'end_program': (1,
'.//{*}end-program-stmt'),
612 'end_selec': (1,
'.//{*}end-select-case-stmt'),
613 'end_select': (1,
'.//{*}end-select-T-stmt'),
614 'end_submodule': (1,
'.//{*}end-submodule-stmt'),
615 'end_subroutine': (1,
'.//{*}end-subroutine-stmt'),
616 'end_team': (1,
'.//{*}end-change-team-stmt'),
617 'end_type': (1,
'.//{*}end-T-stmt'),
618 'end_where': (1,
'.//{*}end-where-stmt'),
619 'go_to': (0,
'.//{*}goto-stmt'),
620 'in_out': (0,
'.//{*}intent-spec'),
621 'select_case': (1,
'.//{*}select-case-stmt'),
622 'select_type': (1,
'.//{*}select-T-stmt'),
623 'implicit_none': (1,
'.//{*}implicit-none-stmt'),
624 'module_procedure': (1,
'.//{*}procedure-stmt'),
636 assert adjacentKeywords
is None or adjacentKeywords == self.
__NO_VALUE__ or \
638 for k
in adjacentKeywords),
"Unknown key in **adjacentKeywords"
641 if adjacentKeywords
is None:
644 return adjaKeyDesc[key][0]
645 return adjacentKeywords.get(key, adjaKeyDesc[key][0])
647 def getvalAfter(key):
650 num = afterKeywords.get(key, afterKey.get(key,
None))
652 num = afterKey.get(key,
None)
655 assert afterProgunit
is None or afterProgunit >= 1
656 assert afterTypeDecl
is None or afterTypeDecl >= 1
657 for k
in (
'implicit_none',
'module_procedure'):
659 assert num
is None or num >= 1, \
660 "adjacentKeywords['" + k +
"'] must be at least 1 (is " + str(num) +
")"
661 for k
in (
'use',
'call',
'end-file',
'do'):
662 num = getvalAfter(k +
'-stmt')
663 assert num
is None or num >= 1, \
664 "afterKeywords['" + k +
"'] must be at least 1 (is " + str(num) +
")"
666 for elem
in self.iter():
667 isNotC = tag(elem) !=
'C'
669 if elem.tail
is None:
671 elem.tail = elem.tail.replace(
'\t',
' ')
674 if beforeParenthesis
is not None:
675 elem.tail = re.sub(
r"[ ]*\(",
" " * beforeParenthesis +
r"(", elem.tail)
676 elem.tail = re.sub(
r"[ ]*\)",
" " * beforeParenthesis +
r")", elem.tail)
677 if elem.text
is not None and isNotC:
678 elem.text = re.sub(
r"[ ]*\(",
" " * beforeParenthesis +
r"(", elem.text)
679 elem.text = re.sub(
r"[ ]*\)",
" " * beforeParenthesis +
r")", elem.text)
680 if afterParenthesis
is not None:
681 elem.tail = re.sub(
r"\([ ]*",
"(" +
" " * afterParenthesis, elem.tail)
682 elem.tail = re.sub(
r"\)[ ]*",
")" +
" " * afterParenthesis, elem.tail)
683 if elem.text
is not None and isNotC:
684 elem.text = re.sub(
r"\([ ]*",
"(" +
" " * afterParenthesis, elem.text)
685 elem.text = re.sub(
r"\)[ ]*",
")" +
" " * afterParenthesis, elem.text)
688 if beforeComma
is not None:
689 elem.tail = re.sub(
r"[ ]*,",
" " * beforeComma +
r",", elem.tail)
690 if elem.text
is not None and isNotC:
691 elem.text = re.sub(
r"[ ]*,",
" " * beforeComma +
r",", elem.text)
692 if afterComma
is not None:
693 elem.tail = re.sub(
r",[ ]*",
"," +
" " * afterComma, elem.tail)
694 if elem.text
is not None and isNotC:
695 elem.text = re.sub(
r",[ ]*",
"," +
" " * afterComma, elem.text)
699 elem.tail = re.sub(
r"[ ]*\n",
r"\n", elem.tail)
702 if tag(elem)
in (
'N',
'T-N',
'attribute-N'):
704 for nnn
in elem.findall(
'{*}n'):
705 if nnn.tail
is not None:
706 nnn.tail = nnn.tail.strip(
' ')
707 if elem.tail
is not None and afterName
is not None:
708 elem.tail =
' ' * afterName + elem.tail.lstrip(
' ')
711 elif tag(elem) ==
'lower-bound' and elem.tail
is not None and ':' in elem.tail:
712 if beforeRangeDelim
is not None:
713 elem.tail =
' ' * beforeRangeDelim + elem.tail.lstrip(
' ')
714 if afterRangeDelim
is not None:
715 elem.tail = elem.tail.rstrip(
' ') +
' ' * beforeRangeDelim
718 elif tag(elem) ==
'module-N' and elem.tail
is not None and ':' in elem.tail:
719 if beforeUseDelim
is not None:
720 elem.tail = re.sub(
r"[ ]*:",
" " * beforeUseDelim +
r":", elem.tail)
721 if afterUseDelim
is not None:
722 elem.tail = re.sub(
r":[ ]*",
":" +
" " * afterUseDelim, elem.tail)
726 elif tag(elem)
in (
'attribute',
'_T-spec_')
and elem.tail
is not None:
728 elem.tail = re.sub(
r":[ ]*:",
r"::", elem.tail)
729 if beforeDeclDelim
is not None:
730 elem.tail = re.sub(
r"[ ]*(:[ ]*:)",
' ' * beforeDeclDelim +
r"\1", elem.tail)
731 if afterDeclDelim
is not None:
732 elem.tail = re.sub(
r"(:[ ]*:)[ ]*",
r"\1" +
' ' * afterDeclDelim, elem.tail)
733 if tag(elem) ==
'_T-spec_' and afterTypeDecl
is not None:
734 elem.tail = elem.tail.rstrip(
' ') +
' ' * afterTypeDecl
738 elif tag(elem) ==
'enumerator-stmt' and elem.text
is not None:
741 elem.text = re.sub(
r":[ ]*:",
r"::", elem.text)
742 if beforeDeclDelim
is not None:
743 elem.text = re.sub(
r"[ ]*(:[ ]*:)",
' ' * beforeDeclDelim +
r"\1",
745 if afterDeclDelim
is not None:
746 elem.text = re.sub(
r"(:[ ]*:)[ ]*",
r"\1" +
' ' * afterDeclDelim,
748 elif afterTypeDecl
is not None:
749 elem.text = elem.text.rstrip(
' ') +
' ' * afterTypeDecl
752 elif (tag(elem)
in (
'subroutine-stmt',
'program-stmt',
'module-stmt',
'function-stmt',
753 'submodule-stmt',
'procedure-stmt',
'interface-stmt',
754 'end-subroutine-stmt',
'end-program-stmt',
755 'end-module-stmt',
'end-function-stmt',
756 'end-submodule-stmt',
'end-procedure-stmt',
'end-interface-stmt')
757 and afterProgunit
is not None):
758 if elem.text
is not None:
759 elem.text = elem.text.rstrip(
' ') +
' ' * afterProgunit
762 elif tag(elem)
in (
'do-V',
'V')
and elem.tail
is not None and '=' in elem.tail:
763 if beforeEqDo
is not None:
764 elem.tail = re.sub(
'[ ]*=',
' ' * beforeEqDo +
'=', elem.tail)
765 if afterEqDo
is not None:
766 elem.tail = re.sub(
'=[ ]*',
'=' +
' ' * beforeEqDo, elem.tail)
769 elif tag(elem) ==
'arg-N' and elem.tail
is not None and '=' in elem.tail:
770 if beforeEqCall
is not None:
771 elem.tail = re.sub(
'[ ]*=',
' ' * beforeEqCall +
'=', elem.tail)
772 if afterEqCall
is not None:
773 elem.tail = re.sub(
'=[ ]*',
'=' +
' ' * beforeEqCall, elem.tail)
776 elif (tag(elem)
in (
'EN-N',
'named-constant')
and
777 elem.tail
is not None and '=' in elem.tail):
778 if beforeEqInit
is not None:
779 elem.tail = re.sub(
'[ ]*=',
' ' * beforeEqInit +
'=', elem.tail)
780 if afterEqInit
is not None:
781 elem.tail = re.sub(
'=[ ]*',
'=' +
' ' * beforeEqInit, elem.tail)
783 elif tag(elem) ==
'smc':
784 if beforeCmdsep
is not None:
785 prev = self.getSiblings(elem, after=
False)
786 if len(prev) != 0
and prev[-1].tail
is not None:
787 prev[-1].tail =
' ' * beforeCmdsep + prev[-1].tail.lstrip(
' ')
788 if afterCmdsep
is not None and elem.tail
is not None:
789 elem.tail = elem.tail.rstrip(
' ') +
' ' * afterCmdsep
792 elif tag(elem) ==
'associate-N' and elem.tail
is not None and '=' in elem.tail:
793 if beforeAffectation
is not None:
794 elem.tail = re.sub(
'[ ]*=',
' ' * beforeAffectation +
'=', elem.tail)
795 if afterAffectation
is not None:
796 elem.tail = re.sub(
'>[ ]*',
'>' +
' ' * beforeAffectation, elem.tail)
798 elem.tail = re.sub(
r'=[ ]*>',
'=>', elem.tail)
812 for elem
in self.iter():
814 if tag(elem) ==
'op-E':
815 for op
in elem.findall(
'{*}op'):
816 if beforeOp
is not None:
817 io = list(elem).index(op)
820 if prev.tail
is None:
821 prev.tail =
' ' * beforeOp
823 prev.tail = prev.tail.rstrip(
' ') +
' ' * beforeOp
824 if afterOp
is not None:
826 op.tail =
' ' * afterOp
828 op.tail = op.tail.lstrip(
' ') +
' ' * afterOp
830 for oo
in op.findall(
'{*}o'):
831 if oo.tail
is not None:
832 oo.tail = oo.tail.strip(
' ')
835 elif tag(elem)
in (
'a-stmt',
'pointer-a-stmt'):
837 for aff
in elem.findall(
'{*}a'):
838 if beforeAffectation
is not None:
839 prev = elem[list(elem).index(aff) - 1]
840 if prev.tail
is None:
841 prev.tail =
' ' * beforeAffectation
843 prev.tail = prev.tail.rstrip(
' ') +
' ' * beforeAffectation
844 if afterAffectation
is not None:
846 aff.tail =
' ' * afterAffectation
848 aff.tail = aff.tail.lstrip(
' ') +
' ' * afterAffectation
850 aff.text = aff.text.replace(
' ',
'')
854 elif tag(elem)
in (
'if-stmt',
'if-then-stmt',
'else-if-stmt',
855 'where-stmt',
'where-construct-stmt',
'else-where-stmt',
856 'select-case-stmt',
'case-stmt',
857 'forall-stmt',
'forall-construct-stmt'):
858 if afterIfwherecase
is not None and elem.text
is not None:
859 if tag(elem) ==
'case-stmt':
861 elem.text = elem.text.rstrip(
' ') +
' ' * afterIfwherecase
863 elem.text = re.sub(
r'[ ]*\(',
' ' * afterIfwherecase +
'(',
865 if tag(elem)
in (
'if-then-stmt',
'else-if-stmt')
and beforeThen
is not None:
866 cond = elem.find(
'{*}condition-E')
867 cond.tail = re.sub(
r'\)[ ]*([a-zA-Z]*$)',
')' +
' ' * beforeThen +
r'\1',
869 elif tag(elem) ==
'if-stmt' and beforeIfaction
is not None:
870 cond = elem.find(
'{*}condition-E')
871 cond.tail = re.sub(
r'\)[ ]*$',
')' +
' ' * beforeIfaction, cond.tail)
872 elif tag(elem) ==
'where-stmt' and beforeIfaction
is not None:
873 cond = elem.find(
'{*}mask-E')
874 cond.tail = re.sub(
r'\)[ ]*$',
')' +
' ' * beforeIfaction, cond.tail)
875 elif tag(elem) ==
'forall-stmt' and beforeIfaction
is not None:
876 sub = elem.find(
'{*}forall-triplet-spec-LT')
877 sub.tail = re.sub(
r'\)[ ]*$',
')' +
' ' * beforeIfaction, sub.tail)
880 if beforeEndcnt
is not None or afterBegincnt
is not None:
881 for elem
in self.findall(
'.//{*}cnt/..'):
882 for cnt
in elem.findall(
'{*}cnt'):
883 ic = list(elem).index(cnt)
892 if '\n' in pstring
and '\n' in cnt.tail:
895 elif '\n' in pstring
and afterBegincnt
is not None:
897 cnt.tail =
' ' * afterBegincnt + cnt.tail.lstrip(
' ')
898 elif beforeEndcnt
is not None:
902 prev.text = prev.text.rstrip(
' ') +
' ' * beforeEndcnt
904 prev.tail = prev.tail.rstrip(
' ') +
' ' * beforeEndcnt
907 for key, val
in adjaKeyDesc.items():
908 num = getvalAdja(key)
910 for node
in self.findall(val[1]):
911 lf =
"[ ]*".join([
"(" + p +
")" for p
in key.split(
'_')])
912 repl = (
" " * num).join([
r"\{i}".format(i=i + 1)
913 for i, _
in enumerate(key.split(
'_'))])
914 node.text = re.sub(lf, repl, node.text, flags=re.IGNORECASE)
1015 Order USE declarations by module type and alphabetically.
1017 Groups USE statements by module type prefix and sorts alphabetically
1018 within each group. The group order is: MODD_, MODE_, MODI_, MODN_,
1019 followed by any other modules sorted alphabetically.
1020 Module names are optionally converted to uppercase.
1021 Trailing comments after USE statements are preserved.
1025 upper : bool, optional
1026 If True, convert module names to uppercase. Default is True.
1031 Returns self for method chaining.
1035 >>> pft = PYFT('input.F90')
1036 >>> pft.formatModuleUse()
1037 >>> pft.formatModuleUse(upper=False)
1051 SORTED_GROUPS = (
'MODD',
'MODE',
'MODI',
'MODN')
1053 for progUnit
in self.findall(
'.//{*}program-unit'):
1054 children = list(progUnit)
1057 while i < len(children):
1059 if tag(child) ==
'use-stmt':
1062 while i < len(children)
and tag(children[i]) ==
'C':
1063 comments.append(children[i])
1065 useGroups.append([child, comments])
1072 def _getModuleName(stmt):
1073 n = stmt.find(
'.//{*}module-N/{*}N/{*}n')
1074 return n.text
if n
is not None and n.text
is not None else ''
1076 groups = {prefix: []
for prefix
in SORTED_GROUPS}
1079 for stmt, comments
in useGroups:
1080 name = _getModuleName(stmt).upper()
1082 for prefix
in SORTED_GROUPS:
1083 if name.startswith(prefix +
'_'):
1084 groups[prefix].append([stmt, comments])
1088 other.append([stmt, comments])
1090 for prefix
in SORTED_GROUPS:
1091 groups[prefix].sort(key=
lambda g: _getModuleName(g[0]).upper())
1092 other.sort(key=
lambda g: _getModuleName(g[0]).upper())
1094 def _addSep(ordered):
1095 prev_stmt, prev_comments = ordered[-1]
1097 prev_comments[-1].tail = (prev_comments[-1].tail
or '').rstrip(
'\n') +
'\n!\n'
1099 prev_stmt.tail = (prev_stmt.tail
or '').rstrip(
'\n') +
'\n!\n'
1102 for prefix
in SORTED_GROUPS:
1106 ordered.extend(groups[prefix])
1110 ordered.extend(other)
1113 for i, child
in enumerate(progUnit):
1114 if tag(child) ==
'use-stmt':
1118 if firstIdx
is not None:
1119 for stmt, comments
in useGroups:
1120 progUnit.remove(stmt)
1124 for stmt, comments
in ordered:
1125 progUnit.insert(idx, stmt)
1128 progUnit.insert(idx, c)
1132 for stmt, _
in ordered:
1133 modN = stmt.find(
'.//{*}module-N')
1134 modNNs = set(modN.findall(
'.//{*}n'))
if modN
is not None else set()
1135 for n
in stmt.findall(
'.//{*}n'):
1136 if n
not in modNNs
and n.text:
1137 n.text = n.text.upper()