[Numpy-svn] r3373 - trunk/numpy/f2py/lib/parser

numpy-svn at scipy.org numpy-svn at scipy.org
Fri Oct 20 01:43:44 CDT 2006


Author: pearu
Date: 2006-10-20 01:43:31 -0500 (Fri, 20 Oct 2006)
New Revision: 3373

Added:
   trunk/numpy/f2py/lib/parser/expressions.py
   trunk/numpy/f2py/lib/parser/test_expressions.py
Modified:
   trunk/numpy/f2py/lib/parser/base_classes.py
   trunk/numpy/f2py/lib/parser/typedecl_statements.py
   trunk/numpy/f2py/lib/parser/utils.py
Log:
F2PY G3: started impl array support and expression parser.

Modified: trunk/numpy/f2py/lib/parser/base_classes.py
===================================================================
--- trunk/numpy/f2py/lib/parser/base_classes.py	2006-10-19 22:55:23 UTC (rev 3372)
+++ trunk/numpy/f2py/lib/parser/base_classes.py	2006-10-20 06:43:31 UTC (rev 3373)
@@ -120,6 +120,11 @@
         self.bind = []
         self.check = []
         self.init = None
+
+        # after calling analyze the following additional attributes are set:
+        # .is_array:
+        #    rank
+        #    shape
         return
 
     def __repr__(self):
@@ -339,7 +344,7 @@
             s += ', ' + ', '.join(a) + ' :: '
         s += self.name
         if self.bounds:
-            s += '(%s)' % (', '.join(self.bounds))
+            s += '(%s)' % (', '.join([':'.join(spec) for spec in self.bounds]))
         if self.length:
             if is_int_literal_constant(self.length):
                 s += '*%s' % (self.length)
@@ -349,8 +354,59 @@
             s += ' = ' + self.init
         return s
 
+    def get_array_spec(self):
+        assert self.is_array(),'array_spec is available only for arrays'
+        if self.bounds:
+            if self.dimension:
+                self.parent.warning('both bounds=%r and dimension=%r are defined, ignoring dimension.' % (self.bounds, self.dimension))
+            array_spec = self.bounds
+        else:
+            array_spec = self.dimension
+        return array_spec
+
+    def is_deferred_shape_array(self):
+        if not self.is_array(): return False
+        return self.is_allocatable() or self.is_pointer():
+
+    def is_assumed_size_array(self):
+        if not self.is_array(): return False
+        return self.get_array_spec()[-1][-1]=='*'
+
+    def is_assumed_shape_array(self):
+        if not self.is_array(): return False
+        if self.is_deferred_shape_array(): return False
+        for spec in self.get_array_spec():
+            if not spec[-1]: return True
+        return False
+
+    def is_explicit_shape_array(self):
+        if not self.is_array(): return False
+        if self.is_deferred_shape_array(): return False
+        for spec in self.get_array_spec():
+            if not spec[-1] or spec[-1] == '*': return False
+        return True
+
+    def is_allocatable_array(self):
+        return self.is_array() and self.is_allocatable()
+
+    def is_array_pointer(self):
+        return self.is_array() and self.is_pointer()
+    
     def analyze(self):
         typedecl = self.get_typedecl()
+        if self.is_array():
+            array_spec = self.get_array_spec()
+            self.rank = len(array_spec)
+            if self.is_deferred_shape_array(): # a(:,:)
+                pass
+            elif self.is_explict_shape_array(self):
+                shape = []
+                for spec in array_spec:
+                    if len(spec)==1:
+                        shape.append(spec[0])
+                    else:
+                        shape.append(spec[1]-spec[0])
+                self.shape = shape  
         return
 
 class ProgramBlock:

Added: trunk/numpy/f2py/lib/parser/expressions.py
===================================================================
--- trunk/numpy/f2py/lib/parser/expressions.py	2006-10-19 22:55:23 UTC (rev 3372)
+++ trunk/numpy/f2py/lib/parser/expressions.py	2006-10-20 06:43:31 UTC (rev 3373)
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+"""
+
+Copyright 2006 Pearu Peterson all rights reserved,
+Pearu Peterson <pearu at cens.ioc.ee>
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License. See http://scipy.org.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+
+$Date: $
+Pearu Peterson
+"""
+
+import re
+
+class DefinedOp:
+    def __new__(cls, letters):
+        if not letters: return None
+        obj = object.__new__(cls)
+        obj._init(letters)
+        return obj
+
+    def _init(self, letters):
+        self.letters = letters.upper()
+        return
+
+    def __str__(self): return '.%s.' % (self.letters)
+    def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.letters)
+
+def set_subclasses(cls):
+    for basecls in cls.__bases__:
+        if issubclass(basecls, Base):
+            try:
+                subclasses = basecls.__dict__['_subclasses']
+            except KeyError:
+                subclasses = basecls._subclasses = []
+            subclasses.append(cls)
+    return
+
+is_name = re.compile(r'\A[a-z]\w*\Z',re.I).match
+
+class Expression:
+    def __new__(cls, string):
+        if is_name(string):
+            obj = object.__new__(Name)
+            obj._init(string)
+            return obj
+
+class NoMatch(Exception):
+    pass
+
+class Base(object):
+    def __new__(cls, string):
+        match = getattr(cls,'match',None)
+        if match is not None:
+            if match(string):
+                obj = object.__new__(cls)
+                obj._init(string)
+                return obj
+        else:
+            assert cls._subclasses,`cls`
+            for c in cls._subclasses:
+                try:
+                    return c(string)
+                except NoMatch, msg:
+                    pass
+        raise NoMatch,'%s: %r' % (cls.__name__, string)
+    def _init(self, string):
+        self.string = string
+        return
+    def __str__(self): return self.string
+    def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.string)
+
+class Primary(Base):
+    """
+    <primary> = <constant>
+                | <designator>
+                | <array-constructor>
+                | <structure-constructor>
+                | <function-reference>
+                | <type-param-inquiry>
+                | <type-param-name>
+                | ( <expr> )
+    <type-param-inquiry> = <designator> % <type-param-name>
+    """
+
+class Constant(Primary):
+    """
+    <constant> = <literal-constant>
+                 | <named-constant>
+    """
+
+class Designator(Primary):
+    """
+    <designator> = <object-name>
+                   | <array-element>
+                   | <array-section>
+                   | <structure-component>
+                   | <substring>
+    <array-element> = <data-ref>
+    <array-section> = <data-ref> [ ( <substring-range> ) ]
+    <data-ref> = <part-ref> [ % <part-ref> ]...
+    <part-ref> = <part-name> [ ( <section-subscript-list> ) ]
+    <substring> = <parent-string> ( <substring-range> )
+    <parent-string> = <scalar-variable-name>
+                      | <array-element>
+                      | <scalar-structure-component>
+                      | <scalar-constant>
+    <substring-range> = [ <scalar-int-expr> ] : [ <scalar-int-expr> ]
+    <structure-component> = <data-ref>
+    """
+
+class Name(Designator):
+    """
+    <name> = <letter> [ <alpha-numeric-character> ]...
+    """
+    match = is_name
+
+class LiteralConstant(Constant):
+    """
+    <constant> = <int-literal-constant>
+                 | <real-literal-constant>
+                 | <complex-literal-constant>
+                 | <logical-literal-constant>
+                 | <char-literal-constant>
+                 | <boz-literal-constant>
+    """
+
+class IntLiteralConstant(LiteralConstant):
+    """
+    <int-literal-constant> = <digit-string> [ _ <kind-param> ]
+    <kind-param> = <digit-string>
+                 | <scalar-int-constant-name>
+    <digit-string> = <digit> [ <digit> ]...
+    """
+    match = re.compile(r'\A\d+\Z').match
+
+class NamedConstant(Constant, Name):
+    """
+    <named-constant> = <name>
+    """
+
+ClassType = type(Base)
+for clsname in dir():
+    cls = eval(clsname)
+    if isinstance(cls, ClassType) and issubclass(cls, Base):
+        set_subclasses(cls)
+
+class Level1Expression(Primary):
+    """
+    <level-1-expr> = [ <defined-unary-op> ] <primary>
+    <defined-unary-op> = . <letter> [ <letter> ]... .
+    """
+    def __new__(cls, primary, defined_unary_op = None):
+        obj = object.__new__(cls)
+        
+        return obj
+
+class Level2Expression:
+    """
+    <level-2-expr> = [ [ <level-2-expr> ] <add-op> ] <add-operand>
+    <add-operand> = [ <add-operand> <mult-op> ] <mult-operand>
+    <mult-operand> = <level-1-expr> [ <power-op> <mult-operand> ]
+    <power-op> = **
+    <mult-op>  = *
+                 | /
+    <add-op>   = +
+                 | -
+    """
+
+class Level3Expression:
+    """
+    <level-3-expr> = [ <level-3-expr> <concat-op> ] <level-2-expr>
+    <concat-op>    = //
+    """
+
+class Level4Expression:
+    """
+    <level-4-expr> = [ <level-3-expr> <rel-op> ] <level-3-expr>
+    <rel-op> = .EQ. | .NE. | .LT. | .LE. | .GT. | .GE. | == | /= | < | <= | > | >=
+    """
+
+class Level5Expression:
+    """
+    <level-5-expr> = [ <level-5-expr> <equiv-op> ] <equiv-operand>
+    <equiv-operand> = [ <equiv-operand> <or-op> ] <or-operand>
+    <or-operand> = [ <or-operand> <and-op> ] <and-operand>
+    <and-operand> = [ <not-op> ] <level-4-expr>
+    <not-op> = .NOT.
+    <and-op> = .AND.
+    <or-op>  = .OR.
+    <equiv-op> = .EQV.
+               | .NEQV.
+    """
+
+class Expression:
+    """
+    <expr> = [ <expr> <defined-binary-op> ] <level-5-expr>
+    <defined-binary-op> = . <letter> [ <letter> ]... .
+    """
+
+from splitline import string_replace_map
+
+def parse_expr(line, lower=False):
+    newline, repmap = string_replace_map(line, lower=lower)
+    if repmap:
+        raise NotImplementedError,`newline,repmap`

Added: trunk/numpy/f2py/lib/parser/test_expressions.py
===================================================================
--- trunk/numpy/f2py/lib/parser/test_expressions.py	2006-10-19 22:55:23 UTC (rev 3372)
+++ trunk/numpy/f2py/lib/parser/test_expressions.py	2006-10-20 06:43:31 UTC (rev 3373)
@@ -0,0 +1,35 @@
+
+
+from numpy.testing import *
+
+from expressions import *
+
+class test_Base(NumpyTestCase):
+
+    def check_name(self):
+        a = Name('a')
+        assert isinstance(a,Name),`a`
+        a = Designator('a')
+        assert isinstance(a,Name),`a`
+        a = Primary('a')
+        assert isinstance(a,Name),`a`
+        a = Base('a')
+        assert isinstance(a,Name),`a`
+        a = NamedConstant('a')
+        assert isinstance(a,Name),`a`
+        a = Constant('a')
+        assert isinstance(a,Name),`a`
+
+    def check_int_literal_constant(self):
+        a = IntLiteralConstant('1')
+        assert isinstance(a,IntLiteralConstant),`a`
+        a = LiteralConstant('1')
+        assert isinstance(a,IntLiteralConstant),`a`
+        a = Constant('1')
+        assert isinstance(a,IntLiteralConstant),`a`
+        a = Base('1')
+        assert isinstance(a,IntLiteralConstant),`a`
+        #a = NamedConstant('1') # raise NoMatch error
+
+if __name__ == "__main__":
+    NumpyTest().run()

Modified: trunk/numpy/f2py/lib/parser/typedecl_statements.py
===================================================================
--- trunk/numpy/f2py/lib/parser/typedecl_statements.py	2006-10-19 22:55:23 UTC (rev 3372)
+++ trunk/numpy/f2py/lib/parser/typedecl_statements.py	2006-10-20 06:43:31 UTC (rev 3373)
@@ -20,7 +20,7 @@
 import string
 from base_classes import Statement, BeginStatement, EndStatement,\
      AttributeHolder, Variable
-from utils import split_comma, AnalyzeError, name_re, is_entity_decl, is_name, CHAR_BIT
+from utils import split_comma, AnalyzeError, name_re, is_entity_decl, is_name, CHAR_BIT, parse_array_spec
 
 # Intrinsic type specification statements
 
@@ -325,7 +325,7 @@
         if line.startswith('('):
             i = line.find(')')
             assert i!=-1,`line`
-            array_spec = split_comma(line[1:i].strip(), item)
+            array_spec = parse_array_spec(line[1:i].strip(), item)
             line = line[i+1:].lstrip()
         char_length = None
         if line.startswith('*'):

Modified: trunk/numpy/f2py/lib/parser/utils.py
===================================================================
--- trunk/numpy/f2py/lib/parser/utils.py	2006-10-19 22:55:23 UTC (rev 3372)
+++ trunk/numpy/f2py/lib/parser/utils.py	2006-10-20 06:43:31 UTC (rev 3373)
@@ -13,7 +13,7 @@
 
 __all__ = ['split_comma', 'specs_split_comma',
            'ParseError','AnalyzeError',
-           'get_module_file','parse_bind','parse_result','is_name',
+           'get_module_file','parse_bind','parse_result','is_name','parse_array_spec',
            'CHAR_BIT','str2stmt']
 
 import re
@@ -30,22 +30,28 @@
 is_entity_decl = re.compile(r'^[a-z_]\w*',re.I).match
 is_int_literal_constant = re.compile(r'^\d+(_\w+|)$').match
 
-def split_comma(line, item = None, comma=','):
+def split_comma(line, item = None, comma=',', keep_empty=False):
     items = []
     if item is None:
         for s in line.split(comma):
             s = s.strip()
-            if not s: continue
+            if not s and not keep_empty: continue
             items.append(s)
         return items
     newitem = item.copy(line, True)
     apply_map = newitem.apply_map
     for s in newitem.get_line().split(comma):
         s = apply_map(s).strip()
-        if not s: continue
+        if not s and not keep_empty: continue
         items.append(s)
     return items
 
+def parse_array_spec(line, item = None):
+    items = []
+    for spec in split_comma(line, item):
+        items.append(tuple(split_comma(spec, item, comma=':', keep_empty=True)))
+    return items
+
 def specs_split_comma(line, item = None, upper=False):
     specs0 = split_comma(line, item)
     specs = []



More information about the Numpy-svn mailing list