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 not (var[
'arg']
or var[
'allocatable']
or
919 var[
'pointer']
or var[
'result'])]:
922 if var[
'init']
is None:
924 varListToTransform.append(var)
928 orderedVarListToTransform = []
929 while len(varListToTransform) > 0:
931 for var
in varListToTransform[:]:
933 listN = [x
for dim
in var[
'asx']
for x
in dim
if x
is not None]
934 listN = [n2name(nodeN).upper()
for asx
in listN
935 for nodeN
in asx.findall(
'.//{*}N/{*}n/..')]
936 if len(set(listN).intersection([v[
'n'].upper()
937 for v
in varListToTransform])) == 0:
939 varListToTransform.remove(var)
940 orderedVarListToTransform.append(var)
943 raise PYFTError(
'It seems that there is a circular reference in ' +
944 'the declaration statements')
946 for var
in orderedVarListToTransform[::-1]:
949 templ = copy.deepcopy(templates)
950 for templPart
in templ:
951 if '{doubledotshape}' in templ[templPart]:
952 templ[templPart] = templ[templPart].replace(
953 '{doubledotshape}',
','.join([
':'] * len(var[
'as'])))
954 if '{shape}' in templ[templPart]:
956 for i
in range(len(var[
'as'])):
957 if var[
'as'][i][0]
is None:
958 result.append(var[
'as'][i][1])
960 result.append(var[
'as'][i][0] +
':' + var[
'as'][i][1])
961 templ[templPart] = templ[templPart].replace(
'{shape}',
', '.join(result))
962 if '{name}' in templ[templPart]:
963 templ[templPart] = templ[templPart].replace(
'{name}', var[
'n'])
964 if '{type}' in templ[templPart]:
965 templ[templPart] = templ[templPart].replace(
'{type}', var[
't'])
966 if '{lowUpList}' in templ[templPart]:
968 for i
in range(len(var[
'as'])):
969 if var[
'as'][i][0]
is None:
970 result.extend([1, var[
'as'][i][1]])
972 result.extend([var[
'as'][i][0], var[
'as'][i][1]])
973 templ[templPart] = templ[templPart].replace(
974 '{lowUpList}',
', '.join([str(r)
for r
in result]))
977 separator =
"!ABCDEFGHIJKLMNOPQRSTUVWabcdefghijklmnopqrstuvwxyz0123456789"
979 for node
in createExpr(templ[
'decl'] +
'\n' + separator +
'\n' +
980 templ[
'start'] +
'\n' + separator +
'\n' +
982 templPart = list(templ.keys())[part]
983 if not isinstance(templ[templPart], list):
984 templ[templPart] = []
985 if tag(node) ==
'C' and node.text == separator:
988 templ[templPart].append(node)
994 for decl
in scope.findall(
'./{*}T-decl-stmt'):
995 index = list(scope).index(decl)
996 if var[
'n']
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
998 scope.removeVar([(scope.path, var[
'n'])], simplify=
False)
999 for nnn
in templ[
'decl'][::-1]:
1000 scope.insert(index, nnn)
1003 for nnn
in templ[
'start'][::-1]:
1004 scope.insertStatement(nnn,
True)
1005 for nnn
in templ[
'end'][::-1]:
1006 scope.insertStatement(nnn,
False)
1108 Transform a array-R into a parens-R node by replacing slices by variables
1109 In 'A(:)', the ':' is in a array-R node whereas in 'A(JL)', 'JL' is in a parens-R node.
1110 Both the array-R and the parens-R nodes are inside a R-LT node
1111 :param namedE: a named-E node
1112 :param table: dictionnary returned by the decode function
1113 :param varList: None or a VarList object in which varaibles are searched for
1136 nodeRLT = namedE.find(
'./{*}R-LT')
1137 arrayR = nodeRLT.find(
'./{*}array-R')
1138 if arrayR
is not None:
1139 index = list(nodeRLT).index(arrayR)
1140 parensR = createElem(
'parens-R', text=
'(', tail=
')')
1141 elementLT = createElem(
'element-LT')
1142 parensR.append(elementLT)
1144 for ss
in nodeRLT[index].findall(
'./{*}section-subscript-LT/{*}section-subscript'):
1145 element = createElem(
'element', tail=
', ')
1146 elementLT.append(element)
1147 if ':' in alltext(ss):
1149 varName = list(table.keys())[ivar]
1150 lower = ss.find(
'./{*}lower-bound')
1151 upper = ss.find(
'./{*}upper-bound')
1152 if lower
is not None:
1153 lower = alltext(lower)
1154 if upper
is not None:
1155 upper = alltext(upper)
1156 if lower
is not None and ss.text
is not None and ':' in ss.text:
1160 if lower
is None and upper
is None:
1164 element.append(createExprPart(varName))
1171 lower = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1172 array=
True)[
'as'][ivar][0]
1177 upper = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1178 array=
True)[
'as'][ivar][1]
1184 newlower = simplifyExpr(lower, add=varName, sub=table[varName][0])
1185 newupper = simplifyExpr(upper, add=varName, sub=table[varName][1])
1186 if newlower != newupper:
1187 raise PYFTError((
"Don't know how to do with an array declared with " +
1188 "'{la}:{ua}' and a loop from '{ll}' to '{ul}'"
1189 ).format(la=lower, ua=upper,
1190 ll=table[varName][0],
1191 ul=table[varName][1]))
1192 element.append(createExprPart(newlower))
1194 element.append(ss.find(
'./{*}lower-bound'))
1196 nodeRLT.remove(nodeRLT[index])
1197 nodeRLT.insert(index, parensR)
1202 Find bounds and loop variable given an array
1203 :param arr: array node (named-E node with a array-R child)
1204 :param loopVar: None to create new variable for each added DO loop
1205 or a function that return the name of the variable to use for the loop
1207 This function returns a string (name of the variable), or True to create
1208 a new variable, or False to not transform this statement
1209 The functions takes as arguments:
1210 - lower and upper bounds as defined in the declaration statement
1211 - lower and upper bounds as given in the statement
1214 :param extraVarList: None or list of variables (such as those contained in a VarList object)
1215 defined but not yet available in the self.varList object.
1216 :return: the tuple (table, newVar) where:
1217 table is a dictionnary: keys are loop variable names
1218 values are tuples with lower and upper bounds
1219 newVar is a list of loop variables not found in varList. This list has the same
1220 format as the varList list.
1222 In case the loop variable cannot be defined, the function returns (None, [])
1225 name = n2name(arr.find(
'./{*}N'))
1227 extraVarList = extraVarList
if extraVarList
is not None else []
1230 for iss, ss
in enumerate(arr.findall(
'./{*}R-LT/{*}array-R/{*}section-subscript-LT/' +
1231 '{*}section-subscript')):
1234 if ':' in alltext(ss):
1239 if varName
is not False and varName
in table:
1240 raise PYFTError((
"The variable {var} must be used for the rank #{i1} whereas " +
1241 "it is already used for rank #{i2} (for array {name})."
1242 ).format(var=varName, i1=str(iss),
1243 i2=str(list(table.keys()).index(varName)),
1254 varName =
'J' + str(j)
1255 var = self.
varList.findVar(varName, extraVarList=extraVarList + varNew)
1256 if (var
is None or var.get(
'new',
False))
and varName
not in table:
1258 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1259 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1260 'scopePath': self.
path}
1261 if varDesc
not in varNew:
1262 varNew.append(varDesc)
1264 elif (varName
is not False and
1265 self.
varList.findVar(varName, array=
False, exactScope=
True)
is None):
1267 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1268 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1269 'scopePath': self.
path}
1270 varNew.append(varDesc)
1273 table[varName] = (lower, upper)
1275 return (
None, [])
if False in table
else (table, varNew)
1324 def isVarUsed(self, varList, exactScope=False, dummyAreAlwaysUsed=False):
1326 :param varList: list of variables to test. Each item is a list or tuple of two elements.
1327 The first one describes where the variable is declared, the second one is
1328 the name of the variable. The first element is a '/'-separated path with
1329 each element having the form 'module:<name of the module>',
1330 'sub:<name of the subroutine>' or 'func:<name of the function>'
1331 :param exactScope: True to search strictly in scope
1332 :param dummyAreAlwaysUsed: Returns True if variable is a dummy argument
1333 :return: a dict whose keys are the elements of varList, and values are True when the
1334 variable is used, False otherwise
1336 If exactScope is True, the function will search for variable usage
1337 only in this scope. But this feature has a limited interest.
1339 If exactScope is False:
1340 - if scopePath is a subroutine/function in a contains section,
1341 and if the variable is not declared in this scope, usages are
1342 searched in the module/subroutine/function upper that declared
1343 the variable and in all subroutines/functions in the contains section
1344 - if scopePath is a module/subroutine/function that has a
1345 contains sections, usages are searched in all subroutines/functions
1346 in the contains section
1348 To know if a variable can be removed, you must use exactScope=False
1352 allScopes = {scope.path: scope
for scope
in self.mainScope.getScopes()}
1356 locsVar = {(scopePath, varName): [scopePath]
1357 for scopePath, varName
in varList}
1360 for scopePath, varName
in varList:
1363 var = allScopes[scopePath].varList.findVar(varName)
1364 path = scopePath.split(
'/')[0]
if var
is None else var[
'scopePath']
1369 for scPath, sc
in allScopes.items():
1370 if scPath.startswith(path +
'/')
and \
1371 scPath.split(
'/')[-1].split(
':')[0] !=
'type':
1373 if sc.varList.findVar(varName, exactScope=
True)
is None:
1375 testScopes.append(scPath)
1376 locsVar[(scopePath, varName)] = testScopes
1380 for scopePath
in list(set(item
for sublist
in locsVar.values()
for item
in sublist)):
1381 usedVar[scopePath] = []
1383 for node
in allScopes[scopePath]:
1386 if not tag(node) ==
'use-stmt':
1387 if tag(node) ==
'T-decl-stmt':
1391 nodesN = node.findall(
'.//{*}_T-spec_//{*}N') + \
1392 node.findall(
'.//{*}shape-spec//{*}N')
1394 nodesN = node.findall(
'.//{*}N')
1397 for nodeN
in nodesN:
1398 if dummyAreAlwaysUsed:
1402 usedVar[scopePath].append(n2name(nodeN).upper())
1404 parPar = allScopes[scopePath].getParent(nodeN, 2)
1407 if parPar
is None or not tag(parPar) ==
'dummy-arg-LT':
1408 usedVar[scopePath].append(n2name(nodeN).upper())
1411 for scopePath, varName
in varList:
1412 assert scopePath.split(
'/')[-1].split(
':')[0] !=
'type', \
1413 'We cannot check type component usage'
1414 result[(scopePath, varName)] = any(varName.upper()
in usedVar[scopePath]
1415 for scopePath
in locsVar[(scopePath, varName)])
1421 def addArgInTree(self, varName, declStmt, pos, stopScopes, moduleVarList=None,
1423 parserOptions=None, wrapH=False):
1425 Adds an argument to the routine and propagates it upward until we encounter a scope
1426 where the variable exists or a scope in stopScopes
1427 :param varName: variable name
1428 :param declStmt: declarative statment (will be used by addVar)
1429 :param pos: position of the variable in the list of dummy argument
1430 :param stopScopes: list of scopes to reach
1431 :param moduleVarList: list of module variable specification to insert in the xml code
1432 a module variable specification is a list of two elements:
1434 - variable name or or list of variable names
1435 or None to add a USE statement without the ONLY attribute
1436 use moduleVarList to not add module variables
1437 :param otherNames: None or list of other variable names that can be used
1438 These variables are used first
1439 :param parserOptions, wrapH: see the PYFT class
1441 Argument is inserted only on paths leading to one of scopes listed in stopScopes
1443 def insertInArgList(varName, varNameToUse, pos, callFuncStmt):
1445 Insert varName in the list of arguments to the subroutine or function call
1446 :param varName: name of the dummy argument
1447 :param varNameToUse: name of the variable
1448 :param pos: inclusion position
1449 :param callFuncStmt: call statement or function call
1451 argList = callFuncStmt.find(
'./{*}R-LT/{*}parens-R/{*}element-LT')
1452 if argList
is not None:
1453 container = createElem(
'element')
1455 argList = callFuncStmt.find(
'./{*}arg-spec')
1456 container = createElem(
'arg')
1459 callFuncStmt.find(
'./{*}procedure-designator').tail =
'('
1460 argList = createElem(
'arg-spec', tail=
')')
1461 callFuncStmt.append(argList)
1462 item = createExprPart(varNameToUse)
1463 previous = pos - 1
if pos >= 0
else len(argList) + pos
1464 while previous >= 0
and tag(argList[previous])
in (
'C',
'cnt'):
1466 following = pos
if pos > 0
else len(argList) + pos + 1
1467 while following <= len(argList) - 1
and tag(argList[following])
in (
'C',
'cnt'):
1469 if (previous >= 0
and argList[previous].find(
'./{*}arg-N/{*}k')
is not None)
or \
1470 (following <= len(argList) - 1
and
1471 argList[following].find(
'./{*}arg-N/{*}k')
is not None)
or \
1472 following == len(argList):
1478 k = createElem(
'k', text=varName)
1479 argN = createElem(
'arg-N', tail=
'=')
1481 argN.set(
'n', varName)
1482 container.append(argN)
1483 container.append(item)
1484 self.insertInList(pos, container, argList)
1486 if self.
path in stopScopes
or self.tree.isUnderStopScopes(self.
path, stopScopes):
1489 var = self.
varList.findVar(varName, exactScope=
True)
1490 if otherNames
is not None:
1491 vOther = [self.
varList.findVar(v, exactScope=
True)
for v
in otherNames]
1492 vOther = [v
for v
in vOther
if v
is not None]
1498 self.
addVar([[self.
path, varName, declStmt, pos]])
1499 if moduleVarList
is not None:
1502 for (moduleName, moduleVarNames)
in moduleVarList])
1505 if len(self.
path.split(
'/')) == 1:
1506 filename, scopePathInterface = self.tree.findScopeInterface(self.
path)
1507 if filename
is not None:
1510 if self.getFileName() == os.path.normpath(filename):
1512 xml = self.mainScope
1516 filename, parserOptions, wrapH, tree=self.tree,
1517 clsPYFT=self._mainScope.__class__)
1519 scopeInterface = xml.getScopeNode(scopePathInterface)
1520 varInterface = scopeInterface.varList.findVar(varName, exactScope=
True)
1521 if varInterface
is None:
1522 scopeInterface.addVar([[scopePathInterface, varName,
1524 if moduleVarList
is not None:
1527 [(scopePathInterface, moduleName, moduleVarNames)
1528 for (moduleName, moduleVarNames)
in moduleVarList])
1535 if var
is None and self.
path not in stopScopes:
1538 for scopePathUp
in self.tree.calledByScope(self.
path):
1539 if scopePathUp
in stopScopes
or self.tree.isUnderStopScopes(scopePathUp,
1544 for filename
in self.tree.scopeToFiles(scopePathUp):
1547 if self.getFileName() == os.path.normpath(filename):
1549 xml = self.mainScope
1553 filename, parserOptions, wrapH,
1555 clsPYFT=self._mainScope.__class__)
1557 scopeUp = xml.getScopeNode(scopePathUp)
1559 scopeUp.addArgInTree(
1560 varName, declStmt, pos,
1561 stopScopes, moduleVarList, otherNames,
1562 parserOptions=parserOptions,
1565 name = self.
path.split(
'/')[-1].split(
':')[1].upper()
1567 varNameToUse = varName
1568 if otherNames
is not None:
1569 vOther = [scopeUp.varList.findVar(v, exactScope=
True)
1570 for v
in otherNames]
1571 vOther = [v
for v
in vOther
if v
is not None]
1573 varNameToUse = vOther[-1][
'n']
1574 if self.
path.split(
'/')[-1].split(
':')[0] ==
'sub':
1576 for callStmt
in scopeUp.findall(
'.//{*}call-stmt'):
1577 callName = n2name(callStmt.find(
1578 './{*}procedure-designator/{*}named-E/{*}N')).upper()
1579 if callName == name:
1580 insertInArgList(varName, varNameToUse, pos, callStmt)
1584 for funcCall
in scopeUp.findall(
1585 './/{*}named-E/{*}R-LT/{*}parens-R/' +
1586 '{*}element-LT/../../..'):
1587 funcName = n2name(funcCall.find(
'./{*}N')).upper()
1588 if funcName == name:
1589 insertInArgList(varName, varNameToUse, pos, funcCall)
1600 for interface
in self.findall(
'.//{*}interface-construct/{*}' +
1601 'program-unit/{*}subroutine-stmt/' +
1602 '{*}subroutine-N/{*}N/../../../'):
1603 if n2name(interface.find(
'./{*}subroutine-stmt/' +
1604 '{*}subroutine-N/' +
1605 '{*}N')).upper() == name:
1607 raise PYFTError(
'This case is not yet implemented')