4This module implements the scope stuff
23 This class acts as a view of an ElementTree exposing only a part of the subelements
26 def __init__(self, xml, excludeContains=False):
28 :param xml: xml corresponding to a scope
29 :param excludeContains: do not take into account the CONTAINS part
38 :param xml: xml corresponding to a scope
39 :return: a node (possibly the xml node) containing only the relevant subelements
42 contains = self.
_xml.
find(
'./{*}contains-stmt')
45 indexContains = list(self.
_xml).index(contains)
46 childNode = createElem(
'virtual')
47 childNode.extend(self.
_xml[:indexContains] + [self.
_xml[-1]])
56 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.tag
63 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.tail
70 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.text
78 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.findtext
84 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.iterfind
90 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.itertext
94 def __getitem__(self, *args, **kwargs):
97 def __len__(self, *args, **kwargs):
103 def find(self, *args, **kwargs):
105 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.find
111 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.findall
115 def iter(self, *args, **kwargs):
117 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.iter
123 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.items
132 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.clear
141 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.append
148 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.extend
155 :param index: index in the virtual node
156 :return: index in the _xml node
160 contains = self.
_xml.
find(
'./{*}contains-stmt')
163 indexContains = list(self.
_xml).index(contains)
165 if index > indexContains
or index < -indexContains - 1:
166 raise IndexError(
'list index out of range')
172 index = indexContains + index + 1
174 return len(self.
_xml)
if index == indexContains
else index
176 def __setitem__(self, index, item):
180 def __delitem__(self, index):
185 https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.insert
192 Remove node from the xml
194 if isinstance(node, ElementView):
196 self.getParent(node).
remove(node)
201 This class wrapps the xml node representing a FORTRAN scope
203 SCOPE_STMT = {
'module':
'module-stmt',
204 'func':
'function-stmt',
205 'sub':
'subroutine-stmt',
207 'prog':
'program-stmt',
208 'interface':
'interface-stmt',
209 'submodule':
'submodule-stmt'}
210 SCOPE_CONSTRUCT = {
'module':
'program-unit',
211 'func':
'program-unit',
212 'sub':
'program-unit',
213 'type':
'T-construct',
214 'prog':
'program-unit',
215 'interface':
'interface-construct',
216 'submodule':
'program-unit'}
218 def __init__(self, xml, scopePath='/', parentScope=None,
219 enableCache=False, tree=None, excludeContains=False):
221 :param xml: xml corresponding to this PYFTscope
222 :param scopePath: scope path ('/' separated string) of this node
223 :param parentScope: parent PYFTscope instance
224 :param enableCache: True to cache node parents
225 :param tree: an optional Tree instance
226 :param excludeContains: do not take into account the CONTAINS part
228 super().
__init__(xml=xml, excludeContains=excludeContains)
229 self.
_mainScope = self
if parentScope
is None else parentScope._mainScope
230 self.
_path = scopePath
232 self.
tree =
Tree()
if tree
is None else tree
235 if enableCache
and parentScope
is None:
237 for node
in self.
iter():
243 result = cls.__new__(cls)
244 result.__dict__.update(self.
__dict__)
247 def __deepcopy__(self, memo):
249 result = cls.__new__(cls)
250 memo[id(self)] = result
252 setattr(result, key, copy.deepcopy(val, memo))
257 Get some attributes defined in the PYFT class
259 if attr
in (
'SHARED_TREE',
'NO_PARALLEL_LOCK',
'PARALLEL_FILE_LOCKS '):
261 raise AttributeError(f
"{attr} doesn't exist")
266 Return the current path
273 Return the main scope (the upper scope in the file)
280 Return the parent of the current scope
287 :param item: item whose parent is to be searched
288 :param level: number of degrees (1 to get the parent, 2 to get
289 the parent of the parent...)
295 return node
in self.
mainScope._cacheParent.get(id(node), [])
and \
297 check(self.
mainScope._cacheParent[id(node)]))
302 parent = self.
mainScope._cacheParent[id(item)]
305 if item
in list(node):
309 self.
mainScope._cacheParent[id(item)] = node
311 return parent
if level == 1
else self.
getParent(parent, level - 1)
315 :param item: item whose siblings are to be searched
316 :param before: returns siblings before
317 :param after: returns siblings after
318 By default before and after are True so that all siblings are returned
323 siblings = siblings[:siblings.index(item)]
325 siblings = siblings[siblings.index(item) + 1:]
326 return [s
for s
in siblings
if s != item]
332 Internal methode to compute the name of a scope
333 :param node: program-unit node
336 if tag(node) ==
'T-stmt':
338 nodeN = node.find(
'./{*}T-N/{*}N')
339 elif tag(node) ==
'submodule-stmt':
340 nodeN = node.find(
'./{*}submodule-module-N')
342 nodeN = node.find(
'.//{*}N')
343 if nodeN
is not None and nodeN.find(
'.//{*}N')
is not None:
345 nodeN = nodeN.find(
'.//{*}N')
346 if nodeN
is not None:
347 name = n2name(nodeN).upper()
355 Internal methode to compute a path part from a node
356 :param node: program-unit node
357 :return: path part (e.g. module:MODU)
367 Method to normalize a scope path
369 return '/'.join([(k.lower() +
':' + w.upper())
370 for (k, w)
in [component.split(
':')
371 for component
in scopePath.split(
'/')]])
376 Shows the list of scopes found in the source code
377 :param includeItself: include itself if self represent a "valid" scope (not a file)
379 print(
"These scopes have been found in the source code:")
380 print(
"\n".join([
' - ' + scope.path
381 for scope
in self.
getScopes(includeItself=includeItself)]))
384 def getScopes(self, level=-1, excludeContains=True, excludeKinds=None, includeItself=True):
386 :param level: -1 to get all child scopes
387 1 to get only direct child scopes
388 2 to get direct and direct of direct ...
389 :param excludeContains: if True, each PYFTscope which is a module, function or subroutine
390 that contain (after a 'CONTAINS' statement) other subroutines or
391 functions, those contained subroutines or functions are excluded
392 from the result; but the PYFTscope contains the 'END' statement
393 of the module/subroutine or function.
394 :param excludeKinds: if not None, is a list of scope kinds to exclude
395 :param includeItself: include itself if self represent a "valid" scope (not a file)
396 :return: list of PYFTscope found in the current scope
398 assert level == -1
or level > 0,
'level must be -1 or a positive int'
400 def _getRecur(node, level, basePath=''):
402 if tag(node) ==
'object':
403 usenode = node.find(
'./{*}file')
407 for child
in [child
for child
in usenode
410 if excludeKinds
is None or nodePath.split(
':')[0]
not in excludeKinds:
411 scopePath = nodePath
if basePath
in (
'',
'/') \
412 else basePath +
'/' + nodePath
414 scopePath=scopePath, parentScope=self,
415 enableCache=
False, tree=self.
tree,
416 excludeContains=excludeContains))
418 results.extend(_getRecur(child, level - 1, scopePath))
426 return _getRecur(self, level, self.
pathpathpath) + itself
429 def getScopeNode(self, scopePath, excludeContains=True, includeItself=True):
431 :param scopePath: scope path to search for
432 :param excludeContains: see getScopes
433 :param includeItself: include itself if self represent a "valid" scope (not a file)
434 :return: PYFTscope whose path is the path asked for
436 scope = [scope
for scope
in self.
getScopes(excludeContains=excludeContains,
437 includeItself=includeItself)
438 if scope.path == scopePath]
440 raise PYFTError(f
'{scopePath} not found')
442 raise PYFTError(f
'{scopePath} found several times')
448 :param node: node to test
449 :return: True if node is a scope node (construct node around a
450 module, subroutine, function or type declaration)
457 :param item: item whose scope parent is to be searched
458 :param mustRaise: True to raise an exception if parent is not found
459 :return: the scope parent node of item
460 Example: if item is a call statement, result is the program-unit node
461 in which the call statement is
464 while result
is not None and not self.
isScopeNode(result):
466 if result
is None and mustRaise:
467 raise PYFTError(
"The scope parent has not been found.")
473 :param item: item whose path must be determined
474 :param includeItself: include the item if it is a scope node
475 :return: the full path of the structure containing item
482 while item
is not None:
485 return '/'.join(result)
489 :return: the name of the input file name or 'unknown' if not available
490 in the xml fragment provided
492 return os.path.normpath(self.
mainScope.
find(
'.//{*}file').attrib[
'name'])
496 def empty(self, addStmt=None, simplify=False):
498 Empties the scope by removing all statements except dummy arguments declaration
499 and USE statements (because they can be useful for the dummy argument declarations).
500 :param addStmt: add this statement in the body of the emptied scopes
501 :param simplify: try to simplify code
504 for scope
in self.
getScopes(level=1, excludeContains=
False):
505 if scope.path.split(
'/')[-1].split(
':')[0] ==
'module':
506 scopes.extend(scope.getScopes(level=1, excludeContains=
False, includeItself=
False))
509 tagExcluded = (list(self.
SCOPE_STMT.values()) +
510 [
'end-' + decl
for decl
in self.
SCOPE_STMT.values()] +
511 [
'T-decl-stmt',
'use-stmt',
'C'])
513 for node
in list(scope):
514 if tag(node)
not in tagExcluded:
516 scope.removeUnusedLocalVar(simplify=simplify)
517 if addStmt
is not None:
518 if isinstance(addStmt, str):
519 addStmt = createExpr(addStmt)
520 elif not isinstance(addStmt, list):
523 scope.insertStatement(stmt,
False)