fparser Reference Guide  0.0.14
api.py
1 # Modified work Copyright (c) 2017-2018 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 """Public API for Fortran parser.
67 
68 Module content
69 --------------
70 """
71 # Author: Pearu Peterson <pearu@cens.ioc.ee>
72 # Created: Oct 2006
73 
74 
75 # import all Statement classes:
76 from fparser.common.base_classes import classes
77 from fparser.common.utils import AnalyzeError
78 
79 __autodoc__ = ["get_reader", "parse", "walk"]
80 
81 
82 def get_reader(
83  source,
84  isfree=None,
85  isstrict=None,
86  include_dirs=None,
87  source_only=None,
88  ignore_comments=True,
89 ):
90  """
91  Returns Fortran reader instance.
92 
93  If ``source`` is a C filename then the functions searches for comment
94  lines starting with ``/*f2py`` and reads following lines as PYF file
95  content until a line ``*/`` is found.
96 
97  :param str source: Specify a string or filename containing Fortran code.
98  :param bool isfree: True if Fortran is free format
99  :param bool isstrict: True if we are to strictly enforce free/fixed format
100  :param list include_dirs: Specify a list of include directories. The
101  default list (when include_dirs=None) contains
102  the current working directory and the directory
103  of ``source``.
104  :param list source_only: Specify a list of Fortran file names that are
105  searched when the ``USE`` statement is
106  encountered.
107  :param bool ignore_comments: Whether or not to ignore (and discard)
108  comments when parsing the source.
109 
110  :returns: a reader instance
111  :rtype: :py:class:`fparser.common.readfortran.FortranReader`
112  """
113  import os
114  import re
115  from fparser.common.readfortran import FortranFileReader, FortranStringReader
116  from fparser.common.sourceinfo import FortranFormat
117 
118  if os.path.isfile(source):
119  _name, ext = os.path.splitext(source)
120  if ext.lower() in [".c"]:
121  # get signatures from C file comments starting with
122  # `/*f2py` and ending with `*/`.
123  # TODO: improve parser to take line number offset making line
124  # numbers in parser messages correct.
125  f2py_c_comments = re.compile(r"/[*]\s*f2py\s.*[*]/", re.I | re.M)
126  handle = open(source, "r")
127  c_input = ""
128  for line in f2py_c_comments.findall(handle.read()):
129  c_input += line[2:-2].lstrip()[4:] + "\n"
130  handle.close()
131  if isfree is None:
132  isfree = True
133  if isstrict is None:
134  isstrict = True
135  return parse(c_input, isfree, isstrict, include_dirs)
136  reader = FortranFileReader(
137  source,
138  include_dirs=include_dirs,
139  source_only=source_only,
140  ignore_comments=ignore_comments,
141  )
142  elif isinstance(source, str):
143  reader = FortranStringReader(
144  source,
145  include_dirs=include_dirs,
146  source_only=source_only,
147  ignore_comments=ignore_comments,
148  )
149  else:
150  raise TypeError("Expected string or filename input but got %s" % (type(input)))
151  if isfree is None:
152  isfree = reader.format.is_free
153  if isstrict is None:
154  isstrict = reader.format.is_strict
155  reader.set_format(FortranFormat(isfree, isstrict))
156  return reader
157 
158 
159 def parse(
160  source,
161  isfree=None,
162  isstrict=None,
163  include_dirs=None,
164  source_only=None,
165  ignore_comments=True,
166  analyze=True,
167  clear_cache=True,
168 ):
169  """
170  Parse input and return Statement tree. Raises an AnalyzeError if the
171  parser can not parse the Fortran code.
172 
173  :param str source: Specify a string or filename containing Fortran code.
174  :param bool isfree: Whether the Fortran source is free-format.
175  :param bool isstrict: Whether we are to strictly enforce the `isfree`
176  setting.
177  :param list include_dirs: Specify a list of include directories. The
178  default list (when include_dirs=None) contains
179  the current working directory and the directory
180  of ``filename``.
181  :param list source_only: A list of Fortran file names that are searched
182  when the ``USE`` statement is encountered.
183  :param bool ignore_comments: When True then discard all comment lines in
184  the Fortran code.
185  :param bool analyze: When True then apply analyze() method on the Fortran
186  code tree.
187  :param bool clear_cache: Whether or not to wipe the parser cache prior
188  to parsing. Necessary when a new tree object
189  is required, even if the Fortran to be parsed has
190  been seen before.
191 
192  :returns: Abstract Syntax Tree of Fortran source.
193  :rtype: :py:class:`fparser.api.BeginSource`
194  """
195  from fparser.one.parsefortran import FortranParser
196 
197  if clear_cache:
198  # Wipe the parser cache if requested
199  FortranParser.cache.clear()
200 
201  reader = get_reader(
202  source,
203  isfree,
204  isstrict,
205  include_dirs,
206  source_only,
207  ignore_comments=ignore_comments,
208  )
209  parser = FortranParser(reader, ignore_comments=ignore_comments)
210  try:
211  parser.parse()
212  except AnalyzeError:
213  raise
214  if analyze:
215  parser.analyze()
216 
217  return parser.block
218 
219 
220 def walk(stmt, depth=-1, _initial_depth=None):
221  """Generate Fortran statements by walking the stmt tree until given depth.
222 
223  For each block statement in stmt, the walk functions yields a
224  tuple ``(statement, depth)`` where ``depth`` is the depth of tree
225  stucture for statement.
226 
227  Parameters
228  ----------
229  stmt : Statement
230  depth : int
231  If depth is positive then walk in the tree until given depth.
232  If depth is negative then walk the whole tree.
233 
234  Returns
235  -------
236  generator
237 
238  Examples
239  --------
240 
241  ::
242 
243  from fparser import api
244  source_str = '''
245  subroutine foo
246  integer i, r
247  do i=1,100
248  r = r + i
249  end do
250  end
251  '''
252  tree = api.parse(source_str)
253  for stmt, depth in api.walk(tree):
254  print depth, stmt.item
255 
256  that will print::
257 
258  1 line #2'subroutine foo'
259  2 line #3'integer i, r'
260  2 line #4'do i=1,100'
261  3 line #5'r = r + i'
262  2 line #6'end do'
263  1 line #7'end'
264 
265  """
266  if _initial_depth is None:
267  if depth == 0:
268  return
269  _initial_depth = depth
270  if not isinstance(stmt, classes.BeginSource):
271  yield stmt, _initial_depth - depth
272  if isinstance(stmt, classes.BeginStatement):
273  last_stmt = stmt.content[-1]
274  last_index = len(stmt.content)
275  if isinstance(last_stmt, classes.EndStatement):
276  last_index -= 1
277  else:
278  last_stmt = None
279  if depth != 0:
280  for substmt in stmt.content[:last_index]:
281  for statement, statement_depth in walk(
282  substmt, depth - 1, _initial_depth
283  ):
284  yield statement, statement_depth
285  if last_stmt is not None:
286  yield last_stmt, _initial_depth - depth