37 ''' Fortran code-generation library. This wraps the f2py fortran parser to
38 provide routines which can be used to generate fortran code. '''
41 from fparser.common.readfortran
import FortranStringReader
42 from fparser.common.sourceinfo
import FortranFormat
43 from fparser.one.statements
import Comment, Case
44 from fparser.one.block_statements
import SelectCase, SelectType, EndSelect
45 from fparser.one.parsefortran
import FortranParser
49 from fparser
import one
as fparser1
56 def bubble_up_type(obj):
58 Checks whether the supplied object must be bubbled-up (e.g. from
61 :returns: True if the supplied object is of a type which must be \
62 bubbled-up and False otherwise.
64 return isinstance(obj, (UseGen, BaseDeclGen))
67 def index_of_object(alist, obj):
68 '''Effectively implements list.index(obj) but returns the index of
69 the first item in the list that *is* the supplied object (rather than
71 for idx, body
in enumerate(alist):
74 raise Exception(f
"Object {obj} not found in list")
83 Base class for directives so we can reason about them when walking
84 the tree. Sub-classes the fparser1 Comment class.
86 :param root: the parent node in the AST to which we are adding the \
88 :type root: subclass of :py:class:`fparser.common.base_classes.Statement`
89 :param line: the fparser object which we will manipulate to create \
90 the desired directive.
91 :type line: :py:class:`fparser.common.readfortran.Comment`
92 :param str position: e.g. 'begin' or 'end' (language specific)
93 :param str dir_type: the type of directive that this is (e.g. \
96 def __init__(self, root, line, position, dir_type):
97 if dir_type
not in self._types:
98 raise RuntimeError(f
"Error, unrecognised directive type "
99 f
"'{dir_type}'. Should be one of {self._types}")
100 if position
not in self._positions:
101 raise RuntimeError(f
"Error, unrecognised position '{position}'. "
102 f
"Should be one of {self._positions}")
105 Comment.__init__(self, root, line)
110 :returns: the type of this Directive.
118 :returns: the position of this Directive ('begin' or 'end').
126 Subclass Directive for OpenMP directives so we can reason about
127 them when walking the tree.
129 :param root: the parent node in the AST to which we are adding the \
131 :type root: subclass of :py:class:`fparser.common.base_classes.Statement`
132 :param line: the fparser object which we will manipulate to create \
133 the desired directive.
134 :type line: :py:class:`fparser.common.readfortran.Comment`
135 :param str position: e.g. 'begin' or 'end' (language specific).
136 :param str dir_type: the type of directive that this is (e.g. \
139 def __init__(self, root, line, position, dir_type):
140 self.
_types_types = [
"parallel do",
"parallel",
"do",
"master",
"single",
141 "taskloop",
"taskwait",
"declare"]
144 super(OMPDirective, self).__init__(root, line, position, dir_type)
149 Subclass Directive for OpenACC directives so we can reason about them
150 when walking the tree.
152 :param root: the parent node in the AST to which we are adding the \
154 :type root: subclass of :py:class:`fparser.common.base_classes.Statement`
155 :param line: the fparser object which we will manipulate to create \
156 the desired directive.
157 :type line: :py:class:`fparser.common.readfortran.Comment`
158 :param str position: e.g. 'begin' or 'end' (language specific).
159 :param str dir_type: the type of directive that this is (e.g. \
162 def __init__(self, root, line, position, dir_type):
163 self.
_types_types = [
"parallel",
"kernels",
"enter data",
"loop",
"routine"]
166 super(ACCDirective, self).__init__(root, line, position, dir_type)
174 ''' The base class for all classes that are responsible for generating
175 distinct code elements (modules, subroutines, do loops etc.) '''
176 def __init__(self, parent, root):
178 self.
_root_root = root
183 ''' Returns the parent of this object '''
188 ''' Returns the list of children of this object '''
193 ''' Returns the root of the tree containing this object '''
194 return self.
_root_root
196 def add(self, new_object, position=None):
197 '''Adds a new object to the tree. The actual position is determined by
198 the position argument. Note, there are two trees, the first is
199 the f2pygen object tree, the other is the f2py generated code
200 tree. These are similar but different. At the moment we
201 specify where to add things in terms of the f2pygen tree
202 (which is a higher level api) but we also insert into the f2py
203 tree at exactly the same location which needs to be sorted out
214 position = [
"append"]
216 if position[0] ==
"auto":
217 raise Exception(
"Error: BaseGen:add: auto option must be "
218 "implemented by the sub class!")
219 options = [
"append",
"first",
"after",
"before",
"insert",
220 "before_index",
"after_index"]
221 if position[0]
not in options:
222 raise Exception(f
"Error: BaseGen:add: supported positions are "
223 f
"{options} but found {position[0]}")
224 if position[0] ==
"append":
225 self.
rootroot.content.append(new_object.root)
226 elif position[0] ==
"first":
227 self.
rootroot.content.insert(0, new_object.root)
228 elif position[0] ==
"insert":
230 self.
rootroot.content.insert(index, new_object.root)
231 elif position[0] ==
"after":
232 idx = index_of_object(self.
rootroot.content, position[1])
233 self.
rootroot.content.insert(idx+1, new_object.root)
234 elif position[0] ==
"after_index":
235 self.
rootroot.content.insert(position[1]+1, new_object.root)
236 elif position[0] ==
"before_index":
237 self.
rootroot.content.insert(position[1], new_object.root)
238 elif position[0] ==
"before":
240 idx = index_of_object(self.
rootroot.content, position[1])
241 except Exception
as err:
244 "Failed to find supplied object in existing content - "
245 "is it a child of the parent?")
246 self.
rootroot.content.insert(idx, new_object.root)
248 raise Exception(
"Error: BaseGen:add: internal error, should "
250 self.
childrenchildren.append(new_object)
253 ''' Returns the *last* occurrence of a loop in the list of
254 siblings of this node '''
255 from fparser.one.block_statements
import Do
256 for sibling
in reversed(self.
rootroot.content):
257 if isinstance(sibling, Do):
259 raise RuntimeError(
"Error, no loop found - there is no previous loop")
262 '''Returns the *last* occurrence of a Declaration in the list of
263 siblings of this node
266 from fparser.one.typedecl_statements
import TypeDeclarationStatement
267 for sibling
in reversed(self.
rootroot.content):
268 if isinstance(sibling, TypeDeclarationStatement):
271 raise RuntimeError(
"Error, no variable declarations found")
274 ''' Searches for the outer-most loop containing this object. Returns
275 the index of that line in the content of the parent. '''
276 from fparser.one.block_statements
import Do
278 print(
"Entered before_parent_loop")
279 print(f
"The type of the current node is {type(self.root)}")
280 print((
"If the current node is a Do loop then move up to the "
281 "top of the do loop nest"))
284 current = self.
rootroot
285 while not isinstance(current, Do)
and getattr(current,
'parent',
None):
286 current = current.parent
287 if not isinstance(current, Do):
288 raise RuntimeError(
"This node has no enclosing Do loop")
290 current = self.
rootroot
292 while isinstance(current.parent, Do):
294 print(
"Parent is a do loop so moving to the parent")
295 current = current.parent
296 local_current = local_current.parent
298 print(
"The type of the current node is now " + str(type(current)))
299 print(
"The type of parent is " + str(type(current.parent)))
300 print(
"Finding the loops position in its parent ...")
301 index = current.parent.content.index(current)
303 print(
"The loop's index is ", index)
304 parent = current.parent
305 local_current = local_current.parent
307 print(
"The type of the object at the index is " +
308 str(type(parent.content[index])))
309 print(
"If preceding node is a directive then move back one")
312 print(
"current index is 0 so finish")
313 elif isinstance(parent.content[index-1], Directive):
316 f
"preceding node is a directive so find out what type ..."
317 f
"\n type is {parent.content[index-1].position}"
318 f
"\n diretive is {parent.content[index-1]}")
319 if parent.content[index-1].position ==
"begin":
321 print(
"type of directive is begin so move back one")
325 print(
"directive type is not begin so finish")
328 print(
"preceding node is not a directive so finish")
330 print(
"type of final location ", type(parent.content[index]))
331 print(
"code for final location ", str(parent.content[index]))
332 return local_current, parent.content[index]
336 ''' Functionality relevant to program units (currently modules,
338 def __init__(self, parent, sub):
339 BaseGen.__init__(self, parent, sub)
341 def add(self, content, position=None, bubble_up=False):
343 Specialise the add method to provide module- and subroutine-
344 -specific intelligent adding of use statements, implicit
345 none statements and declarations if the position argument
346 is set to auto (which is the default).
348 :param content: the Node (or sub-tree of Nodes) to add in to \
350 :type content: :py:class:`psyclone.f2pygen.BaseGen`
351 :param list position: where to insert the node. One of "append", \
352 "first", "insert", "after", "after_index", \
353 "before_index", "before" or "auto". For the \
354 *_index options, the second element of the \
355 list holds the integer index.
356 :param bool bubble_up: whether or not object (content) is in the \
357 process of being bubbled-up.
371 self_ancestor = self.
rootroot
374 obj_parent = content.root.parent
375 while (obj_parent != self_ancestor
and
376 getattr(obj_parent,
'parent',
None)):
377 obj_parent = obj_parent.parent
378 if obj_parent == self_ancestor:
383 if getattr(self_ancestor,
'parent',
None):
384 self_ancestor = self_ancestor.parent
388 if obj_parent != self_ancestor:
390 f
"Cannot add '{content}' to '{self}' because it is not a "
391 f
"descendant of it or of any of its ancestors.")
396 content.root.parent = self.
rootroot
398 if position[0] !=
"auto":
400 BaseGen.add(self, content, position)
403 if isinstance(content, BaseDeclGen):
405 if isinstance(content, (DeclGen, CharDeclGen)):
408 if isinstance(child, (DeclGen, CharDeclGen)):
410 if child.root.name == content.root.name:
413 for var_name
in content.root.entity_decls[:]:
414 for child_name
in child.root.entity_decls:
415 if var_name.lower() == \
417 content.root.entity_decls.\
419 if not content.root.entity_decls:
424 if isinstance(content, TypeDeclGen):
427 if isinstance(child, TypeDeclGen):
429 if child.root.selector[1] == \
430 content.root.selector[1]:
433 for var_name
in content.root.entity_decls[:]:
434 for child_name
in child.root.entity_decls:
435 if var_name.lower() == \
437 content.root.entity_decls.\
439 if not content.root.entity_decls:
455 for attr
in self.
rootroot.content[index].attrspec:
456 if attr.find(
"intent") == 0:
460 except AttributeError:
462 elif isinstance(content.root, fparser1.statements.Use):
465 if isinstance(child, UseGen):
466 if child.root.name == content.root.name:
468 if not child.root.isonly
and not \
473 if child.root.isonly
and not content.root.isonly:
477 if not child.root.isonly
and content.root.isonly:
482 if child.root.isonly
and content.root.isonly:
485 for new_name
in content.root.items[:]:
486 for existing_name
in child.root.items:
487 if existing_name.lower() == \
489 content.root.items.remove(new_name)
490 if not content.root.items:
493 elif isinstance(content, ImplicitNoneGen):
496 if isinstance(child, ImplicitNoneGen):
502 index = len(self.
rootroot.content) - 1
503 self.
rootroot.content.insert(index, content.root)
506 def _skip_use_and_comments(self, index):
507 ''' skip over any use statements and comments in the ast '''
508 while isinstance(self.
rootroot.content[index],
509 fparser1.statements.Use)
or\
510 isinstance(self.
rootroot.content[index],
511 fparser1.statements.Comment):
514 while isinstance(self.
rootroot.content[index-1],
515 fparser1.statements.Comment):
519 def _skip_imp_none_and_comments(self, index):
520 ''' skip over an implicit none statement if it exists and any
521 comments before it '''
523 while isinstance(self.
rootroot.content[index],
524 fparser1.typedecl_statements.Implicit)
or\
525 isinstance(self.
rootroot.content[index],
526 fparser1.statements.Comment):
527 if isinstance(self.
rootroot.content[index],
528 fparser1.typedecl_statements.Implicit):
529 end_index = index + 1
537 ''' Create a Fortran block of code that comes from a given PSyIR tree.
539 :param parent: node in AST to which we are adding the PSyIR block.
540 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
541 :param content: the PSyIR tree we are adding.
542 :type content: :py:class:`psyclone.psyir.nodes.Node`
546 def __init__(self, parent, content):
552 config = Config.get()
557 fortran_writer = FortranWriter(
558 check_global_constraints=config.backend_checks_enabled)
559 reader = FortranStringReader(fortran_writer(content),
560 ignore_comments=
False)
562 reader.set_format(FortranFormat(
True,
True))
563 fparser1_parser = FortranParser(reader, ignore_comments=
False)
564 fparser1_parser.parse()
569 for fparser_node
in fparser1_parser.block.content[:-1]:
570 f2pygen_node =
BaseGen(parent, fparser_node)
571 f2pygen_node.root.parent = parent.root
572 parent.add(f2pygen_node)
576 BaseGen.__init__(self, parent, fparser1_parser.block.content[-1])
577 self.
rootroot.parent = parent.root
581 ''' create a fortran module '''
582 def __init__(self, name="", contains=True, implicitnone=True):
583 from fparser
import api
595 tree = api.parse(code, ignore_comments=
False)
596 module = tree.content[0]
598 endmod = module.content[len(module.content)-1]
600 ProgUnitGen.__init__(self,
None, module)
605 ''' adds a subroutine to the module that is a raw f2py parse object.
606 This is used for inlining kernel subroutines into a module.
609 if not isinstance(content, KernelProcedure):
611 "Expecting a KernelProcedure type but received " +
613 content.ast.parent = self.
rootroot
615 index = len(self.
rootroot.content) - 1
616 self.
rootroot.content.insert(index, content.ast)
620 ''' Create a Fortran Comment '''
623 :param parent: node in AST to which to add the Comment as a child
624 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
625 :param str content: the content of the comment
627 reader = FortranStringReader(
"! content\n", ignore_comments=
False)
628 reader.set_format(FortranFormat(
True,
True))
629 subline = reader.next()
631 my_comment = Comment(parent.root, subline)
632 my_comment.content = content
634 BaseGen.__init__(self, parent, my_comment)
639 Class for creating a Fortran directive, e.g. OpenMP or OpenACC.
641 :param parent: node in AST to which to add directive as a child.
642 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
643 :param str language: the type of directive (e.g. OMP or ACC).
644 :param str position: "end" if this is the end of a directive block.
645 :param str directive_type: the directive itself (e.g. "PARALLEL DO").
646 :param str content: any additional arguments to add to the directive \
647 (e.g. "PRIVATE(ji)").
649 :raises RuntimeError: if an unrecognised directive language is specified.
651 def __init__(self, parent, language, position, directive_type, content=""):
656 reader = FortranStringReader(
"! content\n", ignore_comments=
False)
657 reader.set_format(FortranFormat(
True,
True))
658 subline = reader.next()
660 if language ==
"omp":
661 my_comment =
OMPDirective(parent.root, subline, position,
663 my_comment.content =
"$omp"
664 elif language ==
"acc":
665 my_comment =
ACCDirective(parent.root, subline, position,
667 my_comment.content =
"$acc"
670 f
"Error, unsupported directive language. Expecting one of "
671 f
"{self._supported_languages} but found '{language}'")
672 if position ==
"end":
673 my_comment.content +=
" end"
674 my_comment.content +=
" " + directive_type
676 my_comment.content +=
" " + content
678 BaseGen.__init__(self, parent, my_comment)
682 ''' Generate a Fortran 'implicit none' statement '''
685 :param parent: node in AST to which to add 'implicit none' as a child
686 :type parent: :py:class:`psyclone.f2pygen.ModuleGen` or
687 :py:class:`psyclone.f2pygen.SubroutineGen`
689 :raises Exception: if `parent` is not a ModuleGen or SubroutineGen
691 if not isinstance(parent, ModuleGen)
and not isinstance(parent,
694 f
"The parent of ImplicitNoneGen must be a module or a "
695 f
"subroutine, but found {type(parent)}")
696 reader = FortranStringReader(
"IMPLICIT NONE\n")
697 reader.set_format(FortranFormat(
True,
True))
698 subline = reader.next()
700 from fparser.one.typedecl_statements
import Implicit
701 my_imp_none = Implicit(parent.root, subline)
703 BaseGen.__init__(self, parent, my_imp_none)
707 ''' Generate a Fortran subroutine '''
708 def __init__(self, parent, name="", args=None, implicitnone=False):
710 :param parent: node in AST to which to add Subroutine as a child
711 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
712 :param str name: name of the Fortran subroutine
713 :param list args: list of arguments accepted by the subroutine
714 :param bool implicitnone: whether or not we should specify
715 "implicit none" for the body of this
718 reader = FortranStringReader(
719 "subroutine vanilla(vanilla_arg)\nend subroutine")
720 reader.set_format(FortranFormat(
True,
True))
721 subline = reader.next()
722 endsubline = reader.next()
724 from fparser.one.block_statements
import Subroutine, EndSubroutine
725 self.
_sub_sub = Subroutine(parent.root, subline)
726 self.
_sub_sub.name = name
729 self.
_sub_sub.args = args
730 endsub = EndSubroutine(self.
_sub_sub, endsubline)
731 self.
_sub_sub.content.append(endsub)
732 ProgUnitGen.__init__(self, parent, self.
_sub_sub)
738 ''' Returns the list of arguments of this subroutine '''
739 return self.
_sub_sub.args
743 ''' sets the subroutine arguments to the values in the list provide.'''
744 self.
_sub_sub.args = namelist
748 ''' Generates a Fortran call of a subroutine '''
751 :param parent: node in AST to which to add CallGen as a child
752 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
753 :param str name: the name of the routine to call
754 :param list args: list of arguments to pass to the call
756 reader = FortranStringReader(
"call vanilla(vanilla_arg)")
757 reader.set_format(FortranFormat(
True,
True))
758 myline = reader.next()
760 from fparser.one.block_statements
import Call
761 self.
_call_call = Call(parent.root, myline)
762 self.
_call_call.designator = name
765 self.
_call_call.items = args
767 BaseGen.__init__(self, parent, self.
_call_call)
771 ''' Generate a Fortran use statement '''
772 def __init__(self, parent, name="", only=False, funcnames=None):
774 :param parent: node in AST to which to add UseGen as a child
775 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
776 :param str name: name of the module to USE
777 :param bool only: whether this USE has an ONLY clause
778 :param list funcnames: list of names to follow ONLY clause
780 reader = FortranStringReader(
"use kern,only : func1_kern=>func1")
781 reader.set_format(FortranFormat(
True,
True))
782 myline = reader.next()
784 from fparser.one.block_statements
import Use
785 use = Use(root, myline)
788 if funcnames
is None:
791 local_funcnames = funcnames[:]
792 use.items = local_funcnames
793 BaseGen.__init__(self, parent, use)
796 def adduse(name, parent, only=False, funcnames=None):
798 Adds a use statement with the specified name to the supplied object.
799 This routine is required when modifying an existing AST (e.g. when
800 modifying a kernel). The classes are used when creating an AST from
801 scratch (for the PSy layer).
803 :param str name: name of module to USE
804 :param parent: node in fparser1 AST to which to add this USE as a child
805 :type parent: :py:class:`fparser.one.block_statements.*`
806 :param bool only: whether this USE has an "ONLY" clause
807 :param list funcnames: list of quantities to follow the "ONLY" clause
809 :returns: an fparser1 Use object
810 :rtype: :py:class:`fparser.one.block_statements.Use`
812 reader = FortranStringReader(
"use kern,only : func1_kern=>func1")
813 reader.set_format(FortranFormat(
True,
True))
814 myline = reader.next()
817 while not isinstance(parent, (fparser1.block_statements.Program,
818 fparser1.block_statements.Module,
819 fparser1.block_statements.Subroutine)):
820 parent = parent.parent
821 use = fparser1.block_statements.Use(parent, myline)
824 if funcnames
is None:
827 use.items = funcnames
829 parent.content.insert(0, use)
834 ''' Generates a Fortran allocate statement '''
837 :param parent: node to which to add this ALLOCATE as a child
838 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
839 :param content: string or list of variables to allocate
840 :type content: list of strings or a single string
841 :param mold: A string to be used as the 'mold' parameter of ALLOCATE.
842 :type mold: str or None.
844 :raises RuntimeError: if `content` is not of correct type
846 reader = FortranStringReader(
"allocate(dummy)")
847 reader.set_format(FortranFormat(
True,
False))
848 myline = reader.next()
849 self.
_decl_decl = fparser1.statements.Allocate(parent.root, myline)
850 if isinstance(content, str):
851 self.
_decl_decl.items = [content]
852 elif isinstance(content, list):
853 self.
_decl_decl.items = content
856 f
"AllocateGen expected the content argument to be a str or"
857 f
" a list, but found {type(content)}")
859 self.
_decl_decl.items.append(f
"mold={mold}")
860 BaseGen.__init__(self, parent, self.
_decl_decl)
864 ''' Generates a Fortran deallocate statement '''
867 :param parent: node to which to add this DEALLOCATE as a child
868 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
869 :param content: string or list of variables to deallocate
870 :type content: list of strings or a single string
872 :raises RuntimeError: if `content` is not of correct type
874 reader = FortranStringReader(
"deallocate(dummy)")
875 reader.set_format(FortranFormat(
True,
False))
876 myline = reader.next()
877 self.
_decl_decl = fparser1.statements.Deallocate(parent.root, myline)
878 if isinstance(content, str):
879 self.
_decl_decl.items = [content]
880 elif isinstance(content, list):
881 self.
_decl_decl.items = content
884 f
"DeallocateGen expected the content argument to be a str"
885 f
" or a list, but found {type(content)}")
886 BaseGen.__init__(self, parent, self.
_decl_decl)
891 Abstract base class for all types of Fortran declaration. Uses the
892 abc module so it cannot be instantiated.
894 :param parent: node to which to add this declaration as a child.
895 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
896 :param str datatype: the (intrinsic) type for this declaration.
897 :param list entity_decls: list of variable names to declare.
898 :param str intent: the INTENT attribute of this declaration.
899 :param bool pointer: whether or not this is a pointer declaration.
900 :param str dimension: the DIMENSION specifier (i.e. the xx in \
902 :param bool allocatable: whether this declaration is for an \
903 ALLOCATABLE quantity.
904 :param bool save: whether this declaration has the SAVE attribute.
905 :param bool target: whether this declaration has the TARGET attribute.
906 :param initial_values: initial value to give each variable.
907 :type initial_values: list of str with same no. of elements as entity_decls
908 :param bool private: whether this declaration has the PRIVATE attribute \
911 :raises RuntimeError: if no variable names are specified.
912 :raises RuntimeError: if the wrong number or type of initial values are \
914 :raises RuntimeError: if initial values are supplied for a quantity that \
915 is allocatable or has INTENT(in).
916 :raises NotImplementedError: if initial values are supplied for array \
917 variables (dimension != "").
922 def __init__(self, parent, datatype="", entity_decls=None, intent="",
923 pointer=False, dimension="", allocatable=False,
924 save=False, target=False, initial_values=None, private=False):
925 if entity_decls
is None:
927 "Cannot create a variable declaration without specifying the "
928 "name(s) of the variable(s)")
934 if len(initial_values) != len(entity_decls):
936 f
"f2pygen.DeclGen.init: number of initial values supplied "
937 f
"({len(initial_values)}) does not match the number of "
938 f
"variables to be declared ({len(entity_decls)}: "
942 f
"Cannot specify initial values for variable(s) "
943 f
"{entity_decls} because they have the 'allocatable' "
946 raise NotImplementedError(
947 "Specifying initial values for array declarations is not "
948 "currently supported.")
949 if intent.lower() ==
"in":
951 f
"Cannot assign (initial) values to variable(s) "
952 f
"{entity_decls} as they have INTENT(in).")
958 self.
_names_names = entity_decls[:]
961 local_entity_decls = entity_decls[:]
964 value_pairs = zip(local_entity_decls, initial_values)
966 self.
_decl_decl.entity_decls = [
"=".join(_)
for _
in value_pairs]
968 self.
_decl_decl.entity_decls = local_entity_decls
973 my_attrspec.append(f
"intent({intent})")
975 my_attrspec.append(
"pointer")
977 my_attrspec.append(
"target")
979 my_attrspec.append(
"allocatable")
981 my_attrspec.append(
"save")
983 my_attrspec.append(
"private")
985 my_attrspec.append(f
"dimension({dimension})")
986 self.
_decl_decl.attrspec = my_attrspec
988 super(BaseDeclGen, self).__init__(parent, self.
_decl_decl)
993 :returns: the names of the variables being declared.
1001 :returns: the associated Type object.
1003 :py:class:`fparser.one.typedecl_statements.TypeDeclarationStatement`.
1005 return self.
_decl_decl
1008 def _check_initial_values(self, dtype, values):
1010 Check that the supplied values are consistent with the requested
1011 data type. This method must be overridden in any sub-class of
1012 BaseDeclGen and is called by the BaseDeclGen constructor.
1014 :param str dtype: Fortran type.
1015 :param list values: list of values as strings.
1016 :raises RuntimeError: if the supplied values are not consistent \
1017 with the specified data type or are not \
1022 class DeclGen(BaseDeclGen):
1023 '''Generates a Fortran declaration for variables of various intrinsic
1024 types (integer, real and logical). For character variables
1025 CharDeclGen should be used.
1027 :param parent: node to which to add this declaration as a child.
1028 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
1029 :param str datatype: the (intrinsic) type for this declaration.
1030 :param list entity_decls: list of variable names to declare.
1031 :param str intent: the INTENT attribute of this declaration.
1032 :param bool pointer: whether or not this is a pointer declaration.
1033 :param str kind: the KIND attribute to use for this declaration.
1034 :param str dimension: the DIMENSION specifier (i.e. the xx in \
1036 :param bool allocatable: whether this declaration is for an \
1037 ALLOCATABLE quantity.
1038 :param bool save: whether this declaration has the SAVE attribute.
1039 :param bool target: whether this declaration has the TARGET attribute.
1040 :param initial_values: initial value to give each variable.
1041 :type initial_values: list of str with same no. of elements as \
1043 :param bool private: whether this declaration has the PRIVATE attribute \
1046 :raises RuntimeError: if datatype is not one of DeclGen.SUPPORTED_TYPES.
1050 SUPPORTED_TYPES = [
"integer",
"real",
"logical"]
1052 def __init__(self, parent, datatype="", entity_decls=None, intent="",
1053 pointer=False, kind="", dimension="", allocatable=False,
1054 save=False, target=False, initial_values=None, private=False):
1056 dtype = datatype.lower()
1059 f
"f2pygen.DeclGen.init: Only {self.SUPPORTED_TYPES} types are "
1060 f
"currently supported and you specified '{datatype}'")
1062 fort_fmt = FortranFormat(
True,
False)
1063 if dtype ==
"integer":
1064 reader = FortranStringReader(
"integer :: vanilla")
1065 reader.set_format(fort_fmt)
1066 myline = reader.next()
1067 self.
_decl_decl_decl = fparser1.typedecl_statements.Integer(parent.root,
1069 elif dtype ==
"real":
1070 reader = FortranStringReader(
"real :: vanilla")
1071 reader.set_format(fort_fmt)
1072 myline = reader.next()
1073 self.
_decl_decl_decl = fparser1.typedecl_statements.Real(parent.root, myline)
1074 elif dtype ==
"logical":
1075 reader = FortranStringReader(
"logical :: vanilla")
1076 reader.set_format(fort_fmt)
1077 myline = reader.next()
1078 self.
_decl_decl_decl = fparser1.typedecl_statements.Logical(parent.root,
1084 f
"Type '{dtype}' is in DeclGen.SUPPORTED_TYPES "
1085 f
"but not handled by constructor.")
1091 super(DeclGen, self).__init__(parent=parent, datatype=datatype,
1092 entity_decls=entity_decls,
1093 intent=intent, pointer=pointer,
1094 dimension=dimension,
1095 allocatable=allocatable, save=save,
1097 initial_values=initial_values,
1100 def _check_initial_values(self, dtype, values):
1102 Check that the supplied values are consistent with the requested
1103 data type. Note that this checking is fairly basic and does not
1104 support a number of valid Fortran forms (e.g. arithmetic expressions
1105 involving constants or parameters).
1107 :param str dtype: Fortran intrinsic type.
1108 :param list values: list of values as strings.
1109 :raises RuntimeError: if the supplied values are not consistent \
1110 with the specified data type or are not \
1113 from fparser.two.pattern_tools
import abs_name, \
1114 abs_logical_literal_constant, abs_signed_int_literal_constant, \
1115 abs_signed_real_literal_constant
1116 if dtype ==
"logical":
1119 if not abs_logical_literal_constant.match(val)
and \
1120 not abs_name.match(val):
1122 f
"Initial value of '{val}' for a logical variable is "
1123 f
"invalid or unsupported")
1124 elif dtype ==
"integer":
1127 if not abs_signed_int_literal_constant.match(val)
and \
1128 not abs_name.match(val):
1130 f
"Initial value of '{val}' for an integer variable is "
1131 f
"invalid or unsupported")
1132 elif dtype ==
"real":
1135 if not abs_signed_real_literal_constant.match(val)
and \
1136 not abs_name.match(val):
1138 f
"Initial value of '{val}' for a real variable is "
1139 f
"invalid or unsupported")
1144 f
"unsupported type '{dtype}' - should be "
1145 f
"one of {DeclGen.SUPPORTED_TYPES}")
1150 Generates a Fortran declaration for character variables.
1152 :param parent: node to which to add this declaration as a child.
1153 :type parent: :py:class:`psyclone.f2pygen.BaseGen`.
1154 :param list entity_decls: list of variable names to declare.
1155 :param str intent: the INTENT attribute of this declaration.
1156 :param bool pointer: whether or not this is a pointer declaration.
1157 :param str kind: the KIND attribute to use for this declaration.
1158 :param str dimension: the DIMENSION specifier (i.e. the xx in \
1160 :param bool allocatable: whether this declaration is for an \
1161 ALLOCATABLE quantity.
1162 :param bool save: whether this declaration has the SAVE attribute.
1163 :param bool target: whether this declaration has the TARGET attribute.
1164 :param str length: expression to use for the (len=xx) selector.
1165 :param initial_values: list of initial values, one for each variable. \
1166 Each of these can be either a variable name or a literal, quoted \
1167 string (e.g. "'hello'"). Default is None.
1168 :type initial_values: list of str with same no. of elements as entity_decls
1169 :param bool private: whether this declaration has the PRIVATE attribute.
1172 def __init__(self, parent, entity_decls=None, intent="",
1173 pointer=False, kind="", dimension="", allocatable=False,
1174 save=False, target=False, length="", initial_values=None,
1177 reader = FortranStringReader(
1178 "character(len=vanilla_len) :: vanilla")
1179 reader.set_format(FortranFormat(
True,
False))
1180 myline = reader.next()
1181 self.
_decl_decl_decl = fparser1.typedecl_statements.Character(parent.root,
1184 self.
_decl_decl_decl.selector = (length, kind)
1186 super(CharDeclGen, self).__init__(parent=parent,
1187 datatype=
"character",
1188 entity_decls=entity_decls,
1189 intent=intent, pointer=pointer,
1190 dimension=dimension,
1191 allocatable=allocatable, save=save,
1193 initial_values=initial_values,
1196 def _check_initial_values(self, _, values):
1198 Check that initial values provided for a Character declaration are
1200 :param _: for consistency with base-class interface.
1201 :param list values: list of strings containing initial values.
1202 :raises RuntimeError: if any of the supplied initial values is not \
1203 valid for a Character declaration.
1205 from fparser.two.pattern_tools
import abs_name
1210 if not abs_name.match(val):
1211 if not ((val.startswith(
"'")
and val.endswith(
"'"))
or
1212 (val.startswith(
'"')
and val.endswith(
'"'))):
1214 f
"Initial value of '{val}' for a character variable "
1215 f
"is invalid or unsupported")
1220 Generates a Fortran declaration for variables of a derived type.
1222 :param parent: node to which to add this declaration as a child.
1223 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
1224 :param str datatype: the type for this declaration.
1225 :param list entity_decls: list of variable names to declare.
1226 :param str intent: the INTENT attribute of this declaration.
1227 :param bool pointer: whether or not this is a pointer declaration.
1228 :param str dimension: the DIMENSION specifier (i.e. the xx in \
1230 :param bool allocatable: whether this declaration is for an \
1231 ALLOCATABLE quantity.
1232 :param bool save: whether this declaration has the SAVE attribute.
1233 :param bool target: whether this declaration has the TARGET attribute.
1234 :param bool is_class: whether this is a class rather than type declaration.
1235 :param bool private: whether or not this declaration has the PRIVATE \
1236 attribute. (Defaults to False.)
1238 def __init__(self, parent, datatype="", entity_decls=None, intent="",
1239 pointer=False, dimension="", allocatable=False,
1240 save=False, target=False, is_class=False, private=False):
1242 reader = FortranStringReader(
"class(vanillatype) :: vanilla")
1244 reader = FortranStringReader(
"type(vanillatype) :: vanilla")
1245 reader.set_format(FortranFormat(
True,
False))
1246 myline = reader.next()
1248 self.
_decl_decl_decl = fparser1.typedecl_statements.Class(parent.root,
1251 self.
_decl_decl_decl = fparser1.typedecl_statements.Type(parent.root, myline)
1252 self.
_decl_decl_decl.selector = (
'', datatype)
1254 super(TypeDeclGen, self).__init__(parent=parent, datatype=datatype,
1255 entity_decls=entity_decls,
1256 intent=intent, pointer=pointer,
1257 dimension=dimension,
1258 allocatable=allocatable, save=save,
1259 target=target, private=private)
1261 def _check_initial_values(self, _type, _values):
1263 Simply here to override abstract method in base class. It is an
1264 error if we ever call it because we don't support initial values for
1265 declarations of derived types.
1267 :param str _type: the type of the Fortran variable to be declared.
1268 :param list _values: list of str containing initialisation \
1270 :raises InternalError: because specifying initial values for \
1271 variables of derived type is not supported.
1274 "This method should not have been called because initial values "
1275 "for derived-type declarations are not supported.")
1279 ''' Generate a Fortran SELECT CASE statement '''
1281 def tofortran(self, isfix=None):
1282 tab = self.get_indent_tab(isfix=isfix)
1283 type_str =
'TYPE IS'
1286 for item
in self.items:
1287 item_list.append((
' : '.join(item)).strip())
1288 type_str += f
" ( {(', '.join(item_list))} )"
1290 type_str =
'CLASS DEFAULT'
1292 type_str +=
' ' + self.name
1293 return tab + type_str
1297 ''' Generate a Fortran SELECT block '''
1300 def __init__(self, parent, expr="UNSET", typeselect=False):
1302 Construct a SelectionGen for creating a SELECT block
1304 :param parent: node to which to add this select block as a child
1305 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
1306 :param str expr: the CASE expression
1307 :param bool typeselect: whether or not this is a SELECT TYPE rather
1311 reader = FortranStringReader(
1312 "SELECT CASE (x)\nCASE (1)\nCASE DEFAULT\nEND SELECT")
1313 reader.set_format(FortranFormat(
True,
True))
1314 select_line = reader.next()
1317 end_select_line = reader.next()
1319 select = SelectType(parent.root, select_line)
1321 select = SelectCase(parent.root, select_line)
1322 endselect = EndSelect(select, end_select_line)
1324 select.content.append(endselect)
1325 BaseGen.__init__(self, parent, select)
1328 ''' Add a case to this select block '''
1335 case.items = [casenames]
1336 self.
rootroot.content.insert(0, case)
1338 for stmt
in content:
1340 self.
rootroot.content.insert(idx, stmt.root)
1343 ''' Add the default case to this select block '''
1348 self.
rootroot.content.insert(len(self.
rootroot.content)-1, case_default)
1352 ''' Create a Fortran Do loop '''
1353 def __init__(self, parent, variable_name, start, end, step=None):
1355 :param parent: the node to which to add this do loop as a child
1356 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
1357 :param str variable_name: the name of the loop variable
1358 :param str start: start value for Do loop
1359 :param str end: upper-limit of Do loop
1360 :param str step: increment to use in Do loop
1362 reader = FortranStringReader(
"do i=1,n\nend do")
1363 reader.set_format(FortranFormat(
True,
True))
1364 doline = reader.next()
1365 enddoline = reader.next()
1366 dogen = fparser1.block_statements.Do(parent.root, doline)
1367 dogen.loopcontrol = variable_name +
"=" + start +
"," + end
1368 if step
is not None:
1369 dogen.loopcontrol = dogen.loopcontrol +
"," + step
1370 enddo = fparser1.block_statements.EndDo(dogen, enddoline)
1371 dogen.content.append(enddo)
1373 BaseGen.__init__(self, parent, dogen)
1375 def add(self, content, position=None, bubble_up=False):
1376 if position
is None:
1379 if position[0] ==
"auto" and bubble_up:
1382 self.
parentparent.add(content, bubble_up=
True)
1385 if position[0] ==
"auto" or position[0] ==
"append":
1386 if (position[0] ==
"auto" and
1387 bubble_up_type(content)):
1390 self.
parentparent.add(content, bubble_up=
True)
1396 BaseGen.add(self, content,
1397 position=[
"insert", len(self.
rootroot.content)-1])
1399 BaseGen.add(self, content, position=position)
1403 ''' Generate a fortran if, then, end if statement. '''
1407 :param parent: Node to which to add this IfThen as a child
1408 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
1409 :param str clause: the condition, xx, to evaluate in the if(xx)then
1411 reader = FortranStringReader(
"if (dummy) then\nend if")
1412 reader.set_format(FortranFormat(
True,
True))
1413 ifthenline = reader.next()
1414 endifline = reader.next()
1416 my_if = fparser1.block_statements.IfThen(parent.root, ifthenline)
1418 my_endif = fparser1.block_statements.EndIfThen(my_if, endifline)
1419 my_if.content.append(my_endif)
1421 BaseGen.__init__(self, parent, my_if)
1423 def add(self, content, position=None):
1424 if position
is None:
1426 if position[0] ==
"auto" or position[0] ==
"append":
1427 if position[0] ==
"auto" and bubble_up_type(content):
1430 self.
parentparent.
add(content, bubble_up=
True)
1435 BaseGen.add(self, content,
1436 position=[
"insert", len(self.
rootroot.content)-1])
1438 BaseGen.add(self, content, position=position)
1442 ''' Generates a Fortran statement where a value is assigned to a
1443 variable quantity '''
1445 def __init__(self, parent, lhs="", rhs="", pointer=False):
1447 :param parent: the node to which to add this assignment as a child
1448 :type parent: :py:class:`psyclone.f2pygen.BaseGen`
1449 :param str lhs: the LHS of the assignment expression
1450 :param str rhs: the RHS of the assignment expression
1451 :param bool pointer: whether or not this is a pointer assignment
1454 reader = FortranStringReader(
"lhs=>rhs")
1456 reader = FortranStringReader(
"lhs=rhs")
1457 reader.set_format(FortranFormat(
True,
True))
1458 myline = reader.next()
1460 self.
_assign_assign = fparser1.statements.PointerAssignment(parent.root,
1463 self.
_assign_assign = fparser1.statements.Assignment(parent.root, myline)
1464 self.
_assign_assign.expr = rhs
1465 self.
_assign_assign.variable = lhs
1466 BaseGen.__init__(self, parent, self.
_assign_assign)
def __init__(self, parent, content, mold=None)
def __init__(self, parent, lhs="", rhs="", pointer=False)
def _check_initial_values(self, dtype, values)
def last_declaration(self)
def start_parent_loop(self, debug=False)
def add(self, new_object, position=None)
def __init__(self, parent, name="", args=None)
def __init__(self, parent, content)
def __init__(self, parent, variable_name, start, end, step=None)
def __init__(self, parent, clause)
def add(self, content, position=None)
def __init__(self, parent)
def add_raw_subroutine(self, content)
def add(self, content, position=None, bubble_up=False)
def _skip_imp_none_and_comments(self, index)
def _skip_use_and_comments(self, index)
def addcase(self, casenames, content=None)
def __init__(self, parent, expr="UNSET", typeselect=False)
def __init__(self, parent, name="", args=None, implicitnone=False)
def __init__(self, parent, name="", only=False, funcnames=None)