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

Source Code for Module lepl.matchers.core

  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  Matchers that embody fundamental, common actions. 
 32  ''' 
 33   
 34  # pylint: disable-msg=C0103,W0212 
 35  # (consistent interfaces) 
 36  # pylint: disable-msg=E1101 
 37  # (_args create attributes) 
 38  # pylint: disable-msg=R0901, R0904, W0142 
 39  # lepl conventions 
 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 # it would be nice to make this an error, but for line aware 89 # parsing (and any other heterogenous input) it's legal 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
133 134 -class Lookahead(OperatorMatcher):
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
158 - def _match(self, stream):
159 ''' 160 Do the matching (return a generator that provides successive 161 (result, stream) tuples). 162 ''' 163 try: 164 yield self.matcher._match(stream) # an evaluation, not a return 165 success = True 166 except StopIteration: 167 success = False 168 if success is self.negated: 169 return 170 else: 171 yield ([], stream)
172
173 - def __invert__(self):
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
214 - def __init__(self, matcher=None):
215 ''' 216 Introduce the matcher. It can be defined later with '+=' 217 ''' 218 super(Delayed, self).__init__() 219 self._karg(matcher=matcher) 220 if matcher: 221 self._match = matcher._match
222
223 - def assert_matcher(self):
224 if not self.matcher: 225 raise ValueError('Delayed matcher still unbound.')
226
227 - def _match(self, stream):
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 # pylint: disable-msg=E0203, W0201 236 # _karg defined this in constructor
237 - def __iadd__(self, matcher):
238 if self.matcher: 239 raise ValueError('Delayed matcher already bound.') 240 else: 241 self.matcher = coerce_(matcher) 242 self._match = matcher._match 243 return self
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))
258 -def Consumer(matcher, consume=True):
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