# Copyright, 1999, Regents of the University of California
# Please see file Legal.htm
import types, string

class FortranType:
    "Description of a Fortran type."
    def __init__ (self, name, kind=None):
        assert type(name) == types.StringType
        assert kind is None or type(kind) == types.StringType
        self.name = name
        self.kind = kind

    def __repr__ (self):
        if self.name == 'character' and self.kind:
            s = 'character*(' + self.kind + ')'
        else:
            s = self.name
            if self.kind:
                s = s + '(' + self.kind + ')'
        return s

class FortranAttributes:
    "Attributes a Fortran entity can have"
    def __init__(self, type, intent, allocatable):
        assert is_FortranType(type)
        assert intent == 'in' or intent == 'out' or intent == 'inout' \
                or intent == 'temporary' or intent == 'valued'
        self.intent = intent
        self.type = type
        self.allocatable = allocatable

    def __repr__(self):
        s = repr(self.type) + ", intent = " + self.intent
        if self.allocatable: s = s + ", allocatable"
        return s

class FortranProcedure:
    "Interface info to a Fortran subroutine or function"
    def __init__(self, head, declarations):
        self.head = head
        self.dict = {}
        self.dict[self.routine_name()] = None
        for a in self.arguments():
            self.dict[a] = None
        for d in declarations:
            self.dict[d.name] = d
        for name in self.dict.keys():
            if name == self.routine_name() and not self.is_function():
                if not (self.dict[name] is None):
                    print name + " is declared in the body but it is a subroutine."
                    raise SystemExit, 1
                self.dict[name] = void_declaration(name)
            else:
                if self.dict[name] is None:
                    print name + " is not given a type in routine " + self.routine_name()
                    raise SystemExit, 1
        if self.is_function():
            self.dict[self.routine_name()].set_intent("out")

    def __repr__(self):
        s = repr(self.head) + "\n" 
        for x in self.declarations():
            s = s + "     " + repr(x) + "\n"
        return s

    def routine_name (self):
        "name of the routine"
        return self.head[1]

    def arguments (self):
        "names of the arguments"
        return self.head[2]

    def python_inputs(self):
        "list of arguments whose intent is in or inout"
        result = []
        for x in self.arguments():
            k = self.dict[x].intent
            if k == 'in' or k == 'inout':
                result.append(x)
        return result

    def valued (self):
        "list of arguments whose intent is valued"
        result = []
        for x in self.arguments():
            k = self.dict[x].intent
            if k == 'valued':
                result.append(x)
        return result

    def temporaries(self):
        "list of arguments whose intent is temporary"
        result = []
        for x in self.arguments():
            k = self.dict[x].intent
            if k == "temporary":
                result.append(x)
        return result

    def in_and_outs(self):
        "list of arguments whose intent is inout"
        result = []
        for x in self.arguments():
            k = self.dict[x].intent
            if k == "inout":
                result.append(x)
        return result

    def python_outputs(self):
        """list of arguments of type out, preceded by name of function
           if it is a function"""
# note that inouts are not outputs
        result = []
        if self.is_function():
            result.append(self.routine_name())
        for x in self.arguments():
            k = self.dict[x].intent
            if k == 'out':
                result.append(x)
        return result

    def routine_type (self):
        "subroutine or function?"
        return self.head[0]

    def return_type (self):
        "type returned by this function"
        if self.is_function():
            return self.declaration(self.routine_name()).type
        else:
            return FortranType("none")

    def is_function (self):
        "is this a function?"
        return self.routine_type() == 'function'

    def head_comments (self, indent=0):
        "Comments connected with the procedure itself."
        cs = []
        sep = ' '*indent
        for c in self.head[3]:
            cc = c
            if cc[0:1] == '!': cc = cc[1:]
            if cc[0:1] == ' ': cc = cc[1:]
            cs.append(sep + cc)
        return sep + string.join(cs, '\n')

    def declaration(self, name):
        "the declaration object for the entity with the given name"
        return self.dict[name]

    def declarations(self):
        "The list of declarations"
        result = []
        if self.is_function():
            result.append(self.declaration(self.routine_name()))
        for s in self.arguments():
            result.append(self.declaration(s))
        return result

    def make_procedure(self):
        routine_type = 'procedure'
        routine_name = self.head[1]
        argument_name = routine_name + 'arg'
        arguments = [argument_name] + self.head[2]
        declaration = FortranDeclaration (argument_name,[])
        declaration.set_intent ("out")
        declaration.set_type (self.dict[routine_name].type)
        self.dict[argument_name] = declaration
        self.head = routine_type, self.head[1], arguments, self.head[3]
        type = FortranType ("none")
        self.dict[routine_name].set_type (type)

    def add_argument(self, name, type, intent, preceding):
        arguments = self.head[2]
        i = arguments.index(preceding)
        arguments = arguments[:i+1] + [name] + arguments[i+1:]
        self.head = self.head[0], self.head[1], arguments, self.head[3]
        declaration = FortranDeclaration (name,[])
        declaration.set_intent (intent)
        declaration.set_type (type)
        self.dict[name] = declaration

unknown_type = FortranType('unknown-type')

class FortranDeclaration:
    "Declaration of a variable"
    def __init__(self, name, dimlist):
        assert type(name) == types.StringType
        assert type(dimlist) == types.ListType
        self.name = name
        self.dimlist = dimlist
        self.type = unknown_type
        self.intent = 'unknown-intent'
        self.allocatable = 0
        self.comment = ''

    def set_info(self, attributes):
        assert is_FortranAttributes(attributes)
        self.set_type(attributes.type)
        self.set_intent(attributes.intent)
        self.set_allocatable(attributes.allocatable)

    def set_comment(self, comment):
        assert type(comment) == types.StringType
        self.comment = comment

    def set_type(self, type):
        self.type = type

    def set_intent (self, intent):
        self.intent = intent

    def set_allocatable (self, allocatable):
        self.allocatable = allocatable

    def dimstr(self):
        "String repr of dimlist "
        if self.dimlist:
            s = string.join (self.dimlist, ', ')
        else:
            s = ''
        return s

    def rank (self):
        "Number of dimensions."
        return len(self.dimlist)

    def dimension (self, i):
        "i'th dimension, 0 based."
        assert i <= self.rank()
        assert i >= 0
        return self.dimlist[i]

    def __repr__(self):
        s = repr(self.type) + ', intent(' + self.intent + '):: ' + self.name 
        if self.dimlist:
            s = s + ' (' + self.dimstr() + ')'
        if self.allocatable:
            s = s + ' allocatable'
        return s

def void_declaration (name):
    d = FortranDeclaration (name, [])
    d.set_intent("out")
    d.set_type(FortranType("none"))
    return d

def is_FortranAttributes (s):
    return type(s) == types.InstanceType and s.__class__ == FortranAttributes

def is_FortranProcedure (s):
    return type(s) == types.InstanceType and s.__class__ == FortranProcedure

def is_FortranType (s):
    return type(s) == types.InstanceType and s.__class__ == FortranType

def is_FortranDeclaration (s):
    return type(s) == types.InstanceType and s.__class__ == FortranDeclaration


syntax highlighted by Code2HTML, v. 0.9.1