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

Source Code for Module lepl.matchers.transform

  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  A transformation is a function that modifies the result of calling a matcher 
 32  once. 
 33   
 34  From the point of view of a transformation, a matcher is a function that  
 35  takes no arguments and either returns (results, stream_out) or raises a  
 36  StopIteration (note - this is an interface - the way you typically define  
 37  matchers doesn't conform to that interface, but decorators like  
 38  @function_matcher etc do the necessary work to adapt things as necessary). 
 39   
 40  A transformation takes two arguments - the initial stream and a matcher  
 41  (as described above).  The transformation, when called, should return  
 42  either return a (result, stream_out) pair, or raise a StopIteration.   
 43  A null transformation, therefore, would simply evaluate the matcher it  
 44  receives: 
 45      null_transform = lambda stream, matcher: matcher() 
 46  ''' 
 47   
 48   
 49  # pylint: disable-msg=C0103,W0212 
 50  # (consistent interfaces) 
 51  # pylint: disable-msg=E1101 
 52  # (_args create attributes) 
 53  # pylint: disable-msg=R0901, R0904, W0142 
 54  # lepl conventions 
 55   
 56  from abc import ABCMeta 
 57   
 58  from lepl.core.parser import tagged 
 59  from lepl.matchers.support import Transformable, coerce_, NoMemo,\ 
 60      trampoline_matcher_factory 
 61  from lepl.support.lib import fmt, str 
 62  from lepl.support.node import Node 
 63   
 64   
 65  # pylint: disable-msg=W0105 
 66  # Python 2.6 
 67  #class ApplyRaw(metaclass=ABCMeta): 
 68  ApplyRaw = ABCMeta('ApplyRaw', (object, ), {}) 
 69  ''' 
 70  ABC used to control `Apply`, so that the result is not wrapped in a list.   
 71  ''' 
 72   
 73   
 74  # Python 2.6 
 75  #class ApplyArgs(metaclass=ABCMeta): 
 76  ApplyArgs = ABCMeta('ApplyArgs', (object, ), {}) 
 77  ''' 
 78  ABC used to control `Apply`, so that the results list is supplied as "*args".   
 79  ''' 
 80   
 81  ApplyArgs.register(Node) 
82 83 84 -class NullTransformation(object):
85
86 - def __call__(self, _stream, matcher):
87 return matcher()
88
89 - def __bool__(self):
90 return False
91 92 # Python 2.6
93 - def __nonzero__(self):
94 return self.__bool__()
95
96 97 -class TransformationWrapper(object):
98 ''' 99 Helper object that composes transformations and also keeps a list of 100 the separate transformations for introspection. 101 ''' 102
103 - def __init__(self, functions=None):
104 ''' 105 We accept either a list of a functions or a single value. 106 ''' 107 functions = [] if functions is None else functions 108 if not isinstance(functions, list): 109 functions = [functions] 110 self.functions = [] 111 self.function = NullTransformation() 112 self.extend(functions)
113
114 - def extend(self, functions):
115 for function in functions: 116 self.append(function)
117
118 - def append(self, function):
119 if self: 120 self.function = \ 121 lambda stream, matcher, f=self.function: \ 122 function(stream, lambda: f(stream, matcher)) 123 else: 124 self.function = function 125 self.functions.append(function)
126
127 - def compose(self, wrapper):
128 ''' 129 Apply wrapped transformations to the results of this wrapper. 130 ''' 131 functions = list(self.functions) 132 functions.extend(wrapper.functions) 133 return TransformationWrapper(functions)
134
135 - def precompose(self, wrapper):
136 ''' 137 Insert the transformation before the existing functions. 138 ''' 139 functions = list(wrapper.functions) 140 functions.extend(self.functions) 141 return TransformationWrapper(functions)
142
143 - def __str__(self):
144 return '<' + ','.join(map(lambda x: x.__name__, self.functions)) + '>'
145
146 - def __repr__(self):
147 return fmt('TransformationWrapper({0})', self)
148
149 - def __bool__(self):
150 return bool(self.functions)
151 152 # Python 2.6
153 - def __nonzero__(self):
154 return self.__bool__()
155
156 - def __iter__(self):
157 ''' 158 Co-operate with graph routines. 159 ''' 160 return iter([])
161
162 163 -def raise_(e):
164 ''' 165 Let raise be used as a function. 166 ''' 167 raise e
168
169 170 -class Transform(Transformable, NoMemo):
171 ''' 172 Apply a function to (stream_in, matcher) 173 174 Typically used via `Apply` and `KApply`. 175 ''' 176
177 - def __init__(self, matcher, function):
178 super(Transform, self).__init__(function) 179 self._arg(matcher=coerce_(matcher)) 180 # it's ok that this overwrites the same thing from Transformable 181 # (Transformable cannot have an argument because it subclasses 182 # OperatorMatcher, and passing in function as a constructor arg 183 # is a nightmare). 184 if not isinstance(function, TransformationWrapper): 185 function = TransformationWrapper(function) 186 self._arg(wrapper=function)
187 188 @tagged
189 - def _match(self, stream_in):
190 ''' 191 Do the matching (return a generator that provides successive 192 (result, stream) tuples). 193 194 The protocol here allows functions to "veto" individual entries and 195 also to "append" more results, but doesn't support insertion of 196 additional results. 197 ''' 198 function = self.wrapper.function 199 generator = self.matcher._match(stream_in) 200 while True: 201 try: 202 results = yield generator 203 except StopIteration: 204 yield function(stream_in, lambda: raise_(StopIteration)) 205 else: 206 try: 207 yield function(stream_in, lambda: results) 208 except StopIteration: 209 pass
210
211 - def compose(self, function):
212 ''' 213 Create a new Transform that includes the extra processing. 214 ''' 215 return Transform(self.matcher, self.wrapper.compose(function))
216
217 - def __iadd__(self, other):
218 ''' 219 Allow transforms to wrap Delayed in rewriting. 220 ''' 221 self.matcher += other 222 return self
223
224 225 @trampoline_matcher_factory() 226 -def Assert(matcher):
227 ''' 228 Raise `StopIteration` if the matcher returns an empty result. 229 230 This can be useful when using a transform that raise a `StopIteration` 231 because that only "vetoes" the answer, which might leave an empty list. 232 ''' 233 def match(support, stream): 234 generator = matcher._match(stream) 235 while True: 236 results = yield generator 237 if results[0]: 238 yield results
239 return match 240
241 242 -def PostCondition(matcher, predicate):
243 ''' 244 Apply the predicate to each result in turn and return the result only 245 if it is always True. 246 ''' 247 248 def transformation(stream_in, matcher): 249 (result, stream_out) = matcher() 250 if predicate(result): 251 return (result, stream_out) 252 else: 253 raise StopIteration
254 return Transform(matcher, transformation) 255