66 """Base classes and exception handling for Fortran parser. 87 EXTENSIONS = [
"x-format"]
95 EXTENSIONS += [
"cray-pointer"]
104 EXTENSIONS += [
"hollerith"]
110 EXTENSIONS += [
"dollar-descriptor"]
114 """Base class exception for fparser. This allows an external tool to 115 capture all exceptions if required. 117 :param str info: a string giving contextual error information. 121 def __init__(self, info):
122 Exception.__init__(self, info)
126 """An exception indicating that a particular rule implemented by a 127 class does not match the provided string. It does not necessary 128 mean there is an error as another rule may match. This exception 129 is used internally so should never be visible externally. 134 class FortranSyntaxError(FparserException):
135 """An exception indicating that fparser believes the provided code to 136 be invalid Fortran. Also returns information about the location of 137 the error if that information is available. 139 :param reader: input string or reader where the error took \ 140 place. This is used to provide line number and line content \ 142 :type reader: str or :py:class:`FortranReaderBase` 143 :param str info: a string giving contextual error information. 147 def __init__(self, reader, info):
148 output =
"at unknown location " 149 if isinstance(reader, FortranReaderBase):
150 output =
"at line {0}\n>>>{1}\n".format(
151 reader.linecount, reader.source_lines[reader.linecount - 1]
154 output +=
"{0}".format(info)
155 FparserException.__init__(self, output)
159 """An exception indicating that an unexpected error has occured in the 162 :param str info: a string giving contextual error information. 166 def __init__(self, info):
167 new_info =
"'{0}'. Please report this to the " "authors.".format(info)
168 FparserException.__init__(self, new_info)
172 """An exception indicating that a syntax error has been found by the 173 parser. This is used instead of `FortranSyntaxError` when the 174 reader object is not available. 179 def show_result(func):
182 def new_func(cls, string, **kws):
183 r = func(cls, string, **kws)
184 if r
is not None and isinstance(r, StmtBase):
185 print(
"%s(%r) -> %r" % (cls.__name__, string, str(r)))
197 """Mixin class to provide rich comparison operators. 199 This mixin provides a set of rich comparison operators. Each class using 200 this mixin has to provide a _cmpkey() method that returns a key of objects 201 that can be compared. 203 See also http://python3porting.com/preparing.html#richcomparisons 208 def _compare(self, other, method):
209 """Call the method, if other is able to be used within it. 211 :param object other: The other object to compare with 213 :param method: The method to call to compare self and other. 214 :type method: LambdaType 215 :return: NotImplemented, when the comparison for the given type 216 combination can't be performed. 217 :rtype: :py:type:`NotImplementedType` 223 return method(self._cmpkey(), other._cmpkey())
224 except (AttributeError, TypeError):
230 return NotImplemented
232 def __lt__(self, other):
233 return self.
_compare(other,
lambda s, o: s < o)
235 def __le__(self, other):
236 return self.
_compare(other,
lambda s, o: s <= o)
238 def __eq__(self, other):
239 return self.
_compare(other,
lambda s, o: s == o)
241 def __ge__(self, other):
242 return self.
_compare(other,
lambda s, o: s >= o)
244 def __gt__(self, other):
245 return self.
_compare(other,
lambda s, o: s > o)
247 def __ne__(self, other):
248 return self.
_compare(other,
lambda s, o: s != o)
252 """This class imports a set of fparser.two dependencies that can not 253 be imported during the Python Import time because they have a circular 254 dependency with this file. 256 They are imported once when the Fortran2003 is already processed by 257 calling the import_now() method. 259 The alternative is to have the equivalent top-level imports in the 260 Base.__new__ method, but this method is in the parser critical path and 261 is best to keep expensive operations out of it. 266 """Execute the Import of Fortran2003 dependencies.""" 272 Masked_Elsewhere_Stmt,
276 End_Select_Type_Stmt,
281 add_comments_includes_directives,
285 DynamicImport.Else_If_Stmt = Else_If_Stmt
286 DynamicImport.Else_Stmt = Else_Stmt
287 DynamicImport.End_If_Stmt = End_If_Stmt
288 DynamicImport.Masked_Elsewhere_Stmt = Masked_Elsewhere_Stmt
289 DynamicImport.Elsewhere_Stmt = Elsewhere_Stmt
290 DynamicImport.End_Where_Stmt = End_Where_Stmt
291 DynamicImport.Type_Guard_Stmt = Type_Guard_Stmt
292 DynamicImport.End_Select_Type_Stmt = End_Select_Type_Stmt
293 DynamicImport.Case_Stmt = Case_Stmt
294 DynamicImport.End_Select_Stmt = End_Select_Stmt
295 DynamicImport.Comment = Comment
296 DynamicImport.Include_Stmt = Include_Stmt
297 DynamicImport.C99Preprocessor = C99Preprocessor
298 DynamicImport.add_comments_includes_directives = (
299 add_comments_includes_directives
306 def _set_parent(parent_node, items):
307 """ Recursively set the parent of all of the elements 308 in the list that are a sub-class of Base. (Recursive because 309 sometimes the list of elements itself contains a list or tuple.) 311 :param parent_node: the parent of the nodes listed in `items`. 312 :type parent_node: sub-class of :py:class:`fparser.two.utils.Base` 313 :param items: list or tuple of nodes for which to set the parent. 314 :type items: list or tuple of :py:class:`fparser.two.utils.Base` \ 315 or `str` or `list` or `tuple` or NoneType. 319 if isinstance(item, Base):
322 item.parent = parent_node
323 elif isinstance(item, (list, tuple)):
324 _set_parent(parent_node, item)
328 """ Base class for Fortran 2003 syntax rules. 330 All Base classes have the following attributes: 331 self.string - original argument to construct a class instance, its type \ 332 is either str or FortranReaderBase. 333 self.item - Line instance (holds label) or None. 335 :param type cls: the class of object to create. 336 :param string: (source of) Fortran string to parse. 337 :type string: `str` or \ 338 :py:class:`fparser.common.readfortran.FortranReaderBase` 339 :param parent_cls: the parent class of this object. 340 :type parent_cls: `type` 349 def __init__(self, string, parent_cls=None):
354 def __new__(cls, string, parent_cls=None):
356 if parent_cls
is None:
358 elif cls
not in parent_cls:
359 parent_cls.append(cls)
362 match = getattr(cls,
"match")
if hasattr(cls,
"match")
else None 365 isinstance(string, FortranReaderBase)
367 and not issubclass(cls, BlockBase)
370 item = reader.get_item()
373 if isinstance(item, readfortran.Comment):
379 obj = item.parse_line(cls, parent_cls)
384 reader.put_item(item)
394 result = cls.match(string)
395 except NoMatchError
as msg:
396 if str(msg) ==
"%s: %r" % (cls.__name__, string):
400 if isinstance(result, tuple):
401 obj = object.__new__(cls)
405 _set_parent(obj, result)
406 if hasattr(cls,
"init"):
409 if isinstance(result, Base):
414 for subcls
in Base.subclasses.get(cls.__name__, []):
415 if subcls
in parent_cls:
418 obj = subcls(string, parent_cls=parent_cls)
424 raise AssertionError(repr(result))
426 if isinstance(string, FortranReaderBase):
428 for index
in range(string.linecount):
435 if string.source_lines[index].strip():
444 line = string.source_lines[string.linecount - 1]
445 errmsg = f
"at line {string.linecount}\n>>>{line}\n" 447 errmsg = f
"{cls.__name__}: '{string}'" 452 Gets the node at the root of the parse tree to which this node belongs. 454 :returns: the node at the root of the parse tree. 455 :rtype: :py:class:`fparser.two.utils.Base` 459 while current.parent:
460 current = current.parent
465 """Return an iterable containing the immediate children of this node in 468 If this node represents an expression then its children are 469 contained in a tuple which is immutable. Therefore, the 470 manipulation of the children of such a node must be done by 471 replacing the `items` property of the node directly rather than via the 472 objects returned by this method. 474 :returns: the immediate children of this node. 475 :rtype: list or tuple containing zero or more of \ 476 :py:class:`fparser.two.utils.Base` or NoneType or str 479 child_list = getattr(self,
"content",
None)
480 if child_list
is None:
481 child_list = getattr(self,
"items", [])
486 Store the supplied list of nodes in the `items` list of this node. 488 :param items: the children of this node. 489 :type items: tuple of :py:class:`fparser.two.utils.Base` 495 return "%s(%s)" % (self.__class__.__name__,
", ".join(map(repr, self.
items)))
504 """Provides a key of objects to be used for comparing.""" 509 Produce the Fortran representation of this Comment. 511 :param str tab: characters to pre-pend to output. 512 :param bool isfix: whether or not this is fixed-format code. 514 :returns: Fortran representation of this comment. 519 return tab + this_str
524 def restore_reader(self, reader):
525 reader.put_item(self.item)
530 Base class for matching all block constructs. 532 <block-base> = [ <startcls> ] 548 match_name_classes=(),
549 enable_do_label_construct_hook=
False,
550 enable_if_construct_hook=
False,
551 enable_where_construct_hook=
False,
553 strict_match_names=
False,
556 Checks whether the content in reader matches the given 557 type of block statement (e.g. DO..END DO, IF...END IF etc.) 559 :param type startcls: the class marking the beginning of the block 560 :param list subclasses: list of classes that can be children of \ 562 :param type endcls: the class marking the end of the block. 563 :param reader: content to check for match. 564 :type reader: str or instance of :py:class:`FortranReaderBase` 565 :param bool match_labels: whether or not the statement terminating \ 566 the block must have a label that matches the opening statement. \ 568 :param bool match_names: TBD 569 :param tuple match_name_classes: TBD 570 :param bool enable_do_label_construct_hook: TBD 571 :param bool enable_if_construct_hook: TBD 572 :param bool enable_where_construct_hook: TBD 573 :param bool strict_order: whether to enforce the order of the \ 575 :param bool strict_match_names: if start name present, end name \ 576 must exist and match. 578 :return: instance of startcls or None if no match is found 587 assert isinstance(reader, FortranReaderBase), repr(reader)
590 if startcls
is not None:
592 DynamicImport.add_comments_includes_directives(content, reader)
595 obj = startcls(reader)
602 for obj
in reversed(content):
603 obj.restore_reader(reader)
605 if startcls
in SYMBOL_TABLES.scoping_unit_classes:
610 table_name = str(obj.children[1])
611 SYMBOL_TABLES.enter_scope(table_name)
614 start_idx = len(content)
617 if hasattr(obj,
"get_start_label")
and enable_do_label_construct_hook:
618 start_label = obj.get_start_label()
620 start_name = obj.get_start_name()
623 classes = subclasses + [di.Comment, di.Include_Stmt]
626 getattr(di.C99Preprocessor, cls_name)
627 for cls_name
in di.C99Preprocessor.CPP_CLASS_NAMES
629 classes += cpp_classes
630 if endcls
is not None:
632 endcls_all = tuple([endcls] + endcls.subclasses[endcls.__name__])
640 while i < len(classes):
641 if enable_do_label_construct_hook:
644 obj = startcls(reader)
645 if obj
is not None and hasattr(obj,
"get_start_label"):
646 if start_label == obj.get_start_label():
649 obj.restore_reader(reader)
666 if match_names
and isinstance(obj, match_name_classes):
667 end_name = obj.get_end_name()
668 if end_name
and not start_name:
671 f
"Name '{end_name}' has no corresponding starting name",
676 and end_name.lower() != start_name.lower()
679 reader, f
"Expecting name '{start_name}', got '{end_name}'" 682 if endcls
is not None and isinstance(obj, endcls_all):
684 start_label, end_label = (
685 content[start_idx].get_start_label(),
686 content[-1].get_end_label(),
688 if start_label != end_label:
691 start_name, end_name = (
692 content[start_idx].get_start_name(),
693 content[-1].get_end_name(),
696 if end_name
and not start_name:
699 f
"Name '{end_name}' has no corresponding starting name",
701 elif strict_match_names
and start_name
and not end_name:
703 reader, f
"Expecting name '{start_name}' but none given" 708 and (start_name.lower() != end_name.lower())
712 f
"Expecting name '{start_name}', got '{end_name}'",
720 if enable_if_construct_hook:
721 if isinstance(obj, di.Else_If_Stmt):
725 if isinstance(obj, (di.Else_Stmt, di.End_If_Stmt)):
727 enable_if_construct_hook =
False 728 if enable_where_construct_hook:
729 if isinstance(obj, di.Masked_Elsewhere_Stmt):
731 if isinstance(obj, (di.Elsewhere_Stmt, di.End_Where_Stmt)):
732 enable_where_construct_hook =
False 735 except FortranSyntaxError
as err:
737 if startcls
in SYMBOL_TABLES.scoping_unit_classes:
738 SYMBOL_TABLES.exit_scope()
740 SYMBOL_TABLES.remove(table_name)
743 if startcls
in SYMBOL_TABLES.scoping_unit_classes:
744 SYMBOL_TABLES.exit_scope()
746 if not had_match
or endcls
and not found_end:
749 if endcls
is not None:
750 if startcls
in SYMBOL_TABLES.scoping_unit_classes:
752 SYMBOL_TABLES.remove(table_name)
753 for obj
in reversed(content):
754 obj.restore_reader(reader)
765 if startcls
is not None and endcls
is not None:
767 start_stmt = content[start_idx]
768 end_stmt = content[-1]
770 isinstance(end_stmt, endcls_all)
771 and hasattr(end_stmt,
"get_name")
772 and hasattr(start_stmt,
"get_name")
774 if end_stmt.get_name()
is not None:
776 start_stmt.get_name().string.lower()
777 != end_stmt.get_name().string.lower()
779 end_stmt.item.reader.error(
780 "expected <%s-name> is %s but got %s. Ignoring." 782 end_stmt.get_type().lower(),
783 start_stmt.get_name(),
791 Initialise the `content` attribute with the list of child nodes. 793 :param content: list of nodes that are children of this one. 794 :type content: list of :py:class:`fparser.two.utils.Base` or NoneType 800 """Provides a key of objects to be used for comparing.""" 807 return "%s(%s)" % (self.__class__.__name__,
", ".join(map(repr, self.
content)))
811 Create a string containing the Fortran representation of this class 813 :param str tab: indent to prefix to code. 814 :param bool isfix: whether or not to generate fixed-format code. 816 :return: Fortran representation of this class. 823 if isinstance(end, EndStmtBase):
825 if start
is not None:
826 mylist.append(start.tofortran(tab=tab, isfix=isfix))
827 for item
in self.
content[1:-1]:
828 mylist.append(item.tofortran(tab=tab + extra_tab, isfix=isfix))
830 mylist.append(end.tofortran(tab=tab, isfix=isfix))
831 return "\n".join(mylist)
833 def restore_reader(self, reader):
834 for obj
in reversed(self.
content):
835 obj.restore_reader(reader)
840 Match one or more fparser2 rules separated by a defined separator. 842 sequence-base is obj [sep obj ] ... 847 def match(separator, subcls, string):
848 """Match one or more 'subcls' fparser2 rules in the string 'string' 849 separated by 'separator'. 851 :param str separator: the separator used to split the supplied \ 853 :param subcls: an fparser2 object representing the rule that \ 855 :type subcls: subclass of :py:class:`fparser.two.utils.Base` 856 :param str string: the input string to match. 858 :returns: a tuple containing 1) the separator and 2) the \ 859 matched objects in a tuple, or None if there is no match. 860 :rtype: (str, (Subclass of \ 861 :py:class:`fparser.two.utils.Base`)) or NoneType 863 :raises InternalError: if the separator or string arguments \ 864 are not the expected type. 865 :raises InternalError: if the separator is white space. 868 if not isinstance(separator, str):
870 f
"SequenceBase class match method argument separator expected " 871 f
"to be a string but found '{type(separator)}'." 873 if not isinstance(string, str):
875 f
"SequenceBase class match method argument string expected to " 876 f
"be a string but found '{type(string)}'." 881 "SequenceBase class match method argument separator cannot " 885 line, repmap = string_replace_map(string)
886 splitted = line.split(separator)
891 lst = [subcls(repmap(entry.strip()))
for entry
in splitted]
893 return separator, tuple(lst)
895 def init(self, separator, items):
896 """Store the result of the match method if the match is successful. 898 :param str separator: the separator used to split the supplied string. 899 :param items: a tuple containing the matched objects. 900 :type items: tuple(Subclass of :py:class:`fparser.two.utils.Base`) 908 :returns: The Fortran representation of this object as a string. 918 sep =
" " + sep +
" " 919 return sep.join(map(str, self.
items))
923 :returns: The Python representation of this object as a string. 927 return "{0}('{1}', {2})".format(
944 <unary-op-base> = <unary-op> <rhs> 948 return "%s %s" % tuple(self.
items)
951 def match(op_pattern, rhs_cls, string, exclude_op_pattern=None):
952 m = op_pattern.match(string)
955 rhs = string[m.end() :].lstrip()
958 op = string[: m.end()].rstrip().upper()
959 if exclude_op_pattern
is not None:
960 if exclude_op_pattern.match(op):
962 return op, rhs_cls(rhs)
966 """binary-op-base is lhs op rhs 968 Splits the input text into text to the left of the matched 969 operator and text to the right of the matched operator and tries 970 to match the lhs text with the supplied lhs class rule and the rhs 971 text with the supplied rhs class rule. 977 lhs_cls, op_pattern, rhs_cls, string, right=True, exclude_op_pattern=None
979 """Matches the binary-op-base rule. 981 If the operator defined by argument 'op_pattern' is found in 982 the string provided in argument 'string' then the text to the 983 left-hand-side of the operator is matched with the class rule 984 provided in the 'lhs_cls' argument and the text to the 985 right-hand-side of the operator is matched with the class rule 986 provided in the 'rhs_cls' argument. 988 If the optional 'right' argument is set to true (the default) 989 then, in the case where the pattern matches multiple times in 990 the input string, the right-most match will be chosen. If the 991 'right' argument is set to false then the left-most match will 994 if a pattern is provided to the optional 'exclude_op_pattern' 995 argument then there will be no match if the pattern matched by 996 the 'op_pattern' argument also matches this pattern. The 997 default (None) does nothing. 999 :param lhs_cls: an fparser2 object representing the rule that \ 1000 should be matched to the lhs text. 1001 :type lhs_cls: subclass of :py:class:`fparser.two.utils.Base` 1002 :param op_pattern: the pattern to match. 1003 :type op_pattern: `str` or \ 1004 :py:class:`fparser.two.pattern_tools.Pattern` 1005 :param rhs_cls: an fparser2 object representing the rule that \ 1006 should be matched to the rhs text. 1007 :type rhs_cls: subclass of :py:class:`fparser.two.utils.Base` 1008 :param str string: the string to match with the pattern and \ 1010 :param bool right: in the case where there are multiple \ 1011 matches to the pattern in the string this optional \ 1012 argument specifies whether the righmost pattern match \ 1013 should be chosen (True, the default) or whether the \ 1014 leftmost pattern should be chosen (False). 1015 :param exclude_op_pattern: optional argument which specifies a \ 1016 particular subpattern to exclude from the match. Defaults \ 1017 to None which means there is no subpattern. 1018 :type exclude_op_pattern: :py:class:`fparser.two.pattern_tools.Pattern` 1020 :returns: a tuple containing the matched lhs, the operator and \ 1021 the matched rhs of the input string or None if there is \ 1023 :rtype: (:py:class:`fparser.two.utils.Base`, str, \ 1024 :py:class:`fparser.two.utils.Base`) or NoneType 1027 line, repmap = string_replace_map(string)
1029 if isinstance(op_pattern, str):
1031 text_split = line.rsplit(op_pattern, 1)
1033 text_split = line.split(op_pattern, 1)
1034 if len(text_split) != 2:
1036 lhs, rhs = text_split[0].rstrip(), text_split[1].lstrip()
1040 text_split = op_pattern.rsplit(line)
1042 text_split = op_pattern.lsplit(line)
1043 if not text_split
or len(text_split) != 3:
1045 lhs, oper, rhs = text_split
1049 if not lhs
or not rhs:
1051 if exclude_op_pattern
and exclude_op_pattern.match(oper):
1059 rhs_obj = rhs_cls(repmap(rhs))
1060 lhs_obj = lhs_cls(repmap(lhs))
1064 lhs_obj = lhs_cls(repmap(lhs))
1065 rhs_obj = rhs_cls(repmap(rhs))
1067 return (lhs_obj, oper.replace(
" ",
""), rhs_obj)
1070 """Return the string representation of this object. Uses join() which 1071 is efficient and can make a big performance difference for 1072 complex expressions. 1074 :returns: the string representation of this object. 1078 return " ".join([str(self.
items[0]), str(self.
items[1]), str(self.
items[2])])
1084 <separator-base> = [ <lhs> ] : [ <rhs> ] 1088 def match(lhs_cls, rhs_cls, string, require_lhs=False, require_rhs=False):
1089 line, repmap = string_replace_map(string)
1092 lhs, rhs = line.split(
":", 1)
1095 lhs_obj, rhs_obj =
None,
None 1099 lhs_obj = lhs_cls(repmap(lhs))
1105 rhs_obj = rhs_cls(repmap(rhs))
1108 return lhs_obj, rhs_obj
1112 if self.
items[0]
is not None:
1113 s +=
"%s :" % (self.
items[0])
1116 if self.
items[1]
is not None:
1117 s +=
" %s" % (self.
items[1])
1124 keyword-value-base is [ lhs = ] rhs 1128 R215 keyword is name. 1133 def match(lhs_cls, rhs_cls, string, require_lhs=True, upper_lhs=False):
1135 Attempts to match the supplied `string` with `lhs_cls` = `rhs_cls`. 1136 If `lhs_cls` is a str then it is compared with the content to the 1137 left of the first '=' character in `string`. If that content is a 1138 valid Fortran name but does *not* match `lhs_cls` then the match 1139 fails, irrespective of the setting of `require_lhs`. 1141 :param lhs_cls: list, tuple or single value of classes to attempt to \ 1142 match LHS against (in order), or string containing \ 1144 :type lhs_cls: names of classes deriving from `:py:class:Base` or str 1145 :param rhs_cls: name of class to match RHS against. 1146 :type rhs_cls: name of a class deriving from `:py:class:Base` 1147 :param str string: text to be matched. 1148 :param bool require_lhs: whether the expression to be matched must \ 1149 contain a LHS that is assigned to. 1150 :param bool upper_lhs: whether or not to convert the LHS of the \ 1151 matched expression to upper case. 1153 :return: instances of the classes representing quantities on the LHS \ 1154 and RHS (LHS is optional) or None if no match is found. 1155 :rtype: 2-tuple of objects or NoneType 1158 if require_lhs
and "=" not in string:
1160 if isinstance(lhs_cls, (list, tuple)):
1162 obj = KeywordValueBase.match(
1163 cls, rhs_cls, string, require_lhs=require_lhs, upper_lhs=upper_lhs
1172 pieces = string.split(
"=", 1)
1174 if len(pieces) == 2:
1177 lhs = pieces[0].strip()
1178 if isinstance(lhs_cls, str):
1193 rhs = string.strip()
1195 rhs = pieces[-1].strip()
1203 if self.
items[0]
is None:
1204 return str(self.
items[1])
1205 return "%s = %s" % tuple(self.
items)
1210 bracket-base is left-bracket something right-bracket. 1212 This class is able to cope with nested brackets as long as they 1213 are correctly nested. Brackets in strings are ignored. 1215 The 'something' can be specified as being optional. 1220 def match(brackets, cls, string, require_cls=True):
1221 """A generic match method for all types of bracketed 1224 :param str brackets: the format of the left and right brackets \ 1225 provided as a string, for example '()' 1226 :param cls: the class to match the content within the brackets \ 1227 :type cls: subclass of :py:class:`fparser.two.utils.Base` 1228 :param str string: the content to match 1229 :param bool require_cls: whether the class and associated \ 1230 content is mandatory (True) or optional (False). The default \ 1232 :return: None if there is no match, otherwise a tuple with the \ 1233 first and third entries being strings containing the left and \ 1234 right brackets respectively and the second entry being either \ 1235 None or an instance of the class provided as the second \ 1237 :rtype: 'NoneType', ( `str`, `NoneType`, `str`) or ( `str`, \ 1241 if not cls
and require_cls:
1245 string_strip = string.strip()
1248 brackets_nospc = brackets.replace(
" ",
"")
1249 if not brackets_nospc:
1251 if len(brackets_nospc) % 2 == 1:
1254 bracket_len = len(brackets_nospc) // 2
1255 left = brackets_nospc[:bracket_len]
1256 right = brackets_nospc[-bracket_len:]
1257 if len(string_strip) < bracket_len * 2:
1259 if not (string_strip.startswith(left)
and string_strip.endswith(right)):
1263 line = string_strip[bracket_len:-bracket_len].lstrip()
1264 if (
not line
and cls
and require_cls)
or (line
and not cls):
1266 if not line
and (
not cls
or not require_cls):
1267 return left,
None, right
1268 return left, cls(line), right
1272 :raises InternalError: if the internal items list variable is \ 1273 not the expected size. 1274 :raises InternalError: if the first element of the internal \ 1275 items list is None or is an empty string. 1278 if len(self.
items) != 3:
1280 "Class BracketBase method tostr() has '{0}' items, " 1281 "but expecting 3.".format(len(self.
items))
1283 if not self.
items[0]:
1285 "Class BracketBase method tostr(). 'Items' entry 0 " 1286 "should be a string containing the left hand bracket " 1287 "but it is empty or None" 1289 if not self.
items[2]:
1291 "Class BracketBase method tostr(). 'Items' entry 2 " 1292 "should be a string containing the right hand bracket " 1293 "but it is empty or None" 1295 if self.
items[1]
is None:
1296 return "{0}{1}".format(self.
items[0], self.
items[2])
1297 return "{0}{1}{2}".format(self.
items[0], self.
items[1], self.
items[2])
1303 <number-base> = <number> [ _ <kind-param> ] 1307 def match(number_pattern, string):
1308 m = number_pattern.match(string.replace(
" ",
""))
1312 return d[
"value"].upper(), d.get(
"kind_param")
1315 if self.
items[1]
is None:
1316 return str(self.
items[0])
1317 return "%s_%s" % tuple(self.
items)
1320 """Provides a key of objects to be used for comparing.""" 1321 return self.
items[0]
1327 <call-base> = <lhs> ( [ <rhs> ] ) 1331 def match(lhs_cls, rhs_cls, string, upper_lhs=False, require_rhs=False):
1332 if not string.endswith(
")"):
1334 line, repmap = string_replace_map(string)
1338 lhs = line[:i].rstrip()
1342 rhs = line[i + 1 : j].strip()
1343 if line[j + 1 :].lstrip():
1349 if isinstance(lhs_cls, str):
1355 if isinstance(rhs_cls, str):
1366 if self.
items[1]
is None:
1367 return "%s()" % (self.
items[0])
1368 return "%s(%s)" % (self.
items[0], self.
items[1])
1374 <CALL-base> = <LHS> ( [ <rhs> ] ) 1378 def match(lhs_cls, rhs_cls, string, require_rhs=False):
1379 return CallBase.match(
1380 lhs_cls, rhs_cls, string, upper_lhs=
True, require_rhs=require_rhs
1387 <string-base> = <xyz> 1395 def match(pattern, string):
1396 if isinstance(pattern, (list, tuple)):
1398 obj = StringBase.match(p, string)
1402 if isinstance(pattern, str):
1403 if len(pattern) == len(string)
and pattern == string:
1406 if pattern.match(string):
1410 def init(self, string):
1417 return "%s(%r)" % (self.__class__.__name__, self.
string)
1420 """Provides a key of objects to be used for comparing.""" 1425 """STRINGBase matches an upper case version of the input string with 1426 another a pattern (typically taken from pattern_tools.py) and 1427 returns the string in upper case if there is a match. 1433 """Matches an input string with a specified pattern. Casts the string 1434 to upper case before performing a match and, if there is a 1435 match, returns the string in upper case. 1437 The pattern can be a regular expression, a string, a list or a 1438 tuple. If the input pattern is a regular expression or a 1439 string, a direct equivalence is performed. If the input pattern is a 1440 list or a tuple, then all of the contents of the list 1441 or tuple are searched for a match (by recursing). The list or tuple may 1442 contain regular expressions, strings, lists or tuples. This 1443 functionality can be used to recurse down a tree of lists and 1444 or tuples until regular expressions or strings are found (at 1445 the leaves of the tree) on which to match. The patterns used 1446 to match in fparser can be found in patterns_tools.py. These 1447 make use of the pattern class, whose match method behaves like 1448 a regular expression. For example: 1450 from fparser.two import pattern_tools 1451 pattern = pattern_tools.intrinsic_type_name 1452 result = STRINGBase.match(pattern, "logical") 1454 :param pattern: the pattern to match 1455 :type pattern: `list`, `tuple`, `str` or an `re` expression 1456 :param str string: the string to match with the pattern 1457 :return: None if there is no match, or a tuple containing the \ 1458 matched string in upper case. 1459 :rtype: `NoneType` or ( `str` ) 1464 if not isinstance(string, str):
1466 f
"Supplied string should be of type str, but found {type(string)}" 1468 if isinstance(my_pattern, (list, tuple)):
1469 for child
in my_pattern:
1470 result = STRINGBase.match(child, string)
1474 string_upper = string.upper()
1475 if isinstance(my_pattern, str):
1476 if len(my_pattern) == len(string)
and my_pattern == string_upper:
1477 return (string_upper,)
1480 if my_pattern.match(string_upper):
1481 return (string_upper,)
1482 except AttributeError:
1484 f
"Supplied pattern should be a list, tuple, str or regular " 1485 f
"expression but found {type(my_pattern)}" 1493 [ [ <label> ] [ <construct-name> : ] ] <stmt> 1497 item : readfortran.Line 1500 def tofortran(self, tab="", isfix=None):
1503 if self.item
is not None:
1504 label = self.item.label
1505 name = self.item.name
1516 tab = tab[len(t) :]
or " " 1521 return t + tab + name +
":" + str(self)
1522 return t + tab + str(self)
1524 def get_end_label(self):
1525 return self.item.label
1531 <end-stmt-base> = END [ <stmt> [ <stmt-name>] ] 1535 def match(stmt_type, stmt_name, string, require_stmt_type=False):
1536 start = string[:3].upper()
1539 line = string[3:].lstrip()
1540 start = line[: len(stmt_type)].upper()
1542 if start.replace(
" ",
"") != stmt_type.replace(
" ",
""):
1544 line = line[len(stmt_type) :].lstrip()
1546 if require_stmt_type:
1550 if stmt_name
is None:
1552 return stmt_type, stmt_name(line)
1553 return stmt_type,
None 1555 def init(self, stmt_type, stmt_name):
1557 Initialise this EndStmtBase object. 1559 :param str stmt_type: the type of statement, e.g. 'PROGRAM'. 1560 :param stmt_name: the name associated with the statement or None. 1561 :type stmt_name: :py:class:`fparser.two.Fortran2003.Name` 1564 self.
items = [stmt_type, stmt_name]
1567 return self.
items[1]
1570 return self.
items[0]
1573 if self.
items[1]
is not None:
1574 return "END %s %s" % tuple(self.
items)
1575 if self.
items[0]
is not None:
1576 return "END %s" % (self.
items[0])
1580 return "%s(%r, %r)" % (
1581 self.__class__.__name__,
1586 def get_end_name(self):
1587 name = self.
items[1]
1588 if name
is not None:
1593 return c.isalnum()
or c ==
"_" 1597 """Base class to support situations where there is a keyword which is 1598 optionally followed by further text, potentially separated by 1601 For example 'program fred', or 'import :: a,b' 1603 WORD-cls is WORD [ [ :: ] cls ] 1608 def match(keyword, cls, string, colons=False, require_cls=False):
1609 """Checks whether the content in string matches the expected 1610 WORDClsBase format with 'keyword' providing the keyword, 'cls' 1611 providing the following text, 'colons' specifying whether an 1612 optional '::' is allowed as a separator between the keyword 1613 and cls and 'require_cls' specifying whether cls must have 1616 Note, if the optional '::' is allowed and exists in the string 1617 then 1) cls must also have content i.e. it implies 1618 `require_cls=True` and 2) white space is not required between 1619 the keyword and the '::' and the '::' and cls. 1621 The simplest form of keyword pattern is a string. However this 1622 method can also match more complex patterns as specified by 1623 the Pattern class in pattern_tools.py. As patterns can be 1624 built from combinations of other patterns (again see 1625 pattern_tool.py) this method also supports a hierarchy of 1626 lists and/or tuples of patterns. 1628 :param keyword: the pattern of the WORD to match. This can be \ 1629 a Pattern, string, list or tuple, with a list or tuple \ 1630 containing one or more Pattern, string, list or tuple. 1631 :type keyword: :py:class:`fparser.two.pattern_tools.Pattern`, \ 1632 str, tuple of str/Pattern/tuple/list or list of \ 1633 str/Pattern/tuple/list 1634 :param cls: the class to match. 1635 :type cls: a subclass of :py:class:`fparser.two.utils.Base` 1636 :param str string: Text that we are trying to match. 1637 :param bool colons: whether '::' is allowed as an optional \ 1638 separator between between WORD and cls. 1639 :param bool require_cls: whether content for cls is required \ 1642 :returns: None if there is no match or, if there is a match, a \ 1643 2-tuple containing a string matching the 'WORD' and an \ 1644 instance of 'cls' (or None if an instance of cls is not \ 1645 required and not provided). 1646 :rtype: (str, cls or NoneType) or NoneType 1649 if isinstance(keyword, (tuple, list)):
1650 for child
in keyword:
1652 obj = WORDClsBase.match(
1653 child, cls, string, colons=colons, require_cls=require_cls
1655 except NoMatchError:
1661 if isinstance(keyword, str):
1662 line = string.lstrip()
1663 if line[: len(keyword)].upper() != keyword.upper():
1665 line = line[len(keyword) :]
1666 pattern_value = keyword
1668 my_match = keyword.match(string)
1669 if my_match
is None:
1671 line = string[len(my_match.group()) :]
1672 pattern_value = keyword.value
1678 return pattern_value,
None 1679 if isalnum(line[0]):
1681 line = line.lstrip()
1683 if colons
and line.startswith(
"::"):
1685 line = line[2:].lstrip()
1687 if has_colons
or require_cls:
1690 return pattern_value,
None 1693 return pattern_value, cls(line)
1696 """Convert the class into Fortran. 1698 :return: String representation of this class without any \ 1703 if self.
items[1]
is None:
1704 return str(self.
items[0])
1705 s = str(self.
items[1])
1706 if s
and s[0]
in "(*":
1707 return "%s%s" % (self.
items[0], s)
1708 return "%s %s" % (self.
items[0], s)
1711 """Convert the class into Fortran, adding in "::". 1713 :return: String representation of this class including an \ 1718 if self.
items[1]
is None:
1719 return str(self.
items[0])
1720 return "%s :: %s" % (self.
items[0], self.
items[1])
1724 """<type-declaration-stmt> = <declaration-type-spec> [ [ , 1725 <attr-spec> ]... :: ] <entity-decl-list> 1733 def match(decl_type_spec_cls, attr_spec_list_cls, entity_decl_list_cls, string):
1734 line, repmap = string_replace_map(string)
1737 j = line[:i].find(
",")
1741 if line[:6].upper() ==
"DOUBLE":
1742 m = re.search(
r"\s[a-z_]", line[6:].lstrip(), re.I)
1745 i = m.start() + len(line) - len(line[6:].lstrip())
1747 m = re.search(
r"\s[a-z_]", line, re.I)
1751 type_spec = decl_type_spec_cls(repmap(line[:i].rstrip()))
1752 if type_spec
is None:
1754 line = line[i:].lstrip()
1755 if line.startswith(
","):
1759 attr_specs = attr_spec_list_cls(repmap(line[1:i].strip()))
1760 if attr_specs
is None:
1765 if line.startswith(
"::"):
1766 line = line[2:].lstrip()
1767 entity_decls = entity_decl_list_cls(repmap(line))
1768 if entity_decls
is None:
1770 return type_spec, attr_specs, entity_decls
1774 :returns: the text representation of this node. 1777 if self.
items[1]
is None:
1778 return f
"{self.items[0]} :: {self.items[2]}" 1779 return f
"{self.items[0]}, {self.items[1]} :: {self.items[2]}" 1782 def walk(node_list, types=None, indent=0, debug=False):
1784 Walk down the parse tree produced by fparser2. Returns a list of all 1785 nodes with the specified type(s). 1787 :param node_list: node or list of nodes from which to walk. 1788 :type node_list: (list of) :py:class:fparser.two.utils.Base 1789 :param types: type or tuple of types of Node to return. (Default is to \ 1791 :type types: type or tuple of types 1792 :param int indent: extent to which to indent debug output. 1793 :param bool debug: whether or not to write textual representation of AST \ 1795 :returns: a list of nodes 1796 :rtype: `list` of :py:class:`fparser.two.utils.Base` 1800 if not isinstance(node_list, (list, tuple)):
1801 node_list = [node_list]
1803 for child
in node_list:
1805 if isinstance(child, str):
1806 print(indent *
" " +
"child type = ", type(child), repr(child))
1808 print(indent *
" " +
"child type = ", type(child))
1809 if types
is None or isinstance(child, types):
1810 local_list.append(child)
1812 if isinstance(child, Base):
1813 local_list += walk(child.children, types, indent + 1, debug)
1814 elif isinstance(child, tuple):
1815 for component
in child:
1816 local_list += walk(component, types, indent + 1, debug)
1821 def get_child(node, node_type):
1823 Searches for the first, immediate child of the supplied node that is of 1826 :param node: the node whose children will be searched. 1827 :type node: :py:class:`fparser.two.utils.Base` 1828 :param type node_type: the class of child node to search for. 1830 :returns: the first child node of type node_type that is encountered \ 1832 :rtype: :py:class:`fparser.two.utils.Base` 1835 for child
in node.children:
1836 if isinstance(child, node_type):
def init(self, stmt_type, stmt_name)
def tofortran(self, tab="", isfix=None)
def match(separator, subcls, string)
def match(lhs_cls, op_pattern, rhs_cls, string, right=True, exclude_op_pattern=None)
def match(brackets, cls, string, require_cls=True)
def match(lhs_cls, rhs_cls, string, require_lhs=True, upper_lhs=False)
def match(keyword, cls, string, colons=False, require_cls=False)
def match(my_pattern, string)
def match(startcls, subclasses, endcls, reader, match_labels=False, match_names=False, match_name_classes=(), enable_do_label_construct_hook=False, enable_if_construct_hook=False, enable_where_construct_hook=False, strict_order=False, strict_match_names=False)
def init(self, separator, items)
def tofortran(self, tab="", isfix=None)
def _compare(self, other, method)