372 :param varList: list of variables to remove. Each item is a list or tuple of two elements.
373 The first one describes where the variable is used, the second one is
374 the name of the variable. The first element is a '/'-separated path with
375 each element having the form 'module:<name of the module>',
376 'sub:<name of the subroutine>', 'func:<name of the function>' or
377 'type:<name of the type>'
378 :param simplify: try to simplify code (if we delete a declaration statement that used a
379 variable as kind selector, and if this variable is not used else where,
381 Remove the variable from declaration, and from the argument list if needed
387 for scopePath, varName
in varList:
388 nb = scopePath.count(
'/')
389 sortedVarList[nb] = sortedVarList.get(nb, []) + [(scopePath, varName.upper())]
391 varToRemoveIfUnused = []
393 nbList = []
if len(sortedVarList.keys()) == 0
else \
394 range(max(sortedVarList.keys()) + 1)[::-1]
396 sortedVarList[nb] = sortedVarList.get(nb, [])
398 for scopePath
in list(set(scopePath
for scopePath, _
in sortedVarList[nb])):
400 scope = self.mainScope.getScopeNode(scopePath)
402 varNames = list(set(v
for (w, v)
in sortedVarList[nb]
if w == scopePath))
410 for node
in list(scope):
414 dummyList = node.find(
'{*}dummy-arg-LT')
415 if dummyList
is not None:
417 for arg
in dummyList.findall(
'.//{*}arg-N'):
418 name = n2name(arg.find(
'.//{*}N')).upper()
419 for varName
in [v
for v
in varNames
if v == name]:
421 scope.removeFromList(arg, dummyList)
424 if tag(node) == declStmt:
427 declList = node.find(
'./{*}EN-decl-LT')
428 for enDecl
in declList.findall(
'.//{*}EN-decl'):
429 name = n2name(enDecl.find(
'.//{*}N')).upper()
430 for varName
in [v
for v
in varNames
if v == name]:
433 varNames.remove(varName)
434 scope.removeFromList(enDecl, declList)
436 if len(list(declList.findall(
'./{*}EN-decl'))) == 0:
438 varToRemoveIfUnused.extend([[scopePath, n2name(nodeN)]
439 for nodeN
in node.findall(
'.//{*}N')])
442 if previous
is not None and node.tail
is not None:
443 if previous.tail
is None:
445 previous.tail += node.tail
447 scope.getParent(node).remove(node)
450 if tag(node) ==
'use-stmt':
452 useList = node.find(
'./{*}rename-LT')
453 if useList
is not None:
454 for rename
in useList.findall(
'.//{*}rename'):
455 name = n2name(rename.find(
'.//{*}N')).upper()
456 for varName
in [v
for v
in varNames
if v == name]:
457 varNames.remove(varName)
459 scope.removeFromList(rename, useList)
461 attribute = node.find(
'{*}module-N').tail
462 if attribute
is None:
464 attribute = attribute.replace(
' ',
'').replace(
'\n',
'')
465 attribute = attribute.replace(
'&',
'').upper()
466 useList = node.find(
'./{*}rename-LT')
467 if len(useList) == 0
and attribute[0] ==
',' and \
468 attribute[1:] ==
'ONLY:':
471 if previous
is not None and node.tail
is not None:
472 if previous.tail
is None:
474 previous.tail += node.tail
476 scope.getParent(node).remove(node)
477 scope.tree.signal(scope)
478 elif len(useList) == 0:
480 moduleName = scope.getSiblings(useList, before=
True,
482 previousTail = moduleName.tail
483 if previousTail
is not None:
484 moduleName.tail = previousTail.replace(
',',
'')
485 scope.getParent(useList).remove(useList)
487 if len(varNames) == 0:
494 if len(varNames) != 0:
495 newWhere =
'/'.join(scopePath.split(
'/')[:-1])
496 sortedVarList[nb - 1] = sortedVarList.get(nb - 1, []) + \
497 [(newWhere, varName)
for varName
in varNames]
499 if simplify
and len(varToRemoveIfUnused) > 0:
506 :param varList: list of variable specification to insert in the xml code
507 a variable specification is a list of four elements:
508 - variable scope path (path to module, subroutine, function or type
511 - declarative statment
512 - position of the variable in the list of dummy argument,
513 None for a local variable
517 for (scopePath, name, declStmt, pos)
in varList:
518 scope = self.getScopeNode(scopePath)
522 argN = createElem(
'arg-N')
523 nodeN = createElem(
'N')
524 nodeN.append(createElem(
'n', text=name))
527 argLst = scope.find(
'.//{*}dummy-arg-LT')
530 scope[0][0].tail =
'('
531 argLst = createElem(
'dummy-arg-LT', tail=
')')
532 scope[0].insert(1, argLst)
533 scope.insertInList(pos, argN, argLst)
538 if declStmt
is not None and declStmt !=
'':
542 if scopePath.split(
'/')[-1].split(
':')[0] ==
'type':
545 ds = createExpr(declStmt)[0]
546 previousTail =
'\n' + declStmt[:re.search(
r'\S', declStmt).start()]
552 ds.tail = scope[-2].tail
553 scope[-2].tail = previousTail
559 ds = createExpr(declStmt)[0]
560 previousTail =
'\n' + declStmt[:re.search(
r'\S', declStmt).start()]
563 declLst = [node
for node
in scope
if tag(node) == declStmtTag]
564 if len(declLst) != 0:
569 index = list(scope).index(decl)
570 if name
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
575 stmtLst = [node
for node
in scope
if isExecutable(node)]
576 if len(stmtLst) == 0:
579 index = len(scope) - 1
582 index = list(scope).index(stmtLst[0])
586 ds.tail = scope[index - 1].tail
587 scope[index - 1].tail = previousTail
588 scope.insert(index, ds)
887 :param declTemplate: declaration template
888 :param startTemplate: template for the first executable statement
889 :param: endTemplate: template for the last executable statement
890 :return: number of arrays modified
891 Modifies all automatic arrays declaration in subroutine and functions. The declaration is
892 replaced by the declaration template, the start template is inserted as first executable
893 statement and the end template as last executable statement. Each template can use the
894 following place holders:
895 "{doubledotshape}", "{shape}", "{lowUpList}", "{name}" and "{type}" wich are, respectively
896 modified into ":, :, :", "I, I:J, 0:I", "1, I, I, J, 0, I", "A", "REAL" if the original
897 declaration statement was "A(I, I:J, 0:I)". The template
898 "{type}, DIMENSION({doubledotshape}), ALLOCATABLE :: {name}#
899 ALLOCATE({name}({shape}))#DEALLOCATE({name})"
900 replaces automatic arrays by allocatables
902 templates = {
'decl': declTemplate
if declTemplate
is not None else '',
903 'start': startTemplate
if startTemplate
is not None else '',
904 'end': endTemplate
if endTemplate
is not None else ''}
907 for scope
in [scope
for scope
in self.getScopes()
908 if scope.path.split(
'/')[-1].split(
':')[0]
in (
'sub',
'func')]:
911 varListToTransform = []
912 for var
in [var
for var
in scope.varList
913 if var[
'as']
is not None and
914 len(var[
'as']) > 0
and
915 not (var[
'arg']
or var[
'allocatable']
or
916 var[
'pointer']
or var[
'result'])]:
919 if var[
'init']
is not None:
920 logging.warning(
"An array (%s) has an initial value, it can't be " +
921 "processed by modifyAutomaticArrays.)", var[
'n'])
923 varListToTransform.append(var)
927 orderedVarListToTransform = []
928 while len(varListToTransform) > 0:
930 for var
in varListToTransform[:]:
932 listN = [x
for dim
in var[
'asx']
for x
in dim
if x
is not None]
933 listN = [n2name(nodeN).upper()
for asx
in listN
934 for nodeN
in asx.findall(
'.//{*}N/{*}n/..')]
935 if len(set(listN).intersection([v[
'n'].upper()
936 for v
in varListToTransform])) == 0:
938 varListToTransform.remove(var)
939 orderedVarListToTransform.append(var)
942 raise PYFTError(
'It seems that there is a circular reference in ' +
943 'the declaration statements')
945 for var
in orderedVarListToTransform[::-1]:
948 templ = copy.deepcopy(templates)
949 for templPart
in templ:
950 if '{doubledotshape}' in templ[templPart]:
951 templ[templPart] = templ[templPart].replace(
952 '{doubledotshape}',
','.join([
':'] * len(var[
'as'])))
953 if '{shape}' in templ[templPart]:
955 for i
in range(len(var[
'as'])):
956 if var[
'as'][i][0]
is None:
957 result.append(var[
'as'][i][1])
959 result.append(var[
'as'][i][0] +
':' + var[
'as'][i][1])
960 templ[templPart] = templ[templPart].replace(
'{shape}',
', '.join(result))
961 if '{name}' in templ[templPart]:
962 templ[templPart] = templ[templPart].replace(
'{name}', var[
'n'])
963 if '{type}' in templ[templPart]:
964 templ[templPart] = templ[templPart].replace(
'{type}', var[
't'])
965 if '{lowUpList}' in templ[templPart]:
967 for i
in range(len(var[
'as'])):
968 if var[
'as'][i][0]
is None:
969 result.extend([1, var[
'as'][i][1]])
971 result.extend([var[
'as'][i][0], var[
'as'][i][1]])
972 templ[templPart] = templ[templPart].replace(
973 '{lowUpList}',
', '.join([str(r)
for r
in result]))
976 separator =
"!ABCDEFGHIJKLMNOPQRSTUVWabcdefghijklmnopqrstuvwxyz0123456789"
978 for node
in createExpr(templ[
'decl'] +
'\n' + separator +
'\n' +
979 templ[
'start'] +
'\n' + separator +
'\n' +
981 templPart = list(templ.keys())[part]
982 if not isinstance(templ[templPart], list):
983 templ[templPart] = []
984 if tag(node) ==
'C' and node.text == separator:
987 templ[templPart].append(node)
993 for decl
in scope.findall(
'./{*}T-decl-stmt'):
994 index = list(scope).index(decl)
995 if var[
'n']
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
997 scope.removeVar([(scope.path, var[
'n'])], simplify=
False)
998 for nnn
in templ[
'decl'][::-1]:
999 scope.insert(index, nnn)
1002 for nnn
in templ[
'start'][::-1]:
1003 scope.insertStatement(nnn,
True)
1004 for nnn
in templ[
'end'][::-1]:
1005 scope.insertStatement(nnn,
False)
1104 Transform a array-R into a parens-R node by replacing slices by variables
1105 In 'A(:)', the ':' is in a array-R node whereas in 'A(JL)', 'JL' is in a parens-R node.
1106 Both the array-R and the parens-R nodes are inside a R-LT node
1107 :param namedE: a named-E node
1108 :param table: dictionnary returned by the decode function
1109 :param varList: None or a VarList object in which varaibles are searched for
1132 nodeRLT = namedE.find(
'./{*}R-LT')
1133 arrayR = nodeRLT.find(
'./{*}array-R')
1134 if arrayR
is not None:
1135 index = list(nodeRLT).index(arrayR)
1136 parensR = createElem(
'parens-R', text=
'(', tail=
')')
1137 elementLT = createElem(
'element-LT')
1138 parensR.append(elementLT)
1140 for ss
in nodeRLT[index].findall(
'./{*}section-subscript-LT/{*}section-subscript'):
1141 element = createElem(
'element', tail=
', ')
1142 elementLT.append(element)
1143 if ':' in alltext(ss):
1145 varName = list(table.keys())[ivar]
1146 lower = ss.find(
'./{*}lower-bound')
1147 upper = ss.find(
'./{*}upper-bound')
1148 if lower
is not None:
1149 lower = alltext(lower)
1150 if upper
is not None:
1151 upper = alltext(upper)
1152 if lower
is not None and ss.text
is not None and ':' in ss.text:
1156 if lower
is None and upper
is None:
1160 element.append(createExprPart(varName))
1167 lower = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1168 array=
True)[
'as'][ivar][0]
1173 upper = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1174 array=
True)[
'as'][ivar][1]
1180 newlower = simplifyExpr(lower, add=varName, sub=table[varName][0])
1181 newupper = simplifyExpr(upper, add=varName, sub=table[varName][1])
1182 if newlower != newupper:
1183 raise PYFTError((
"Don't know how to do with an array declared with " +
1184 "'{la}:{ua}' and a loop from '{ll}' to '{ul}'"
1185 ).format(la=lower, ua=upper,
1186 ll=table[varName][0],
1187 ul=table[varName][1]))
1188 element.append(createExprPart(newlower))
1190 element.append(ss.find(
'./{*}lower-bound'))
1192 nodeRLT.remove(nodeRLT[index])
1193 nodeRLT.insert(index, parensR)
1198 Find bounds and loop variable given an array
1199 :param arr: array node (named-E node with a array-R child)
1200 :param loopVar: None to create new variable for each added DO loop
1201 or a function that return the name of the variable to use for the loop
1203 This function returns a string (name of the variable), or True to create
1204 a new variable, or False to not transform this statement
1205 The functions takes as arguments:
1206 - lower and upper bounds as defined in the declaration statement
1207 - lower and upper bounds as given in the statement
1210 :param extraVarList: None or list of variables (such as those contained in a VarList object)
1211 defined but not yet available in the self.varList object.
1212 :return: the tuple (table, newVar) where:
1213 table is a dictionnary: keys are loop variable names
1214 values are tuples with lower and upper bounds
1215 newVar is a list of loop variables not found in varList. This list has the same
1216 format as the varList list.
1218 In case the loop variable cannot be defined, the function returns (None, [])
1221 name = n2name(arr.find(
'./{*}N'))
1223 extraVarList = extraVarList
if extraVarList
is not None else []
1226 for iss, ss
in enumerate(arr.findall(
'./{*}R-LT/{*}array-R/{*}section-subscript-LT/' +
1227 '{*}section-subscript')):
1230 if ':' in alltext(ss):
1235 if varName
is not False and varName
in table:
1236 raise PYFTError((
"The variable {var} must be used for the rank #{i1} whereas " +
1237 "it is already used for rank #{i2} (for array {name})."
1238 ).format(var=varName, i1=str(iss),
1239 i2=str(list(table.keys()).index(varName)),
1250 varName =
'J' + str(j)
1251 var = self.
varList.findVar(varName, extraVarList=extraVarList + varNew)
1252 if (var
is None or var.get(
'new',
False))
and varName
not in table:
1254 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1255 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1256 'scopePath': self.
path}
1257 if varDesc
not in varNew:
1258 varNew.append(varDesc)
1260 elif (varName
is not False and
1261 self.
varList.findVar(varName, array=
False, exactScope=
True)
is None):
1263 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1264 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1265 'scopePath': self.
path}
1266 varNew.append(varDesc)
1269 table[varName] = (lower, upper)
1271 return (
None, [])
if False in table
else (table, varNew)
1320 def isVarUsed(self, varList, exactScope=False, dummyAreAlwaysUsed=False):
1322 :param varList: list of variables to test. Each item is a list or tuple of two elements.
1323 The first one describes where the variable is declared, the second one is
1324 the name of the variable. The first element is a '/'-separated path with
1325 each element having the form 'module:<name of the module>',
1326 'sub:<name of the subroutine>' or 'func:<name of the function>'
1327 :param exactScope: True to search strictly in scope
1328 :param dummyAreAlwaysUsed: Returns True if variable is a dummy argument
1329 :return: a dict whose keys are the elements of varList, and values are True when the
1330 variable is used, False otherwise
1332 If exactScope is True, the function will search for variable usage
1333 only in this scope. But this feature has a limited interest.
1335 If exactScope is False:
1336 - if scopePath is a subroutine/function in a contains section,
1337 and if the variable is not declared in this scope, usages are
1338 searched in the module/subroutine/function upper that declared
1339 the variable and in all subroutines/functions in the contains section
1340 - if scopePath is a module/subroutine/function that has a
1341 contains sections, usages are searched in all subroutines/functions
1342 in the contains section
1344 To know if a variable can be removed, you must use exactScope=False
1348 allScopes = {scope.path: scope
for scope
in self.mainScope.getScopes()}
1352 locsVar = {(scopePath, varName): [scopePath]
1353 for scopePath, varName
in varList}
1356 for scopePath, varName
in varList:
1359 var = allScopes[scopePath].varList.findVar(varName)
1360 path = scopePath.split(
'/')[0]
if var
is None else var[
'scopePath']
1365 for scPath, sc
in allScopes.items():
1366 if scPath.startswith(path +
'/')
and \
1367 scPath.split(
'/')[-1].split(
':')[0] !=
'type':
1369 if sc.varList.findVar(varName, exactScope=
True)
is None:
1371 testScopes.append(scPath)
1372 locsVar[(scopePath, varName)] = testScopes
1376 for scopePath
in list(set(item
for sublist
in locsVar.values()
for item
in sublist)):
1377 usedVar[scopePath] = []
1379 for node
in allScopes[scopePath]:
1382 if not tag(node) ==
'use-stmt':
1383 if tag(node) ==
'T-decl-stmt':
1387 nodesN = node.findall(
'.//{*}_T-spec_//{*}N') + \
1388 node.findall(
'.//{*}shape-spec//{*}N')
1390 nodesN = node.findall(
'.//{*}N')
1393 for nodeN
in nodesN:
1394 if dummyAreAlwaysUsed:
1398 usedVar[scopePath].append(n2name(nodeN).upper())
1400 parPar = allScopes[scopePath].getParent(nodeN, 2)
1403 if parPar
is None or not tag(parPar) ==
'dummy-arg-LT':
1404 usedVar[scopePath].append(n2name(nodeN).upper())
1407 for scopePath, varName
in varList:
1408 assert scopePath.split(
'/')[-1].split(
':')[0] !=
'type', \
1409 'We cannot check type component usage'
1410 result[(scopePath, varName)] = any(varName.upper()
in usedVar[scopePath]
1411 for scopePath
in locsVar[(scopePath, varName)])
1417 def addArgInTree(self, varName, declStmt, pos, stopScopes, moduleVarList=None,
1419 parserOptions=None, wrapH=False):
1421 Adds an argument to the routine and propagates it upward until we encounter a scope
1422 where the variable exists or a scope in stopScopes
1423 :param varName: variable name
1424 :param declStmt: declarative statment (will be used by addVar)
1425 :param pos: position of the variable in the list of dummy argument
1426 :param stopScopes: list of scopes to reach
1427 :param moduleVarList: list of module variable specification to insert in the xml code
1428 a module variable specification is a list of two elements:
1430 - variable name or or list of variable names
1431 or None to add a USE statement without the ONLY attribute
1432 use moduleVarList to not add module variables
1433 :param otherNames: None or list of other variable names that can be used
1434 These variables are used first
1435 :param parserOptions, wrapH: see the PYFT class
1437 Argument is inserted only on paths leading to one of scopes listed in stopScopes
1439 def insertInArgList(varName, varNameToUse, pos, callFuncStmt):
1441 Insert varName in the list of arguments to the subroutine or function call
1442 :param varName: name of the dummy argument
1443 :param varNameToUse: name of the variable
1444 :param pos: inclusion position
1445 :param callFuncStmt: call statement or function call
1447 argList = callFuncStmt.find(
'./{*}R-LT/{*}parens-R/{*}element-LT')
1448 if argList
is not None:
1449 container = createElem(
'element')
1451 argList = callFuncStmt.find(
'./{*}arg-spec')
1452 container = createElem(
'arg')
1455 callFuncStmt.find(
'./{*}procedure-designator').tail =
'('
1456 argList = createElem(
'arg-spec', tail=
')')
1457 callFuncStmt.append(argList)
1458 item = createExprPart(varNameToUse)
1459 previous = pos - 1
if pos >= 0
else len(argList) + pos
1460 while previous >= 0
and tag(argList[previous])
in (
'C',
'cnt'):
1462 following = pos
if pos > 0
else len(argList) + pos + 1
1463 while following <= len(argList) - 1
and tag(argList[following])
in (
'C',
'cnt'):
1465 if (previous >= 0
and argList[previous].find(
'./{*}arg-N/{*}k')
is not None)
or \
1466 (following <= len(argList) - 1
and
1467 argList[following].find(
'./{*}arg-N/{*}k')
is not None)
or \
1468 following == len(argList):
1474 k = createElem(
'k', text=varName)
1475 argN = createElem(
'arg-N', tail=
'=')
1477 argN.set(
'n', varName)
1478 container.append(argN)
1479 container.append(item)
1480 self.insertInList(pos, container, argList)
1482 if self.
path in stopScopes
or self.tree.isUnderStopScopes(self.
path, stopScopes):
1485 var = self.
varList.findVar(varName, exactScope=
True)
1486 if otherNames
is not None:
1487 vOther = [self.
varList.findVar(v, exactScope=
True)
for v
in otherNames]
1488 vOther = [v
for v
in vOther
if v
is not None]
1494 self.
addVar([[self.
path, varName, declStmt, pos]])
1495 if moduleVarList
is not None:
1498 for (moduleName, moduleVarNames)
in moduleVarList])
1501 if len(self.
path.split(
'/')) == 1:
1502 filename, scopePathInterface = self.tree.findScopeInterface(self.
path)
1503 if filename
is not None:
1506 if self.getFileName() == os.path.normpath(filename):
1508 xml = self.mainScope
1512 filename, parserOptions, wrapH, tree=self.tree,
1513 clsPYFT=self._mainScope.__class__)
1515 scopeInterface = xml.getScopeNode(scopePathInterface)
1516 varInterface = scopeInterface.varList.findVar(varName, exactScope=
True)
1517 if varInterface
is None:
1518 scopeInterface.addVar([[scopePathInterface, varName,
1520 if moduleVarList
is not None:
1523 [(scopePathInterface, moduleName, moduleVarNames)
1524 for (moduleName, moduleVarNames)
in moduleVarList])
1531 if var
is None and self.
path not in stopScopes:
1534 for scopePathUp
in self.tree.calledByScope(self.
path):
1535 if scopePathUp
in stopScopes
or self.tree.isUnderStopScopes(scopePathUp,
1540 for filename
in self.tree.scopeToFiles(scopePathUp):
1543 if self.getFileName() == os.path.normpath(filename):
1545 xml = self.mainScope
1549 filename, parserOptions, wrapH,
1551 clsPYFT=self._mainScope.__class__)
1553 scopeUp = xml.getScopeNode(scopePathUp)
1555 scopeUp.addArgInTree(
1556 varName, declStmt, pos,
1557 stopScopes, moduleVarList, otherNames,
1558 parserOptions=parserOptions,
1561 name = self.
path.split(
'/')[-1].split(
':')[1].upper()
1563 varNameToUse = varName
1564 if otherNames
is not None:
1565 vOther = [scopeUp.varList.findVar(v, exactScope=
True)
1566 for v
in otherNames]
1567 vOther = [v
for v
in vOther
if v
is not None]
1569 varNameToUse = vOther[-1][
'n']
1570 if self.
path.split(
'/')[-1].split(
':')[0] ==
'sub':
1572 for callStmt
in scopeUp.findall(
'.//{*}call-stmt'):
1573 callName = n2name(callStmt.find(
1574 './{*}procedure-designator/{*}named-E/{*}N')).upper()
1575 if callName == name:
1576 insertInArgList(varName, varNameToUse, pos, callStmt)
1580 for funcCall
in scopeUp.findall(
1581 './/{*}named-E/{*}R-LT/{*}parens-R/' +
1582 '{*}element-LT/../../..'):
1583 funcName = n2name(funcCall.find(
'./{*}N')).upper()
1584 if funcName == name:
1585 insertInArgList(varName, varNameToUse, pos, funcCall)
1596 for interface
in self.findall(
'.//{*}interface-construct/{*}' +
1597 'program-unit/{*}subroutine-stmt/' +
1598 '{*}subroutine-N/{*}N/../../../'):
1599 if n2name(interface.find(
'./{*}subroutine-stmt/' +
1600 '{*}subroutine-N/' +
1601 '{*}N')).upper() == name:
1603 raise PYFTError(
'This case is not yet implemented')