375 :param varList: list of variables to remove. Each item is a list or tuple of two elements.
376 The first one describes where the variable is used, the second one is
377 the name of the variable. The first element is a '/'-separated path with
378 each element having the form 'module:<name of the module>',
379 'sub:<name of the subroutine>', 'func:<name of the function>' or
380 'type:<name of the type>'
381 :param simplify: try to simplify code (if we delete a declaration statement that used a
382 variable as kind selector, and if this variable is not used else where,
384 Remove the variable from declaration, and from the argument list if needed
390 for scopePath, varName
in varList:
391 nb = scopePath.count(
'/')
392 sortedVarList[nb] = sortedVarList.get(nb, []) + [(scopePath, varName.upper())]
394 varToRemoveIfUnused = []
396 nbList = []
if len(sortedVarList.keys()) == 0
else \
397 range(max(sortedVarList.keys()) + 1)[::-1]
399 sortedVarList[nb] = sortedVarList.get(nb, [])
401 for scopePath
in list(set(scopePath
for scopePath, _
in sortedVarList[nb])):
403 scope = self.mainScope.getScopeNode(scopePath)
405 varNames = list(set(v
for (w, v)
in sortedVarList[nb]
if w == scopePath))
413 for node
in list(scope):
417 dummyList = node.find(
'{*}dummy-arg-LT')
418 if dummyList
is not None:
420 for arg
in dummyList.findall(
'.//{*}arg-N'):
421 name = n2name(arg.find(
'.//{*}N')).upper()
422 for varName
in [v
for v
in varNames
if v == name]:
424 scope.removeFromList(arg, dummyList)
427 if tag(node) == declStmt:
430 declList = node.find(
'./{*}EN-decl-LT')
431 for enDecl
in declList.findall(
'.//{*}EN-decl'):
432 name = n2name(enDecl.find(
'.//{*}N')).upper()
433 for varName
in [v
for v
in varNames
if v == name]:
436 varNames.remove(varName)
437 scope.removeFromList(enDecl, declList)
439 if len(list(declList.findall(
'./{*}EN-decl'))) == 0:
441 varToRemoveIfUnused.extend([[scopePath, n2name(nodeN)]
442 for nodeN
in node.findall(
'.//{*}N')])
445 if previous
is not None and node.tail
is not None:
446 if previous.tail
is None:
448 previous.tail += node.tail
450 scope.getParent(node).remove(node)
453 if tag(node) ==
'use-stmt':
455 useList = node.find(
'./{*}rename-LT')
456 if useList
is not None:
457 for rename
in useList.findall(
'.//{*}rename'):
458 name = n2name(rename.find(
'.//{*}N')).upper()
459 for varName
in [v
for v
in varNames
if v == name]:
460 varNames.remove(varName)
462 scope.removeFromList(rename, useList)
464 attribute = node.find(
'{*}module-N').tail
465 if attribute
is None:
467 attribute = attribute.replace(
' ',
'').replace(
'\n',
'')
468 attribute = attribute.replace(
'&',
'').upper()
469 useList = node.find(
'./{*}rename-LT')
470 if len(useList) == 0
and attribute[0] ==
',' and \
471 attribute[1:] ==
'ONLY:':
474 if previous
is not None and node.tail
is not None:
475 if previous.tail
is None:
477 previous.tail += node.tail
479 scope.getParent(node).remove(node)
480 scope.tree.signal(scope)
481 elif len(useList) == 0:
483 moduleName = scope.getSiblings(useList, before=
True,
485 previousTail = moduleName.tail
486 if previousTail
is not None:
487 moduleName.tail = previousTail.replace(
',',
'')
488 scope.getParent(useList).remove(useList)
490 if len(varNames) == 0:
497 if len(varNames) != 0:
498 newWhere =
'/'.join(scopePath.split(
'/')[:-1])
499 sortedVarList[nb - 1] = sortedVarList.get(nb - 1, []) + \
500 [(newWhere, varName)
for varName
in varNames]
502 if simplify
and len(varToRemoveIfUnused) > 0:
509 :param varList: list of variable specification to insert in the xml code
510 a variable specification is a list of four elements:
511 - variable scope path (path to module, subroutine, function or type
514 - declarative statment
515 - position of the variable in the list of dummy argument,
516 None for a local variable
520 for (scopePath, name, declStmt, pos)
in varList:
521 scope = self.getScopeNode(scopePath)
525 argN = createElem(
'arg-N')
526 nodeN = createElem(
'N')
527 nodeN.append(createElem(
'n', text=name))
530 argLst = scope.find(
'.//{*}dummy-arg-LT')
533 scope[0][0].tail =
'('
534 argLst = createElem(
'dummy-arg-LT', tail=
')')
535 scope[0].insert(1, argLst)
536 scope.insertInList(pos, argN, argLst)
541 if declStmt
is not None and declStmt !=
'':
545 if scopePath.split(
'/')[-1].split(
':')[0] ==
'type':
548 ds = createExpr(declStmt)[0]
549 previousTail =
'\n' + declStmt[:re.search(
r'\S', declStmt).start()]
555 ds.tail = scope[-2].tail
556 scope[-2].tail = previousTail
562 ds = createExpr(declStmt)[0]
563 previousTail =
'\n' + declStmt[:re.search(
r'\S', declStmt).start()]
566 declLst = [node
for node
in scope
if tag(node) == declStmtTag]
567 if len(declLst) != 0:
572 index = list(scope).index(decl)
573 if name
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
578 stmtLst = [node
for node
in scope
if isExecutable(node)]
579 if len(stmtLst) == 0:
582 index = len(scope) - 1
585 index = list(scope).index(stmtLst[0])
589 ds.tail = scope[index - 1].tail
590 scope[index - 1].tail = previousTail
591 scope.insert(index, ds)
890 :param declTemplate: declaration template
891 :param startTemplate: template for the first executable statement
892 :param: endTemplate: template for the last executable statement
893 :return: number of arrays modified
894 Modifies all automatic arrays declaration in subroutine and functions. The declaration is
895 replaced by the declaration template, the start template is inserted as first executable
896 statement and the end template as last executable statement. Each template can use the
897 following place holders:
898 "{doubledotshape}", "{shape}", "{lowUpList}", "{name}" and "{type}" wich are, respectively
899 modified into ":, :, :", "I, I:J, 0:I", "1, I, I, J, 0, I", "A", "REAL" if the original
900 declaration statement was "A(I, I:J, 0:I)". The template
901 "{type}, DIMENSION({doubledotshape}), ALLOCATABLE :: {name}#
902 ALLOCATE({name}({shape}))#DEALLOCATE({name})"
903 replaces automatic arrays by allocatables
905 templates = {
'decl': declTemplate
if declTemplate
is not None else '',
906 'start': startTemplate
if startTemplate
is not None else '',
907 'end': endTemplate
if endTemplate
is not None else ''}
910 for scope
in [scope
for scope
in self.getScopes()
911 if scope.path.split(
'/')[-1].split(
':')[0]
in (
'sub',
'func')]:
914 varListToTransform = []
915 for var
in [var
for var
in scope.varList
916 if var[
'as']
is not None and
917 len(var[
'as']) > 0
and
918 'CHARACTER' not in var[
't']
and
919 not (var[
'arg']
or var[
'allocatable']
or
920 var[
'pointer']
or var[
'result'])]:
923 if var[
'init']
is None:
925 varListToTransform.append(var)
929 orderedVarListToTransform = []
930 while len(varListToTransform) > 0:
932 for var
in varListToTransform[:]:
934 listN = [x
for dim
in var[
'asx']
for x
in dim
if x
is not None]
935 listN = [n2name(nodeN).upper()
for asx
in listN
936 for nodeN
in asx.findall(
'.//{*}N/{*}n/..')]
937 if len(set(listN).intersection([v[
'n'].upper()
938 for v
in varListToTransform])) == 0:
940 varListToTransform.remove(var)
941 orderedVarListToTransform.append(var)
944 raise PYFTError(
'It seems that there is a circular reference in ' +
945 'the declaration statements')
947 for var
in orderedVarListToTransform[::-1]:
950 templ = copy.deepcopy(templates)
951 for templPart
in templ:
952 if '{doubledotshape}' in templ[templPart]:
953 templ[templPart] = templ[templPart].replace(
954 '{doubledotshape}',
','.join([
':'] * len(var[
'as'])))
955 if '{shape}' in templ[templPart]:
957 for i
in range(len(var[
'as'])):
958 if var[
'as'][i][0]
is None:
959 result.append(var[
'as'][i][1])
961 result.append(var[
'as'][i][0] +
':' + var[
'as'][i][1])
962 templ[templPart] = templ[templPart].replace(
'{shape}',
', '.join(result))
963 if '{name}' in templ[templPart]:
964 templ[templPart] = templ[templPart].replace(
'{name}', var[
'n'])
965 if '{type}' in templ[templPart]:
966 templ[templPart] = templ[templPart].replace(
'{type}', var[
't'])
967 if '{lowUpList}' in templ[templPart]:
969 for i
in range(len(var[
'as'])):
970 if var[
'as'][i][0]
is None:
971 result.extend([1, var[
'as'][i][1]])
973 result.extend([var[
'as'][i][0], var[
'as'][i][1]])
974 templ[templPart] = templ[templPart].replace(
975 '{lowUpList}',
', '.join([str(r)
for r
in result]))
978 separator =
"!ABCDEFGHIJKLMNOPQRSTUVWabcdefghijklmnopqrstuvwxyz0123456789"
980 for node
in createExpr(templ[
'decl'] +
'\n' + separator +
'\n' +
981 templ[
'start'] +
'\n' + separator +
'\n' +
983 templPart = list(templ.keys())[part]
984 if not isinstance(templ[templPart], list):
985 templ[templPart] = []
986 if tag(node) ==
'C' and node.text == separator:
989 templ[templPart].append(node)
995 for decl
in scope.findall(
'./{*}T-decl-stmt'):
996 index = list(scope).index(decl)
997 if var[
'n']
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
999 scope.removeVar([(scope.path, var[
'n'])], simplify=
False)
1000 for nnn
in templ[
'decl'][::-1]:
1001 scope.insert(index, nnn)
1004 for nnn
in templ[
'start'][::-1]:
1005 scope.insertStatement(nnn,
True)
1006 for nnn
in templ[
'end'][::-1]:
1007 scope.insertStatement(nnn,
False)
1109 Transform a array-R into a parens-R node by replacing slices by variables
1110 In 'A(:)', the ':' is in a array-R node whereas in 'A(JL)', 'JL' is in a parens-R node.
1111 Both the array-R and the parens-R nodes are inside a R-LT node
1112 :param namedE: a named-E node
1113 :param table: dictionnary returned by the decode function
1114 :param varList: None or a VarList object in which varaibles are searched for
1137 nodeRLT = namedE.find(
'./{*}R-LT')
1138 arrayR = nodeRLT.find(
'./{*}array-R')
1139 if arrayR
is not None:
1140 index = list(nodeRLT).index(arrayR)
1141 parensR = createElem(
'parens-R', text=
'(', tail=
')')
1142 elementLT = createElem(
'element-LT')
1143 parensR.append(elementLT)
1145 for ss
in nodeRLT[index].findall(
'./{*}section-subscript-LT/{*}section-subscript'):
1146 element = createElem(
'element', tail=
', ')
1147 elementLT.append(element)
1148 if ':' in alltext(ss):
1150 varName = list(table.keys())[ivar]
1151 lower = ss.find(
'./{*}lower-bound')
1152 upper = ss.find(
'./{*}upper-bound')
1153 if lower
is not None:
1154 lower = alltext(lower)
1155 if upper
is not None:
1156 upper = alltext(upper)
1157 if lower
is not None and ss.text
is not None and ':' in ss.text:
1161 if lower
is None and upper
is None:
1165 element.append(createExprPart(varName))
1172 lower = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1173 array=
True)[
'as'][ivar][0]
1178 upper = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1179 array=
True)[
'as'][ivar][1]
1185 newlower = simplifyExpr(lower, add=varName, sub=table[varName][0])
1186 newupper = simplifyExpr(upper, add=varName, sub=table[varName][1])
1187 if newlower != newupper:
1188 raise PYFTError((
"Don't know how to do with an array declared with " +
1189 "'{la}:{ua}' and a loop from '{ll}' to '{ul}'"
1190 ).format(la=lower, ua=upper,
1191 ll=table[varName][0],
1192 ul=table[varName][1]))
1193 element.append(createExprPart(newlower))
1195 element.append(ss.find(
'./{*}lower-bound'))
1197 nodeRLT.remove(nodeRLT[index])
1198 nodeRLT.insert(index, parensR)
1203 Find bounds and loop variable given an array
1204 :param arr: array node (named-E node with a array-R child)
1205 :param loopVar: None to create new variable for each added DO loop
1206 or a function that return the name of the variable to use for the loop
1208 This function returns a string (name of the variable), or True to create
1209 a new variable, or False to not transform this statement
1210 The functions takes as arguments:
1211 - lower and upper bounds as defined in the declaration statement
1212 - lower and upper bounds as given in the statement
1215 :param extraVarList: None or list of variables (such as those contained in a VarList object)
1216 defined but not yet available in the self.varList object.
1217 :return: the tuple (table, newVar) where:
1218 table is a dictionnary: keys are loop variable names
1219 values are tuples with lower and upper bounds
1220 newVar is a list of loop variables not found in varList. This list has the same
1221 format as the varList list.
1223 In case the loop variable cannot be defined, the function returns (None, [])
1226 name = n2name(arr.find(
'./{*}N'))
1228 extraVarList = extraVarList
if extraVarList
is not None else []
1231 for iss, ss
in enumerate(arr.findall(
'./{*}R-LT/{*}array-R/{*}section-subscript-LT/' +
1232 '{*}section-subscript')):
1235 if ':' in alltext(ss):
1240 if varName
is not False and varName
in table:
1241 raise PYFTError((
"The variable {var} must be used for the rank #{i1} whereas " +
1242 "it is already used for rank #{i2} (for array {name})."
1243 ).format(var=varName, i1=str(iss),
1244 i2=str(list(table.keys()).index(varName)),
1255 varName =
'J' + str(j)
1256 var = self.
varList.findVar(varName, extraVarList=extraVarList + varNew)
1257 if (var
is None or var.get(
'new',
False))
and varName
not in table:
1259 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1260 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1261 'scopePath': self.
path}
1262 if varDesc
not in varNew:
1263 varNew.append(varDesc)
1265 elif (varName
is not False and
1266 self.
varList.findVar(varName, array=
False, exactScope=
True)
is None):
1268 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1269 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1270 'scopePath': self.
path}
1271 varNew.append(varDesc)
1274 table[varName] = (lower, upper)
1276 return (
None, [])
if False in table
else (table, varNew)
1325 def isVarUsed(self, varList, exactScope=False, dummyAreAlwaysUsed=False):
1327 :param varList: list of variables to test. Each item is a list or tuple of two elements.
1328 The first one describes where the variable is declared, the second one is
1329 the name of the variable. The first element is a '/'-separated path with
1330 each element having the form 'module:<name of the module>',
1331 'sub:<name of the subroutine>' or 'func:<name of the function>'
1332 :param exactScope: True to search strictly in scope
1333 :param dummyAreAlwaysUsed: Returns True if variable is a dummy argument
1334 :return: a dict whose keys are the elements of varList, and values are True when the
1335 variable is used, False otherwise
1337 If exactScope is True, the function will search for variable usage
1338 only in this scope. But this feature has a limited interest.
1340 If exactScope is False:
1341 - if scopePath is a subroutine/function in a contains section,
1342 and if the variable is not declared in this scope, usages are
1343 searched in the module/subroutine/function upper that declared
1344 the variable and in all subroutines/functions in the contains section
1345 - if scopePath is a module/subroutine/function that has a
1346 contains sections, usages are searched in all subroutines/functions
1347 in the contains section
1349 To know if a variable can be removed, you must use exactScope=False
1353 allScopes = {scope.path: scope
for scope
in self.mainScope.getScopes()}
1357 locsVar = {(scopePath, varName): [scopePath]
1358 for scopePath, varName
in varList}
1361 for scopePath, varName
in varList:
1364 var = allScopes[scopePath].varList.findVar(varName)
1365 path = scopePath.split(
'/')[0]
if var
is None else var[
'scopePath']
1370 for scPath, sc
in allScopes.items():
1371 if scPath.startswith(path +
'/')
and \
1372 scPath.split(
'/')[-1].split(
':')[0] !=
'type':
1374 if sc.varList.findVar(varName, exactScope=
True)
is None:
1376 testScopes.append(scPath)
1377 locsVar[(scopePath, varName)] = testScopes
1381 for scopePath
in list(set(item
for sublist
in locsVar.values()
for item
in sublist)):
1382 usedVar[scopePath] = []
1384 for node
in allScopes[scopePath]:
1387 if not tag(node) ==
'use-stmt':
1388 if tag(node) ==
'T-decl-stmt':
1392 nodesN = node.findall(
'.//{*}_T-spec_//{*}N') + \
1393 node.findall(
'.//{*}shape-spec//{*}N')
1395 nodesN = node.findall(
'.//{*}N')
1398 for nodeN
in nodesN:
1399 if dummyAreAlwaysUsed:
1403 usedVar[scopePath].append(n2name(nodeN).upper())
1405 parPar = allScopes[scopePath].getParent(nodeN, 2)
1408 if parPar
is None or not tag(parPar) ==
'dummy-arg-LT':
1409 usedVar[scopePath].append(n2name(nodeN).upper())
1412 for scopePath, varName
in varList:
1413 assert scopePath.split(
'/')[-1].split(
':')[0] !=
'type', \
1414 'We cannot check type component usage'
1415 result[(scopePath, varName)] = any(varName.upper()
in usedVar[scopePath]
1416 for scopePath
in locsVar[(scopePath, varName)])
1422 def addArgInTree(self, varName, declStmt, pos, stopScopes, moduleVarList=None,
1424 parserOptions=None, wrapH=False):
1426 Adds an argument to the routine and propagates it upward until we encounter a scope
1427 where the variable exists or a scope in stopScopes
1428 :param varName: variable name
1429 :param declStmt: declarative statment (will be used by addVar)
1430 :param pos: position of the variable in the list of dummy argument
1431 :param stopScopes: list of scopes to reach
1432 :param moduleVarList: list of module variable specification to insert in the xml code
1433 a module variable specification is a list of two elements:
1435 - variable name or or list of variable names
1436 or None to add a USE statement without the ONLY attribute
1437 use moduleVarList to not add module variables
1438 :param otherNames: None or list of other variable names that can be used
1439 These variables are used first
1440 :param parserOptions, wrapH: see the PYFT class
1442 Argument is inserted only on paths leading to one of scopes listed in stopScopes
1444 def insertInArgList(varName, varNameToUse, pos, callFuncStmt):
1446 Insert varName in the list of arguments to the subroutine or function call
1447 :param varName: name of the dummy argument
1448 :param varNameToUse: name of the variable
1449 :param pos: inclusion position
1450 :param callFuncStmt: call statement or function call
1452 argList = callFuncStmt.find(
'./{*}R-LT/{*}parens-R/{*}element-LT')
1453 if argList
is not None:
1454 container = createElem(
'element')
1456 argList = callFuncStmt.find(
'./{*}arg-spec')
1457 container = createElem(
'arg')
1460 callFuncStmt.find(
'./{*}procedure-designator').tail =
'('
1461 argList = createElem(
'arg-spec', tail=
')')
1462 callFuncStmt.append(argList)
1463 item = createExprPart(varNameToUse)
1464 previous = pos - 1
if pos >= 0
else len(argList) + pos
1465 while previous >= 0
and tag(argList[previous])
in (
'C',
'cnt'):
1467 following = pos
if pos > 0
else len(argList) + pos + 1
1468 while following <= len(argList) - 1
and tag(argList[following])
in (
'C',
'cnt'):
1470 if (previous >= 0
and argList[previous].find(
'./{*}arg-N/{*}k')
is not None)
or \
1471 (following <= len(argList) - 1
and
1472 argList[following].find(
'./{*}arg-N/{*}k')
is not None)
or \
1473 following == len(argList):
1479 k = createElem(
'k', text=varName)
1480 argN = createElem(
'arg-N', tail=
'=')
1482 argN.set(
'n', varName)
1483 container.append(argN)
1484 container.append(item)
1485 self.insertInList(pos, container, argList)
1487 if self.
path in stopScopes
or self.tree.isUnderStopScopes(self.
path, stopScopes):
1490 var = self.
varList.findVar(varName, exactScope=
True)
1491 if otherNames
is not None:
1492 vOther = [self.
varList.findVar(v, exactScope=
True)
for v
in otherNames]
1493 vOther = [v
for v
in vOther
if v
is not None]
1499 self.
addVar([[self.
path, varName, declStmt, pos]])
1500 if moduleVarList
is not None:
1503 for (moduleName, moduleVarNames)
in moduleVarList])
1506 if len(self.
path.split(
'/')) == 1:
1507 filename, scopePathInterface = self.tree.findScopeInterface(self.
path)
1508 if filename
is not None:
1511 if self.getFileName() == os.path.normpath(filename):
1513 xml = self.mainScope
1517 filename, parserOptions, wrapH, tree=self.tree,
1518 clsPYFT=self._mainScope.__class__)
1520 scopeInterface = xml.getScopeNode(scopePathInterface)
1521 varInterface = scopeInterface.varList.findVar(varName, exactScope=
True)
1522 if varInterface
is None:
1523 scopeInterface.addVar([[scopePathInterface, varName,
1525 if moduleVarList
is not None:
1528 [(scopePathInterface, moduleName, moduleVarNames)
1529 for (moduleName, moduleVarNames)
in moduleVarList])
1536 if var
is None and self.
path not in stopScopes:
1539 for scopePathUp
in self.tree.calledByScope(self.
path):
1540 if scopePathUp
in stopScopes
or self.tree.isUnderStopScopes(scopePathUp,
1545 for filename
in self.tree.scopeToFiles(scopePathUp):
1548 if self.getFileName() == os.path.normpath(filename):
1550 xml = self.mainScope
1554 filename, parserOptions, wrapH,
1556 clsPYFT=self._mainScope.__class__)
1558 scopeUp = xml.getScopeNode(scopePathUp)
1560 scopeUp.addArgInTree(
1561 varName, declStmt, pos,
1562 stopScopes, moduleVarList, otherNames,
1563 parserOptions=parserOptions,
1566 name = self.
path.split(
'/')[-1].split(
':')[1].upper()
1568 varNameToUse = varName
1569 if otherNames
is not None:
1570 vOther = [scopeUp.varList.findVar(v, exactScope=
True)
1571 for v
in otherNames]
1572 vOther = [v
for v
in vOther
if v
is not None]
1574 varNameToUse = vOther[-1][
'n']
1575 if self.
path.split(
'/')[-1].split(
':')[0] ==
'sub':
1577 for callStmt
in scopeUp.findall(
'.//{*}call-stmt'):
1578 callName = n2name(callStmt.find(
1579 './{*}procedure-designator/{*}named-E/{*}N')).upper()
1580 if callName == name:
1581 insertInArgList(varName, varNameToUse, pos, callStmt)
1585 for funcCall
in scopeUp.findall(
1586 './/{*}named-E/{*}R-LT/{*}parens-R/' +
1587 '{*}element-LT/../../..'):
1588 funcName = n2name(funcCall.find(
'./{*}N')).upper()
1589 if funcName == name:
1590 insertInArgList(varName, varNameToUse, pos, funcCall)
1601 for interface
in self.findall(
'.//{*}interface-construct/{*}' +
1602 'program-unit/{*}subroutine-stmt/' +
1603 '{*}subroutine-N/{*}N/../../../'):
1604 if n2name(interface.find(
'./{*}subroutine-stmt/' +
1605 '{*}subroutine-N/' +
1606 '{*}N')).upper() == name:
1608 raise PYFTError(
'This case is not yet implemented')