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)
828 :param declTemplate: declaration template
829 :param startTemplate: template for the first executable statement
830 :param: endTemplate: template for the last executable statement
831 :return: number of arrays modified
832 Modifies all automatic arrays declaration in subroutine and functions. The declaration is
833 replaced by the declaration template, the start template is inserted as first executable
834 statement and the end template as last executable statement. Each template can use the
835 following place holders:
836 "{doubledotshape}", "{shape}", "{lowUpList}", "{name}" and "{type}" wich are, respectively
837 modified into ":, :, :", "I, I:J, 0:I", "1, I, I, J, 0, I", "A", "REAL" if the original
838 declaration statement was "A(I, I:J, 0:I)". The template
839 "{type}, DIMENSION({doubledotshape}), ALLOCATABLE :: {name}#
840 ALLOCATE({name}({shape}))#DEALLOCATE({name})"
841 replaces automatic arrays by allocatables
843 templates = {
'decl': declTemplate
if declTemplate
is not None else '',
844 'start': startTemplate
if startTemplate
is not None else '',
845 'end': endTemplate
if endTemplate
is not None else ''}
848 for scope
in [scope
for scope
in self.getScopes()
849 if scope.path.split(
'/')[-1].split(
':')[0]
in (
'sub',
'func')]:
852 varListToTransform = []
853 for var
in [var
for var
in scope.varList
854 if var[
'as']
is not None and
855 len(var[
'as']) > 0
and
856 not (var[
'arg']
or var[
'allocatable']
or
857 var[
'pointer']
or var[
'result'])]:
860 if var[
'init']
is not None:
861 logging.warning(
"An array (%s) has an initial value, it can't be " +
862 "processed by modifyAutomaticArrays.)", var[
'n'])
864 varListToTransform.append(var)
868 orderedVarListToTransform = []
869 while len(varListToTransform) > 0:
871 for var
in varListToTransform[:]:
873 listN = [x
for dim
in var[
'asx']
for x
in dim
if x
is not None]
874 listN = [n2name(nodeN).upper()
for asx
in listN
875 for nodeN
in asx.findall(
'.//{*}N/{*}n/..')]
876 if len(set(listN).intersection([v[
'n'].upper()
877 for v
in varListToTransform])) == 0:
879 varListToTransform.remove(var)
880 orderedVarListToTransform.append(var)
883 raise PYFTError(
'It seems that there is a circular reference in ' +
884 'the declaration statements')
886 for var
in orderedVarListToTransform[::-1]:
889 templ = copy.deepcopy(templates)
890 for templPart
in templ:
891 if '{doubledotshape}' in templ[templPart]:
892 templ[templPart] = templ[templPart].replace(
893 '{doubledotshape}',
','.join([
':'] * len(var[
'as'])))
894 if '{shape}' in templ[templPart]:
896 for i
in range(len(var[
'as'])):
897 if var[
'as'][i][0]
is None:
898 result.append(var[
'as'][i][1])
900 result.append(var[
'as'][i][0] +
':' + var[
'as'][i][1])
901 templ[templPart] = templ[templPart].replace(
'{shape}',
', '.join(result))
902 if '{name}' in templ[templPart]:
903 templ[templPart] = templ[templPart].replace(
'{name}', var[
'n'])
904 if '{type}' in templ[templPart]:
905 templ[templPart] = templ[templPart].replace(
'{type}', var[
't'])
906 if '{lowUpList}' in templ[templPart]:
908 for i
in range(len(var[
'as'])):
909 if var[
'as'][i][0]
is None:
910 result.extend([1, var[
'as'][i][1]])
912 result.extend([var[
'as'][i][0], var[
'as'][i][1]])
913 templ[templPart] = templ[templPart].replace(
914 '{lowUpList}',
', '.join([str(r)
for r
in result]))
917 separator =
"!ABCDEFGHIJKLMNOPQRSTUVWabcdefghijklmnopqrstuvwxyz0123456789"
919 for node
in createExpr(templ[
'decl'] +
'\n' + separator +
'\n' +
920 templ[
'start'] +
'\n' + separator +
'\n' +
922 templPart = list(templ.keys())[part]
923 if not isinstance(templ[templPart], list):
924 templ[templPart] = []
925 if tag(node) ==
'C' and node.text == separator:
928 templ[templPart].append(node)
934 for decl
in scope.findall(
'./{*}T-decl-stmt'):
935 index = list(scope).index(decl)
936 if var[
'n']
in [n2name(nodeN)
for nodeN
in decl.findall(
'.//{*}N')]:
938 scope.removeVar([(scope.path, var[
'n'])], simplify=
False)
939 for nnn
in templ[
'decl'][::-1]:
940 scope.insert(index, nnn)
943 for nnn
in templ[
'start'][::-1]:
944 scope.insertStatement(nnn,
True)
945 for nnn
in templ[
'end'][::-1]:
946 scope.insertStatement(nnn,
False)
1045 Transform a array-R into a parens-R node by replacing slices by variables
1046 In 'A(:)', the ':' is in a array-R node whereas in 'A(JL)', 'JL' is in a parens-R node.
1047 Both the array-R and the parens-R nodes are inside a R-LT node
1048 :param namedE: a named-E node
1049 :param table: dictionnary returned by the decode function
1050 :param varList: None or a VarList object in which varaibles are searched for
1073 nodeRLT = namedE.find(
'./{*}R-LT')
1074 arrayR = nodeRLT.find(
'./{*}array-R')
1075 if arrayR
is not None:
1076 index = list(nodeRLT).index(arrayR)
1077 parensR = createElem(
'parens-R', text=
'(', tail=
')')
1078 elementLT = createElem(
'element-LT')
1079 parensR.append(elementLT)
1081 for ss
in nodeRLT[index].findall(
'./{*}section-subscript-LT/{*}section-subscript'):
1082 element = createElem(
'element', tail=
', ')
1083 elementLT.append(element)
1084 if ':' in alltext(ss):
1086 varName = list(table.keys())[ivar]
1087 lower = ss.find(
'./{*}lower-bound')
1088 upper = ss.find(
'./{*}upper-bound')
1089 if lower
is not None:
1090 lower = alltext(lower)
1091 if upper
is not None:
1092 upper = alltext(upper)
1093 if lower
is not None and ss.text
is not None and ':' in ss.text:
1097 if lower
is None and upper
is None:
1101 element.append(createExprPart(varName))
1108 lower = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1109 array=
True)[
'as'][ivar][0]
1114 upper = self.
varList.findVar(n2name(namedE.find(
'{*}N')),
1115 array=
True)[
'as'][ivar][1]
1121 newlower = simplifyExpr(lower, add=varName, sub=table[varName][0])
1122 newupper = simplifyExpr(upper, add=varName, sub=table[varName][1])
1123 if newlower != newupper:
1124 raise PYFTError((
"Don't know how to do with an array declared with " +
1125 "'{la}:{ua}' and a loop from '{ll}' to '{ul}'"
1126 ).format(la=lower, ua=upper,
1127 ll=table[varName][0],
1128 ul=table[varName][1]))
1129 element.append(createExprPart(newlower))
1131 element.append(ss.find(
'./{*}lower-bound'))
1133 nodeRLT.remove(nodeRLT[index])
1134 nodeRLT.insert(index, parensR)
1139 Find bounds and loop variable given an array
1140 :param arr: array node (named-E node with a array-R child)
1141 :param loopVar: None to create new variable for each added DO loop
1142 or a function that return the name of the variable to use for the loop
1144 This function returns a string (name of the variable), or True to create
1145 a new variable, or False to not transform this statement
1146 The functions takes as arguments:
1147 - lower and upper bounds as defined in the declaration statement
1148 - lower and upper bounds as given in the statement
1151 :param extraVarList: None or list of variables (such as those contained in a VarList object)
1152 defined but not yet available in the self.varList object.
1153 :return: the tuple (table, newVar) where:
1154 table is a dictionnary: keys are loop variable names
1155 values are tuples with lower and upper bounds
1156 newVar is a list of loop variables not found in varList. This list has the same
1157 format as the varList list.
1159 In case the loop variable cannot be defined, the function returns (None, [])
1162 name = n2name(arr.find(
'./{*}N'))
1164 extraVarList = extraVarList
if extraVarList
is not None else []
1167 for iss, ss
in enumerate(arr.findall(
'./{*}R-LT/{*}array-R/{*}section-subscript-LT/' +
1168 '{*}section-subscript')):
1171 if ':' in alltext(ss):
1176 if varName
is not False and varName
in table:
1177 raise PYFTError((
"The variable {var} must be used for the rank #{i1} whereas " +
1178 "it is already used for rank #{i2} (for array {name})."
1179 ).format(var=varName, i1=str(iss),
1180 i2=str(list(table.keys()).index(varName)),
1191 varName =
'J' + str(j)
1192 var = self.
varList.findVar(varName, extraVarList=extraVarList + varNew)
1193 if (var
is None or var.get(
'new',
False))
and varName
not in table:
1195 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1196 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1197 'scopePath': self.
path}
1198 if varDesc
not in varNew:
1199 varNew.append(varDesc)
1201 elif (varName
is not False and
1202 self.
varList.findVar(varName, array=
False, exactScope=
True)
is None):
1204 varDesc = {
'as': [],
'asx': [],
'n': varName,
'i':
None,
1205 't':
'INTEGER',
'arg':
False,
'use':
False,
'opt':
False,
1206 'scopePath': self.
path}
1207 varNew.append(varDesc)
1210 table[varName] = (lower, upper)
1212 return (
None, [])
if False in table
else (table, varNew)
1261 def isVarUsed(self, varList, exactScope=False, dummyAreAlwaysUsed=False):
1263 :param varList: list of variables to test. Each item is a list or tuple of two elements.
1264 The first one describes where the variable is declared, the second one is
1265 the name of the variable. The first element is a '/'-separated path with
1266 each element having the form 'module:<name of the module>',
1267 'sub:<name of the subroutine>' or 'func:<name of the function>'
1268 :param exactScope: True to search strictly in scope
1269 :param dummyAreAlwaysUsed: Returns True if variable is a dummy argument
1270 :return: a dict whose keys are the elements of varList, and values are True when the
1271 variable is used, False otherwise
1273 If exactScope is True, the function will search for variable usage
1274 only in this scope. But this feature has a limited interest.
1276 If exactScope is False:
1277 - if scopePath is a subroutine/function in a contains section,
1278 and if the variable is not declared in this scope, usages are
1279 searched in the module/subroutine/function upper that declared
1280 the variable and in all subroutines/functions in the contains section
1281 - if scopePath is a module/subroutine/function that has a
1282 contains sections, usages are searched in all subroutines/functions
1283 in the contains section
1285 To know if a variable can be removed, you must use exactScope=False
1289 allScopes = {scope.path: scope
for scope
in self.mainScope.getScopes()}
1293 locsVar = {(scopePath, varName): [scopePath]
1294 for scopePath, varName
in varList}
1297 for scopePath, varName
in varList:
1300 var = allScopes[scopePath].varList.findVar(varName)
1301 path = scopePath.split(
'/')[0]
if var
is None else var[
'scopePath']
1306 for scPath, sc
in allScopes.items():
1307 if scPath.startswith(path +
'/')
and \
1308 scPath.split(
'/')[-1].split(
':')[0] !=
'type':
1310 if sc.varList.findVar(varName, exactScope=
True)
is None:
1312 testScopes.append(scPath)
1313 locsVar[(scopePath, varName)] = testScopes
1317 for scopePath
in list(set(item
for sublist
in locsVar.values()
for item
in sublist)):
1318 usedVar[scopePath] = []
1320 for node
in allScopes[scopePath]:
1323 if not tag(node) ==
'use-stmt':
1324 if tag(node) ==
'T-decl-stmt':
1328 nodesN = node.findall(
'.//{*}_T-spec_//{*}N') + \
1329 node.findall(
'.//{*}shape-spec//{*}N')
1331 nodesN = node.findall(
'.//{*}N')
1334 for nodeN
in nodesN:
1335 if dummyAreAlwaysUsed:
1339 usedVar[scopePath].append(n2name(nodeN).upper())
1341 parPar = allScopes[scopePath].getParent(nodeN, 2)
1344 if parPar
is None or not tag(parPar) ==
'dummy-arg-LT':
1345 usedVar[scopePath].append(n2name(nodeN).upper())
1348 for scopePath, varName
in varList:
1349 assert scopePath.split(
'/')[-1].split(
':')[0] !=
'type', \
1350 'We cannot check type component usage'
1351 result[(scopePath, varName)] = any(varName.upper()
in usedVar[scopePath]
1352 for scopePath
in locsVar[(scopePath, varName)])
1358 def addArgInTree(self, varName, declStmt, pos, stopScopes, moduleVarList=None,
1360 parserOptions=None, wrapH=False):
1362 Adds an argument to the routine and propagates it upward until we encounter a scope
1363 where the variable exists or a scope in stopScopes
1364 :param varName: variable name
1365 :param declStmt: declarative statment (will be used by addVar)
1366 :param pos: position of the variable in the list of dummy argument
1367 :param stopScopes: list of scopes to reach
1368 :param moduleVarList: list of module variable specification to insert in the xml code
1369 a module variable specification is a list of two elements:
1371 - variable name or or list of variable names
1372 or None to add a USE statement without the ONLY attribute
1373 use moduleVarList to not add module variables
1374 :param otherNames: None or list of other variable names that can be used
1375 These variables are used first
1376 :param parserOptions, wrapH: see the PYFT class
1378 Argument is inserted only on paths leading to one of scopes listed in stopScopes
1380 def insertInArgList(varName, varNameToUse, pos, callFuncStmt):
1382 Insert varName in the list of arguments to the subroutine or function call
1383 :param varName: name of the dummy argument
1384 :param varNameToUse: name of the variable
1385 :param pos: inclusion position
1386 :param callFuncStmt: call statement or function call
1388 argList = callFuncStmt.find(
'./{*}R-LT/{*}parens-R/{*}element-LT')
1389 if argList
is not None:
1390 container = createElem(
'element')
1392 argList = callFuncStmt.find(
'./{*}arg-spec')
1393 container = createElem(
'arg')
1396 callFuncStmt.find(
'./{*}procedure-designator').tail =
'('
1397 argList = createElem(
'arg-spec', tail=
')')
1398 callFuncStmt.append(argList)
1399 item = createExprPart(varNameToUse)
1400 previous = pos - 1
if pos >= 0
else len(argList) + pos
1401 while previous >= 0
and tag(argList[previous])
in (
'C',
'cnt'):
1403 following = pos
if pos > 0
else len(argList) + pos + 1
1404 while following <= len(argList) - 1
and tag(argList[following])
in (
'C',
'cnt'):
1406 if (previous >= 0
and argList[previous].find(
'./{*}arg-N/{*}k')
is not None)
or \
1407 (following <= len(argList) - 1
and
1408 argList[following].find(
'./{*}arg-N/{*}k')
is not None)
or \
1409 following == len(argList):
1415 k = createElem(
'k', text=varName)
1416 argN = createElem(
'arg-N', tail=
'=')
1418 argN.set(
'n', varName)
1419 container.append(argN)
1420 container.append(item)
1421 self.insertInList(pos, container, argList)
1423 if self.
path in stopScopes
or self.tree.isUnderStopScopes(self.
path, stopScopes):
1426 var = self.
varList.findVar(varName, exactScope=
True)
1427 if otherNames
is not None:
1428 vOther = [self.
varList.findVar(v, exactScope=
True)
for v
in otherNames]
1429 vOther = [v
for v
in vOther
if v
is not None]
1435 self.
addVar([[self.
path, varName, declStmt, pos]])
1436 if moduleVarList
is not None:
1439 for (moduleName, moduleVarNames)
in moduleVarList])
1442 if len(self.
path.split(
'/')) == 1:
1443 filename, scopePathInterface = self.tree.findScopeInterface(self.
path)
1444 if filename
is not None:
1447 if self.getFileName() == os.path.normpath(filename):
1449 xml = self.mainScope
1453 filename, parserOptions, wrapH, tree=self.tree,
1454 clsPYFT=self._mainScope.__class__)
1456 scopeInterface = xml.getScopeNode(scopePathInterface)
1457 varInterface = scopeInterface.varList.findVar(varName, exactScope=
True)
1458 if varInterface
is None:
1459 scopeInterface.addVar([[scopePathInterface, varName,
1461 if moduleVarList
is not None:
1464 [(scopePathInterface, moduleName, moduleVarNames)
1465 for (moduleName, moduleVarNames)
in moduleVarList])
1472 if var
is None and self.
path not in stopScopes:
1475 for scopePathUp
in self.tree.calledByScope(self.
path):
1476 if scopePathUp
in stopScopes
or self.tree.isUnderStopScopes(scopePathUp,
1481 for filename
in self.tree.scopeToFiles(scopePathUp):
1484 if self.getFileName() == os.path.normpath(filename):
1486 xml = self.mainScope
1490 filename, parserOptions, wrapH,
1492 clsPYFT=self._mainScope.__class__)
1494 scopeUp = xml.getScopeNode(scopePathUp)
1496 scopeUp.addArgInTree(
1497 varName, declStmt, pos,
1498 stopScopes, moduleVarList, otherNames,
1499 parserOptions=parserOptions,
1502 name = self.
path.split(
'/')[-1].split(
':')[1].upper()
1504 varNameToUse = varName
1505 if otherNames
is not None:
1506 vOther = [scopeUp.varList.findVar(v, exactScope=
True)
1507 for v
in otherNames]
1508 vOther = [v
for v
in vOther
if v
is not None]
1510 varNameToUse = vOther[-1][
'n']
1511 if self.
path.split(
'/')[-1].split(
':')[0] ==
'sub':
1513 for callStmt
in scopeUp.findall(
'.//{*}call-stmt'):
1514 callName = n2name(callStmt.find(
1515 './{*}procedure-designator/{*}named-E/{*}N')).upper()
1516 if callName == name:
1517 insertInArgList(varName, varNameToUse, pos, callStmt)
1521 for funcCall
in scopeUp.findall(
1522 './/{*}named-E/{*}R-LT/{*}parens-R/' +
1523 '{*}element-LT/../../..'):
1524 funcName = n2name(funcCall.find(
'./{*}N')).upper()
1525 if funcName == name:
1526 insertInArgList(varName, varNameToUse, pos, funcCall)
1537 for interface
in self.findall(
'.//{*}interface-construct/{*}' +
1538 'program-unit/{*}subroutine-stmt/' +
1539 '{*}subroutine-N/{*}N/../../../'):
1540 if n2name(interface.find(
'./{*}subroutine-stmt/' +
1541 '{*}subroutine-N/' +
1542 '{*}N')).upper() == name:
1544 raise PYFTError(
'This case is not yet implemented')