Beispiel #1
0
def entry(control, name, data, date):
    db = toml.load(control.dbfile)

    if date == None:
        date = datetime.date.today()

    if name in db:

        reps, weight, rir = re.match(r'(\d+)x(\d+\.?\d*)r?(\d*)',
                                     data).groups()
        if rir == '':
            rir = 0
        csv_buffer = StringIO(db[name]['amraps'])
        fieldnames = csv_buffer.__next__()
        fieldnames = fieldnames.rstrip()
        fieldnames = fieldnames.split(',')
        csv_buffer.seek(0, 2)
        wtr = csv.DictWriter(csv_buffer, fieldnames=fieldnames)
        wtr.writerow({
            'date': date,
            'reps': reps,
            'weight': weight,
            'rir': rir
        })

        csv_buffer.seek(0)
        db[name]['amraps'] = csv_buffer.read()

    else:
        # check if name is a measurement
        if name not in db['measurements']['options']:
            return

        csv_buffer = StringIO(db['measurements']['data'])
        fieldnames = csv_buffer.__next__()
        fieldnames = fieldnames.rstrip()
        fieldnames = fieldnames.split(',')
        csv_buffer.seek(0, 2)
        wtr = csv.DictWriter(csv_buffer, fieldnames=fieldnames)
        wtr.writerow({
            'date': date,
            'name': name,
            'measurement': data,
        })

        csv_buffer.seek(0)
        db['measurements']['data'] = csv_buffer.read()

    with open(control.dbfile, 'w') as out_toml:
        toml.dump(db, out_toml)
Beispiel #2
0
class MockConfigFile(object):
    def __init__(self, buffer=""):
        self.set_contents(buffer)

    def __enter__(self):
        return self.buffer

    def __exit__(self, *args, **kwargs):
        self.set_contents(self.buffer.getvalue())

    def __call__(self, path, mode):
        if mode == "w":
            self.buffer = StringIO()

        return self

    def read(self):
        return self.buffer.read()

    def write(self, str):
        return self.buffer.write(str)

    def close(self):
        self.set_contents(self.buffer.getvalue())

    def readline(self):
        return self.buffer.readline()

    def __next__(self):
        return self.buffer.__next__()

    def __iter__(self):
        return self

    def set_contents(self, buffer):
        self.buffer = StringIO(dedent(buffer).strip())
Beispiel #3
0
def entry(control, exercise, amrap, date):
    db = toml.load(control.dbfile)

    if date == None:
        date = datetime.date.today()

    reps, weight = re.match(r'(\d+)x(\d+\.?\d*)', amrap).groups()
    csv_buffer = StringIO(db[exercise]['amraps'])
    fieldnames = csv_buffer.__next__()
    fieldnames = fieldnames.rstrip()
    fieldnames = fieldnames.split(',')
    csv_buffer.seek(0, 2)
    wtr = csv.DictWriter(csv_buffer, fieldnames=fieldnames)
    wtr.writerow({
        'date': date,
        'reps': reps,
        'weight': weight,
    })

    csv_buffer.seek(0)
    db[exercise]['amraps'] = csv_buffer.read()

    with open(control.dbfile, 'w') as out_toml:
        toml.dump(db, out_toml)
Beispiel #4
0
class FortranReader(object):
    """
    An iterator which will convert a free-form Fortran source file into
    a format more conducive for analyzing. It does the following:
        - combine line continuations into one
        - remove any normal comments and any comments following an ampersand
          (line continuation)
        - if there are documentation comments preceding a piece of code, buffer
          them and return them after the code, but before any documentation
          following it
        - keep any documentation comments and, if they are at the end of a line
          of actual code, place them on a new line
        - removes blank lines and trailing white-space
        - split lines along semicolons
    """

    # Regexes
    COM_RE = re.compile("^([^\"'!]|(\'[^']*')|(\"[^\"]*\"))*(!.*)$")
    SC_RE = re.compile("^([^;]*);(.*)$")

    def __init__(self,
                 filename,
                 docmark='!',
                 predocmark='',
                 docmark_alt='',
                 predocmark_alt='',
                 fixed=False,
                 length_limit=True,
                 preprocessor=None,
                 macros=[],
                 inc_dirs=[]):
        self.name = filename

        # Check that none of the docmarks are the same
        if docmark == predocmark != '':
            raise Exception('Error: docmark and predocmark are the same.')
        if docmark == docmark_alt != '':
            raise Exception('Error: docmark and docmark_alt are the same.')
        if docmark == predocmark_alt != '':
            raise Exception('Error: docmark and predocmark_alt are the same.')
        if docmark_alt == predocmark != '':
            raise Exception('Error: docmark_alt and predocmark are the same.')
        if docmark_alt == predocmark_alt != '':
            raise Exception(
                'Error: docmark_alt and predocmark_alt are the same.')
        if predocmark == predocmark_alt != '':
            raise Exception(
                'Error: predocmark and predocmark_alt are the same.')

        if preprocessor:
            # Populate the macro definition and include directory path from
            # the input lists.  To define a macro we prepend '-D' and for an
            # include path we prepend '-I'.  It's important that we do not
            # prepend to an empty string as 'cpp ... -D file.F90' doesn't do
            # what is desired; use filter to remove these.
            macros = ['-D' + mac.strip() for mac in filter(None, macros)]
            incdirs = ['-I' + d.strip() for d in filter(None, inc_dirs)]
            preprocessor = preprocessor + macros + incdirs + [filename]
            fpp = subprocess.Popen(preprocessor,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   universal_newlines=True)
            (out, err) = fpp.communicate()

            if len(err) > 0:
                print('Warning: error preprocessing ' + filename)
                print(err)
                self.reader = open(filename, 'r')
            else:
                self.reader = StringIO(str(out))
        else:
            self.reader = open(filename, 'r')

        if fixed:
            self.reader = convertToFree(self.reader, length_limit)

        self.fixed = fixed
        self.length_limit = length_limit
        self.inc_dirs = inc_dirs
        self.docbuffer = []
        self.pending = []
        self.prevdoc = False
        self.reading_alt = 0
        self.docmark = docmark
        self.doc_re = re.compile(
            "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                re.escape(docmark)))
        self.predocmark = predocmark
        if len(self.predocmark) != 0:
            self.predoc_re = re.compile(
                "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                    re.escape(predocmark)))
        else:
            self.predoc_re = None
        self.docmark_alt = docmark_alt
        if len(self.docmark_alt) != 0:
            self.doc_alt_re = re.compile(
                "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                    re.escape(docmark_alt)))
        else:
            self.doc_alt_re = None
        self.predocmark_alt = predocmark_alt
        if len(self.predocmark_alt) != 0:
            self.predoc_alt_re = re.compile(
                "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                    re.escape(predocmark_alt)))
        else:
            self.predoc_alt_re = None

    def __iter__(self):
        return self

    def pass_back(self, line):
        self.pending.insert(0, line)

    #for Python 2:
    def next(self):
        return self.__next__()

    def __next__(self):  # Python 3
        # If there are any lines waiting to be returned, return them
        if len(self.pending) != 0:
            self.include()
            self.prevdoc = False
            return self.pending.pop(0)
        # If there are any documentation lines waiting to be returned, return them.
        # This can happen for inline and preceding docs
        elif len(self.docbuffer) != 0:
            self.prevdoc = True
            return self.docbuffer.pop(0)

        # Loop through the source code until you have a complete line (including
        # all line continuations), or a complete preceding doc block
        done = False
        continued = False
        reading_predoc = False
        reading_predoc_alt = 0
        linebuffer = ""
        count = 0
        while not done:
            count += 1

            if (sys.version_info[0] > 2):
                line = self.reader.__next__()
            else:  #Python 2
                line = self.reader.next()
            if len(line.strip()) > 0 and line.strip()[0] == '#': continue

            # Capture any preceding documenation comments
            if self.predoc_re:
                match = self.predoc_re.match(line)
            else:
                match = False
            if match:
                # Switch to predoc: following comment lines are predoc until the end of the block
                reading_predoc = True
                self.reading_alt = 0
                readeing_predoc_alt = 0
                # Substitute predocmark with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1 + len(self.predocmark):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception(
                        "Preceding documentation lines can not be inline")

            # Check for alternate preceding documentation
            if self.predoc_alt_re:
                match = self.predoc_alt_re.match(line)
            else:
                match = False
            if match:
                # Switch to doc_alt: following comment lines are documentation until end of the block
                reading_predoc_alt = 1
                self.reading_alt = 0
                reading_predoc = False
                # Substitute predocmark_alt with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1 +
                                                   len(self.predocmark_alt):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception(
                        "Alternate documentation lines can not be inline")

            # Check for alternate succeeding documentation
            if self.doc_alt_re:
                match = self.doc_alt_re.match(line)
            else:
                match = False
            if match:
                # Switch to doc_alt: following comment lines are documentation until end of the block
                self.reading_alt = 1
                reading_predoc = False
                reading_predoc_alt = 0
                # Substitute predocmark_alt with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1 + len(self.docmark_alt):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception(
                        "Alternate documentation lines can not be inline")

            # Capture any documentation comments
            match = self.doc_re.match(line)
            if match:
                self.reading_alt = 0
                reading_predoc_alt = 0
                self.docbuffer.append(match.group(4))
                line = line[0:match.start(4)]

            if len(line.strip()) == 0 or line.strip()[0] != '!':
                self.reading_alt = 0

            if len(line.strip()) != 0 and line.strip()[0] != '!':
                reading_predoc_alt = 0

            # Remove any regular comments, unless following an alternative (pre)docmark
            match = self.COM_RE.match(line)
            if match:
                if (reading_predoc_alt > 1 or self.reading_alt > 1) and len(
                        line[0:match.start(4)].strip()) == 0:
                    tmp = match.group(4)
                    tmp = tmp[:1] + self.docmark + tmp[1:]
                    self.docbuffer.append(tmp)
                line = line[0:match.start(4)]
            line = line.strip()

            # If this is a blank line following previous documentation, return
            # a line of empty documentation.
            if len(line) == 0:
                if self.prevdoc and len(self.docbuffer) == 0:
                    #~ self.prevdoc = False
                    self.docbuffer.append("!" + self.docmark)
            else:
                reading_predoc = False
                reading_predoc_alt = 0
                self.reading_alt = 0
                # Check if line is immediate continuation of previous
                if line[0] == '&':
                    if continued:
                        line = line[1:]
                    else:
                        raise Exception(
                            "Can not start a new line in Fortran with \"&\"")
                else:
                    linebuffer = linebuffer.strip() + ' '
                # Check if line will be continued
                if line[-1] == '&':
                    continued = True
                    line = line[0:-1]
                else:
                    continued = False

            if self.reading_alt > 0:
                self.reading_alt += 1

            if reading_predoc_alt > 0:
                reading_predoc_alt += 1

            # Add this line to the buffer then check whether we're done here
            linebuffer += line
            #~ print(((len(self.docbuffer) > 0) or (len(linebuffer) > 0)), not continued, not reading_predoc, (reading_predoc_alt == 0))
            done = (((len(self.docbuffer) > 0) or (len(linebuffer) > 0))
                    and not continued and not reading_predoc
                    and (reading_predoc_alt == 0))

        # Split buffer with semicolons
        #~ print(count,linebuffer,len(linebuffer))
        frags = ford.utils.quote_split(';', linebuffer)
        self.pending.extend([s.strip() for s in frags if len(s) > 0])

        # Return the line
        if len(self.pending) > 0:
            self.include()
            self.prevdoc = False
            return self.pending.pop(0)
        else:
            tmp = self.docbuffer.pop(0)
            if tmp != "!" + self.docmark:
                self.prevdoc = True
            return tmp

    def include(self):
        """
        If the next line is an include statement, inserts the contents
        of the included file into the pending buffer.
        """
        if len(self.pending
               ) == 0 or not self.pending[0].startswith('include '):
            return
        name = self.pending.pop(0)[8:].strip()[1:-1]
        for b in [os.path.dirname(self.name)] + self.inc_dirs:
            pname = os.path.abspath(os.path.expanduser(os.path.join(b, name)))
            if os.path.isfile(pname):
                name = pname
                break
        else:
            raise Exception('Can not find include file "{}".'.format(name))
        self.pending = list(
            FortranReader(name,
                          self.docmark,
                          self.predocmark,
                          self.docmark_alt,
                          self.predocmark_alt,
                          self.fixed,
                          self.length_limit,
                          inc_dirs=self.inc_dirs)) + self.pending
Beispiel #5
0
class FortranReader(object):
    """
    An iterator which will convert a free-form Fortran source file into
    a format more conducive for analyzing. It does the following:
        - combine line continuations into one
        - remove any normal comments and any comments following an ampersand
          (line continuation)
        - if there are documentation comments preceding a piece of code, buffer
          them and return them after the code, but before any documentation
          following it
        - keep any documentation comments and, if they are at the end of a line
          of actual code, place them on a new line
        - removes blank lines and trailing white-space
        - split lines along semicolons
    """

    # Regexes
    COM_RE = re.compile("^([^\"'!]|(\'[^']*')|(\"[^\"]*\"))*(!.*)$")
    SC_RE = re.compile("^([^;]*);(.*)$")

    def __init__(self,filename,docmark='!',predocmark='',docmark_alt='',
                 predocmark_alt='',preprocess=False,macros=[],inc_dirs=[]):
        self.name = filename
        
        # Check that none of the docmarks are the same
        if docmark == predocmark != '':
            raise Exception('Error: docmark and predocmark are the same.')
        if docmark == docmark_alt != '':
            raise Exception('Error: docmark and docmark_alt are the same.')
        if docmark == predocmark_alt != '':
            raise Exception('Error: docmark and predocmark_alt are the same.')
        if docmark_alt == predocmark != '':
            raise Exception('Error: docmark_alt and predocmark are the same.')
        if docmark_alt == predocmark_alt != '':
            raise Exception('Error: docmark_alt and predocmark_alt are the same.')
        if predocmark == predocmark_alt != '':
            raise Exception('Error: predocmark and predocmark_alt are the same.')
        
        if preprocess:
            macros = ['-D' + mac.strip() for mac in macros]
            incdirs = ['-I' + d.strip() for d in inc_dirs]
            fpp = subprocess.Popen(["gfortran", "-E", "-cpp", filename]+macros+incdirs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = fpp.communicate()
            if len(err) > 0:
                print('Warning: error preprocessing '+filename)
                print(err)
                self.reader = open(filename,'r')
            else:
                self.reader = StringIO(str(out))
        else:
            self.reader = open(filename,'r')
        
        self.inc_dirs = inc_dirs
        self.docbuffer = []
        self.pending = []
        self.prevdoc = False
        self.reading_alt = 0
        self.docmark = docmark
        self.doc_re = re.compile("^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(re.escape(docmark)))
        self.predocmark = predocmark
        if len(self.predocmark) != 0:
            self.predoc_re = re.compile("^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(re.escape(predocmark)))
        else:
            self.predoc_re = None
        self.docmark_alt = docmark_alt
        if len(self.docmark_alt) != 0:
            self.doc_alt_re = re.compile("^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(re.escape(docmark_alt)))
        else:
            self.doc_alt_re = None
        self.predocmark_alt = predocmark_alt
        if len(self.predocmark_alt) != 0:
            self.predoc_alt_re = re.compile("^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(re.escape(predocmark_alt)))
        else:
            self.predoc_alt_re = None

    def __iter__(self):
        return self
        
    def pass_back(self,line):
        self.pending.insert(0,line)
    
    #for Python 2:
    def next(self):
        return self.__next__()
    
    def __next__(self):   # Python 3
        # If there are any lines waiting to be returned, return them
        if len(self.pending) != 0:
            self.include()
            self.prevdoc = False
            return self.pending.pop(0)
        # If there are any documentation lines waiting to be returned, return them.
        # This can happen for inline and preceding docs
        elif len(self.docbuffer) != 0:
            self.prevdoc = True
            return self.docbuffer.pop(0)
            
        # Loop through the source code until you have a complete line (including
        # all line continuations), or a complete preceding doc block
        done = False
        continued = False
        reading_predoc = False
        reading_predoc_alt = 0
        linebuffer = ""
        count = 0
        while not done:
            count += 1
            
            if (sys.version_info[0]>2):
                line = self.reader.__next__()
            else:   #Python 2
                line = self.reader.next()

            if len(line.strip()) > 0 and line.strip()[0] == '#': continue

            # Capture any preceding documenation comments
            if self.predoc_re:
                match = self.predoc_re.match(line)
            else:
                match = False
            if match:
                # Switch to predoc: following comment lines are predoc until the end of the block
                reading_predoc = True
                self.reading_alt = 0
                readeing_predoc_alt = 0
                # Substitute predocmark with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1+len(self.predocmark):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception("Preceding documentation lines can not be inline")

            # Check for alternate preceding documentation
            if self.predoc_alt_re:
                match = self.predoc_alt_re.match(line)
            else:
                match = False
            if match:
                # Switch to doc_alt: following comment lines are documentation until end of the block
                reading_predoc_alt = 1
                self.reading_alt = 0
                reading_predoc = False
                # Substitute predocmark_alt with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1+len(self.predocmark_alt):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception("Alternate documentation lines can not be inline")

            # Check for alternate succeeding documentation
            if self.doc_alt_re:
                match = self.doc_alt_re.match(line)
            else:
                match = False
            if match:
                # Switch to doc_alt: following comment lines are documentation until end of the block
                self.reading_alt = 1
                reading_predoc = False
                reading_predoc_alt = 0
                # Substitute predocmark_alt with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1+len(self.docmark_alt):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception("Alternate documentation lines can not be inline")

            # Capture any documentation comments
            match = self.doc_re.match(line)
            if match:
                self.reading_alt = 0
                reading_predoc_alt = 0
                self.docbuffer.append(match.group(4))
                line = line[0:match.start(4)]

            if len(line.strip()) == 0 or line.strip()[0] != '!':
                self.reading_alt = 0

            if len(line.strip()) !=0 and line.strip()[0] != '!':
                reading_predoc_alt = 0

            # Remove any regular comments, unless following an alternative (pre)docmark
            match = self.COM_RE.match(line)
            if match:
                if (reading_predoc_alt > 1 or self.reading_alt > 1) and len(line[0:match.start(4)].strip()) == 0:
                    tmp = match.group(4)
                    tmp = tmp[:1] + self.docmark + tmp[1:]
                    self.docbuffer.append(tmp)
                line = line[0:match.start(4)]
            line = line.strip()

            # If this is a blank line following previous documentation, return
            # a line of empty documentation.
            if len(line) == 0:
                if self.prevdoc and len(self.docbuffer) == 0:
                    #~ self.prevdoc = False
                    self.docbuffer.append("!"+self.docmark)
            else:
                reading_predoc = False
                reading_predoc_alt = 0
                self.reading_alt = 0
                # Check if line is immediate continuation of previous
                if line[0] == '&':
                    if continued:
                        line = line[1:]
                    else:
                        raise Exception("Can not start a new line in Fortran with \"&\"")
                else:
                    linebuffer = linebuffer.strip()
                # Check if line will be continued
                if line[-1] == '&':
                    continued = True
                    line = line[0:-1]
                else:
                    continued = False

            if self.reading_alt > 0:
                self.reading_alt += 1
            
            if reading_predoc_alt > 0:
                reading_predoc_alt += 1

            # Add this line to the buffer then check whether we're done here
            linebuffer += line
            #~ print(((len(self.docbuffer) > 0) or (len(linebuffer) > 0)), not continued, not reading_predoc, (reading_predoc_alt == 0))
            done = ( ((len(self.docbuffer) > 0) or (len(linebuffer) > 0)) and
                     not continued and not reading_predoc and (reading_predoc_alt == 0) )

        # Split buffer with semicolons
        #~ print(count,linebuffer,len(linebuffer))
        frags = ford.utils.quote_split(';',linebuffer)
        self.pending.extend([ s.strip() for s in frags if len(s) > 0])
        
        # Return the line
        if len(self.pending) > 0:
            self.include()
            self.prevdoc = False
            return self.pending.pop(0)
        else:
            tmp = self.docbuffer.pop(0)
            if tmp != "!"+self.docmark:
                self.prevdoc = True;
            return tmp

    def include(self):
        """
        If the next line is an include statement, inserts the contents
        of the included file into the pending buffer.
        """
        if len(self.pending) == 0 or not self.pending[0].startswith('include '):
            return
        name = self.pending.pop(0)[8:].strip()[1:-1]
        for b in [os.path.dirname(self.name)] + self.inc_dirs:
            pname = os.path.join(b, name)
            if os.path.isfile(pname):
                name = pname
                break
        else:
            raise Exception('Can not find include file "{}".'.format(name))
        self.pending = list(FortranReader(name, self.docmark, self.predocmark, 
                                          self.docmark_alt, self.predocmark_alt,
                                          inc_dirs=self.inc_dirs)) \
                       + self.pending
Beispiel #6
0
class FortranReader(object):
    """
    An iterator which will convert a free-form Fortran source file into
    a format more conducive for analyzing. It does the following:
        - combine line continuations into one
        - remove any normal comments and any comments following an ampersand
          (line continuation)
        - if there are documentation comments preceding a piece of code, buffer
          them and return them after the code, but before any documentation
          following it
        - keep any documentation comments and, if they are at the end of a line
          of actual code, place them on a new line
        - removes blank lines and trailing white-space
        - split lines along semicolons
    """

    # Regexes
    COM_RE = re.compile("^([^\"'!]|(\'[^']*')|(\"[^\"]*\"))*(!.*)$")
    SC_RE = re.compile("^([^;]*);(.*)$")

    #TODO: Add checks that there are no conflicts between docmark, predocmark, alt marks etc.
    def __init__(self,
                 filename,
                 docmark='!',
                 predocmark='',
                 docmark_alt='',
                 predocmark_alt='',
                 preprocess=False,
                 macros=[]):
        self.name = filename

        if preprocess:
            macros = ['-D' + mac.strip() for mac in macros]
            fpp = subprocess.Popen(["gfortran", "-E", "-cpp", filename] +
                                   macros,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            (out, err) = fpp.communicate()
            if len(err) > 0:
                print('Warning: error preprocessing ' + filename)
                self.reader = open(filename, 'r')
            else:
                self.reader = StringIO(str(out))
        else:
            self.reader = open(filename, 'r')

        self.docbuffer = []
        self.pending = []
        self.prevdoc = False
        self.reading_alt = 0
        self.docmark = docmark
        self.doc_re = re.compile(
            "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                re.escape(docmark)))
        self.predocmark = predocmark
        if len(self.predocmark) != 0:
            self.predoc_re = re.compile(
                "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                    re.escape(predocmark)))
        else:
            self.predoc_re = None
        self.docmark_alt = docmark_alt
        if len(self.docmark_alt) != 0:
            self.doc_alt_re = re.compile(
                "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                    re.escape(docmark_alt)))
        else:
            self.doc_alt_re = None
        self.predocmark_alt = predocmark_alt
        if len(self.predocmark_alt) != 0:
            self.predoc_alt_re = re.compile(
                "^([^\"'!]|('[^']*')|(\"[^\"]*\"))*(!{}.*)$".format(
                    re.escape(predocmark_alt)))
        else:
            self.predoc_alt_re = None

    def __iter__(self):
        return self

    def pass_back(self, line):
        self.pending.insert(0, line)

    #for Python 2:
    def next(self):
        return self.__next__()

    def __next__(self):  # Python 3
        # If there are any lines waiting to be returned, return them
        if len(self.pending) != 0:
            self.prevdoc = False
            return self.pending.pop(0)
        # If there are any documentation lines waiting to be returned, return them.
        # This can happen for inline and preceding docs
        elif len(self.docbuffer) != 0:
            self.prevdoc = True
            return self.docbuffer.pop(0)

        # Loop through the source code until you have a complete line (including
        # all line continuations), or a complete preceding doc block
        done = False
        continued = False
        reading_predoc = False
        reading_predoc_alt = 0
        linebuffer = ""
        count = 0
        while not done:
            count += 1

            if (sys.version_info[0] > 2):
                line = self.reader.__next__()
            else:  #Python 2
                line = self.reader.next()

            if len(line.strip()) > 0 and line.strip()[0] == '#': continue

            # Capture any preceding documenation comments
            if self.predoc_re:
                match = self.predoc_re.match(line)
            else:
                match = False
            if match:
                # Switch to predoc: following comment lines are predoc until the end of the block
                reading_predoc = True
                self.reading_alt = 0
                readeing_predoc_alt = 0
                # Substitute predocmark with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1 + len(self.predocmark):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception(
                        "Preceding documentation lines can not be inline")

            # Check for alternate preceding documentation
            if self.predoc_alt_re:
                match = self.predoc_alt_re.match(line)
            else:
                match = False
            if match:
                # Switch to doc_alt: following comment lines are documentation until end of the block
                reading_predoc_alt = 1
                self.reading_alt = 0
                reading_predoc = False
                # Substitute predocmark_alt with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1 +
                                                   len(self.predocmark_alt):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception(
                        "Alternate documentation lines can not be inline")

            # Check for alternate succeeding documentation
            if self.doc_alt_re:
                match = self.doc_alt_re.match(line)
            else:
                match = False
            if match:
                # Switch to doc_alt: following comment lines are documentation until end of the block
                self.reading_alt = 1
                reading_predoc = False
                reading_predoc_alt = 0
                # Substitute predocmark_alt with docmark
                tmp = match.group(4)
                tmp = tmp[:1] + self.docmark + tmp[1 + len(self.docmark_alt):]
                self.docbuffer.append(tmp)
                if len(line[0:match.start(4)].strip()) > 0:
                    raise Exception(
                        "Alternate documentation lines can not be inline")

            # Capture any documentation comments
            match = self.doc_re.match(line)
            if match:
                self.reading_alt = 0
                reading_predoc_alt = 0
                self.docbuffer.append(match.group(4))
                line = line[0:match.start(4)]

            if len(line.strip()) == 0 or line.strip()[0] != '!':
                self.reading_alt = 0

            if len(line.strip()) != 0 and line.strip()[0] != '!':
                reading_predoc_alt = 0

            # Remove any regular comments, unless following an alternative (pre)docmark
            match = self.COM_RE.match(line)
            if match:
                if (reading_predoc_alt > 1 or self.reading_alt > 1) and len(
                        line[0:match.start(4)].strip()) == 0:
                    tmp = match.group(4)
                    tmp = tmp[:1] + self.docmark + tmp[1:]
                    self.docbuffer.append(tmp)
                line = line[0:match.start(4)]
            line = line.strip()

            # If this is a blank line following previous documentation, return
            # a line of empty documentation.
            if len(line) == 0:
                if self.prevdoc and len(self.docbuffer) == 0:
                    #~ self.prevdoc = False
                    self.docbuffer.append("!" + self.docmark)
            else:
                reading_predoc = False
                reading_predoc_alt = 0
                self.reading_alt = 0
                # Check if line is immediate continuation of previous
                if line[0] == '&':
                    if continued:
                        line = line[1:]
                    else:
                        raise Exception(
                            "Can not start a new line in Fortran with \"&\"")
                else:
                    linebuffer = linebuffer.strip()
                # Check if line will be continued
                if line[-1] == '&':
                    continued = True
                    line = line[0:-1]
                else:
                    continued = False

            if self.reading_alt > 0:
                self.reading_alt += 1

            if reading_predoc_alt > 0:
                reading_predoc_alt += 1

            # Add this line to the buffer then check whether we're done here
            linebuffer += line
            #~ print(((len(self.docbuffer) > 0) or (len(linebuffer) > 0)), not continued, not reading_predoc, (reading_predoc_alt == 0))
            done = (((len(self.docbuffer) > 0) or (len(linebuffer) > 0))
                    and not continued and not reading_predoc
                    and (reading_predoc_alt == 0))

        # Split buffer with semicolons
        #~ print(count,linebuffer,len(linebuffer))
        frags = ford.utils.quote_split(';', linebuffer)
        self.pending.extend([s.strip() for s in frags if len(s) > 0])

        # Return the line
        if len(self.pending) > 0:
            self.prevdoc = False
            return self.pending.pop(0)
        else:
            tmp = self.docbuffer.pop(0)
            if tmp != "!" + self.docmark:
                self.prevdoc = True
            return tmp