Package lepl :: Package matchers :: Module matcher
[hide private]
[frames] | no frames]

Source Code for Module lepl.matchers.matcher

  1   
  2  # The contents of this file are subject to the Mozilla Public License 
  3  # (MPL) Version 1.1 (the "License"); you may not use this file except 
  4  # in compliance with the License. You may obtain a copy of the License 
  5  # at http://www.mozilla.org/MPL/ 
  6  # 
  7  # Software distributed under the License is distributed on an "AS IS" 
  8  # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
  9  # the License for the specific language governing rights and 
 10  # limitations under the License. 
 11  # 
 12  # The Original Code is LEPL (http://www.acooke.org/lepl) 
 13  # The Initial Developer of the Original Code is Andrew Cooke. 
 14  # Portions created by the Initial Developer are Copyright (C) 2009-2010 
 15  # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 
 16  # 
 17  # Alternatively, the contents of this file may be used under the terms 
 18  # of the LGPL license (the GNU Lesser General Public License, 
 19  # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 
 20  # of the LGPL License are applicable instead of those above. 
 21  # 
 22  # If you wish to allow use of your version of this file only under the 
 23  # terms of the LGPL License and not to allow others to use your version 
 24  # of this file under the MPL, indicate your decision by deleting the 
 25  # provisions above and replace them with the notice and other provisions 
 26  # required by the LGPL License.  If you do not delete the provisions 
 27  # above, a recipient may use your version of this file under either the 
 28  # MPL or the LGPL License. 
 29   
 30  ''' 
 31  Base class for matchers. 
 32  ''' 
 33   
 34   
 35  from abc import ABCMeta 
 36  from types import FunctionType 
 37   
 38  from lepl.support.lib import fmt, singleton, identity 
 39   
 40  # pylint: disable-msg=C0103, W0105 
 41  # Python 2.6 
 42  #class Matcher(metaclass=ABCMeta): 
 43  _Matcher = ABCMeta('_Matcher', (object, ), {}) 
 44  ''' 
 45  ABC used to identify matchers.   
 46   
 47  Note that graph traversal assumes subclasses are hashable and iterable. 
 48  ''' 
 49   
50 -class Matcher(_Matcher):
51
52 - def __init__(self):
53 self._small_str = self.__class__.__name__
54 55 # @abstractmethod
56 - def _match(self, stream):
57 ''' 58 This is the core method called during recursive decent. It must 59 yield (stream, results) pairs until the matcher has exhausted all 60 possible matches. 61 62 To evaluate a sub-matcher it should yield the result of calling 63 this method on the sub-matcher: 64 65 generator = sub_matcher._match(stream_in) 66 try: 67 while True: 68 # evaluate the sub-matcher 69 (stream_out, result) = yield generator 70 .... 71 # return the result from this matcher 72 yield (stream_out, result) 73 except StopIteration: 74 ... 75 76 The implementation should be decorated with @tagged in almost all 77 cases. 78 '''
79 80 # @abstractmethod
81 - def indented_repr(self, indent, key=None):
82 ''' 83 Called by repr; should recursively call contents. 84 '''
85 86 87 # Python 2.6 88 #class FactoryMatcher(metaclass=ABCMeta): 89 _FactoryMatcher = ABCMeta('_FactoryMatcher', (object, ), {}) 90 ''' 91 ABC used to identify factory matchers (have a property factory that 92 identifies the matcher they generate). 93 ''' 94 95
96 -class FactoryMatcher(_FactoryMatcher):
97 ''' 98 Imagine an abstract property called 'factory' below. 99 '''
100 101
102 -class MatcherTypeException(Exception):
103 ''' 104 Used to flag problems related to matcher types. 105 '''
106
107 -def raiseException(msg):
108 raise MatcherTypeException(msg)
109 110
111 -def case_type(matcher, if_factory, if_matcher):
112 if isinstance(matcher, FunctionType) and hasattr(matcher, 'factory'): 113 return if_factory(matcher.factory) 114 elif issubclass(matcher, Matcher): 115 return if_matcher(matcher) 116 else: 117 raise MatcherTypeException( 118 fmt('{0!s} ({1}) does not appear to be a matcher type', 119 matcher, type(matcher)))
120 121
122 -def case_instance(matcher, if_wrapper, if_matcher):
123 from lepl.matchers.support import FactoryMatcher 124 try: 125 if isinstance(matcher, FactoryMatcher): 126 return if_wrapper(matcher.factory) 127 except TypeError: 128 pass # bug in python impl 129 # may already be unpacked 130 if isinstance(matcher, FunctionType): 131 return if_wrapper(matcher) 132 if isinstance(matcher, Matcher): 133 return if_matcher(matcher) 134 else: 135 raise MatcherTypeException( 136 fmt('{0!s} ({1}) does not appear to be a matcher', 137 matcher, type(matcher)))
138 139
140 -def canonical_matcher_type(matcher):
141 ''' 142 Given a "constructor" (either a real constructor, or an annotated 143 function), generate something that uniquely identifies that (the class 144 for real constructors, and the embedded function for the output from 145 the factories). 146 ''' 147 return case_type(matcher, identity, identity)
148
149 -def matcher_type(matcher, fail=True):
150 ''' 151 ''' 152 try: 153 return case_instance(matcher, identity, type) 154 except MatcherTypeException as e: 155 if fail: 156 raise e 157 else: 158 return False
159
160 -def matcher_map(map_):
161 ''' 162 Rewrite a map whose keys are matchers to use canonical_matcher_type. 163 ''' 164 return dict((canonical_matcher_type(key), map_[key]) for key in map_)
165
166 -def matcher_instance(matcher):
167 return case_instance(matcher, identity, identity)
168 169
170 -class Relations(object):
171 ''' 172 Some kind of parent/child management for wrapped classes that I no longer 173 understand, but which appears to be used and working (it doesn't look 174 like rocket science, but until it breaks I don't care enough to know 175 more...) 176 ''' 177
178 - def __init__(self, base):
179 self.base = base 180 self.factories = set()
181
182 - def add_child(self, child):
183 return case_type(child, 184 lambda m: self.factories.add(m), 185 lambda m: self.base.register(m))
186
187 - def child_of(self, child):
188 return case_instance(child, 189 lambda m: m is self.base or m in self.factories, 190 lambda m: isinstance(self.base, type) 191 and isinstance(m, self.base))
192 193
194 -def relations(base):
195 # if base is a factory then we want the related type 196 try: 197 base = canonical_matcher_type(base) 198 except MatcherTypeException: 199 pass 200 table = singleton(Relations, dict) 201 if base not in table: 202 table[base] = Relations(base) 203 return table[base]
204 205
206 -def is_child(child, base, fail=True):
207 try: 208 return relations(base).child_of(child) 209 except MatcherTypeException as e: 210 if fail: 211 raise e 212 else: 213 return False
214
215 -def add_child(base, child):
216 relations(base).add_child(child)
217
218 -def add_children(base, *children):
219 for child in children: 220 add_child(base, child)
221