Source code for f90wrap.codegen
# f90wrap: F90 to Python interface generator with derived type support
#
# Copyright James Kermode 2011-2018
#
# This file is part of f90wrap
# For the latest version see github.com/jameskermode/f90wrap
#
# f90wrap is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# f90wrap is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with f90wrap. If not, see <http://www.gnu.org/licenses/>.
#
# If you would like to license the source code under different terms,
# please contact James Kermode, james.kermode@gmail.com
[docs]
class CodeGenerator(object):
"""
Simple class to handle code generation.
Handles simple tasks such as indent/dedent and continuation symbols.
Parameters
----------
indent : `str`
Specification of the indent size/type. Typical choices may be ``" "*4``
or ``"\t"``.
max_length : `int`
Maximum length of a code line.
continuation : `str`
Symbol to represent continuation in the desired language (eg. '&' in
Fortran)
comment : `str`
Character used to define comments (e.g. '!' in Fortran, '#' in Python)
"""
def __init__(self, indent, max_length, continuation, comment):
self._indent = indent
self.max_length = max_length
self.continuation = continuation
self.comment = comment
self.level = 0
self.code = []
[docs]
def indent(self):
"""Indent code level"""
self.level += 1
[docs]
def dedent(self):
"""Dedent code level"""
self.level -= 1
[docs]
def write(self, *args):
"""
Write arbitrary string arguments to the instance's code, split by
newline characters and implied newline after last arg.
"""
if args == ():
args = ('\n',)
args = ' '.join(args).rstrip() + '\n'
lines = args.splitlines(True)
self.code.extend([self._indent * self.level + line for line in lines])
[docs]
def writelines(self, items, insert=None, level=None):
"""
Write the given code lines to the instance's code.
Parameters
----------
items : list of strings
A list of code lines to be appended to the instance's code.
Newline characters with strings will automatically be propagated
into the code.
insert : integer or None
If present, insert lines after index `insert` rather than appending
level : integer or None
If present, override the current indent level
Returns
-------
index : index for next line to be added (equal to len(code) if insert=None)
"""
if level is None:
level = self.level
lines = []
for item in items:
item_lines = item.splitlines(True)
if not item_lines[-1].endswith('\n'):
item_lines[-1] += '\n'
lines.extend(item_lines)
lines = [self._indent * level + line for line in lines]
if insert is not None:
self.code = self.code[:insert] + lines + self.code[insert:]
return insert + len(lines)
else:
self.code.extend(lines)
return len(self.code)
[docs]
def split_long_lines(self):
"""
Split lines longer than `max_length` using `continuation`
Ignores lines starting with comment marker
"""
out = []
for line in self.code:
if len(line) > self.max_length and not line.strip().startswith(self.comment):
indent = line[:len(line) - len(line.lstrip())]
tokens = line.split()
split_lines = [[]]
while tokens:
token = tokens.pop(0)
current_line = ' '.join(split_lines[-1])
if len(current_line) == 0 or len(current_line) + len(token) < self.max_length:
split_lines[-1].append(token)
else:
split_lines[-1].append(self.continuation)
split_lines.append([self._indent + token])
out.extend([indent + ' '.join(line) + '\n' for line in split_lines])
else:
out.append(line)
return out
def __str__(self):
return "".join(self.split_long_lines())