fparser Reference Guide  0.0.14
typedecl_statements.py
1 # Modified work Copyright (c) 2017 Science and Technology Facilities Council
2 # Original work Copyright (c) 1999-2008 Pearu Peterson
3 
4 # All rights reserved.
5 
6 # Modifications made as part of the fparser project are distributed
7 # under the following license:
8 
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions are
11 # met:
12 
13 # 1. Redistributions of source code must retain the above copyright
14 # notice, this list of conditions and the following disclaimer.
15 
16 # 2. Redistributions in binary form must reproduce the above copyright
17 # notice, this list of conditions and the following disclaimer in the
18 # documentation and/or other materials provided with the distribution.
19 
20 # 3. Neither the name of the copyright holder nor the names of its
21 # contributors may be used to endorse or promote products derived from
22 # this software without specific prior written permission.
23 
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 
36 # --------------------------------------------------------------------
37 
38 # The original software (in the f2py project) was distributed under
39 # the following license:
40 
41 # Redistribution and use in source and binary forms, with or without
42 # modification, are permitted provided that the following conditions are met:
43 
44 # a. Redistributions of source code must retain the above copyright notice,
45 # this list of conditions and the following disclaimer.
46 # b. Redistributions in binary form must reproduce the above copyright
47 # notice, this list of conditions and the following disclaimer in the
48 # documentation and/or other materials provided with the distribution.
49 # c. Neither the name of the F2PY project nor the names of its
50 # contributors may be used to endorse or promote products derived from
51 # this software without specific prior written permission.
52 
53 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
57 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
59 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
60 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
63 # DAMAGE.
64 
65 """
66 Fortran type-declaration statements.
67 
68 """
69 
70 __all__ = [
71  "Integer",
72  "Real",
73  "DoublePrecision",
74  "Complex",
75  "DoubleComplex",
76  "Character",
77  "Logical",
78  "Byte",
79  "TypeStmt",
80  "Class",
81  "intrinsic_type_spec",
82  "declaration_type_spec",
83  "Implicit",
84 ]
85 
86 import re
87 import string
88 from fparser.common.base_classes import (
89  Statement,
90  BeginStatement,
91  EndStatement,
92  AttributeHolder,
93  Variable,
94 )
95 from fparser.common.utils import (
96  split_comma,
97  AnalyzeError,
98  name_re,
99  is_entity_decl,
100  is_name,
101  parse_array_spec,
102 )
103 
104 # Intrinsic type specification statements
105 
106 
107 class TypeDeclarationStatement(Statement):
108  """
109  <declaration-type-spec> [ [, <attr-spec>] :: ] <entity-decl-list>
110  <declaration-type-spec> = <intrinsic-type-spec>
111  | TYPE ( <derived-type-spec> )
112  | CLASS ( <derived-type-spec> )
113  | CLASS ( * )
114 
115  <derived-type-spec> = <type-name> [ ( <type-param-spec-list> ) ]
116  <type-param-spec> = [ <keyword> = ] <type-param-value>
117  <type-param-value> = <scalar-int-expr> | * | :
118 
119  <intrinsic-type-spec> = INTEGER [<kind-selector>]
120  | REAL [<kind-selector>]
121  | DOUBLE PRECISION
122  | COMPLEX [<kind-selector>]
123  | CHARACTER [<char-selector>]
124  | LOGICAL [<kind-selector>]
125 
126  <kind-selector> = ( [ KIND = ] <scalar-int-initialization-expr> )
127  EXTENSION:
128  <kind-selector> = ( [ KIND = ] <scalar-int-initialization-expr> )
129  | * <length>
130 
131  <char-selector> = <length-selector>
132  | ( LEN = <type-param-value>, KIND = <scalar-int-initialization-expr> )
133  | ( <type-param-value>, [ KIND = ] <scalar-int-initialization-expr> )
134  | ( KIND = <scalar-int-initialization-expr> [, LEN = <type-param-value>] )
135  <length-selector> = ( [ LEN = ] <type-param-value> )
136  | * <char-length> [ , ]
137  <char-length> = ( <type-param-value> ) | <scalar-int-literal-constant>
138 
139  <attr-spec> = <access-spec> | ALLOCATABLE | ASYNCHRONOUS
140  | DIMENSION ( <array-spec> ) | EXTERNAL
141  | INTENT ( <intent-spec> ) | INTRINSIC
142  | <language-binding-spec> | OPTIONAL
143  | PARAMETER | POINTER | PROTECTED | SAVE
144  | TARGET | VALUE | VOLATILE
145  <entity-decl> = <object-name> [ ( <array-spec> ) ] [ * <char-length> ] [ <initialization> ]
146  | <function-name> [ * <char-length> ]
147  <initialization> = = <initialization-expr>
148  | => NULL
149  <access-spec> = PUBLIC | PRIVATE
150  <language-binding-spec> = BIND ( C [ , NAME = <scalar-char-initialization-expr>] )
151  <array-spec> = <explicit-shape-spec-list>
152  | <assumed-shape-spec-list>
153  | <deferred-shape-spec-list>
154  | <assumed-size-spec>
155  <explicit-shape-spec> = [ <lower-bound> : ] <upper-bound>
156  <assumed-shape-spec> = [ <lower-bound> ] :
157  <deferred-shape-spec> = :
158  <assumed-size-spec> = [ <explicit-shape-spec-list> , ] [ <lower-bound> : ] *
159  <bound> = <specification-expr>
160 
161  <int-literal-constant> = <digit-string> [ _ <kind-param> ]
162  <digit-string> = <digit> [ <digit> ]..
163  <kind-param> = <digit-string> | <scalar-int-constant-name>
164  """
165 
166  _repr_attr_names = [
167  "selector",
168  "attrspec",
169  "entity_decls",
170  ] + Statement._repr_attr_names
171 
172  def process_item(self):
173  item = self.item
174  apply_map = item.apply_map
175  clsname = self.__class__.__name__.lower()
176  line = item.get_line()
177  from .block_statements import Function
178 
179  if not line.lower().startswith(clsname):
180  i = 0
181  j = 0
182  for c in line:
183  i += 1
184  if c == " ":
185  continue
186  j += 1
187  if j == len(clsname):
188  break
189  line = line[:i].replace(" ", "") + line[i:]
190 
191  assert line.lower().startswith(clsname), repr((line, clsname))
192  line = line[len(clsname) :].lstrip()
193 
194  if line.startswith("("):
195  i = line.find(")")
196  selector = apply_map(line[: i + 1].strip())
197  line = line[i + 1 :].lstrip()
198  elif line.startswith("*"):
199  selector = "*"
200  line = line[1:].lstrip()
201  if line.startswith("("):
202  i = line.find(")")
203  selector += apply_map(line[: i + 1].rstrip())
204  line = line[i + 1 :].lstrip()
205  else:
206  m = re.match(r"\d+(_\w+|)|[*]", line)
207  if not m:
208  self.isvalid = False
209  return
210  i = m.end()
211  selector += line[:i].rstrip()
212  line = line[i:].lstrip()
213  else:
214  selector = ""
215 
216  fm = Function.match(line)
217  if fm:
218  l2 = line[: fm.end()]
219  m2 = re.match(r".*?\b(?P<name>\w+)\Z", l2)
220  if not m2:
221  self.isvalid = False
222  return
223  fname = m2.group("name")
224  fitem = item.copy(clsname + selector + " :: " + fname, apply_map=True)
225  self.parent.put_item(fitem)
226  item.clone(line)
227  self.isvalid = False
228  return
229 
230  if line.startswith(","):
231  line = line[1:].lstrip()
232 
233  self.raw_selector = selector
234  if isinstance(self, Character):
235  self.selector = self._parse_char_selector(selector)
236  else:
237  self.selector = self._parse_kind_selector(selector)
238 
239  i = line.find("::")
240  if i == -1:
241  self.attrspec = []
242  self.entity_decls = split_comma(line, self.item)
243  else:
244  self.attrspec = split_comma(line[:i].rstrip(), self.item)
245  self.entity_decls = split_comma(line[i + 2 :].lstrip(), self.item)
246  for entity in self.entity_decls:
247  if not is_entity_decl(entity):
248  self.isvalid = False
249  return
250 
251  if isinstance(self.parent, Function) and self.parent.name in self.entity_decls:
252  assert self.parent.typedecl is None, repr(self.parent.typedecl)
253  self.parent.typedecl = self
254  self.ignore = True
255  if isinstance(self, Type):
256  self.name = self.selector[1].lower()
257  assert is_name(self.name), repr(self.name)
258  else:
259  self.name = clsname
260  return
261 
262  def _parse_kind_selector(self, selector):
263  if not selector:
264  return "", ""
265  length, kind = "", ""
266  if selector.startswith("*"):
267  length = selector[1:].lstrip()
268  else:
269  assert selector[0] + selector[-1] == "()", repr(selector)
270  l = selector[1:-1].strip()
271  if l.lower().startswith("kind"):
272  l = l[4:].lstrip()
273  if l[0] + l[-1] == "()":
274  kind = "kind" + l
275  else:
276  assert l.startswith("="), repr(l)
277  kind = l[1:].lstrip()
278  else:
279  kind = l
280  return length, kind
281 
282  def _split_char_selector(self, line):
283  """line=``[key=]value`` -> key, value.
284  If line does not have name part then return None, value.
285  """
286  for name in ["len", "kind"]:
287  if line[: len(name)].lower() == name:
288  value_part = line[len(name) :].lstrip()
289  if value_part.startswith("="):
290  return name, value_part[1:].lstrip()
291  return None, line
292 
293  def _parse_char_selector(self, selector):
294  if not selector:
295  return "", ""
296  if selector.startswith("*"):
297  l = selector[1:].lstrip()
298  if l.startswith("("):
299  if l.endswith(","):
300  l = l[:-1].rstrip()
301  assert l.endswith(")"), repr(l)
302  l = l[1:-1].strip()
303  if l.lower().startswith("len"):
304  l = l[3:].lstrip()[1:].lstrip()
305  kind = ""
306  else:
307  assert selector[0] + selector[-1] == "()", repr(selector)
308  l = split_comma(selector[1:-1].strip(), self.item)
309  if len(l) == 1:
310  l = l[0]
311  key, value = self._split_char_selector(l)
312  if key == "len":
313  kind, l = "", value
314  elif key == "kind":
315  kind, l = value, ""
316  else:
317  kind = ""
318  else:
319  assert len(l) == 2, repr(l)
320  key0, value0 = self._split_char_selector(l[0])
321  key1, value1 = self._split_char_selector(l[1])
322  if key0 == "len":
323  assert key1 in [None, "kind"], repr(key1)
324  l, kind = value0, value1
325  elif key0 == "kind":
326  assert key1 == "len", repr(key1)
327  l, kind = value1, value0
328  else:
329  assert key0 is None, repr(key0)
330  assert key1 in [None, "kind"], repr(key1)
331  l, kind = value0, value1
332  return l, kind
333 
334  def tostr(self):
335  """Create a text representation of this object and return it"""
336  clsname = self.__class__.__name__.upper()
337  text = ""
338  length, kind = self.selector
339  if isinstance(self, Character):
340  if length and kind:
341  text += "(LEN=%s, KIND=%s)" % (length, kind)
342  elif length:
343  text += "(LEN=%s)" % (length)
344  elif kind:
345  text += "(KIND=%s)" % (kind)
346  elif isinstance(self, Type):
347  text += "(%s)" % (kind)
348  elif isinstance(self, Class):
349  if kind:
350  # For a class declaration, 'kind' is actually the class
351  # that the variable is an instance of. Therefore there
352  # is no "(KIND=xxx)", just (xxx).
353  text += "({0})".format(kind)
354  else:
355  if length:
356  text += "*%s" % (length)
357  if kind:
358  text += "(KIND=%s)" % (kind)
359 
360  return clsname + text
361 
362  def tofortran(self, isfix=None):
363  tab = self.get_indent_tab(isfix=isfix)
364  s = self.tostr()
365  if self.attrspec:
366  s += ", " + ", ".join(self.attrspec)
367  # If we were to change fparser so that it always produces the
368  # '::' separator then we'd simply comment-out the if below.
369  if self.attrspec or "=" in str(self.entity_decls):
370  s += " ::"
371  if self.entity_decls:
372  s += " " + ", ".join(self.entity_decls)
373  return tab + s
374 
375  def __str__(self):
376  return self.tofortran()
377 
378  def __eq__(self, other):
379  if self.__class__ is not other.__class__:
380  return False
381  return self.selector == other.selector
382 
383  def astypedecl(self):
384  if self.entity_decls or self.attrspec:
385  return self.__class__(self.parent, self.item.copy(self.tostr()))
386  return self
387 
388  def analyze(self):
389  if not self.entity_decls:
390  return
391  variables = self.parent.a.variables
392  typedecl = self.astypedecl()
393  attrspec = self.attrspec[:]
394  access_spec_lst = [a for a in attrspec if a.lower() in ["private", "public"]]
395  if access_spec_lst:
396  access_spec = access_spec_lst[0]
397  attrspec.remove(access_spec)
398  else:
399  access_spec = None
400  for item in self.entity_decls:
401  name, array_spec, char_length, value = self._parse_entity(item)
402  var = self.parent.get_variable(name)
403  var.add_parent(self)
404  if char_length:
405  var.set_length(char_length)
406  var.set_type(typedecl)
407  var.update(self.attrspec)
408  if array_spec:
409  var.set_bounds(array_spec)
410  if value:
411  var.set_init(value)
412  if access_spec is not None:
413  l = getattr(self.parent.a, access_spec.lower() + "_id_list")
414  l.append(name)
415  var.analyze()
416  return
417 
418  def _parse_entity(self, line):
419  m = name_re(line)
420  assert m, repr((line, self.item, self.__class__.__name__))
421  name = line[: m.end()]
422  line = line[m.end() :].lstrip()
423  array_spec = None
424  char_length = None
425  value = None
426  if line:
427  item = self.item.copy(line)
428  line = item.get_line()
429  if line.startswith("("):
430  i = line.find(")")
431  assert i != -1, repr(line)
432  array_spec = parse_array_spec(line[1:i].strip(), item)
433  line = line[i + 1 :].lstrip()
434 
435  if line.startswith("*"):
436  i = line.find("=")
437  if i == -1:
438  char_length = item.apply_map(line[1:].lstrip())
439  line = ""
440  else:
441  char_length = item.apply_map(line[1:i].strip())
442  line = line[i:]
443  if line.startswith("="):
444  value = item.apply_map(line[1:].lstrip())
445  return name, array_spec, char_length, value
446 
447  def get_zero_value(self):
448  raise NotImplementedError(repr(self.__class__.__name__))
449 
450  def assign_expression(self, name, value):
451  return "%s = %s" % (name, value)
452 
453  def get_kind(self):
454  return self.selector[1] or self.default_kind
455 
456  def get_length(self):
457  return self.selector[0] or 1
458 
459  def get_byte_size(self):
460  length, kind = self.selector
461  if length:
462  return int(length)
463  if kind:
464  return int(kind)
465  return self.default_kind
466 
467  def is_intrinsic(self):
468  return not isinstance(self, (Type, Class))
469 
470  def is_derived(self):
471  return isinstance(self, Type)
472 
473  def is_numeric(self):
474  return isinstance(
475  self, (Integer, Real, DoublePrecision, Complex, DoubleComplex, Byte)
476  )
477 
478  def is_nonnumeric(self):
479  return isinstance(self, (Character, Logical))
480 
481 
483  match = re.compile(r"integer\b", re.I).match
484  default_kind = 4
485 
486  def get_zero_value(self):
487  kind = self.get_kind()
488  if kind == self.default_kind:
489  return "0"
490  return "0_%s" % (kind)
491 
492 
494  match = re.compile(r"real\b", re.I).match
495  default_kind = 4
496 
497  def get_zero_value(self):
498  kind = self.get_kind()
499  if kind == self.default_kind:
500  return "0.0"
501  return "0_%s" % (kind)
502 
503 
505  match = re.compile(r"double\s*precision\b", re.I).match
506  default_kind = 8
507 
508  def get_byte_size(self):
509  return self.default_kind
510 
511  def get_zero_value(self):
512  return "0.0D0"
513 
514 
516  match = re.compile(r"complex\b", re.I).match
517  default_kind = 4
518 
519  def get_byte_size(self):
520  length, kind = self.selector
521  if length:
522  return int(length)
523  if kind:
524  return 2 * int(kind)
525  return 2 * self.default_kind
526 
527  def get_zero_value(self):
528  kind = self.get_kind()
529  if kind == self.default_kind:
530  return "(0.0, 0.0)"
531  return "(0.0_%s, 0.0_%s)" % (kind, kind)
532 
533  def get_part_typedecl(self):
534  bz = self.get_byte_size() / 2
535  return Real(self.parent, self.item.copy("REAL*%s" % (bz)))
536 
537 
539  # not in standard
540  match = re.compile(r"double\s*complex\b", re.I).match
541  default_kind = 8
542 
543  def get_byte_size(self):
544  return 2 * self.default_kind
545 
546  def get_zero_value(self):
547  return "(0.0D0,0.0D0)"
548 
549 
551  match = re.compile(r"logical\b", re.I).match
552  default_kind = 4
553 
554  def get_zero_value(self):
555  return ".FALSE."
556 
557 
559  match = re.compile(r"character\b", re.I).match
560  default_kind = 1
561 
562  def get_zero_value(self):
563  return "''"
564 
565 
567  # not in standard
568  match = re.compile(r"byte\b", re.I).match
569  default_kind = 1
570 
571  def get_zero_value(self):
572  return "0"
573 
574 
576  match = re.compile(r"type\s*\(", re.I).match
577 
578  def get_zero_value(self):
579  type_decl = self.get_type_decl(self.name)
580  component_names = type_decl.a.component_names
581  components = type_decl.a.components
582  l = []
583  for name in component_names:
584  var = components[name]
585  l.append(var.typedecl.get_zero_value())
586  return "%s(%s)" % (type_decl.name, ", ".join(l))
587 
588  def get_kind(self):
589  # See 4.5.2, page 48
590  raise NotImplementedError(repr(self.__class__.__name__))
591 
592 
593 TypeStmt = Type
594 
595 
597  match = re.compile(r"class\s*\(", re.I).match
598 
599 
600 class Implicit(Statement):
601  """
602  IMPLICIT <implicit-spec-list>
603  IMPLICIT NONE
604  <implicit-spec> = <declaration-type-spec> ( <letter-spec-list> )
605  <letter-spec> = <letter> [ - <letter> ]
606  """
607 
608  match = re.compile(r"implicit\b", re.I).match
609 
610  letters = string.ascii_lowercase
611 
612  def process_item(self):
613  line = self.item.get_line()[8:].lstrip()
614  if line.lower() == "none":
615  self.items = []
616  return
617  items = []
618  for item in split_comma(line, self.item):
619  i = item.find("(")
620  assert i != -1 and item.endswith(")"), repr(item)
621  specs = []
622  for spec in split_comma(item[i + 1 : -1].strip(), self.item):
623  if "-" in spec:
624  s, e = spec.lower().split("-")
625  s = s.strip()
626  e = e.strip()
627  assert s in self.letters and e in self.letters, repr((s, e))
628  else:
629  e = s = spec.lower().strip()
630  assert s in self.letters, repr((s, e))
631  specs.append((s, e))
632  tspec = item[:i].rstrip()
633  stmt = None
634  for cls in declaration_type_spec:
635  if cls.match(tspec):
636  stmt = cls(self, self.item.copy(tspec))
637  if stmt.isvalid:
638  break
639  assert stmt is not None, repr((item, line))
640  items.append((stmt, specs))
641  self.items = items
642  return
643 
644  def tofortran(self, isfix=None):
645  tab = self.get_indent_tab(isfix=isfix)
646  if not self.items:
647  return tab + "IMPLICIT NONE"
648  l = []
649  for stmt, specs in self.items:
650  l1 = []
651  for s, e in specs:
652  if s == e:
653  l1.append(s)
654  else:
655  l1.append(s + "-" + e)
656  l.append("%s ( %s )" % (stmt.tostr(), ", ".join(l1)))
657  return tab + "IMPLICIT " + ", ".join(l)
658 
659  def analyze(self):
660  """
661  Analyze the Implicit statments constructed by the parser and
662  set-up the associated implicit_rules belonging to the parent
663  of this object in the AST.
664  """
665  implicit_rules = self.parent.a.implicit_rules
666  if not self.items:
667  if implicit_rules:
668  self.warning(
669  "overriding previously set implicit rule mapping"
670  " %r." % (implicit_rules)
671  )
672  self.parent.a.implicit_rules = None
673  return
674  if implicit_rules is None:
675  self.warning("overriding previously set IMPLICIT NONE")
676  self.parent.a.implicit_rules = implicit_rules = {}
677  for stmt, specs in self.items:
678  for start, end in specs:
679  start_idx = string.ascii_lowercase.index(start.lower())
680  end_idx = string.ascii_lowercase.index(end.lower())
681  for lchar in string.ascii_lowercase[start_idx : end_idx + 1]:
682  implicit_rules[lchar] = stmt
683  return
684 
685 
686 intrinsic_type_spec = [
687  Integer,
688  Real,
689  DoublePrecision,
690  Complex,
691  DoubleComplex,
692  Character,
693  Logical,
694  Byte,
695 ]
696 declaration_type_spec = intrinsic_type_spec + [TypeStmt, Class]