diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2006-10-20 12:32:23 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2006-10-20 12:32:23 +0000 |
commit | b5a17594bc7e45f30fea4a651783361b6caaa294 (patch) | |
tree | 75962edeb373a75d23d1a96d75c570b7936eb93e /numpy | |
parent | 5e6ae5b1b7c335a70d7c73711d9cb180e3caf7aa (diff) | |
download | numpy-b5a17594bc7e45f30fea4a651783361b6caaa294.tar.gz |
F2PY G3: Impl. pattern tools for expression parsing.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/f2py/lib/parser/expressions.py | 166 | ||||
-rw-r--r-- | numpy/f2py/lib/parser/pattern_tools.py | 119 | ||||
-rw-r--r-- | numpy/f2py/lib/parser/test_expressions.py | 6 |
3 files changed, 251 insertions, 40 deletions
diff --git a/numpy/f2py/lib/parser/expressions.py b/numpy/f2py/lib/parser/expressions.py index d871757ca..6f1ef6520 100644 --- a/numpy/f2py/lib/parser/expressions.py +++ b/numpy/f2py/lib/parser/expressions.py @@ -28,50 +28,47 @@ class DefinedOp: 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 +class NoChildAllowed: + pass +class NoChildAllowedError(Exception): + pass +class NoMatchError(Exception): + pass 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 Base(object): -class NoMatch(Exception): - pass + subclasses = {} -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) + init = cls.__dict__.get('init', Base.init) + init(obj, 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): + for c in Base.subclasses.get(cls.__name__,[]): + try: + return c(string) + except NoMatchError: + pass + raise NoMatchError,'%s: %r' % (cls.__name__, string) + + def init(self, string): self.string = string return - def __str__(self): return self.string + + def __str__(self): + str_func = self.__class__.__dict__.get('tostr', None) + if str_func is not None: + return str_func(self) + return self.string def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.string) + + class Primary(Base): """ <primary> = <constant> @@ -111,11 +108,7 @@ class Designator(Primary): <structure-component> = <data-ref> """ -class Name(Designator): - """ - <name> = <letter> [ <alpha-numeric-character> ]... - """ - match = is_name + class LiteralConstant(Constant): """ @@ -127,27 +120,122 @@ class LiteralConstant(Constant): | <boz-literal-constant> """ -class IntLiteralConstant(LiteralConstant): +class SignedIntLiteralConstant(LiteralConstant): + """ + <signed-int-literal-constant> = [ <sign> ] <int-literal-constant> + <sign> = + | - + """ + match = re.compile(r'\A[+-]\s*\d+\Z').match + + def init(self, string): + Base.init(self, string) + self.content = [string[0], IntLiteralConstant(string[1:].lstrip())] + return + def tostr(self): + return '%s%s' % tuple(self.content) + +class NamedConstant(Constant): + """ + <named-constant> = <name> + """ + +def compose_patterns(pattern_list, names join=''): + return join.join(pattern_list) + +def add_pattern(pattern_name, *pat_list): + p = '' + for pat in pat_list: + if isinstance(pat, PatternOptional): + p += '(%s|)' % (add_pattern(None, pat.args)) + elif isinstance(pat, PatternOr): + p += '(%s)' % ('|'.join([add_pattern(None, p1) for p1 in par.args])) + else: + subpat = pattern_map.get(pat,None) + if subpat is None: + p += pat + else: + p += '(?P<%s>%s)' % (pat, subpat) + if pattern_map is not None: + pattern_map[pattern_name] = p + return p + + + +class PatternBase: + def __init__(self,*args): + self.args = args + return + +class PatternOptional(PatternBase): + pass +class PatternOr(PatternBase): + pass +class PatternJoin(PatternBase): + join = '' + +pattern_map = { + 'name': r'[a-zA-Z]\w+' + 'digit-string': r'\d+' + } +add_pattern('kind-param', + PatternOr('digit-string','name')) +add_pattern('int-literal-constant', + 'digit-string',PatternOptional('_','kind-param')) + +name_pat = r'[a-z]\w*' +digit_pat = r'\d' +digit_string_pat = r'\d+' +kind_param_pat = '(%s|%s)' % (digit_string_pat, name_pat) + +class Name(Designator, NamedConstant, NoChildAllowed): + """ + <name> = <letter> [ <alpha-numeric-character> ]... + """ + match = re.compile(r'\A'+name_pat+r'\Z',re.I).match + +class IntLiteralConstant(SignedIntLiteralConstant, NoChildAllowed): """ <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 + match = compose_pattern([digit_string_pat, '_', kind_param_pat],r'\s*') + + compose_pattern('int-literal-constant','digit-string','_','kind-param') -class NamedConstant(Constant, Name): +class DigitString(IntLiteralConstant, NoChildAllowed): """ - <named-constant> = <name> + <digit-string> = <digit> [ <digit> ]... """ + match = re.compile(r'\A\d+\Z').match + +################# Setting up Base.subclasses ##################### +def set_subclasses(cls): + """ + Append cls to cls base classes attribute lists `_subclasses` + so that all classes derived from Base know their subclasses + one level down. + """ + for basecls in cls.__bases__: + if issubclass(basecls, Base): + if issubclass(basecls, NoChildAllowed): + raise NoChildAllowedError,'%s while adding %s' % (basecls.__name__,cls.__name__) + try: + Base.subclasses[basecls.__name__].append(cls) + except KeyError: + Base.subclasses[basecls.__name__] = [cls] + return ClassType = type(Base) for clsname in dir(): cls = eval(clsname) if isinstance(cls, ClassType) and issubclass(cls, Base): set_subclasses(cls) -class Level1Expression(Primary): +#################################################################### + +class Level1Expression:#(Primary): """ <level-1-expr> = [ <defined-unary-op> ] <primary> <defined-unary-op> = . <letter> [ <letter> ]... . diff --git a/numpy/f2py/lib/parser/pattern_tools.py b/numpy/f2py/lib/parser/pattern_tools.py new file mode 100644 index 000000000..9cd4b5a1c --- /dev/null +++ b/numpy/f2py/lib/parser/pattern_tools.py @@ -0,0 +1,119 @@ + +import re + +class Pattern: + """ + p1 | p2 -> <p1> | <p2> + p1 + p2 -> <p1> <p2> + p1 & p2 -> <p1><p2> + ~p1 -> [ <p1> ] + ~~p1 -> [ <p1> ]... + ~~~p1 -> <p1> [ <p1> ]... + ~~~~p1 -> <p1> [ <p1> ]... + abs(p1) -> whole string match of <p1> + p1.named(name) -> match of <p1> has name + p1.match(string) -> return string match with <p1> + """ + _special_symbol_map = {'.': '[.]', + '*': '[*]', + '+': '[+]', + '|': '[|]', + '(': r'\(', + ')': r'\)', + } + + def __init__(self, label, pattern, optional=0): + self.label = label + self.pattern = pattern + self.optional = optional + return + + def match(self, string): + if hasattr(self, '_compiled_match'): + return self._compiled.match(string) + self._compiled = compiled = re.compile(self.pattern) + return compiled.match(string) + + def __abs__(self): + return Pattern(self.label, r'\A' + self.pattern+ r'\Z') + + def __repr__(self): + return '%s(%r, %r)' % (self.__class__.__name__, self.label, self.pattern) + + def __or__(self, other): + label = '( %s OR %s )' % (self.label, other.label) + pattern = '(%s|%s)' % (self.pattern, other.pattern) + return Pattern(label, pattern) + + def __and__(self, other): + if isinstance(other, Pattern): + label = '%s%s' % (self.label, other.label) + pattern = self.pattern + other.pattern + else: + assert isinstance(other,str),`other` + label = '%s%s' % (self.label, other) + pattern = self.pattern + other + return Pattern(label, pattern) + + def __rand__(self, other): + assert isinstance(other,str),`other` + label = '%s%s' % (other, self.label) + pattern = other + self.pattern + return Pattern(label, pattern) + + def __invert__(self): + if self.optional: + if self.optional==1: + return Pattern(self.label + '...', self.pattern[:-1] + '*', 2) + if self.optional==2: + return Pattern('%s %s' % (self.label[1:-4].strip(), self.label), self.pattern[:-1] + '+', 3) + return self + label = '[ %s ]' % (self.label) + pattern = '(%s)?' % (self.pattern) + return Pattern(label, pattern, 1) + + def __add__(self, other): + if isinstance(other, Pattern): + label = '%s %s' % (self.label, other.label) + pattern = self.pattern + r'\s*' + other.pattern + else: + assert isinstance(other,str),`other` + label = '%s %s' % (self.label, other) + other = self._special_symbol_map.get(other, other) + pattern = self.pattern + r'\s*' + other + return Pattern(label, pattern) + + def __radd__(self, other): + assert isinstance(other,str),`other` + label = '%s %s' % (other, self.label) + other = self._special_symbol_map.get(other, other) + pattern = other + r'\s*' + self.pattern + return Pattern(label, pattern) + + def named(self, name = None): + if name is None: + label = self.label + assert label[0]+label[-1]=='<>' and ' ' not in label,`label` + else: + label = '<%s>' % (name) + pattern = '(?P%s%s)' % (label.replace('-','_'), self.pattern) + return Pattern(label, pattern) + +name = Pattern('<name>', r'[a-z]\w*') +digit_string = Pattern('<digit-string>',r'\d+') +sign = Pattern('<sign>',r'[+-]') +exponent_letter = Pattern('<exponent-letter>',r'[ED]') + +kind_param = digit_string | name +signed_digit_string = ~sign + digit_string +int_literal_constant = digit_string + ~('_' + kind_param) +signed_int_literal_constant = ~sign + int_literal_constant + +exponent = signed_digit_string +significand = digit_string + '.' + ~digit_string | '.' + digit_string +real_literal_constant = significand + ~(exponent_letter + exponent) + ~ ('_' + kind_param) | \ + digit_string + exponent_letter + exponent + ~ ('_' + kind_param) +signed_real_literal_constant = ~sign + real_literal_constant + + +print signed_real_literal_constant diff --git a/numpy/f2py/lib/parser/test_expressions.py b/numpy/f2py/lib/parser/test_expressions.py index 5feddaba2..3f4722019 100644 --- a/numpy/f2py/lib/parser/test_expressions.py +++ b/numpy/f2py/lib/parser/test_expressions.py @@ -11,7 +11,7 @@ class test_Base(NumpyTestCase): assert isinstance(a,Name),`a` a = Designator('a') assert isinstance(a,Name),`a` - a = Primary('a') + a = Constant('a') assert isinstance(a,Name),`a` a = Base('a') assert isinstance(a,Name),`a` @@ -29,6 +29,10 @@ class test_Base(NumpyTestCase): assert isinstance(a,IntLiteralConstant),`a` a = Base('1') assert isinstance(a,IntLiteralConstant),`a` + a = Base('+1') + assert isinstance(a,SignedIntLiteralConstant),`a` + a = IntLiteralConstant('0') + assert isinstance(a,IntLiteralConstant),`a` #a = NamedConstant('1') # raise NoMatch error if __name__ == "__main__": |