#!/usr/bin/env python
# Copyright, 1999, Regents of the University of California
# Please see file Legal.htm
import string
from spark import GenericScanner, GenericParser
from semantics import *

#
#    SCANNING
#
class Token:
    def __init__(self, type, attr=None, filename="?", lineno="?"):
        self.type = type
        self.attr = attr
        self.filename = filename
        self.lineno = lineno

    def __cmp__(self, o):
        return cmp(self.type, o)

    def __repr__ (self):
        if self.attr:
            s = repr(self.attr)
        else:
            s = repr(self.type)
        return s

    def info(self):
        return "File " + repr(self.filename) + ", line " + repr(self.lineno)

    def error(self):
        print "Error concerning token", self, "(", self.info(), ")"
        raise SystemExit, 1

keywords=['integer','real',
    'doubleprecision',  
    'complex', 'doublecomplex', 
    'string', 'character', 'logical','type', 'kind',
    'function', 'subroutine', 'module', 'interface', 'contains',
    'intent', 'in', 'out', 'inout', 'optional', 'dimension', 'common',
    'temporary', 'size', 'allocatable',
    'end']

class FortranScanner(GenericScanner):
    def __init__(self, column1_comments):
        GenericScanner.__init__(self)
        self.c = column1_comments
    
    def tokenize(self, filename):
        self.lineno = 1
        self.filename = filename
        f = open(filename, "r")
        if self.c:
            input = ''
            line = f.readline()
            while line:
                if line[0] == 'C': 
                    line = '!' + line[1:]
                if line[0] == 'c': 
                    line = '!' + line[1:]
                input = input + line
                line = f.readline()
        else:
            input = f.read()
        f.close()
 
        self.rv = []
        GenericScanner.tokenize(self, input)
        return self.rv

    def new(self, type, attr=None):
        "Create a new token."
        return Token(type, attr, self.filename, self.lineno)

    def t_continue(self, s):
        r' &.*\n '
        self.lineno = self.lineno + 1
        pass
    
    def t_nl(self, s):
        r' \n '
        self.lineno = self.lineno + 1
        pass

    def t_whitespace(self, s):
        r' [ \t]+ '
        pass

    def t_comment(self, s):
        r' \!.* '
        self.rv.append(self.new(type='comment', attr=s))

    def t_colon(self, s):
        r' : '
        self.rv.append(self.new(type=s))

    def t_equal(self, s):
        r' = '
        self.rv.append(self.new(type=s))

    def t_comma(self, s):
        r' , '
        self.rv.append(self.new(type=s))

    def t_lparen(self, s):
        r' \( '
        self.rv.append(self.new(type=s))

    def t_rparen(self, s):
        r' \) '
        self.rv.append(self.new(type=s))

    def t_dp(self, s):
        r' double[\ \t]*precision '
        self.rv.append(self.new(type='doubleprecision'))
        
    def t_dc(self, s):
        r' double[\ \t]*complex '
        self.rv.append(self.new(type='doublecomplex'))
        
    def t_id(self, s):
        r' [a-zA-Z_][a-zA-Z_0-9]* '
        ls = string.lower(s)
        if ls in keywords:
            self.rv.append(self.new(type=ls))
        else:
            self.rv.append(self.new(type='id',attr=ls))

    def t_op(self, s):
        r' \+ | \* | \- | \/ '
        self.rv.append(self.new(type=s))
        
    def t_number(self, s):
        r' \d+ '
        t = self.new(type='number', attr=s)
        self.rv.append(t)

def scan(f, column1_comments):
    scanner = FortranScanner(column1_comments)
    return scanner.tokenize(f)

#
#    PARSING
#

class InterfaceParser(GenericParser):
    def __init__(self, start='afile'):
        GenericParser.__init__(self, start)
                
    def error(self, token):
        print token.error()
        raise SystemExit, 1

    def p_unchecked(self, args):
        """
        unchecked ::= *
        """
        return '1'

    def p_rules0(self, args):
        """
        dimspec ::= unchecked
        dimspec ::= expr
        term ::= factor
        expr ::= term
        factor ::= initializer
        """
        return args[0]

    def p_type0(self, args):
        """
        procdec ::= subroutine
        procdec ::= function
        intentid ::= in
        intentid ::= out
        intentid ::= inout
        intentid ::= temporary
        typename ::= integer
        typename ::= real
        typename ::= complex
        typename ::= logical
        typename ::= doubleprecision
        typename ::= character
        """
        return args[0].type

    def p_attr0(self, args):
        """
        factor ::= number
        factor ::= id
        """
        return args[0].attr

    def p_rules1(self, args):
        """
        argspec ::= ( arglist ) 
        """
        return args[1]

    def p_typeid_1(self, args):
        """
        typeid ::= typename kindspec
        """
        return FortranType(name=args[0], kind=args[1])   

    def p_typeid_2(self, args):
        """
        typeid ::= type ( id )
        """
        return FortranType(name=args[0].type, kind=args[2].attr)
          
    def p_kindspec_0(self, args):
        """
        kindspec ::= 
        """
        return None
         
    def p_kindspec_1(self, args):
        """
        kindspec ::= * ( akind )
        """
        return args[2]

    def p_kindspec_2(self, args):
        """
        kindspec ::= ( akind )
        kindspec ::= * akind
        """
        return args[1]

    def p_akind(self, args):
        """
        akind ::= number
        akind ::= id
        """
        return args[0].attr

    def p_typeid_3(self, args):
        """
        typeid ::= character * ( * )
        """
        return FortranType(name=args[0].type, kind='*')

    def p_typespec_1(self, args):
        """
        typespec ::= typeid
        """
        return FortranAttributes(type=args[0], intent='in', allocatable=0)

    def p_typespec_2(self, args):
        """
        typespec ::= typeid attributes : :
        """
        attrs = args[1]
        return FortranAttributes(type=args[0], intent=attrs.get('intent', 'in'), allocatable=attrs.get('allocatable', 0))

    def p_attrlist_1 (self, args):
        """
        attributes ::= 
        """
        return {}

    def p_attrlist_2 (self, args):
        """
        attributes ::= attributes , attribute
        """
        aname, avalue = args[2]
        args[0][aname] = avalue
        return args[0]

    def p_attribute_intent (self, args):
        """
        attribute ::= intent ( intentid )
        """
        return ("intent", args[2])

    def p_attribute_optional (self, args):
        """
        attribute ::= optional
        """
        return ("optional", 1)

    def p_attribute_allocatable (self, args):
        """
        attribute ::= allocatable
        """
        return ("allocatable", 1)

    def p_parens (self, args):
        """
        factor ::= ( expr )
        """
        return '(' + args[1] + ')'

    def p_binary (self, args):
        """
        expr ::= expr + term
        expr ::= expr - term
        term ::= term * factor
        term ::= term / factor
        dimspec ::= expr : expr
        """
        s = args[0] + args[1].type + args[2]
        return s

    def p_list_0 (self, args):
        """
        argspec ::= ( )
        declarations ::=
        """
        return []
                                
    def p_declarations (self, args):
        """
        declarations ::= declarations declaration
        """
        for x in args[-1]:
            args[0].append(x)
        return args[0]

    def p_list_1 (self, args):
        """
        proclist ::= procspec
        dimensions ::= dimspec
        idlist ::= item
        """
        return [args[0]]

    def p_list_2 (self, args):
        """
        proclist ::= proclist procspec
        dimensions ::= dimensions , dimspec
        idlist ::= idlist , item
        """
        args[0].append(args[-1])
        return args[0]

    def p_arglist_1 (self, args):
        """
        arglist ::= id 
        """
        return [args[0].attr]

    def p_arglist_2 (self, args):
        """
        arglist ::= arglist , id 
        comments ::= comments comment
        """
        args[0].append(args[-1].attr)
        return args[0]

    def p_procspec (self, args):
        """
        procspec ::= prochead declarations end_stmt
        """
        return FortranProcedure(args[0], args[1])

    def p_prochead (self, args):
        """
        prochead ::= procdec id argspec comments
        """
        return (args[0], args[1].attr, args[2], args[3])

    def p_item_1 (self, args):
        """
        item ::= id
        item ::= id = expr
        """
        x = FortranDeclaration(name=args[0].attr, dimlist=[])
        if len(args) == 3:
           x.value = args[2]
        return x

    def p_item_2 (self, args):
        """
        item ::= id ( dimensions )
        """
        return FortranDeclaration(name=args[0].attr, dimlist=args[2])

    def p_declaration_1 (self, args):
        """
        declaration ::= typespec idlist comments
        """
        result = []
        idlist = args[1]
        typespec = args[0]
        comment = string.join(args[2], '\n')
        for x in idlist:
            x.set_comment(comment)
            x.set_info(typespec)
            if hasattr(x, 'value'):
               x.intent = 'valued'
            result.append(x)
        return result

    def p_initializer (self, args):
        """
        initializer ::= size ( id )
        initializer ::= size ( id , number )
        """
        if len(args) == 4:
            return 'size(' + args[2].attr + ', 1)'
        else:
            return 'size(' + args[2].attr + ', ' + args[4].attr + ')'

    def p_module (self, args):
        """
        module_interface ::= module id comments proclist end_stmt
        """
        t = """Fortran 90 constructs not yet supported. 
               A change to Pyfort no longer uses this statement to determine
               the Python module name. Please see manual.
            """
        self.error (t, args[1])

    def p_emptyrhs (self, args):
        """
        comments ::=
        """
        return []

    def p_end_stmt (self, args):
        """
        end_stmt ::= end endtag comments
        """
        return args[:-1]

    def p_endtag_1 (self, args):
        """
        endtag ::= 
        """
        return (None, None)

    def p_endtag_2 (self, args):
        """
        endtag ::= blockkey
        """
        return (args[0], None)

    def p_endtag_3 (self, args):
        """
        endtag ::= blockkey id
        """
        return (args[0], args[1])

    def p_blockkey(self, args):
        """
        blockkey ::= module
        blockkey ::= interface
        blockkey ::= subroutine
        blockkey ::= function
        """
        return args[0]

    def verify_blockend (self, endinfo, key, id):
        actual_key, actual_id = endinfo[1]
        if actual_key and (actual_key.type != key):
            print "end statement error"
            self.error(actual_key)
        if actual_id and (actual_id.attr != id):
            print "end statement error"
            self.error(actual_id)

    def p_afile (self, args):
        """
        afile ::= comments proclist comments 
        """
        return args[1]

def parse(tokens):
    parser = InterfaceParser()
    return parser.parse(tokens)



syntax highlighted by Code2HTML, v. 0.9.1