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 None:
921 varListToTransform.append(var)
925 orderedVarListToTransform = []
926 while len(varListToTransform) > 0:
928 for var
in varListToTransform[:]:
930 listN = [x
for dim
in var[
'asx']
for x
in dim
if x
is not None]
931 listN = [n2name(nodeN).upper()
for asx
in listN
932 for nodeN
in asx.findall(
'.//{*}N/{*}n/..')]
933 if len(set(listN).intersection([v[
'n'].upper()
934 for v
in varListToTransform])) == 0:
936 varListToTransform.remove(var)
937 orderedVarListToTransform.append(var)
940 raise PYFTError(
'It seems that there is a circular reference in ' +
941 'the declaration statements')
943 for var
in orderedVarListToTransform[::-1]:
946 templ = copy.deepcopy(templates)
947 for templPart
in templ:
948 if '{doubledotshape}' in templ[templPart]:
949 templ[templPart] = templ[templPart].replace(
950 '{doubledotshape}',
','.join([
':'] * len(var[
'as'])))
951 if '{shape}' in templ[templPart]:
953 for i
in range(len(var[
'as'])):
954 if var[
'as'][i][0]
is None:
955 result.append(var[
'as'][i][1])
957 result.append(var[
'as'][i][0] +
':' + var[
'as'][i][1])
958 templ[templPart] = templ[templPart].replace(
'{shape}',
', '.join(result))
959 if '{name}' in templ[templPart]:
960 templ[templPart] = templ[templPart].replace(
'{name}', var[
'n'])
961 if '{type}' in templ[templPart]:
962 templ[templPart] = templ[templPart].replace(
'{type}', var[
't'])
963 if '{lowUpList}' in templ[templPart]:
965 for i
in range(len(var[
'as'])):
966 if var[
'as'][i][0]
is None:
967 result.extend([1, var[
'as'][i][1]])
969 result.extend([var[
'as'][i][0], var[
'as'][i][1]])
970 templ[templPart] = templ[templPart].replace(
971 '{lowUpList}',
', '.join([str(r)
for r
in result]))
974 separator =
"!ABCDEFGHIJKLMNOPQRSTUVWabcdefghijklmnopqrstuvwxyz0123456789"
976 for node
in createExpr(templ[
'decl'] +
'\n' + separator +
'\n' +
977 templ[
'start'] +
'\n' + separator +
'\n' +
979 templPart = list(templ.keys())[part]
980 if not isinstance(templ[templPart], list):
981 templ[templPart] = []
982 if tag(node) ==
'C' and node.text == separator:
985 templ[templPart].append(node)
991 for decl
in scope.findall(
'./{*}T-decl-stmt'):
992 index = list(scope).index(decl)
993 if var[
'n']
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
995 scope.removeVar([(scope.path, var[
'n'])], simplify=
False)
996 for nnn
in templ[
'decl'][::-1]:
997 scope.insert(index, nnn)
1000 for nnn
in templ[
'start'][::-1]:
1001 scope.insertStatement(nnn,
True)
1002 for nnn
in templ[
'end'][::-1]:
1003 scope.insertStatement(nnn,
False)
1102 Transform a array-R into a parens-R node by replacing slices by variables
1103 In 'A(:)', the ':' is in a array-R node whereas in 'A(JL)', 'JL' is in a parens-R node.
1104 Both the array-R and the parens-R nodes are inside a R-LT node
1105 :param namedE: a named-E node
1106 :param table: dictionnary returned by the decode function
1107 :param varList: None or a VarList object in which varaibles are searched for
1130 nodeRLT = namedE.find(
'./{*}R-LT')
1131 arrayR = nodeRLT.find(
'./{*}array-R')
1132 if arrayR
is not None:
1133 index = list(nodeRLT).index(arrayR)
1134 parensR = createElem(
'parens-R', text=
'(', tail=
')')
1135 elementLT = createElem(
'element-LT')
1136 parensR.append(elementLT)
1138 for ss
in nodeRLT[index].findall(
'./{*}section-subscript-LT/{*}section-subscript'):
1139 element = createElem(
'element', tail=
', ')
1140 elementLT.append(element)
1141 if ':' in alltext(ss):
1143 varName = list(table.keys())[ivar]
1144 lower = ss.find(
'./{*}lower-bound')
1145 upper = ss.find(
'./{*}upper-bound')
1146 if lower
is not None:
1147 lower = alltext(lower)
1148 if upper
is not None:
1149 upper = alltext(upper)
1150 if lower
is not None and ss.text
is not None and ':' in ss.text:
1154 if lower
is None and upper
is None:
1158 element.append(createExprPart(varName))
1165 lower = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1166 array=
True)[
'as'][ivar][0]
1171 upper = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1172 array=
True)[
'as'][ivar][1]
1178 newlower = simplifyExpr(lower, add=varName, sub=table[varName][0])
1179 newupper = simplifyExpr(upper, add=varName, sub=table[varName][1])
1180 if newlower != newupper:
1181 raise PYFTError((
"Don't know how to do with an array declared with " +
1182 "'{la}:{ua}' and a loop from '{ll}' to '{ul}'"
1183 ).format(la=lower, ua=upper,
1184 ll=table[varName][0],
1185 ul=table[varName][1]))
1186 element.append(createExprPart(newlower))
1188 element.append(ss.find(
'./{*}lower-bound'))
1190 nodeRLT.remove(nodeRLT[index])
1191 nodeRLT.insert(index, parensR)
1196 Find bounds and loop variable given an array
1197 :param arr: array node (named-E node with a array-R child)
1198 :param loopVar: None to create new variable for each added DO loop
1199 or a function that return the name of the variable to use for the loop
1201 This function returns a string (name of the variable), or True to create
1202 a new variable, or False to not transform this statement
1203 The functions takes as arguments:
1204 - lower and upper bounds as defined in the declaration statement
1205 - lower and upper bounds as given in the statement
1208 :param extraVarList: None or list of variables (such as those contained in a VarList object)
1209 defined but not yet available in the self.varList object.
1210 :return: the tuple (table, newVar) where:
1211 table is a dictionnary: keys are loop variable names
1212 values are tuples with lower and upper bounds
1213 newVar is a list of loop variables not found in varList. This list has the same
1214 format as the varList list.
1216 In case the loop variable cannot be defined, the function returns (None, [])
1219 name = n2name(arr.find(
'./{*}N'))
1221 extraVarList = extraVarList
if extraVarList
is not None else []
1224 for iss, ss
in enumerate(arr.findall(
'./{*}R-LT/{*}array-R/{*}section-subscript-LT/' +
1225 '{*}section-subscript')):
1228 if ':' in alltext(ss):
1233 if varName
is not False and varName
in table:
1234 raise PYFTError((
"The variable {var} must be used for the rank #{i1} whereas " +
1235 "it is already used for rank #{i2} (for array {name})."
1236 ).format(var=varName, i1=str(iss),
1237 i2=str(list(table.keys()).index(varName)),
1248 varName =
'J' + str(j)
1249 var = self.
varList.findVar(varName, extraVarList=extraVarList + varNew)
1250 if (var
is None or var.get(
'new',
False))
and varName
not in table:
1252 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1253 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1254 'scopePath': self.
path}
1255 if varDesc
not in varNew:
1256 varNew.append(varDesc)
1258 elif (varName
is not False and
1259 self.
varList.findVar(varName, array=
False, exactScope=
True)
is None):
1261 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1262 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1263 'scopePath': self.
path}
1264 varNew.append(varDesc)
1267 table[varName] = (lower, upper)
1269 return (
None, [])
if False in table
else (table, varNew)
1318 def isVarUsed(self, varList, exactScope=False, dummyAreAlwaysUsed=False):
1320 :param varList: list of variables to test. Each item is a list or tuple of two elements.
1321 The first one describes where the variable is declared, the second one is
1322 the name of the variable. The first element is a '/'-separated path with
1323 each element having the form 'module:<name of the module>',
1324 'sub:<name of the subroutine>' or 'func:<name of the function>'
1325 :param exactScope: True to search strictly in scope
1326 :param dummyAreAlwaysUsed: Returns True if variable is a dummy argument
1327 :return: a dict whose keys are the elements of varList, and values are True when the
1328 variable is used, False otherwise
1330 If exactScope is True, the function will search for variable usage
1331 only in this scope. But this feature has a limited interest.
1333 If exactScope is False:
1334 - if scopePath is a subroutine/function in a contains section,
1335 and if the variable is not declared in this scope, usages are
1336 searched in the module/subroutine/function upper that declared
1337 the variable and in all subroutines/functions in the contains section
1338 - if scopePath is a module/subroutine/function that has a
1339 contains sections, usages are searched in all subroutines/functions
1340 in the contains section
1342 To know if a variable can be removed, you must use exactScope=False
1346 allScopes = {scope.path: scope
for scope
in self.mainScope.getScopes()}
1350 locsVar = {(scopePath, varName): [scopePath]
1351 for scopePath, varName
in varList}
1354 for scopePath, varName
in varList:
1357 var = allScopes[scopePath].varList.findVar(varName)
1358 path = scopePath.split(
'/')[0]
if var
is None else var[
'scopePath']
1363 for scPath, sc
in allScopes.items():
1364 if scPath.startswith(path +
'/')
and \
1365 scPath.split(
'/')[-1].split(
':')[0] !=
'type':
1367 if sc.varList.findVar(varName, exactScope=
True)
is None:
1369 testScopes.append(scPath)
1370 locsVar[(scopePath, varName)] = testScopes
1374 for scopePath
in list(set(item
for sublist
in locsVar.values()
for item
in sublist)):
1375 usedVar[scopePath] = []
1377 for node
in allScopes[scopePath]:
1380 if not tag(node) ==
'use-stmt':
1381 if tag(node) ==
'T-decl-stmt':
1385 nodesN = node.findall(
'.//{*}_T-spec_//{*}N') + \
1386 node.findall(
'.//{*}shape-spec//{*}N')
1388 nodesN = node.findall(
'.//{*}N')
1391 for nodeN
in nodesN:
1392 if dummyAreAlwaysUsed:
1396 usedVar[scopePath].append(n2name(nodeN).upper())
1398 parPar = allScopes[scopePath].getParent(nodeN, 2)
1401 if parPar
is None or not tag(parPar) ==
'dummy-arg-LT':
1402 usedVar[scopePath].append(n2name(nodeN).upper())
1405 for scopePath, varName
in varList:
1406 assert scopePath.split(
'/')[-1].split(
':')[0] !=
'type', \
1407 'We cannot check type component usage'
1408 result[(scopePath, varName)] = any(varName.upper()
in usedVar[scopePath]
1409 for scopePath
in locsVar[(scopePath, varName)])
1415 def addArgInTree(self, varName, declStmt, pos, stopScopes, moduleVarList=None,
1417 parserOptions=None, wrapH=False):
1419 Adds an argument to the routine and propagates it upward until we encounter a scope
1420 where the variable exists or a scope in stopScopes
1421 :param varName: variable name
1422 :param declStmt: declarative statment (will be used by addVar)
1423 :param pos: position of the variable in the list of dummy argument
1424 :param stopScopes: list of scopes to reach
1425 :param moduleVarList: list of module variable specification to insert in the xml code
1426 a module variable specification is a list of two elements:
1428 - variable name or or list of variable names
1429 or None to add a USE statement without the ONLY attribute
1430 use moduleVarList to not add module variables
1431 :param otherNames: None or list of other variable names that can be used
1432 These variables are used first
1433 :param parserOptions, wrapH: see the PYFT class
1435 Argument is inserted only on paths leading to one of scopes listed in stopScopes
1437 def insertInArgList(varName, varNameToUse, pos, callFuncStmt):
1439 Insert varName in the list of arguments to the subroutine or function call
1440 :param varName: name of the dummy argument
1441 :param varNameToUse: name of the variable
1442 :param pos: inclusion position
1443 :param callFuncStmt: call statement or function call
1445 argList = callFuncStmt.find(
'./{*}R-LT/{*}parens-R/{*}element-LT')
1446 if argList
is not None:
1447 container = createElem(
'element')
1449 argList = callFuncStmt.find(
'./{*}arg-spec')
1450 container = createElem(
'arg')
1453 callFuncStmt.find(
'./{*}procedure-designator').tail =
'('
1454 argList = createElem(
'arg-spec', tail=
')')
1455 callFuncStmt.append(argList)
1456 item = createExprPart(varNameToUse)
1457 previous = pos - 1
if pos >= 0
else len(argList) + pos
1458 while previous >= 0
and tag(argList[previous])
in (
'C',
'cnt'):
1460 following = pos
if pos > 0
else len(argList) + pos + 1
1461 while following <= len(argList) - 1
and tag(argList[following])
in (
'C',
'cnt'):
1463 if (previous >= 0
and argList[previous].find(
'./{*}arg-N/{*}k')
is not None)
or \
1464 (following <= len(argList) - 1
and
1465 argList[following].find(
'./{*}arg-N/{*}k')
is not None)
or \
1466 following == len(argList):
1472 k = createElem(
'k', text=varName)
1473 argN = createElem(
'arg-N', tail=
'=')
1475 argN.set(
'n', varName)
1476 container.append(argN)
1477 container.append(item)
1478 self.insertInList(pos, container, argList)
1480 if self.
path in stopScopes
or self.tree.isUnderStopScopes(self.
path, stopScopes):
1483 var = self.
varList.findVar(varName, exactScope=
True)
1484 if otherNames
is not None:
1485 vOther = [self.
varList.findVar(v, exactScope=
True)
for v
in otherNames]
1486 vOther = [v
for v
in vOther
if v
is not None]
1492 self.
addVar([[self.
path, varName, declStmt, pos]])
1493 if moduleVarList
is not None:
1496 for (moduleName, moduleVarNames)
in moduleVarList])
1499 if len(self.
path.split(
'/')) == 1:
1500 filename, scopePathInterface = self.tree.findScopeInterface(self.
path)
1501 if filename
is not None:
1504 if self.getFileName() == os.path.normpath(filename):
1506 xml = self.mainScope
1510 filename, parserOptions, wrapH, tree=self.tree,
1511 clsPYFT=self._mainScope.__class__)
1513 scopeInterface = xml.getScopeNode(scopePathInterface)
1514 varInterface = scopeInterface.varList.findVar(varName, exactScope=
True)
1515 if varInterface
is None:
1516 scopeInterface.addVar([[scopePathInterface, varName,
1518 if moduleVarList
is not None:
1521 [(scopePathInterface, moduleName, moduleVarNames)
1522 for (moduleName, moduleVarNames)
in moduleVarList])
1529 if var
is None and self.
path not in stopScopes:
1532 for scopePathUp
in self.tree.calledByScope(self.
path):
1533 if scopePathUp
in stopScopes
or self.tree.isUnderStopScopes(scopePathUp,
1538 for filename
in self.tree.scopeToFiles(scopePathUp):
1541 if self.getFileName() == os.path.normpath(filename):
1543 xml = self.mainScope
1547 filename, parserOptions, wrapH,
1549 clsPYFT=self._mainScope.__class__)
1551 scopeUp = xml.getScopeNode(scopePathUp)
1553 scopeUp.addArgInTree(
1554 varName, declStmt, pos,
1555 stopScopes, moduleVarList, otherNames,
1556 parserOptions=parserOptions,
1559 name = self.
path.split(
'/')[-1].split(
':')[1].upper()
1561 varNameToUse = varName
1562 if otherNames
is not None:
1563 vOther = [scopeUp.varList.findVar(v, exactScope=
True)
1564 for v
in otherNames]
1565 vOther = [v
for v
in vOther
if v
is not None]
1567 varNameToUse = vOther[-1][
'n']
1568 if self.
path.split(
'/')[-1].split(
':')[0] ==
'sub':
1570 for callStmt
in scopeUp.findall(
'.//{*}call-stmt'):
1571 callName = n2name(callStmt.find(
1572 './{*}procedure-designator/{*}named-E/{*}N')).upper()
1573 if callName == name:
1574 insertInArgList(varName, varNameToUse, pos, callStmt)
1578 for funcCall
in scopeUp.findall(
1579 './/{*}named-E/{*}R-LT/{*}parens-R/' +
1580 '{*}element-LT/../../..'):
1581 funcName = n2name(funcCall.find(
'./{*}N')).upper()
1582 if funcName == name:
1583 insertInArgList(varName, varNameToUse, pos, funcCall)
1594 for interface
in self.findall(
'.//{*}interface-construct/{*}' +
1595 'program-unit/{*}subroutine-stmt/' +
1596 '{*}subroutine-N/{*}N/../../../'):
1597 if n2name(interface.find(
'./{*}subroutine-stmt/' +
1598 '{*}subroutine-N/' +
1599 '{*}N')).upper() == name:
1601 raise PYFTError(
'This case is not yet implemented')