diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2006-09-16 05:56:18 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2006-09-16 05:56:18 +0000 |
commit | 617b46e97017ca4614d04090db5c7d999d1be422 (patch) | |
tree | 31fe8bd3b9a28b01d233eceae83735631789ee9b | |
parent | 8b6db6e885dec95dda7bbe2001d95bdee63e6cf1 (diff) | |
download | numpy-617b46e97017ca4614d04090db5c7d999d1be422.tar.gz |
4G f2py: first working example.
-rw-r--r-- | numpy/f2py/lib/analyzefortran.py | 4 | ||||
-rw-r--r-- | numpy/f2py/lib/base_classes.py | 156 | ||||
-rw-r--r-- | numpy/f2py/lib/block_statements.py | 149 | ||||
-rw-r--r-- | numpy/f2py/lib/doc.txt | 311 | ||||
-rw-r--r-- | numpy/f2py/lib/parsefortran.py | 8 | ||||
-rw-r--r-- | numpy/f2py/lib/python_wrapper.py | 549 | ||||
-rw-r--r-- | numpy/f2py/lib/research/pointers/pointer_size.c | 30 | ||||
-rw-r--r-- | numpy/f2py/lib/research/pointers/settypeinfo.f90 | 13 | ||||
-rw-r--r-- | numpy/f2py/lib/src/F_FUNC.cpp | 34 | ||||
-rw-r--r-- | numpy/f2py/lib/src/pyobj_to_Py_complex.c | 39 | ||||
-rw-r--r-- | numpy/f2py/lib/src/pyobj_to_double.c | 39 | ||||
-rw-r--r-- | numpy/f2py/lib/src/pyobj_to_long.c | 31 | ||||
-rw-r--r-- | numpy/f2py/lib/src/pyobj_to_npy_longlong.c | 36 | ||||
-rw-r--r-- | numpy/f2py/lib/src/pyobj_to_string_len.c | 10 | ||||
-rw-r--r-- | numpy/f2py/lib/statements.py | 15 | ||||
-rw-r--r-- | numpy/f2py/lib/typedecl_statements.py | 185 | ||||
-rw-r--r-- | numpy/f2py/lib/utils.py | 29 |
17 files changed, 1586 insertions, 52 deletions
diff --git a/numpy/f2py/lib/analyzefortran.py b/numpy/f2py/lib/analyzefortran.py index 4b2d3ca25..bf6fc5ee0 100644 --- a/numpy/f2py/lib/analyzefortran.py +++ b/numpy/f2py/lib/analyzefortran.py @@ -19,11 +19,14 @@ class FortranAnalyzer: block is a BeginSource instance with relevant attributes: name - reader name content - a list of statements + Statements are either block statements or simple statements. + Block statements have the following relevant attributes: name - block name blocktype - statement name (equal to lowered statement class name) content - a list of statements + Block statements may have additional attributes: BeginSource: top Module: @@ -42,6 +45,7 @@ class FortranAnalyzer: Associate: associations Type: specs, params Enum: + Simple statements have various attributes: Assignment: variable, expr PointerAssignment: variable, expr diff --git a/numpy/f2py/lib/base_classes.py b/numpy/f2py/lib/base_classes.py index ee19caaae..d1835edfa 100644 --- a/numpy/f2py/lib/base_classes.py +++ b/numpy/f2py/lib/base_classes.py @@ -50,12 +50,28 @@ class AttributeHolder: % (self.__class__.__name__,name,','.join(self._attributes.keys())) self._attributes[name] = value - def __repr__(self): - l = [] + def isempty(self): + for k in self._attributes.keys(): + v = getattr(self,k) + if v: return False + return True + + def __repr__(self): return self.torepr() + + def torepr(self, depth=-1, tab = ''): + if depth==0: return tab + self.__class__.__name__ + l = [self.__class__.__name__+':'] + ttab = tab + ' ' for k in self._attributes.keys(): v = getattr(self,k) - l.append('%s=%r' % (k,v)) - return '%s(%s)' % (self.__class__.__name__,', '.join(l)) + if v: + if isinstance(v,list): + l.append(ttab + '%s=<%s-list>' % (k,len(v))) + elif isinstance(v,dict): + l.append(ttab + '%s=<dict with keys %s>' % (k,v.keys())) + else: + l.append(ttab + '%s=<%s>' % (k,type(v))) + return '\n'.join(l) def todict(self): d = {} @@ -95,6 +111,33 @@ class Variable: self.init = None return + def get_bit_size(self): + typesize = self.typedecl.get_bit_size(self) + if self.is_pointer(): + # The size of pointer descriptor is compiler version dependent. Read: + # http://www.nersc.gov/vendor_docs/intel/f_ug1/pgwarray.htm + # https://www.cca-forum.org/pipermail/cca-fortran/2003-February/000123.html + # https://www.cca-forum.org/pipermail/cca-fortran/2003-February/000122.html + # On sgi descriptor size may be 128+ bits! + if self.is_array(): + wordsize = 4 # XXX: on a 64-bit system it is 8. + rank = len(self.bounds or self.dimension) + return 6 * wordsize + 12 * rank + return typesize + if self.is_array(): + size = reduce(lambda x,y:x*y,self.bounds or self.dimension,1) + if self.length: + size *= self.length + return size * typesize + if self.length: + return self.length * typesize + return typesize + + def get_typedecl(self): + if self.typedecl is None: + self.set_type(self.parent.get_type(self.name)) + return self.typedecl + def add_parent(self, parent): if id(parent) not in map(id, self.parents): self.parents.append(parent) @@ -182,6 +225,9 @@ class Variable: def is_parameter(self): return 'PARAMETER' in self.attributes def is_optional(self): return 'OPTIONAL' in self.attributes def is_required(self): return 'REQUIRED' in self.attributes + def is_pointer(self): return 'POINTER' in self.attributes + + def is_array(self): return not not (self.bounds or self.dimensions) def update(self, *attrs): attributes = self.attributes @@ -221,8 +267,9 @@ class Variable: def __str__(self): s = '' - if self.typedecl is not None: - s += self.typedecl.tostr() + ' ' + typedecl = self.get_typedecl() + if typedecl is not None: + s += typedecl.tostr() + ' ' a = self.attributes[:] if self.dimension is not None: a.append('DIMENSION(%s)' % (', '.join(self.dimension))) @@ -246,6 +293,10 @@ class Variable: s += ' = ' + self.init return s + def analyze(self): + typedecl = self.get_typedecl() + return + class ProgramBlock: pass @@ -257,6 +308,7 @@ class Statement: isvalid - boolean, when False, the Statement instance will be ignored """ modes = ['free90','fix90','fix77','pyf'] + _repr_attr_names = [] def __init__(self, parent, item): self.parent = parent @@ -264,15 +316,18 @@ class Statement: self.reader = item.reader else: self.reader = parent.reader - self.top = getattr(parent,'top',None) + self.top = getattr(parent,'top',None) # the top of statement tree + self.item = item + if isinstance(parent, ProgramBlock): self.programblock = parent elif isinstance(self, ProgramBlock): self.programblock = self elif hasattr(parent,'programblock'): self.programblock = parent.programblock - - self.item = item + else: + #self.warning('%s.programblock attribute not set.' % (self.__class__.__name__)) + pass # when a statement instance is constructed by error, set isvalid to False self.isvalid = True @@ -292,6 +347,31 @@ class Statement: return + def __repr__(self): + return self.torepr() + + def torepr(self, depth=-1,incrtab=''): + tab = incrtab + self.get_indent_tab() + clsname = self.__class__.__name__ + l = [tab + yellow_text(clsname)] + if depth==0: + return '\n'.join(l) + ttab = tab + ' ' + for n in self._repr_attr_names: + attr = getattr(self, n, None) + if not attr: continue + if hasattr(attr, 'torepr'): + r = attr.torepr(depht-1,incrtab) + else: + r = repr(attr) + l.append(ttab + '%s=%s' % (n, r)) + if self.item is not None: l.append(ttab + 'item=%r' % (self.item)) + if not self.isvalid: l.append(ttab + 'isvalid=%r' % (self.isvalid)) + if self.ignore: l.append(ttab + 'ignore=%r' % (self.ignore)) + if not self.a.isempty(): + l.append(ttab + 'a=' + self.a.torepr(depth-1,incrtab+' ').lstrip()) + return '\n'.join(l) + def get_indent_tab(self,colon=None,deindent=False): if self.reader.isfix: tab = ' '*6 @@ -321,8 +401,11 @@ class Statement: return tab def format_message(self, kind, message): - message = self.reader.format_message(kind, message, - self.item.span[0], self.item.span[1]) + if self.item is not None: + message = self.reader.format_message(kind, message, + self.item.span[0], self.item.span[1]) + else: + return message return message def show_message(self, message, stream=sys.stderr): @@ -350,13 +433,26 @@ class Statement: return def get_variable(self, name): - variables = self.parent.a.variables - if not variables.has_key(name): - variables[name] = var = Variable(self, name) - else: - var = variables[name] - var.add_parent(self) - return var + """ Return Variable instance of variable name. + """ + mth = getattr(self,'get_variable_by_name', self.parent.get_variable) + return mth(name) + + def get_type(self, name): + """ Return type declaration using implicit rules + for name. + """ + mth = getattr(self,'get_type_by_name', self.parent.get_type) + return mth(name) + + def get_type_decl(self, kind): + mth = getattr(self,'get_type_decl_by_kind', self.parent.get_type_decl) + return mth(kind) + + def get_provides(self): + """ Returns dictonary containing statements that block provides or None when N/A. + """ + return class BeginStatement(Statement): """ <blocktype> <name> @@ -371,13 +467,14 @@ class BeginStatement(Statement): with the line label parent - Block or FortranParser instance item - Line instance containing the block start statement - get_item, put_item - methods to retrive/submit Line instaces + get_item, put_item - methods to retrive/submit Line instances from/to Fortran reader. isvalid - boolean, when False, the Block instance will be ignored. stmt_cls, end_stmt_cls """ + _repr_attr_names = ['blocktype','name'] + Statement._repr_attr_names def __init__(self, parent, item=None): self.content = [] @@ -388,7 +485,6 @@ class BeginStatement(Statement): if not hasattr(self, 'name'): # process_item may change this self.name = '__'+self.blocktype.upper()+'__' - Statement.__init__(self, parent, item) return @@ -401,6 +497,20 @@ class BeginStatement(Statement): l.append(str(c)) return '\n'.join(l) + def torepr(self, depth=-1, incrtab=''): + tab = incrtab + self.get_indent_tab() + ttab = tab + ' ' + l=[Statement.torepr(self, depth=depth,incrtab=incrtab)] + if depth==0 or not self.content: + return '\n'.join(l) + l.append(ttab+'content:') + for c in self.content: + if isinstance(c,EndStatement): + l.append(c.torepr(depth-1,incrtab)) + else: + l.append(c.torepr(depth-1,incrtab + ' ')) + return '\n'.join(l) + def process_item(self): """ Process the line """ @@ -543,6 +653,7 @@ class EndStatement(Statement): name blocktype """ + _repr_attr_names = ['blocktype','name'] + Statement._repr_attr_names def __init__(self, parent, item): if not hasattr(self, 'blocktype'): @@ -571,7 +682,10 @@ class EndStatement(Statement): def analyze(self): return + def get_indent_tab(self,colon=None,deindent=False): + return Statement.get_indent_tab(self, colon=colon, deindent=True) + def __str__(self): - return self.get_indent_tab()[:-2] + 'END %s %s'\ + return self.get_indent_tab() + 'END %s %s'\ % (self.blocktype.upper(),self.name or '') diff --git a/numpy/f2py/lib/block_statements.py b/numpy/f2py/lib/block_statements.py index 446b1610c..fa08fc470 100644 --- a/numpy/f2py/lib/block_statements.py +++ b/numpy/f2py/lib/block_statements.py @@ -14,7 +14,7 @@ class HasImplicitStmt: a = AttributeHolder(implicit_rules = {}) - def get_type(self, name): + def get_type_by_name(self, name): implicit_rules = self.a.implicit_rules if implicit_rules is None: raise AnalyzeError,'Implicit rules mapping is null' @@ -74,7 +74,18 @@ class HasUseStmt: class HasVariables: - a = AttributeHolder(variables = {}) + a = AttributeHolder(variables = {}, + variable_names = [] # defines the order of declarations + ) + + def get_variable_by_name(self, name): + variables = self.a.variables + if variables.has_key(name): + var = variables[name] + else: + var = variables[name] = Variable(self, name) + self.a.variable_names.append(name) + return var def topyf(self,tab=''): s = '' @@ -92,8 +103,17 @@ class HasTypeDecls: s += stmt.topyf(tab=' '+tab) return s + def get_type_decl_by_kind(self, kind): + type_decls = self.a.type_decls + type_decl = type_decls.get(kind, None) + if type_decl is None: + return self.get_entity(kind) + raise NotImplementedError,'get type_decl from use modules' + return type_decl + class HasAttributes: + known_attributes = [] a = AttributeHolder(attributes = []) def topyf(self, tab=''): @@ -102,6 +122,29 @@ class HasAttributes: s += tab + attr + '\n' return s + def is_private(self): + attributes = self.a.attributes + if 'PUBLIC' in attributes: return False + if 'PRIVATE' in attributes: return True + return + def is_public(self): return not self.is_private() + + def update_attributes(self,*attrs): + attributes = self.a.attributes + known_attributes = self.known_attributes + if len(attrs)==1 and isinstance(attrs[0],(tuple,list)): + attrs = attrs[0] + for attr in attrs: + uattr = attr.upper() + if uattr not in attributes: + if isinstance(known_attributes,(list, tuple)): + if uattr not in known_attributes: + self.warning('unknown attribute %r' % (attr)) + elif known_attributes(uattr): + self.warning('unknown attribute %r' % (attr)) + attributes.append(uattr) + return + class HasModuleProcedures: a = AttributeHolder(module_procedures = []) @@ -196,10 +239,15 @@ class Module(BeginStatement, HasAttributes, """ match = re.compile(r'module\s*\w+\Z', re.I).match end_stmt_cls = EndModule + a = AttributeHolder(module_subprogram = {}, - module_data = {}, + module_provides = {}, # all symbols that are public and so + # can be imported via USE statement + # by other blocks ) + known_attributes = ['PUBLIC', 'PRIVATE'] + def get_classes(self): return access_spec + specification_part + module_subprogram_part @@ -208,6 +256,9 @@ class Module(BeginStatement, HasAttributes, self.name = name return BeginStatement.process_item(self) + def get_provides(self): + return self.a.module_provides + def analyze(self): content = self.content[:] @@ -225,11 +276,19 @@ class Module(BeginStatement, HasAttributes, if content: self.show_message('Not analyzed content: %s' % content) + module_provides = self.a.module_provides + for name, var in self.a.variables.items(): + if var.is_public(): + if module_provides.has_key(name): + self.warning('module data object name conflict with %s, overriding.' % (name)) + module_provides[name] = var + return def topyf(self, tab=''): s = tab + 'MODULE '+self.name + '\n' s += HasImplicitStmt.topyf(self, tab=tab+' ') + s += HasAttributesStmt.topyf(self, tab=tab+' ') s += HasTypeDecls.topyf(self, tab=tab+' ') s += HasVariables.topyf(self, tab=tab+' ') s += tab + ' CONTAINS\n' @@ -270,7 +329,7 @@ class EndProgram(EndStatement): match = re.compile(r'end(\s*program\s*\w*|)\Z', re.I).match class Program(BeginStatement, ProgramBlock, - HasAttributes, + HasAttributes, # XXX: why Program needs .attributes? HasImplicitStmt, HasUseStmt): """ PROGRAM [name] """ @@ -340,6 +399,8 @@ class Interface(BeginStatement, HasImplicitStmt, HasUseStmt, end_stmt_cls = EndInterface blocktype = 'interface' + a = AttributeHolder(interface_provides = {}) + def get_classes(self): return intrinsic_type_spec + interface_specification @@ -358,6 +419,29 @@ class Interface(BeginStatement, HasImplicitStmt, HasUseStmt, return 'ABSTRACT INTERFACE' return 'INTERFACE '+ str(self.generic_spec) + def get_provides(self): + return self.a.interface_provides + + def analyze(self): + content = self.content[:] + + while content: + stmt = content.pop(0) + if isinstance(stmt, self.end_stmt_cls): + break + stmt.analyze() + assert isinstance(stmt, SubProgramStatement),`stmt.__class__.__name__` + if content: + self.show_message('Not analyzed content: %s' % content) + + parent_provides = self.parent.get_provides() + if parent_provides is not None: + if self.is_public(): + if parent_provides.has_key(self.name): + self.warning('interface name conflict with %s, overriding.' % (self.name)) + parent_provides[self.name] = self + + return # Subroutine @@ -369,6 +453,8 @@ class SubProgramStatement(BeginStatement, ProgramBlock, """ [ <prefix> ] <FUNCTION|SUBROUTINE> <name> [ ( <args> ) ] [ <suffix> ] """ + known_attributes = ['PUBLIC', 'PRIVATE'] + a = AttributeHolder(internal_subprogram = {}) def process_item(self): @@ -430,7 +516,7 @@ class SubProgramStatement(BeginStatement, ProgramBlock, content = self.content[:] if self.prefix: - self.a.attributes.extend(prefix.upper().split()) + self.update_attributes(prefix.upper().split()) variables = self.a.variables for a in self.args: @@ -459,6 +545,13 @@ class SubProgramStatement(BeginStatement, ProgramBlock, if content: self.show_message('Not analyzed content: %s' % content) + parent_provides = self.parent.get_provides() + if parent_provides is not None: + if self.is_public(): + if parent_provides.has_key(self.name): + self.warning('module subprogram name conflict with %s, overriding.' % (self.name)) + parent_provides[self.name] = self + return def topyf(self, tab=''): @@ -486,6 +579,7 @@ class Subroutine(SubProgramStatement): """ end_stmt_cls = EndSubroutine match = re.compile(r'(recursive|pure|elemental|\s)*subroutine\s*\w+', re.I).match + _repr_attr_names = ['prefix','bind','suffix','args'] + Statement._repr_attr_names # Function @@ -506,6 +600,7 @@ class Function(SubProgramStatement): """ end_stmt_cls = EndFunction match = re.compile(r'(recursive|pure|elemental|\s)*function\s*\w+', re.I).match + _repr_attr_names = ['prefix','bind','suffix','args','typedecl'] + Statement._repr_attr_names # Handle subprogram prefixes @@ -800,7 +895,11 @@ class Type(BeginStatement, HasVariables, HasAttributes): end_stmt_cls = EndType a = AttributeHolder(extends = None, - parameters = []) + parameters = {}, + component_names = [], # specifies component order for sequence types + components = {} + ) + known_attributes = re.compile(r'\A(PUBLIC|PRIVATE|SEQUENCE|ABSTRACT|BIND\s*\(.*\))\Z').match def process_item(self): line = self.item.get_line()[4:].lstrip() @@ -858,15 +957,41 @@ class Type(BeginStatement, HasVariables, HasAttributes): spec = 'BIND(%s)' % (', '.join(args)) else: spec = '%s(%s)' % (s,n) - self.warning('Unknown type-attr-spec %r' % (spec)) else: spec = spec.upper() - if spec not in ['PUBLIC', 'PRIVATE', 'ABSTRACT']: - self.warning('Unknown type-attr-spec %r' % (spec)) - self.a.attributes.append(spec) - self.a.parameters.extend(self.params) - assert isinstance(self.parent,HasTypeDecls),`self.parent.__class__` + self.update_attributes(spec) + + component_names = self.a.component_names + content = self.content[:] + while content: + stmt = content.pop(0) + if isinstance(stmt, self.end_stmt_cls): + break + stmt.analyze() + + if content: + self.show_message('Not analyzed content: %s' % content) + + parameters = self.a.parameters + components = self.a.components + component_names = self.a.component_names + for name in self.a.variable_names: + var = self.a.variables[name] + if name in self.params: + parameters[name] = var + else: + component_names.append(name) + components[name] = var + self.parent.a.type_decls[self.name] = self + + parent_provides = self.parent.get_provides() + if parent_provides is not None: + if self.is_public(): + if parent_provides.has_key(self.name): + self.warning('type declaration name conflict with %s, overriding.' % (self.name)) + parent_provides[self.name] = self + return def topyf(self, tab=''): diff --git a/numpy/f2py/lib/doc.txt b/numpy/f2py/lib/doc.txt new file mode 100644 index 000000000..159943224 --- /dev/null +++ b/numpy/f2py/lib/doc.txt @@ -0,0 +1,311 @@ + +Created: September 2006 +Author: Pearu Peterson <pearu.peterson@gmail.com> + +Structure +========= + +numpy.f2py.lib package contains the following files: + +readfortran.py +-------------- + +Tools for reading Fortran codes from file and string objects. + +To read Fortran code from a file, use FortranFileReader class. + +FortranFileReader class is iterator over Fortran code lines +as is derived from FortranReaderBase class. +It automatically handles line continuations and comments as +well as detects if Fortran file is in free or fixed format. + +For example, + +:: + >>> from readfortran import * + >>> import os + >>> reader = FortranFileReader(os.path.expanduser('~/src/blas/daxpy.f')) + >>> reader.next() + Line('subroutine daxpy(n,da,dx,incx,dy,incy)',(1, 1),'') + >>> reader.next() + Comment('c constant times a vector plus a vector.\nc uses unrolled loops for increments equal to one.\nc jack dongarra, linpack, 3/11/78.\nc modified 12/3/93, array(1) declarations changed to array(*)',(3, 6)) + >>> reader.next() + Line('double precision dx(*),dy(*),da',(8, 8),'') + >>> reader.next() + Line('integer i,incx,incy,ix,iy,m,mp1,n',(9, 9),'') + +FortranReaderBase.next() method may return Line, SyntaxErrorLine, Comment, MultiLine, +SyntaxErrorMultiLine instances. + +Line instance has the following attributes: + + * .line - contains Fortran code line + * .span - a 2-tuple containing the span of line numbers containing + Fortran code in the original Fortran file + * .label - the label of Fortran code line + * .reader - the FortranReaderBase class instance + * .strline - if not None then contains Fortran code line with parenthesis + content and string literal constants saved in .strlinemap dictionary. + * .is_f2py_directive - True if line started with f2py directive comment. + +and the following methods: + + * .get_line() - returns .strline (also evalutes it if None). Also + handles Hollerith contstants in fixed F77 mode. + * .isempty() - returns True if Fortran line contains no code. + * .copy(line=None, apply_map=False) - returns a Line instance + with given .span, .label, .reader information but line content + replaced with line (when not None) and applying .strlinemap + mapping (when apply_map is True). + * .apply_map(line) - apply .strlinemap mapping to line. + * .has_map() - returns True if .strlinemap mapping exists. + +For example, + +:: + + >>> item = reader.next() + >>> item + Line('if(n.le.0)return',(11, 11),'') + >>> item.line + 'if(n.le.0)return' + >>> item.strline + 'if(F2PY_EXPR_TUPLE_4)return' + >>> item.strlinemap + {'F2PY_EXPR_TUPLE_4': 'n.le.0'} + >>> item.label + '' + >>> item.span + (11, 11) + >>> item.get_line() + 'if(F2PY_EXPR_TUPLE_4)return' + >>> item.copy('if(F2PY_EXPR_TUPLE_4)pause',True) + Line('if(n.le.0)pause',(11, 11),'') + +Comment instance has the following attributes: + + * .comment - comment string + * .span - a 2-tuple containing the span of line numbers containing + Fortran comment in the original Fortran file + * .reader - the FortranReaderBase class instance + +and .isempty() method. + +MultiLine class represents multiline syntax in .pyf files:: + + <prefix>'''<lines>'''<suffix> + +MultiLine instance has the following attributes: + + * .prefix - the content of <prefix> + * .block - a list of lines + * .suffix - the content of <suffix> + * .span - a 2-tuple containing the span of line numbers containing + multiline syntax in the original Fortran file + * .reader - the FortranReaderBase class instance + +and .isempty() method. + +SyntaxErrorLine and SyntaxErrorMultiLine are like Line and MultiLine +classes, respectively, with a functionality of issuing an error +message to sys.stdout when constructing an instance of the corresponding +class. + +To read a Fortran code from a string, use FortranStringReader class:: + + reader = FortranStringReader(<string>, <isfree>, <isstrict>) + +where the second and third arguments are used to specify the format +of the given <string> content. When <isfree> and <isstrict> are both +True, the content of a .pyf file is assumed. For example, + +:: + + >>> code = """ + ... c comment + ... subroutine foo(a) + ... print*, "a=",a + ... end + ... """ + >>> reader = FortranStringReader(code, False, True) + >>> reader.next() + Comment('c comment',(2, 2)) + >>> reader.next() + Line('subroutine foo(a)',(3, 3),'') + >>> reader.next() + Line('print*, "a=",a',(4, 4),'') + >>> reader.next() + Line('end',(5, 5),'') + +FortranReaderBase has the following attributes: + + * .source - a file-like object with .next() method to retrive + a source code line + * .source_lines - a list of read source lines + * .reader - a FortranReaderBase instance for reading files + from INCLUDE statements. + * .include_dirs - a list of directories where INCLUDE files + are searched. Default is ['.']. + +and the following methods: + + * .set_mode(isfree, isstrict) - set Fortran code format information + * .close_source() - called when .next() raises StopIteration exception. + +parsefortran.py +--------------- + +Parse Fortran code from FortranReaderBase iterator. + +FortranParser class holds the parser information while +iterating over items returned by FortranReaderBase iterator. +The parsing information, collected when calling .parse() method, +is saved in .block attribute as an instance +of BeginSource class defined in block_statements.py file. + +For example, + +:: + + >>> reader = FortranStringReader(code, False, True) + >>> parser = FortranParser(reader) + >>> parser.parse() + >>> print parser.block + !BEGINSOURCE <cStringIO.StringI object at 0xb751d500> mode=fix77 + SUBROUTINE foo(a) + PRINT *, "a=", a + END SUBROUTINE foo + +block_statements.py, base_classes.py, typedecl_statements.py, statements.py +--------------------------------------------------------------------------- + +The model for representing Fortran code statements consists of a tree of Statement +classes defined in base_classes.py. There are two types of statements: one line +statements and block statements. Block statements consists of start and end +statements, and content statements in between that can be of both types again. + +Statement instance has the following attributes: + + * .parent - it is either parent block-type statement or FortranParser instance. + * .item - Line instance containing Fortran statement line information, see above. + * .isvalid - when False then processing this Statement instance will be skipped, + for example, when the content of .item does not match with + the Statement class. + * .ignore - when True then the Statement instance will be ignored. + * .modes - a list of Fortran format modes where the Statement instance is valid. + +and the following methods: + + * .info(message), .warning(message), .error(message) - to spit messages to + sys.stderr stream. + * .get_variable(name) - get Variable instance by name that is defined in + current namespace. If name is not defined, then the corresponding + Variable instance is created. + * .analyze() - calculate various information about the Statement, this information + is saved in .a attribute that is AttributeHolder instance. + +All statement classes are derived from Statement class. Block statements are +derived from BeginStatement class and is assumed to end with EndStatement +instance in .content attribute list. BeginStatement and EndStatement instances +have the following attributes: + + * .name - name of the block, blocks without names use line label + as the name. + * .blocktype - type of the block (derived from class name) + * .content - a list of Statement (or Line) instances. + +and the following methods: + + * .__str__() - returns string representation of Fortran code. + +A number of statements may declare a variable that is used in other +statement expressions. Variables are represented via Variable class +and its instances have the following attributes: + + * .name - name of the variable + * .typedecl - type declaration + * .dimension - list of dimensions + * .bounds - list of bounds + * .length - length specs + * .attributes - list of attributes + * .bind - list of bind information + * .intent - list of intent information + * .check - list of check expressions + * .init - initial value of the variable + * .parent - statement instance declaring the variable + * .parents - list of statements that specify variable information + +and the following methods: + + * .is_private() + * .is_public() + * .is_allocatable() + * .is_external() + * .is_intrinsic() + * .is_parameter() + * .is_optional() + * .is_required() + +The following type declaration statements are defined in typedecl_statements.py: + + Integer, Real, DoublePrecision, Complex, DoubleComplex, Logical, + Character, Byte, Type, Class + +and they have the following attributes: + + * .selector - contains lenght and kind specs + * .entity_decls, .attrspec + +and methods: + + * .tostr() - return string representation of Fortran type declaration + * .astypedecl() - pure type declaration instance, it has no .entity_decls + and .attrspec. + * .analyze() - processes .entity_decls and .attsspec attributes and adds + Variable instance to .parent.a.variables dictionary. + +The following block statements are defined in block_statements.py: + + BeginSource, Module, PythonModule, Program, BlockData, Interface, + Subroutine, Function, Select, Where, Forall, IfThen, If, Do, + Associate, TypeDecl (Type), Enum + +Block statement classes may have different properties which are declared via +deriving them from the following classes: + + HasImplicitStmt, HasUseStmt, HasVariables, HasTypeDecls, + HasAttributes, HasModuleProcedures, ProgramBlock + +In summary, .a attribute may hold different information sets as follows: + + BeginSource - .module, .external_subprogram, .blockdata + Module - .attributes, .implicit_rules, .use, .use_provides, .variables, + .type_decls, .module_subprogram, .module_data + PythonModule - .implicit_rules, .use, .use_provides + Program - .attributes, .implicit_rules, .use, .use_provides + BlockData - .implicit_rules, .use, .use_provides, .variables + Interface - .implicit_rules, .use, .use_provides, .module_procedures + Function, Subroutine - .implicit_rules, .attributes, .use, .use_statements, + .variables, .type_decls, .internal_subprogram + TypeDecl - .variables, .attributes + +Block statements have the following methods: + + * .get_classes() - returns a list of Statement classes that are valid + as a content of given block statement. + +The following one line statements are defined: + + Implicit, TypeDeclarationStatement derivatives (see above), + Assignment, PointerAssignment, Assign, Call, Goto, ComputedGoto, + AssignedGoto, Continue, Return, Stop, Print, Read, Write, Flush, + Wait, Contains, Allocate, Deallocate, ModuleProcedure, Access, + Public, Private, Close, Cycle, Backspace, Endfile, Reeinf, Open, + Format, Save, Data, Nullify, Use, Exit, Parameter, Equivalence, + Dimension, Target, Pointer, Protected, Volatile, Value, + ArithmeticIf, Intrinsic, Inquire, Sequence, External, Namelist, + Common, Optional, Intent, Entry, Import, Forall, + SpecificBinding, GenericBinding, FinalBinding, Allocatable, + Asynchronous, Bind, Else, ElseIf, Case, Where, ElseWhere, + Enumerator, FortranName, Threadsafe, Depend, Check, + CallStatement, CallProtoArgument, Pause diff --git a/numpy/f2py/lib/parsefortran.py b/numpy/f2py/lib/parsefortran.py index 280823a52..ac3df3068 100644 --- a/numpy/f2py/lib/parsefortran.py +++ b/numpy/f2py/lib/parsefortran.py @@ -10,6 +10,8 @@ Author: Pearu Peterson <pearu@cens.ioc.ee> Created: May 2006 """ +__all__ = ['FortranParser'] + import re import sys import traceback @@ -24,6 +26,10 @@ class FortranParser: cache = {} def __init__(self, reader): + """ + Parser of FortranReader structure. + Use .parse() method for parsing, parsing result is saved in .block attribute. + """ self.reader = reader if self.cache.has_key(reader.id): parser = self.cache[reader.id] @@ -156,7 +162,7 @@ def simple_main(): parser = FortranParser(reader) parser.parse() parser.analyze() - print parser.block.topyf() + print parser.block.torepr(4) #print parser.block def profile_main(): diff --git a/numpy/f2py/lib/python_wrapper.py b/numpy/f2py/lib/python_wrapper.py new file mode 100644 index 000000000..f31e6293b --- /dev/null +++ b/numpy/f2py/lib/python_wrapper.py @@ -0,0 +1,549 @@ + +__all__ = ['TypeWrapper'] + +import re +import os +import sys + +from block_statements import * +#from typedecl_statements import intrinsic_type_spec, Character +from utils import CHAR_BIT + +class WrapperBase: + + + def __init__(self): + self.srcdir = os.path.join(os.path.dirname(__file__),'src') + return + def warning(self, message): + print >> sys.stderr, message + def info(self, message): + print >> sys.stderr, message + + def get_resource_content(self, name, ext): + if name.startswith('pyobj_to_'): + body = self.generate_pyobj_to_ctype_c(name[9:]) + if body is not None: return body + generator_mth_name = 'generate_' + name + ext.replace('.','_') + generator_mth = getattr(self, generator_mth_name, lambda : None) + body = generator_mth() + if body is not None: + return body + fn = os.path.join(self.srcdir,name+ext) + if os.path.isfile(fn): + f = open(fn,'r') + body = f.read() + f.close() + return body + self.warning('No such file: %r' % (fn)) + return + + def get_dependencies(self, code): + l = [] + for uses in re.findall(r'(?<=depends:)([,\w\s.]+)', code, re.I): + for use in uses.split(','): + use = use.strip() + if not use: continue + l.append(use) + return l + + def apply_attributes(self, template): + """ + Apply instance attributes to template string. + + Replace rules for attributes: + _list - will be joined with newline + _clist - _list will be joined with comma + _elist - _list will be joined + ..+.. - attributes will be added + [..] - will be evaluated + """ + replace_names = set(re.findall(r'[ ]*%\(.*?\)s', template)) + d = {} + for name in replace_names: + tab = ' ' * (len(name)-len(name.lstrip())) + name = name.lstrip()[2:-2] + names = name.split('+') + joinsymbol = '\n' + attrs = None + for n in names: + realname = n.strip() + if n.endswith('_clist'): + joinsymbol = ', ' + realname = realname[:-6] + '_list' + elif n.endswith('_elist'): + joinsymbol = '' + realname = realname[:-6] + '_list' + if hasattr(self, realname): + attr = getattr(self, realname) + elif realname.startswith('['): + attr = eval(realname) + else: + self.warning('Undefined %r attribute: %r' % (self.__class__.__name__, realname)) + continue + if attrs is None: + attrs = attr + else: + attrs += attr + if isinstance(attrs, list): + attrs = joinsymbol.join(attrs) + d[name] = str(attrs).replace('\n','\n'+tab) + return template % d + +class PythonWrapperModule(WrapperBase): + + main_template = '''\ +#ifdef __cplusplus +extern \"C\" { +#endif +#include "Python.h" + +#define PY_ARRAY_UNIQUE_SYMBOL PyArray_API +#include "numpy/arrayobject.h" + +%(include_list)s +%(cppmacro_list)s +%(typedef_list)s +%(objdecl_list)s +%(extern_list)s +%(c_function_list)s +%(capi_function_list)s +static PyObject *f2py_module; +static PyMethodDef f2py_module_methods[] = { + %(module_method_list)s + {NULL,NULL,0,NULL} +}; +PyMODINIT_FUNC init%(modulename)s(void) { + f2py_module = Py_InitModule("%(modulename)s", f2py_module_methods); + %(initialize_interface_list)s + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_ImportError, "can\'t initialize module %(modulename)s"); + return; + } +} +#ifdef __cplusplus +} +#endif +''' + + main_fortran_template = '''\ +%(fortran_code_list)s +''' + def __init__(self, modulename): + WrapperBase.__init__(self) + self.modulename = modulename + self.include_list = [] + self.typedef_list = [] + self.cppmacro_list = [] + self.objdecl_list = [] + self.c_function_list = [] + self.extern_list = [] + self.capi_function_list = [] + self.module_method_list = [] + self.initialize_interface_list = [] + self.fortran_code_list = [] + + self.defined_types = [] + self.defined_macros = [] + self.defined_c_functions = [] + self.defined_typedefs = [] + return + + def add(self, block): + if isinstance(block, BeginSource): + for name, subblock in block.a.external_subprogram.items(): + self.add(subblock) + elif isinstance(block, (Subroutine, Function)): + self.info('Generating interface for %s' % (block.name)) + f = PythonCAPIFunction(self, block) + f.fill() + else: + raise NotImplementedError,`block.__class__.__name__` + return + + def c_code(self): + return self.apply_attributes(self.main_template) + def fortran_code(self): + return self.apply_attributes(self.main_fortran_template) + + def add_c_function(self, name): + if name not in self.defined_c_functions: + body = self.get_resource_content(name,'.c') + if body is None: + self.warning('Failed to get C function %r content.' % (name)) + return + for d in self.get_dependencies(body): + if d.endswith('.cpp'): + self.add_cppmacro(d[:-4]) + elif d.endswith('.c'): + self.add_c_function(d[:-2]) + else: + self.warning('Unknown dependence: %r.' % (d)) + self.defined_c_functions.append(name) + self.c_function_list.append(body) + return + + def add_cppmacro(self, name): + if name not in self.defined_macros: + body = self.get_resource_content(name,'.cpp') + if body is None: + self.warning('Failed to get CPP macro %r content.' % (name)) + return + for d in self.get_dependencies(body): + if d.endswith('.cpp'): + self.add_cppmacro(d[:-4]) + elif d.endswith('.c'): + self.add_c_function(d[:-2]) + else: + self.warning('Unknown dependence: %r.' % (d)) + self.defined_macros.append(name) + self.cppmacro_list.append(body) + return + + def add_type(self, typedecl): + typewrap = TypeDecl(self, typedecl) + typename = typewrap.typename + if typename not in self.defined_types: + self.defined_types.append(typename) + typewrap.fill() + return typename + + def add_typedef(self, name, code): + if name not in self.defined_typedefs: + self.typedef_list.append(code) + self.defined_types.append(name) + return + + def add_include(self, include): + if include not in self.include_list: + self.include_list.append(include) + return + + def add_subroutine(self, block): + f = PythonCAPIFunction(self, block) + f.fill() + return + + def generate_pyobj_to_ctype_c(self, ctype): + if ctype.startswith('npy_int'): + ctype_bits = int(ctype[7:]) + return ''' +/* depends: pyobj_to_long.c, pyobj_to_npy_longlong.c */ +#if NPY_BITSOF_LONG == %(ctype_bits)s +#define pyobj_to_%(ctype)s pyobj_to_long +#else +#if NPY_BITSOF_LONG > %(ctype_bits)s +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + long tmp; + if (pyobj_to_long(obj,&tmp)) { + *value = (%(ctype)s)tmp; + return 1; + } + return 0; +} +#else +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + npy_longlong tmp; + if (pyobj_to_npy_longlong(obj,&tmp)) { + *value = (%(ctype)s)tmp; + return 1; + } + return 0; +} +#endif +#endif +''' % (locals()) + elif ctype.startswith('npy_float'): + ctype_bits = int(ctype[9:]) + return ''' +/* depends: pyobj_to_double.c */ +#if NPY_BITSOF_DOUBLE == %(ctype_bits)s +#define pyobj_to_%(ctype)s pyobj_to_double +#else +#if NPY_BITSOF_DOUBLE > %(ctype_bits)s +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + double tmp; + if (pyobj_to_double(obj,&tmp)) { + *value = (%(ctype)s)tmp; + return 1; + } + return 0; +} +#else +#error, "NOTIMPLEMENTED pyobj_to_%(ctype)s" +#endif +#endif +''' % (locals()) + elif ctype.startswith('npy_complex'): + ctype_bits = int(ctype[11:]) + cfloat_bits = ctype_bits/2 + return ''' +/* depends: pyobj_to_Py_complex.c */ +#if NPY_BITSOF_DOUBLE >= %(cfloat_bits)s +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + Py_complex c; + if (pyobj_to_Py_complex(obj,&c)) { + (*value).real = (npy_float%(cfloat_bits)s)c.real; + (*value).imag = (npy_float%(cfloat_bits)s)c.imag; + return 1; + } + return 0; +} +#else +#error, "NOTIMPLEMENTED pyobj_to_%(ctype)s" +#endif +''' % (locals()) + elif ctype.startswith('f2py_string'): + ctype_bits = int(ctype[11:]) + ctype_bytes = ctype_bits / CHAR_BIT + self.add_typedef('f2py_string','typedef char * f2py_string;') + self.add_typedef(ctype,'typedef struct { char data[%s]; } %s;' % (ctype_bytes,ctype)) + self.add_include('#include <string.h>') + return ''' +/* depends: pyobj_to_string_len.c */ +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + return pyobj_to_string_len(obj, (f2py_string*)value, %(ctype_bytes)s); +} +''' % (locals()) + +class PythonCAPIFunction(WrapperBase): + capi_function_template = ''' +static char f2py_doc_%(function_name)s[] = "%(function_doc)s"; +static PyObject* f2py_%(function_name)s(PyObject *capi_self, PyObject *capi_args, PyObject *capi_keywds) { + PyObject * volatile capi_buildvalue = NULL; + volatile int f2py_success = 1; + %(decl_list)s + static char *capi_kwlist[] = {%(keyword_clist+optkw_clist+extrakw_clist+["NULL"])s}; + if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds, + "%(pyarg_format_elist)s", + %(["capi_kwlist"]+pyarg_obj_clist)s)) + return NULL; + %(frompyobj_list)s + %(call_list)s + f2py_success = !PyErr_Occurred(); + if (f2py_success) { + %(pyobjfrom_list)s + capi_buildvalue = Py_BuildValue(%(buildvalue_clist)s); + %(clean_pyobjfrom_list)s + } + %(clean_frompyobj_list)s + return capi_buildvalue; +} +''' + + pymethoddef_template = '''\ +{"%(function_name)s", (PyCFunction)f2py_%(function_name)s, METH_VARARGS | METH_KEYWORDS, f2py_doc_%(function_name)s},\ +''' + + cppmacro_template = '''\ +#define %(function_name)s_f F_FUNC(%(function_name)s,%(FUNCTION_NAME)s) +''' + + extdef_template = '''\ +extern void %(function_name)s_f();\ +''' + + def __init__(self, parent, block): + WrapperBase.__init__(self) + self.parent = parent + self.block = block + self.function_name = block.name + self.FUNCTION_NAME = self.function_name.upper() + self.function_doc = '' + self.args_list = block.args + self.decl_list = [] + self.keyword_list = [] + self.optkw_list = [] + self.extrakw_list = [] + self.frompyobj_list = [] + self.call_list = [] + self.pyobjfrom_list = [] + self.buildvalue_list = [] + self.clean_pyobjfrom_list = [] + self.clean_frompyobj_list = [] + self.pyarg_format_list = [] + self.pyarg_obj_list = [] + return + + def fill(self): + for argname in self.args_list: + var = self.block.a.variables[argname] + argwrap = ArgumentWrapper(self, var) + argwrap.fill() + self.call_list.append('%s_f(%s);' % (self.function_name, ', '.join(['&'+a for a in self.args_list]))) + if not self.buildvalue_list: + self.buildvalue_list.append('""') + self.parent.capi_function_list.append(self.apply_attributes(self.capi_function_template)) + self.parent.module_method_list.append(self.apply_attributes(self.pymethoddef_template)) + self.parent.extern_list.append(self.apply_attributes(self.extdef_template)) + self.parent.add_cppmacro('F_FUNC') + self.parent.cppmacro_list.append(self.apply_attributes(self.cppmacro_template)) + return + +class ArgumentWrapper(WrapperBase): + + objdecl_template = '%(ctype)s %(name)s;' + pyarg_obj_template = '\npyobj_to_%(ctype)s, &%(name)s' + + def __init__(self, parent, variable): + WrapperBase.__init__(self) + self.parent = parent + self.grand_parent = parent.parent + self.variable = variable + self.typedecl = variable.typedecl + self.name = variable.name + self.ctype = self.typedecl.get_c_type() + + def fill(self): + typename = self.grand_parent.add_type(self.typedecl) + self.parent.decl_list.append(self.apply_attributes(self.objdecl_template)) + + self.parent.pyarg_obj_list.append(self.apply_attributes(self.pyarg_obj_template)) + self.parent.pyarg_format_list.append('O&') + self.parent.keyword_list.append('"%s"' % (self.name)) + + self.grand_parent.add_c_function('pyobj_to_%s' % (self.ctype)) + return + +class TypeDecl(WrapperBase): + cppmacro_template = '''\ +#define initialize_%(typename)s_interface F_FUNC(initialize_%(typename)s_interface_f,INITIALIZE_%(TYPENAME)s_INTERFACE_F)\ +''' + typedef_template = '''\ +typedef struct { char data[%(byte_size)s] } %(ctype)s; +typedef %(ctype)s (*create_%(typename)s_functype)(void); +typedef void (*initialize_%(typename)s_interface_functype)(create_%(typename)s_functype);\ +''' + objdecl_template = '''\ +static create_%(typename)s_functype create_%(typename)s_object; +''' + funcdef_template = '''\ +static void initialize_%(typename)s_interface_c(create_%(typename)s_functype create_object_f) { + create_%(typename)s_object = create_object_f; +} +''' + extdef_template = '''\ +extern void initialize_%(typename)s_interface(initialize_%(typename)s_interface_functype);\ +''' + initcall_template = '''\ +initialize_%(typename)s_interface(initialize_%(typename)s_interface_c);\ +''' + fortran_code_template = '''\ + function create_%(typename)s_object_f() result (obj) + %(typedecl)s obj +! %(initexpr)s + end + subroutine initialize_%(typename)s_interface_f(init_c) + external create_%(typename)s_object_f + call init_c(create_%(typename)s_object_f) + end +''' + + def __init__(self, parent, typedecl): + WrapperBase.__init__(self) + self.parent = parent + self.typedecl = typedecl.astypedecl() + self.ctype = self.typedecl.get_c_type() + self.byte_size = self.typedecl.get_byte_size() + self.typename = self.typedecl.name.lower() + self.TYPENAME = self.typedecl.name.upper() + self.initexpr = self.typedecl.assign_expression('obj',self.typedecl.get_zero_value()) + return + + def fill(self): + ctype =self.typedecl.get_c_type() + if ctype.startswith('npy_'): + pass + elif ctype.startswith('f2py_string'): + pass + else: + self.parent.typedef_list.append(self.apply_attributes(self.typedef_template)) + self.parent.objdecl_list.append(self.apply_attributes(self.objdecl_template)) + self.parent.c_function_list.append(self.apply_attributes(self.funcdef_template)) + self.parent.extern_list.append(self.apply_attributes(self.extdef_template)) + self.parent.initialize_interface_list.append(self.apply_attributes(self.initcall_template)) + self.parent.fortran_code_list.append(self.apply_attributes(self.fortran_code_template)) + self.parent.add_cppmacro('F_FUNC') + self.parent.cppmacro_list.append(self.apply_attributes(self.cppmacro_template)) + return + + + + +if __name__ == '__main__': + from utils import str2stmt, get_char_bit + + stmt = str2stmt(""" + module rat + integer :: i + type rational + integer n + integer*8 d + end type rational + end module rat + subroutine foo(a) + use rat + type(rational) a + end + """) + #stmt = stmt.content[-1].content[1] + #print stmt + #wrapgen = TypeWrapper(stmt) + #print wrapgen.fortran_code() + #print wrapgen.c_code() + + foo_code = """! -*- f90 -*- + module rat + type rational + integer d,n + end type rational + end module rat + subroutine foo(a,b) + use rat + integer a + character*5 b + type(rational) c + print*,'a=',a,b,c + end +""" + + wm = PythonWrapperModule('foo') + wm.add(str2stmt(foo_code)) + #wm.add_fortran_code(foo_code) + #wm.add_subroutine(str2stmt(foo_code)) + #print wm.c_code() + + c_code = wm.c_code() + f_code = wm.fortran_code() + + f = open('foomodule.c','w') + f.write(c_code) + f.close() + f = open('foo.f','w') + f.write(foo_code) + f.close() + f = open('foo_wrap.f','w') + f.write(f_code) + f.close() + f = open('foo_setup.py','w') + f.write('''\ +def configuration(parent_package='',top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration('foopack',parent_package,top_path) + config.add_library('foolib', + sources = ['foo.f','foo_wrap.f']) + config.add_extension('foo', + sources=['foomodule.c'], + libraries = ['foolib'], + ) + return config +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(configuration=configuration) +''') + f.close() + print get_char_bit() + os.system('python foo_setup.py config_fc --fcompiler=gnu95 build build_ext --inplace') + import foo + print dir(foo) + foo.foo(2,"abcdefg") diff --git a/numpy/f2py/lib/research/pointers/pointer_size.c b/numpy/f2py/lib/research/pointers/pointer_size.c new file mode 100644 index 000000000..f3d97dfc4 --- /dev/null +++ b/numpy/f2py/lib/research/pointers/pointer_size.c @@ -0,0 +1,30 @@ +#include <stdio.h> + +#define settypeinfo settypeinfo_ + +void settypeinfo(int*); + +int main(int argc, char* argv[]) +{ + int i; + int a[512]; + for(i=0;i<512;i++) a[i] = 0; + + settypeinfo(a); + + if (a[0] != 333331) { + printf("FAILED, start flag is incorrect = %d\n", a[0]); + return 1; + } + + for (i = 0; i < 512; i++) { + if (a[i] == 333332) { + printf("SUCCESSFULLY found Fortran pointer length of %d bytes\n", + (i-1)*sizeof(int)); + return 0; + } + } + + printf("FAILED to find end flag\n"); + return 1; +} diff --git a/numpy/f2py/lib/research/pointers/settypeinfo.f90 b/numpy/f2py/lib/research/pointers/settypeinfo.f90 new file mode 100644 index 000000000..9b375863b --- /dev/null +++ b/numpy/f2py/lib/research/pointers/settypeinfo.f90 @@ -0,0 +1,13 @@ +! Author: Pearu Peterson +! Got idea from https://www.cca-forum.org/pipermail/cca-fortran/2003-February/000123.html + +subroutine settypeinfo(wrapper) + type data_wrapper + integer :: ibegin + character*20 :: p_data*10 + integer :: iend + end type data_wrapper + type(data_wrapper), intent(out) :: wrapper + wrapper%ibegin = 333331 + wrapper%iend = 333332 +end subroutine settypeinfo diff --git a/numpy/f2py/lib/src/F_FUNC.cpp b/numpy/f2py/lib/src/F_FUNC.cpp new file mode 100644 index 000000000..edaa98064 --- /dev/null +++ b/numpy/f2py/lib/src/F_FUNC.cpp @@ -0,0 +1,34 @@ +#if defined(PREPEND_FORTRAN) +#if defined(NO_APPEND_FORTRAN) +#if defined(UPPERCASE_FORTRAN) +#define F_FUNC(f,F) _##F +#else +#define F_FUNC(f,F) _##f +#endif +#else +#if defined(UPPERCASE_FORTRAN) +#define F_FUNC(f,F) _##F##_ +#else +#define F_FUNC(f,F) _##f##_ +#endif +#endif +#else +#if defined(NO_APPEND_FORTRAN) +#if defined(UPPERCASE_FORTRAN) +#define F_FUNC(f,F) F +#else +#define F_FUNC(f,F) f +#endif +#else +#if defined(UPPERCASE_FORTRAN) +#define F_FUNC(f,F) F##_ +#else +#define F_FUNC(f,F) f##_ +#endif +#endif +#endif +#if defined(UNDERSCORE_G77) +#define F_FUNC_US(f,F) F_FUNC(f##_,F##_) +#else +#define F_FUNC_US(f,F) F_FUNC(f,F) +#endif diff --git a/numpy/f2py/lib/src/pyobj_to_Py_complex.c b/numpy/f2py/lib/src/pyobj_to_Py_complex.c new file mode 100644 index 000000000..036c79904 --- /dev/null +++ b/numpy/f2py/lib/src/pyobj_to_Py_complex.c @@ -0,0 +1,39 @@ +int pyobj_to_Py_complex(PyObject* obj, Py_complex* value) { + if (PyComplex_Check(obj)) { + *value =PyComplex_AsCComplex(obj); + return 1; + } + /* Python does not provide PyNumber_Complex function :-( */ + (*value).imag=0.0; + if (PyFloat_Check(obj)) { +#ifdef __sgi + (*value).real = PyFloat_AsDouble(obj); + return (!PyErr_Occurred()); +#else + (*value).real = PyFloat_AS_DOUBLE(obj); + return 1; +#endif + } + if (PyInt_Check(obj)) { + (*value).real = (double)PyInt_AS_LONG(obj); + return 1; + } + if (PyLong_Check(obj)) { + (*value).real = PyLong_AsDouble(obj); + return (!PyErr_Occurred()); + } + if (PySequence_Check(obj) && (!PyString_Check(obj))) { + PyObject *tmp = PySequence_GetItem(obj,0); + if (tmp) { + if (pyobj_to_Py_complex(tmp,value)) { + Py_DECREF(tmp); + return 1; + } + Py_DECREF(tmp); + } + } + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Failed to convert python object to C Py_complex."); + } + return 0; +} diff --git a/numpy/f2py/lib/src/pyobj_to_double.c b/numpy/f2py/lib/src/pyobj_to_double.c new file mode 100644 index 000000000..aad68f0c9 --- /dev/null +++ b/numpy/f2py/lib/src/pyobj_to_double.c @@ -0,0 +1,39 @@ +int pyobj_to_double(PyObject* obj, double* value) { + PyObject* tmp = NULL; + if (PyFloat_Check(obj)) { +#ifdef __sgi + *value = PyFloat_AsDouble(obj); + return (!PyErr_Occurred()); +#else + *value = PyFloat_AS_DOUBLE(obj); + return 1; +#endif + } + tmp = PyNumber_Float(obj); + if (tmp) { +#ifdef __sgi + *value = PyFloat_AsDouble(tmp); + Py_DECREF(tmp); + return (!PyErr_Occurred()); +#else + *value = PyFloat_AS_DOUBLE(tmp); + Py_DECREF(tmp); + return 1; +#endif + } + if (PyComplex_Check(obj)) + tmp = PyObject_GetAttrString(obj,"real"); + else if (PyString_Check(obj)) + /*pass*/; + else if (PySequence_Check(obj)) + tmp = PySequence_GetItem(obj,0); + if (tmp) { + PyErr_Clear(); + if (pyobj_to_double(tmp, value)) {Py_DECREF(tmp); return 1;} + Py_DECREF(tmp); + } + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Failed to convert python object to C double."); + } + return 0; +} diff --git a/numpy/f2py/lib/src/pyobj_to_long.c b/numpy/f2py/lib/src/pyobj_to_long.c new file mode 100644 index 000000000..2d80ee3c0 --- /dev/null +++ b/numpy/f2py/lib/src/pyobj_to_long.c @@ -0,0 +1,31 @@ +int pyobj_to_long(PyObject *obj, long* value) { + PyObject* tmp = NULL; + if (PyInt_Check(obj)) { + *value = PyInt_AS_LONG(obj); + return 1; + } + tmp = PyNumber_Int(obj); + if (tmp) { + *value = PyInt_AS_LONG(tmp); + Py_DECREF(tmp); + return 1; + } + if (PyComplex_Check(obj)) + tmp = PyObject_GetAttrString(obj,"real"); + else if (PyString_Check(obj)) + /*pass*/; + else if (PySequence_Check(obj)) + tmp = PySequence_GetItem(obj,0); + if (tmp) { + PyErr_Clear(); + if (pyobj_to_long(tmp, value)) { + Py_DECREF(tmp); + return 1; + } + Py_DECREF(tmp); + } + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Failed to convert python object to C long."); + } + return 0; +} diff --git a/numpy/f2py/lib/src/pyobj_to_npy_longlong.c b/numpy/f2py/lib/src/pyobj_to_npy_longlong.c new file mode 100644 index 000000000..33b23c060 --- /dev/null +++ b/numpy/f2py/lib/src/pyobj_to_npy_longlong.c @@ -0,0 +1,36 @@ +int pyobj_to_npy_longlong(PyObject *obj, npy_longlong* value) { + PyObject* tmp = NULL; + if (PyLong_Check(obj)) { + *value = PyLong_AsLongLong(obj); + return (!PyErr_Occurred()); + } + if (PyInt_Check(obj)) { + *value = (npy_longlong)PyInt_AS_LONG(obj); + return 1; + } + tmp = PyNumber_Long(obj); + if (tmp) { + *value = PyLong_AsLongLong(tmp); + Py_DECREF(tmp); + return (!PyErr_Occurred()); + } + if (PyComplex_Check(obj)) + tmp = PyObject_GetAttrString(obj,"real"); + else if (PyString_Check(obj)) + /*pass*/; + else if (PySequence_Check(obj)) + tmp = PySequence_GetItem(obj,0); + if (tmp) { + PyErr_Clear(); + if (pyobj_to_npy_longlong(tmp, value)) { + Py_DECREF(tmp); + return 1; + } + Py_DECREF(tmp); + } + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Failed to convert python object to C npy_longlong."); + } + return 0; +} + diff --git a/numpy/f2py/lib/src/pyobj_to_string_len.c b/numpy/f2py/lib/src/pyobj_to_string_len.c new file mode 100644 index 000000000..754b7af34 --- /dev/null +++ b/numpy/f2py/lib/src/pyobj_to_string_len.c @@ -0,0 +1,10 @@ +int pyobj_to_string_len(PyObject* obj, f2py_string* value, size_t length) { + if (PyString_Check(obj)) { + if (strncpy((char*)value,PyString_AS_STRING(obj), length)) + return 1; + } + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Failed to convert python object to C f2py_string."); + } + return 0; +} diff --git a/numpy/f2py/lib/statements.py b/numpy/f2py/lib/statements.py index 32e40d5b2..2ca399bb1 100644 --- a/numpy/f2py/lib/statements.py +++ b/numpy/f2py/lib/statements.py @@ -52,6 +52,7 @@ class GeneralAssignment(Statement): match = re.compile(r'\w[^=]*\s*=\>?').match item_re = re.compile(r'(?P<variable>\w[^=]*)\s*(?P<sign>=\>?)\s*(?P<expr>.*)\Z',re.I).match + _repr_attr_names = ['variable','sign','expr'] + Statement._repr_attr_names def process_item(self): m = self.item_re(self.item.get_line()) @@ -525,6 +526,7 @@ class ModuleProcedure(Statement): def analyze(self): module_procedures = self.parent.a.module_procedures module_procedures.extend(self.items) + # XXX: add names to parent_provides return class Access(Statement): @@ -560,7 +562,7 @@ class Access(Statement): var = self.get_variable(name) var.update(clsname) else: - self.parent.a.attributes.append(clsname) + self.parent.update_attributes(clsname) return class Public(Access): @@ -847,8 +849,11 @@ class Use(Statement): return tab + s def analyze(self): - modules = self.top.a.module + use = self.parent.a.use + if use.has_key(self.name): + return + modules = self.top.a.module if not modules.has_key(self.name): fn = None for d in self.reader.include_dirs: @@ -867,9 +872,10 @@ class Use(Statement): modules.update(parser.block.a.module) if not modules.has_key(self.name): - self.warning('no information about the use module %r' % (self.name)) + self.warning('no information about the module %r in use statement' % (self.name)) return + module = modules[self.name] use_provides = self.parent.a.use_provides @@ -1111,7 +1117,7 @@ class Sequence(Statement): return def __str__(self): return self.get_indent_tab() + 'SEQUENCE' def analyze(self): - self.parent.a.attributes.append('SEQUENCE') + self.parent.update_attributes('SEQUENCE') return class External(StatementWithNamelist): @@ -1218,6 +1224,7 @@ class Common(Statement): var = self.get_variable(name) if shape is not None: var.set_bounds(shape) + # XXX: add name,var to parent_provides return class Optional(StatementWithNamelist): diff --git a/numpy/f2py/lib/typedecl_statements.py b/numpy/f2py/lib/typedecl_statements.py index 492c094aa..6850cbb97 100644 --- a/numpy/f2py/lib/typedecl_statements.py +++ b/numpy/f2py/lib/typedecl_statements.py @@ -1,9 +1,14 @@ +__all__ = ['Integer', 'Real', 'DoublePrecision', 'Complex', 'DoubleComplex', + 'Character', 'Logical', 'Byte', 'TypeStmt','Class', + 'intrinsic_type_spec', 'declaration_type_spec', + 'Implicit'] + import re import string from base_classes import Statement, BeginStatement, EndStatement,\ AttributeHolder, Variable -from utils import split_comma, AnalyzeError, name_re, is_entity_decl +from utils import split_comma, AnalyzeError, name_re, is_entity_decl, is_name, CHAR_BIT # Intrinsic type specification statements @@ -27,6 +32,10 @@ class TypeDeclarationStatement(Statement): | LOGICAL [<kind-selector>] <kind-selector> = ( [ KIND = ] <scalar-int-initialization-expr> ) + EXTENSION: + <kind-selector> = ( [ KIND = ] <scalar-int-initialization-expr> ) + | * <length> + <char-selector> = <length-selector> | ( LEN = <type-param-value>, KIND = <scalar-int-initialization-expr> ) | ( <type-param-value>, [ KIND = ] <scalar-int-initialization-expr> ) @@ -61,6 +70,7 @@ class TypeDeclarationStatement(Statement): <digit-string> = <digit> [ <digit> ].. <kind-param> = <digit-string> | <scalar-int-constant-name> """ + _repr_attr_names = ['selector','attrspec','entity_decls'] + Statement._repr_attr_names def process_item(self): item = self.item @@ -146,13 +156,19 @@ class TypeDeclarationStatement(Statement): assert self.parent.typedecl is None,`self.parent.typedecl` self.parent.typedecl = self self.ignore = True + if isinstance(self, Type): + self.name = self.selector[1].lower() + assert is_name(self.name),`self.name` + else: + self.name = clsname return def _parse_kind_selector(self, selector): if not selector: - return '' + return '','' + length,kind = '','' if selector.startswith('*'): - kind = selector[1:].lstrip() + length = selector[1:].lstrip() else: assert selector[0]+selector[-1]=='()',`selector` l = selector[1:-1].strip() @@ -162,7 +178,7 @@ class TypeDeclarationStatement(Statement): kind = l[1:].lstrip() else: kind = l - return kind + return length,kind def _parse_char_selector(self, selector): if not selector: @@ -213,8 +229,8 @@ class TypeDeclarationStatement(Statement): def tostr(self): clsname = self.__class__.__name__.upper() s = '' + length, kind = self.selector if isinstance(self, Character): - length, kind = self.selector if length and kind: s += '(LEN=%s, KIND=%s)' % (length,kind) elif length: @@ -222,9 +238,14 @@ class TypeDeclarationStatement(Statement): elif kind: s += '(KIND=%s)' % (kind) else: - kind = self.selector - if kind: - s += '(KIND=%s)' % (kind) + if isinstance(self, Type): + s += '(%s)' % (kind) + else: + if length: + s += '*%s' % (length) + if kind: + s += '(KIND=%s)' % (kind) + return clsname + s def __str__(self): @@ -244,7 +265,9 @@ class TypeDeclarationStatement(Statement): return self.selector==other.selector def astypedecl(self): - return self.__class__(self.parent, self.item.copy(self.tostr())) + if self.entity_decls or self.attrspec: + return self.__class__(self.parent, self.item.copy(self.tostr())) + return self def analyze(self): if not self.entity_decls: @@ -253,11 +276,8 @@ class TypeDeclarationStatement(Statement): typedecl = self.astypedecl() for item in self.entity_decls: name, array_spec, char_length, value = self._parse_entity(item) - if not variables.has_key(name): - variables[name] = var = Variable(self, name) - else: - var = variables[name] - var.add_parent(self) + var = self.parent.get_variable(name) + var.add_parent(self) if char_length: var.set_length(char_length) else: @@ -267,6 +287,7 @@ class TypeDeclarationStatement(Statement): var.set_bounds(array_spec) if value: var.set_init(value) + var.analyze() return def _parse_entity(self, line): @@ -296,34 +317,170 @@ class TypeDeclarationStatement(Statement): value = item.apply_map(line[1:].lstrip()) return name, array_spec, char_length, value + def get_zero_value(self): + raise NotImplementedError,`self.__class__.__name__` + + def assign_expression(self, name, value): + return '%s = %s' % (name, value) + + def get_kind(self): + return self.selector[1] or self.default_kind + + def get_length(self): + return self.selector[0] or 1 + + def get_bit_size(self): + return CHAR_BIT * int(self.get_kind()) + + def get_byte_size(self): + return self.get_bit_size() / CHAR_BIT + + def is_intrinsic(self): return not isinstance(self,(Type,Class)) + def is_derived(self): return isinstance(self,Type) + + def is_numeric(self): return isinstance(self,(Integer,Real, DoublePrecision,Complex,DoubleComplex,Byte)) + def is_nonnumeric(self): return isinstance(self,(Character,Logical)) + + class Integer(TypeDeclarationStatement): match = re.compile(r'integer\b',re.I).match + default_kind = 4 + + def get_c_type(self): + return 'npy_int%s' % (self.get_bit_size()) + + def get_zero_value(self): + kind = self.get_kind() + if kind==self.default_kind: return '0' + return '0_%s' % (kind) class Real(TypeDeclarationStatement): match = re.compile(r'real\b',re.I).match + default_kind = 4 + + def get_c_type(self): + return 'npy_float%s' % (self.get_bit_size()) + + def get_zero_value(self): + kind = self.get_kind() + if kind==self.default_kind: return '0.0' + return '0_%s' % (kind) class DoublePrecision(TypeDeclarationStatement): match = re.compile(r'double\s*precision\b',re.I).match + default_kind = 8 + + def get_zero_value(self): + return '0.0D0' + + def get_c_type(self): + return 'npy_float%s' % (self.get_bit_size()) class Complex(TypeDeclarationStatement): match = re.compile(r'complex\b',re.I).match + default_kind = 4 + + def get_kind(self): + length, kind = self.selector + if kind: + return kind + if length: + return int(length)/2 + return self.default_kind + + def get_length(self): + return 2 * int(self.get_kind()) + + def get_bit_size(self): + return CHAR_BIT * self.get_length() + + def get_zero_value(self): + kind = self.get_kind() + if kind==self.default_kind: return '(0.0, 0.0)' + return '(0.0_%s, 0.0_%s)' % (kind, kind) + + def get_c_type(self): + return 'npy_complex%s' % (self.get_bit_size()) class DoubleComplex(TypeDeclarationStatement): # not in standard match = re.compile(r'double\s*complex\b',re.I).match + default_kind = 8 + + def get_kind(self): return self.default_kind + def get_length(self): return 2 * self.get_kind() + def get_bit_size(self): + return CHAR_BIT * self.get_length() + + def get_zero_value(self): + return '(0.0D0,0.0D0)' + + def get_c_type(self): + return 'npy_complex%s' % (self.get_bit_size()) class Logical(TypeDeclarationStatement): match = re.compile(r'logical\b',re.I).match + default_kind = 4 + + def get_zero_value(self): + return ".FALSE." + + def get_c_type(self): + return 'npy_int%s' % (self.get_bit_size()) class Character(TypeDeclarationStatement): match = re.compile(r'character\b',re.I).match + default_kind = 1 + + def get_bit_size(self): + return CHAR_BIT * int(self.get_length()) * int(self.get_kind()) + + def get_c_type(self): + return 'f2py_string%s' % (self.get_bit_size()) + + def get_zero_value(self): + return "''" class Byte(TypeDeclarationStatement): # not in standard match = re.compile(r'byte\b',re.I).match + default_kind = 1 + + def get_zero_value(self): + return '0' + + def get_c_type(self): + return 'npy_int%s' % (self.get_bit_size()) class Type(TypeDeclarationStatement): match = re.compile(r'type\s*\(', re.I).match + + def get_zero_value(self): + kind = self.selector + assert is_name(kind),`kind` + type_decl = self.get_type_decl(kind) + component_names = type_decl.a.component_names + components = type_decl.a.components + l = [] + for name in component_names: + var = components[name] + l.append(var.typedecl.get_zero_value()) + return '%s(%s)' % (type_decl.name, ', '.join(l)) + + def get_kind(self): + # See 4.5.2, page 48 + raise NotImplementedError,`self.__class__.__name__` + + def get_bit_size(self): + type_decl = self.get_type_decl(self.name) + s = 0 + for name,var in type_decl.a.components.items(): + s += var.get_bit_size() + return s + + def get_c_type(self): + return 'f2py_type_%s_%s' % (self.name, self.get_bit_size()) + TypeStmt = Type class Class(TypeDeclarationStatement): diff --git a/numpy/f2py/lib/utils.py b/numpy/f2py/lib/utils.py index 7a194c9c7..bdd838596 100644 --- a/numpy/f2py/lib/utils.py +++ b/numpy/f2py/lib/utils.py @@ -74,6 +74,8 @@ def parse_result(line, item = None): return name, line[i+1:].lstrip() def filter_stmts(content, classes): + """ Pop and return classes instances from content. + """ stmts = [] indices = [] for i in range(len(content)): @@ -123,3 +125,30 @@ def get_module_file(name, directory, _cache={}): return fn f.close() return + +def str2stmt(string, isfree=True, isstrict=False): + """ Convert Fortran code to Statement tree. + """ + from readfortran import Line, FortranStringReader + from parsefortran import FortranParser + reader = FortranStringReader(string, isfree, isstrict) + parser = FortranParser(reader) + parser.parse() + parser.analyze() + block = parser.block + while len(block.content)==1: + block = block.content[0] + return block + +def get_char_bit(): + import numpy + one = numpy.ubyte(1) + two = numpy.ubyte(2) + n = numpy.ubyte(2) + i = 1 + while n>=two: + n <<= one + i += 1 + return i + +CHAR_BIT = get_char_bit() |