274 loopVar=None, reuseLoop=True, funcList=None,
275 updateMemSet=False, updateCopy=False, addAccIndependentCollapse=True):
277 Transform array syntax assignments into explicit DO loops.
279 Converts Fortran array syntax (e.g., A(:) = B(:)) into equivalent DO loop form.
283 concurrent : bool, optional
284 If True, use 'DO CONCURRENT' loops instead of simple 'DO' loops.
286 useMnhExpand : bool, optional
287 If True, respect mnh_expand directives to transform entire blocks
288 into a single loop. Default is True.
289 everywhere : bool, optional
290 If True, transform all array syntax in the code.
291 If False, only transform sections marked with !$mnh_expand directives.
293 loopVar : callable or None, optional
294 Function to determine loop index variable name.
295 Takes arguments: (lowerDecl, upperDecl, lowerUsed, upperUsed, name, index)
296 Returns: str (variable name), True (auto-generate name), or False (skip).
297 None (default) auto-generates variable names (J1, J2, etc.).
298 reuseLoop : bool, optional
299 If True, attempt to reuse loops when consecutive statements
300 have identical bounds. Default is True.
301 funcList : list of str, optional
302 Additional function names to recognize as array functions.
303 These functions will not be expanded. Default is None (empty list).
304 updateMemSet : bool, optional
305 If True, transform constant array initializations (e.g., A(:) = 0)
306 into DO loops. Default is False.
307 updateCopy : bool, optional
308 If True, transform array copy operations (e.g., A(:) = B(:))
309 into DO loops. Default is False.
310 addAccIndependentCollapse : bool, optional
311 If True, add !$acc loop independent collapse(N) directive
312 before DO constructs. Default is True.
318 Transformation Examples
319 ----------------------
326 DO J1 = LBOUND(A, 1), UBOUND(A, 1)
327 A(J1) = B(J1) + C(J1)
331 DO CONCURRENT (J1=LBOUND(A, 1):UBOUND(A, 1))
332 A(J1) = B(J1) + C(J1)
338 WHERE (MASK(:)) X(:) = Y(:)
341 DO J1 = 1, SIZE(X, 1)
342 IF (MASK(J1)) X(J1) = Y(J1)
347 - Only transforms array syntax using explicit ':' notation.
348 - Intrinsic array functions (COUNT, ANY, SUM, etc.) are preserved.
349 - Does not transform:
350 - A=A(:) (no-op on left side)
351 - A(:)=A (single array without slice on right side)
352 - When useMnhExpand=True, requires specific directive format:
353 !$mnh_expand_array(INDEX=bounds)
354 ... code to transform ...
355 !$mnh_end_expand_array(INDEX=bounds)
391 def decode(directive):
393 Decode mnh_expand directive
394 :param directive: mnh directive text
395 :return: (table, kind) where
396 table is a dictionnary: keys are variable names, values are tuples with first
398 kind is 'array' or 'where'
406 table = directive.split(
'(')[1].split(
')')[0].split(
',')
407 table = {c.split(
'=')[0]: c.split(
'=')[1].split(
':')
409 table.pop(
'OPENACC',
None)
410 if directive.lstrip(
' ').startswith(
'!$mnh_expand'):
411 kind = directive[13:].lstrip(
' ').split(
'(')[0].strip()
413 kind = directive[17:].lstrip(
' ').split(
'(')[0].strip()
416 def updateStmt(stmt, table, kind, extraindent, parent, scope):
418 Updates the statement given the table dictionnary '(:, :)' is replaced by '(JI, JK)' if
419 table.keys() is ['JI', 'JK']
420 :param stmt: statement to update
421 :param table: dictionnary retruned by the decode function
422 :param kind: kind of mnh directives: 'array' or 'where'
423 or None if transformation is not governed by
425 :param scope: current scope
428 def addExtra(node, extra):
429 """Helper function to add indentation spaces"""
430 if extra != 0
and (node.tail
is not None)
and '\n' in node.tail:
436 node.tail = re.sub(
r"(\n[ ]*)(\Z|[^\n ]+)",
437 r"\1" + extra *
' ' +
r"\2", node.tail)
439 addExtra(stmt, extraindent)
442 elif tag(stmt) ==
'cpp':
443 i = list(parent).index(stmt)
448 parent[i - 1].tail = parent[i - 1].tail.rstrip(
' ')
449 elif tag(stmt) ==
'a-stmt':
450 sss = stmt.findall(
'./{*}E-1/{*}named-E/{*}R-LT/{*}array-R/' +
451 '{*}section-subscript-LT/{*}section-subscript')
452 if len([ss
for ss
in sss
if ':' in alltext(ss)]) != len(table):
453 raise PYFTError(
"Inside code sections to transform in DO loops, " +
454 "all affectations must use ':'.\n" +
455 "This is not the case in:\n{stmt}".format(stmt=alltext(stmt)))
456 if stmt.find(
'./{*}E-1/{*}named-E/{*}N').tail
is not None and kind
is not None:
457 raise PYFTError(
"To keep the compatibility with the filepp version of loop " +
458 "expansion, nothing must appear between array names and " +
459 "opening parethesis inside mnh directive sections.")
462 for namedE
in stmt.findall(
'.//{*}R-LT/..'):
463 scope.arrayR2parensR(namedE, table)
464 for cnt
in stmt.findall(
'.//{*}cnt'):
465 addExtra(cnt, extraindent)
466 elif tag(stmt) ==
'if-stmt':
468 "An if statement is inside a code section transformed in DO loop in %s",
471 updateStmt(stmt.find(
'./{*}action-stmt')[0], table, kind, 0, stmt, scope)
472 elif tag(stmt) ==
'if-construct':
474 "An if construct is inside a code section transformed in DO loop in %s",
477 for ifBlock
in stmt.findall(
'./{*}if-block'):
478 for child
in ifBlock:
479 if tag(child)
not in (
'if-then-stmt',
'else-if-stmt',
480 'else-stmt',
'end-if-stmt'):
481 updateStmt(child, table, kind, extraindent, ifBlock, scope)
484 addExtra(child, extraindent)
485 for cnt
in child.findall(
'.//{*}cnt'):
487 addExtra(cnt, extraindent)
488 elif tag(stmt) ==
'where-stmt':
490 stmt.tag = f
'{{{NAMESPACE}}}if-stmt'
491 stmt.text =
'IF (' + stmt.text.split(
'(', 1)[1]
493 updateStmt(stmt.find(
'./{*}action-stmt')[0], table, kind,
494 extraindent, stmt, scope)
495 mask = stmt.find(
'./{*}mask-E')
496 mask.tag = f
'{{{NAMESPACE}}}condition-E'
497 for namedE
in mask.findall(
'.//{*}R-LT/..'):
498 scope.arrayR2parensR(namedE, table)
499 for cnt
in stmt.findall(
'.//{*}cnt'):
500 addExtra(cnt, extraindent)
501 elif tag(stmt) ==
'where-construct':
502 if kind !=
'where' and kind
is not None:
503 raise PYFTError(
'To keep the compatibility with the filepp version of loop " + \
504 "expansion, no where construct must appear " + \
505 "in mnh_expand_array blocks.')
507 stmt.tag = f
'{{{NAMESPACE}}}if-construct'
509 for whereBlock
in stmt.findall(
'./{*}where-block'):
510 whereBlock.tag = f
'{{{NAMESPACE}}}if-block'
511 for child
in whereBlock:
512 if tag(child) ==
'end-where-stmt':
514 child.tag = f
'{{{NAMESPACE}}}end-if-stmt'
515 child.text =
'END IF'
517 addExtra(child, extraindent)
518 elif tag(child)
in (
'where-construct-stmt',
'else-where-stmt'):
520 addExtra(child, extraindent)
521 if tag(child) ==
'where-construct-stmt':
523 child.tag = f
'{{{NAMESPACE}}}if-then-stmt'
524 child.text =
'IF (' + child.text.split(
'(', 1)[1]
529 if '(' in child.text:
531 child.tag = f
'{{{NAMESPACE}}}else-if-stmt'
532 child.text =
'ELSE IF (' + child.text.split(
'(', 1)[1]
535 child.tag = f
'{{{NAMESPACE}}}else-stmt'
537 for mask
in child.findall(
'./{*}mask-E'):
539 mask.tag = f
'{{{NAMESPACE}}}condition-E'
541 for namedE
in mask.findall(
'.//{*}R-LT/..'):
543 scope.arrayR2parensR(namedE, table)
544 for cnt
in child.findall(
'.//{*}cnt'):
546 addExtra(cnt, extraindent)
548 updateStmt(child, table, kind, extraindent, whereBlock, scope)
550 raise PYFTError(
'Unexpected tag found in mnh_expand ' +
551 'directives: {t}'.format(t=tag(stmt)))
554 def closeLoop(loopdesc):
555 """Helper function to deal with indentation"""
557 inner, outer, indent, extraindent = loopdesc
558 if inner[-2].tail
is not None:
560 outer.tail = inner[-2].tail[:-extraindent]
561 inner[-2].tail =
'\n' + (indent + extraindent - 2) *
' '
568 def recur(elem, scope):
572 for ie, sElem
in enumerate(list(elem)):
573 if tag(sElem) ==
'C' and sElem.text.lstrip(
' ').startswith(
'!$mnh_expand')
and \
577 raise PYFTError(
'Nested mnh_directives are not allowed')
579 inEverywhere = closeLoop(inEverywhere)
582 table, kind = decode(sElem.text)
584 indent = len(sElem.tail) - len(sElem.tail.rstrip(
' '))
585 toremove.append((elem, sElem))
589 if elem[ie - 1].tail
is None:
590 elem[ie - 1].tail =
''
591 elem[ie - 1].tail += sElem.tail.replace(
'\n',
'', 1).rstrip(
' ')
594 if addAccIndependentCollapse:
595 accCollapse = createElem(
'C', text=
'!$acc loop independent collapse(' +
596 str(len(table.keys())) +
')',
597 tail=
'\n' + indent *
' ')
598 toinsert.append((elem, accCollapse, ie))
601 inner, outer, extraindent = scope.createDoConstruct(table, indent=indent,
602 concurrent=concurrent)
603 toinsert.append((elem, outer, ie))
605 elif (tag(sElem) ==
'C' and
606 sElem.text.lstrip(
' ').startswith(
'!$mnh_end_expand')
and useMnhExpand):
609 raise PYFTError(
'End mnh_directive found before begin directive ' +
610 'in {f}'.format(f=scope.getFileName()))
611 if (table, kind) != decode(sElem.text):
612 raise PYFTError(
"Opening and closing mnh directives must be conform " +
613 "in {f}".format(f=scope.getFileName()))
615 toremove.append((elem, sElem))
619 outer.tail += sElem.tail.replace(
'\n',
'', 1)
621 elem[ie - 1].tail = elem[ie - 1].tail[:-2]
625 toremove.append((elem, sElem))
626 inner.insert(-1, sElem)
628 updateStmt(sElem, table, kind, extraindent, inner, scope)
630 elif everywhere
and tag(sElem)
in (
'a-stmt',
'if-stmt',
'where-stmt',
635 if tag(sElem) ==
'a-stmt':
637 arr = sElem.find(
'./{*}E-1/{*}named-E/{*}R-LT/{*}array-R/../..')
641 nodeE2 = sElem.find(
'./{*}E-2')
643 num = len(nodeE2.findall(
'.//{*}array-R'))
650 elif (len(nodeE2) == 1
and tag(nodeE2[0]) ==
'named-E' and num == 1
and
651 nodeE2[0].find(
'.//{*}parens-R')
is None):
657 if (isMemSet
and not updateMemSet)
or (isCopy
and not updateCopy):
659 elif tag(sElem) ==
'if-stmt':
661 arr = sElem.find(
'./{*}action-stmt/{*}a-stmt/{*}E-1/' +
662 '{*}named-E/{*}R-LT/{*}array-R/../..')
665 scope.changeIfStatementsInIfConstructs(singleItem=sElem)
668 elif tag(sElem) ==
'where-stmt':
669 arr = sElem.find(
'./{*}mask-E//{*}named-E/{*}R-LT/{*}array-R/../..')
670 elif tag(sElem) ==
'where-construct':
671 arr = sElem.find(
'./{*}where-block/{*}where-construct-stmt/' +
672 '{*}mask-E//{*}named-E/{*}R-LT/{*}array-R/../..')
679 elif len(set(alltext(a).count(
':')
680 for a
in sElem.findall(
'.//{*}R-LT/{*}array-R'))) > 1:
684 elif len(set([
'ALL',
'ANY',
'COSHAPE',
'COUNT',
'CSHIFT',
'DIMENSION',
685 'DOT_PRODUCT',
'EOSHIFT',
'LBOUND',
'LCOBOUND',
'MATMUL',
686 'MAXLOC',
'MAXVAL',
'MERGE',
'MINLOC',
'MINVAL',
'PACK',
687 'PRODUCT',
'REDUCE',
'RESHAPE',
'SHAPE',
'SIZE',
'SPREAD',
688 'SUM',
'TRANSPOSE',
'UBOUND',
'UCOBOUND',
'UNPACK'] +
689 (funcList
if funcList
is not None else [])
690 ).intersection(set(n2name(nodeN)
for nodeN
691 in sElem.findall(
'.//{*}named-E/{*}N')))) > 0:
697 newtable, varNew = scope.findArrayBounds(arr, loopVar, newVarList)
700 if var
not in newVarList:
701 newVarList.append(var)
708 inEverywhere = closeLoop(inEverywhere)
711 if not (inEverywhere
and table == newtable):
713 inEverywhere = closeLoop(inEverywhere)
715 if ie != 0
and elem[ie - 1].tail
is not None:
718 tail = tailSave.get(elem[ie - 1], elem[ie - 1].tail)
719 indent = len(tail) - len(tail.rstrip(
' '))
725 inner, outer, extraindent = scope.createDoConstruct(
726 table, indent=indent, concurrent=concurrent)
727 toinsert.append((elem, outer, ie))
728 inEverywhere = (inner, outer, indent, extraindent)
729 tailSave[sElem] = sElem.tail
730 toremove.append((elem, sElem))
731 inner.insert(-1, sElem)
733 updateStmt(sElem, table, kind, extraindent, inner, scope)
736 inEverywhere = closeLoop(inEverywhere)
739 inEverywhere = closeLoop(inEverywhere)
743 inEverywhere = closeLoop(inEverywhere)
745 for scope
in self.getScopes():
748 for elem, outer, ie
in toinsert[::-1]:
749 elem.insert(ie, outer)
751 for parent, elem
in toremove:
754 self.addVar([(v[
'scopePath'], v[
'n'], f
"INTEGER :: {v['n']}",
None)
755 for v
in newVarList])
839 def inline(self, subContained, callStmt, mainScope,
840 simplify=False, loopVar=None):
842 Inline a single contained subroutine at its call site.
844 This method performs the actual inlining of a contained subroutine
845 into the calling scope. It handles:
846 - ELEMENTAL subroutines with array arguments
847 - Optional arguments (PRESENT intrinsic)
848 - Variable name conflicts
849 - USE statement merging
853 subContained : xml element
854 XML fragment corresponding to the contained subroutine scope.
855 callStmt : xml element
856 The call-stmt node to replace with inlined code.
857 mainScope : PYFTscope
858 Scope of the main (calling) subroutine.
859 simplify : bool, optional
860 If True, remove empty constructs and unused variables
861 after inlining. Default is False.
862 loopVar : callable or None, optional
863 Function to determine loop index variable name.
864 Used when inlining ELEMENTAL subroutines called on arrays.
865 Takes: (lowerDecl, upperDecl, lowerUsed, upperUsed, name, index)
866 Returns: str, True (auto-generate), or False (skip).
870 - For ELEMENTAL subroutines on arrays: DO loops are introduced.
871 - Optional arguments: PRESENT(var) is replaced with .TRUE. or .FALSE.
872 - Missing optional arguments: code paths using them are removed.
873 - Name conflicts: local variables are renamed with _N suffixes.
875 def setPRESENTby(node, var, val):
877 Replace PRESENT(var) by .TRUE. if val is True, by .FALSE. otherwise on node
879 :param node: xml node to work on (a contained subroutine)
880 :param var: string of the name of the optional variable to check
882 for namedE
in node.findall(
'.//{*}named-E/{*}N/..'):
883 if n2name(namedE.find(
'./{*}N')).upper() ==
'PRESENT':
884 presentarg = n2name(namedE.find(
'./{*}R-LT/{*}parens-R/{*}element-LT/'
885 '{*}element/{*}named-E/{*}N'))
886 if presentarg.upper() == var.upper():
887 for nnn
in namedE[:]:
889 namedE.tag = f
'{{{NAMESPACE}}}literal-E'
890 namedE.text =
'.TRUE.' if val
else '.FALSE.'
893 parent = mainScope.getParent(callStmt)
896 if tag(parent) ==
'action-stmt':
897 mainScope.changeIfStatementsInIfConstructs(mainScope.getParent(parent))
898 parent = mainScope.getParent(callStmt)
902 prefix = subContained.findall(
'.//{*}prefix')
903 if len(prefix) > 0
and 'ELEMENTAL' in [p.text.upper()
for p
in prefix]:
905 mainScope.addArrayParenthesesInNode(callStmt)
908 mainScope.addExplicitArrayBounds(node=callStmt)
911 arrayRincallStmt = callStmt.findall(
'.//{*}array-R')
912 if len(arrayRincallStmt) > 0:
914 table, _ = mainScope.findArrayBounds(mainScope.getParent(arrayRincallStmt[0], 2),
918 for varName
in table.keys():
919 if not mainScope.varList.findVar(varName):
920 var = {
'as': [],
'asx': [],
921 'n': varName,
'i':
None,
't':
'INTEGER',
'arg':
False,
922 'use':
False,
'opt':
False,
'allocatable':
False,
923 'parameter':
False,
'init':
None,
'scopePath': mainScope.path}
924 mainScope.addVar([[mainScope.path, var[
'n'],
925 mainScope.varSpec2stmt(var),
None]])
928 inner, outer, _ = mainScope.createDoConstruct(table)
931 inner.insert(-1, callStmt)
933 parent.insert(list(parent).index(callStmt), outer)
934 parent.remove(callStmt)
936 for namedE
in callStmt.findall(
'./{*}arg-spec/{*}arg/{*}named-E'):
938 if namedE.find(
'./{*}R-LT'):
939 mainScope.arrayR2parensR(namedE, table)
942 node = copy.deepcopy(subContained)
947 varList = copy.deepcopy(self.varList)
948 for var
in [var
for var
in varList.restrict(subContained.path,
True)
949 if not var[
'arg']
and not var[
'use']]:
951 if varList.restrict(mainScope.path,
True).findVar(var[
'n']):
954 newName = re.sub(
r'_\d+$',
'', var[
'n'])
956 while (varList.restrict(subContained.path,
True).findVar(newName +
'_' + str(i))
or
957 varList.restrict(mainScope.path,
True).findVar(newName +
'_' + str(i))):
959 newName +=
'_' + str(i)
960 node.renameVar(var[
'n'], newName)
961 subst.append((var[
'n'], newName))
964 var[
'scopePath'] = mainScope.path
965 localVarToAdd.append(var)
968 for oldName, newName
in subst:
969 for var
in localVarToAdd + varList[:]:
970 if var[
'as']
is not None:
971 var[
'as'] = [[re.sub(
r'\b' + oldName +
r'\b', newName, dim[i])
972 if dim[i]
is not None else None
974 for dim
in var[
'as']]
981 localUseToAdd = node.findall(
'./{*}use-stmt')
982 for sNode
in node.findall(
'./{*}T-decl-stmt') + localUseToAdd + \
983 node.findall(
'./{*}implicit-none-stmt'):
986 while tag(node[icom]) ==
'C':
987 if node[icom].text.startswith(
'!$acc'):
988 if 'routine' in node[icom].text:
989 node.remove(node[icom])
993 node.remove(node[icom])
1001 for argN
in subContained.findall(
'.//{*}subroutine-stmt/{*}dummy-arg-LT/{*}arg-N'):
1002 vartable[alltext(argN).upper()] =
None
1003 for iarg, arg
in enumerate(callStmt.findall(
'.//{*}arg')):
1004 key = arg.find(
'.//{*}arg-N')
1007 dummyName = alltext(key).upper()
1010 dummyName = list(vartable.keys())[iarg]
1012 nodeRLTarray = argnode.findall(
'.//{*}R-LT/{*}array-R')
1013 if len(nodeRLTarray) > 0:
1015 if len(nodeRLTarray) > 1
or \
1016 argnode.find(
'./{*}R-LT/{*}array-R')
is None or \
1017 tag(argnode) !=
'named-E':
1019 raise PYFTError(
'Argument to complicated: ' + str(alltext(argnode)))
1020 dim = nodeRLTarray[0].find(
'./{*}section-subscript-LT')[:]
1026 argname =
"".join(argnode.itertext())
1029 tmp = copy.deepcopy(argnode)
1030 nodeRLT = tmp.find(
'./{*}R-LT')
1031 nodeRLT.remove(nodeRLT.find(
'./{*}array-R'))
1032 argname =
"".join(tmp.itertext())
1033 vartable[dummyName] = {
'node': argnode,
'name': argname,
'dim': dim}
1036 for dummyName
in [dummyName
for (dummyName, value)
in vartable.items()
1037 if value
is not None]:
1038 setPRESENTby(node, dummyName,
True)
1039 for dummyName
in [dummyName
for (dummyName, value)
in vartable.items()
1041 setPRESENTby(node, dummyName,
False)
1044 for dummyName
in [dummyName
for (dummyName, value)
in vartable.items()
if value
is None]:
1045 for nodeN
in [nodeN
for nodeN
in node.findall(
'.//{*}named-E/{*}N')
1046 if n2name(nodeN).upper() == dummyName]:
1049 par = node.getParent(nodeN, level=2)
1050 allreadySuppressed = []
1051 while par
and not removed
and par
not in allreadySuppressed:
1054 if tagName
in (
'a-stmt',
'print-stmt'):
1057 elif tagName ==
'call-stmt':
1062 raise NotImplementedError(
'call-stmt not (yet?) implemented')
1063 elif tagName
in (
'if-stmt',
'where-stmt'):
1066 elif tagName
in (
'if-then-stmt',
'else-if-stmt',
'where-construct-stmt',
1070 toSuppress = node.getParent(par, 2)
1071 elif tagName
in (
'select-case-stmt',
'case-stmt'):
1074 toSuppress = node.getParent(par, 2)
1075 elif tagName.endswith(
'-block')
or tagName.endswith(
'-stmt')
or \
1076 tagName.endswith(
'-construct'):
1083 raise PYFTError((
"We shouldn't be here. A case may have been " +
1084 "overlooked (tag={tag}).".format(tag=tagName)))
1085 if toSuppress
is not None:
1087 if toSuppress
not in allreadySuppressed:
1090 node.removeStmtNode(toSuppress,
False, simplify)
1091 allreadySuppressed.extend(list(toSuppress.iter()))
1093 par = node.getParent(par)
1096 for name, dummy
in vartable.items():
1100 for namedE
in [namedE
for namedE
in node.findall(
'.//{*}named-E/{*}N/{*}n/../..')
1101 if n2name(namedE.find(
'{*}N')).upper() == name]:
1103 nodeN = namedE.find(
'./{*}N')
1104 ns = nodeN.findall(
'./{*}n')
1105 ns[0].text = n2name(nodeN)
1111 descMain = varList.restrict(mainScope.path,
True).findVar(dummy[
'name'])
1112 descSub = varList.restrict(subContained.path,
True).findVar(name)
1116 if var[
'as']
is not None:
1117 var[
'as'] = [[re.sub(
r'\b' + name +
r'\b', dummy[
'name'], dim[i])
1118 if dim[i]
is not None else None
1120 for dim
in var[
'as']]
1125 nodeRLT = namedE.find(
'./{*}R-LT')
1126 if nodeRLT
is not None and tag(nodeRLT[0]) !=
'component-R':
1128 assert tag(nodeRLT[0])
in (
'array-R',
'parens-R'),
'Internal error'
1129 slices = nodeRLT[0].findall(
'./{*}section-subscript-LT/' +
1130 '{*}section-subscript')
1131 slices += nodeRLT[0].findall(
'./{*}element-LT/{*}element')
1134 if (descMain
is not None and descMain[
'as']
is not None and
1135 len(descMain[
'as']) > 0)
or \
1136 len(descSub[
'as']) > 0
or dummy[
'dim']
is not None:
1138 if len(descSub[
'as']) > 0:
1139 ndim = len(descSub[
'as'])
1142 if dummy[
'dim']
is not None:
1145 ndim = len([d
for d
in dummy[
'dim']
if ':' in alltext(d)])
1148 ndim = len(descMain[
'as'])
1149 ns[0].text +=
'(' + (
', '.join([
':'] * ndim)) +
')'
1150 updatedNamedE = createExprPart(alltext(namedE))
1151 namedE.tag = updatedNamedE.tag
1152 namedE.text = updatedNamedE.text
1153 for nnn
in namedE[:]:
1155 namedE.extend(updatedNamedE[:])
1156 slices = namedE.find(
'./{*}R-LT')[0].findall(
1157 './{*}section-subscript-LT/{*}section-subscript')
1163 namedE.find(
'./{*}N')[0].text = dummy[
'name']
1171 for isl, sl
in enumerate(slices):
1173 if len(descSub[
'as']) == 0
or descSub[
'as'][isl][1]
is None:
1176 if dummy[
'dim']
is not None:
1178 tagName =
'./{*}lower-bound' if i == 0
else './{*}upper-bound'
1179 descSub[i] = dummy[
'dim'][isl].find(tagName)
1182 if descSub[i]
is not None:
1184 descSub[i] = alltext(descSub[i])
1187 if descMain
is not None and descMain[
'as'][isl][1]
is not None:
1189 descSub[i] = descMain[
'as'][isl][i]
1190 if i == 0
and descSub[i]
is None:
1193 descSub[i] =
"L" if i == 0
else "U"
1194 descSub[i] +=
"BOUND({name}, {isl})".format(
1195 name=dummy[
'name'], isl=isl + 1)
1197 descSub[0] = descSub[
'as'][isl][0]
1198 if descSub[0]
is None:
1200 descSub[1] = descSub[
'as'][isl][1]
1212 if dummy[
'dim']
is not None and \
1213 not alltext(dummy[
'dim'][isl]).strip().startswith(
':'):
1214 offset = alltext(dummy[
'dim'][isl].find(
'./{*}lower-bound'))
1216 if descMain
is not None and descMain[
'as']
is not None:
1217 offset = descMain[
'as'][isl][0]
1220 elif offset.strip().startswith(
'-'):
1221 offset =
'(' + offset +
')'
1223 offset =
"LBOUND({name}, {isl})".format(
1224 name=dummy[
'name'], isl=isl + 1)
1225 if offset.upper() == descSub[0].upper():
1228 if descSub[0].strip().startswith(
'-'):
1229 offset +=
'- (' + descSub[0] +
')'
1231 offset +=
'-' + descSub[0]
1235 if tag(sl) ==
'element' or \
1236 (tag(sl) ==
'section-subscript' and ':' not in alltext(sl)):
1240 low = sl.find(
'./{*}lower-bound')
1242 low = createElem(
'lower-bound', tail=sl.text)
1243 low.append(createExprPart(descSub[0]))
1246 up = sl.find(
'./{*}upper-bound')
1248 up = createElem(
'upper-bound')
1249 up.append(createExprPart(descSub[1]))
1252 for bound
in bounds:
1254 if bound[-1].tail
is None:
1256 bound[-1].tail +=
'+' + offset
1262 if dummy[
'dim']
is not None and len(dummy[
'dim']) > len(slices):
1263 slices[-1].tail =
', '
1264 par = node.getParent(slices[-1])
1265 par.extend(dummy[
'dim'][len(slices):])
1270 updatedNamedE = createExprPart(alltext(namedE))
1271 namedE.tag = updatedNamedE.tag
1272 namedE.text = updatedNamedE.text
1273 for nnn
in namedE[:]:
1275 namedE.extend(updatedNamedE[:])
1277 node.remove(node.find(
'./{*}subroutine-stmt'))
1278 node.remove(node.find(
'./{*}end-subroutine-stmt'))
1281 mainScope.addVar([[mainScope.path, var[
'n'], mainScope.varSpec2stmt(var),
None]
1282 for var
in localVarToAdd])
1283 mainScope.addModuleVar([[mainScope.path, n2name(useStmt.find(
'.//{*}module-N//{*}N')),
1284 [n2name(v.find(
'.//{*}N'))
1285 for v
in useStmt.findall(
'.//{*}use-N')]]
1286 for useStmt
in localUseToAdd])
1289 index = list(parent).index(callStmt)
1290 parent.remove(callStmt)
1291 if callStmt.tail
is not None:
1292 if node[-1].tail
is None:
1293 node[-1].tail = callStmt.tail
1295 node[-1].tail = node[-1].tail + callStmt.tail
1296 for node
in node[::-1]:
1298 parent.insert(index, node)
1480 Remove statement nodes with optional code simplification.
1484 nodes : xml element or list of xml elements
1485 Node(s) to remove from the code tree.
1487 If True, also remove variables that become unused after
1488 the deletion of the nodes.
1489 simplifyStruct : bool
1490 If True, also remove empty enclosing constructs
1491 (IF blocks, loops) that become empty after node removal.
1495 >>> pft = PYFT('input.F90')
1496 >>> nodes = pft.findall('.//{*}call-stmt')
1497 >>> pft.removeStmtNode(nodes, simplifyVar=True, simplifyStruct=True)
1501 - Handles nested structures (removes inner statements first).
1502 - When simplifyStruct=True:
1503 - Empty IF blocks are removed
1504 - Empty loops are removed
1505 - WHERE constructs are handled
1506 - When simplifyVar=True:
1507 - Unused local variables are removed
1508 - Empty type declarations are cleaned up
1513 nodesToSuppress = []
1514 if not isinstance(nodes, list):
1517 if tag(node)
in (
'if-stmt',
'where-stmt'):
1518 action = node.find(
'./{*}action-stmt')
1519 if action
is not None and len(action) != 0:
1520 nodesToSuppress.append(action[0])
1522 nodesToSuppress.append(node)
1523 elif tag(node) ==
'}action-stmt':
1525 nodesToSuppress.append(node[0])
1527 nodesToSuppress.append(node)
1529 nodesToSuppress.append(node)
1534 for node
in nodesToSuppress:
1535 scopePath = self.getScopePath(node)
1536 if tag(node) ==
'do-construct':
1538 varToCheck.extend([(scopePath, n2name(arg))
1539 for arg
in node.find(
'./{*}do-stmt').findall(
'.//{*}N')])
1540 elif tag(node)
in (
'if-construct',
'if-stmt'):
1542 varToCheck.extend([(scopePath, n2name(arg))
1543 for arg
in node.findall(
'.//{*}condition-E//{*}N')])
1544 elif tag(node)
in (
'where-construct',
'where-stmt'):
1546 varToCheck.extend([(scopePath, n2name(arg))
1547 for arg
in node.findall(
'.//{*}mask-E//{*}N')])
1548 elif tag(node) ==
'call-stmt':
1550 varToCheck.extend([(scopePath, n2name(arg))
1551 for arg
in node.findall(
'./{*}arg-spec//{*}N')])
1553 varToCheck.append((scopePath,
1554 n2name(node.find(
'./{*}procedure-designator//{*}N'))))
1555 elif tag(node)
in (
'a-stmt',
'print-stmt'):
1556 varToCheck.extend([(scopePath, n2name(arg))
for arg
in node.findall(
'.//{*}N')])
1557 elif tag(node) ==
'selectcase-construct':
1559 varToCheck.extend([(scopePath, n2name(arg))
1560 for arg
in node.findall(
'.//{*}case-E//{*}N')])
1561 varToCheck.extend([(scopePath, n2name(arg))
1562 for arg
in node.findall(
'.//{*}case-value//{*}N')])
1566 for node
in nodesToSuppress:
1567 parent = self.getParent(node)
1568 parents[id(node)] = parent
1569 newlines =
'\n' * (alltext(node).count(
'\n')
if tag(node).endswith(
'-construct')
else 0)
1570 if node.tail
is not None or len(newlines) > 0:
1571 previous = self.getSiblings(node, after=
False)
1572 if len(previous) == 0:
1575 previous = previous[-1]
1576 if previous.tail
is None:
1578 previous.tail = (previous.tail.replace(
'\n',
'') +
1579 (node.tail
if node.tail
is not None else ''))
1583 self.removeVarIfUnused(varToCheck, excludeDummy=
True,
1584 excludeModule=
True, simplify=simplifyVar)
1587 newNodesToSuppress = []
1588 for node
in nodesToSuppress:
1589 parent = parents[id(node)]
1592 if tag(parent) ==
'action-stmt':
1593 newNodesToSuppress.append(self.getParent(parent))
1595 elif simplifyStruct:
1596 if tag(parent) ==
'do-construct' and len(
_nodesInDo(parent)) == 0:
1597 newNodesToSuppress.append(parent)
1598 elif tag(parent) ==
'if-block':
1599 parPar = self.getParent(parent)
1601 newNodesToSuppress.append(parPar)
1602 elif tag(parent) ==
'where-block':
1603 parPar = self.getParent(parent)
1605 newNodesToSuppress.append(parPar)
1606 elif tag(parent) ==
'selectcase-block':
1607 parPar = self.getParent(parent)
1609 newNodesToSuppress.append(parPar)
1611 constructNodes, otherNodes = [], []
1612 for nnn
in newNodesToSuppress:
1613 if tag(nnn).endswith(
'-construct'):
1614 if nnn
not in constructNodes:
1615 constructNodes.append(nnn)
1617 if nnn
not in otherNodes:
1618 otherNodes.append(nnn)
1620 if len(otherNodes) > 0:
1623 for nnn
in constructNodes:
1680 :param loopVariables: ordered dictionnary with loop variables as key and bounds as values.
1681 Bounds are expressed with a 2-tuple.
1682 Keys must be in the same order as the order used when addressing an
1683 element: if loopVariables.keys is [JI, JK], arrays are
1684 addressed with (JI, JK)
1685 :param indent: current indentation
1686 :param concurrent: if False, output is made of nested 'DO' loops
1687 if True, output is made of a single 'DO CONCURRENT' loop
1688 :return: (inner, outer, extraindent) with
1689 - inner the inner do-construct where statements must be added
1690 - outer the outer do-construct to be inserted somewhere
1691 - extraindent the number of added indentation
1692 (2 if concurrent else 2*len(loopVariables))
1718 for var, (lo, up)
in list(loopVariables.items())[::-1]:
1719 nodeV = createElem(
'V', tail=
'=')
1720 nodeV.append(createExprPart(var))
1721 lower, upper = createArrayBounds(lo, up,
'DOCONCURRENT')
1723 triplet = createElem(
'forall-triplet-spec')
1724 triplet.extend([nodeV, lower, upper])
1726 triplets.append(triplet)
1728 tripletLT = createElem(
'forall-triplet-spec-LT', tail=
')')
1729 for triplet
in triplets[:-1]:
1731 tripletLT.extend(triplets)
1733 dostmt = createElem(
'do-stmt', text=
'DO CONCURRENT (', tail=
'\n')
1734 dostmt.append(tripletLT)
1735 enddostmt = createElem(
'end-do-stmt', text=
'END DO')
1737 doconstruct = createElem(
'do-construct', tail=
'\n')
1738 doconstruct.extend([dostmt, enddostmt])
1739 inner = outer = doconstruct
1740 doconstruct[0].tail += (indent + 2) *
' '
1751 def makeDo(var, lo, up):
1752 doV = createElem(
'do-V', tail=
'=')
1753 doV.append(createExprPart(var))
1754 lower, upper = createArrayBounds(lo, up,
'DO')
1756 dostmt = createElem(
'do-stmt', text=
'DO ', tail=
'\n')
1757 dostmt.extend([doV, lower, upper])
1759 enddostmt = createElem(
'end-do-stmt', text=
'END DO')
1761 doconstruct = createElem(
'do-construct', tail=
'\n')
1762 doconstruct.extend([dostmt, enddostmt])
1767 for i, (var, (lo, up))
in enumerate(list(loopVariables.items())[::-1]):
1768 doconstruct = makeDo(var, lo, up)
1770 doconstruct[0].tail += (indent + 2 * i + 2) *
' '
1775 inner.insert(1, doconstruct)
1778 doconstruct.tail += (indent + 2 * i - 2) *
' '
1779 return inner, outer, 2
if concurrent
else 2 * len(loopVariables)
1804 :param item: item to remove from list
1805 :param itemPar: the parent of item (the list)
1808 nodesToSuppress = [item]
1811 i = list(itemPar).index(item)
1812 if item.tail
is not None and ',' in item.tail:
1815 item.tail = tail.replace(
',',
'')
1816 elif i != 0
and ',' in itemPar[i - 1].tail:
1818 tail = itemPar[i - 1].tail
1819 itemPar[i - 1].tail = tail.replace(
',',
'')
1825 while j < len(itemPar)
and not found:
1826 if nonCode(itemPar[j]):
1828 if itemPar[j].tail
is not None and ',' in itemPar[j].tail:
1831 tail = itemPar[j].tail
1832 itemPar[j].tail = tail.replace(
',',
'')
1842 while j >= 0
and not found:
1843 if itemPar[j].tail
is not None and ',' in itemPar[j].tail:
1846 tail = itemPar[j].tail
1847 itemPar[j].tail = tail.replace(
',',
'')
1849 if nonCode(itemPar[j]):
1857 len([e
for e
in itemPar
if not nonCode(e)]) != 1:
1858 raise RuntimeError(
"Something went wrong here....")
1861 if i + 1 < len(itemPar)
and tag(itemPar[i + 1]) ==
'cnt':
1863 reason =
'lastOnLine'
1875 elif len([itemPar[j]
for j
in range(i + 1, len(itemPar))
if not nonCode(itemPar[j])]) == 0:
1889 if reason
is not None:
1890 def _getPrecedingCnt(itemPar, i):
1892 Return the index of the preceding node which is a continuation character
1893 :param itemPar: the list containig the node to suppress
1894 :param i: the index of the current node, i-1 is the starting index for the search
1895 :return: a tuple with three elements:
1896 - the node containing the preceding '&' character
1897 - the parent of the node containing the preceding '&' character
1898 - index of the preceding '&' in the parent (previsous element
1901 - In the general case the preceding '&' belongs to the same list:
1902 USE MODD, ONLY: X, &
1904 - But it exists a special case, where the preceding '&' don't belong to
1905 the same list (in the following example, both '&' are attached to the parent):
1910 while j >= 0
and tag(itemPar[j]) ==
'C':
1912 if j >= 0
and tag(itemPar[j]) ==
'cnt':
1913 return itemPar[j], itemPar, j
1919 siblings = self.getSiblings(itemPar, before=
True, after=
False)
1920 j2 = len(siblings) - 1
1921 while j2 >= 0
and tag(siblings[j2]) ==
'C':
1923 if j2 >= 0
and tag(siblings[j2]) ==
'cnt':
1924 return siblings[j2], siblings, j2
1925 return None,
None,
None
1927 precCnt, newl, j = _getPrecedingCnt(itemPar, i)
1928 if precCnt
is not None:
1930 nodesToSuppress.append(precCnt
if reason ==
'last' else itemPar[i + 1])
1932 precCnt2, _, _ = _getPrecedingCnt(newl, j)
1933 if precCnt2
is not None:
1935 nodesToSuppress.append(precCnt2
if reason ==
'last' else precCnt)
1938 for node
in nodesToSuppress:
1945 parent = self.getParent(itemPar)
1946 i = list(parent).index(node)
1947 if i != 0
and node.tail
is not None:
1948 if parent[i - 1].tail
is None:
1949 parent[i - 1].tail =
''
1950 parent[i - 1].tail = parent[i - 1].tail + node.tail