Skip to content

Commit

Permalink
F2PY G3: started impl array support and expression parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
pearu committed Oct 20, 2006
1 parent d4b47b7 commit b489d3b
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 7 deletions.
58 changes: 57 additions & 1 deletion numpy/f2py/lib/parser/base_classes.py
Expand Up @@ -120,6 +120,11 @@ def __init__(self, parent, name):
self.bind = []
self.check = []
self.init = None

# after calling analyze the following additional attributes are set:
# .is_array:
# rank
# shape
return

def __repr__(self):
Expand Down Expand Up @@ -339,7 +344,7 @@ def __str__(self):
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)
Expand All @@ -349,8 +354,59 @@ def __str__(self):
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:
Expand Down
208 changes: 208 additions & 0 deletions numpy/f2py/lib/parser/expressions.py
@@ -0,0 +1,208 @@
#!/usr/bin/env python
"""
Copyright 2006 Pearu Peterson all rights reserved,
Pearu Peterson <pearu@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`
35 changes: 35 additions & 0 deletions numpy/f2py/lib/parser/test_expressions.py
@@ -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()
4 changes: 2 additions & 2 deletions numpy/f2py/lib/parser/typedecl_statements.py
Expand Up @@ -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

Expand Down Expand Up @@ -325,7 +325,7 @@ def _parse_entity(self, line):
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('*'):
Expand Down
14 changes: 10 additions & 4 deletions numpy/f2py/lib/parser/utils.py
Expand Up @@ -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
Expand All @@ -30,22 +30,28 @@ class AnalyzeError(Exception):
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 = []
Expand Down

0 comments on commit b489d3b

Please sign in to comment.