#!/usr/bin/env python
# -*- coding: us-ascii -*-
#----------------------------------------------------------------------------
# Copyright (C) 2004-2018 ABINIT group (TD)
# This file is distributed under the terms of the
# GNU General Public License, see ~abinit/COPYING
# or http://www.gnu.org/copyleft/gpl.txt .
# For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
#----------------------------------------------------------------------------
# Add a module for each routine in ABINIT
# in each xxx which contains an interface
# for each subroutine inside a directory.
# Modification for the version 5.x of ABINIT
# Date: 25/10/2009
#----------------------------------------------------------------------------
#i# Lines commented: before used for #ifdef interfaces

#Principles
#----------
# 1 - Analyze without changing any part of the code
# 2 - Finally change if it is needed


#Plan
#----
# 1 - List all routines and store all in memory.
# 2 - Search all routines belonging to a directory.
# 3 - Remove all interfaces contained in the source files.
# 4 - Create a module containing the interface of all routines
#     for each directory.
# 5 - Add this file in the directory 'src/interfaces'
# 6 - Beautify the code if required
# 7 - Build some graphs concerning the calls of routines
# 8 - Copy the new version


#TODO
#----
# Add include files for dependencies
# The module ftfvw2 needs interfaces: Check if everything is correct
# Add correct children to _xxx_ files.
# Add children and parents to the files interfaces_xxx.F90.
# Add a true functionality to remove variables
# Call after a ';' is not detected.
# Handle the preprocessing directives (?)
# Unify the detection and the creation of the interfaces by means of the class Fortran_Interface


#Description
#-----------
# The script abilint uses class to implement the notion of project, files, routines and so on.
# The class Project (maybe Package is more appropriate)
# contains all the information about directories, files and routines.
# There are lists of:
# - directories (abilint.dirs) which are only names of directories,
# - files (abilint.files) which are the File objects (see File class) and
# - routines (abilint.routines) which are the Routine objects (see Routine class).
# So it is easy to access directly to an object in the project.
# The only restriction is that the names for the routines and the files have to be unique.
# The class Structure is a general class which implements the method of read or write which is
# propagated by inheritance for File, File_F90, File_F77, Module, Routine, Function, Program, Code,
# Comment, Declaration, Execution, Fortran_Type, ...

#Hierarchy of classes
#--------------------
# File_F77 is a special class of the class File_F90 with a filter to parse the code Fortran77.

# File_F90 -> Comment (Robodoc_Header, Robodoc_Footer)
#          -> Module   ----> Comment, Declaration, Routine, Function
#          -> Routine  --|
#          -> Function   |
#                        --> Comment, Header_Routine, Use, Implicit, Declaration, Execution
#                                                                    |
#                                                                    |
#                                Fortran_Type, Fortran_Interface <----
#
# The class Code is a generic class for all program structures.
#
# Each part of a code is handled by a class:
# Comment   lines of comments, especially robodoc
# Module    module fortran
# Routine   subroutine fortran
# Function  function fortran

# The classes 'Module', 'Routine', 'Function'
#              contain methods to handle the code and especially to analyze it into:
# Comment      lines of comments
# Header       header of the routine (ex. subroutine foo(one,two))
# Use          use statements
# Implicit     implicit statements
# Declaration  declaration statements of all variables
# Execution    execution statements
#
# Two special classes are not added as children because they duplicate the code:
# Fortran_Type      inherited of the class Declaration to handle fortran types.
# Fortran_Interface inherited of the class Fortran_Type to handle declaration of interfaces.
#
# The class Include is a special class to handle the include statement.
# abilint gives an ersatz of 'mpif.f' file for commodity.
from __future__ import print_function


#Usage (and help)
def usage(error=0):
    "Display the usage of abilint"
    sys.stderr.write("""
abilint [options] <source> <dest>
    where:
      <source> is the directory that contains the ABINIT project
      <dest>   is the directory of destination
    options:
      --beautify  beautify the code (experimental, only add robodoc headers)
      --dependencies build the files 'abinit.dep' and 'abinit.dir' in each src/* directories
      --dump_dtset=<filename>
                  dump in <filename> the fortran type dataset_type in cPickle format
      --graph=<routine1,routine2,...> or --graph=all (experimental)
                  build the graph of calls for the <routine> in the file 'routine.dot'
      --graph=directories
                  build the graph of interdependences between directories in the file
                  'directories.dot' (need the package graphviz and the command 'dot')
      --help      display this message
      --libraries build the files lib/xxx/_xxx_ (experimental)
      --lint      complete analysis (experimental)
      --nocache   do not use cached information and delete it
      --nofatal   no fatal message: always generate interfaces
      --verbose   display more information (very verbose)
""")
    sys.exit(error)


#Operating System (used for os.path)
import os
#filename match as Unix shell
import fnmatch
#stderr and stdout
import sys
#Options
import getopt
#Regular expressions
import re
from functools import reduce
#To dump python object in a file
try:
    import cPickle as pickle
except ImportError:
    import pickle

#Check the version of python
version_info = list(map(int,sys.version_info[0:3]))
if  version_info < [2,3,0]:
    sys.stderr.write("Detected version %d.%d.%d\n" % tuple(version_info))
    sys.stderr.write("Minimal required version is python 2.3.0")
    sys.exit(1)

#If the class 'set' does not exist use instead Set
if 'set' not in dir(__builtins__):
    from sets import Set as set

#Indentation (one blank character)
indent = " "*1

#Prefix for the modules containing the interfaces
prefix = "interfaces_"

#To add before and after statements added by abilint
abilint_start = "!This section has been created automatically by the script Abilint (TD).\n" \
                + "!Do not modify the following lines by hand.\n"
abilint_end =  "!End of the abilint section\n"

#i# use_before = abilint_start + "#ifdef HAVE_FORTRAN_INTERFACES\n"
#i# notuse_before = abilint_start + "#ifndef HAVE_FORTRAN_INTERFACES\n"
use_before = abilint_start
notuse_before = abilint_start

#i# use_after = "#endif\n" + abilint_end
#i# notuse_after = use_after
use_after = abilint_end
notuse_after = abilint_end

#End of local variables (for abirules)
re_end_variables = re.compile("[\n]*![ ]*[*]{6,}.*\n",re.MULTILINE)
end_variables = "\n!"+72*"*"+"\n"

#Intrinsic routines of the Fortran
intrinsic_routines = [ "cpu_time",
                       "date_and_time",
                       "mvbits",
                       "random_number",
                       "random_seed",
                       "system_clock" ]
#Intrinsic functions of the Fortran
intrinsic_functions = [ "abs", "achar", "acos", "adjustl", "adjustr", "aimag", "aint", "all",
                       "allocated", "anint", "any", "asin", "associated", "atan", "atan2",
                       "bit_size", "btest", "ceiling", "char", "cmplx", "conjg", "cos", "cosh",
                       "count",
                       "dabs", "dble", "dcmplx", "dfloat", "digits", "dim", "dot_product", "dprod",
                       "dreal", "eoshift", "epsilon", "exp", "exponent",
                       "float", "floor", "fraction", "huge", "iachar", "iand",
                       "ibclr", "ibits", "ibset", "ichar", "ieor", "index", "int", "ior", "ishft",
                       "ishftc", "kind", "lbound", "len", "len_trim", "lge", "lgt", "ll", "llt",
                       "log", "logical", "log10", "matmul", "max", "maxexponent", "maxloc",
                       "maxval", "merge", "min", "minexponent", "minloc", "minval", "mod", "modulo",
                       "nearest", "nint", "not", "null", "pack", "pad", "precision", "present", "product",
                       "radix", "range", "real", "repeat", "reshape", "rrspacing", "scale", "scan",
                       "selected_int_kind", "selected_real_kind", "set_exponent", "shape", "sign",
                       "sin", "sinh", "size", "spacing", "spread", "sqrt", "tan", "tanh",
                       "tiny", "transfer", "transpose", "trim", "ubound", "unpack", "verify",
                       "datan", "dcos", "dexp", "dlog", "dsin", "dsqrt", "dtan"
                      ]

#Comment "!Arguments"
comment_arguments = "!Arguments " + "-"*50 + "\n"
#Comment "!Local variables"
comment_local_variables = "!Local variables " + "-"*44 + "\n"
#Last line of declaration
comment_declaration = "!" + "*"*60

#Regular expression to detect this section
re_section_abilint = re.compile("[ \n]*" + abilint_start[:11] + ".+?" \
                        + abilint_end.rstrip() + "[^\n]*[\n]*", \
                        re.MULTILINE+re.IGNORECASE+re.DOTALL)
re_section_abilint_start = re.compile("[ \n]*" + abilint_start[:11] + ".+?" \
                        + "#if" + "[^\n]*[\n]*", \
                        re.MULTILINE+re.IGNORECASE+re.DOTALL)
re_section_abilint_end = re.compile("[ \n]*" + "#endif" + ".+?" \
                        + abilint_end.rstrip() + "[^\n]*[\n]*", \
                        re.MULTILINE+re.IGNORECASE+re.DOTALL)

#Regular Expressions
#-------------------
#Detect !Arguments
re_arguments = re.compile("[\n ]*!Argument[^\n]+\n")
#Detect !Local
re_local_variables = re.compile("[\n ]*!Local[^\n]+\n")
#Remove use replaced by the system of interfaces
re_use_replaced = re.compile('^[ ]+use[ ]*(defs_interfaces|defs_xc|defs_xfuncmpi)[ ]*?\n',re.MULTILINE+re.IGNORECASE)


#Function to split variable in declaration statements (critics and robust but ugly)
def split_variables(string):
    """Split declaration into variables: [ (varname,declaration), ...]
    Accept comments and \n (!)"""
    a = list()
    varname = ""
    declaration = ""
    it = iter(string)
    for c in it:
        if c == "=":
            #Parameters: iterate up to a comma or the end
            declaration += c
            for c in it:
                #Check if inside an array (/
                if c == "(":
                    declaration += c
                    c = next(it)
                    if c == "/":
                        #Inside: waiting for /)
                        declaration += c
                        c = next(it)
                        while True:
                            declaration += c
                            c = next(it)
                            if c == "/":
                                #Check if )
                                declaration += c
                                c = next(it)
                                if c == ")":
                                    declaration +=c
                                    #Out
                                    break
                if c == '"' or c == "'":
                    #Iterate up to the last quote
                    quote = c
                    declaration += c
                    c = next(it)
                    while c != quote:
                        declaration += c
                        c = next(it)
                    declaration += c
                elif c == " ":
                    #Remove
                    c = ""
                elif c == ",":
                    #Remove "," and new variable
                    a.append( (varname.lower(),varname+declaration) )
                    varname = ""
                    declaration = ""
                    c = ""
                    #Finish this loop
                    break
                elif c ==  "!":
                    #Comment: Finish all
                    break
                else:
                    declaration += c
            #Continue except for a comment
            if c != "!":
                continue
        #Inside a declaration
        if c == "(":
            par = 1
            while par:
                declaration += c
                c = next(it)
                if c == "(":
                    par += 1
                elif c == ")":
                    par -= 1
            declaration += c
            continue
        if c == " ":
            #Remove
            c = ""
        if c == ",":
            #Remove "," and new variable
            a.append( (varname.lower(),varname+declaration) )
            varname = ""
            declaration = ""
            c = ""
        if c == "!":
            #Comment: finished
            break
        varname += c
    #Append and return
    a.append( (varname.lower(),varname+declaration) )
    return a


#Functions to split the code (experimental but almost robust)
def split_code(text):
    "Split a statement of code, do not keep floats and structure components"
    logical = [ "and", "eq", "eqv", "false", "ge", "gt", "le", "lt", "ne", "neqv", "not", "or", "true" ]
    re_eq = re.compile("[0-9]*[.]e")
    statements = list()
    statement = list()
    word = ''
    #True if continuation line
    continuation = False
    iter_text = iter(text.lower())
    for char in iter_text:
        #Numbers
        while char.isdigit() or char == '.':
            word += char
            char = next(iter_text)
            if char == 'd' or char == 'e':
                word += char
                char = next(iter_text)
                if char == '-' or char == '+':
                    word += char
                    char = next(iter_text)
                elif not char.isdigit():
                    #Assume .e q
                    if re_eq.match(word) and char == 'q':
                        if len(word) > 2:
                            statement.append(word[:-2])
                        word = '.'
                        char = 'eq'
                    else:
                        sys.stdout.write('statement=%s\n' % statement)
                        sys.stdout.write('word=%s, char=%s\n' % (word,char))
                        sys.stdout.write('Error in split_code\n')
                        sys.exit(1)
            elif char in 'aglno' and word[-1] == '.':
                statement.append(word[:-1])
                word = '.'
            elif char == "_":
                #1._
                statement.append(word)
                word = char
                char = ''
        if word:
            statement.append(word)
            continuation = False
            word = ''
        #Alpha-Alphanumeric
        while char.isalnum() or char == '_':
            word += char
            char = next(iter_text)
        if word:
            statement.append(word)
            continuation = False
            word = ''
        if char == '%':
            #Remove the next word
            char = next(iter_text)
            while char.isalnum() or char == '_' or char == '%':
                char = next(iter_text)
        if char == ';' or char == '\n' or char == '!' or char == '#':
            #End of statement
            if (not continuation) and statement:
                statements.append(statement)
                statement = list()
            #Remove after ! and #
            if char == '!' or char == '#':
                while char != '\n':
                    char = next(iter_text)
            #New line
            continuation = False
        elif char == "'" or char == '"':
            #Remove boolean operator or characters
            end = char
            char = next(iter_text)
            while char != end:
                char = next(iter_text)
        elif char == '&':
            #Ignore it and flag continuation
            continuation = True
        elif char == ' ' or char == '\t':
            #Remove
            pass
        else:
            if char == '.':
                ll = statement[-1]
                if ll in logical and statement[-2] == '.':
                    #Detect logical operator
                    char = '.%s.' % ll
                    statement.pop()
                    statement.pop()
            statement.append(char)
    return statements


#indent the code
def indent_code(text,n=0):
    "Indent a Fortran code"
    re_indented = re.compile("^[ \t]*(interface|function|subroutine)",re.IGNORECASE)
    re_end = re.compile("^[ \t]*end",re.IGNORECASE)
    new = ""
    for line in text.splitlines():
        if re_end.match(line):
            n -= 1
        #sys.stdout("[%d][%s]\n" % (n,n*indent+line.lstrip()[:-1]))
        new += n*indent+line.lstrip()+"\n"
        if re_indented.match(line):
            n += 1
    return new


#Class for a project
class Project:
    "Class to define a project which is a set of directories and files."
    def __init__(self,dir,pat_dir=['*'],pat_file=["*"],exclude=[],name="",logfile="project.log",\
                 given_include=dict(),File_Class=dict()):
        "Initialisation"
        self.ROOT = dir
        self.re_ROOT = re.compile("^"+self.ROOT+"/")
        self.name = name
        #Instantiation of the class message
        self.message = Message(verbose=verbose,logfile=logfile)
        #List of directories
        self.dirs = list()
        #List of files in each directory
        self.dirsfiles = dict()
        self.dirsfiles[dir] = list()
        #Dictionary of files referenced by file (the name should be unique: file.dir/file.name)
        self.files = dict()
        #Dictionary of routines (the name should be unique)
        self.routines = dict()
        #Dictionary of modules
        self.modules = dict()
        #Includes given by the script if missing
        self.given_include = given_include
        #Dictionary of class of files for each pattern
        self.File_Class = File_Class
        #Add directories and files
        self.add(self.ROOT,pat_dir,pat_file,exclude)
        #List of functions
        self.functions = list()
        #Dictionary of fortran type
        self.ftypes = dict()
        #Dictionary of cpp macros to be translated in code sections
        self.macro_dict=dict()
        self.macro_dirs=list()
        #Number of copied files
        self.copied = 0
        #Data which are saved for a new process
        self.cached = dict()
        #Message in the case of routines with the same name and building of interfaces
        self.message_interfaces = ""
        #Statistics
        self.message.write("Project %s (%d directories, %d files)\n" % \
                         (self.name,len(self.dirs),len(self.files)),verbose=-10)
    #
    def add(self,dir,pat_dir=['*'],pat_file=['*'],exclude=[],read_only=False,File_Class=None):
        "Add directories and files with a pattern"
        try:
            files = os.listdir(dir)
        except OSError:
            self.message.error("The directory '%s' does not exist!\n" % dir)
            return
        #Check if files are in pattern and not in exclude
        #Use [:] because we modify files in the block
        for file in files[:]:
            #Check also as */*
            d1 = "%s/%s" % (os.path.basename(dir),file)
            OK = False
            if os.path.isdir("%s/%s" % (dir,file)):
                pattern = pat_dir
            else:
                pattern = pat_file
            for pat in pattern:
                OK = OK or fnmatch.fnmatch(file,pat) or fnmatch.fnmatch(d1,pat)
            if not OK:
                files.remove(file)
                continue
            for excl in exclude:
                if fnmatch.fnmatch(file,excl) or fnmatch.fnmatch(d1,excl):
                    files.remove(file)
        for file in files:
            dd = "%s/%s" % (dir,file)
            if os.path.isdir(dd):
                self.message.write("[add dir %s ---> " % dd)
                if dd not in self.dirsfiles:
                    self.dirs.append(dd)
                    self.dirsfiles[dd] = list()
                #Add all files in this sub-directory
                self.add(dd,pat_dir,pat_file,exclude,read_only,File_Class)
                self.message.write(" <--- add dir %s]\n" % dd)
            elif os.path.isfile(dd):
                self.add_file(dir,file,read_only=read_only,File_Class=File_Class)
    #
    def add_file(self,dir,file,create=False,read_only=False,File_Class=None):
        "Add a file in the project and return the file."
        fullname = "%s/%s" % (dir,file)
        if dir not in self.dirsfiles:
            self.dirs.append(dir)
            self.dirsfiles[dir] = list()
        if not file in self.dirsfiles[dir]:
            self.dirsfiles[dir].append(file)
        else:
            #Already created: Do nothing
            return self.files[fullname]
        tt = file.split(".")
        if len(tt) > 1:
            suffix = tt[-1]
        else:
            suffix = ""
        self.message.write("(%s)" % file)
        if not File_Class:
            File_Class=File
            for (pattern,file_class) in self.File_Class:
                if fnmatch.fnmatch(file,pattern):
                    File_Class=file_class
                    break
        self.files[fullname] = File_Class(file,dir=dir,read_only=read_only,message=self.message)
        if not create:
            self.files[fullname].read()
        #Return the File instance
        return self.files[fullname]
    #
    def add_routine(self,routine):
        "Add a routine to the project (routine.lower have to be defined)"
        # MG: procedures contained in other procedures can have the same name.
        # this is a bit risky because abilint uses the basename as key of the dict
        # so only one procedure will be registered in this particular case.
        # I'm not enforcing this rule because using basenames as keys
        # is a serious design flaw. One should change abilint, not the Fortran standard!
        if routine.lower in self.routines and not (routine.is_contained and
            isinstance(routine.parent, (Routine, Function))):
            old = self.routines[routine.lower]
            #Then add a fatal message in the case of building interfaces
            self.message_interfaces = self.message_interfaces \
                    + "Two subroutines have the same name:\n" \
                    + "   %s in %s/%s\n" % (old.name,old.dir,old.file) \
                    + "   %s in %s/%s\n" % (routine.name,routine.dir,routine.file)
            #Change the name of the routine (to be improved)
            name = routine.lower
            i = 1
            while "%s_%d" % (name,i) in self.routines:
                i += 1
            self.routines["%s_%d" % (name,i)] = routine
        else:
            self.routines[routine.lower] = routine
    #
    def add_use_interface(self):
        "Add 'use interface_' at each file"
        self.message.section("Add 'use interface_xxx' in each routine...")
        #First we add a module for each routine
        for routine in self.routines.values():
            if not routine.module:
                routine.module = "%s%s" % (prefix,os.path.basename(routine.dir))
        #Do for each routine stored in self.routines
        for (name,routine) in self.routines.items():
            self.message.write("(%s/%s <%s>)\n" % (routine.dir,routine.file,name))
            routine.add_use_interface(self)
        self.message.done()
    #
    def analyze_all(self,exclude="a"*40):
        "Analyze all the project except the files which contain exclude"
        self.message.section("Analyze all the project...")
        for file in list(self.files.values()):
            #print file.name
            if exclude not in file.name:
                file.analyze(self)
        self.message.done()
        #Build the list of arguments of all routines
        self.get_arguments()
        #Determine the list of functions
        self.set_functions()
    #
    def analyze_execution(self):
        "Analyze the execution statements of all routines"
        self.message.section("Analyze the execution statements of routines...")
        for (name,routine) in self.routines.items():
            self.message.write("(%s)" % name)
            routine.analyze_execution(self)
        self.message.done()

    def analyze_explicit_interfaces(self):
        """
        Print the list of procedures requiring an explicit interface that are not contained 
        in modules or other procedures. Returns the number of procedures detected.
        """
        self.message.section("Analyze routines that require an explicit interface...")
        retcode = 0
        for name, routine in self.routines.items():
            self.message.write("(%s)" % name)
            reasons = routine.needs_explicit_interface() 
            if reasons and not routine.is_contained: 
               retcode += 1
               msg = "%s %s %s" % (routine.name, " requires an explicit interface due to: ", reasons)
               self.message.error(msg)
               #print routine.name, routine.__class__, routine.parent.name, routine.parent.__class__
               #print routine.arguments

            if (isinstance(routine, Function) and not (routine.is_contained or isinstance(routine.parent, Module))):
                msg = "Functions %s is not contained inside a module or in another procedure" % routine.name
                self.message.error(msg)

        self.message.done()
        return retcode
    #
    def analyze_comments(self,exclude="a"*40):
        "Analyze the comments of all the files except the files which contain exclude"
        self.message.section("Analyze the comments...")
        for file in self.files.values():
            if isinstance(file,File_F90) and exclude not in file.name:
                file.analyze_comments(self)
        self.message.done()
    #
    def analyze_directories(self):
        "Analyze the hierarchy of directories given by 'rank_dir'"
        #First establish the hierarchy
        self.hierarchy()
        for routine in self.routines.values():
            rank_routine = routine.rank
            for called in routine.called:
                try:
                    if rank_routine < self.routines[called].rank:
                        self.message.error("Misplacing <%s> (%s) calls <%s> (%s)" \
                                % (routine.name,routine.dir,called,self.routines[called].dir))
                except KeyError:
                    pass
    #
    def backup(self):
        "Backup the code"
        self.message.section("Backup all the code before changing it...")
        for file in self.files.values():
            file.backup()
        self.message.done()
    #
    def beautify(self):
        "Improve the appearance of the code"
        self.message.section("Beautify the code...\n")
        for file in self.files.values():
            file.beautify()
        self.message.done()
    #
    def copy_all(self,NEW,only_if_changed=True):
        "Copy all the project in a newdir."
        self.message.section("Copy all the project in the directory '%s'..." % NEW)
        for file in self.files.values():
            newdir = file.dir.replace(self.ROOT+"/",NEW+"/")
            if not os.path.exists(newdir):
                os.makedirs(newdir)
            file.write(newdir,file.name,fd=None,only_if_changed=only_if_changed)
            self.copied += file.copied
        if self.copied <= 1:
            self.message.write("(%d copied file)" % self.copied)
        else:
            self.message.write("%d copied files)" % self.copied)
        self.message.done()
        #Statistics
        self.message.final()
    #
    def cache_load(self,NEW,OLD=None,nocache=False):
        "Load a cached file for the project in order to decrease the execution of abilint next time"
        cached_name = "%s/.abilint" % NEW
        if os.path.exists(cached_name):
            if nocache:
                #Remove the cache file
                self.message.section("Remove the cached file '%s'..." % cached_name)
                os.remove(cached_name)
                self.message.done()
                return
            try:
                self.message.section("Read the cached file '%s'..." % cached_name)
                #self.cached = pickle.load(open(cached_name,"r"))
                self.cached = pickle.load(open(cached_name,"rb"))
                self.message.done()
            except (EOFError, pickle.UnpicklingError):
                self.cached = dict()
            #Check if
            # - the ROOT directory is the same to use safely the cache
            # - the python version is the same (bug from MT)
        version_info = list(map(int,sys.version_info[0:3]))
        if "ROOT" in self.cached and self.cached["ROOT"] == OLD and \
           "python_version" in self.cached and self.cached["python_version"] == version_info:
            #We can use the cache
            pass
        else:
            #Do not use the cache
            self.cached = dict()
    #
    def cache_save(self,NEW):
        "Save a cached file for the project in order to decrease the execution of abilint next time"
        #Add the ROOT directory
        self.cached["ROOT"] = self.ROOT
        #Add the version of python
        self.cached["python_version"] = list(map(int,sys.version_info[0:3]))
        for (name,routine) in list(self.routines.items()):
            self.cached[name] = routine.cache_save()
        cached_name = "%s/.abilint" % NEW
        self.message.section("Write the cached file '%s'..." % cached_name)
        #pickle.dump(self.cached,open(cached_name,"w"))
        pickle.dump(self.cached,open(cached_name,"wb"))
        self.message.done()
    #
    def cache_use(self):
        "Use the information in the cache"
        for routine in self.routines.values():
            routine.cache_use(self)
    #
    def dependencies(self,inside_name,outside_name):
        "Build the list of dependencies for all routines inside and outside the directory"
        self.message.section("Build the list of dependencies for each file...")
        #First create a list of all modules per directories (dir_modules)
        dir_modules = dict()
        for module in list(self.modules.values()):
            if module.dir not in dir_modules:
                dir_modules[module.dir] = [ module.name ]
            else:
                dir_modules[module.dir].append(module.name)
        #Then build the dependencies in the same directory (dependencies) and outside
        for (dir,files) in self.dirsfiles.items():
            dep = dict()
            dirs = set()
            dependencies = None
            for filename in files:
                fullname = "%s/%s" %(dir,filename)
                file = self.files[fullname]
                if isinstance(file,File_F90):
                    (dependencies,dirs_child) = file.dependencies(self)
                    dirs.update(dirs_child)
                    if dependencies:
                        dep[file.name] = [x.file for x in dependencies]
                        dep[file.name].sort()
            #Create the file 'inside_name' only if modules are inside the directories
            if dir in dir_modules or dirs:
                self.message.write("[%s:" % dir,verbose=-10)
            if dir in dir_modules:
                #Build the file inside_name
                self.message.write("(%s)" % inside_name,verbose=-10)
                depfile = self.add_file(dir,inside_name,create=True)
                #Add header of the file
                depfile.add_code(head_dependencies % {"dir": dir,
                                                      "message": "(inside the directory)"})
                #Add to clean files .mod
                depfile.add_code("CLEANFILES += ")
                linmod = dir_modules[dir]
                linmod.sort()
                for module in linmod:
                    depfile.add_code("\\\n\t%s.$(MODEXT) " % module)
                depfile.add_code("\n")
            if dep:
                files = list(dep.keys())
                files.sort()
                for file in files:
                    #Remove circular dependencies
                    deps = [x for x in dep[file] if x!=file] 
                    if deps:
                        #Remove all suffixes (ex. .F90 and .F90.in)
                        depfile.add_code("\n%s.%s: " % (file.split(".")[0],"$(OBJEXT)"))
                        for module in deps:
                            depfile.add_code("%s.%s " % (module.split(".")[0],"$(OBJEXT)"))
                        depfile.add_code("\n")
            if dirs:
                #Build the file outside_name
                self.message.write("(%s)" % outside_name,verbose=-10)
                depfile = self.add_file(dir,outside_name,create=True)
                #Add header of the file
                depfile.add_code(head_dependencies % {"dir": dir,
                                                      "message": "(outside the directory)"})
                #Add the current directory
                depfile.add_code("include_dirs = [")
                comma = ""
                for d in dirs:
                    depfile.add_code('%s\n\t"%s"' % (comma,d.replace("%s/src/" % OLD,"")))
                    comma = ","
                depfile.add_code("]\n")
            if dir in dir_modules or dirs:
                self.message.write("]",verbose=-10)
        self.message.done()
    #
    def dump_ftype(self,name,filename):
        "Dump with pickle a Fortran type in a file"
        if name in self.ftypes:
            self.message.section("Write the fortran type '%s' into the file '%s'..." % (name,filename))
            pickle.dump(self.ftypes[name].dict_vars,open(filename,"wb"))
            self.message.done()
        else:
            self.message.fatal("The Fortran type '%s' is not inside the project!\n" % name)

    #
    def find_file(self,filename):
        "Find the instance File given by its name"
        for file in self.files.values():
            if file.name == filename:
                return file
        return None
    #
    def generate(self,format,pattern_dir='.*'):
        "Generate a file which contains the interfaces of all routines in dir"
        self.message.section("Generate all signature of directories '%s'..." % pattern_dir)
        re_dir = re.compile(pattern_dir)
        for dir in self.dirs:
            if re_dir.match(dir):
                signature = ""
                generated = format % os.path.basename(dir)
                fullgenerated = "%s/%s" % (dir,generated)
                file=self.files[fullgenerated]
                for file in self.dirsfiles[dir]:
                    if file != generated:
                        fullname = "%s/%s" % (dir,filename)
                        signature += self.files[fullname].signature()
                #Create the file if it doesn't exist
                if fullgenerated not in self.files:
                    file = self.add_file(dir,generated,create=True,File_Class=File_F90_Library)
                else:
                    file = self.files[fullgenerated]
                #Add code to the file
                file.add_code(signature)
        self.message.done()
    #
    def get_arguments(self):
        "Build the list of arguments of all routines"
        self.message.section("Build the list of arguments in each routine...")
        #Do for each routine stored in self.routines
        for (name,routine) in self.routines.items():
            self.message.write("(%s/%s <%s>)" % (routine.dir,routine.file,name))
            routine.get_arguments(self)
        self.message.done()
    #
    def graph(self,names,graph_excluded=list()):
        "Build some graphs of calls"
        #Build a graph dictionary
        if names == "directories":
            #Determine the rank of all routines
            self.analyze_directories()
            filename = "directories.dot"
        else:
            filename = "bigdft.dot"
        fd = open(filename,"w")
        if names == "directories":
            #Build the graph of module interdependences
            self.message.section("Build the graphs of all directories...")
            dict_graph = dict()
            #Dictionary of the color of nodes (red, wrong calls, green OK)
            dict_color = dict()
            for routine in self.routines.values():
                dir = os.path.basename(routine.dir)
                if dir not in dict_graph:
                    dict_graph[dir] = set()
                    dict_color[dir] = "green"
                for called in routine.called:
                    try:
                        called_dir = self.routines[called].dir.split('/')[-1]
                        num = rank_dir(called_dir)
                        dict_graph[dir].add(called_dir)
                        if routine.rank < num:
                            dict_color[dir] = "red"
                            self.message.warning("Misplacing <%s> (%s) calls <%s> (%s)" \
                                    % (routine.name,dir,called,called_dir),verbose=-10)
                    except KeyError:
                        pass
            self.graph_build("directories",fd,dict_graph,dict_color,graph_excluded=graph_excluded)
        elif names == "all":
            #Build the graph of all routines
            self.message.section("Build the graphs of all routines (parents and children)...")
            names = self.routines.keys()
            names.sort()
            for name in names:
                dict_graph = dict()
                self.message.write("(%s)" % name,verbose=-10,flush=True)
                self.routines[name].graph_children(dict_graph,routines=self.routines)
                self.routines[name].graph_parents(dict_graph,routines=self.routines)
                self.graph_build(name,fd,dict_graph,graph_excluded=graph_excluded)
        else:
            self.message.section("Build the graph for the routines...")
            for name in names.split(','):
                dict_graph = dict()
                self.message.write("(%s)" % name,verbose=-10,flush=True)
                if name not in self.routines.keys():
                    self.message.error("The routine %s does not exist: no graph\n" % name)
                    return
                self.routines[name].graph_children(dict_graph,routines=self.routines,recursive=True)
                self.routines[name].graph_parents(dict_graph,routines=self.routines,recursive=True)
                self.graph_build(name,fd,dict_graph,graph_excluded=graph_excluded)
        fd.close()
        self.message.write("(%s)" % filename,verbose=-10)
        self.message.done()
    #
    #Build the tree and give an identifier at each node
    def graph_build(self,name,fd,dict_graph,dict_color=None,graph_excluded=list()):
        "Build the graph"
        fd.write("digraph routine_%s {\n" % name)
        fd.write("   node [shape = record,height=.1,style=filled];\n")
        #Build the dict of colors
        if not dict_color:
            dict_color = dict()
            for key in dict_graph.keys():
                dict_color[key] = "white"
                for called in dict_graph[key]:
                    dict_color[called] = "white"
        id = 0
        node_graph = dict()
        for (calling,called_set) in dict_graph.items():
            if calling not in node_graph and calling not in graph_excluded:
                id += 1
                node_graph[calling] = id
                fd.write(3*' '+'n%d [label="%s",color=%s];\n' \
                    % (id,calling,dict_color[calling]))
            for called in called_set:
                if called not in node_graph and called not in graph_excluded:
                    id += 1
                    node_graph[called] = id
                    fd.write(3*' '+'n%d [label="%s",color=%s];\n' \
                        % (id,called,dict_color[called]))
        for (calling,called_set) in dict_graph.items():
            for called in called_set:
                if called not in graph_excluded:
                    fd.write(3*' '+'n%d -> n%d;\n' % (node_graph[calling],node_graph[called]))
        fd.write("}\n")
    #
    def has_module(self,module):
        "Check if the module exists and check if also in the given modules."
        return module in self.modules
    #
    def hierarchy(self):
        "Establish the hierarchy of routines: a routine rank_dir should be defined."
        for routine in self.routines.values():
            dir = os.path.basename(routine.dir)
            routine.rank = rank_dir(dir)
    #
    def interfaces_all(self,dir_mod,exclude,warning=""):
        "Build the interfaces of all files"
        self.message.section("Build the interfaces...")
        #First test if there are routines with the same name
        if self.message_interfaces:
            self.message.fatal("\n"+self.message_interfaces)
        #Do for each directory
        for dir in self.dirs:
            main = os.path.basename(dir)
            #exclude contains the excluded directories
            if main in exclude:
                continue
            #Directory where the interface files are stored
            dir_module = "%s/src/%s" % (self.ROOT,main)
            if ( not os.path.exists(dir_module) ):
                dir_module = "%s/%s" % (self.ROOT,dir_mod)
            module = "%s%s" % (prefix,main)
            file_module = "%s.F90" % module
            pfile = self.add_file(dir_module,file_module,create=True)
            directory = self.re_ROOT.sub('',dir)
            pfile.code = head_interface % { \
                    'name': module,
                    'description': "in the directory %s" % directory,
                    'warning': warning }
            #Add interfaces in the file
            flist = self.dirsfiles[dir]
            #Sort alphabetically
            flist.sort()
            for file in flist:
                fullname = "%s/%s" % (dir,file)
                pfile.code += self.files[fullname].interface()
            pfile.code += "end module %s\n" % module \
                          + "!!***\n"
            #Analyze this file
            pfile.analyze(self)
        self.message.done()
    #
    def no_fatal(self):
        "Routine which prepares the project to have no fatal error in order to generate interfaces"
        #Remove possible message for duplicated routines
        self.message_interfaces = ""
        #Remove all children
        self.remove_children()
    #
    def remove_children(self):
        "Remove children of all files"
        for file in self.files.values():
            file.remove_children()
    #
    def remove_code(self,pattern_file):
        "Remove code except comments of files"
        re_file = re.compile(pattern_file)
        for (dir,files) in self.dirsfiles.items():
            for file in files:
                if re_file.match(file):
                    fullname = "%s/%s" % (dir,file)
                    self.files[fullname].remove_code()
    #
    def remove_files(self,pattern):
        "Remove files which correspond to the given pattern (do not clean properly!)"
        self.message.section("Remove the files with pattern '%s'..." % pattern)
        for (dir,files) in self.dirsfiles.items():
            for file in files:
                if pattern in file:
                    fullname = "%s/%s" % (dir,file)
                    self.message.write("[%s]" % (fullname))
                    del self.files[fullname]
                    self.dirsfiles[dir].remove(file)
        self.message.done()
    #
    def set_children_parents(self):
        "Build the list of children and parents routines"
        self.message.section("Determine children and parents of all routines...")
        for routine in self.routines.values():
            routine.set_children_parents(self)
        self.message.done()
    #
    def set_functions(self):
        "Build the list of functions"
        self.message.section("Build the list of functions...")
        self.message.write("\n")
        for routine in self.routines.values():
            if isinstance(routine,Function):
                self.message.write("Function %s -- (%s)\n" % (routine.name,routine.function_type))
                self.functions.append(routine.name.lower())
        #We sort the functions
        self.functions.sort()
        functions = ""
        for function in self.functions:
            #We do not detect function with only one character
            if len(function) > 1:
                functions = "%s|%s" % (functions,function)
            else:
                self.message.warning( \
                     "The function %s (%s/%s) has a too short name for the detection" \
                     % (function,self.routines[function].dir,self.routines[function].file))
        nf = len(self.functions)
        self.message.write("Number of functions: %d\n" % nf)
        #Build the regular expression to detect functions ('xx(')
        if nf  == 0:
            self.re_functions = None
        else:
            self.re_functions = re.compile("(?<![a-z])(%s)[ ]*[(]" % functions[1:],re.IGNORECASE)
        #Warning: This detection is not fully robust but works
        self.message.done()
    #
    def special(self,special_modif):
        "Do some special modifications"
        if len(special_modif) == 0:
            #Nothing to be done
            return
        self.message.write("\nSpecial transformations:\n")
        for name in special_modif:
            self.message.write("[special:%s]" % name)
            eval("self.routines[name].%s" % special_modif[name])
        self.message.write("\n")
    #
    def statistics(self):
        "Display some statistics about the project"
        self.message.write("Statistics of the project %s:\n" % self.name \
            + "%d directories, " % len(self.dirs) \
            + "%d files, " % len(self.files) \
            + "%d modules, " % len(self.modules) \
            + "%d routines (from which %d functions)" % (len(self.routines),len(self.functions)) \
            + " -- [%d files copied]\n" % self.copied,verbose=-10)
    #
    def unused_routines(self):
        "Give list of unused routines and if a routine inside a generic routine is not called"
        #Do for each routine stored in self.routines
        unused = list()
        for (name,routine) in self.routines.items():
            #Only routine (not program) and not in lib directory
            if len(routine.calling) == 0 and routine.nogeneric and routine.rank < 100 and routine.rank >= 0:
                unused.append((routine.dir,routine.file,name))
            if len(routine.calling) != 0 and (not routine.nogeneric) and routine.rank < 100 and routine.rank >= 0:
                #A routine part of a generic routine is called directly
                self.message.error("<%s> (%s/%s), part of a generic routine, is called directly by %s" % \
                        (name,routine.dir,routine.file,list(routine.calling)))
        unused.sort()
        self.message.section("List of unused routines:")
        for lists in unused:
                self.message.write("(%s/%s <%s>)" % lists,verbose=-10)
        self.message.done()
    #
    def add_cpp_macros_naive(self,my_dir,my_hfile,my_active_macro):
        "Look, in a given DIR, for a given .h FILE and extract macros to be translated"
        #Look, in MY_DIR directory, for a the file MY_FILE (*.h file)
        #Build a dictionary with macros in a section delimited by MY_ACTIVE_MACRO
        #Works only for macros without arguments
        self.macro_dirs.append(my_dir)
        hfilenm=os.path.join(my_dir,my_hfile)
        if os.path.exists(hfilenm):
          #Read .h file
          hfile_d=open(hfilenm,'r')
          hfile_lines=hfile_d.readlines()
          hfile_d.close()
          #Build macro dictionary
          cpp_detect_status=0
          re_cpp_begin=re.compile("^[ ]*#[ \t]*if[ \t]*defined[ \t]*"+my_active_macro)
          re_cpp_end  =re.compile("^[ ]*#[ \t]*endif")
          re_define   =re.compile("^[ ]*#[ \t]*define[ \t]+")
          for line in hfile_lines:
            if cpp_detect_status!=1:
              if re_cpp_begin.match(line):
                cpp_detect_status=1
            else:
              if re_cpp_end.match(line):
                cpp_detect_status=2
              if re_define.match(line):
                line2=re.sub(re_define,'',line)
                mac_in =line2.split()[0]
                mac_out=line2.replace(mac_in,'').strip()
                self.macro_dict[mac_in]=mac_out

# BEGIN MG CHANGES: Use regular expressions for F2003
_BLOCK_NONE = 0
_BLOCK_PROG = 1
_BLOCK_MOD = 2
_BLOCK_SUB = 3
_BLOCK_FUNC = 4

# PROGRAM [name]
#    ...
# END [PROGRAM [name]]
_REM_PROG_START = re.compile(r'program\s*\w*\Z', re.I).match
_REM_PROG_END = re.compile(r'end(\s*program\s*\w*|)\Z', re.I).match

# MODULE <name>
# ...
# END [MODULE [name]]
_REM_MOD_START= re.compile(r'module\s*\w+\Z', re.I).match
_REM_MOD_END = re.compile(r'end(\s*module\s*\w*|)\Z', re.I).match

# [ <prefix> ] <SUBROUTINE> <name> [ ( <args> ) ] [ <suffix> ]
# ...
# END [SUBROUTINE [name]]
_REM_SUB_START = re.compile(r'(recursive|pure|elemental|\s)*subroutine\s*\w+', re.I).match
_REM_SUB_END = re.compile(r'end(\s*subroutine\s*\w*|)\Z', re.I).match

# [ <prefix> ] FUNCTION <name> ( [<dummy-arg-list>] ) [<suffix>]
# ...
# END [FUNCTION [name]]
#_REM_FUNC_START = re.compile(r'(recursive|pure|elemental|integer|double(\s)+precision|real\(dp\)|character\(len=\d+\)\s)*function\s*\w+', re.I).match
_REM_FUNC_END = re.compile(r'end(\s*function\s*\w*|)\Z', re.I).match

# This one is problematic. The firs one is stricter but it does not play well with the other code!!
# I use the old one used by abilint
#_REM_FUNC_END = re.compile('^[ \t]*end[ \t]*(function\n)',re.IGNORECASE).match
_REM_FUNC_START = re.compile('^[ \t]*(([^!\'"\n]*?)function)',re.IGNORECASE).match

def trace(func):
    """Decorator used for following the parsing of the blocks"""
    def wrapped(line):
        res = func(line)
        if res: 
            print(line)
            print(res)
        return res
    return wrapped

#@trace
def start_block(line):
    """
    Signals the begging of a block. Return its type or 0 if line does not start a block
    """
    line = line.strip()
    if _REM_SUB_START(line): return _BLOCK_SUB
    if _REM_MOD_START(line): return _BLOCK_MOD
    if _REM_FUNC_START(line): return _BLOCK_FUNC
    if _REM_PROG_START(line): return _BLOCK_PROG
    return _BLOCK_NONE

#@trace
def end_block(line):
    """
    Signals the end of a block. Return -type or 0 if line does not end a block
    """
    line = line.strip()
    if _REM_SUB_END(line): return -_BLOCK_SUB
    if _REM_MOD_END(line): return -_BLOCK_MOD
    if _REM_FUNC_END(line): return -_BLOCK_FUNC
    if _REM_PROG_END(line): return -_BLOCK_PROG
    return _BLOCK_NONE
# END MG CHANGES: Use regular expressions for F2003


#General class handling File and the other ones.
class Structure:
    "Define a structure (a routine, header, etc) which can have a portion of code"
    #Comments and also preprocessing commands
    re_comment_match = re.compile("^([ \t]*(([!#].*)|[ ]*)\n)+")
    #Detect the beginning and the end of a block
    re_sub_start = re.compile('^[ \t]*(module|program|recursive|subroutine|(([^!\'"\n]*?)function))',re.IGNORECASE)
    re_sub_end   = re.compile('^[ \t]*end[ \t]*(function|module|program|subroutine|\n)',re.IGNORECASE)

    # Changed by MG: 
    # Patch re_sub_starr and re_sub_end with the new functions supporting F2003
    # Comment the block below to revert to the previous version.
    class Foo(object): pass
    re_sub_start, re_sub_end = Foo(), Foo()
    re_sub_start.match = start_block
    re_sub_end.match = end_block


    def __init__(self,name=None,parent=None,message=None):
        "Initialisation"
        self.name = name
        if name:
            self.lower = self.name.lower()
        self.parent = parent
        if parent:
            #Add self in child parent
            self.message = self.parent.message
            #Add to parent's children (if it exists)
            parent.children.append(self)
        elif message:
            self.message = message
        else:
            sys.stderr.write("Structure %s (%s) has no message class!\n" \
                    % (str(self.__class__),self.name))
            sys.exit(1)
        self.children = list()
        #Code of the structure
        self.code = ""
        self.code_backup = ""
        #Is analyzed
        self.is_analyzed = False
    #Add code
    def add_code(self,code):
        "Add code"
        self.code += code
    #Analyze
    def analyze(self):
        "Check only if it is already analyzed"
        if self.is_analyzed:
            self.message.fatal("The structure %s is already analyzed" % self.name)
    #Backup
    def backup(self):
        "Do a copy of the code"
        self.code_backup = self.code
        for child in self.children:
            child.backup()
    #Beautify
    def beautify(self):
        "Beautify the children  "
        for child in self.children:
            child.beautify()
    #Compare
    def cmp(st1,st2):
        "Compare two structures"
        if st1.lower < st2.lower:
            return -1
        else:
            return 1
    #Check if the code and the backup are identical
    def is_changed(self):
        changed = self.code != self.code_backup
        if changed:
            return True
        #Test the children
        for child in self.children:
            changed = child.is_changed()
            if changed:
                return True
        return False
    #String representation
    def __str__(self):
        "Display all the code with children"
        #The code is inserted before the child codes
        code = self.code
        for child in self.children:
            code +=str(child)
        return code
    #Read
    def read(self,fd):
        "Read from a file descriptor"
        #We ban \r !!
        self.code = fd.read().replace('\r','')
    #Remove children
    def remove_children(self):
        "Remove all children"
        self.children = list()
    #Replace
    def replace(self,old,new):
        "Replace the regular expressions 'old' by the string 'new'"
        self.code = re.sub(old,new,self.code)
    #Write
    def write(self,fd,only_if_changed):
        "write into a file descriptor"
        if only_if_changed:
            #Check if the code is changed
            if not self.is_changed():
                #Do nothing
                return
        #The code is inserted before the child codes
        fd.write(self.code)
        for child in self.children:
            child.write(fd,only_if_changed)


class File(Structure):
    "Class to define a file (for all files) which is a code with a structure"
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        Structure.__init__(self,name=name,parent=None,message=message)
        self.dir = dir
        self.copied = 0
        self.read_only = read_only
        #File which contains the structure
        self.file = self.name
        #Timestamp for cache (default None)
        self.timestamp = None
    #Analyze
    def analyze(self,project=None):
        "Do nothing"
        if self.is_analyzed:
            self.message.fatal("The file %s/%s is already analyzed" % (self.dir,self.file))
    #Interface
    def interface(self):
        "Do nothing"
        return ""
    #
    def read(self,fd=None):
        "Read the file."
        if fd == None:
            fd = open("%s/%s" % (self.dir,self.name))
        #Time stamp i.e. the time of the last modification
        self.timestamp = os.path.getmtime(fd.name)
        #We ban \r !!
        self.code = fd.read().replace('\r','')
        fd.close()
    #
    def write(self,dir,file,fd=None,only_if_changed=True):
        "Write the file as '%s/%s' % (dir,file)."
        #If read_only, return
        if self.read_only:
            return
        #Check if the code is changed
        if only_if_changed:
            if not self.is_changed():
                #Do nothing
                return
        self.message.write("[%s/%s]" % (dir,file))
        if fd == None:
            fd = open("%s/%s" % (dir,file),"w")
        #The code is inserted before the child codes
        fd.write(self.code)
        for child in self.children:
            child.write(fd,only_if_changed=False)
        fd.close()
        self.copied += 1


class File_F90(File):
    "Class to define a fortran90 file which is a code with a structure"
    #
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        File.__init__(self,name,dir,read_only,message=message)
        self.is_module = False
        self.module = None
    #
    def analyze(self,project):
        "Analyze the code of the file"
        File.analyze(self,project)
        self.message.write("[%s/%s:" % (self.dir,self.name),verbose=10)
        #Translate macros in code
        self.translate_macros(project)
        #Create an iterator
        iter_code = iter(self.code.splitlines(1))
        #All the code will be inside child structures
        self.code = ""
        struct = None
        for line in iter_code:
            if self.re_comment_match.match(line):
                if not isinstance(struct,Comment):
                    struct = Comment(parent=self)
                struct.add_code(line)
            elif self.re_sub_start.match(line):
                line_lower = line.lower()
                if "subroutine" in line_lower:
                    struct = Routine(parent=self)
                elif "module" in line_lower:
                    struct = Module(parent=self)
                    self.is_module = True
                elif "program" in line_lower:
                    struct = Program(parent=self)
                else:
                    struct = Function(parent=self)
                struct.analyze(line,iter_code,project)
            else:
                self.message.fatal("\n'%s'\n--> No detected routine!\n" % line \
                        + "This part of code can not be parsed as Fortran file:\n" \
                        + "Analysis Error in %s/%s\n" % (self.dir,self.file))
        self.message.write("]\n",verbose=10)
    #
    def analyze_comments(self,project):
        "Analyze comments to detect robodoc headers and footers"
        temp_structs = self.children
        self.children = []
        for struct in temp_structs:
            if isinstance(struct,Comment):
                #Detect robodc comments, split comments if necessary and add to children of self
                struct.detect_robodoc_comment()
            else:
                #Add the structure
                self.children.append(struct)
        #We have splitted the comments. Now we build robodoc structure
        #(Robodoc_Header -- [comment] -- Routine -- Robodoc_Footer)
        self.build_robodoc_structure()
        #Finally, analyze comment
        for struct in self.children:
            if isinstance(struct,Comment):
                struct.analyze_comment()
    #
    def build_robodoc_structure(self):
        "Build the robodoc structure around each routine"
        robodoc_header = None
        robodoc_footer = None
        routine = None
        for struct in self.children:
            if isinstance(struct,Robodoc_Header):
                if robodoc_header != None:
                    self.message.fatal("\n%s%s" % (robodoc_header.code,struct.code) \
                            + "Two robodoc headers for a subroutine or a robodoc header without footer!\n" \
                            + "Analysis Error in %s/%s" % (self.dir,self.name))
                else:
                    robodoc_header = struct
                robodoc_footer = None
                routine = None
            elif isinstance(struct,Robodoc_Footer):
                if routine == None:
                    self.message.error("Robodoc footer without routine in %s/%s" % (self.dir,self.name))
                    if robodoc_header:
                        robodoc_header.routine = None
                    else:
                        self.message.fatal("Robodoc footer without header!\n" \
                                + "Analysis error in %s/%s" % (self.dir,self.name))
                robodoc_footer = struct
                routine = None
                robodoc_header = None
            elif isinstance(struct,Module):
                if robodoc_header:
                    if routine:
                        #There is already a routine
                        self.message.fatal("\n%s%s" % (robodoc_header.code,struct.code) \
                                + "Robodoc header for more than one subroutine or a robodoc header without footer!\n" \
                                + "Analysis Error in %s/%s" % (self.dir,self.name))
                    robodoc_header.routine = struct
                else:
                    self.message.error("Routine %s without robodoc header in %s/%s" % (struct.name,self.dir,self.name))
                routine = struct
    #
    def build_robodoc_structure_generic(self):
        "Build the robodoc structure for a library or a generic routine (Robodoc_Header [Routine] Robodoc_Footer"
        robodoc_header = None
        robodoc_footer = None
        routine = None
        for struct in self.children:
            if isinstance(struct,Robodoc_Header):
                if robodoc_header != None:
                    self.message.fatal("\n%s%s" % (robodoc_header.code,struct.code) \
                            + "Two robodoc headers for a generic subroutine!\n" \
                            + "Analysis Error in %s/%s" % (self.dir,self.name))
                elif routine:
                    self.message.fatal("\n%s%s" % (robodoc_header.code,struct.code) \
                            + "Only one robodoc header for a generic subroutine on top of the file!\n" \
                            + "Analysis Error in %s/%s" % (self.dir,self.name))
                else:
                    robodoc_header = struct
                    robodoc_header.routine = self
                    self.robodoc_header = robodoc_header
            elif isinstance(struct,Robodoc_Footer):
                if routine == None:
                    self.message.error("Robodoc footer without routine in %s/%s" % (self.dir,self.name))
                    if not robodoc_header:
                        self.message.fatal("Robodoc footer without header!\n" \
                                + "Analysis error in %s/%s\n" % (self.dir,self.name))
                if robodoc_footer != None:
                    self.message.fatal("\n%s%s" % (robodoc_footer.code,struct.code) \
                            + "Two robodoc footers for a generic subroutine!\n" \
                            + "Analysis Error in %s/%s" % (self.dir,self.name))
                robodoc_footer = struct
                robodoc_footer.routine = self
                self.robodoc_footer = robodoc_footer
            elif isinstance(struct,Routine):
                if not robodoc_header:
                    self.message.error("Generic routine without robodoc header in %s/%s" % (self.dir,self.name))
                if robodoc_footer:
                    self.message.fatal("\n%s%s" % (robodoc_header.code,struct.code) \
                            + "Robodoc footer for a generic subroutine before a routine!\n" \
                            + "Analysis Error in %s/%s" % (self.dir,self.name))
                routine = struct
    #
    def dependencies(self,project):
        "Build the dependencies"
        dependencies = set()
        dirs = set()
        for child in self.children:
            if isinstance(child,Routine) or isinstance(child,Function) or isinstance(child,Module):
                (dependencies_child,dirs_child) = child.dependencies(project)
                dependencies.update(dependencies_child)
                dirs.update(dirs_child)
        return (dependencies,dirs)
    #
    def interface(self):
        "Build the interface"
        code = ""
        for child in self.children:
            if isinstance(child,Routine) or isinstance(child,Function):
                code += child.interface() + "\n"
        return code
    #
    def remove_code(self):
        "Remove code except first comments"
        code = ""
        for line in self.code.splitlines(1):
            if self.re_comment_match.match(line):
                code += line
            else:
                #We have finished
                break
        self.code = code
    #
    def signature(self):
        "Return all signatures of routines"
        code = ""
        for child in self.children:
            if isinstance(child,Routine) or isinstance(child,Function):
                code += child.signature + "\n"
        return code
    #
    def special(self,modif):
        "Special modifications"
        for child in self.children:
            if isinstance(child,Routine):
                eval("child.%s" % modif)
    #
    def translate_macros(self,project):
        "Translate macros stored in project.macro_dict"
        if self.dir in project.macro_dirs:
            for mac in project.macro_dict.keys():
                self.code = self.code.replace(mac,project.macro_dict[mac])

class File_F90_Library(File_F90):
    "Special class to generate the interfaces from a library"
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        File_F90.__init__(self,name,dir,read_only,message=message)
        self.robodoc_header = None
        self.robodoc_footer = None
    #
    def build_robodoc_structure(self):
        "Build the robodoc structure for a library (Robodoc_Header [Routine] Robodoc_Footer"
        File_F90.build_robodoc_structure_generic(self)

class File_F90_Generic(File_F90):
    "Special class to generate a generic routine from Fortran90 files"
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        File_F90.__init__(self,name,dir,read_only,message=message)
        #Generic_Routine will be associated to the generic routine (see analyze)
        self.robodoc_header = None
        self.robodoc_footer = None
        self.generic_routine = None
    #
    def analyze(self,project=None):
        "Analyze the code of the file"
        #First analyze normally the file
        File_F90.analyze(self,project)
        #Add a routine which has the name of the file
        name = self.name.replace(".F90","")
        #Do not add to the list of children of the file
        struct = Generic_Routine(name=name,file=self)
        self.generic_routine = struct
        if project != None:
            #Add to the list of routines
            project.routines[struct.lower] = struct
        #Add the cached information
        struct.cache_use(project)
        #All routines are a part of the generic routine (used of unused_routines)
        for child in self.children:
            if isinstance(child,Routine):
                child.nogeneric = False
    #
    def build_robodoc_structure(self):
        "Build the robodoc structure for a generic interface (Robodoc_Header [Routine] Robodoc_Footer"
        File_F90.build_robodoc_structure_generic(self)
        if self.robodoc_header:
            self.robodoc_header.routine = self.generic_routine
        if self.robodoc_footer:
            self.robodoc_footer.routine = self.generic_routine
    #
    def interface(self):
        "Build a generic interface"
        name = self.name.replace(".F90","")
        code = "\n!Generic interface of the routines %s\n" % name \
                + "interface %s\n" % name
        for child in self.children:
            if isinstance(child,Routine):
                child.interface()
                code += child.signature
        code += "end interface\n" \
             + "!End of the generic interface of %s\n\n" % name
        self.generic_routine.code_interface = indent_code(code)
        return self.generic_routine.code_interface


class File_F77(File_F90):
    "Class to define a fortran 77 file (always read only)"
    def analyze(self,project):
        "Analyze the code of the file"
        File.analyze(self,project)
        #First, we transform the file into Fortran 90
        #Replace tabulation as blank characters
        self.code = self.code.replace("\t"," "*6)
        code = ""
        for line in self.code.splitlines(1):
            if len(line) < 6:
                continue
            first_char = line[0].lower()
            if first_char == "c" or first_char == "*" or first_char == "!":
                code += "!" + line[1:]
            elif first_char == "#" or first_char == "$":
                code += "#" + line[1:]
            elif line[5] != " ":
                code = code[:-1] + "&\n" + line[6:]
            else:
                code += line
        self.code = code
        #Then we analyze as a F90 File
        File_F90.analyze(self,project)


class Code(Structure):
    "Generic Class for fortran code inside the routines or the functions"
    #Comments and also preprocessing commands
    re_allcomment = re.compile("([ \t]*(([!#].*)|[ ]*)\n)+")
    #Commment inside a line
    re_comment = re.compile("[!].*")
    #Contains statement
    re_contains = re.compile('^[ \t]*contains',re.IGNORECASE)
    #Continuation
    re_continuation = re.compile("&[ \t]*(![^\n]*)?\n")
    #Include command
    re_include = re.compile('^[ ]*include.*?\n',re.MULTILINE+re.IGNORECASE)
    #
    def __init__(self,name=None,parent=None,message=None):
        "Initialisation"
        Structure.__init__(self,name=name,parent=parent,message=message)
        if parent:
            self.file = parent.file
            self.dir = parent.dir
        else:
            self.file = None
            self.dir = None


class Comment(Structure):
    "Class for comments inside Fortran files"
    #Detect robodoc header (!***)
    re_robodoc_header = re.compile("!!\*\*\*\*[a-z]\*")
    #
    def __init__(self,parent):
        "Initialisation"
        Structure.__init__(self,name="comment",parent=parent)
        self.file = self.parent.name
        self.dir = self.parent.dir
    #
    def analyze_comment(self):
        "Analyze the comment"
        pass
    #
    def detect_robodoc_comment(self):
        "Split the comment (self) to have robodoc header or footer"
        struct = None
        for line in self.code.splitlines(1):
            if "!!" == line[0:2]:
                if "!!***" == line[:5]:
                    if self.re_robodoc_header.match(line):
                        #Add struct to the children of self.parent
                        struct = Robodoc_Header(parent=self.parent)
                    else:
                        struct = Robodoc_Footer(parent=self.parent)
                else:
                    if struct == None:
                        struct = Comment(parent=self.parent)
            else:
                if struct == None or isinstance(struct,Robodoc_Header) or isinstance(struct,Robodoc_Footer):
                    struct = Comment(parent=self.parent)
            struct.add_code(line)


class Robodoc_Header(Comment):
    "Class for the robodoc header (comment)"
    #Sections of robodoc
    robodoc_sections = ["NAME", "FUNCTION", "NOTE", "COPYRIGHT", "INPUTS", "OUTPUT", "PARENTS", "CHILDREN", "SOURCE"]
    #
    def __init__(self,parent=None):
        "Initialisation"
        Comment.__init__(self,parent)
        self.sections = dict()
        self.categories = []
    #
    def analyze_comment(self):
        "Analyze the robodoc header"
        iter_code = iter(self.code.splitlines())
        #First line
        line = next(iter_code)
        self.type = line[6]
        self.title = line[9:-1]
        category = None
        for line in iter_code:
            sec = line[2:].strip()
            if sec != "" and sec == sec.upper():
                category = sec.upper()
                self.sections[category] = ""
                self.categories.append(category)
            elif category:
                self.sections[category] += "%s\n" % line[2:]
        #Check the coherency between the robodoc header and the routine
        if isinstance(self.parent,File_F90_Library) and self.type != "d":
            self.message.error("The type for the robodoc header '%s' is not for the file '%s'!\n" \
                    % (self.type,self.routine.file) \
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
            return
        if self.type != "m" and isinstance(self.routine,Module) and not isinstance(self.routine,Routine):
            self.message.error("The type for the robodoc header '%s' is not for the module '%s'!\n" \
                    % (self.type,self.routine.name) \
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
        if self.type != "f" and isinstance(self.routine,Routine) and not isinstance(self.routine,Program):
            self.message.error("The type for the robodoc header '%s' is not for the routine '%s'!\n" \
                    % (self.type,self.routine.name)\
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
        if self.type != "p" and isinstance(self.routine,Program):
            self.message.error("The type for the robodoc header '%s' is not for the program '%s'!\n" \
                    % (self.type,self.routine.name) \
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
    #
    def beautify(self):
        "Display Robodoc header (experimental)"
        #Define the type
        if self.routine == None:
            #Do nothing
            return
        elif isinstance(self.routine,Program):
            self.type = "p"
        elif isinstance(self.routine,Routine):
            self.type = "f"
        elif isinstance(self.routine,Module):
            self.type = "m"
        else:
            #Keep the type.
            pass
        #Define the title
        self.title = "bigdft/%s" % self.routine.name
        if isinstance(self.routine,Routine):
            #Add parents
            line = 2*indent
            routines = list(self.routine.calling)
            routines.sort()
            for name in routines:
                line += name+","
            self.sections["PARENTS"] = line[:-1]+"\n"
            #Add children
            line = 2*indent
            routines = list(self.routine.called)
            routines.sort()
            for name in routines:
                line += name+","
            self.sections["CHILDREN"] = line[:-1]+"\n"
        #Header
        self.code = "!!****%s* %s\n" % (self.type, self.title)
        for section in self.categories:
            self.code += "!! %s\n" % section
            for line in self.sections[section].splitlines(1):
                self.code += "!!%s" % line


class Robodoc_Footer(Comment):
    "Class for the robodoc footer (comment)"


class Module(Code):
    "Fortran module"
    #Detect module
    re_module = re.compile("^[ ]*module[ ]*(?P<name>\w+)",re.MULTILINE+re.IGNORECASE)
    #
    def __init__(self,name=None,parent=None,message=None):
        "Initialisation"
        Structure.__init__(self,name,parent=parent,message=message)
        #No implicit from parent (no module inside a module)
        self.implicit = None
        if parent:
            self.dir = self.parent.dir
            self.file = self.parent.file
            #None if not in a module
            self.module = self.parent.module
        else:
            self.file = None
            self.dir = None
            self.module = None
        #List of used modules
        self.use_modules = set()
        #Gestion of the cache
        #1 - Time stamp
        if self.parent:
            self.timestamp = self.parent.timestamp
        else:
            self.timestamp = None
    #
    def analyze(self,line,iter_code,project):
        "Analyze the module"
        Code.analyze(self)
        self.Header = Code(parent=self)
        self.Header.add_code(line)
        #No argument
        self.Header.arguments = list()
        #First line contains the name of the module
        self.name = self.re_module.match(line).groupdict()['name']
        self.message.write("{%s:" % self.name)
        self.lower = self.name.lower()
        #The module belongs to him!
        self.module = self.lower
        #Add to the list of modules
        if self.lower in project.modules:
            previous = project.modules[self.lower]
            self.message.fatal("\n%s/%s: There does exist an already module <%s>\n" \
                               % (self.dir,self.file,self.name) + \
                               "in %s/%s: <%s>\n" % (previous.dir,previous.file,previous.name))
        else:
            project.modules[self.lower] = self
        #Analyze the use statement
        self.Use = Use(parent=self)
        (final_comments,line) = self.Use.analyze(iter_code)
        #Add the modules
        self.use_modules.update(self.Use.modules)
        #Test if implicit statement
        self.Implicit = Implicit(parent=self)
        #More than one line are analyzed
        line = self.Implicit.analyze(line,iter_code,comments=final_comments)
        #Analyze the declarations
        self.Declaration = Declaration(parent=self,comments=final_comments)
        line = self.Declaration.analyze(line,iter_code,self.Implicit.dict,project)
        #Now we have contains or end module
        self.analyze_contains(line,iter_code,project)
        self.message.write("}")
    #
    def analyze_contains(self,line,iter_code,project):
        "Analyze a contains statement or end module"
        if line:
            if self.re_contains.match(line):
                Code(parent=self).add_code(line)
            elif self.re_sub_end.match(line):
                #Detect the end of a subroutine or module: We have finished
                Code(parent=self).add_code(line)
            else:
                self.message.fatal("\n%s\n--> No detected 'contains' or 'end' statements!\n" % line \
                        + "This part of code can not be parsed as Fortran file:\n" \
                                   + "Analysis Error in %s/%s:<%s>\n" % (self.dir,self.file,self.name))
        #Start the analyze of the 'contains' section
        struct = None
        for line in iter_code:
            if self.re_comment_match.match(line):
                if not isinstance(struct,Comment):
                    struct = Comment(parent=self)
                struct.add_code(line)
            elif self.re_sub_end.match(line):
                #Detect the end of a subroutine or module (before re_sub_start ortherwise trouble)
                #We have finished
                Code(parent=self).add_code(line)
                return
            elif self.re_sub_start.match(line):
                line_lower = line.lower()
                if "subroutine" in line_lower or "recursive" in line_lower:
                    struct = Routine(parent=self,implicit=self.Implicit.dict)
                else:
                    struct = Function(parent=self,implicit=self.Implicit.dict)
                #Analyze the code
                struct.analyze(line,iter_code,project)
                #Add the modules used in the module to the used modules by the routine
                struct.use_modules.update(self.use_modules)
                #Add also the module used by the routine
                self.use_modules.update(struct.use_modules)
                #Add the variables declared in the module
                struct.Declaration.dict_vars.update(self.Declaration.dict_vars)
                #Add also the private variables
                struct.Declaration.dict_vars.update(self.Declaration.dict_vars_private)
            elif self.re_include.match(line):
                #Include directive
                struct = Include(project,parent=self,line=line)
            else:
                self.message.fatal("\n%s\n--> No detected routine!\n" % line \
                        + "This part of code can not be parsed as Fortran file:\n" \
                        + "Analysis Error in %s/%s:<%s>\n" % (self.dir,self.file,self.name))
    #
    def dependencies(self,project):
        "Build the dependencies from the list of use"
        dependencies = set()
        dirs = set()
        for mod in self.use_modules:
            a = project.modules.get(mod)
            if a:
                if a.dir == self.dir:
                    #Only dependencies of modules with the same directories
                    dependencies.add(a)
                else:
                    #Add dir which contains a module needed for the compilation of self
                    dirs.add(a.dir)
        return (dependencies,dirs)
    #
    def update_declaration(self,name,tt):
        "Add name into the declaration and also in each children"
        if name not in self.Declaration.dict_vars:
            self.Declaration.dict_vars[name] = tt
        for child in self.children:
            if isinstance(child,Module):
                child.update_declaration(name,tt)


class Routine(Module):
    "Class to handle subroutines or function"
    #Ampersand (continuation line)
    re_amp = re.compile('[ ]*&[ ]*')
    #Detect call (not after an if or ;)
    re_call = re.compile('(^[ \t]*call[ ]*)(\w+)', re.MULTILINE+re.IGNORECASE)
    #Detect optional arguments
    re_optional = re.compile("optional.*::", re.IGNORECASE)
    re_assumed_shape = re.compile("\(:.*\)", re.IGNORECASE)
    #
    def __init__(self,name=None,parent=None,implicit=None,message=None):
        "Initialisation"
        Module.__init__(self,name,parent=parent,message=message)
        self.nature = "subroutine"
        self.called = set()
        self.calling = set()
        #No interface defined for this routine (call self.interface to do that)
        self.has_interface = False
        self.optional = False
        #Signature (header+arguments) of the routine
        self.signature = ""
        #Use implicit (if inside module)
        self.implicit = implicit
        #If not a part of a generic routine
        self.nogeneric = True
        #Gestion of the cache
        #1 - Time stamp (done in Module class)
        #2 - The information of the cache is updated
        self.cached_updated = False
        #3 - called routines
        self.cached_called = None
        #4 - arguments
        self.cached_arguments = None
        #5 - calling
        self.cached_calling = None
    #
    def add_use_interface(self,project):
        "Add 'use self.module'"
        #if self.name == "vid":
        #  for called in self.called: print(called)
        #  sys.exit(1)

        modules = set()
        #Add routines and functions
        functions = list()
        else_modules = set()
        for called in self.called:
            try:
                routine = project.routines[called]
            except KeyError:
                if called in intrinsic_routines:
                    #Next routine
                    continue
                else:
                    #Next routine
                    self.message.warning_no_interface(self.dir,self.file,self.name,called)
                    continue

            # If the procedure is contained, the interface is explicit
            # so we can safely ignore it.
            # This small change leads to a significant speedup: 1m 20s vs 10s on ubu!

            if routine.is_contained:
                #print("Ignoring function %s because it's contained in %s" % (
                # routine.name, routine.parent.name)
                continue

            modules.add(routine.module)
            if isinstance(routine,Generic_Routine):
                #Use generic routine
                generic_routines = True
            if isinstance(routine,Function):
                functions.append(routine)

        #Remove main (for the program "optic" which has some subroutines !!!)
        modules.discard("main")
        #Add all modules for routine
        self.use_modules.update(modules)
        #Build a list in order to sort
        list_modules = list(modules)
        list_modules.sort()
        list_else_modules = list(else_modules)
        list_else_modules.sort()
        #Modify the interfaces
        self.Use.add_use_interface(list_modules,list_else_modules)
        #Add declarations of functions
        if functions:
            #Sort functions (to have unique ordering)
            #functions.sort(cmp=Structure.cmp)
            functions.sort(key=lambda f: f.lower)
            self.Declaration.add_functions(functions)
    #
    def analyze(self,line,iter_code,project):
        "Iterate the iterator up to the end of the routine"
        Code.analyze(self)
        #We are already inside a routine
        #We have the first line of header
        self.Header = Header_Routine(parent=self)
        #One line is not enough: Add line if continuation line
        self.Header.analyze(line,iter_code)
        #Add the routine to the project
        project.add_routine(self)
        #Add the cached information
        self.cache_use(project)
        #Analyze the use statement
        self.Use = Use(parent=self)
        (final_comments,line) = self.Use.analyze(iter_code)
        #Add the modules
        self.use_modules.update(self.Use.modules)
        #Test if implicit statement
        self.Implicit = Implicit(parent=self)
        #More than one line are analyzed
        line = self.Implicit.analyze(line,iter_code,comments=final_comments)
        #Analyze the declarations
        self.Declaration = Declaration(parent=self,comments=final_comments)
        line = self.Declaration.analyze(line,iter_code,self.Implicit.dict,project)
        #Analyze execution statements
        self.Execution = Execution(parent=self)
        self.Execution.analyze(line,iter_code,project)
    #
    def analyze_execution(self,project):
        "Analyze the execution statements of the routine"
        #Analyze the execution statements
        self.Execution.analyze_execution(self.Declaration.dict_vars,self.Implicit.dict,project)
    #
    def beautify(self):
        "Beautify the code of the routines (experimental)"
        #Remove tabulations
        self.code = self.code.replace("\t",indent)
        #Format the comment "!Arguments"
        self.code = re_arguments.sub(self.code,"\n\n" + comment_arguments)
        #Format the comment "!Local variables"
        self.code = re_local_variables.sub(self.code,"\n\n" + comment_local_variables)
    #
    def cache_save(self):
         "Save some information for the cache"
         return { "timestamp": self.timestamp,
                  "called": self.called,
                  "arguments": self.arguments,
                  "calling": self.calling }
    #
    def cache_use(self,project):
        "Use the cached information"
        #Test time stamp to save time
        if self.lower in project.cached:
            cached = project.cached[self.lower]
            if cached["timestamp"] == self.timestamp:
                self.cached_updated = True
                self.cached_called = cached["called"]
                self.cached_arguments = cached["arguments"]
                self.cached_calling = cached["calling"]
            else:
                #The cache is not updated. Nevertheless, we use some information
                self.cached_updated = False
                self.cached_called = cached["called"]
                self.cached_calling = cached["calling"]
    #
    def get_arguments(self,project):
        "Build the list of arguments of a routine"
        #Arguments
        if self.cached_arguments != None:
            self.arguments = self.cached_arguments
        else:
            self.arguments = self.Declaration.arguments(self.Header.arguments,self.Implicit.dict,project)

    #
    def graph_children(self,dict_graph,routines=list(),recursive=False):
        "Add children in the graph"
        calling = self.name
        if calling in dict_graph:
            #Already done: To avoid Recursive call, return
            return
        else:
            dict_graph[calling] = set()
        for name in self.called:
            try:
                struct = routines[name]
                dict_graph[calling].add(struct.name)
                if recursive:
                    struct.graph_children(dict_graph,routines=routines,recursive=True)
            except KeyError:
                dict_graph[calling].add(name)
    #
    def graph_parents(self,dict_graph,routines=list(),recursive=False):
        "Add parents in the graph"
        for name in self.calling:
            #Add the parents routines
            try:
                struct = routines[name]
                calling = struct.name
                if calling not in dict_graph:
                    dict_graph[calling] = set()
                called = self.name
                if called in dict_graph[calling]:
                    #Already done: To avoid recursive call, continue
                    continue
                dict_graph[calling].add(called)
                if recursive:
                    struct.graph_parents(dict_graph,routines=routines,recursive=True)
            except KeyError:
                pass
    #
    def interface(self):
        "Build the interface"
        if self.arguments == "":
            #Nothing to be detected
            self.has_interface = False
            return ""
        #Header (de-indent inside a continuation line)
        header = self.re_amp.sub('&'+indent*2,self.Header.code.lstrip())
        #Check if an optional argument is present
        if self.re_optional.search(self.arguments):
            self.optional = True
        code = self.arguments
        #Store the interface without interface...end interface
        self.signature = header \
                        + code \
                        + "end %s %s\n" % (self.nature,self.name)
        #For the interface, no implicit none
        code = indent_code("interface\n" \
                           + header \
                           + code \
                           + "end %s %s\n" % (self.nature,self.name) \
                           + "end interface\n")
        #Store the interface for this routine
        self.code_interface = code
        self.has_interface = True
        return code
    #
    def needs_explicit_interface(self):
        """
        Return a list with the reasons why self requires an explicit interface.
        Empyt list (i.e. False in boolean context) if the explicit interface is not needed.

        See:

        http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-79A3D50D-99F2-409F-AE8A-6A84FD1E47FA.htm

        for the list of conditions.
        """
        # List of attributes that require an explicit interface.
        # Here we have False alarms if these keywords are used to name variables
        attributes = [
          "allocatable", 
          "asynchronous", 
          "optional", 
          "pointer", 
          "target", 
          "value", 
          "volatile", 
          "class", 
          "codimension",
          "elemental",
          "bind",
        ]

        reasons = []
        for a in attributes:
           if a in self.arguments: reasons.append(a)

        # Assumed-shape arrays
        if self.re_assumed_shape.search(self.arguments): reasons.append("assumed shape")

        #TODO:
        # A result that is an array, or a pointer, or is allocatable (functions only)
        # A result whose length is neither assumed nor a constant (character functions only)
        return reasons

    @property
    def is_contained(self):
       """
       True if the procedure is contained in a module or in another procedure.
       That is if the parent of self is not a File!
       """
       if isinstance(self.parent, File): return False
       return True

    def set_called_routines(self,project):
        "Determine routines and functions which are called by the routine (only this part is different for a generic routine"
        #Define the called routines
        list = self.re_call.findall(self.Execution.code)
        for tt in list:
            called = tt[1].lower()
            self.called.add(called)
        #Add functions
        if project.re_functions:
            #We detect function in comments!!
            list = project.re_functions.findall(self.Execution.code)
            functions = set()
            for tt in list:
                called = tt.lower()
                if called not in intrinsic_routines and called != self.name:
                    #Add the functions
                    functions.add(called)
            self.called.update(functions)
    #
    def set_children_parents(self,project):
        "Create the list of children and parents of the routines"
        #Save time if called is already done
        if self.cached_calling != None:
            #Even it is not updated, we use this information
            self.calling = self.cached_calling
        if self.cached_updated:
            self.called = self.cached_called
        else:
            #In this case, read the code and search called routines (time consuming)
            self.set_called_routines(project)
            #Now change the calling routines of other routines.
            if self.cached_called:
                called_to_remove = self.cached_called.difference(self.called)
                #Remove self into calling routines which are not already called
                for called in called_to_remove:
                    if called in project.routines:
                        #Remove itself from the call
                        project.routines[called].calling.discard(self.lower)
                #New called routine to add
                called_to_add = self.called.difference(self.cached_called)
            else:
                called_to_add = self.called
            #Add self into calling routines for called
            for called in called_to_add:
                if called in project.routines:
                    project.routines[called].calling.add(self.lower)
                #elif called in intrinsic_routines:
                #    #Do nothing
                #    pass
                #else:
                #    self.message.warning_no_reference(self.dir,self.file,self.name,called)
        #Verbose message
        #text = "(%s:" % self.name
        #for called in self.called:
        #    text += "{%s}" % called
        #self.message.write(text+")")
        self.message.write("<%s>" % self.name)


class Generic_Routine(Routine):
    "Class to handle generic routine"
    def __init__(self,name,file):
        "Initialisation"
        Routine.__init__(self,name,parent=None,message=file.message)
        #A generic routine is not the child of a file but it is related to a file.
        #Initialisation done
        self.generic_file = file
        self.file = file.name
        self.dir = file.dir
        self.module = file.module
    #
    def add_use_interface(self,project):
        "Do nothing"
        return
    #
    def analyze_execution(self,project):
        "Do nothing"
        return
    #
    def get_arguments(self,project):
        "Do nothing"
        self.arguments = ""
        return
    #
    def interface(self):
        "Do nothing"
        return ""
    #
    def set_called_routines(self,project):
        "Determine routines and functions which are called by the routine: Use all routines from the other routines"
        for routine in self.generic_file.children:
            if isinstance(routine,Routine):
                self.called.update(routine.called)


class Function(Routine):
    "Class to handle Function"
    def __init__(self,name=None,parent=None,implicit=None,message=None):
        "Initialisation"
        Routine.__init__(self,name,parent,implicit,message)
        self.nature = "function"
        #Type of the function
        self.function_type = None
    #
    def analyze(self,line,iter_code,project):
        "Analyze the function"
        Code.analyze(self)
        self.Header = Header_Function(parent=self)
        self.Header.analyze(line,iter_code)
        #Add in the dictionary of all routines
        project.add_routine(self)
        #Add the cached information
        self.cache_use(project)
        #Analyze the use statement
        self.Use = Use(parent=self)
        (final_comments,line) = self.Use.analyze(iter_code)
        #Add the modules
        self.use_modules.update(self.Use.modules)
        #Test if implicit statement
        self.Implicit = Implicit(parent=self)
        #More than one line are analyzed
        line = self.Implicit.analyze(line,iter_code,comments=final_comments)
        #Analyze the declarations
        self.Declaration = Declaration(parent=self,comments=final_comments)
        line = self.Declaration.analyze(line,iter_code,self.Implicit.dict,project)
        #Analyze the execution
        self.Execution = Execution(parent=self)
        self.Execution.analyze(line,iter_code,project)

    #
    def get_arguments(self,project):
        "Build the list of arguments of a function"
        #Arguments (use the method of Routine class)
        Routine.get_arguments(self,project)
        #Check if the function has already a type
        if not self.function_type:
            #Check if dict_vars is not equal to None (case if the cache is used)
            if self.Declaration.dict_vars == None:
                #Dictionary of variables
                self.Declaration.analyze_variables(self.Implicit.dict,project)
            name = self.func_name.lower()
            if name not in self.Declaration.dict_vars:
                self.message.fatal("\n%s/%s: Check if the arguments are well formatted:\n" \
                    % (self.dir,self.file) \
                    + "Type not declared for the function '%s'\n" % self.name)
            else:
                #We are looking for self.name in dict_vars
                self.function_type = self.Declaration.dict_vars[name][0]
        #Add the type of the function
        name = self.name.lower()
        if name not in self.Declaration.dict_vars:
            self.Declaration.dict_vars[name] = (self.function_type,self.name)
        #If in a module or a subroutine, add the type of the function in parent.dict_vars
        if isinstance(self.parent,Module):
            #Need to update dict_vars for dict_vars of the module and other functions
            self.parent.update_declaration(name,self.Declaration.dict_vars[name])


class Program(Routine):
    "Fortran program (as a routine)"
    def interface(self):
        "No interface for program"
        self.code_interface = ""
        self.has_interface = False
        return ""

class Header_Routine(Code):
    "Header of a routine"
    #Detect the beginning of a block (program, subroutine, function)
    re_startblock = re.compile('(?P<header>[ \t]*' \
        + '(?P<type>(program|(recursive|elemental|pure)?[ ]*subroutine|(([^!\n]*?)function)))' \
        + '[ ]*(?P<name>\w+)[ ]*(?P<arguments>[(][^)]*[)])?[^\n]*)\n', \
           re.MULTILINE+re.IGNORECASE)
    #In arguments, remove some characters
    re_inarg = re.compile('[ \t\n&()]+')
    #
    #Analyze the code
    def analyze(self,line,iter_code):
        "Analyze the header"
        Code.analyze(self)
        self.add_code(line)
        while self.re_continuation.search(line):
            line = next(iter_code)
            self.add_code(line)
        #Analyze the header
        search = self.re_startblock.match(self.code).groupdict()
        self.parent.type = search['type']
        self.parent.name = search['name']
        args = search['arguments']
        if args == '()':
            args = None
        if args:
            #Remove comments
            args = self.re_comment.sub('',args)
            self.arguments = self.re_inarg.sub('',args)
            self.arguments = self.arguments.split(',')
        else:
            self.arguments = list()
        self.parent.lower = self.parent.name.lower()
        self.message.write("<%s" % self.parent.name)


class Header_Function(Header_Routine):
    "Header of a function"
    #Use to determine the variable in result statement for a function
    re_result = re.compile('result[(](?P<result>[^)]+)[)]')
    def analyze(self,line,iter_code):
        "Analyze the header"
        Code.analyze(self)
        Header_Routine.analyze(self,line,iter_code)
        #Determine the type of the function
        if (len(self.parent.type) > 9):
            #'xxx function'
            self.parent.function_type = self.parent.type[:-9].strip()
        else:
            #Determine inside the code
            #First check if result and use the corresponding name
            search = self.re_result.search(self.code)
            if search:
                self.parent.func_name = search.groupdict()['result']
            else:
                self.parent.func_name = self.parent.name
            #Add at the beginning of the list of arguments
            self.arguments.insert(0,self.parent.func_name)


class Use(Code):
    "Use statement"
    #Empty preprocessing directives
    re_empty_preproc = re.compile(\
                "^[ ]*#[ \t]*if[^\n]+\n([ ]*#[ \t]*else[ ]*\n)?[ ]*#[ \t]*endif[^\n]*\n?",re.MULTILINE)
    #Multiple (more than 3 \n): bug or unavailable
    re_multi_n = re.compile("[\n]{3,}")
    #Use statement
    re_use = re.compile('^[ \t]*use[ ]*(?P<name>\w+)', re.MULTILINE+re.IGNORECASE)
    re_use_prefix = re.compile('^[ \t]*use[ ]*'+prefix+'.*?\n', re.MULTILINE+re.IGNORECASE)
    #
    #Add use
    def add_use_interface(self,modules,else_modules):
        "Add 'use interfaces_xxx'"
        #Remove section created by abilint (replace by \n if a use after)
        self.code = re_section_abilint.sub('\n',self.code)
        if re_section_abilint_start.search(self.code):
            self.message.error("Routine %s: alone header of an abilint section" % self.parent.name)
            #Remove the beginning of a section created by abilint (if separated)
            self.code = re_section_abilint_start.sub('',self.code)
        if re_section_abilint_end.search(self.code):
            self.message.error("Routine %s: alone footer of an abilint section" % self.parent.name)
            #Remove the end of a section created by abilint (if separated)
            self.code = re_section_abilint_end.sub('\n',self.code)
        #Remove line with use prefix
        self.code = self.re_use_prefix.sub('',self.code)
        #Remove some replaced use
        self.code = re_use_replaced.sub('',self.code)
        #Remove empty preprocessing directives
        self.code = self.re_empty_preproc.sub('',self.code)
        #Add preprocessing commands, comments and implicit none
        #if True: # Old code
        if False: # New code
          if modules or else_modules:
              text_use = ""
              #Add modules
              for module in modules:
                  #Special and ugly
                  if "cuda" in module:
                      text_use += "#if defined HAVE_GPU_CUDA\n"
                  if prefix in module:
                      if module == self.parent.module and self.parent.has_interface:
                          #We except the given subroutine
                          text_use += indent + "use %s, except_this_one => %s\n" \
                                 % (self.parent.module,self.parent.name)
                      else:
                          text_use += indent + "use %s\n" % module
                  if "cuda" in module:
                      text_use += "#endif\n"
              if else_modules:
                  text_use += "#else\n"
                  for name in else_modules:
                      text_use += indent + "use %s\n" % name
              if text_use != "":
                  text_use = "\n\n" + use_before + text_use + use_after + "\n"
              #Add text_use inside use statements
              self.code += text_use
          else:
              #Be sure to have 2 \n
              self.code += (2-self.code[-2:].count("\n"))*"\n"

        # MG Add CPP variable with the name of the procedure.
        #self.code += "\n #undef ABI_FUNC \n #def ABI_FUNC " + str(self.parent.name)
        #print self.code + "\n #undef ABI_FUNC \n #define ABI_FUNC " + str(self.parent.name)

        else: # New code that adds the definition of ABI_FUNC
          #print "parent.name:" + self.parent.name
          text_use = \
            "\n\n" + use_before + "#undef ABI_FUNC\n#define ABI_FUNC '" + self.parent.name.strip() + "'\n"
          if modules or else_modules:
              #text_use = ""
              #Add modules
              for module in modules:
                  #Special and ugly
                  if "cuda" in module:
                      text_use += "#if defined HAVE_GPU_CUDA\n"
                  if prefix in module:
                      if module == self.parent.module and self.parent.has_interface:
                          #We except the given subroutine
                          text_use += indent + "use %s, except_this_one => %s\n" \
                                 % (self.parent.module,self.parent.name)
                      else:
                          text_use += indent + "use %s\n" % module
                  if "cuda" in module:
                      text_use += "#endif\n"
              if else_modules:
                  text_use += "#else\n"
                  for name in else_modules:
                      text_use += indent + "use %s\n" % name
              #if text_use != "":
              #    text_use = "\n\n" + use_before + text_use + use_after + "\n"
              #Add text_use inside use statements
              #self.code += text_use
          text_use += use_after + "\n"
          self.code += text_use

        #Remove multiple \n
        self.code = self.re_multi_n.sub('\n\n',self.code)
    #
    #Analyze the code
    def analyze(self,iter_code):
        """Analyze use statements
           (special treatment for preprocessing commands which needs to be clarified)
           only is ignored."""
        Code.analyze(self)
        #List of used modules
        self.modules = set()
        comments = ""
        inside_if = False
        for line in iter_code:
            res = self.re_use.match(line)
            if res:
                #Add comments and the line
                self.add_code(comments+line)
                #Add this module
                self.modules.add(res.groupdict()["name"].lower())
                while self.re_continuation.search(line):
                    #print line,res.groupdict()["name"].lower()
                    line = next(iter_code)
                    self.add_code(line)
                comments=""
            elif self.re_allcomment.match(line):
                if "#if" in line:
                    inside_if = True
                elif "#endif" in line:
                    inside_if = False
                comments += line
            else:
                #We have finished
                final_comments = ""
                if inside_if:
                    #We remove the beginning of the preprocessing directives
                    a_comments = comments.split("\n")
                    #Last element is not a line
                    a_comments.pop()
                    a_comments.reverse()
                    comments = ""
                    l_final = len(a_comments)
                    for cline in a_comments:
                        if "#if" in cline:
                            l_final = 2
                        if l_final > 0:
                            final_comments = cline + "\n" + final_comments
                        else:
                            comments = cline + "\n" + comments
                        l_final -= 1
                if comments:
                    self.add_code(comments)
                return (final_comments,line)


class Implicit(Code):
    "Class for the statement 'implicit'"
    re_imp_stat = re.compile("(?P<type>.*)[ ]*[(](?P<range>[^)]+)[)]")
    default_dict = dict()
    #Default in Fortran
    for i in range(97,105):
        #real
        default_dict[chr(i)] = "real"
    for i in range(105,111):
        #integer
        default_dict[chr(i)] = "integer"
    for i in range(111,123):
        #real
        default_dict[chr(i)] = "real"
    #
    #Detect implicit statements
    def analyze(self,line,iter_code,comments=""):
        "Test if implicit statements are present"
        Code.analyze(self)
        if "implicit" in line.lower():
            if comments:
                #Fatal error
                self.message.fatal("%s/%s[%s]: " % (self.dir,self.file,self.parent.name) \
                    + "Preprocessing block not closed for use statements\n" )
            #Check if many implicit statements
            while True:
                search = self.re_continuation.search(line)
                self.code += line
                line = next(iter_code)
                if not ("implicit" in line.lower() or search):
                    #The last line is not an implicit statement or the previous one has not a continuation sign
                    break
            #Build a dictionary
            self.implicit()
            return line
        elif self.parent.implicit != None:
            self.dict = self.parent.implicit
            return line
        else:
            #Error (no implicit statement) and build a dictionary
            self.implicit()
            self.message.error("No implicit statement in %s (%s/%s)" \
                % (self.parent.name,self.dir,self.file))
            return line
    #
    #Build a dictionary
    def implicit(self):
        "Give the type of variables given by implicit statement"
        lines = self.code.lower()
        if "none" in lines:
            self.dict = dict()
            return
        #Default implicit in Fortran (warning: copy the defaut dict)
        self.dict = self.default_dict.copy()
        if self.code == "":
            #Finished
            return
        #Split in lines and remove blank lines
        lines = lines.split("\n")
        lines.remove('')
        for line in lines:
            #We analyze the implicit statements
            line = line.replace('implicit','').strip()
            search = self.re_imp_stat.match(line)
            if search:
                search = self.re_imp_stat.match(line).groupdict()
            else:
                self.message.fatal("%s/%s[%s]: " % (self.dir,self.file,self.parent.name) \
                    + "%s" % line \
                    + "Analysis error of implicit statement\n" )
            type_imp = search["type"].strip()
            #a-h,...
            table = search["range"].split(",")
            for letters in table:
                (a1,a2) = letters.split('-')
                a1 = ord(a1)
                a2 = ord(a2)+1
                for i in range(a1,a2):
                    self.dict[chr(i)] = type_imp
        #All lines are analyzed
        return


class Declaration(Code):
    "Declaration statement"
    #Reserved words for declaration
    reserved = [ "allocatable", "character", "complex", "dimension", "doublecomplex", "doubleprecision",
                 "integer","intent",
                 "in", "out", "inout", "kind", "len", "optional", "real", "target", "pointer",
                 "logical", "type", "parameter", "max", "min", "modulo", "merge",
                 "private", "public", "len_trim", "interface", "function" ]
    #Detection of character as style f77 + f95
    re_character_f77 = re.compile('[ ]*character[ ]*([*]?[(][^)]+[)]|[*][0-9]+|)',re.IGNORECASE)
    #Detect declarations
    re_declaration = re.compile('^[ \t]*' \
        + '(allocatable|character|common|complex|data|dimension|double|end[ ]+type|equivalence|external|'\
        + 'integer|intrinsic|logical|parameter|private|public|real|save|type|class)', re.IGNORECASE)
    #Detect "type " or "type," or "type::"
    re_def_type = re.compile('^[ \t]*(type|class)[ ]*(?![(])',re.IGNORECASE)
    #Detect digits only (1.2d0 or 1.3e-4 etc.)
    re_digits = re.compile("^\d+[.]?\d*[de]?[+-]?[\d]*$")
    #Detect digits at the beginning for 0.0_dp
    re_digits_start = re.compile("^\d+[.]?\d*[de]?[+-]?[\d]*_")
    #Remove elements of type
    re_element = re.compile("%\w+")
    #Detect a group after =
    re_equal = re.compile('[ ]*=.*')
    #Detect interface
    re_interface_start = re.compile('^[ \t]*interface',re.IGNORECASE)
    re_interface_end   = re.compile('^[ \t]*end[ ]+interface',re.IGNORECASE)
    #Multiple \n
    re_mn = re.compile('\n+',re.MULTILINE)
    #No letter (add . to have 1.d0 and % for type)
    re_noletter = re.compile("[^a-zA-Z0-9_.%]+")
    #No ampersand
    re_noamp = re.compile("[ ]*[&]+[ ]*")
    #Detect only declarations without include, data, external and save
    re_only_declaration = re.compile('^[ \t]*' \
        + '(character|complex|dimension|double|end[ ]+type|integer|interface|logical|parameter|private|public|real|type|class)',\
        re.IGNORECASE)
    #For character(len=xxx)
    re_character = re.compile('character[(](len[ =]+)?(?P<len>[^)]+)[)]',re.IGNORECASE)
    #For complex(kind=dp) or complex(kind(dp))
    re_complex = re.compile('complex[(](kind[=(])?(?P<kind>[^)]+)[)]+',re.IGNORECASE)
    #Detect !Local
    re_local = re.compile("!Local",re.IGNORECASE)
    #For parameter(xxx)
    re_parameter = re.compile('parameter[ ]*[(](?P<liste>[^)]+)[)]',re.IGNORECASE)
    #For real(kind=dp) or real(kind(dp))
    re_real = re.compile('real[(](kind[=(])?(?P<kind>[^)]+)[)]+',re.IGNORECASE)
    #For type(xxx)
    re_type = re.compile('type[(](?P<type>[^)]+)[)]',re.IGNORECASE)
    #Detect variable var(.*) and also xxx=
    #re_var = re.compile('(\w+([(][^)]+[)])?)')
    re_var = re.compile('(\w+[ ]*((=[^,]+)|([(][^)]+[)])?))')
    #
    def __init__(self,name=None,parent=None,comments=""):
        "Initialisation"
        Code.__init__(self,name,parent)
        self.code = comments
        #Dictionary of all variables
        self.dict_vars = None
        #Include directives
        self.includes = list()
    #
    #Add functions in the declaration
    def add_functions(self,functions):
        "Add declaration of function with preprocessing"
        #Remove section created by abilint
        self.code = re_section_abilint.sub('\n',self.code)
        if re_section_abilint_start.search(self.code):
            self.message.error("Routine %s: alone header of an abilint section" % self.parent.name)
            #Remove the beginning of a section created by abilint (if separated)
            self.code = re_section_abilint_start.sub('',self.code)
        if re_section_abilint_end.search(self.code):
            self.message.error("Routine %s: alone footer of an abilint section" % self.parent.name)
            #Remove the end of a section created by abilint (if separated)
            self.code = re_section_abilint_end.sub('',self.code)
        #We remove the declaration of the function
        for struct in functions:
            #Match at least 4 characters before :: which are not !, ' and $ in order to keep preprocessing directives
            #Warning: does not detect '& xxxxx' or in a continuation line
            self.code = re.sub("(?i)([^$!']{4,}[ ]*::.*?),?[ ]*%s(?=[, \n])" % struct.name, r"\1",self.code)
        #We remove two commas ',,'
        self.code = re.sub(",,",",",self.code)
        #We remove a comma behind "::"
        self.code = re.sub( "::[ ]*,", ":: ",self.code)
        #We remove a line ending by "::" with a comma or not behind
        self.code = re.sub( ".*::[ ]*[,]*[ ]*\n", "",self.code)
        #i# text_functions = ""
        #i# for struct in functions:
        #i#     text_functions += indent + "%s :: %s\n" % (struct.function_type,struct.name)
        #i# #Add at the end of declaration
        #i# if re_end_variables.search(self.code):
        #i#     #Add before "!******" only once (count=1)
        #i#     self.code = re_end_variables.sub("\n" + notuse_before + text_functions \
        #i#             + notuse_after + end_variables,self.code,count=1)
        #i# else:
        #i#     self.code += notuse_before + text_functions + notuse_after + "\n"
    #
    #Analyze the code
    def analyze(self,line,iter_code,dict_implicit,project):
        "Analyze the declaration statements"
        Code.analyze(self)
        if not line:
            line = next(iter_code)
        while True:
            if self.re_declaration.search(line):
                #We can not avoid assignation as 'reali=2'
                self.add_code(line)
                #Is it a continuation line or a comment line inside continuations lines?
                while self.re_continuation.search(line) or self.re_comment_match.match(line):
                    line = next(iter_code)
                    self.add_code(line)
            elif self.re_include.match(line):
                #Include directive (not add as child)
                struct = Include(project,line=line,file=self.file,dir=self.dir)
                self.includes.append(struct)
                self.add_code(line)
            elif self.re_comment_match.match(line):
                self.add_code(line)
            elif self.re_interface_start.match(line):
                #We enter in an interface block
                self.add_code(line)
                self.analyze_interface(iter_code)
            else:
                #We have finished
                #Test if not implicit
                if "implicit" in line:
                    self.message.fatal("\n%s/%s: [%s]\n--> Implicit statement '%s' inside declaration of variables" \
                            % (self.parent.dir,self.parent.file,self.parent.name,line[:-1]) \
                            + "\nPossible reason: An include statement before implicit statement\n")
                else:
                    break
            try:
                line = next(iter_code)
            except StopIteration:
                self.message.fatal("\n%s/%s: [%s]\n--> End of the code inside declaration!!\n" \
                        % (self.parent.dir,self.parent.file,self.parent.name))
        #We analyze all the variables
        self.analyze_variables(dict_implicit,project)
        #Get the last line
        return line
    #
    def analyze_interface(self,iter_code):
        "Iterate inside an interface"
        in_interface = 1
        while in_interface > 0:
            try:
                line = next(iter_code)
            except StopIteration:
                self.message.fatal("\n%s\n--> No detected end of the interface!\n" % line\
                                + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
            self.add_code(line)
            line_lower = line.lower()
            if "interface" in line_lower:
                if self.re_interface_end.match(line):
                    in_interface -= 1
                elif self.re_interface_start.match(line):
                    in_interface += 1
    #
    #Analyze all variables xxxx :: var -> dict[var] == xxx
    def analyze_variables(self,dict_implicit,project):
        "Analyze all variables xxxx :: var -> dict[var] == xxx"
        #First, we build a long line without continuation, comments and interface and define types
        code = self.code
        #We add the include files at the end of declaration
        for struct in self.includes:
            code += struct.struct.code
        iter_code = iter(code.splitlines(-1))
        code = list()
        dict_vars = dict()
        for line in iter_code:
            if self.re_only_declaration.search(line):
                #Definition of a type?
                if self.re_def_type.match(line):
                    #Create a type
                    ftype = Fortran_Type(parent=self.parent)
                    #Analyze the type (and go to "end type")
                    ftype.analyze(line,iter_code,dict_implicit,project)
                    #Add the type and the name
                    dict_vars[ftype.lower] = ("type",ftype.name)
                    #Next line
                    continue
                elif self.re_interface_start.match(line):
                    #Definition of an interface : Create an interface
                    interface = Fortran_Interface(parent=self.parent)
                    #Analyze the interface (and go to "end interface"
                    #and add the subroutines or the interface
                    dict_vars.update(interface.analyze(line,iter_code,dict_implicit,project))
                    continue
                #Remove '\n'
                code.append(line[:-1])
                while self.re_continuation.search(line):
                    null_line = True
                    #Detect if comment lines inside continuation lines
                    while null_line:
                        line = next(iter_code)
                        line = self.re_comment.sub('',line).lstrip()
                        null_line = (len(line) == 0)
                        code[-1] += line[:-1]
                #Remove ampersand
                code[-1] = self.re_noamp.sub('',code[-1])
        #First we assume to have ::
        iter_code = iter(code)
        #Build: declaration (decl) :: list of variables
        for line in iter_code:
            # xxx :: yyy
            i = line.find("::")
            if i > -1:
                decl = line[:i]
                liste = line[i+2:]
            else:
                line_lower = line.lower()
                #Keyword variables (not very robust)
                liste = line.strip().split()
                decl = liste[0]
                decl_lower = decl.lower()
                if len(liste) == 1 and (decl_lower == "private" or decl_lower == "public"):
                    #It is 'private' or 'public'
                    continue
                elif decl_lower == "double":
                    #Add 'precision' or 'complex'
                    decl += " " + liste[1]
                    liste = reduce(lambda x,y: x + " " + y, liste[2:])
                elif decl_lower[0:4] == "real" or \
                        decl_lower[0:7] == "complex" or \
                        decl_lower[0:7] == "integer" or \
                        decl_lower[0:7] == "logical" or \
                        decl_lower == "dimension":
                    liste = reduce(lambda x,y: x + " " + y, liste[1:])
                elif "character" in decl_lower:
                    #Detect character without ::
                    decl = self.re_character_f77.match(line).group()
                    liste = line.replace(decl,"")
                    decl = decl.replace(" ","")
                elif decl_lower[0:9] == "parameter":
                    res = self.re_parameter.search(line.strip())
                    if res:
                        decl="parameter"
                        liste=res.groupdict()["liste"]
                    else:
                        self.message.fatal("\n%s\n--> Parameter statement not correct!\n" % line\
                            + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
                else:
                    self.message.fatal("\n%s\n--> Strange declaration!\n" % line\
                        + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
            #Declaration -- lists of variables
            decl = decl.lower().strip()
            liste = liste.strip()
            #Check if dimension
            for (name,var) in split_variables(liste):
                if name in dict_vars:
                    #Already a statement (maybe dimension ??)
                    if "dimension" == decl:
                        #We keep the first declaration: dimension var(xx)
                        dict_vars[name] = (dict_vars[name][0],var)
                    else:
                        #We add decl
                        dname = dict_vars[name][0]
                        if dname == "":
                            dict_vars[name] = (decl,var)
                        else:
                            dict_vars[name] = ("%s, %s" % (dict_vars[name][0],decl),var)
                elif "dimension" == decl:
                    #We do not use
                    dict_vars[name] = ("",var)
                else:
                    dict_vars[name] = (decl,var)
        #Check if all variables have a type
        if not dict_implicit:
            #Implicit none
            for (name,(decl,var)) in dict_vars.items():
                if decl == "" or decl =="parameter":
                    self.message.fatal( "[%s/%s:%s] Variable {%s} has no type\n" \
                        % (self.parent.dir,self.parent.file,self.parent.name,name))
        else:
            #There is an implicit dictionary
            for (name,(decl,var)) in dict_vars.items():
                if decl == "":
                    #Add type of the implicit dictionary
                    dict_vars[name] = (dict_implicit[name[0]],var)
                elif decl =="parameter":
                    #Add type of the implicit dictionary
                    dict_vars[name] = ("%s, %s" % (dict_implicit[name[0]],decl),var)
        #Put private variables in a private directory and remove it
        self.dict_vars_private = dict()
        for (name,(decl,var)) in list(dict_vars.items()):
            if "private" in decl.lower():
                self.dict_vars_private[name] = dict_vars[name]
                del(dict_vars[name])
        self.dict_vars = dict_vars
    #
    #Give the declaration of arguments
    def arguments(self,arguments,dict_implicit,project):
        "Give declarations of arguments"
        #List of declarations
        declarations = list()
        #Set for parameters in argument (ex: selected_kind)
        parameters = set()
        #Set for modules in parameters
        modules = set()
        arguments_lower = [x.lower() for x in arguments]
        for argument in arguments:
            names = set()
            argument_lower = argument.lower()
            if argument_lower in self.dict_vars:
                (type_arg,arg) = self.dict_vars[argument_lower]
                if type_arg == "interface":
                    #Special case for interface
                    arg = str(arg)
                #Test if correct:
                if type_arg.count("real") > 1 or \
                   type_arg.count("integer") > 1 or type_arg.count("intent") > 1:
                    self.message.fatal("[%s/%s:%s]:" \
                        % (self.parent.dir,self.parent.file,self.parent.name) \
                        + " Argument '%s' has a declaration which depends on preprocessing directives\n" % arg \
                        + "Do not use preprocessing directives for argument\n")
            else:
                type_arg = ""
                arg = ""
            if type_arg == "":
                #We use the implicit
                if dict_implicit == {}:
                    text = "\nArguments of the routine '%s':" % self.parent.name
                    for arg in arguments_lower:
                        text += " '%s'" % arg
                    text += "\n==Code"+"="*50+">\n%s" % self.code + "="*56+"<\n"
                    self.message.fatal( \
                        text + "[%s/%s:%s] Argument {%s} is not declared\n" \
                        % (self.parent.dir,self.parent.file,self.parent.name,argument))
                else:
                    #Use implicit dictionary
                    type_arg = dict_implicit[argument_lower[0]]
                    if arg == "":
                        arg = argument
                    #Add in the dictionary of variables
                    self.dict_vars[argument_lower] = type_arg
            #Add declaration + (yyy) and then remove spaces
            if type_arg == "interface":
                liste = list()
            else:
                i = len(argument_lower)
                string = type_arg.lower().replace(" ","") + " " + arg.lower()[i:]
                #Each non-alphanumeric character is converted into blank, split and remove keywords.
                liste = self.re_noletter.sub(" ",string).split()
            #Check if type_arg does not depend on a parameter or a module (ex: real(ddp) or type(bidon))
            has_name = False
            for name in liste:
                if name in self.reserved:
                    pass
                elif not self.re_digits.match(name):
                    has_name = True
                    #Remove %xxx in order to keep the name of the type
                    i = name.find("%")
                    if i > -1:
                        name = name[:i]
                    names.add(name)
            #Store as a scalar or an array
            if "dimension" in liste or argument_lower+"(" in arg.lower():
                if has_name:
                    order = 100
                else:
                    order = 10
            elif "integer" in liste:
                #Insert first the integer
                order = 0
            else:
                #Scalar after the integer
                order = 1
            #Add the declaration of the argument with order
            if type_arg != "":
                declarations.append((order,arg,type_arg))
            #For each variables which depend arguments, we add information
            for name in names:
                #Find where some information about 'name' is stored
                has_name = False
                if name in arguments_lower:
                    #We have already
                    has_name = True
                elif name in self.dict_vars:
                    #Declared in the variable for the subroutine
                    parameters.add(self.dict_vars[name])
                    has_name = True
                elif name in intrinsic_functions:
                    #This is an intrinsic functions
                    has_name = True
                else:
                    #Check if some information is not stored in a module
                    unfound_module = None
                    for module in self.parent.use_modules:
                        if project.has_module(module):
                            dict_vars = project.modules[module].Declaration.dict_vars
                            if name in dict_vars:
                                modules.add(module)
                                has_name = True
                                break
                        else:
                            #A module is missing
                            self.message.warning("[%s/%s:%s] The module '%s' is missing!\n" \
                                % (self.parent.dir,self.parent.file,self.parent.name,module))
                            unfound_module=module
                if not has_name:
                    texte = "%s/%s:%s\n" % (self.parent.dir,self.parent.file,self.parent.name)
                    texte += "--> Arguments of the routine '%s':" % self.parent.name
                    for argu in arguments:
                        texte += " '%s'" % argu
                    if len(self.parent.use_modules) > 0:
                        texte += "\n    Used modules:"
                        for module in self.parent.use_modules:
                            texte += " %s" % module
                    #If inside a module: don't care for interface
                    if self.parent.module:
                        message = self.message.error
                    else:
                        message = self.message.fatal
                    if unfound_module:
                        message("%s\n   " % texte \
                                + " The module '%s' is not found and" % module \
                                + " the argument '%s' depends on '%s' which could be in this module" \
                                % (arg,name))
                    else:
                        message("%s\n[%s/%s:%s]:" % \
                                           (texte,self.parent.dir,self.parent.file,self.parent.name) \
                                + " The argument '%s' depends on '%s'" % (arg,name) \
                                + " which is not found in the declaration even in a module" )
        #Build the declaration of arguments
        decl_args = ""
        #Sort arguments (integer, scalar, arrays)
        declarations.sort()
        #Add modules
        for module in modules:
            decl_args += "use %s\n" % module
        #Add implicit none
        decl_args += "implicit none\n"
        #Add parameters
        for param in parameters:
            decl_args += "%s :: %s\n" % param
        for (a,arg,type_arg) in declarations:
            if type_arg == "interface":
                #Special case
                line = "interface\n%send interface\n" % arg
            else:
                line = "%s :: %s\n" % (type_arg,arg)
                if len(line) > 100:
                    #Split the line
                    line = line[:60] + line[60:].replace(',',', &\n&'+9*' ',1)
            decl_args += line
        return decl_args


class Execution(Code):
    "The execution of the routine or a function"
    #Comment at the end of a line
    re_comment_line = re.compile('[!].*\n')
    #Character string
    re_string = re.compile('''['"][^'"]*['"]''')
    #Alphanumeric
    re_word = re.compile('[a-z]\w*')
    #List of reserved keywords (special case for call, end, format)
    reserved_keywords = [ "case", "close", "continue", "cycle",
                         "default", "do", "else", "elseif", "exit", "goto","go","to",
                         "if", "nullify",
                         "print", "return", "select", "stop", "then", "where", "while" ]
    #Add intrinsic functions
    reserved_keywords.extend(intrinsic_functions)
    #List of reserved keywords with variables
    dict_keywords = { "allocate": [ "stat" ],
                      "backspace": [ "unit", "iostat", "err" ],
                      "close": [ "unit", "iostat", "err", "status"],
                      "deallocate": [ "stat" ],
                      "endfile": [ "unit", "iostat", "err" ],
                      "inquire": [ "unit", "exist", "file", "opened", "number", "named", "access", "sequential", "form", "formatted",
                                   "recl", "nextrec", "blank", "position", "action", "read", "write", "readwrite", "delim", "pad"],
                      "maxval": [ "mask" ],
                      "minval": [ "mask" ],
                      "open": [ "access", "action", "blank", "delim", "file", "form", "iostat", "pad", "position", "recl", "status", "unit"],
                      "pack": [ "mask" ],
                      "read": [ "fmt", "iostat", "rec", "unit"],
                      "rewind": [ "unit", "iostat", "err" ],
                      "sum": [ "mask" ],
                      "write": [ "advance", "fmt", "iostat", "unit"]}
    #
    def analyze(self,line,iter_code,project):
        "Iterate the iterator up to the end of the routine."
        Code.analyze(self)
        self.code = ""
        while True:
            line_lower = line.lstrip()[:8].lower()
            if "contains" == line_lower:
                if self.re_contains.match(line):
                    self.message.write(":")
                    Module.analyze_contains(self.parent,line,iter_code,project)
                    #The end is detected
                    self.message.write(">")
                    return
            elif "end" == line_lower[:3]:
                if self.re_sub_end.match(line):
                    #Detect the end of the subroutine: We have finished
                    self.code += line
                    self.message.write(">")
                    return
            self.code += line
            try:
                line = next(iter_code)
            except StopIteration:
                self.message.fatal("\n%s\n--> No detected end of the routine!\n" % line\
                        + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
    #
    def analyze_execution(self,dict_vars,dict_implicit,project):
        "Analyze the execution statements (initialization, unused variables)"
        if dict_vars == None:
            self.message.fatal("No dictionary of variables\n" \
                    + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
        #We have a dictionary
        code = split_code(self.code)
        variables = set()
        #Check if all variables are declared
        for line in code:
            iter_line = iter(line)
            reserved = list()
            #Use to detect inside a call and remove xxx=
            in_call = False
            for word in iter_line:
                if self.re_word.match(word):
                    if word in self.dict_keywords.keys():
                        #Is a keyword with variables
                        reserved = self.dict_keywords[word]
                    elif word in self.reserved_keywords:
                        #Is a keyword
                        pass
                    elif word in reserved:
                        #Reserved keywords
                        pass
                    elif word == "call":
                        #We iterate (not consider the next word)
                        in_call = True
                        next(iter_line)
                    elif word == "end" or word == "enddo" or word == "endif":
                        #We iterate at the end of the line
                        #if "end do", we detect if there is a block name
                        l=len(line)
                        if (word == "endif" and l == 2) or (l == 3 and line[1] == "if") or \
                           (word == "enddo" and l == 2) or (l == 3 and line[1] == "do"):
                            variables.remove(line[-1])
                        break
                    elif word == "format":
                        #We skip the line
                        break
                    else:
                        if in_call:
                            #Check if xxx= (should be better to have also reserved words)
                            nxt_ = next(iter_line)
                            if nxt_ == "=":
                                #Do not add
                                continue
                        #Add the variable
                        variables.add(word)
        declared_variables = set(dict_vars.keys())
        #unused = declared_variables.difference(variables)
        #Add the variables from modules
        for module in self.parent.use_modules:
            if project.has_module(module):
                declared_variables.update(list(project.modules[module].Declaration.dict_vars.keys()))
            else:
                #A module is missing
                self.message.warning("[%s/%s:%s] The module '%s' is missing!\n" \
                    % (self.parent.dir,self.parent.file,self.parent.name,module))
        #Treat undeclared variables
        undeclared = list(variables.difference(declared_variables))
        if undeclared:
            undeclared.sort()
            self.message.section("%s/%s:%s\nUndeclared variables: %s\n" % \
                (self.parent.dir,self.parent.file,self.parent.name,undeclared))
            line = ""
            implicit = ""
            #Build the declaration
            declaration=""
            for var in undeclared:
                i=var[0]
                if i not in dict_implicit:
                    self.message.error("%s/%s: [%s]\n--> The variable '%s' has no type!\n" \
                        % (self.dir,self.file,self.parent.name,var))
                    continue
                if len(line) > 80 or dict_implicit[i] != implicit:
                    if line:
                        declaration += line+"\n"
                    implicit = dict_implicit[i]
                    line = "%s :: %s" % (dict_implicit[i],var)
                else:
                    if line:
                        line += "," + var
            if line:
                declaration += line+"\n"
                #self.message.write("Added declaration to the routine '%s':\n" % self.parent.name,verbose=-10)
            if declaration:
                self.message.section(declaration)
            #Add to the declaration of the routine
            #self.parent.Declaration.code += declaration
            self.message.done()


#Class to handle the type of Fortran datastructure
class Fortran_Type(Declaration):
    "Class to handle the Fortran type of datastructure"
    #Name of the type ("type xxxx", "type :: xxxx" or "type, yyy :: xxxx"
    re_name_type = re.compile('^[ \t]*(type|class)[ ]*(|.*::)[ ]*(?P<name>\w+)',re.IGNORECASE)
    #Detect "end type"
    re_end_type = re.compile('^[ \t]*end[ ]+type',re.IGNORECASE)
    #Detect only declarations without include, data, external and save
    re_type_declaration = re.compile('^[ \t]*' \
        + '(character|complex|dimension|double|integer|logical|parameter|private|public|real|type|class)', re.IGNORECASE)
    #
    def __init__(self,parent,name=None):
        "Special initialisation: we do not add as child to the parent"
        self.name = name
        if name:
            self.lower = self.name.lower()
        self.parent = parent
        #From parent, pick up the implicit dictionary
        self.implicit = parent.implicit
        self.message = parent.message
        self.file = parent.file
        self.dir = parent.dir
        self.code = ""
        self.code_backup = ""
        #Below are defined for compatibility with the class Routine
        self.module = self.parent.module
        self.timestamp = None
        self.children = list()
        self.cached = dict()
        #List of used modules
        self.use_modules = set()
        #List of includes directives
        self.includes = list()
        self.is_analyzed = False
    #
    def analyze(self,line,iter_code,dict_implicit,project):
        "Analyze the type statements"
        Code.analyze(self)
        #Determine the name
        self.name = self.re_name_type.match(line).groupdict()["name"]
        self.lower = self.name.lower()
        #Add a new fortran type and check if it is already defined
        if self.lower in project.ftypes:
            struct = project.ftypes[self.lower]
            self.message.error("%s/%s: [%s]\n--> This fortran type is already defined in %s/%s!" \
                    % (self.dir,self.file,self.name,struct.dir,struct.file))
        else:
            project.ftypes[self.lower] = self
        #Header (type <name>)
        self.header = line
        while True:
            try:
                #Add "\n" because it is removed before
                line = next(iter_code) + "\n"
            except StopIteration:
                self.message.fatal("%s/%s: [%s]\n--> Incorrect end of a fortran type!!\n'%s'\n" \
                        % (self.dir,self.file,self.name,line[:-1]))
            if self.re_type_declaration.search(line):
                self.add_code(line)
                #Is it a continuation line or a comment line inside continuations lines?
                while self.re_continuation.search(line) or self.re_comment_match.match(line):
                    line = next(iter_code) + "\n"
                    self.add_code(line)
            elif self.re_comment_match.match(line):
                self.add_code(line)
            elif self.re_end_type.search(line):
                #We have finished
                self.end = line
                break
            else:
                self.message.fatal("%s/%s: [%s]\n--> Incorrect line in a fortran type declaration!\n'%s'\n" \
                        % (self.dir,self.file,self.name,line[:-1]))
        #We analyze all variables
        self.analyze_variables(dict_implicit,project)


#Class to handle the fortran interfaces inside declarations
class Fortran_Interface(Fortran_Type):
    "Class to handle a fortran interface (__init__ function as Fortran_Type)"
    #Detect the name of the interface
    re_interface_name = re.compile('^[ \t]*interface[ ]+(?P<name>\w+)?',re.IGNORECASE)
    re_module_procedure = re.compile('^[ \t]*module[ ]+procedure[ ]+(?P<name>\w+)?',re.IGNORECASE)
    #
    def add_routine(self,routine):
        "Do nothing"
        pass
    #
    def analyze(self,line,iter_code,dict_implicit,project):
        "Analyze an interface"
        Code.analyze(self)
        #Determine the name of the interface
        reg = self.re_interface_name.match(line)
        if reg:
            #This is a generic interface
            self.name = reg.groupdict()['name']
            self.lower = self.name.lower()
            self.generic = True
        else:
            self.generic = False
        in_interface = 1
        #Detect subroutines, functions or 'module procedure'
        children = dict()
        for line in iter_code:
            line_lower = self.re_allcomment.sub('',line.lower())
            if self.re_comment_match.match(line):
                continue
            elif "subroutine" in line_lower:
                struct = Routine(parent=self,implicit=dict_implicit)
                struct.analyze(line,iter_code,self)
                #Add the modules used in the interface
                self.use_modules.update(struct.use_modules)
            elif "function" in line_lower:
                struct = Function(parent=self,implicit=dict_implicit)
                struct.analyze(line,iter_code,self)
                #Add the modules used in the module to the used modules by the routine
                self.use_modules.update(struct.use_modules)
            elif "module" in line_lower and "procedure" in line_lower:
                name = self.re_module_procedure.match(line_lower).groupdict()['name']
                children[name] = ("subroutine",name)
                struct = Routine(parent=self,name=name,implicit=dict_implicit)
                #If continuation mark, iterate
                while self.re_continuation.search(line):
                    line = next(iter_code)
            elif self.re_interface_end.match(line_lower):
                in_interface -= 1
                self.add_code(line)
                #We have finished
                break
            else:
                self.message.fatal("\n'%s'\n--> No detected routine!\n" % line \
                        + "This part of code can not be parsed as Fortran file:\n" \
                                   + "Analysis Error in %s/%s:<%s>\n" % (self.dir,self.file,self.name))
        if in_interface:
            self.message.fatal("\n%s\n--> No detected end of the interface!\n" % line\
                               + "Analysis Error in %s/%s:<%s>\n" % (self.dir,self.file,self.name))
        #Give modules in parent
        self.parent.use_modules.update(self.use_modules)
        #Return a dictionary
        if self.generic:
            return { self.lower: ("interface",self.name)}
        else:
            for child in self.children:
                children[child.lower] = ("interface",child)
            return children


class Include(Code):
    "Class to handle include statement"
    #Extract file of the include file
    re_include_file = re.compile("""^[ ]*include[ ]+['"](?P<file>[\w./_-]+)['"]""",re.IGNORECASE)
    def __init__(self,project,parent=None,line=None,file=None,dir=None,):
        "Initialisation"
        Code.__init__(self,parent=parent,message=project.message)
        if not parent:
            if file:
                self.file = file
            if dir:
                self.dir = dir
        if line:
            try:
                self.includefile = self.re_include_file.match(line).groupdict()['file']
            except AttributeError:
                self.message.fatal("\n'%s'\n--> Wrong include statement!\n" % line[:-1] \
                            + "This part of code can not be parsed as Fortran file:\n" \
                            + "Analysis Error in %s/%s\n" % (self.dir,self.file))
            self.message.write("(include file %s)" % self.includefile)
            self.code = line
            self.struct = None
            if project:
                #Find the include file in the project
                self.struct = project.find_file(self.includefile)
                if not self.struct:
                    if self.includefile in project.given_include:
                        #We use own version of these include files as 'mpif.h'
                        self.message.error("Use own version of '%s'" % self.includefile)
                        file = project.add_file('.',self.includefile,create=True,read_only=True,File_Class=File_F90)
                        file.add_code(project.given_include[self.includefile])
                        self.struct = file
                    else:
                        self.message.error("The include file '%s' does not exist in the project!" % self.includefile)
                        #We create one with no code
                        file = project.add_file('.',self.includefile,create=True,read_only=True,File_Class=File_F90)
                        self.struct = file


class FatalError(Exception):
    "Error raised when Fatal message"
    def __init__(self,value):
       self.value = value
    def __str__(self):
        text = ""
        for line in self.value.splitlines(1):
            text += "!!   %s" % line
        return text

class Message:
    "Message management"
    def __init__(self,verbose=0,logfile="message.log"):
        "Initialisation"
        #List of errors
        self.terrors = list()
        #List of warnings
        self.twarnings = list()
        #Text of the Fatal error (None if not fatal error)
        self.tfatal = None
        #Message to avoid fatal error
        self.mfatal = ""
        #Raise an exception (if nofatal==True) for a fatal error instead of exiting
        self.nofatal = False
        #Set of not referenced routines
        self.noreference = dict()
        #Verbosity
        self.verbose = verbose
        #Open the log file
        self.logfile = logfile
        self.log = open(logfile,"w")
        #Inform if the last character was a '\n'
        self.ret = True
    #
    def bug(self,text):
        "Display a message about a possible bug and stops"
        tt = "Bug: %s\n" % text
        sys.stderr.write(tt)
        sys.exit(1)
    #
    def close(self):
        "Close the log flie"
        self.log.close()
    #
    def done(self):
        "Write done"
        self.write("done.\n",verbose=-10,flush=True)
    #
    def error(self,text):
        "Display an error message"
        tt = "Error[%d]: %s\n" % (len(self.terrors)+1,text)
        self.terrors.append(tt)
        if len(self.terrors) == 1 or not self.ret:
            sys.stdout.write("\n"+tt)
        else:
            sys.stdout.write(tt)
        self.ret = True
    #
    def flush(self):
        "Flush the buffer"
        sys.stdout.flush()
    #
    def fatal(self,text):
        "Display an error message and stops"
        #Flush before writing into stderr
        sys.stdout.flush()
        sys.stderr.write("\nFatal Error: %s\n" % text)
        sys.stderr.write(self.mfatal)
        self.tfatal = text
        if self.nofatal:
            #Raise an exception
            raise FatalError(text)
        else:
            sys.exit(1)
    #
    def final(self):
        "Final statistics: Write in a log file and resume."
        #Write at the end of the log file all warning and errors
        for tt in self.twarnings:
            self.log.write(tt)
        #Not referenced routines
        items = list(self.noreference.items())
        items.sort()
        for item in items:
            if item == 1:
                self.log.write("The routine %s is not referenced once.\n")
            else:
                self.log.write("The routine %s is not referenced %d times.\n" % item)
        #Errors
        for tt in self.terrors:
            self.log.write(tt)
        if self.tfatal:
            self.log.write(self.tfatal)
            self.log.write("\n" + "*"*20 + " Fatal Error " + "*"*20 + "\n\n")
            sys.stdout.write("\n" + "*"*20 + " Fatal Error " + "*"*20 + "\n\n")
        else:
            self.log.write("%d warnings.\n" % len(self.twarnings))
            self.log.write("%d not referenced routines.\n" % len(self.noreference))
            self.log.write("%d error(s).\n" % (len(self.terrors)))
            sys.stdout.write("%d warnings (%d not referenced routines) -- %d errors.\n" \
                    % (len(self.twarnings),len(self.noreference),len(self.terrors)))
        sys.stdout.write("See the file '%s' for warnings.\n" % self.logfile)
    #
    def section(self,text):
        "Display a message and flush"
        if not self.ret:
            self.write('\n')
        self.write(text,verbose=-10,flush=True)
        self.ret = (text[-1] == '\n')
    #
    def warning(self,text,verbose=0):
        "Display a warning message"
        tt = "Warning[%d]: %s\n" % (len(self.twarnings)+1,text)
        self.twarnings.append(tt)
        if not self.ret:
            tt = '\n'+tt
        if verbose <= self.verbose:
            sys.stdout.write(tt)
        self.ret = True
    #
    def warning_no_interface(self,dir,file,routine,called):
        "Display a warning message concerning not referenced routine"
        if called in self.noreference.keys():
            self.noreference[called] += 1
        else:
            self.noreference[called] = 1
        self.warning("[%s/%s:%s] No interface for the routine '%s'" \
               % (dir,file,routine,called))
    #
    def warning_no_reference(self,dir,file,routine,called):
        "Display a warning message concerning not referenced routine (for interface)"
        if called in self.noreference.keys():
            self.noreference[called] += 1
        else:
            self.noreference[called] = 1
        self.warning("[%s/%s:%s] No reference for the routine '%s'" \
               % (dir,file,routine,called))
    #
    def write(self,text,verbose=0,flush=False):
        "Display a message"
        if not text:
            return
        if verbose <= self.verbose:
            sys.stdout.write(text)
            #Copy to the log file
            self.log.write(text)
            self.ret = (text[-1] == '\n')
        if flush:
            #Flush stdout
            sys.stdout.flush()

#-----------------------
#End of class defintions
#-----------------------


#Header of the interface module.
head_interface = \
"""!!****m* ABINIT/%(name)s
!! NAME
!! %(name)s
!!
!! FUNCTION
!! This module contains the interfaces of the routines
!! %(description)s
!!
!! COPYRIGHT
!! Copyright (C) 2010-2018 ABINIT group
!! This file is distributed under the terms of the
!! GNU General Public License, see ~abinit/COPYING
!! or http://www.gnu.org/copyleft/gpl.txt .
!!
!! NOTES
!! THIS FILE IS GENERATED AUTOMATICALLY BY abilint.
!! To do that: config/scripts/abilint . .
!!
%(warning)s!!
!! SOURCE

#if defined HAVE_CONFIG_H
#include "config.h"
#endif

module %(name)s

 implicit none

"""


#Header of the dependencies files (abinit.dep and abinit.dir).
head_dependencies = \
"""#Dependencies %(message)s of the directory %(dir)s
#
#COPYRIGHT
#Copyright (C) 2010-2018 ABINIT group
#This file is distributed under the terms of the
#GNU General Public License, see ~abinit/COPYING
#or http://www.gnu.org/copyleft/gpl.txt .
#
#THIS FILE IS GENERATED AUTOMATICALLY BY abilint.
#To do that: config/scripts/abilint --dependencies . .

"""


#Ersatz of 'mpif.h' file
mpif_file = """
integer, parameter :: mpi_comm_self = 1, mpi_comm_null = 2
integer, parameter :: mpi_comm_world = 0, mpi_max = 1, mpi_min = 2, mpi_sum = 3
integer, parameter :: mpi_character = 5
integer, parameter :: mpi_integer = 7, mpi_integer8=11
integer, parameter :: mpi_real = 13, mpi_complex=18
integer, parameter :: mpi_double_precision = 17, mpi_double_complex=22
!Seems to have no value ?
integer, parameter :: mpi_in_place = 999
"""
#Ersatz of 'fftw3.f' file
fftw3_file = """
integer, parameter :: fftw_estimate = 64
integer, parameter :: fftw_forward = -1
"""
#Include files given by abilint
abinit_include = { "mpif.h": mpif_file,
                   "fftw3.f": fftw3_file}


#Exclude files
abinit_exclude = [ "m_build_info.F90", "psp9cc.F90", "quarantine" ]


#Files to generate generic routines
generic_routines = [ "chgbas.F90",
                     # Commented by MG: generic interfaces are now defined in m_header
                     #"hdr_io.F90",
                     #"hdr_skip.F90",
                     #"wffreaddatarec.F90",
                     #"wffwritedatarec.F90",
                     #"xderiveread.F90",
                     #"xderivewrite.F90",
                     ]

#File class per pattern
abinit_File_Class = [("*.F90",File_F90),("*.F90.in", File_F90), ("_*_",File_F90_Library),\
                     ("*.f",File_F77)]
#Add before *.F90
for file in generic_routines:
    abinit_File_Class.insert(0,(file,File_F90_Generic))


#Special modification inside the code
special_modif =  {}


#Define the hierarchy of directories to check the correctness of calls
def rank_dir(dir):
    "Define the hierarchy of directories in the project"
    try:
        rank = int(dir[0:2])
    except ValueError:
        if dir[0:2] == "ma":
            rank = 100
        else:
            rank = -1
    return rank


#Routines excluded in the graph
graph_excluded = [ "MPI_INIT", "MPI_COMM_RANK", "MPI_COMM_SIZE", "leave_new", "wrtout" ]
#Add intrinsic routines
graph_excluded.extend(intrinsic_routines)

#----------------
#Start the script
#----------------
if __name__ == "__main__":
    #Check arguments
    try:
        optlist, args = getopt.getopt(sys.argv[1:],\
                "bcdu:g:hlin:vs",\
                ["beautify","nocache","dependencies","dump_dtset=","graph=","help","libraries","lint","nofatal","verbose","skip_add_interface"])
    except getopt.error:
        sys.stderr.write("Error in arguments\n")
        usage(error=1)
    #Help message
    verbose = -10
    beautify = False
    graph = False
    libraries = False
    lint = False
    nofatal = False
    dump_dtset = False
    dependencies = False
    nocache = False
    skip_add_interface = False # Option used to skip the call to add_use_interface (very slow)

    for opt,arg in optlist:
        if   opt == "-b" or opt == "--beautify":
            beautify = True
            #Complete analysis
            lint = True
        elif opt == "-d" or opt == "--dependencies":
            dependencies = True
        elif opt == "-u" or opt == "--dump_dtset":
            dump_dtset = True
            dtset_arg = arg
        elif opt == "-g" or opt == "--graph":
            graph = True
            graph_arg = arg
        elif opt == "-h" or opt == "--help":
            usage(error=0)
        elif opt == "-l" or opt == "--libraries":
            libraries = True
        elif opt == "-i" or opt == "--lint":
            lint = True
        elif opt == "-c" or opt == "--nocache":
            nocache = True
        elif opt == "-n" or opt == "--nofatal":
            nofatal = True
        elif opt == "-v" or opt == "--verbose":
            verbose = 10
        elif opt in ["-s", "--skip-add-interface"]:
            skip_add_interface = True

    #Two arguments are required
    if len(args) != 2:
        sys.stderr.write('You have to specify the ABINIT source directory and the destination.\n')
        sys.stderr.write('    Ex: abilint . .\n')
        usage(error=1)
    #Define OLD and NEW directories
    OLD = args[0]
    NEW = args[1]
    #Create the project and read all files
    #Add file *.F90.in in src/27_toolbox_oop
    abinit = Project(OLD,name="ABINIT",\
                     pat_dir=["src","src/*"],pat_file=["*.F90","*.inc","27_toolbox_oop/*.F90.in"],\
                     logfile="abilint.log",\
                     exclude=abinit_exclude,given_include=abinit_include,\
                     File_Class=abinit_File_Class)
    #Add file Fortran77 from 01_macroav_ext
    abinit.add("%s/src/01_macroavnew_ext" % OLD,pat_file=["*.f"],read_only=True)
    #Add the description file of each library
    abinit.message.section("Add the libraries...")
    if libraries:
        #Add _xxx_
        abinit.add("%s/abilint" % OLD,pat_dir=["abilint/*"],pat_file=["_*_"])
        #Remove code except the first comments
        abinit.remove_code("^_.*_$")
    else:
        #Add description files of the libraries
        abinit.add(OLD,pat_dir=["abilint","abilint/*"],\
                   pat_file=["_*_"],read_only=True)
    #Build dictionary of macros for libPAW/libTetra
    abinit.add_cpp_macros_naive('./src/42_libpaw','libpaw.h','HAVE_LIBPAW_ABINIT')
    abinit.add_cpp_macros_naive('./src/17_libtetra_ext','libtetra.h','HAVE_LIBTETRA_ABINIT')
    abinit.message.write("(%d directories, %d files)" \
            % (len(abinit.dirs),len(abinit.files)),verbose=-10)
    abinit.message.done()
    #Raise an exception for a fatal error
    abinit.message.nofatal = nofatal
    #Message to display to avoid fatal error
    abinit.message.mfatal = "With the option --nofatal, abilint will " +\
            "always generate the files of interfaces (already processed).\n"
    try:
        #We check for a cache file
        abinit.cache_load(NEW,OLD=OLD,nocache=nocache)
        #Analyze the project
        abinit.analyze_all(exclude="interfaces_")
        #Set the called routines
        abinit.set_children_parents()
        if NEW == OLD:
            #Backup before changing to optimize the minimal number of copies
            abinit.backup()
        #Create files for all the interfaces and put them in "defs".
        abinit.interfaces_all(dir_mod="src/01_interfaces_ext",exclude=["src","98_main","10_defs","01_interfaces_ext","01_linalg_ext","01_triqs_ext","incs","libs","mods","quarantine"])
        if libraries:
            #Regenerate the file _xxx_
            abinit.generate(format="_%s_",pattern_dir="%s/abilint" % OLD)
        #Add "use interface_", remove the declarations of functions and add declarations

        if skip_add_interface:
            print("Skip call to add_use_interface")
        else:
            abinit.add_use_interface()

        #Special modifications (to be done once).
        abinit.special(special_modif)
        retcode = abinit.analyze_explicit_interfaces()

    except FatalError as value:
        #Rescue routine: Create all interfaces
        abinit.no_fatal()
        abinit.interfaces_all(dir_mod="01_interfaces_ext",exclude=["98_main","10_defs","01_interfaces_ext","01_linalg_ext","01_triqs_ext","incs","libs","mods","quarantine"],\
                              warning="!!\n!! WARNING\n" \
                + "!! No interface generated by abilint\n" \
                + "!! Please, correct the following problem in the code:\n" \
                + str(value))
    if dependencies:
        #Build the dependencies
        abinit.dependencies("abinit.dep","abinit.dir")
    if dump_dtset:
        #Build in the file dtset_arg the fortran type dataset_type
        abinit.dump_ftype('dataset_type',dtset_arg)
    if lint:
        #Analyze the interdependencies between directories
        abinit.analyze_directories()
        #Unused routines
        abinit.unused_routines()
        #Analyze all the comments in order to detect the robodoc structure
        abinit.analyze_comments()
        #Analyze the code (execution statements)
        abinit.analyze_execution()
    if beautify:
        #Improve the appearance of the code
        abinit.beautify()
    if graph:
        #Build in the file graph_arg some graphs
        abinit.graph(graph_arg,graph_excluded=graph_excluded)
    #We copy everything
    abinit.copy_all(NEW,only_if_changed=(NEW == OLD))
    #We copy a cache file
    abinit.cache_save(NEW)
    #Display some statistics
    abinit.statistics()
    #if libraries:
    #    abinit.message.write("\nExecute again abilint without the option '--libraries'\n", verbose=-10)
    #Close the log file
    abinit.message.close()

