| Home | Trees | Indices | Help |
|---|
|
|
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)
95
98 '''
99 Helper object that composes transformations and also keeps a list of
100 the separate transformations for introspection.
101 '''
102
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
117
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
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
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
144 return '<' + ','.join(map(lambda x: x.__name__, self.functions)) + '>'
145
147 return fmt('TransformationWrapper({0})', self)
148
151
152 # Python 2.6
154 return self.__bool__()
155
161
168
171 '''
172 Apply a function to (stream_in, matcher)
173
174 Typically used via `Apply` and `KApply`.
175 '''
176
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
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
212 '''
213 Create a new Transform that includes the extra processing.
214 '''
215 return Transform(self.matcher, self.wrapper.compose(function))
216
218 '''
219 Allow transforms to wrap Delayed in rewriting.
220 '''
221 self.matcher += other
222 return self
223
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
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
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Sun May 13 16:31:10 2012 | http://epydoc.sourceforge.net |