1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 '''
31 Matchers that embody fundamental, common actions.
32 '''
33
34
35
36
37
38
39
40
41 from re import compile as compile_
42
43 from lepl.stream.core import s_next, s_eq, s_empty, s_line
44 from lepl.core.parser import tagged
45 from lepl.matchers.support import OperatorMatcher, coerce_, \
46 function_matcher, function_matcher_factory, trampoline_matcher_factory, \
47 to, sequence_matcher
48 from lepl.support.lib import fmt
49
50
51 @sequence_matcher
52 -def Never(support, stream):
53 '''
54 Always fails to match.
55
56 (in this package rather than lepl.matchers.memo to simplify import
57 dependencies)
58 '''
59 if False:
60 yield
61
62
63 @function_matcher_factory()
64 -def Any(restrict=None):
65 '''
66 Create a matcher for a single character.
67
68 :Parameters:
69
70 restrict (optional)
71 A list of tokens (or a string of suitable characters).
72 If omitted any single token is accepted.
73
74 **Note:** This argument is *not* a sub-matcher.
75 '''
76 warned = [False]
77
78 def match(support, stream):
79 '''
80 Do the matching. The result will be a single matching character.
81 '''
82 (value, next_stream) = s_next(stream)
83 if restrict:
84 try:
85 if value not in restrict:
86 raise StopIteration
87 except TypeError:
88
89
90 if not warned[0]:
91 support._warn(fmt('Cannot restrict {0} with {1!r}',
92 value, restrict))
93 warned[0] = True
94 raise StopIteration
95 return ([value], next_stream)
96
97 return match
98
99
100 @function_matcher_factory()
101 -def Literal(text):
102 '''
103 Match a series of tokens in the stream (**''**).
104
105 Typically the argument is a string but a list might be appropriate
106 with some streams.
107 '''
108 delta = len(text)
109 def match(support, stream):
110 '''
111 Do the matching (return a generator that provides successive
112 (result, stream) tuples).
113
114 Need to be careful here to use only the restricted functionality
115 provided by the stream interface.
116 '''
117 try:
118 (value, next_stream) = s_next(stream, count=delta)
119 if text == value:
120 return ([value], next_stream)
121 except IndexError:
122 pass
123 return match
124
125
126 @function_matcher
127 -def Empty(support, stream):
128 '''
129 Match any stream, consumes no input, and returns nothing.
130 '''
131 return ([], stream)
132
135 '''
136 Tests to see if the embedded matcher *could* match, but does not do the
137 matching. On success an empty list (ie no result) and the original
138 stream are returned.
139
140 When negated (preceded by ~) the behaviour is reversed - success occurs
141 only if the embedded matcher would fail to match.
142
143 This is a consumer because it's correct functioning depends directly on
144 the stream's contents.
145 '''
146
147 - def __init__(self, matcher, negated=False):
148 '''
149 On success, no input is consumed.
150 If negated, this will succeed if the matcher fails. If the matcher is
151 a string it is coerced to a literal match.
152 '''
153 super(Lookahead, self).__init__()
154 self._arg(matcher=coerce_(matcher))
155 self._karg(negated=negated)
156
157 @tagged
159 '''
160 Do the matching (return a generator that provides successive
161 (result, stream) tuples).
162 '''
163 try:
164 yield self.matcher._match(stream)
165 success = True
166 except StopIteration:
167 success = False
168 if success is self.negated:
169 return
170 else:
171 yield ([], stream)
172
174 '''
175 Invert the semantics (this overrides the usual meaning for ~).
176 '''
177 return Lookahead(self.matcher, negated=not self.negated)
178
179
180 @function_matcher_factory()
181 -def Regexp(pattern):
182 '''
183 Match a regular expression. If groups are defined, they are returned
184 as results. Otherwise, the entire expression is returned.
185
186 If the pattern contains groups, they are returned as separate results,
187 otherwise the whole match is returned.
188
189 :Parameters:
190
191 pattern
192 The regular expression to match.
193 '''
194 pattern = compile_(pattern)
195
196 def match(support, stream):
197 (line, _) = s_line(stream, True)
198 match = pattern.match(line)
199 if match:
200 eaten = len(match.group())
201 if match.groups():
202 return (list(match.groups()), s_next(stream, count=eaten)[1])
203 else:
204 return ([match.group()], s_next(stream, count=eaten)[1])
205 return match
206
207
208 -class Delayed(OperatorMatcher):
209 '''
210 A placeholder that allows forward references (**+=**). Before use a
211 matcher must be assigned via '+='.
212 '''
213
222
224 if not self.matcher:
225 raise ValueError('Delayed matcher still unbound.')
226
228 '''
229 Do the matching (return a generator that provides successive
230 (result, stream) tuples). This is overwritten when a matcher is
231 defined.
232 '''
233 self.assert_matcher()
234
235
236
244
245
246 @function_matcher
247 -def Eof(support, stream):
248 '''
249 Match the end of a stream. Returns nothing.
250
251 This is also aliased to Eos in lepl.derived.
252 '''
253 if s_empty(stream):
254 return ([], stream)
255
256
257 @trampoline_matcher_factory(matcher=to(Literal))
259 '''
260 Only accept the match if it consumes data from the input
261 '''
262 def match(support, stream_in):
263 '''
264 Do the match and test whether the stream has progressed.
265 '''
266 try:
267 generator = matcher._match(stream_in)
268 while True:
269 (result, stream_out) = yield generator
270 if consume != s_eq(stream_in, stream_out):
271 yield (result, stream_out)
272 except StopIteration:
273 pass
274 return match
275