fparser Reference Guide  0.0.14
statements.py
1 # Modified work Copyright (c) 2017-2022 Science and Technology
2 # Facilities Council
3 # Original work Copyright (c) 1999-2008 Pearu Peterson
4 
5 # All rights reserved.
6 
7 # Modifications made as part of the fparser project are distributed
8 # under the following license:
9 
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions are
12 # met:
13 
14 # 1. Redistributions of source code must retain the above copyright
15 # notice, this list of conditions and the following disclaimer.
16 
17 # 2. Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution.
20 
21 # 3. Neither the name of the copyright holder nor the names of its
22 # contributors may be used to endorse or promote products derived from
23 # this software without specific prior written permission.
24 
25 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 
37 # --------------------------------------------------------------------
38 
39 # The original software (in the f2py project) was distributed under
40 # the following license:
41 
42 # Redistribution and use in source and binary forms, with or without
43 # modification, are permitted provided that the following conditions are met:
44 
45 # a. Redistributions of source code must retain the above copyright notice,
46 # this list of conditions and the following disclaimer.
47 # b. Redistributions in binary form must reproduce the above copyright
48 # notice, this list of conditions and the following disclaimer in the
49 # documentation and/or other materials provided with the distribution.
50 # c. Neither the name of the F2PY project nor the names of its
51 # contributors may be used to endorse or promote products derived from
52 # this software without specific prior written permission.
53 
54 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
55 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
58 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
60 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
61 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
64 # DAMAGE.
65 
66 """
67 Fortran single line statements.
68 
69 """
70 
71 import re
72 import sys
73 
74 from fparser.common.base_classes import Statement
75 
76 # Auxiliary tools
77 
78 from fparser.common.utils import (
79  split_comma,
80  specs_split_comma,
81  AnalyzeError,
82  ParseError,
83  parse_bind,
84  parse_result,
85  is_name,
86  extract_bracketed_list_items,
87 )
88 from fparser.common.utils import classes
89 
90 __all__ = [
91  "GeneralAssignment",
92  "Assignment",
93  "PointerAssignment",
94  "Assign",
95  "Call",
96  "Goto",
97  "ComputedGoto",
98  "AssignedGoto",
99  "Continue",
100  "Return",
101  "Stop",
102  "Print",
103  "Read",
104  "Read0",
105  "Read1",
106  "Write",
107  "Flush",
108  "Wait",
109  "Contains",
110  "Allocate",
111  "Deallocate",
112  "ModuleProcedure",
113  "Access",
114  "Public",
115  "Private",
116  "Close",
117  "Cycle",
118  "Backspace",
119  "Endfile",
120  "Rewind",
121  "Open",
122  "Format",
123  "Save",
124  "Data",
125  "Nullify",
126  "Use",
127  "Exit",
128  "Parameter",
129  "Equivalence",
130  "Dimension",
131  "Target",
132  "Pointer",
133  "Protected",
134  "Volatile",
135  "Value",
136  "ArithmeticIf",
137  "Intrinsic",
138  "Inquire",
139  "Sequence",
140  "External",
141  "Namelist",
142  "Common",
143  "Optional",
144  "Intent",
145  "Entry",
146  "Import",
147  "ForallStmt",
148  "SpecificBinding",
149  "GenericBinding",
150  "FinalBinding",
151  "Allocatable",
152  "Asynchronous",
153  "Bind",
154  "Else",
155  "ElseIf",
156  "Case",
157  "TypeIs",
158  "ClassIs",
159  "WhereStmt",
160  "ElseWhere",
161  "Enumerator",
162  "FortranName",
163  "Threadsafe",
164  "Depend",
165  "Check",
166  "CallStatement",
167  "CallProtoArgument",
168  "Pause",
169  "Comment",
170 ]
171 
172 
174  """
175  <statement> [ :: ] <name-list>
176  """
177 
178  def process_item(self):
179  if self.item.has_map():
180  self.isvalid = False
181  return
182  if hasattr(self, "stmtname"):
183  clsname = self.stmtname
184  else:
185  clsname = self.__class__.__name__
186  line = self.item.get_line()[len(clsname) :].lstrip()
187  if line.startswith("::"):
188  line = line[2:].lstrip()
189  self.items = items = []
190  for item in split_comma(line):
191  if not is_name(item):
192  self.isvalid = False
193  return
194  items.append(item)
195  return
196 
197  def tofortran(self, isfix=None):
198  if hasattr(self, "stmtname"):
199  clsname = self.stmtname.upper()
200  else:
201  clsname = self.__class__.__name__.upper()
202  s = ", ".join(self.items)
203  if s:
204  s = " " + s
205  return self.get_indent_tab(isfix=isfix) + clsname + s
206 
207 
208 # Execution statements
209 
210 
212  """
213  <variable> = <expr>
214  <pointer variable> => <expr>
215  """
216 
217  match = re.compile(r"\w[^=]*\s*=>?").match
218  item_re = re.compile(
219  r"(?P<variable>\w[^=]*)\s*(?P<sign>=>?)\s*(?P<expr>.*)\Z", re.I
220  ).match
221  _repr_attr_names = ["variable", "sign", "expr"] + Statement._repr_attr_names
222 
223  def process_item(self):
224  m = self.item_re(self.item.get_line())
225  if not m:
226  self.isvalid = False
227  return
228  self.sign = sign = m.group("sign")
229  if isinstance(self, Assignment) and sign != "=":
230  self.isvalid = False
231  return
232  elif isinstance(self, PointerAssignment) and sign != "=>":
233  self.isvalid = False
234  return
235  else:
236  if sign == "=>":
237  self.__class__ = PointerAssignment
238  else:
239  self.__class__ = Assignment
240  apply_map = self.item.apply_map
241  v1 = v = m.group("variable").replace(" ", "")
242  while True:
243  i = v.find(")")
244  if i == -1:
245  break
246  v = v[i + 1 :]
247  if v.startswith("(") or v.startswith(r"%"):
248  continue
249  if v:
250  self.isvalid = False
251  return
252  self.variable = apply_map(v1)
253  self.expr = apply_map(m.group("expr"))
254  return
255 
256  def tofortran(self, isfix=None):
257  return self.get_indent_tab(isfix=isfix) + "%s %s %s" % (
258  self.variable,
259  self.sign,
260  self.expr,
261  )
262 
263  def analyze(self):
264  return
265 
266 
267 class Assignment(GeneralAssignment):
268  pass
269 
270 
272  pass
273 
274 
276  """
277  ASSIGN <label> TO <int-variable-name>
278  """
279 
280  modes = ["fix77"]
281  match = re.compile(r"assign\s*\d+\s*to\s*\w+\s*\Z", re.I).match
282 
283  def process_item(self):
284  line = self.item.get_line()[6:].lstrip()
285  i = line.lower().find("to")
286  assert not self.item.has_map()
287  self.items = [line[:i].rstrip(), line[i + 2 :].lstrip()]
288  return
289 
290  def tofortran(self, isfix=None):
291  return self.get_indent_tab(isfix=isfix) + "ASSIGN %s TO %s" % (
292  self.items[0],
293  self.items[1],
294  )
295 
296  def analyze(self):
297  return
298 
299 
300 class Call(Statement):
301  """Call statement class
302  CALL <procedure-designator> [ ( [ <actual-arg-spec-list> ] ) ]
303 
304  <procedure-designator> = <procedure-name>
305  | <proc-component-ref>
306  | <data-ref> % <binding-name>
307 
308  <actual-arg-spec> = [ <keyword> = ] <actual-arg>
309  <actual-arg> = <expr>
310  | <variable>
311  | <procedure-name>
312  | <proc-component-ref>
313  | <alt-return-spec>
314  <alt-return-spec> = * <label>
315 
316  <proc-component-ref> = <variable> % <procedure-component-name>
317 
318  <variable> = <designator>
319 
320  Call instance has attributes:
321  designator
322  arg_list
323  """
324 
325  # As indicated in the specification above, a call to a subroutine
326  # that has no arguments does *not* require parentheses.
327  # e.g.:
328  # call bob
329  # is valid Fortran.
330  match = re.compile(r"call\b\s*\w([\s\w\(\)\%]*\w)?\s*(\(.*\))?\s*$", re.I).match
331 
332  def process_item(self):
333  """Parse the string containing the Call and store the
334  designator and list of arguments (if any)"""
335  item = self.item
336  apply_map = item.apply_map
337  line = item.get_line()[4:].strip()
338  # Work backwards from the end of the line in order to allow
339  # for code like:
340  # call my_type(1)%my_function(arg(2))
341  # The following code will also support something like:
342  # call my_type(1)%my_function("(")
343  # because fparser will previously have identified the "(" as a
344  # string expression and replaced it with something like
345  # "F2PY_EXPR_TUPLE_2"
346  if line.endswith(")"):
347  # Work back down the line until we find the matching '('
348  i = len(line) - 2
349  nopen = 1
350  while i > 0:
351  if line[i] == ")":
352  nopen += 1
353  elif line[i] == "(":
354  nopen -= 1
355  if nopen == 0:
356  # Have found the matching '(' at position i
357  break
358  i -= 1
359  if i <= 0:
360  # Have reached the beginning of the string without
361  # finding the matching '('
362  self.isvalid = False
363  return
364  self.designator = apply_map(line[:i]).strip()
365  items = split_comma(line[i + 1 : -1], item)
366  else:
367  # Call has no argument list
368  items = []
369  self.designator = apply_map(line).strip()
370  self.items = items
371  return
372 
373  def tofortran(self, isfix=None):
374  """Returns the Fortran representation of this object as a string"""
375  txt = self.get_indent_tab(isfix=isfix) + "CALL " + str(self.designator)
376  if self.items:
377  txt += "(" + ", ".join(map(str, self.items)) + ")"
378  return txt
379 
380  def analyze(self):
381  a = self.programblock.a
382  variables = a.variables
383  if hasattr(a, "external"):
384  external = a.external
385  if self.designator in external:
386  print("Need to analyze:", self, file=sys.stderr)
387  return
388 
389 
391  """
392  GO TO <label>
393  """
394 
395  match = re.compile(r"go\s*to\s*\d+\s*\Z", re.I).match
396 
397  def process_item(self):
398  assert not self.item.has_map()
399  self.label = self.item.get_line()[2:].lstrip()[2:].lstrip()
400  return
401 
402  def tofortran(self, isfix=None):
403  return self.get_indent_tab(isfix=isfix) + "GO TO %s" % (self.label)
404 
405  def analyze(self):
406  return
407 
408 
409 class ComputedGoto(Statement):
410  """
411  GO TO ( <label-list> ) [ , ] <scalar-int-expr>
412  """
413 
414  match = re.compile(r"go\s*to\s*\(", re.I).match
415 
416  def process_item(self):
417  apply_map = self.item.apply_map
418  line = self.item.get_line()[2:].lstrip()[2:].lstrip()
419  i = line.index(")")
420  self.items = split_comma(line[1:i], self.item)
421  line = line[i + 1 :].lstrip()
422  if line.startswith(","):
423  line = line[1:].lstrip()
424  self.expr = apply_map(line)
425  return
426 
427  def tofortran(self, isfix=None):
428  return self.get_indent_tab(isfix=isfix) + "GO TO (%s) %s" % (
429  ", ".join(self.items),
430  self.expr,
431  )
432 
433  def analyze(self):
434  return
435 
436 
437 class AssignedGoto(Statement):
438  """
439  GO TO <int-variable-name> [ ( <label> [ , <label> ]... ) ]
440  """
441 
442  modes = ["fix77"]
443  match = re.compile(r"go\s*to\s*\w+\s*\(?", re.I).match
444 
445  def process_item(self):
446  line = self.item.get_line()[2:].lstrip()[2:].lstrip()
447  i = line.find("(")
448  if i == -1:
449  self.varname = line
450  self.items = []
451  return
452  self.varname = line[:i].rstrip()
453  assert line[-1] == ")", repr(line)
454  self
455  self.items = split_comma(line[i + 1 : -1], self.item)
456  return
457 
458  def tofortran(self, isfix=None):
459  tab = self.get_indent_tab(isfix=isfix)
460  if self.items:
461  return tab + "GO TO %s (%s)" % (self.varname, ", ".join(self.items))
462  return tab + "GO TO %s" % (self.varname)
463 
464  def analyze(self):
465  return
466 
467 
468 class Continue(Statement):
469  """
470  CONTINUE
471  """
472 
473  match = re.compile(r"continue\Z", re.I).match
474 
475  def process_item(self):
476  self.label = self.item.label
477  return
478 
479  def tofortran(self, isfix=None):
480  return self.get_indent_tab(deindent=True) + "CONTINUE"
481 
482  def analyze(self):
483  return
484 
485 
486 class Return(Statement):
487  """
488  RETURN [ <scalar-int-expr> ]
489  """
490 
491  match = re.compile(r"return\b", re.I).match
492 
493  def process_item(self):
494  self.expr = self.item.apply_map(self.item.get_line()[6:].lstrip())
495  return
496 
497  def tofortran(self, isfix=None):
498  tab = self.get_indent_tab(isfix=isfix)
499  if self.expr:
500  return tab + "RETURN %s" % (self.expr)
501  return tab + "RETURN"
502 
503  def analyze(self):
504  return
505 
506 
507 class Stop(Statement):
508  """
509  STOP [ <stop-code> ]
510  <stop-code> = <scalar-char-constant> | <1-5-digit>
511  """
512 
513  match = re.compile(r'stop\s*((\'\w*\'|"\w*")+|\d+|)\Z', re.I).match
514 
515  def process_item(self):
516  self.code = self.item.apply_map(self.item.get_line()[4:].lstrip())
517  return
518 
519  def tofortran(self, isfix=None):
520  tab = self.get_indent_tab(isfix=isfix)
521  if self.code:
522  return tab + "STOP %s" % (self.code)
523  return tab + "STOP"
524 
525  def analyze(self):
526  return
527 
528 
529 class Print(Statement):
530  """
531  PRINT <format> [, <output-item-list>]
532  <format> = <default-char-expr> | <label> | *
533 
534  <output-item> = <expr> | <io-implied-do>
535  <io-implied-do> = ( <io-implied-do-object-list> , <implied-do-control> )
536  <io-implied-do-object> = <input-item> | <output-item>
537  <implied-do-control> = <do-variable>
538  = <scalar-int-expr> ,
539  <scalar-int-expr> [ , <scalar-int-expr> ]
540  <input-item> = <variable> | <io-implied-do>
541  """
542 
543  match = re.compile(r"print\s*(\'\w*\'|\"\w*\"|\d+|[*]|\b\w)", re.I).match
544 
545  def process_item(self):
546  item = self.item
547  apply_map = item.apply_map
548  line = item.get_line()[5:].lstrip()
549  items = split_comma(line, item)
550  self.format = items[0]
551  self.items = items[1:]
552  return
553 
554  def tofortran(self, isfix=None):
555  return self.get_indent_tab(isfix=isfix) + "PRINT %s" % (
556  ", ".join([self.format] + self.items)
557  )
558 
559  def analyze(self):
560  return
561 
562 
563 class Read(Statement):
564  """
565  Read0: READ ( <io-control-spec-list> ) [ <input-item-list> ]
566 
567  <io-control-spec-list> = [ UNIT = ] <io-unit>
568  | [ FORMAT = ] <format>
569  | [ NML = ] <namelist-group-name>
570  | ADVANCE = <scalar-default-char-expr>
571  ...
572 
573  Read1: READ <format> [, <input-item-list>]
574  <format> == <default-char-expr> | <label> | *
575  """
576 
577  match = re.compile(r'read\b\s*[\w(*\'"]', re.I).match
578 
579  def process_item(self):
580  item = self.item
581  line = item.get_line()[4:].lstrip()
582  if line.startswith("("):
583  self.__class__ = Read0
584  else:
585  self.__class__ = Read1
586  self.process_item()
587  return
588 
589  def analyze(self):
590  return
591 
592 
593 class Read0(Read):
594  def process_item(self):
595  item = self.item
596  line = item.get_line()[4:].lstrip()
597  i = line.find(")")
598  self.specs = specs_split_comma(line[1:i], item)
599  self.items = split_comma(line[i + 1 :], item)
600  return
601 
602  def tofortran(self, isfix=None):
603  s = self.get_indent_tab(isfix=isfix) + "READ (%s)" % (", ".join(self.specs))
604  if self.items:
605  return s + " " + ", ".join(self.items)
606  return s
607 
608 
609 class Read1(Read):
610  def process_item(self):
611  item = self.item
612  line = item.get_line()[4:].lstrip()
613  items = split_comma(line, item)
614  self.format = items[0]
615  self.items = items[1:]
616  return
617 
618  def tofortran(self, isfix=None):
619  return (
620  self.get_indent_tab(isfix=isfix)
621  + "READ "
622  + ", ".join([self.format] + self.items)
623  )
624 
625 
627  """
628  WRITE ( io-control-spec-list ) [<output-item-list>]
629  """
630 
631  match = re.compile(r"write\s*\(", re.I).match
632 
633  def process_item(self):
634  item = self.item
635  line = item.get_line()[5:].lstrip()
636  i = line.find(")")
637  assert i != -1, repr(line)
638  self.specs = specs_split_comma(line[1:i], item)
639  self.items = split_comma(line[i + 1 :], item)
640  return
641 
642  def tofortran(self, isfix=None):
643  s = self.get_indent_tab(isfix=isfix) + "WRITE (%s)" % ", ".join(self.specs)
644  if self.items:
645  s += " " + ", ".join(self.items)
646  return s
647 
648  def analyze(self):
649  return
650 
651 
652 class Flush(Statement):
653  """
654  FLUSH <file-unit-number>
655  FLUSH ( <flush-spec-list> )
656  <flush-spec> = [ UNIT = ] <file-unit-number>
657  | IOSTAT = <scalar-int-variable>
658  | IOMSG = <iomsg-variable>
659  | ERR = <label>
660  """
661 
662  match = re.compile(r"flush\b", re.I).match
663 
664  def process_item(self):
665  line = self.item.get_line()[5:].lstrip()
666  if not line:
667  self.isvalid = False
668  return
669  if line.startswith("("):
670  assert line[-1] == ")", repr(line)
671  self.specs = specs_split_comma(line[1:-1], self.item)
672  else:
673  self.specs = specs_split_comma(line, self.item)
674  return
675 
676  def tofortran(self, isfix=None):
677  tab = self.get_indent_tab(isfix=isfix)
678  return tab + "FLUSH (%s)" % (", ".join(self.specs))
679 
680  def analyze(self):
681  return
682 
683 
684 class Wait(Statement):
685  """
686  WAIT ( <wait-spec-list> )
687  <wait-spec> = [ UNIT = ] <file-unit-number>
688  | END = <label>
689  | EOR = <label>
690  | ERR = <label>
691  | ID = <scalar-int-expr>
692  | IOMSG = <iomsg-variable>
693  | IOSTAT = <scalar-int-variable>
694 
695  """
696 
697  match = re.compile(r"wait\s*\(.*\)\Z", re.I).match
698 
699  def process_item(self):
700  self.specs = specs_split_comma(
701  self.item.get_line()[4:].lstrip()[1:-1], self.item
702  )
703  return
704 
705  def tofortran(self, isfix=None):
706  tab = self.get_indent_tab(isfix=isfix)
707  return tab + "WAIT (%s)" % (", ".join(self.specs))
708 
709  def analyze(self):
710  return
711 
712 
713 class Contains(Statement):
714  """
715  CONTAINS
716  """
717 
718  match = re.compile(r"contains\Z", re.I).match
719 
720  def process_item(self):
721  return
722 
723  def tofortran(self, isfix=None):
724  return self.get_indent_tab(isfix=isfix) + "CONTAINS"
725 
726 
728  """
729  ALLOCATE ( [ <type-spec> :: ] <allocation-list> [ , <alloc-opt-list> ] )
730  <alloc-opt> = STAT = <stat-variable>
731  | ERRMSG = <errmsg-variable>
732  | SOURCE = <source-expr>
733  <allocation> = <allocate-object> [ ( <allocate-shape-spec-list> ) ]
734  """
735 
736  match = re.compile(r"allocate\s*\(.*\)\Z", re.I).match
737 
738  def process_item(self):
739  """
740  Process the ALLOCATE statement and store the various entities being
741  allocated in self.items. Any type-specification is stored in
742  self.spec.
743 
744  :raises ParseError: if an invalid type-specification is used
745  """
746  line = self.item.get_line()[8:].lstrip()[1:-1].strip()
747  item2 = self.item.copy(line, True)
748  line2 = item2.get_line()
749  i = line2.find("::")
750  if i != -1:
751  spec = item2.apply_map(line2[:i].rstrip())
752  from .block_statements import type_spec
753 
754  stmt = None
755  for cls in type_spec:
756  if cls.match(spec):
757  stmt = cls(self, item2.copy(spec))
758  if stmt.isvalid:
759  break
760  if stmt is not None and stmt.isvalid:
761  spec = stmt
762  elif is_name(spec):
763  # Type spec is the name of a derived type
764  pass
765  else:
766  raise ParseError(
767  "Unrecognised type-specification in ALLOCATE statement: "
768  "{0}".format(self.item.line)
769  )
770  line2 = line2[i + 2 :].lstrip()
771  else:
772  spec = None
773  self.spec = spec
774  self.items = specs_split_comma(line2, item2)
775  return
776 
777  def tofortran(self, isfix=None):
778  """
779  Create the Fortran code for this ALLOCATE statement
780 
781  :param bool isfix: whether or not to generate fixed-format code
782  :return: Fortran code
783  :rtype: str
784  """
785  type_spec = ""
786  if self.spec:
787  if isinstance(self.spec, str):
788  type_spec = self.spec
789  else:
790  type_spec = self.spec.tostr()
791  type_spec += " :: "
792  return self.get_indent_tab(isfix=isfix) + "ALLOCATE (%s%s)" % (
793  type_spec,
794  ", ".join(self.items),
795  )
796 
797  def analyze(self):
798  return
799 
800 
801 class Deallocate(Statement):
802  """
803  DEALLOCATE ( <allocate-object-list> [ , <dealloc-opt-list> ] )
804  <allocate-object> = <variable-name>
805  | <structure-component>
806  <structure-component> = <data-ref>
807  <dealloc-opt> = STAT = <stat-variable>
808  | ERRMSG = <errmsg-variable>
809  """
810 
811  match = re.compile(r"deallocate\s*\(.*\)\Z", re.I).match
812 
813  def process_item(self):
814  line = self.item.get_line()[10:].lstrip()[1:-1].strip()
815  self.items = specs_split_comma(line, self.item)
816  return
817 
818  def tofortran(self, isfix=None):
819  return self.get_indent_tab(isfix=isfix) + "DEALLOCATE (%s)" % (
820  ", ".join(self.items)
821  )
822 
823  def analyze(self):
824  return
825 
826 
827 class ModuleProcedure(Statement):
828  """
829  [ MODULE ] PROCEDURE [::] <procedure-name-list>
830  """
831 
832  match = re.compile(r"(module\s*|)procedure\b\s*(::)?", re.I).match
833 
834  def process_item(self):
835  line = self.item.get_line()
836  m = self.match(line)
837  assert m, repr(line)
838  items = split_comma(line[m.end() :].strip(), self.item)
839  for n in items:
840  if not is_name(n):
841  self.isvalid = False
842  return
843  self.items = items
844  return
845 
846  def tofortran(self, isfix=None):
847  tab = self.get_indent_tab(isfix=isfix)
848  return tab + "MODULE PROCEDURE %s" % (", ".join(self.items))
849 
850  def analyze(self):
851  module_procedures = self.parent.a.module_procedures
852  module_procedures.extend(self.items)
853  # XXX: add names to parent_provides
854  return
855 
856 
858  """
859  <access-spec> [ [::] <access-id-list>]
860  <access-spec> = PUBLIC | PRIVATE
861  <access-id> = <use-name> | <generic-spec>
862  """
863 
864  match = re.compile(r"(public|private)\b", re.I).match
865 
866  def process_item(self):
867  clsname = self.__class__.__name__.lower()
868  line = self.item.get_line()
869  if not line.lower().startswith(clsname):
870  self.isvalid = False
871  return
872  line = line[len(clsname) :].lstrip()
873  if line.startswith("::"):
874  line = line[2:].lstrip()
875  self.items = split_comma(line, self.item)
876  return
877 
878  def tofortran(self, isfix=None):
879  clsname = self.__class__.__name__.upper()
880  tab = self.get_indent_tab(isfix=isfix)
881  if self.items:
882  return tab + clsname + " " + ", ".join(self.items)
883  return tab + clsname
884 
885  def analyze(self):
886  clsname = self.__class__.__name__
887  bits = getattr(self.parent.a, clsname.lower() + "_id_list")
888  if self.items:
889  for name in self.items:
890  if name not in bits:
891  bits.append(name)
892  else:
893  if "" not in bits:
894  bits.append("")
895  if not isinstance(self.parent, classes.Module):
896  parentclsname = self.parent.__class__.__name__
897  message = (
898  "C548 violation: %s statement only allowed in the"
899  " specification-part of a module, not in a %s."
900  % (clsname.upper(), parentclsname.lower())
901  )
902  self.warning(message)
903  access_id_list = self.parent.a.private_id_list + self.parent.a.public_id_list
904  if access_id_list.count("") > 1:
905  message = (
906  "C548 violation: only one access-stmt with omitted"
907  " access-id-list is permitted in"
908  " the module-specification-part."
909  )
910  self.warning(message)
911  # todo: check for conflicting access statement usages
912  # (e.g. private foo; public foo)
913  # todo: check for conflicting generic-spec id-s.
914  return
915 
916 
917 class Public(Access):
918  is_public = True
919 
920 
922  is_public = False
923 
924 
926  """
927  CLOSE ( <close-spec-list> )
928  <close-spec> = [ UNIT = ] <file-unit-number>
929  | IOSTAT = <scalar-int-variable>
930  | IOMSG = <iomsg-variable>
931  | ERR = <label>
932  | STATUS = <scalar-default-char-expr>
933  """
934 
935  match = re.compile(r"close\s*\(.*\)\Z", re.I).match
936 
937  def process_item(self):
938  line = self.item.get_line()[5:].lstrip()[1:-1].strip()
939  self.specs = specs_split_comma(line, self.item)
940  return
941 
942  def tofortran(self, isfix=None):
943  tab = self.get_indent_tab(isfix=isfix)
944  return tab + "CLOSE (%s)" % (", ".join(self.specs))
945 
946  def analyze(self):
947  return
948 
949 
950 class Cycle(Statement):
951  """
952  CYCLE [ <do-construct-name> ]
953  """
954 
955  match = re.compile(r"cycle\b\s*\w*\s*\Z", re.I).match
956 
957  def process_item(self):
958  self.name = self.item.get_line()[5:].lstrip()
959  return
960 
961  def tofortran(self, isfix=None):
962  if self.name:
963  return self.get_indent_tab(isfix=isfix) + "CYCLE " + self.name
964  return self.get_indent_tab(isfix=isfix) + "CYCLE"
965 
966  def analyze(self):
967  return
968 
969 
970 class FilePositioningStatement(Statement):
971  """
972  REWIND <file-unit-number>
973  REWIND ( <position-spec-list> )
974  <position-spec-list> = [ UNIT = ] <file-unit-number>
975  | IOMSG = <iomsg-variable>
976  | IOSTAT = <scalar-int-variable>
977  | ERR = <label>
978  The same for BACKSPACE, ENDFILE.
979  """
980 
981  match = re.compile(r"(rewind|backspace|endfile)\b", re.I).match
982 
983  def process_item(self):
984  clsname = self.__class__.__name__.lower()
985  line = self.item.get_line()
986  if not line.lower().startswith(clsname):
987  self.isvalid = False
988  return
989  line = line[len(clsname) :].lstrip()
990  if line.startswith("("):
991  assert line[-1] == ")", repr(line)
992  spec = line[1:-1].strip()
993  else:
994  spec = line
995  self.specs = specs_split_comma(spec, self.item)
996  return
997 
998  def tofortran(self, isfix=None):
999  clsname = self.__class__.__name__.upper()
1000  return (
1001  self.get_indent_tab(isfix=isfix)
1002  + clsname
1003  + " (%s)" % (", ".join(self.specs))
1004  )
1005 
1006  def analyze(self):
1007  return
1008 
1009 
1010 class Backspace(FilePositioningStatement):
1011  pass
1012 
1013 
1015  pass
1016 
1017 
1019  pass
1020 
1021 
1023  """
1024  OPEN ( <connect-spec-list> )
1025  <connect-spec> = [ UNIT = ] <file-unit-number>
1026  | ACCESS = <scalar-default-char-expr>
1027  | ..
1028  """
1029 
1030  match = re.compile(r"open\s*\(.*\)\Z", re.I).match
1031 
1032  def process_item(self):
1033  line = self.item.get_line()[4:].lstrip()[1:-1].strip()
1034  self.specs = specs_split_comma(line, self.item)
1035  return
1036 
1037  def tofortran(self, isfix=None):
1038  return self.get_indent_tab(isfix=isfix) + "OPEN (%s)" % (", ".join(self.specs))
1039 
1040  def analyze(self):
1041  return
1042 
1043 
1044 class Format(Statement):
1045  """
1046  FORMAT <format-specification>
1047  <format-specification> = ( [ <format-item-list> ] )
1048  <format-item> = [ <r> ] <data-edit-descr>
1049  | <control-edit-descr>
1050  | <char-string-edit-descr>
1051  | [ <r> ] ( <format-item-list> )
1052  <data-edit-descr> = I <w> [ . <m> ]
1053  | B <w> [ . <m> ]
1054  ...
1055  <r|w|m|d|e> = <int-literal-constant>
1056  <v> = <signed-int-literal-constant>
1057  <control-edit-descr> = <position-edit-descr>
1058  | [ <r> ] /
1059  | :
1060  ...
1061  <position-edit-descr> = T <n>
1062  | TL <n>
1063  ...
1064  <sign-edit-descr> = SS | SP | S
1065  ...
1066 
1067  """
1068 
1069  match = re.compile(r"format\s*\(.*\)\Z", re.I).match
1070 
1071  def process_item(self):
1072  item = self.item
1073  if item.label is None:
1074  # R1001:
1075  self.warning(
1076  "FORMAT statement must be labeled (F2008:C1001)." % (item.label)
1077  )
1078  line = item.get_line()[6:].lstrip()
1079  assert line[0] + line[-1] == "()", repr(line)
1080  self.specs = split_comma(line[1:-1], item)
1081  return
1082 
1083  def tofortran(self, isfix=None):
1084  return self.get_indent_tab(isfix=isfix) + "FORMAT (%s)" % (
1085  ", ".join(self.specs)
1086  )
1087 
1088  def analyze(self):
1089  return
1090 
1091 
1092 class Save(Statement):
1093  """
1094  SAVE [ [ :: ] <saved-entity-list> ]
1095  <saved-entity> = <object-name>
1096  | <proc-pointer-name>
1097  | / <common-block-name> /
1098  <proc-pointer-name> = <name>
1099  <object-name> = <name>
1100  """
1101 
1102  match = re.compile(r"save\b", re.I).match
1103 
1104  def process_item(self):
1105  assert not self.item.has_map()
1106  line = self.item.get_line()[4:].lstrip()
1107  if line.startswith("::"):
1108  line = line[2:].lstrip()
1109  items = []
1110  for s in line.split(","):
1111  s = s.strip()
1112  if not s:
1113  continue
1114  if s.startswith("/"):
1115  assert s.endswith("/"), repr(s)
1116  n = s[1:-1].strip()
1117  assert is_name(n), repr(n)
1118  items.append("/%s/" % (n))
1119  elif is_name(s):
1120  items.append(s)
1121  else:
1122  self.isvalid = False
1123  return
1124  self.items = items
1125  return
1126 
1127  def tofortran(self, isfix=None):
1128  tab = self.get_indent_tab(isfix=isfix)
1129  if not self.items:
1130  return tab + "SAVE"
1131  return tab + "SAVE %s" % (", ".join(self.items))
1132 
1133  def analyze(self):
1134  return
1135 
1136 
1137 class Data(Statement):
1138  """
1139  DATA <data-stmt-set> [ [ , ] <data-stmt-set> ]...
1140  <data-stmt-set> = <data-stmt-object-list> / <data-stmt-value-list> /
1141  <data-stmt-object> = <variable> | <data-implied-do>
1142  <data-implied-do> = ( <data-i-do-object-list> ,
1143  <data-i-do-variable> = <scalar-int-expr> ,
1144  <scalar-int-expr> [ , <scalar-int-expr> ] )
1145  <data-i-do-object> = <array-element>
1146  | <scalar-structure-component>
1147  | <data-implied-do>
1148  <data-i-do-variable> = <scalar-int-variable>
1149  <variable> = <designator>
1150  <designator> = <object-name>
1151  | <array-element>
1152  | <array-section>
1153  | <structure-component>
1154  | <substring>
1155  <array-element> = <data-ref>
1156  <array-section> = <data-ref> [ ( <substring-range> ) ]
1157 
1158  """
1159 
1160  match = re.compile(r"data\b", re.I).match
1161 
1162  def process_item(self):
1163  line = self.item.get_line()[4:].lstrip()
1164  stmts = []
1165  self.isvalid = False
1166  while line:
1167  i = line.find("/")
1168  if i == -1:
1169  return
1170  j = line.find("/", i + 1)
1171  if j == -1:
1172  return
1173  l1, l2 = line[:i].rstrip(), line[i + 1 : j].strip()
1174  l1 = split_comma(l1, self.item)
1175  l2 = split_comma(l2, self.item)
1176  stmts.append((l1, l2))
1177  line = line[j + 1 :].lstrip()
1178  if line.startswith(","):
1179  line = line[1:].lstrip()
1180  self.stmts = stmts
1181  self.isvalid = True
1182  return
1183 
1184  def tofortran(self, isfix=None):
1185  tab = self.get_indent_tab(isfix=isfix)
1186  bits = []
1187  for o, v in self.stmts:
1188  bits.append("%s / %s /" % (", ".join(o), ", ".join(v)))
1189  return tab + "DATA " + " ".join(bits)
1190 
1191  def analyze(self):
1192  return
1193 
1194 
1195 class Nullify(Statement):
1196  """
1197  NULLIFY ( <pointer-object-list> )
1198  <pointer-object> = <variable-name>
1199  """
1200 
1201  match = re.compile(r"nullify\s*\(.*\)\Z", re.I).match
1202 
1203  def process_item(self):
1204  line = self.item.get_line()[7:].lstrip()[1:-1].strip()
1205  self.items = split_comma(line, self.item)
1206  return
1207 
1208  def tofortran(self, isfix=None):
1209  return self.get_indent_tab(isfix=isfix) + "NULLIFY (%s)" % (
1210  ", ".join(self.items)
1211  )
1212 
1213  def analyze(self):
1214  return
1215 
1216 
1217 class Use(Statement):
1218  """Parses USE statement.
1219 
1220  :param class Statement: Base fparser class.
1221  :raises AnalyzeError: If entity name is not in module.
1222 
1223  Fortran syntax construct:
1224 
1225  USE [ [ , <module-nature> ] :: ] <module-name> [ , <rename-list> ]
1226  USE [ [ , <module-nature> ] :: ] <module-name> , ONLY : [ <only-list> ]
1227  <module-nature> = INTRINSIC | NON_INTRINSIC
1228  <rename> = <local-name> => <use-name>
1229  | OPERATOR ( <local-defined-operator> ) =>
1230  OPERATOR ( <use-defined-operator> )
1231  <only> = <generic-spec> | <only-use-name> | <rename>
1232  <only-use-name> = <use-name>
1233  """
1234 
1235  match = re.compile(r"use\b", re.I).match
1236 
1237  def process_item(self):
1238  """Parse the string containing the Use and store the
1239  module name and list of attributes (if any)"""
1240  line = self.item.get_line()[3:].lstrip()
1241  nature = ""
1242  if line.startswith(","):
1243  i = line.find("::")
1244  nature = line[1:i].strip().upper()
1245  line = line[i + 2 :].lstrip()
1246  if line.startswith("::"):
1247  line = line[2:].lstrip()
1248  if nature and not is_name(nature):
1249  self.isvalid = False
1250  return
1251  self.nature = nature
1252  i = line.find(",")
1253  self.isonly = False
1254  if i == -1:
1255  self.name = line
1256  self.items = []
1257  else:
1258  self.name = line[:i].rstrip()
1259  line = line[i + 1 :].lstrip()
1260  if line.lower().startswith("only") and line[4:].lstrip().startswith(":"):
1261  self.isonly = True
1262  line = line[4:].lstrip()[1:].lstrip()
1263  self.items = split_comma(line, self.item)
1264  return
1265 
1266  def tofortran(self, isfix=None): # pylint: disable=invalid-name
1267  """
1268  Returns the Fortran representation of this object as a string
1269 
1270  :param bool isfix: Whether or not to generated fixed-format Fortran
1271  :return: Fortran representation of this object
1272  :rtype: str
1273  """
1274  tab = self.get_indent_tab(isfix=isfix)
1275  s = "USE"
1276  if self.nature:
1277  s += ", " + self.nature + " ::"
1278  s += " " + self.name
1279  if self.isonly:
1280  s += ", ONLY:"
1281  elif self.items:
1282  s += ","
1283  if self.items:
1284  s += " " + ", ".join(self.items)
1285  return tab + s
1286 
1287  def analyze(self): # pylint: disable=invalid-name
1288  """Returns warnings if this object is incorrect"""
1289  use = self.parent.a.use
1290  if self.name in use:
1291  return
1292 
1293  modules = self.top.a.module
1294  if self.name not in modules:
1295  fn = self.reader.find_module_source_file(self.name)
1296  if fn is not None:
1297  from fparser.common.readfortran import FortranFileReader
1298  from fparser.one.parsefortran import FortranParser
1299 
1300  self.info("looking module information from %r" % (fn))
1301  reader = FortranFileReader(
1302  fn,
1303  include_dirs=self.reader.include_dirs,
1304  source_only=self.reader.source_only,
1305  )
1306  parser = FortranParser(reader)
1307  parser.parse()
1308  parser.block.a.module.update(modules)
1309  parser.analyze()
1310  modules.update(parser.block.a.module)
1311 
1312  if self.name not in modules:
1313  self.warning(
1314  "no information about the module %r in use statement" % (self.name)
1315  )
1316  return
1317 
1318  module = modules[self.name]
1319  use[self.name] = module
1320  use_provides = self.parent.a.use_provides
1321  renames = [split_comma(item, comma="=>") for item in self.items if "=>" in item]
1322  norenames = [item for item in self.items if "=>" not in item]
1323  all_mod_provides = dict(module.a.module_provides)
1324  all_mod_provides.update(module.a.use_provides)
1325  if self.isonly:
1326  # populate use_provides with items/renames only.
1327  for rename, orig in renames:
1328  self.populate_use_provides(all_mod_provides, use_provides, orig, rename)
1329  for name in norenames:
1330  self.populate_use_provides(all_mod_provides, use_provides, name)
1331  else:
1332  # norenames should be empty
1333  if norenames:
1334  self.warning(
1335  "'use' without 'only' clause does not rename the "
1336  "variables '%s'" % ", ".join(norenames)
1337  )
1338  # populate use_provides with renamed vars from module.
1339  for rename, orig in renames:
1340  self.populate_use_provides(all_mod_provides, use_provides, orig, rename)
1341  # get all the rest
1342  unrenamed = set(all_mod_provides) - set([b for (a, b) in renames])
1343  for name in unrenamed:
1344  self.populate_use_provides(all_mod_provides, use_provides, name)
1345  return
1346 
1347  def populate_use_provides(self, all_mod_provides, use_provides, name, rename=None):
1348  """Checks for entity name in the module"""
1349  ovar = all_mod_provides.get(name, None)
1350  if ovar is None:
1351  raise AnalyzeError(
1352  "entity name '%s' is not in module '%s'" % (name, self.name)
1353  )
1354  if rename:
1355  # XXX: rename != ovar.name -- should mark this somehow?
1356  name_idx = rename
1357  else:
1358  name_idx = name
1359  if name_idx in use_provides:
1360  if ovar != use_provides[name_idx]:
1361  self.warning(
1362  "entity name '%s' is already declared in module "
1363  "'%s' while adding it to '%s', overriding."
1364  % (name, self.name, self.parent.name)
1365  )
1366  use_provides[name_idx] = ovar
1367 
1368 
1370  """
1371  EXIT [ <do-construct-name> ]
1372  """
1373 
1374  match = re.compile(r"exit\b\s*\w*\s*\Z", re.I).match
1375 
1376  def process_item(self):
1377  self.name = self.item.get_line()[4:].lstrip()
1378  return
1379 
1380  def tofortran(self, isfix=None):
1381  if self.name:
1382  return self.get_indent_tab(isfix=isfix) + "EXIT " + self.name
1383  return self.get_indent_tab(isfix=isfix) + "EXIT"
1384 
1385  def analyze(self):
1386  return
1387 
1388 
1389 class Parameter(Statement):
1390  """
1391  PARAMETER ( <named-constant-def-list> )
1392  <named-constant-def> = <named-constant> = <initialization-expr>
1393  """
1394 
1395  match = re.compile(r"parameter\s*\(.*\)\Z", re.I).match
1396 
1397  def process_item(self):
1398  line = self.item.get_line()[9:].lstrip()[1:-1].strip()
1399  self.items = split_comma(line, self.item)
1400  return
1401 
1402  def tofortran(self, isfix=None):
1403  return self.get_indent_tab(isfix=isfix) + "PARAMETER (%s)" % (
1404  ", ".join(self.items)
1405  )
1406 
1407  def analyze(self):
1408  for item in self.items:
1409  i = item.find("=")
1410  assert i != -1, repr(item)
1411  name = item[:i].rstrip()
1412  value = item[i + 1 :].lstrip()
1413  var = self.get_variable(name)
1414  var.update("parameter")
1415  var.set_init(value)
1416  return
1417 
1418 
1420  """
1421  EQUIVALENCE <equivalence-set-list>
1422  <equivalence-set> = ( <equivalence-object> , <equivalence-object-list> )
1423  <equivalence-object> = <variable-name> | <array-element> | <substring>
1424  """
1425 
1426  match = re.compile(r"equivalence\s*\(.*\)\Z", re.I).match
1427 
1428  def process_item(self):
1429  items = []
1430  for s in self.item.get_line()[11:].lstrip().split(","):
1431  s = s.strip()
1432  assert s[0] + s[-1] == "()", repr((s, self.item.get_line()))
1433  s = ", ".join(split_comma(s[1:-1], self.item))
1434  items.append("(" + s + ")")
1435  self.items = items
1436  return
1437 
1438  def tofortran(self, isfix=None):
1439  return self.get_indent_tab(isfix=isfix) + "EQUIVALENCE %s" % (
1440  ", ".join(self.items)
1441  )
1442 
1443  def analyze(self):
1444  return
1445 
1446 
1447 class Dimension(Statement):
1448  """
1449  DIMENSION [ :: ] <array-name> ( <array-spec> )
1450  [ , <array-name> ( <array-spec> ) ]...
1451 
1452  """
1453 
1454  match = re.compile(r"dimension\b", re.I).match
1455 
1456  def process_item(self):
1457  line = self.item.get_line()[9:].lstrip()
1458  if line.startswith("::"):
1459  line = line[2:].lstrip()
1460  self.items = split_comma(line, self.item)
1461  return
1462 
1463  def tofortran(self, isfix=None):
1464  return self.get_indent_tab(isfix=isfix) + "DIMENSION %s" % (
1465  ", ".join(self.items)
1466  )
1467 
1468  def analyze(self):
1469  for line in self.items:
1470  i = line.find("(")
1471  assert i != -1 and line.endswith(")"), repr(line)
1472  name = line[:i].rstrip()
1473  array_spec = split_comma(line[i + 1 : -1].strip(), self.item)
1474  var = self.get_variable(name)
1475  var.set_bounds(array_spec)
1476  return
1477 
1478 
1480  """
1481  TARGET [ :: ] <object-name> ( <array-spec> )
1482  [ , <object-name> ( <array-spec> ) ]...
1483 
1484  """
1485 
1486  match = re.compile(r"target\b", re.I).match
1487 
1488  def process_item(self):
1489  line = self.item.get_line()[6:].lstrip()
1490  if line.startswith("::"):
1491  line = line[2:].lstrip()
1492  self.items = split_comma(line, self.item)
1493  return
1494 
1495  def tofortran(self, isfix=None):
1496  return self.get_indent_tab(isfix=isfix) + "TARGET %s" % (", ".join(self.items))
1497 
1498  def analyze(self):
1499  for line in self.items:
1500  i = line.find("(")
1501  assert i != -1 and line.endswith(")"), repr(line)
1502  name = line[:i].rstrip()
1503  array_spec = split_comma(line[i + 1 : -1].strip(), self.item)
1504  var = self.get_variable(name)
1505  var.set_bounds(array_spec)
1506  var.update("target")
1507  return
1508 
1509 
1511  """
1512  POINTER [ :: ] <pointer-decl-list>
1513  <pointer-decl> = <object-name> [ ( <deferred-shape-spec-list> ) ]
1514  | <proc-entity-name>
1515 
1516  """
1517 
1518  match = re.compile(r"pointer\b", re.I).match
1519 
1520  def process_item(self):
1521  line = self.item.get_line()[7:].lstrip()
1522  if line.startswith("::"):
1523  line = line[2:].lstrip()
1524  self.items = split_comma(line, self.item)
1525  return
1526 
1527  def tofortran(self, isfix=None):
1528  return self.get_indent_tab(isfix=isfix) + "POINTER %s" % (", ".join(self.items))
1529 
1530  def analyze(self):
1531  for line in self.items:
1532  i = line.find("(")
1533  if i == -1:
1534  name = line
1535  array_spec = None
1536  else:
1537  assert line.endswith(")"), repr(line)
1538  name = line[:i].rstrip()
1539  array_spec = split_comma(line[i + 1 : -1].strip(), self.item)
1540  var = self.get_variable(name)
1541  var.set_bounds(array_spec)
1542  var.update("pointer")
1543  return
1544 
1545 
1547  """
1548  PROTECTED [ :: ] <entity-name-list>
1549  """
1550 
1551  match = re.compile(r"protected\b", re.I).match
1552 
1553  def analyze(self):
1554  for name in self.items:
1555  var = self.get_variable(name)
1556  var.update("protected")
1557  return
1558 
1559 
1561  """
1562  VOLATILE [ :: ] <object-name-list>
1563  """
1564 
1565  match = re.compile(r"volatile\b", re.I).match
1566 
1567  def analyze(self):
1568  for name in self.items:
1569  var = self.get_variable(name)
1570  var.update("volatile")
1571  return
1572 
1573 
1575  """
1576  VALUE [ :: ] <dummy-arg-name-list>
1577  """
1578 
1579  match = re.compile(r"value\b", re.I).match
1580 
1581  def analyze(self):
1582  for name in self.items:
1583  var = self.get_variable(name)
1584  var.update("value")
1585  return
1586 
1587 
1589  """
1590  IF ( <scalar-numeric-expr> ) <label> , <label> , <label>
1591  """
1592 
1593  match = re.compile(r"if\s*\(.*\)\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\Z", re.I).match
1594 
1595  def process_item(self):
1596  line = self.item.get_line()[2:].lstrip()
1597  line, l2, l3 = line.rsplit(",", 2)
1598  i = line.rindex(")")
1599  l1 = line[i + 1 :]
1600  self.expr = self.item.apply_map(line[1:i]).strip()
1601  self.labels = [l1.strip(), l2.strip(), l3.strip()]
1602  return
1603 
1604  def tofortran(self, isfix=None):
1605  return self.get_indent_tab(isfix=isfix) + "IF (%s) %s" % (
1606  self.expr,
1607  ", ".join(self.labels),
1608  )
1609 
1610  def analyze(self):
1611  return
1612 
1613 
1614 class Intrinsic(StatementWithNamelist):
1615  """
1616  INTRINSIC [ :: ] <intrinsic-procedure-name-list>
1617  """
1618 
1619  match = re.compile(r"intrinsic\b", re.I).match
1620 
1621  def analyze(self):
1622  for name in self.items:
1623  var = self.get_variable(name)
1624  var.update("intrinsic")
1625  return
1626 
1627 
1629  """
1630  INQUIRE ( <inquire-spec-list> )
1631  INQUIRE ( IOLENGTH = <scalar-int-variable> ) <output-item-list>
1632 
1633  <inquire-spec> = [ UNIT = ] <file-unit-number>
1634  | FILE = <file-name-expr>
1635  ...
1636  <output-item> = <expr>
1637  | <io-implied-do>
1638  """
1639 
1640  match = re.compile(r"inquire\s*\(", re.I).match
1641 
1642  def process_item(self):
1643  line = self.item.get_line()[7:].lstrip()
1644  i = line.index(")")
1645  self.specs = specs_split_comma(line[1:i].strip(), self.item)
1646  self.items = split_comma(line[i + 1 :].lstrip(), self.item)
1647  return
1648 
1649  def tofortran(self, isfix=None):
1650  if self.items:
1651  return self.get_indent_tab(isfix=isfix) + "INQUIRE (%s) %s" % (
1652  ", ".join(self.specs),
1653  ", ".join(self.items),
1654  )
1655  return self.get_indent_tab(isfix=isfix) + "INQUIRE (%s)" % (
1656  ", ".join(self.specs)
1657  )
1658 
1659  def analyze(self):
1660  return
1661 
1662 
1663 class Sequence(Statement):
1664  """
1665  SEQUENCE
1666  """
1667 
1668  match = re.compile(r"sequence\Z", re.I).match
1669 
1670  def process_item(self):
1671  return
1672 
1673  def tofortran(self, isfix=None):
1674  return self.get_indent_tab(isfix=isfix) + "SEQUENCE"
1675 
1676  def analyze(self):
1677  self.parent.update_attributes("SEQUENCE")
1678  return
1679 
1680 
1682  """
1683  EXTERNAL [ :: ] <external-name-list>
1684  """
1685 
1686  match = re.compile(r"external\b", re.I).match
1687 
1688  def analyze(self):
1689  for name in self.items:
1690  var = self.get_variable(name)
1691  var.update("external")
1692  return
1693 
1694 
1696  """
1697  NAMELIST / <namelist-group-name> / <namelist-group-object-list> [ [ , ]
1698  / <namelist-group-name> / <namelist-group-object-list> ]...
1699  <namelist-group-object> = <variable-name>
1700  """
1701 
1702  match = re.compile(r"namelist\b", re.I).match
1703 
1704  def process_item(self):
1705  line = self.item.get_line()[8:].lstrip()
1706  items = []
1707  while line:
1708  assert line.startswith("/"), repr(line)
1709  i = line.find("/", 1)
1710  assert i != -1, repr(line)
1711  name = line[: i + 1]
1712  line = line[i + 1 :].lstrip()
1713  i = line.find("/")
1714  if i == -1:
1715  items.append((name, line))
1716  line = ""
1717  continue
1718  s = line[:i].rstrip()
1719  if s.endswith(","):
1720  s = s[:-1].rstrip()
1721  items.append((name, s))
1722  line = line[i + 1 :].lstrip()
1723  self.items = items
1724  return
1725 
1726  def tofortran(self, isfix=None):
1727  bits = []
1728  for name, s in self.items:
1729  bits.append("%s %s" % (name, s))
1730  tab = self.get_indent_tab(isfix=isfix)
1731  return tab + "NAMELIST " + ", ".join(bits)
1732 
1733 
1735  """
1736  COMMON [ / [ <common-block-name> ] / ] <common-block-object-list> \
1737  [ [ , ] / [ <common-block-name> ] / <common-block-object-list> ]...
1738  <common-block-object> = <variable-name> [ ( <explicit-shape-spec-list> ) ]
1739  | <proc-pointer-name>
1740  """
1741 
1742  match = re.compile(r"common\b", re.I).match
1743 
1744  def process_item(self):
1745  item = self.item
1746  line = item.get_line()[6:].lstrip()
1747  items = []
1748  while line:
1749  if not line.startswith("/"):
1750  name = ""
1751  assert not items, repr(line)
1752  else:
1753  i = line.find("/", 1)
1754  assert i != -1, repr(line)
1755  name = line[1:i].strip()
1756  line = line[i + 1 :].lstrip()
1757  i = line.find("/")
1758  if i == -1:
1759  items.append((name, split_comma(line, item)))
1760  line = ""
1761  continue
1762  s = line[:i].rstrip()
1763  if s.endswith(","):
1764  s = s[:-1].rstrip()
1765  items.append((name, split_comma(s, item)))
1766  line = line[i:].lstrip()
1767  self.items = items
1768  return
1769 
1770  def tofortran(self, isfix=None):
1771  bits = []
1772  for name, s in self.items:
1773  s = ", ".join(s)
1774  if name:
1775  bits.append("/ %s / %s" % (name, s))
1776  else:
1777  bits.append(s)
1778  tab = self.get_indent_tab(isfix=isfix)
1779  return tab + "COMMON " + " ".join(bits)
1780 
1781  def analyze(self):
1782  for cname, items in self.items:
1783  for item in items:
1784  i = item.find("(")
1785  if i != -1:
1786  assert item.endswith(")"), repr(item)
1787  name = item[:i].rstrip()
1788  shape = split_comma(item[i + 1 : -1].strip(), self.item)
1789  else:
1790  name = item
1791  shape = None
1792  var = self.get_variable(name)
1793  if shape is not None:
1794  var.set_bounds(shape)
1795  # XXX: add name,var to parent_provides
1796  return
1797 
1798 
1800  """
1801  OPTIONAL [ :: ] <dummy-arg-name-list>
1802  <dummy-arg-name> = <name>
1803  """
1804 
1805  match = re.compile(r"optional\b", re.I).match
1806 
1807  def analyze(self):
1808  for name in self.items:
1809  var = self.get_variable(name)
1810  var.update("optional")
1811  return
1812 
1813 
1815  """
1816  INTENT ( <intent-spec> ) [ :: ] <dummy-arg-name-list>
1817  <intent-spec> = IN | OUT | INOUT
1818 
1819  generalization for pyf-files:
1820  INTENT ( <intent-spec-list> ) [ :: ] <dummy-arg-name-list>
1821  <intent-spec> = IN | OUT | INOUT | CACHE | HIDE | OUT = <name>
1822  """
1823 
1824  match = re.compile(r"intent\s*\(", re.I).match
1825 
1826  def process_item(self):
1827  line = self.item.get_line()[6:].lstrip()
1828  i = line.find(")")
1829  self.specs = specs_split_comma(line[1:i], self.item, upper=True)
1830  line = line[i + 1 :].lstrip()
1831  if line.startswith("::"):
1832  line = line[2:].lstrip()
1833  self.items = [s.strip() for s in line.split(",")]
1834  for n in self.items:
1835  if not is_name(n):
1836  self.isvalid = False
1837  return
1838  return
1839 
1840  def tofortran(self, isfix=None):
1841  return self.get_indent_tab(isfix=isfix) + "INTENT (%s) %s" % (
1842  ", ".join(self.specs),
1843  ", ".join(self.items),
1844  )
1845 
1846  def analyze(self):
1847  for name in self.items:
1848  var = self.get_variable(name)
1849  var.set_intent(self.specs)
1850  return
1851 
1852 
1854  """
1855  ENTRY <entry-name> [ ( [ <dummy-arg-list> ] ) [ <suffix> ] ]
1856  <suffix> = <proc-language-binding-spec> [ RESULT ( <result-name> ) ]
1857  | RESULT ( <result-name> ) [ <proc-language-binding-spec> ]
1858  <proc-language-binding-spec> = <language-binding-spec>
1859  <language-binding-spec> =
1860  BIND ( C [ , NAME = <scalar-char-initialization-expr> ] )
1861  <dummy-arg> = <dummy-arg-name> | *
1862  """
1863 
1864  match = re.compile(r"entry\s+[a-zA-Z]", re.I).match
1865 
1866  def process_item(self):
1867  line = self.item.get_line()[5:].lstrip()
1868  m = re.match(r"\w+", line)
1869  name = line[: m.end()]
1870  line = line[m.end() :].lstrip()
1871  if line.startswith("("):
1872  i = line.find(")")
1873  assert i != -1, repr(line)
1874  items = split_comma(line[1:i], self.item)
1875  line = line[i + 1 :].lstrip()
1876  else:
1877  items = []
1878  self.bind, line = parse_bind(line, self.item)
1879  self.result, line = parse_result(line, self.item)
1880  if line:
1881  assert self.bind is None, repr(self.bind)
1882  self.bind, line = parse_bind(line, self.item)
1883  assert not line, repr(line)
1884  self.name = name
1885  self.items = items
1886  return
1887 
1888  def tofortran(self, isfix=None):
1889  tab = self.get_indent_tab(isfix=isfix)
1890  s = tab + "ENTRY " + self.name
1891  if self.items:
1892  s += " (%s)" % (", ".join(self.items))
1893  if self.result:
1894  s += " RESULT (%s)" % (self.result)
1895  if self.bind:
1896  s += " BIND (%s)" % (", ".join(self.bind))
1897  return s
1898 
1899 
1901  """
1902  IMPORT [ [ :: ] <import-name-list> ]
1903  """
1904 
1905  match = re.compile(r"import(\b|\Z)", re.I).match
1906 
1907 
1909  """
1910  FORALL <forall-header> <forall-assignment-stmt>
1911  <forall-header> = ( <forall-triplet-spec-list> [ , <scalar-mask-expr> ] )
1912  <forall-triplet-spec> =
1913  <index-name> = <subscript> : <subscript> [ : <stride> ]
1914  <subscript|stride> = <scalar-int-expr>
1915  <forall-assignment-stmt> = <assignment-stmt> | <pointer-assignment-stmt>
1916  """
1917 
1918  match = re.compile(r"forall\s*\(.*\).*=", re.I).match
1919 
1920  def process_item(self):
1921  line = self.item.get_line()[6:].lstrip()
1922  i = line.index(")")
1923 
1924  line0 = line[1:i]
1925  line = line[i + 1 :].lstrip()
1926  stmt = GeneralAssignment(self, self.item.copy(line, True))
1927  if stmt.isvalid:
1928  self.content = [stmt]
1929  else:
1930  self.isvalid = False
1931  return
1932 
1933  specs = []
1934  mask = ""
1935  for l in split_comma(line0, self.item):
1936  j = l.find("=")
1937  if j == -1:
1938  assert not mask, repr((mask, l))
1939  mask = l
1940  continue
1941  assert j != -1, repr(l)
1942  index = l[:j].rstrip()
1943  it = self.item.copy(l[j + 1 :].lstrip())
1944  line = it.get_line()
1945  k = line.split(":")
1946  if len(k) == 3:
1947  s1, s2, s3 = list(
1948  map(it.apply_map, [k[0].strip(), k[1].strip(), k[2].strip()])
1949  )
1950  else:
1951  assert len(k) == 2, repr(k)
1952  s1, s2 = list(map(it.apply_map, [k[0].strip(), k[1].strip()]))
1953  s3 = "1"
1954  specs.append((index, s1, s2, s3))
1955 
1956  self.specs = specs
1957  self.mask = mask
1958  return
1959 
1960  def tofortran(self, isfix=None):
1961  tab = self.get_indent_tab(isfix=isfix)
1962  lines = []
1963  for index, s1, s2, s3 in self.specs:
1964  s = "%s = %s : %s" % (index, s1, s2)
1965  if s3 != "1":
1966  s += " : %s" % (s3)
1967  lines.append(s)
1968  s = ", ".join(lines)
1969  if self.mask:
1970  s += ", " + self.mask
1971  return tab + "FORALL (%s) %s" % (s, str(self.content[0]).lstrip())
1972 
1973  def analyze(self):
1974  return
1975 
1976 
1977 ForallStmt = Forall
1978 
1979 
1981  """
1982  PROCEDURE [ ( <interface-name> ) ] [ [ , <binding-attr-list> ] :: ]
1983  <binding-name> [ => <procedure-name> ]
1984  <binding-attr> = PASS [ ( <arg-name> ) ]
1985  | NOPASS
1986  | NON_OVERRIDABLE
1987  | DEFERRED
1988  | <access-spec>
1989  <access-spec> = PUBLIC | PRIVATE
1990 
1991  """
1992 
1993  match = re.compile(r"procedure\b", re.I).match
1994 
1995  def process_item(self):
1996  line = self.item.get_line()[9:].lstrip()
1997  if line.startswith("("):
1998  i = line.index(")")
1999  name = line[1:i].strip()
2000  line = line[i + 1 :].lstrip()
2001  else:
2002  name = ""
2003  self.iname = name
2004  if line.startswith(","):
2005  line = line[1:].lstrip()
2006  i = line.find("::")
2007  if i != -1:
2008  attrs = split_comma(line[:i], self.item)
2009  line = line[i + 2 :].lstrip()
2010  else:
2011  attrs = []
2012  attrs1 = []
2013  for attr in attrs:
2014  if is_name(attr):
2015  attr = attr.upper()
2016  else:
2017  i = attr.find("(")
2018  assert i != -1 and attr.endswith(")"), repr(attr)
2019  attr = "%s (%s)" % (attr[:i].rstrip().upper(), attr[i + 1 : -1].strip())
2020  attrs1.append(attr)
2021  self.attrs = attrs1
2022  i = line.find("=")
2023  if i == -1:
2024  self.name = line
2025  self.bname = ""
2026  else:
2027  self.name = line[:i].rstrip()
2028  self.bname = line[i + 1 :].lstrip()[1:].lstrip()
2029  return
2030 
2031  def tofortran(self, isfix=None):
2032  tab = self.get_indent_tab(isfix=isfix)
2033  s = "PROCEDURE "
2034  if self.iname:
2035  s += "(" + self.iname + ") "
2036  if self.attrs:
2037  s += ", " + ", ".join(self.attrs) + " :: "
2038  if self.bname:
2039  s += "%s => %s" % (self.name, self.bname)
2040  else:
2041  s += self.name
2042  return tab + s
2043 
2044 
2046  """
2047  GENERIC [ , <access-spec> ] :: <generic-spec> => <binding-name-list>
2048 
2049  """
2050 
2051  match = re.compile(r"generic\b.*::.*=>.*\Z", re.I).match
2052 
2053  def process_item(self):
2054  line = self.item.get_line()[7:].lstrip()
2055  if line.startswith(","):
2056  line = line[1:].lstrip()
2057  i = line.index("::")
2058  self.aspec = line[:i].rstrip().upper()
2059  line = line[i + 2 :].lstrip()
2060  i = line.index("=>")
2061  self.spec = self.item.apply_map(line[:i].rstrip())
2062  self.items = split_comma(line[i + 2 :].lstrip())
2063  return
2064 
2065  def tofortran(self, isfix=None):
2066  tab = self.get_indent_tab(isfix=isfix)
2067  s = "GENERIC"
2068  if self.aspec:
2069  s += ", " + self.aspec
2070  s += " :: " + self.spec + " => " + ", ".join(self.items)
2071  return tab + s
2072 
2073 
2075  """
2076  FINAL [ :: ] <final-subroutine-name-list>
2077 
2078  """
2079 
2080  stmtname = "final"
2081  match = re.compile(r"final\b", re.I).match
2082 
2083 
2085  """
2086  ALLOCATABLE [ :: ] <object-name> [ ( <deferred-shape-spec-list> ) ]
2087  [ , <object-name>
2088  [ ( <deferred-shape-spec-list> ) ]
2089  ]...
2090 
2091  """
2092 
2093  match = re.compile(r"allocatable\b", re.I).match
2094 
2095  def process_item(self):
2096  line = self.item.get_line()[11:].lstrip()
2097  if line.startswith("::"):
2098  line = line[2:].lstrip()
2099  self.items = split_comma(line, self.item)
2100  return
2101 
2102  def tofortran(self, isfix=None):
2103  return self.get_indent_tab(isfix=isfix) + "ALLOCATABLE " + ", ".join(self.items)
2104 
2105  def analyze(self):
2106  for line in self.items:
2107  i = line.find("(")
2108  if i == -1:
2109  name = line
2110  array_spec = None
2111  else:
2112  assert line.endswith(")")
2113  name = line[:i].rstrip()
2114  array_spec = split_comma(line[i + 1 : -1], self.item)
2115  var = self.get_variable(name)
2116  var.update("allocatable")
2117  if array_spec is not None:
2118  var.set_bounds(array_spec)
2119  return
2120 
2121 
2123  """
2124  ASYNCHRONOUS [ :: ] <object-name-list>
2125  """
2126 
2127  match = re.compile(r"asynchronous\b", re.I).match
2128 
2129  def analyze(self):
2130  for name in self.items:
2131  var = self.get_variable(name)
2132  var.update("asynchronous")
2133  return
2134 
2135 
2137  """
2138  <language-binding-spec> [ :: ] <bind-entity-list>
2139  <language-binding-spec> =
2140  BIND ( C [ , NAME = <scalar-char-initialization-expr> ] )
2141  <bind-entity> = <entity-name> | / <common-block-name> /
2142 
2143  """
2144 
2145  match = re.compile(r"bind\s*\(.*\)", re.I).match
2146 
2147  def process_item(self):
2148  line = self.item.line
2149  self.specs, line = parse_bind(line, self.item)
2150  if line.startswith("::"):
2151  line = line[2:].lstrip()
2152  items = []
2153  for item in split_comma(line, self.item):
2154  if item.startswith("/"):
2155  assert item.endswith("/"), repr(item)
2156  item = "/ " + item[1:-1].strip() + " /"
2157  items.append(item)
2158  self.items = items
2159  return
2160 
2161  def tofortran(self, isfix=None):
2162  return self.get_indent_tab(isfix=isfix) + "BIND (%s) %s" % (
2163  ", ".join(self.specs),
2164  ", ".join(self.items),
2165  )
2166 
2167 
2168 # IF construct statements
2169 
2170 
2172  """
2173  ELSE [<if-construct-name>]
2174  """
2175 
2176  match = re.compile(r"else\b\s*\w*\s*\Z", re.I).match
2177 
2178  def process_item(self):
2179  item = self.item
2180  self.name = item.get_line()[4:].strip()
2181  parent_name = getattr(self.parent, "name", "")
2182  if self.name and self.name != parent_name:
2183  self.warning(
2184  "expected if-construct-name %r but got %r, skipping."
2185  % (parent_name, self.name)
2186  )
2187  self.isvalid = False
2188  return
2189 
2190  def tofortran(self, isfix=None):
2191  if self.name:
2192  return self.get_indent_tab(deindent=True) + "ELSE " + self.name
2193  return self.get_indent_tab(deindent=True) + "ELSE"
2194 
2195  def analyze(self):
2196  return
2197 
2198 
2199 class ElseIf(Statement):
2200  """
2201  ELSE IF ( <scalar-logical-expr> ) THEN [ <if-construct-name> ]
2202  """
2203 
2204  match = re.compile(r"else\s*if\s*\(.*\)\s*then\s*\w*\s*\Z", re.I).match
2205 
2206  def process_item(self):
2207  item = self.item
2208  line = item.get_line()[4:].lstrip()[2:].lstrip()
2209  i = line.find(")")
2210  assert line[0] == "("
2211  self.expr = item.apply_map(line[1:i])
2212  self.name = line[i + 1 :].lstrip()[4:].strip()
2213  parent_name = getattr(self.parent, "name", "")
2214  if self.name and self.name != parent_name:
2215  self.warning(
2216  "expected if-construct-name %r but got %r, skipping."
2217  % (parent_name, self.name)
2218  )
2219  self.isvalid = False
2220  return
2221 
2222  def tofortran(self, isfix=None):
2223  s = ""
2224  if self.name:
2225  s = " " + self.name
2226  return self.get_indent_tab(deindent=True) + "ELSE IF (%s) THEN%s" % (
2227  self.expr,
2228  s,
2229  )
2230 
2231  def analyze(self):
2232  return
2233 
2234 
2235 # SelectCase construct statements
2236 
2237 
2238 class Case(Statement):
2239  """
2240  CASE <case-selector> [ <case-construct-name> ]
2241  <case-selector> = ( <case-value-range-list> ) | DEFAULT
2242  <case-value-range> = <case-value>
2243  | <case-value> :
2244  | : <case-value>
2245  | <case-value> : <case-value>
2246  <case-value> = <scalar-(int|char|logical)-initialization-expr>
2247 
2248  """
2249 
2250  match = re.compile(r"case\b\s*(\(.*\)|DEFAULT)\s*\w*\Z", re.I).match
2251 
2252  def process_item(self):
2253  """Populate the state of this item by parsing the associated line
2254  of code"""
2255  line = self.item.get_line()[4:].lstrip()
2256  try:
2257  self.items = extract_bracketed_list_items(line, self.item)
2258  idx = line.rfind(")")
2259  self.name = line[idx + 1 :].lstrip()
2260  except ParseError:
2261  # No list in parentheses found so we must have a 'case default'
2262  if not line.lower().startswith("default"):
2263  # We should never get to here because such a string should
2264  # not have generated a match
2265  self.warning(
2266  "Internal error when parsing CASE statement: {0}".format(line)
2267  )
2268  self.isvalid = False
2269  return
2270  self.items = []
2271  self.name = line[7:].lstrip()
2272  parent_name = getattr(self.parent, "name", "")
2273  if self.name and self.name != parent_name:
2274  self.warning(
2275  "Expected case-construct-name {0} but got {1}, "
2276  "skipping.".format(parent_name, self.name)
2277  )
2278  self.isvalid = False
2279  return
2280 
2281  def tofortran(self, isfix=None):
2282  """Return the Fortran for this object as a string"""
2283  tab = self.get_indent_tab(isfix=isfix)
2284  txt = tab + "CASE"
2285  if self.items:
2286  lst = []
2287  for item in self.items:
2288  lst.append((" : ".join(item)).strip())
2289  txt += " ( %s )" % (", ".join(lst))
2290  else:
2291  txt += " DEFAULT"
2292  if self.name:
2293  txt += " " + self.name
2294  return txt
2295 
2296  def analyze(self):
2297  return
2298 
2299 
2300 class TypeIs(Statement):
2301  """
2302  TYPE IS <type-selector> [ <case-construct-name> ]
2303  <type-selector> = ( <type-value-range-list> )
2304  <type-value-range> = <case-value>
2305  <case-value> = <char>
2306  """
2307 
2308  match = re.compile(r"type\b\s*is\b\s*\(.*\)\s*\w*\Z", re.I).match
2309 
2310  def process_item(self):
2311  """Populate the state of this object by parsing the associated
2312  line of code"""
2313  line = self.item.get_line()
2314  # We have a 'type is (...)' statement. At this point
2315  # any expression used for the type specifier will have
2316  # been replaced with e.g. F2PY_EXPR_TUPLE_3 and so
2317  # will not itself contain any parentheses.
2318  self.items = extract_bracketed_list_items(line, self.item)
2319  # Get and store the case-construct-name (if any)
2320  idx2 = line.rfind(")")
2321  self.name = line[idx2 + 1 :].lstrip()
2322  parent_name = getattr(self.parent, "name", "")
2323  if self.name and self.name != parent_name:
2324  self.warning(
2325  "expected type-is-construct-name %r but got %r, skipping."
2326  % (parent_name, self.name)
2327  )
2328  self.isvalid = False
2329  return
2330 
2331  def tofortran(self, isfix=None):
2332  """Create the Fortran representation of this object and return
2333  it as a string"""
2334  tab = self.get_indent_tab(isfix=isfix)
2335  text = tab + "TYPE IS"
2336  if self.items:
2337  lst = []
2338  for item in self.items:
2339  lst.append((" : ".join(item)).strip())
2340  text += " ( %s )" % (", ".join(lst))
2341  else:
2342  raise ParseError("TYPE IS construct must have arguments")
2343  if self.name:
2344  text += " " + self.name
2345  return text
2346 
2347  def analyze(self):
2348  return
2349 
2350 
2351 class ClassIs(Statement):
2352  """
2353  CLASS <class-selector>
2354  <class-selector> = ( IS <type-value-range-list> | DEFAULT )
2355  <type-value-range> = <case-value>
2356  <case-value> = <char>
2357  """
2358 
2359  match = re.compile(r"class\b\s*(is\b\s*\(.*\)|default)\s*\w*\Z", re.I).match
2360 
2361  def process_item(self):
2362  """Populate the state of this object by parsing the string"""
2363  line = self.item.get_line()[5:].lstrip()
2364  try:
2365  self.items = extract_bracketed_list_items(line, self.item)
2366  # We have a 'class is ...' statement. At this point
2367  # any expression used for the class specifier will have
2368  # been replaced with e.g. F2PY_EXPR_TUPLE_3 and so
2369  # will not contain any parentheses.
2370  idx = line.rfind(")")
2371  self.name = line[idx + 1 :].lstrip()
2372  except ParseError:
2373  # We have a 'class default' statement
2374  if not line.lower().startswith("default"):
2375  # We should never get here because such a string should
2376  # not have generated a match
2377  self.warning(
2378  "Internal error when parsing CLASS statement: {0}".format(line)
2379  )
2380  self.isvalid = False
2381  return
2382  self.items = []
2383  self.name = line[7:].lstrip()
2384  parent_name = getattr(self.parent, "name", "")
2385  if self.name and self.name != parent_name:
2386  self.warning(
2387  "expected class-construct-name %r but got %r, skipping."
2388  % (parent_name, self.name)
2389  )
2390  self.isvalid = False
2391  return
2392 
2393  def tofortran(self, isfix=None):
2394  """Returns the Fortran for this object as a string"""
2395  tab = self.get_indent_tab(isfix=isfix)
2396  text = tab + "CLASS"
2397  if self.items:
2398  text += " IS"
2399  lchar = []
2400  for item in self.items:
2401  lchar.append((" : ".join(item)).strip())
2402  text += " ( %s )" % (", ".join(lchar))
2403  else:
2404  text += " DEFAULT"
2405  if self.name:
2406  text += " " + self.name
2407  return text
2408 
2409  def analyze(self):
2410  return
2411 
2412 
2413 # Where construct statements
2414 
2415 
2416 class Where(Statement):
2417  """
2418  WHERE ( <mask-expr> ) <where-assignment-stmt>
2419  """
2420 
2421  match = re.compile(r"where\s*\(.*\)\s*\w.*\Z", re.I).match
2422 
2423  def process_item(self):
2424  line = self.item.get_line()[5:].lstrip()
2425  i = line.index(")")
2426  self.expr = self.item.apply_map(line[1:i].strip())
2427  line = line[i + 1 :].lstrip()
2428  newitem = self.item.copy(line)
2429  cls = Assignment
2430  if cls.match(line):
2431  stmt = cls(self, newitem)
2432  if stmt.isvalid:
2433  self.content = [stmt]
2434  return
2435  self.isvalid = False
2436  return
2437 
2438  def tofortran(self, isfix=None):
2439  tab = self.get_indent_tab(isfix=isfix)
2440  return tab + "WHERE ( %s ) %s" % (self.expr, str(self.content[0]).lstrip())
2441 
2442  def analyze(self):
2443  return
2444 
2445 
2446 WhereStmt = Where
2447 
2448 
2450  """
2451  ELSE WHERE ( <mask-expr> ) [ <where-construct-name> ]
2452  ELSE WHERE [ <where-construct-name> ]
2453  """
2454 
2455  match = re.compile(r"else\s*where\b", re.I).match
2456 
2457  def process_item(self):
2458  line = self.item.get_line()[4:].lstrip()[5:].lstrip()
2459  self.expr = None
2460  if line.startswith("("):
2461  i = line.index(")")
2462  assert i != -1, repr(line)
2463  self.expr = self.item.apply_map(line[1:i].strip())
2464  line = line[i + 1 :].lstrip()
2465  self.name = line
2466  parent_name = getattr(self.parent, "name", "")
2467  if self.name and not self.name == parent_name:
2468  self.warning(
2469  "expected where-construct-name %r but got %r, "
2470  "skipping." % (parent_name, self.name)
2471  )
2472  self.isvalid = False
2473  return
2474 
2475  def tofortran(self, isfix=None):
2476  tab = self.get_indent_tab(isfix=isfix)
2477  s = "ELSE WHERE"
2478  if self.expr is not None:
2479  s += " ( %s )" % (self.expr)
2480  if self.name:
2481  s += " " + self.name
2482  return tab + s
2483 
2484  def analyze(self):
2485  return
2486 
2487 
2488 # Enum construct statements
2489 
2490 
2491 class Enumerator(Statement):
2492  """
2493  ENUMERATOR [ :: ] <enumerator-list>
2494  <enumerator> = <named-constant> [ = <scalar-int-initialization-expr> ]
2495  """
2496 
2497  match = re.compile(r"enumerator\b", re.I).match
2498 
2499  def process_item(self):
2500  line = self.item.get_line()[10:].lstrip()
2501  if line.startswith("::"):
2502  line = line[2:].lstrip()
2503  self.items = split_comma(line, self.item)
2504  return
2505 
2506  def tofortran(self, isfix=None):
2507  return self.get_indent_tab(isfix=isfix) + "ENUMERATOR " + ", ".join(self.items)
2508 
2509 
2510 # F2PY specific statements
2511 
2512 
2514  """
2515  FORTRANNAME <name>
2516  """
2517 
2518  match = re.compile(r"fortranname\s*\w+\Z", re.I).match
2519 
2520  def process_item(self):
2521  self.value = self.item.get_line()[11:].lstrip()
2522  return
2523 
2524  def tofortran(self, isfix=None):
2525  return self.get_indent_tab(isfix=isfix) + "FORTRANNAME " + self.value
2526 
2527 
2529  """
2530  THREADSAFE
2531  """
2532 
2533  match = re.compile(r"threadsafe\Z", re.I).match
2534 
2535  def process_item(self):
2536  return
2537 
2538  def tofortran(self, isfix=None):
2539  return self.get_indent_tab(isfix=isfix) + "THREADSAFE"
2540 
2541 
2543  """
2544  DEPEND ( <name-list> ) [ :: ] <dummy-arg-name-list>
2545 
2546  """
2547 
2548  match = re.compile(r"depend\s*\(", re.I).match
2549 
2550  def process_item(self):
2551  line = self.item.get_line()[6:].lstrip()
2552  i = line.find(")")
2553  self.depends = split_comma(line[1:i].strip(), self.item)
2554  line = line[i + 1 :].lstrip()
2555  if line.startswith("::"):
2556  line = line[2:].lstrip()
2557  self.items = split_comma(line)
2558  return
2559 
2560  def tofortran(self, isfix=None):
2561  return self.get_indent_tab(isfix=isfix) + "DEPEND ( %s ) %s" % (
2562  ", ".join(self.depends),
2563  ", ".join(self.items),
2564  )
2565 
2566 
2568  """
2569  CHECK ( <c-int-scalar-expr> ) [ :: ] <name>
2570 
2571  """
2572 
2573  match = re.compile(r"check\s*\(", re.I).match
2574 
2575  def process_item(self):
2576  line = self.item.get_line()[5:].lstrip()
2577  i = line.find(")")
2578  assert i != -1, repr(line)
2579  self.expr = self.item.apply_map(line[1:i].strip())
2580  line = line[i + 1 :].lstrip()
2581  if line.startswith("::"):
2582  line = line[2:].lstrip()
2583  self.value = line
2584  return
2585 
2586  def tofortran(self, isfix=None):
2587  return self.get_indent_tab(isfix=isfix) + "CHECK ( %s ) %s" % (
2588  self.expr,
2589  self.value,
2590  )
2591 
2592 
2594  """
2595  CALLSTATEMENT <c-expr>
2596  """
2597 
2598  match = re.compile(r"callstatement\b", re.I).match
2599 
2600  def process_item(self):
2601  self.expr = self.item.apply_map(self.item.get_line()[13:].lstrip())
2602  return
2603 
2604  def tofortran(self, isfix=None):
2605  return self.get_indent_tab(isfix=isfix) + "CALLSTATEMENT " + self.expr
2606 
2607 
2609  """
2610  CALLPROTOARGUMENT <c-type-spec-list>
2611  """
2612 
2613  match = re.compile(r"callprotoargument\b", re.I).match
2614 
2615  def process_item(self):
2616  self.specs = self.item.apply_map(self.item.get_line()[17:].lstrip())
2617  return
2618 
2619  def tofortran(self, isfix=None):
2620  return self.get_indent_tab(isfix=isfix) + "CALLPROTOARGUMENT " + self.specs
2621 
2622 
2623 # Non-standard statements
2624 
2625 
2627  """
2628  PAUSE [ <char-literal-constant|int-literal-constant> ]
2629  """
2630 
2631  match = re.compile(r'pause\s*(\d+|\'\w*\'|"\w*"|)\Z', re.I).match
2632 
2633  def process_item(self):
2634  self.value = self.item.apply_map(self.item.get_line()[5:].lstrip())
2635  return
2636 
2637  def tofortran(self, isfix=None):
2638  if self.value:
2639  return self.get_indent_tab(isfix=isfix) + "PAUSE " + self.value
2640  return self.get_indent_tab(isfix=isfix) + "PAUSE"
2641 
2642  def analyze(self):
2643  return
2644 
2645 
2646 class Comment(Statement):
2647  """
2648 
2649  Attributes
2650 
2651  content : str
2652  Content of the comment.
2653  is_blank : bool
2654  When True then Comment represents blank line.
2655 
2656  """
2657 
2658  match = lambda s: True
2659 
2660  def process_item(self):
2661  assert self.item.comment.count("\n") <= 1, repr(self.item)
2662  stripped = self.item.comment.lstrip()
2663  self.is_blank = not stripped
2664  self.content = stripped[1:] if stripped else ""
2665 
2666  def tofortran(self, isfix=None):
2667  if self.is_blank:
2668  return ""
2669  if isfix:
2670  tab = "C" + self.get_indent_tab(isfix=isfix)[1:]
2671  else:
2672  tab = self.get_indent_tab(isfix=isfix) + "!"
2673  return tab + self.content
2674 
2675  def analyze(self):
2676  return
def tofortran(self, isfix=None)
Definition: statements.py:777
def get_indent_tab(self, deindent=False, isfix=None)
def tofortran(self, isfix=None)
Definition: statements.py:2393
def populate_use_provides(self, all_mod_provides, use_provides, name, rename=None)
Definition: statements.py:1347
def tofortran(self, isfix=None)
Definition: statements.py:373
def tofortran(self, isfix=None)
Definition: statements.py:1266
def tofortran(self, isfix=None)
Definition: statements.py:2281
def tofortran(self, isfix=None)
Definition: statements.py:2331