Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# module pyparsing.py 

2# 

3# Copyright (c) 2003-2016 Paul T. McGuire 

4# 

5# Permission is hereby granted, free of charge, to any person obtaining 

6# a copy of this software and associated documentation files (the 

7# "Software"), to deal in the Software without restriction, including 

8# without limitation the rights to use, copy, modify, merge, publish, 

9# distribute, sublicense, and/or sell copies of the Software, and to 

10# permit persons to whom the Software is furnished to do so, subject to 

11# the following conditions: 

12# 

13# The above copyright notice and this permission notice shall be 

14# included in all copies or substantial portions of the Software. 

15# 

16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 

17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 

18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 

19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 

20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 

21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 

22# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 

23# 

24 

25__doc__ = \ 

26""" 

27pyparsing module - Classes and methods to define and execute parsing grammars 

28 

29The pyparsing module is an alternative approach to creating and executing simple grammars, 

30vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you 

31don't need to learn a new syntax for defining grammars or matching expressions - the parsing module 

32provides a library of classes that you use to construct the grammar directly in Python. 

33 

34Here is a program to parse "Hello, World!" (or any greeting of the form  

35C{"<salutation>, <addressee>!"}), built up using L{Word}, L{Literal}, and L{And} elements  

36(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to 

37L{Literal} expressions):: 

38 

39 from pyparsing import Word, alphas 

40 

41 # define grammar of a greeting 

42 greet = Word(alphas) + "," + Word(alphas) + "!" 

43 

44 hello = "Hello, World!" 

45 print (hello, "->", greet.parseString(hello)) 

46 

47The program outputs the following:: 

48 

49 Hello, World! -> ['Hello', ',', 'World', '!'] 

50 

51The Python representation of the grammar is quite readable, owing to the self-explanatory 

52class names, and the use of '+', '|' and '^' operators. 

53 

54The L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.parseString>} can be accessed as a nested list, a dictionary, or an 

55object with named attributes. 

56 

57The pyparsing module handles some of the problems that are typically vexing when writing text parsers: 

58 - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) 

59 - quoted strings 

60 - embedded comments 

61""" 

62 

63__version__ = "2.1.10" 

64__versionTime__ = "07 Oct 2016 01:31 UTC" 

65__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" 

66 

67import string 

68from weakref import ref as wkref 

69import copy 

70import sys 

71import warnings 

72import re 

73import sre_constants 

74import collections 

75import pprint 

76import traceback 

77import types 

78from datetime import datetime 

79 

80try: 

81 from _thread import RLock 

82except ImportError: 

83 from threading import RLock 

84 

85try: 

86 from collections import OrderedDict as _OrderedDict 

87except ImportError: 

88 try: 

89 from ordereddict import OrderedDict as _OrderedDict 

90 except ImportError: 

91 _OrderedDict = None 

92 

93#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) 

94 

95__all__ = [ 

96'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', 

97'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', 

98'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', 

99'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', 

100'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', 

101'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 

102'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', 

103'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', 

104'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', 

105'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', 

106'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno', 

107'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', 

108'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', 

109'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 

110'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', 

111'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', 

112'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', 

113'CloseMatch', 'tokenMap', 'pyparsing_common', 

114] 

115 

116system_version = tuple(sys.version_info)[:3] 

117PY_3 = system_version[0] == 3 

118if PY_3: 

119 _MAX_INT = sys.maxsize 

120 basestring = str 

121 unichr = chr 

122 _ustr = str 

123 

124 # build list of single arg builtins, that can be used as parse actions 

125 singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max] 

126 

127else: 

128 _MAX_INT = sys.maxint 

129 range = xrange 

130 

131 def _ustr(obj): 

132 """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries 

133 str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It 

134 then < returns the unicode object | encodes it with the default encoding | ... >. 

135 """ 

136 if isinstance(obj,unicode): 

137 return obj 

138 

139 try: 

140 # If this works, then _ustr(obj) has the same behaviour as str(obj), so 

141 # it won't break any existing code. 

142 return str(obj) 

143 

144 except UnicodeEncodeError: 

145 # Else encode it 

146 ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') 

147 xmlcharref = Regex(r'&#\d+;') 

148 xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) 

149 return xmlcharref.transformString(ret) 

150 

151 # build list of single arg builtins, tolerant of Python version, that can be used as parse actions 

152 singleArgBuiltins = [] 

153 import __builtin__ 

154 for fname in "sum len sorted reversed list tuple set any all min max".split(): 

155 try: 

156 singleArgBuiltins.append(getattr(__builtin__,fname)) 

157 except AttributeError: 

158 continue 

159 

160_generatorType = type((y for y in range(1))) 

161 

162def _xml_escape(data): 

163 """Escape &, <, >, ", ', etc. in a string of data.""" 

164 

165 # ampersand must be replaced first 

166 from_symbols = '&><"\'' 

167 to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split()) 

168 for from_,to_ in zip(from_symbols, to_symbols): 

169 data = data.replace(from_, to_) 

170 return data 

171 

172class _Constants(object): 

173 pass 

174 

175alphas = string.ascii_uppercase + string.ascii_lowercase 

176nums = "0123456789" 

177hexnums = nums + "ABCDEFabcdef" 

178alphanums = alphas + nums 

179_bslash = chr(92) 

180printables = "".join(c for c in string.printable if c not in string.whitespace) 

181 

182class ParseBaseException(Exception): 

183 """base exception class for all parsing runtime exceptions""" 

184 # Performance tuning: we construct a *lot* of these, so keep this 

185 # constructor as small and fast as possible 

186 def __init__( self, pstr, loc=0, msg=None, elem=None ): 

187 self.loc = loc 

188 if msg is None: 

189 self.msg = pstr 

190 self.pstr = "" 

191 else: 

192 self.msg = msg 

193 self.pstr = pstr 

194 self.parserElement = elem 

195 self.args = (pstr, loc, msg) 

196 

197 @classmethod 

198 def _from_exception(cls, pe): 

199 """ 

200 internal factory method to simplify creating one type of ParseException  

201 from another - avoids having __init__ signature conflicts among subclasses 

202 """ 

203 return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) 

204 

205 def __getattr__( self, aname ): 

206 """supported attributes by name are: 

207 - lineno - returns the line number of the exception text 

208 - col - returns the column number of the exception text 

209 - line - returns the line containing the exception text 

210 """ 

211 if( aname == "lineno" ): 

212 return lineno( self.loc, self.pstr ) 

213 elif( aname in ("col", "column") ): 

214 return col( self.loc, self.pstr ) 

215 elif( aname == "line" ): 

216 return line( self.loc, self.pstr ) 

217 else: 

218 raise AttributeError(aname) 

219 

220 def __str__( self ): 

221 return "%s (at char %d), (line:%d, col:%d)" % \ 

222 ( self.msg, self.loc, self.lineno, self.column ) 

223 def __repr__( self ): 

224 return _ustr(self) 

225 def markInputline( self, markerString = ">!<" ): 

226 """Extracts the exception line from the input string, and marks 

227 the location of the exception with a special symbol. 

228 """ 

229 line_str = self.line 

230 line_column = self.column - 1 

231 if markerString: 

232 line_str = "".join((line_str[:line_column], 

233 markerString, line_str[line_column:])) 

234 return line_str.strip() 

235 def __dir__(self): 

236 return "lineno col line".split() + dir(type(self)) 

237 

238class ParseException(ParseBaseException): 

239 """ 

240 Exception thrown when parse expressions don't match class; 

241 supported attributes by name are: 

242 - lineno - returns the line number of the exception text 

243 - col - returns the column number of the exception text 

244 - line - returns the line containing the exception text 

245  

246 Example:: 

247 try: 

248 Word(nums).setName("integer").parseString("ABC") 

249 except ParseException as pe: 

250 print(pe) 

251 print("column: {}".format(pe.col)) 

252  

253 prints:: 

254 Expected integer (at char 0), (line:1, col:1) 

255 column: 1 

256 """ 

257 pass 

258 

259class ParseFatalException(ParseBaseException): 

260 """user-throwable exception thrown when inconsistent parse content 

261 is found; stops all parsing immediately""" 

262 pass 

263 

264class ParseSyntaxException(ParseFatalException): 

265 """just like L{ParseFatalException}, but thrown internally when an 

266 L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop  

267 immediately because an unbacktrackable syntax error has been found""" 

268 pass 

269 

270#~ class ReparseException(ParseBaseException): 

271 #~ """Experimental class - parse actions can raise this exception to cause 

272 #~ pyparsing to reparse the input string: 

273 #~ - with a modified input string, and/or 

274 #~ - with a modified start location 

275 #~ Set the values of the ReparseException in the constructor, and raise the 

276 #~ exception in a parse action to cause pyparsing to use the new string/location. 

277 #~ Setting the values as None causes no change to be made. 

278 #~ """ 

279 #~ def __init_( self, newstring, restartLoc ): 

280 #~ self.newParseText = newstring 

281 #~ self.reparseLoc = restartLoc 

282 

283class RecursiveGrammarException(Exception): 

284 """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive""" 

285 def __init__( self, parseElementList ): 

286 self.parseElementTrace = parseElementList 

287 

288 def __str__( self ): 

289 return "RecursiveGrammarException: %s" % self.parseElementTrace 

290 

291class _ParseResultsWithOffset(object): 

292 def __init__(self,p1,p2): 

293 self.tup = (p1,p2) 

294 def __getitem__(self,i): 

295 return self.tup[i] 

296 def __repr__(self): 

297 return repr(self.tup[0]) 

298 def setOffset(self,i): 

299 self.tup = (self.tup[0],i) 

300 

301class ParseResults(object): 

302 """ 

303 Structured parse results, to provide multiple means of access to the parsed data: 

304 - as a list (C{len(results)}) 

305 - by list index (C{results[0], results[1]}, etc.) 

306 - by attribute (C{results.<resultsName>} - see L{ParserElement.setResultsName}) 

307 

308 Example:: 

309 integer = Word(nums) 

310 date_str = (integer.setResultsName("year") + '/'  

311 + integer.setResultsName("month") + '/'  

312 + integer.setResultsName("day")) 

313 # equivalent form: 

314 # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") 

315 

316 # parseString returns a ParseResults object 

317 result = date_str.parseString("1999/12/31") 

318 

319 def test(s, fn=repr): 

320 print("%s -> %s" % (s, fn(eval(s)))) 

321 test("list(result)") 

322 test("result[0]") 

323 test("result['month']") 

324 test("result.day") 

325 test("'month' in result") 

326 test("'minutes' in result") 

327 test("result.dump()", str) 

328 prints:: 

329 list(result) -> ['1999', '/', '12', '/', '31'] 

330 result[0] -> '1999' 

331 result['month'] -> '12' 

332 result.day -> '31' 

333 'month' in result -> True 

334 'minutes' in result -> False 

335 result.dump() -> ['1999', '/', '12', '/', '31'] 

336 - day: 31 

337 - month: 12 

338 - year: 1999 

339 """ 

340 def __new__(cls, toklist=None, name=None, asList=True, modal=True ): 

341 if isinstance(toklist, cls): 

342 return toklist 

343 retobj = object.__new__(cls) 

344 retobj.__doinit = True 

345 return retobj 

346 

347 # Performance tuning: we construct a *lot* of these, so keep this 

348 # constructor as small and fast as possible 

349 def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ): 

350 if self.__doinit: 

351 self.__doinit = False 

352 self.__name = None 

353 self.__parent = None 

354 self.__accumNames = {} 

355 self.__asList = asList 

356 self.__modal = modal 

357 if toklist is None: 

358 toklist = [] 

359 if isinstance(toklist, list): 

360 self.__toklist = toklist[:] 

361 elif isinstance(toklist, _generatorType): 

362 self.__toklist = list(toklist) 

363 else: 

364 self.__toklist = [toklist] 

365 self.__tokdict = dict() 

366 

367 if name is not None and name: 

368 if not modal: 

369 self.__accumNames[name] = 0 

370 if isinstance(name,int): 

371 name = _ustr(name) # will always return a str, but use _ustr for consistency 

372 self.__name = name 

373 if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])): 

374 if isinstance(toklist,basestring): 

375 toklist = [ toklist ] 

376 if asList: 

377 if isinstance(toklist,ParseResults): 

378 self[name] = _ParseResultsWithOffset(toklist.copy(),0) 

379 else: 

380 self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) 

381 self[name].__name = name 

382 else: 

383 try: 

384 self[name] = toklist[0] 

385 except (KeyError,TypeError,IndexError): 

386 self[name] = toklist 

387 

388 def __getitem__( self, i ): 

389 if isinstance( i, (int,slice) ): 

390 return self.__toklist[i] 

391 else: 

392 if i not in self.__accumNames: 

393 return self.__tokdict[i][-1][0] 

394 else: 

395 return ParseResults([ v[0] for v in self.__tokdict[i] ]) 

396 

397 def __setitem__( self, k, v, isinstance=isinstance ): 

398 if isinstance(v,_ParseResultsWithOffset): 

399 self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] 

400 sub = v[0] 

401 elif isinstance(k,(int,slice)): 

402 self.__toklist[k] = v 

403 sub = v 

404 else: 

405 self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] 

406 sub = v 

407 if isinstance(sub,ParseResults): 

408 sub.__parent = wkref(self) 

409 

410 def __delitem__( self, i ): 

411 if isinstance(i,(int,slice)): 

412 mylen = len( self.__toklist ) 

413 del self.__toklist[i] 

414 

415 # convert int to slice 

416 if isinstance(i, int): 

417 if i < 0: 

418 i += mylen 

419 i = slice(i, i+1) 

420 # get removed indices 

421 removed = list(range(*i.indices(mylen))) 

422 removed.reverse() 

423 # fixup indices in token dictionary 

424 for name,occurrences in self.__tokdict.items(): 

425 for j in removed: 

426 for k, (value, position) in enumerate(occurrences): 

427 occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) 

428 else: 

429 del self.__tokdict[i] 

430 

431 def __contains__( self, k ): 

432 return k in self.__tokdict 

433 

434 def __len__( self ): return len( self.__toklist ) 

435 def __bool__(self): return ( not not self.__toklist ) 

436 __nonzero__ = __bool__ 

437 def __iter__( self ): return iter( self.__toklist ) 

438 def __reversed__( self ): return iter( self.__toklist[::-1] ) 

439 def _iterkeys( self ): 

440 if hasattr(self.__tokdict, "iterkeys"): 

441 return self.__tokdict.iterkeys() 

442 else: 

443 return iter(self.__tokdict) 

444 

445 def _itervalues( self ): 

446 return (self[k] for k in self._iterkeys()) 

447 

448 def _iteritems( self ): 

449 return ((k, self[k]) for k in self._iterkeys()) 

450 

451 if PY_3: 

452 keys = _iterkeys 

453 """Returns an iterator of all named result keys (Python 3.x only).""" 

454 

455 values = _itervalues 

456 """Returns an iterator of all named result values (Python 3.x only).""" 

457 

458 items = _iteritems 

459 """Returns an iterator of all named result key-value tuples (Python 3.x only).""" 

460 

461 else: 

462 iterkeys = _iterkeys 

463 """Returns an iterator of all named result keys (Python 2.x only).""" 

464 

465 itervalues = _itervalues 

466 """Returns an iterator of all named result values (Python 2.x only).""" 

467 

468 iteritems = _iteritems 

469 """Returns an iterator of all named result key-value tuples (Python 2.x only).""" 

470 

471 def keys( self ): 

472 """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" 

473 return list(self.iterkeys()) 

474 

475 def values( self ): 

476 """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" 

477 return list(self.itervalues()) 

478 

479 def items( self ): 

480 """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" 

481 return list(self.iteritems()) 

482 

483 def haskeys( self ): 

484 """Since keys() returns an iterator, this method is helpful in bypassing 

485 code that looks for the existence of any defined results names.""" 

486 return bool(self.__tokdict) 

487 

488 def pop( self, *args, **kwargs): 

489 """ 

490 Removes and returns item at specified index (default=C{last}). 

491 Supports both C{list} and C{dict} semantics for C{pop()}. If passed no 

492 argument or an integer argument, it will use C{list} semantics 

493 and pop tokens from the list of parsed tokens. If passed a  

494 non-integer argument (most likely a string), it will use C{dict} 

495 semantics and pop the corresponding value from any defined  

496 results names. A second default return value argument is  

497 supported, just as in C{dict.pop()}. 

498 

499 Example:: 

500 def remove_first(tokens): 

501 tokens.pop(0) 

502 print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] 

503 print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] 

504 

505 label = Word(alphas) 

506 patt = label("LABEL") + OneOrMore(Word(nums)) 

507 print(patt.parseString("AAB 123 321").dump()) 

508 

509 # Use pop() in a parse action to remove named result (note that corresponding value is not 

510 # removed from list form of results) 

511 def remove_LABEL(tokens): 

512 tokens.pop("LABEL") 

513 return tokens 

514 patt.addParseAction(remove_LABEL) 

515 print(patt.parseString("AAB 123 321").dump()) 

516 prints:: 

517 ['AAB', '123', '321'] 

518 - LABEL: AAB 

519 

520 ['AAB', '123', '321'] 

521 """ 

522 if not args: 

523 args = [-1] 

524 for k,v in kwargs.items(): 

525 if k == 'default': 

526 args = (args[0], v) 

527 else: 

528 raise TypeError("pop() got an unexpected keyword argument '%s'" % k) 

529 if (isinstance(args[0], int) or 

530 len(args) == 1 or 

531 args[0] in self): 

532 index = args[0] 

533 ret = self[index] 

534 del self[index] 

535 return ret 

536 else: 

537 defaultvalue = args[1] 

538 return defaultvalue 

539 

540 def get(self, key, defaultValue=None): 

541 """ 

542 Returns named result matching the given key, or if there is no 

543 such name, then returns the given C{defaultValue} or C{None} if no 

544 C{defaultValue} is specified. 

545 

546 Similar to C{dict.get()}. 

547  

548 Example:: 

549 integer = Word(nums) 

550 date_str = integer("year") + '/' + integer("month") + '/' + integer("day")  

551 

552 result = date_str.parseString("1999/12/31") 

553 print(result.get("year")) # -> '1999' 

554 print(result.get("hour", "not specified")) # -> 'not specified' 

555 print(result.get("hour")) # -> None 

556 """ 

557 if key in self: 

558 return self[key] 

559 else: 

560 return defaultValue 

561 

562 def insert( self, index, insStr ): 

563 """ 

564 Inserts new element at location index in the list of parsed tokens. 

565  

566 Similar to C{list.insert()}. 

567 

568 Example:: 

569 print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] 

570 

571 # use a parse action to insert the parse location in the front of the parsed results 

572 def insert_locn(locn, tokens): 

573 tokens.insert(0, locn) 

574 print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] 

575 """ 

576 self.__toklist.insert(index, insStr) 

577 # fixup indices in token dictionary 

578 for name,occurrences in self.__tokdict.items(): 

579 for k, (value, position) in enumerate(occurrences): 

580 occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) 

581 

582 def append( self, item ): 

583 """ 

584 Add single element to end of ParseResults list of elements. 

585 

586 Example:: 

587 print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] 

588  

589 # use a parse action to compute the sum of the parsed integers, and add it to the end 

590 def append_sum(tokens): 

591 tokens.append(sum(map(int, tokens))) 

592 print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] 

593 """ 

594 self.__toklist.append(item) 

595 

596 def extend( self, itemseq ): 

597 """ 

598 Add sequence of elements to end of ParseResults list of elements. 

599 

600 Example:: 

601 patt = OneOrMore(Word(alphas)) 

602  

603 # use a parse action to append the reverse of the matched strings, to make a palindrome 

604 def make_palindrome(tokens): 

605 tokens.extend(reversed([t[::-1] for t in tokens])) 

606 return ''.join(tokens) 

607 print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' 

608 """ 

609 if isinstance(itemseq, ParseResults): 

610 self += itemseq 

611 else: 

612 self.__toklist.extend(itemseq) 

613 

614 def clear( self ): 

615 """ 

616 Clear all elements and results names. 

617 """ 

618 del self.__toklist[:] 

619 self.__tokdict.clear() 

620 

621 def __getattr__( self, name ): 

622 try: 

623 return self[name] 

624 except KeyError: 

625 return "" 

626 

627 if name in self.__tokdict: 

628 if name not in self.__accumNames: 

629 return self.__tokdict[name][-1][0] 

630 else: 

631 return ParseResults([ v[0] for v in self.__tokdict[name] ]) 

632 else: 

633 return "" 

634 

635 def __add__( self, other ): 

636 ret = self.copy() 

637 ret += other 

638 return ret 

639 

640 def __iadd__( self, other ): 

641 if other.__tokdict: 

642 offset = len(self.__toklist) 

643 addoffset = lambda a: offset if a<0 else a+offset 

644 otheritems = other.__tokdict.items() 

645 otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) 

646 for (k,vlist) in otheritems for v in vlist] 

647 for k,v in otherdictitems: 

648 self[k] = v 

649 if isinstance(v[0],ParseResults): 

650 v[0].__parent = wkref(self) 

651 

652 self.__toklist += other.__toklist 

653 self.__accumNames.update( other.__accumNames ) 

654 return self 

655 

656 def __radd__(self, other): 

657 if isinstance(other,int) and other == 0: 

658 # useful for merging many ParseResults using sum() builtin 

659 return self.copy() 

660 else: 

661 # this may raise a TypeError - so be it 

662 return other + self 

663 

664 def __repr__( self ): 

665 return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) 

666 

667 def __str__( self ): 

668 return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']' 

669 

670 def _asStringList( self, sep='' ): 

671 out = [] 

672 for item in self.__toklist: 

673 if out and sep: 

674 out.append(sep) 

675 if isinstance( item, ParseResults ): 

676 out += item._asStringList() 

677 else: 

678 out.append( _ustr(item) ) 

679 return out 

680 

681 def asList( self ): 

682 """ 

683 Returns the parse results as a nested list of matching tokens, all converted to strings. 

684 

685 Example:: 

686 patt = OneOrMore(Word(alphas)) 

687 result = patt.parseString("sldkj lsdkj sldkj") 

688 # even though the result prints in string-like form, it is actually a pyparsing ParseResults 

689 print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] 

690  

691 # Use asList() to create an actual list 

692 result_list = result.asList() 

693 print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] 

694 """ 

695 return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] 

696 

697 def asDict( self ): 

698 """ 

699 Returns the named parse results as a nested dictionary. 

700 

701 Example:: 

702 integer = Word(nums) 

703 date_str = integer("year") + '/' + integer("month") + '/' + integer("day") 

704  

705 result = date_str.parseString('12/31/1999') 

706 print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) 

707  

708 result_dict = result.asDict() 

709 print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} 

710 

711 # even though a ParseResults supports dict-like access, sometime you just need to have a dict 

712 import json 

713 print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable 

714 print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} 

715 """ 

716 if PY_3: 

717 item_fn = self.items 

718 else: 

719 item_fn = self.iteritems 

720 

721 def toItem(obj): 

722 if isinstance(obj, ParseResults): 

723 if obj.haskeys(): 

724 return obj.asDict() 

725 else: 

726 return [toItem(v) for v in obj] 

727 else: 

728 return obj 

729 

730 return dict((k,toItem(v)) for k,v in item_fn()) 

731 

732 def copy( self ): 

733 """ 

734 Returns a new copy of a C{ParseResults} object. 

735 """ 

736 ret = ParseResults( self.__toklist ) 

737 ret.__tokdict = self.__tokdict.copy() 

738 ret.__parent = self.__parent 

739 ret.__accumNames.update( self.__accumNames ) 

740 ret.__name = self.__name 

741 return ret 

742 

743 def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): 

744 """ 

745 (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names. 

746 """ 

747 nl = "\n" 

748 out = [] 

749 namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items() 

750 for v in vlist) 

751 nextLevelIndent = indent + " " 

752 

753 # collapse out indents if formatting is not desired 

754 if not formatted: 

755 indent = "" 

756 nextLevelIndent = "" 

757 nl = "" 

758 

759 selfTag = None 

760 if doctag is not None: 

761 selfTag = doctag 

762 else: 

763 if self.__name: 

764 selfTag = self.__name 

765 

766 if not selfTag: 

767 if namedItemsOnly: 

768 return "" 

769 else: 

770 selfTag = "ITEM" 

771 

772 out += [ nl, indent, "<", selfTag, ">" ] 

773 

774 for i,res in enumerate(self.__toklist): 

775 if isinstance(res,ParseResults): 

776 if i in namedItems: 

777 out += [ res.asXML(namedItems[i], 

778 namedItemsOnly and doctag is None, 

779 nextLevelIndent, 

780 formatted)] 

781 else: 

782 out += [ res.asXML(None, 

783 namedItemsOnly and doctag is None, 

784 nextLevelIndent, 

785 formatted)] 

786 else: 

787 # individual token, see if there is a name for it 

788 resTag = None 

789 if i in namedItems: 

790 resTag = namedItems[i] 

791 if not resTag: 

792 if namedItemsOnly: 

793 continue 

794 else: 

795 resTag = "ITEM" 

796 xmlBodyText = _xml_escape(_ustr(res)) 

797 out += [ nl, nextLevelIndent, "<", resTag, ">", 

798 xmlBodyText, 

799 "</", resTag, ">" ] 

800 

801 out += [ nl, indent, "</", selfTag, ">" ] 

802 return "".join(out) 

803 

804 def __lookup(self,sub): 

805 for k,vlist in self.__tokdict.items(): 

806 for v,loc in vlist: 

807 if sub is v: 

808 return k 

809 return None 

810 

811 def getName(self): 

812 r""" 

813 Returns the results name for this token expression. Useful when several  

814 different expressions might match at a particular location. 

815 

816 Example:: 

817 integer = Word(nums) 

818 ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") 

819 house_number_expr = Suppress('#') + Word(nums, alphanums) 

820 user_data = (Group(house_number_expr)("house_number")  

821 | Group(ssn_expr)("ssn") 

822 | Group(integer)("age")) 

823 user_info = OneOrMore(user_data) 

824  

825 result = user_info.parseString("22 111-22-3333 #221B") 

826 for item in result: 

827 print(item.getName(), ':', item[0]) 

828 prints:: 

829 age : 22 

830 ssn : 111-22-3333 

831 house_number : 221B 

832 """ 

833 if self.__name: 

834 return self.__name 

835 elif self.__parent: 

836 par = self.__parent() 

837 if par: 

838 return par.__lookup(self) 

839 else: 

840 return None 

841 elif (len(self) == 1 and 

842 len(self.__tokdict) == 1 and 

843 next(iter(self.__tokdict.values()))[0][1] in (0,-1)): 

844 return next(iter(self.__tokdict.keys())) 

845 else: 

846 return None 

847 

848 def dump(self, indent='', depth=0, full=True): 

849 """ 

850 Diagnostic method for listing out the contents of a C{ParseResults}. 

851 Accepts an optional C{indent} argument so that this string can be embedded 

852 in a nested display of other data. 

853 

854 Example:: 

855 integer = Word(nums) 

856 date_str = integer("year") + '/' + integer("month") + '/' + integer("day") 

857  

858 result = date_str.parseString('12/31/1999') 

859 print(result.dump()) 

860 prints:: 

861 ['12', '/', '31', '/', '1999'] 

862 - day: 1999 

863 - month: 31 

864 - year: 12 

865 """ 

866 out = [] 

867 NL = '\n' 

868 out.append( indent+_ustr(self.asList()) ) 

869 if full: 

870 if self.haskeys(): 

871 items = sorted((str(k), v) for k,v in self.items()) 

872 for k,v in items: 

873 if out: 

874 out.append(NL) 

875 out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) 

876 if isinstance(v,ParseResults): 

877 if v: 

878 out.append( v.dump(indent,depth+1) ) 

879 else: 

880 out.append(_ustr(v)) 

881 else: 

882 out.append(repr(v)) 

883 elif any(isinstance(vv,ParseResults) for vv in self): 

884 v = self 

885 for i,vv in enumerate(v): 

886 if isinstance(vv,ParseResults): 

887 out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) 

888 else: 

889 out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) 

890 

891 return "".join(out) 

892 

893 def pprint(self, *args, **kwargs): 

894 """ 

895 Pretty-printer for parsed results as a list, using the C{pprint} module. 

896 Accepts additional positional or keyword args as defined for the  

897 C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint}) 

898 

899 Example:: 

900 ident = Word(alphas, alphanums) 

901 num = Word(nums) 

902 func = Forward() 

903 term = ident | num | Group('(' + func + ')') 

904 func <<= ident + Group(Optional(delimitedList(term))) 

905 result = func.parseString("fna a,b,(fnb c,d,200),100") 

906 result.pprint(width=40) 

907 prints:: 

908 ['fna', 

909 ['a', 

910 'b', 

911 ['(', 'fnb', ['c', 'd', '200'], ')'], 

912 '100']] 

913 """ 

914 pprint.pprint(self.asList(), *args, **kwargs) 

915 

916 # add support for pickle protocol 

917 def __getstate__(self): 

918 return ( self.__toklist, 

919 ( self.__tokdict.copy(), 

920 self.__parent is not None and self.__parent() or None, 

921 self.__accumNames, 

922 self.__name ) ) 

923 

924 def __setstate__(self,state): 

925 self.__toklist = state[0] 

926 (self.__tokdict, 

927 par, 

928 inAccumNames, 

929 self.__name) = state[1] 

930 self.__accumNames = {} 

931 self.__accumNames.update(inAccumNames) 

932 if par is not None: 

933 self.__parent = wkref(par) 

934 else: 

935 self.__parent = None 

936 

937 def __getnewargs__(self): 

938 return self.__toklist, self.__name, self.__asList, self.__modal 

939 

940 def __dir__(self): 

941 return (dir(type(self)) + list(self.keys())) 

942 

943collections.MutableMapping.register(ParseResults) 

944 

945def col (loc,strg): 

946 """Returns current column within a string, counting newlines as line separators. 

947 The first column is number 1. 

948 

949 Note: the default parsing behavior is to expand tabs in the input string 

950 before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information 

951 on parsing strings containing C{<TAB>}s, and suggested methods to maintain a 

952 consistent view of the parsed string, the parse location, and line and column 

953 positions within the parsed string. 

954 """ 

955 s = strg 

956 return 1 if 0<loc<len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc) 

957 

958def lineno(loc,strg): 

959 """Returns current line number within a string, counting newlines as line separators. 

960 The first line is number 1. 

961 

962 Note: the default parsing behavior is to expand tabs in the input string 

963 before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information 

964 on parsing strings containing C{<TAB>}s, and suggested methods to maintain a 

965 consistent view of the parsed string, the parse location, and line and column 

966 positions within the parsed string. 

967 """ 

968 return strg.count("\n",0,loc) + 1 

969 

970def line( loc, strg ): 

971 """Returns the line of text containing loc within a string, counting newlines as line separators. 

972 """ 

973 lastCR = strg.rfind("\n", 0, loc) 

974 nextCR = strg.find("\n", loc) 

975 if nextCR >= 0: 

976 return strg[lastCR+1:nextCR] 

977 else: 

978 return strg[lastCR+1:] 

979 

980def _defaultStartDebugAction( instring, loc, expr ): 

981 print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))) 

982 

983def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): 

984 print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) 

985 

986def _defaultExceptionDebugAction( instring, loc, expr, exc ): 

987 print ("Exception raised:" + _ustr(exc)) 

988 

989def nullDebugAction(*args): 

990 """'Do-nothing' debug action, to suppress debugging output during parsing.""" 

991 pass 

992 

993# Only works on Python 3.x - nonlocal is toxic to Python 2 installs 

994#~ 'decorator to trim function calls to match the arity of the target' 

995#~ def _trim_arity(func, maxargs=3): 

996 #~ if func in singleArgBuiltins: 

997 #~ return lambda s,l,t: func(t) 

998 #~ limit = 0 

999 #~ foundArity = False 

1000 #~ def wrapper(*args): 

1001 #~ nonlocal limit,foundArity 

1002 #~ while 1: 

1003 #~ try: 

1004 #~ ret = func(*args[limit:]) 

1005 #~ foundArity = True 

1006 #~ return ret 

1007 #~ except TypeError: 

1008 #~ if limit == maxargs or foundArity: 

1009 #~ raise 

1010 #~ limit += 1 

1011 #~ continue 

1012 #~ return wrapper 

1013 

1014# this version is Python 2.x-3.x cross-compatible 

1015'decorator to trim function calls to match the arity of the target' 

1016def _trim_arity(func, maxargs=2): 

1017 if func in singleArgBuiltins: 

1018 return lambda s,l,t: func(t) 

1019 limit = [0] 

1020 foundArity = [False] 

1021 

1022 # traceback return data structure changed in Py3.5 - normalize back to plain tuples 

1023 if system_version[:2] >= (3,5): 

1024 def extract_stack(limit=0): 

1025 # special handling for Python 3.5.0 - extra deep call stack by 1 

1026 offset = -3 if system_version == (3,5,0) else -2 

1027 frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] 

1028 return [(frame_summary.filename, frame_summary.lineno)] 

1029 def extract_tb(tb, limit=0): 

1030 frames = traceback.extract_tb(tb, limit=limit) 

1031 frame_summary = frames[-1] 

1032 return [(frame_summary.filename, frame_summary.lineno)] 

1033 else: 

1034 extract_stack = traceback.extract_stack 

1035 extract_tb = traceback.extract_tb 

1036 

1037 # synthesize what would be returned by traceback.extract_stack at the call to  

1038 # user's parse action 'func', so that we don't incur call penalty at parse time 

1039 

1040 LINE_DIFF = 6 

1041 # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND  

1042 # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! 

1043 this_line = extract_stack(limit=2)[-1] 

1044 pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) 

1045 

1046 def wrapper(*args): 

1047 while 1: 

1048 try: 

1049 ret = func(*args[limit[0]:]) 

1050 foundArity[0] = True 

1051 return ret 

1052 except TypeError: 

1053 # re-raise TypeErrors if they did not come from our arity testing 

1054 if foundArity[0]: 

1055 raise 

1056 else: 

1057 try: 

1058 tb = sys.exc_info()[-1] 

1059 if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: 

1060 raise 

1061 finally: 

1062 del tb 

1063 

1064 if limit[0] <= maxargs: 

1065 limit[0] += 1 

1066 continue 

1067 raise 

1068 

1069 # copy func name to wrapper for sensible debug output 

1070 func_name = "<parse action>" 

1071 try: 

1072 func_name = getattr(func, '__name__', 

1073 getattr(func, '__class__').__name__) 

1074 except Exception: 

1075 func_name = str(func) 

1076 wrapper.__name__ = func_name 

1077 

1078 return wrapper 

1079 

1080class ParserElement(object): 

1081 """Abstract base level parser element class.""" 

1082 DEFAULT_WHITE_CHARS = " \n\t\r" 

1083 verbose_stacktrace = False 

1084 

1085 @staticmethod 

1086 def setDefaultWhitespaceChars( chars ): 

1087 r""" 

1088 Overrides the default whitespace chars 

1089 

1090 Example:: 

1091 # default whitespace chars are space, <TAB> and newline 

1092 OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] 

1093  

1094 # change to just treat newline as significant 

1095 ParserElement.setDefaultWhitespaceChars(" \t") 

1096 OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] 

1097 """ 

1098 ParserElement.DEFAULT_WHITE_CHARS = chars 

1099 

1100 @staticmethod 

1101 def inlineLiteralsUsing(cls): 

1102 """ 

1103 Set class to be used for inclusion of string literals into a parser. 

1104  

1105 Example:: 

1106 # default literal class used is Literal 

1107 integer = Word(nums) 

1108 date_str = integer("year") + '/' + integer("month") + '/' + integer("day")  

1109 

1110 date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] 

1111 

1112 

1113 # change to Suppress 

1114 ParserElement.inlineLiteralsUsing(Suppress) 

1115 date_str = integer("year") + '/' + integer("month") + '/' + integer("day")  

1116 

1117 date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] 

1118 """ 

1119 ParserElement._literalStringClass = cls 

1120 

1121 def __init__( self, savelist=False ): 

1122 self.parseAction = list() 

1123 self.failAction = None 

1124 #~ self.name = "<unknown>" # don't define self.name, let subclasses try/except upcall 

1125 self.strRepr = None 

1126 self.resultsName = None 

1127 self.saveAsList = savelist 

1128 self.skipWhitespace = True 

1129 self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS 

1130 self.copyDefaultWhiteChars = True 

1131 self.mayReturnEmpty = False # used when checking for left-recursion 

1132 self.keepTabs = False 

1133 self.ignoreExprs = list() 

1134 self.debug = False 

1135 self.streamlined = False 

1136 self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index 

1137 self.errmsg = "" 

1138 self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) 

1139 self.debugActions = ( None, None, None ) #custom debug actions 

1140 self.re = None 

1141 self.callPreparse = True # used to avoid redundant calls to preParse 

1142 self.callDuringTry = False 

1143 

1144 def copy( self ): 

1145 """ 

1146 Make a copy of this C{ParserElement}. Useful for defining different parse actions 

1147 for the same parsing pattern, using copies of the original parse element. 

1148  

1149 Example:: 

1150 integer = Word(nums).setParseAction(lambda toks: int(toks[0])) 

1151 integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") 

1152 integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") 

1153  

1154 print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) 

1155 prints:: 

1156 [5120, 100, 655360, 268435456] 

1157 Equivalent form of C{expr.copy()} is just C{expr()}:: 

1158 integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") 

1159 """ 

1160 cpy = copy.copy( self ) 

1161 cpy.parseAction = self.parseAction[:] 

1162 cpy.ignoreExprs = self.ignoreExprs[:] 

1163 if self.copyDefaultWhiteChars: 

1164 cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS 

1165 return cpy 

1166 

1167 def setName( self, name ): 

1168 """ 

1169 Define name for this expression, makes debugging and exception messages clearer. 

1170  

1171 Example:: 

1172 Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) 

1173 Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) 

1174 """ 

1175 self.name = name 

1176 self.errmsg = "Expected " + self.name 

1177 if hasattr(self,"exception"): 

1178 self.exception.msg = self.errmsg 

1179 return self 

1180 

1181 def setResultsName( self, name, listAllMatches=False ): 

1182 """ 

1183 Define name for referencing matching tokens as a nested attribute 

1184 of the returned parse results. 

1185 NOTE: this returns a *copy* of the original C{ParserElement} object; 

1186 this is so that the client can define a basic element, such as an 

1187 integer, and reference it in multiple places with different names. 

1188 

1189 You can also set results names using the abbreviated syntax, 

1190 C{expr("name")} in place of C{expr.setResultsName("name")} -  

1191 see L{I{__call__}<__call__>}. 

1192 

1193 Example:: 

1194 date_str = (integer.setResultsName("year") + '/'  

1195 + integer.setResultsName("month") + '/'  

1196 + integer.setResultsName("day")) 

1197 

1198 # equivalent form: 

1199 date_str = integer("year") + '/' + integer("month") + '/' + integer("day") 

1200 """ 

1201 newself = self.copy() 

1202 if name.endswith("*"): 

1203 name = name[:-1] 

1204 listAllMatches=True 

1205 newself.resultsName = name 

1206 newself.modalResults = not listAllMatches 

1207 return newself 

1208 

1209 def setBreak(self,breakFlag = True): 

1210 """Method to invoke the Python pdb debugger when this element is 

1211 about to be parsed. Set C{breakFlag} to True to enable, False to 

1212 disable. 

1213 """ 

1214 if breakFlag: 

1215 _parseMethod = self._parse 

1216 def breaker(instring, loc, doActions=True, callPreParse=True): 

1217 import pdb 

1218 pdb.set_trace() 

1219 return _parseMethod( instring, loc, doActions, callPreParse ) 

1220 breaker._originalParseMethod = _parseMethod 

1221 self._parse = breaker 

1222 else: 

1223 if hasattr(self._parse,"_originalParseMethod"): 

1224 self._parse = self._parse._originalParseMethod 

1225 return self 

1226 

1227 def setParseAction( self, *fns, **kwargs ): 

1228 """ 

1229 Define action to perform when successfully matching parse element definition. 

1230 Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, 

1231 C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: 

1232 - s = the original string being parsed (see note below) 

1233 - loc = the location of the matching substring 

1234 - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object 

1235 If the functions in fns modify the tokens, they can return them as the return 

1236 value from fn, and the modified list of tokens will replace the original. 

1237 Otherwise, fn does not need to return any value. 

1238 

1239 Optional keyword arguments: 

1240 - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing 

1241 

1242 Note: the default parsing behavior is to expand tabs in the input string 

1243 before starting the parsing process. See L{I{parseString}<parseString>} for more information 

1244 on parsing strings containing C{<TAB>}s, and suggested methods to maintain a 

1245 consistent view of the parsed string, the parse location, and line and column 

1246 positions within the parsed string. 

1247  

1248 Example:: 

1249 integer = Word(nums) 

1250 date_str = integer + '/' + integer + '/' + integer 

1251 

1252 date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] 

1253 

1254 # use parse action to convert to ints at parse time 

1255 integer = Word(nums).setParseAction(lambda toks: int(toks[0])) 

1256 date_str = integer + '/' + integer + '/' + integer 

1257 

1258 # note that integer fields are now ints, not strings 

1259 date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] 

1260 """ 

1261 self.parseAction = list(map(_trim_arity, list(fns))) 

1262 self.callDuringTry = kwargs.get("callDuringTry", False) 

1263 return self 

1264 

1265 def addParseAction( self, *fns, **kwargs ): 

1266 """ 

1267 Add parse action to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}. 

1268  

1269 See examples in L{I{copy}<copy>}. 

1270 """ 

1271 self.parseAction += list(map(_trim_arity, list(fns))) 

1272 self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) 

1273 return self 

1274 

1275 def addCondition(self, *fns, **kwargs): 

1276 """Add a boolean predicate function to expression's list of parse actions. See  

1277 L{I{setParseAction}<setParseAction>} for function call signatures. Unlike C{setParseAction},  

1278 functions passed to C{addCondition} need to return boolean success/fail of the condition. 

1279 

1280 Optional keyword arguments: 

1281 - message = define a custom message to be used in the raised exception 

1282 - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException 

1283  

1284 Example:: 

1285 integer = Word(nums).setParseAction(lambda toks: int(toks[0])) 

1286 year_int = integer.copy() 

1287 year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") 

1288 date_str = year_int + '/' + integer + '/' + integer 

1289 

1290 result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) 

1291 """ 

1292 msg = kwargs.get("message", "failed user-defined condition") 

1293 exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException 

1294 for fn in fns: 

1295 def pa(s,l,t): 

1296 if not bool(_trim_arity(fn)(s,l,t)): 

1297 raise exc_type(s,l,msg) 

1298 self.parseAction.append(pa) 

1299 self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) 

1300 return self 

1301 

1302 def setFailAction( self, fn ): 

1303 """Define action to perform if parsing fails at this expression. 

1304 Fail acton fn is a callable function that takes the arguments 

1305 C{fn(s,loc,expr,err)} where: 

1306 - s = string being parsed 

1307 - loc = location where expression match was attempted and failed 

1308 - expr = the parse expression that failed 

1309 - err = the exception thrown 

1310 The function returns no value. It may throw C{L{ParseFatalException}} 

1311 if it is desired to stop parsing immediately.""" 

1312 self.failAction = fn 

1313 return self 

1314 

1315 def _skipIgnorables( self, instring, loc ): 

1316 exprsFound = True 

1317 while exprsFound: 

1318 exprsFound = False 

1319 for e in self.ignoreExprs: 

1320 try: 

1321 while 1: 

1322 loc,dummy = e._parse( instring, loc ) 

1323 exprsFound = True 

1324 except ParseException: 

1325 pass 

1326 return loc 

1327 

1328 def preParse( self, instring, loc ): 

1329 if self.ignoreExprs: 

1330 loc = self._skipIgnorables( instring, loc ) 

1331 

1332 if self.skipWhitespace: 

1333 wt = self.whiteChars 

1334 instrlen = len(instring) 

1335 while loc < instrlen and instring[loc] in wt: 

1336 loc += 1 

1337 

1338 return loc 

1339 

1340 def parseImpl( self, instring, loc, doActions=True ): 

1341 return loc, [] 

1342 

1343 def postParse( self, instring, loc, tokenlist ): 

1344 return tokenlist 

1345 

1346 #~ @profile 

1347 def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): 

1348 debugging = ( self.debug ) #and doActions ) 

1349 

1350 if debugging or self.failAction: 

1351 #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) 

1352 if (self.debugActions[0] ): 

1353 self.debugActions[0]( instring, loc, self ) 

1354 if callPreParse and self.callPreparse: 

1355 preloc = self.preParse( instring, loc ) 

1356 else: 

1357 preloc = loc 

1358 tokensStart = preloc 

1359 try: 

1360 try: 

1361 loc,tokens = self.parseImpl( instring, preloc, doActions ) 

1362 except IndexError: 

1363 raise ParseException( instring, len(instring), self.errmsg, self ) 

1364 except ParseBaseException as err: 

1365 #~ print ("Exception raised:", err) 

1366 if self.debugActions[2]: 

1367 self.debugActions[2]( instring, tokensStart, self, err ) 

1368 if self.failAction: 

1369 self.failAction( instring, tokensStart, self, err ) 

1370 raise 

1371 else: 

1372 if callPreParse and self.callPreparse: 

1373 preloc = self.preParse( instring, loc ) 

1374 else: 

1375 preloc = loc 

1376 tokensStart = preloc 

1377 if self.mayIndexError or loc >= len(instring): 

1378 try: 

1379 loc,tokens = self.parseImpl( instring, preloc, doActions ) 

1380 except IndexError: 

1381 raise ParseException( instring, len(instring), self.errmsg, self ) 

1382 else: 

1383 loc,tokens = self.parseImpl( instring, preloc, doActions ) 

1384 

1385 tokens = self.postParse( instring, loc, tokens ) 

1386 

1387 retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) 

1388 if self.parseAction and (doActions or self.callDuringTry): 

1389 if debugging: 

1390 try: 

1391 for fn in self.parseAction: 

1392 tokens = fn( instring, tokensStart, retTokens ) 

1393 if tokens is not None: 

1394 retTokens = ParseResults( tokens, 

1395 self.resultsName, 

1396 asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), 

1397 modal=self.modalResults ) 

1398 except ParseBaseException as err: 

1399 #~ print "Exception raised in user parse action:", err 

1400 if (self.debugActions[2] ): 

1401 self.debugActions[2]( instring, tokensStart, self, err ) 

1402 raise 

1403 else: 

1404 for fn in self.parseAction: 

1405 tokens = fn( instring, tokensStart, retTokens ) 

1406 if tokens is not None: 

1407 retTokens = ParseResults( tokens, 

1408 self.resultsName, 

1409 asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), 

1410 modal=self.modalResults ) 

1411 

1412 if debugging: 

1413 #~ print ("Matched",self,"->",retTokens.asList()) 

1414 if (self.debugActions[1] ): 

1415 self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) 

1416 

1417 return loc, retTokens 

1418 

1419 def tryParse( self, instring, loc ): 

1420 try: 

1421 return self._parse( instring, loc, doActions=False )[0] 

1422 except ParseFatalException: 

1423 raise ParseException( instring, loc, self.errmsg, self) 

1424 

1425 def canParseNext(self, instring, loc): 

1426 try: 

1427 self.tryParse(instring, loc) 

1428 except (ParseException, IndexError): 

1429 return False 

1430 else: 

1431 return True 

1432 

1433 class _UnboundedCache(object): 

1434 def __init__(self): 

1435 cache = {} 

1436 self.not_in_cache = not_in_cache = object() 

1437 

1438 def get(self, key): 

1439 return cache.get(key, not_in_cache) 

1440 

1441 def set(self, key, value): 

1442 cache[key] = value 

1443 

1444 def clear(self): 

1445 cache.clear() 

1446 

1447 self.get = types.MethodType(get, self) 

1448 self.set = types.MethodType(set, self) 

1449 self.clear = types.MethodType(clear, self) 

1450 

1451 if _OrderedDict is not None: 

1452 class _FifoCache(object): 

1453 def __init__(self, size): 

1454 self.not_in_cache = not_in_cache = object() 

1455 

1456 cache = _OrderedDict() 

1457 

1458 def get(self, key): 

1459 return cache.get(key, not_in_cache) 

1460 

1461 def set(self, key, value): 

1462 cache[key] = value 

1463 if len(cache) > size: 

1464 cache.popitem(False) 

1465 

1466 def clear(self): 

1467 cache.clear() 

1468 

1469 self.get = types.MethodType(get, self) 

1470 self.set = types.MethodType(set, self) 

1471 self.clear = types.MethodType(clear, self) 

1472 

1473 else: 

1474 class _FifoCache(object): 

1475 def __init__(self, size): 

1476 self.not_in_cache = not_in_cache = object() 

1477 

1478 cache = {} 

1479 key_fifo = collections.deque([], size) 

1480 

1481 def get(self, key): 

1482 return cache.get(key, not_in_cache) 

1483 

1484 def set(self, key, value): 

1485 cache[key] = value 

1486 if len(cache) > size: 

1487 cache.pop(key_fifo.popleft(), None) 

1488 key_fifo.append(key) 

1489 

1490 def clear(self): 

1491 cache.clear() 

1492 key_fifo.clear() 

1493 

1494 self.get = types.MethodType(get, self) 

1495 self.set = types.MethodType(set, self) 

1496 self.clear = types.MethodType(clear, self) 

1497 

1498 # argument cache for optimizing repeated calls when backtracking through recursive expressions 

1499 packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail 

1500 packrat_cache_lock = RLock() 

1501 packrat_cache_stats = [0, 0] 

1502 

1503 # this method gets repeatedly called during backtracking with the same arguments - 

1504 # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression 

1505 def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): 

1506 HIT, MISS = 0, 1 

1507 lookup = (self, instring, loc, callPreParse, doActions) 

1508 with ParserElement.packrat_cache_lock: 

1509 cache = ParserElement.packrat_cache 

1510 value = cache.get(lookup) 

1511 if value is cache.not_in_cache: 

1512 ParserElement.packrat_cache_stats[MISS] += 1 

1513 try: 

1514 value = self._parseNoCache(instring, loc, doActions, callPreParse) 

1515 except ParseBaseException as pe: 

1516 # cache a copy of the exception, without the traceback 

1517 cache.set(lookup, pe.__class__(*pe.args)) 

1518 raise 

1519 else: 

1520 cache.set(lookup, (value[0], value[1].copy())) 

1521 return value 

1522 else: 

1523 ParserElement.packrat_cache_stats[HIT] += 1 

1524 if isinstance(value, Exception): 

1525 raise value 

1526 return (value[0], value[1].copy()) 

1527 

1528 _parse = _parseNoCache 

1529 

1530 @staticmethod 

1531 def resetCache(): 

1532 ParserElement.packrat_cache.clear() 

1533 ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) 

1534 

1535 _packratEnabled = False 

1536 @staticmethod 

1537 def enablePackrat(cache_size_limit=128): 

1538 """Enables "packrat" parsing, which adds memoizing to the parsing logic. 

1539 Repeated parse attempts at the same string location (which happens 

1540 often in many complex grammars) can immediately return a cached value, 

1541 instead of re-executing parsing/validating code. Memoizing is done of 

1542 both valid results and parsing exceptions. 

1543  

1544 Parameters: 

1545 - cache_size_limit - (default=C{128}) - if an integer value is provided 

1546 will limit the size of the packrat cache; if None is passed, then 

1547 the cache size will be unbounded; if 0 is passed, the cache will 

1548 be effectively disabled. 

1549  

1550 This speedup may break existing programs that use parse actions that 

1551 have side-effects. For this reason, packrat parsing is disabled when 

1552 you first import pyparsing. To activate the packrat feature, your 

1553 program must call the class method C{ParserElement.enablePackrat()}. If 

1554 your program uses C{psyco} to "compile as you go", you must call 

1555 C{enablePackrat} before calling C{psyco.full()}. If you do not do this, 

1556 Python will crash. For best results, call C{enablePackrat()} immediately 

1557 after importing pyparsing. 

1558  

1559 Example:: 

1560 import pyparsing 

1561 pyparsing.ParserElement.enablePackrat() 

1562 """ 

1563 if not ParserElement._packratEnabled: 

1564 ParserElement._packratEnabled = True 

1565 if cache_size_limit is None: 

1566 ParserElement.packrat_cache = ParserElement._UnboundedCache() 

1567 else: 

1568 ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) 

1569 ParserElement._parse = ParserElement._parseCache 

1570 

1571 def parseString( self, instring, parseAll=False ): 

1572 """ 

1573 Execute the parse expression with the given string. 

1574 This is the main interface to the client code, once the complete 

1575 expression has been built. 

1576 

1577 If you want the grammar to require that the entire input string be 

1578 successfully parsed, then set C{parseAll} to True (equivalent to ending 

1579 the grammar with C{L{StringEnd()}}). 

1580 

1581 Note: C{parseString} implicitly calls C{expandtabs()} on the input string, 

1582 in order to report proper column numbers in parse actions. 

1583 If the input string contains tabs and 

1584 the grammar uses parse actions that use the C{loc} argument to index into the 

1585 string being parsed, you can ensure you have a consistent view of the input 

1586 string by: 

1587 - calling C{parseWithTabs} on your grammar before calling C{parseString} 

1588 (see L{I{parseWithTabs}<parseWithTabs>}) 

1589 - define your parse action using the full C{(s,loc,toks)} signature, and 

1590 reference the input string using the parse action's C{s} argument 

1591 - explictly expand the tabs in your input string before calling 

1592 C{parseString} 

1593  

1594 Example:: 

1595 Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] 

1596 Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text 

1597 """ 

1598 ParserElement.resetCache() 

1599 if not self.streamlined: 

1600 self.streamline() 

1601 #~ self.saveAsList = True 

1602 for e in self.ignoreExprs: 

1603 e.streamline() 

1604 if not self.keepTabs: 

1605 instring = instring.expandtabs() 

1606 try: 

1607 loc, tokens = self._parse( instring, 0 ) 

1608 if parseAll: 

1609 loc = self.preParse( instring, loc ) 

1610 se = Empty() + StringEnd() 

1611 se._parse( instring, loc ) 

1612 except ParseBaseException as exc: 

1613 if ParserElement.verbose_stacktrace: 

1614 raise 

1615 else: 

1616 # catch and re-raise exception from here, clears out pyparsing internal stack trace 

1617 raise exc 

1618 else: 

1619 return tokens 

1620 

1621 def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): 

1622 """ 

1623 Scan the input string for expression matches. Each match will return the 

1624 matching tokens, start location, and end location. May be called with optional 

1625 C{maxMatches} argument, to clip scanning after 'n' matches are found. If 

1626 C{overlap} is specified, then overlapping matches will be reported. 

1627 

1628 Note that the start and end locations are reported relative to the string 

1629 being parsed. See L{I{parseString}<parseString>} for more information on parsing 

1630 strings with embedded tabs. 

1631 

1632 Example:: 

1633 source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" 

1634 print(source) 

1635 for tokens,start,end in Word(alphas).scanString(source): 

1636 print(' '*start + '^'*(end-start)) 

1637 print(' '*start + tokens[0]) 

1638  

1639 prints:: 

1640  

1641 sldjf123lsdjjkf345sldkjf879lkjsfd987 

1642 ^^^^^ 

1643 sldjf 

1644 ^^^^^^^ 

1645 lsdjjkf 

1646 ^^^^^^ 

1647 sldkjf 

1648 ^^^^^^ 

1649 lkjsfd 

1650 """ 

1651 if not self.streamlined: 

1652 self.streamline() 

1653 for e in self.ignoreExprs: 

1654 e.streamline() 

1655 

1656 if not self.keepTabs: 

1657 instring = _ustr(instring).expandtabs() 

1658 instrlen = len(instring) 

1659 loc = 0 

1660 preparseFn = self.preParse 

1661 parseFn = self._parse 

1662 ParserElement.resetCache() 

1663 matches = 0 

1664 try: 

1665 while loc <= instrlen and matches < maxMatches: 

1666 try: 

1667 preloc = preparseFn( instring, loc ) 

1668 nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) 

1669 except ParseException: 

1670 loc = preloc+1 

1671 else: 

1672 if nextLoc > loc: 

1673 matches += 1 

1674 yield tokens, preloc, nextLoc 

1675 if overlap: 

1676 nextloc = preparseFn( instring, loc ) 

1677 if nextloc > loc: 

1678 loc = nextLoc 

1679 else: 

1680 loc += 1 

1681 else: 

1682 loc = nextLoc 

1683 else: 

1684 loc = preloc+1 

1685 except ParseBaseException as exc: 

1686 if ParserElement.verbose_stacktrace: 

1687 raise 

1688 else: 

1689 # catch and re-raise exception from here, clears out pyparsing internal stack trace 

1690 raise exc 

1691 

1692 def transformString( self, instring ): 

1693 """ 

1694 Extension to C{L{scanString}}, to modify matching text with modified tokens that may 

1695 be returned from a parse action. To use C{transformString}, define a grammar and 

1696 attach a parse action to it that modifies the returned token list. 

1697 Invoking C{transformString()} on a target string will then scan for matches, 

1698 and replace the matched text patterns according to the logic in the parse 

1699 action. C{transformString()} returns the resulting transformed string. 

1700  

1701 Example:: 

1702 wd = Word(alphas) 

1703 wd.setParseAction(lambda toks: toks[0].title()) 

1704  

1705 print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) 

1706 Prints:: 

1707 Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. 

1708 """ 

1709 out = [] 

1710 lastE = 0 

1711 # force preservation of <TAB>s, to minimize unwanted transformation of string, and to 

1712 # keep string locs straight between transformString and scanString 

1713 self.keepTabs = True 

1714 try: 

1715 for t,s,e in self.scanString( instring ): 

1716 out.append( instring[lastE:s] ) 

1717 if t: 

1718 if isinstance(t,ParseResults): 

1719 out += t.asList() 

1720 elif isinstance(t,list): 

1721 out += t 

1722 else: 

1723 out.append(t) 

1724 lastE = e 

1725 out.append(instring[lastE:]) 

1726 out = [o for o in out if o] 

1727 return "".join(map(_ustr,_flatten(out))) 

1728 except ParseBaseException as exc: 

1729 if ParserElement.verbose_stacktrace: 

1730 raise 

1731 else: 

1732 # catch and re-raise exception from here, clears out pyparsing internal stack trace 

1733 raise exc 

1734 

1735 def searchString( self, instring, maxMatches=_MAX_INT ): 

1736 """ 

1737 Another extension to C{L{scanString}}, simplifying the access to the tokens found 

1738 to match the given parse expression. May be called with optional 

1739 C{maxMatches} argument, to clip searching after 'n' matches are found. 

1740  

1741 Example:: 

1742 # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters 

1743 cap_word = Word(alphas.upper(), alphas.lower()) 

1744  

1745 print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) 

1746 prints:: 

1747 ['More', 'Iron', 'Lead', 'Gold', 'I'] 

1748 """ 

1749 try: 

1750 return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) 

1751 except ParseBaseException as exc: 

1752 if ParserElement.verbose_stacktrace: 

1753 raise 

1754 else: 

1755 # catch and re-raise exception from here, clears out pyparsing internal stack trace 

1756 raise exc 

1757 

1758 def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): 

1759 """ 

1760 Generator method to split a string using the given expression as a separator. 

1761 May be called with optional C{maxsplit} argument, to limit the number of splits; 

1762 and the optional C{includeSeparators} argument (default=C{False}), if the separating 

1763 matching text should be included in the split results. 

1764  

1765 Example::  

1766 punc = oneOf(list(".,;:/-!?")) 

1767 print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) 

1768 prints:: 

1769 ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] 

1770 """ 

1771 splits = 0 

1772 last = 0 

1773 for t,s,e in self.scanString(instring, maxMatches=maxsplit): 

1774 yield instring[last:s] 

1775 if includeSeparators: 

1776 yield t[0] 

1777 last = e 

1778 yield instring[last:] 

1779 

1780 def __add__(self, other ): 

1781 """ 

1782 Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement 

1783 converts them to L{Literal}s by default. 

1784  

1785 Example:: 

1786 greet = Word(alphas) + "," + Word(alphas) + "!" 

1787 hello = "Hello, World!" 

1788 print (hello, "->", greet.parseString(hello)) 

1789 Prints:: 

1790 Hello, World! -> ['Hello', ',', 'World', '!'] 

1791 """ 

1792 if isinstance( other, basestring ): 

1793 other = ParserElement._literalStringClass( other ) 

1794 if not isinstance( other, ParserElement ): 

1795 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1796 SyntaxWarning, stacklevel=2) 

1797 return None 

1798 return And( [ self, other ] ) 

1799 

1800 def __radd__(self, other ): 

1801 """ 

1802 Implementation of + operator when left operand is not a C{L{ParserElement}} 

1803 """ 

1804 if isinstance( other, basestring ): 

1805 other = ParserElement._literalStringClass( other ) 

1806 if not isinstance( other, ParserElement ): 

1807 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1808 SyntaxWarning, stacklevel=2) 

1809 return None 

1810 return other + self 

1811 

1812 def __sub__(self, other): 

1813 """ 

1814 Implementation of - operator, returns C{L{And}} with error stop 

1815 """ 

1816 if isinstance( other, basestring ): 

1817 other = ParserElement._literalStringClass( other ) 

1818 if not isinstance( other, ParserElement ): 

1819 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1820 SyntaxWarning, stacklevel=2) 

1821 return None 

1822 return And( [ self, And._ErrorStop(), other ] ) 

1823 

1824 def __rsub__(self, other ): 

1825 """ 

1826 Implementation of - operator when left operand is not a C{L{ParserElement}} 

1827 """ 

1828 if isinstance( other, basestring ): 

1829 other = ParserElement._literalStringClass( other ) 

1830 if not isinstance( other, ParserElement ): 

1831 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1832 SyntaxWarning, stacklevel=2) 

1833 return None 

1834 return other - self 

1835 

1836 def __mul__(self,other): 

1837 """ 

1838 Implementation of * operator, allows use of C{expr * 3} in place of 

1839 C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer 

1840 tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples 

1841 may also include C{None} as in: 

1842 - C{expr*(n,None)} or C{expr*(n,)} is equivalent 

1843 to C{expr*n + L{ZeroOrMore}(expr)} 

1844 (read as "at least n instances of C{expr}") 

1845 - C{expr*(None,n)} is equivalent to C{expr*(0,n)} 

1846 (read as "0 to n instances of C{expr}") 

1847 - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} 

1848 - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} 

1849 

1850 Note that C{expr*(None,n)} does not raise an exception if 

1851 more than n exprs exist in the input stream; that is, 

1852 C{expr*(None,n)} does not enforce a maximum number of expr 

1853 occurrences. If this behavior is desired, then write 

1854 C{expr*(None,n) + ~expr} 

1855 """ 

1856 if isinstance(other,int): 

1857 minElements, optElements = other,0 

1858 elif isinstance(other,tuple): 

1859 other = (other + (None, None))[:2] 

1860 if other[0] is None: 

1861 other = (0, other[1]) 

1862 if isinstance(other[0],int) and other[1] is None: 

1863 if other[0] == 0: 

1864 return ZeroOrMore(self) 

1865 if other[0] == 1: 

1866 return OneOrMore(self) 

1867 else: 

1868 return self*other[0] + ZeroOrMore(self) 

1869 elif isinstance(other[0],int) and isinstance(other[1],int): 

1870 minElements, optElements = other 

1871 optElements -= minElements 

1872 else: 

1873 raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) 

1874 else: 

1875 raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) 

1876 

1877 if minElements < 0: 

1878 raise ValueError("cannot multiply ParserElement by negative value") 

1879 if optElements < 0: 

1880 raise ValueError("second tuple value must be greater or equal to first tuple value") 

1881 if minElements == optElements == 0: 

1882 raise ValueError("cannot multiply ParserElement by 0 or (0,0)") 

1883 

1884 if (optElements): 

1885 def makeOptionalList(n): 

1886 if n>1: 

1887 return Optional(self + makeOptionalList(n-1)) 

1888 else: 

1889 return Optional(self) 

1890 if minElements: 

1891 if minElements == 1: 

1892 ret = self + makeOptionalList(optElements) 

1893 else: 

1894 ret = And([self]*minElements) + makeOptionalList(optElements) 

1895 else: 

1896 ret = makeOptionalList(optElements) 

1897 else: 

1898 if minElements == 1: 

1899 ret = self 

1900 else: 

1901 ret = And([self]*minElements) 

1902 return ret 

1903 

1904 def __rmul__(self, other): 

1905 return self.__mul__(other) 

1906 

1907 def __or__(self, other ): 

1908 """ 

1909 Implementation of | operator - returns C{L{MatchFirst}} 

1910 """ 

1911 if isinstance( other, basestring ): 

1912 other = ParserElement._literalStringClass( other ) 

1913 if not isinstance( other, ParserElement ): 

1914 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1915 SyntaxWarning, stacklevel=2) 

1916 return None 

1917 return MatchFirst( [ self, other ] ) 

1918 

1919 def __ror__(self, other ): 

1920 """ 

1921 Implementation of | operator when left operand is not a C{L{ParserElement}} 

1922 """ 

1923 if isinstance( other, basestring ): 

1924 other = ParserElement._literalStringClass( other ) 

1925 if not isinstance( other, ParserElement ): 

1926 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1927 SyntaxWarning, stacklevel=2) 

1928 return None 

1929 return other | self 

1930 

1931 def __xor__(self, other ): 

1932 """ 

1933 Implementation of ^ operator - returns C{L{Or}} 

1934 """ 

1935 if isinstance( other, basestring ): 

1936 other = ParserElement._literalStringClass( other ) 

1937 if not isinstance( other, ParserElement ): 

1938 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1939 SyntaxWarning, stacklevel=2) 

1940 return None 

1941 return Or( [ self, other ] ) 

1942 

1943 def __rxor__(self, other ): 

1944 """ 

1945 Implementation of ^ operator when left operand is not a C{L{ParserElement}} 

1946 """ 

1947 if isinstance( other, basestring ): 

1948 other = ParserElement._literalStringClass( other ) 

1949 if not isinstance( other, ParserElement ): 

1950 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1951 SyntaxWarning, stacklevel=2) 

1952 return None 

1953 return other ^ self 

1954 

1955 def __and__(self, other ): 

1956 """ 

1957 Implementation of & operator - returns C{L{Each}} 

1958 """ 

1959 if isinstance( other, basestring ): 

1960 other = ParserElement._literalStringClass( other ) 

1961 if not isinstance( other, ParserElement ): 

1962 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1963 SyntaxWarning, stacklevel=2) 

1964 return None 

1965 return Each( [ self, other ] ) 

1966 

1967 def __rand__(self, other ): 

1968 """ 

1969 Implementation of & operator when left operand is not a C{L{ParserElement}} 

1970 """ 

1971 if isinstance( other, basestring ): 

1972 other = ParserElement._literalStringClass( other ) 

1973 if not isinstance( other, ParserElement ): 

1974 warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), 

1975 SyntaxWarning, stacklevel=2) 

1976 return None 

1977 return other & self 

1978 

1979 def __invert__( self ): 

1980 """ 

1981 Implementation of ~ operator - returns C{L{NotAny}} 

1982 """ 

1983 return NotAny( self ) 

1984 

1985 def __call__(self, name=None): 

1986 """ 

1987 Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}. 

1988  

1989 If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be 

1990 passed as C{True}. 

1991  

1992 If C{name} is omitted, same as calling C{L{copy}}. 

1993 

1994 Example:: 

1995 # these are equivalent 

1996 userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") 

1997 userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")  

1998 """ 

1999 if name is not None: 

2000 return self.setResultsName(name) 

2001 else: 

2002 return self.copy() 

2003 

2004 def suppress( self ): 

2005 """ 

2006 Suppresses the output of this C{ParserElement}; useful to keep punctuation from 

2007 cluttering up returned output. 

2008 """ 

2009 return Suppress( self ) 

2010 

2011 def leaveWhitespace( self ): 

2012 """ 

2013 Disables the skipping of whitespace before matching the characters in the 

2014 C{ParserElement}'s defined pattern. This is normally only used internally by 

2015 the pyparsing module, but may be needed in some whitespace-sensitive grammars. 

2016 """ 

2017 self.skipWhitespace = False 

2018 return self 

2019 

2020 def setWhitespaceChars( self, chars ): 

2021 """ 

2022 Overrides the default whitespace chars 

2023 """ 

2024 self.skipWhitespace = True 

2025 self.whiteChars = chars 

2026 self.copyDefaultWhiteChars = False 

2027 return self 

2028 

2029 def parseWithTabs( self ): 

2030 """ 

2031 Overrides default behavior to expand C{<TAB>}s to spaces before parsing the input string. 

2032 Must be called before C{parseString} when the input grammar contains elements that 

2033 match C{<TAB>} characters. 

2034 """ 

2035 self.keepTabs = True 

2036 return self 

2037 

2038 def ignore( self, other ): 

2039 """ 

2040 Define expression to be ignored (e.g., comments) while doing pattern 

2041 matching; may be called repeatedly, to define multiple comment or other 

2042 ignorable patterns. 

2043  

2044 Example:: 

2045 patt = OneOrMore(Word(alphas)) 

2046 patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] 

2047  

2048 patt.ignore(cStyleComment) 

2049 patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] 

2050 """ 

2051 if isinstance(other, basestring): 

2052 other = Suppress(other) 

2053 

2054 if isinstance( other, Suppress ): 

2055 if other not in self.ignoreExprs: 

2056 self.ignoreExprs.append(other) 

2057 else: 

2058 self.ignoreExprs.append( Suppress( other.copy() ) ) 

2059 return self 

2060 

2061 def setDebugActions( self, startAction, successAction, exceptionAction ): 

2062 """ 

2063 Enable display of debugging messages while doing pattern matching. 

2064 """ 

2065 self.debugActions = (startAction or _defaultStartDebugAction, 

2066 successAction or _defaultSuccessDebugAction, 

2067 exceptionAction or _defaultExceptionDebugAction) 

2068 self.debug = True 

2069 return self 

2070 

2071 def setDebug( self, flag=True ): 

2072 """ 

2073 Enable display of debugging messages while doing pattern matching. 

2074 Set C{flag} to True to enable, False to disable. 

2075 

2076 Example:: 

2077 wd = Word(alphas).setName("alphaword") 

2078 integer = Word(nums).setName("numword") 

2079 term = wd | integer 

2080  

2081 # turn on debugging for wd 

2082 wd.setDebug() 

2083 

2084 OneOrMore(term).parseString("abc 123 xyz 890") 

2085  

2086 prints:: 

2087 Match alphaword at loc 0(1,1) 

2088 Matched alphaword -> ['abc'] 

2089 Match alphaword at loc 3(1,4) 

2090 Exception raised:Expected alphaword (at char 4), (line:1, col:5) 

2091 Match alphaword at loc 7(1,8) 

2092 Matched alphaword -> ['xyz'] 

2093 Match alphaword at loc 11(1,12) 

2094 Exception raised:Expected alphaword (at char 12), (line:1, col:13) 

2095 Match alphaword at loc 15(1,16) 

2096 Exception raised:Expected alphaword (at char 15), (line:1, col:16) 

2097 

2098 The output shown is that produced by the default debug actions - custom debug actions can be 

2099 specified using L{setDebugActions}. Prior to attempting 

2100 to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"} 

2101 is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} 

2102 message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, 

2103 which makes debugging and exception messages easier to understand - for instance, the default 

2104 name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. 

2105 """ 

2106 if flag: 

2107 self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) 

2108 else: 

2109 self.debug = False 

2110 return self 

2111 

2112 def __str__( self ): 

2113 return self.name 

2114 

2115 def __repr__( self ): 

2116 return _ustr(self) 

2117 

2118 def streamline( self ): 

2119 self.streamlined = True 

2120 self.strRepr = None 

2121 return self 

2122 

2123 def checkRecursion( self, parseElementList ): 

2124 pass 

2125 

2126 def validate( self, validateTrace=[] ): 

2127 """ 

2128 Check defined expressions for valid structure, check for infinite recursive definitions. 

2129 """ 

2130 self.checkRecursion( [] ) 

2131 

2132 def parseFile( self, file_or_filename, parseAll=False ): 

2133 """ 

2134 Execute the parse expression on the given file or filename. 

2135 If a filename is specified (instead of a file object), 

2136 the entire file is opened, read, and closed before parsing. 

2137 """ 

2138 try: 

2139 file_contents = file_or_filename.read() 

2140 except AttributeError: 

2141 with open(file_or_filename, "r") as f: 

2142 file_contents = f.read() 

2143 try: 

2144 return self.parseString(file_contents, parseAll) 

2145 except ParseBaseException as exc: 

2146 if ParserElement.verbose_stacktrace: 

2147 raise 

2148 else: 

2149 # catch and re-raise exception from here, clears out pyparsing internal stack trace 

2150 raise exc 

2151 

2152 def __eq__(self,other): 

2153 if isinstance(other, ParserElement): 

2154 return self is other or vars(self) == vars(other) 

2155 elif isinstance(other, basestring): 

2156 return self.matches(other) 

2157 else: 

2158 return super(ParserElement,self)==other 

2159 

2160 def __ne__(self,other): 

2161 return not (self == other) 

2162 

2163 def __hash__(self): 

2164 return hash(id(self)) 

2165 

2166 def __req__(self,other): 

2167 return self == other 

2168 

2169 def __rne__(self,other): 

2170 return not (self == other) 

2171 

2172 def matches(self, testString, parseAll=True): 

2173 """ 

2174 Method for quick testing of a parser against a test string. Good for simple  

2175 inline microtests of sub expressions while building up larger parser. 

2176  

2177 Parameters: 

2178 - testString - to test against this expression for a match 

2179 - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests 

2180  

2181 Example:: 

2182 expr = Word(nums) 

2183 assert expr.matches("100") 

2184 """ 

2185 try: 

2186 self.parseString(_ustr(testString), parseAll=parseAll) 

2187 return True 

2188 except ParseBaseException: 

2189 return False 

2190 

2191 def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False): 

2192 """ 

2193 Execute the parse expression on a series of test strings, showing each 

2194 test, the parsed results or where the parse failed. Quick and easy way to 

2195 run a parse expression against a list of sample strings. 

2196  

2197 Parameters: 

2198 - tests - a list of separate test strings, or a multiline string of test strings 

2199 - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests  

2200 - comment - (default=C{'#'}) - expression for indicating embedded comments in the test  

2201 string; pass None to disable comment filtering 

2202 - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; 

2203 if False, only dump nested list 

2204 - printResults - (default=C{True}) prints test output to stdout 

2205 - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing 

2206 

2207 Returns: a (success, results) tuple, where success indicates that all tests succeeded 

2208 (or failed if C{failureTests} is True), and the results contain a list of lines of each  

2209 test's output 

2210  

2211 Example:: 

2212 number_expr = pyparsing_common.number.copy() 

2213 

2214 result = number_expr.runTests(''' 

2215 # unsigned integer 

2216 100 

2217 # negative integer 

2218 -100 

2219 # float with scientific notation 

2220 6.02e23 

2221 # integer with scientific notation 

2222 1e-12 

2223 ''') 

2224 print("Success" if result[0] else "Failed!") 

2225 

2226 result = number_expr.runTests(''' 

2227 # stray character 

2228 100Z 

2229 # missing leading digit before '.' 

2230 -.100 

2231 # too many '.' 

2232 3.14.159 

2233 ''', failureTests=True) 

2234 print("Success" if result[0] else "Failed!") 

2235 prints:: 

2236 # unsigned integer 

2237 100 

2238 [100] 

2239 

2240 # negative integer 

2241 -100 

2242 [-100] 

2243 

2244 # float with scientific notation 

2245 6.02e23 

2246 [6.02e+23] 

2247 

2248 # integer with scientific notation 

2249 1e-12 

2250 [1e-12] 

2251 

2252 Success 

2253  

2254 # stray character 

2255 100Z 

2256 ^ 

2257 FAIL: Expected end of text (at char 3), (line:1, col:4) 

2258 

2259 # missing leading digit before '.' 

2260 -.100 

2261 ^ 

2262 FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) 

2263 

2264 # too many '.' 

2265 3.14.159 

2266 ^ 

2267 FAIL: Expected end of text (at char 4), (line:1, col:5) 

2268 

2269 Success 

2270 

2271 Each test string must be on a single line. If you want to test a string that spans multiple 

2272 lines, create a test like this:: 

2273 

2274 expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") 

2275  

2276 (Note that this is a raw string literal, you must include the leading 'r'.) 

2277 """ 

2278 if isinstance(tests, basestring): 

2279 tests = list(map(str.strip, tests.rstrip().splitlines())) 

2280 if isinstance(comment, basestring): 

2281 comment = Literal(comment) 

2282 allResults = [] 

2283 comments = [] 

2284 success = True 

2285 for t in tests: 

2286 if comment is not None and comment.matches(t, False) or comments and not t: 

2287 comments.append(t) 

2288 continue 

2289 if not t: 

2290 continue 

2291 out = ['\n'.join(comments), t] 

2292 comments = [] 

2293 try: 

2294 t = t.replace(r'\n','\n') 

2295 result = self.parseString(t, parseAll=parseAll) 

2296 out.append(result.dump(full=fullDump)) 

2297 success = success and not failureTests 

2298 except ParseBaseException as pe: 

2299 fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" 

2300 if '\n' in t: 

2301 out.append(line(pe.loc, t)) 

2302 out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) 

2303 else: 

2304 out.append(' '*pe.loc + '^' + fatal) 

2305 out.append("FAIL: " + str(pe)) 

2306 success = success and failureTests 

2307 result = pe 

2308 except Exception as exc: 

2309 out.append("FAIL-EXCEPTION: " + str(exc)) 

2310 success = success and failureTests 

2311 result = exc 

2312 

2313 if printResults: 

2314 if fullDump: 

2315 out.append('') 

2316 print('\n'.join(out)) 

2317 

2318 allResults.append((t, result)) 

2319 

2320 return success, allResults 

2321 

2322 

2323class Token(ParserElement): 

2324 """ 

2325 Abstract C{ParserElement} subclass, for defining atomic matching patterns. 

2326 """ 

2327 def __init__( self ): 

2328 super(Token,self).__init__( savelist=False ) 

2329 

2330 

2331class Empty(Token): 

2332 """ 

2333 An empty token, will always match. 

2334 """ 

2335 def __init__( self ): 

2336 super(Empty,self).__init__() 

2337 self.name = "Empty" 

2338 self.mayReturnEmpty = True 

2339 self.mayIndexError = False 

2340 

2341 

2342class NoMatch(Token): 

2343 """ 

2344 A token that will never match. 

2345 """ 

2346 def __init__( self ): 

2347 super(NoMatch,self).__init__() 

2348 self.name = "NoMatch" 

2349 self.mayReturnEmpty = True 

2350 self.mayIndexError = False 

2351 self.errmsg = "Unmatchable token" 

2352 

2353 def parseImpl( self, instring, loc, doActions=True ): 

2354 raise ParseException(instring, loc, self.errmsg, self) 

2355 

2356 

2357class Literal(Token): 

2358 """ 

2359 Token to exactly match a specified string. 

2360  

2361 Example:: 

2362 Literal('blah').parseString('blah') # -> ['blah'] 

2363 Literal('blah').parseString('blahfooblah') # -> ['blah'] 

2364 Literal('blah').parseString('bla') # -> Exception: Expected "blah" 

2365  

2366 For case-insensitive matching, use L{CaselessLiteral}. 

2367  

2368 For keyword matching (force word break before and after the matched string), 

2369 use L{Keyword} or L{CaselessKeyword}. 

2370 """ 

2371 def __init__( self, matchString ): 

2372 super(Literal,self).__init__() 

2373 self.match = matchString 

2374 self.matchLen = len(matchString) 

2375 try: 

2376 self.firstMatchChar = matchString[0] 

2377 except IndexError: 

2378 warnings.warn("null string passed to Literal; use Empty() instead", 

2379 SyntaxWarning, stacklevel=2) 

2380 self.__class__ = Empty 

2381 self.name = '"%s"' % _ustr(self.match) 

2382 self.errmsg = "Expected " + self.name 

2383 self.mayReturnEmpty = False 

2384 self.mayIndexError = False 

2385 

2386 # Performance tuning: this routine gets called a *lot* 

2387 # if this is a single character match string and the first character matches, 

2388 # short-circuit as quickly as possible, and avoid calling startswith 

2389 #~ @profile 

2390 def parseImpl( self, instring, loc, doActions=True ): 

2391 if (instring[loc] == self.firstMatchChar and 

2392 (self.matchLen==1 or instring.startswith(self.match,loc)) ): 

2393 return loc+self.matchLen, self.match 

2394 raise ParseException(instring, loc, self.errmsg, self) 

2395_L = Literal 

2396ParserElement._literalStringClass = Literal 

2397 

2398class Keyword(Token): 

2399 """ 

2400 Token to exactly match a specified string as a keyword, that is, it must be 

2401 immediately followed by a non-keyword character. Compare with C{L{Literal}}: 

2402 - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. 

2403 - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} 

2404 Accepts two optional constructor arguments in addition to the keyword string: 

2405 - C{identChars} is a string of characters that would be valid identifier characters, 

2406 defaulting to all alphanumerics + "_" and "$" 

2407 - C{caseless} allows case-insensitive matching, default is C{False}. 

2408  

2409 Example:: 

2410 Keyword("start").parseString("start") # -> ['start'] 

2411 Keyword("start").parseString("starting") # -> Exception 

2412 

2413 For case-insensitive matching, use L{CaselessKeyword}. 

2414 """ 

2415 DEFAULT_KEYWORD_CHARS = alphanums+"_$" 

2416 

2417 def __init__( self, matchString, identChars=None, caseless=False ): 

2418 super(Keyword,self).__init__() 

2419 if identChars is None: 

2420 identChars = Keyword.DEFAULT_KEYWORD_CHARS 

2421 self.match = matchString 

2422 self.matchLen = len(matchString) 

2423 try: 

2424 self.firstMatchChar = matchString[0] 

2425 except IndexError: 

2426 warnings.warn("null string passed to Keyword; use Empty() instead", 

2427 SyntaxWarning, stacklevel=2) 

2428 self.name = '"%s"' % self.match 

2429 self.errmsg = "Expected " + self.name 

2430 self.mayReturnEmpty = False 

2431 self.mayIndexError = False 

2432 self.caseless = caseless 

2433 if caseless: 

2434 self.caselessmatch = matchString.upper() 

2435 identChars = identChars.upper() 

2436 self.identChars = set(identChars) 

2437 

2438 def parseImpl( self, instring, loc, doActions=True ): 

2439 if self.caseless: 

2440 if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and 

2441 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and 

2442 (loc == 0 or instring[loc-1].upper() not in self.identChars) ): 

2443 return loc+self.matchLen, self.match 

2444 else: 

2445 if (instring[loc] == self.firstMatchChar and 

2446 (self.matchLen==1 or instring.startswith(self.match,loc)) and 

2447 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and 

2448 (loc == 0 or instring[loc-1] not in self.identChars) ): 

2449 return loc+self.matchLen, self.match 

2450 raise ParseException(instring, loc, self.errmsg, self) 

2451 

2452 def copy(self): 

2453 c = super(Keyword,self).copy() 

2454 c.identChars = Keyword.DEFAULT_KEYWORD_CHARS 

2455 return c 

2456 

2457 @staticmethod 

2458 def setDefaultKeywordChars( chars ): 

2459 """Overrides the default Keyword chars 

2460 """ 

2461 Keyword.DEFAULT_KEYWORD_CHARS = chars 

2462 

2463class CaselessLiteral(Literal): 

2464 """ 

2465 Token to match a specified string, ignoring case of letters. 

2466 Note: the matched results will always be in the case of the given 

2467 match string, NOT the case of the input text. 

2468 

2469 Example:: 

2470 OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] 

2471  

2472 (Contrast with example for L{CaselessKeyword}.) 

2473 """ 

2474 def __init__( self, matchString ): 

2475 super(CaselessLiteral,self).__init__( matchString.upper() ) 

2476 # Preserve the defining literal. 

2477 self.returnString = matchString 

2478 self.name = "'%s'" % self.returnString 

2479 self.errmsg = "Expected " + self.name 

2480 

2481 def parseImpl( self, instring, loc, doActions=True ): 

2482 if instring[ loc:loc+self.matchLen ].upper() == self.match: 

2483 return loc+self.matchLen, self.returnString 

2484 raise ParseException(instring, loc, self.errmsg, self) 

2485 

2486class CaselessKeyword(Keyword): 

2487 """ 

2488 Caseless version of L{Keyword}. 

2489 

2490 Example:: 

2491 OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] 

2492  

2493 (Contrast with example for L{CaselessLiteral}.) 

2494 """ 

2495 def __init__( self, matchString, identChars=None ): 

2496 super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) 

2497 

2498 def parseImpl( self, instring, loc, doActions=True ): 

2499 if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and 

2500 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): 

2501 return loc+self.matchLen, self.match 

2502 raise ParseException(instring, loc, self.errmsg, self) 

2503 

2504class CloseMatch(Token): 

2505 """ 

2506 A variation on L{Literal} which matches "close" matches, that is,  

2507 strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: 

2508 - C{match_string} - string to be matched 

2509 - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match 

2510  

2511 The results from a successful parse will contain the matched text from the input string and the following named results: 

2512 - C{mismatches} - a list of the positions within the match_string where mismatches were found 

2513 - C{original} - the original match_string used to compare against the input string 

2514  

2515 If C{mismatches} is an empty list, then the match was an exact match. 

2516  

2517 Example:: 

2518 patt = CloseMatch("ATCATCGAATGGA") 

2519 patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) 

2520 patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) 

2521 

2522 # exact match 

2523 patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) 

2524 

2525 # close match allowing up to 2 mismatches 

2526 patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) 

2527 patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) 

2528 """ 

2529 def __init__(self, match_string, maxMismatches=1): 

2530 super(CloseMatch,self).__init__() 

2531 self.name = match_string 

2532 self.match_string = match_string 

2533 self.maxMismatches = maxMismatches 

2534 self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) 

2535 self.mayIndexError = False 

2536 self.mayReturnEmpty = False 

2537 

2538 def parseImpl( self, instring, loc, doActions=True ): 

2539 start = loc 

2540 instrlen = len(instring) 

2541 maxloc = start + len(self.match_string) 

2542 

2543 if maxloc <= instrlen: 

2544 match_string = self.match_string 

2545 match_stringloc = 0 

2546 mismatches = [] 

2547 maxMismatches = self.maxMismatches 

2548 

2549 for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): 

2550 src,mat = s_m 

2551 if src != mat: 

2552 mismatches.append(match_stringloc) 

2553 if len(mismatches) > maxMismatches: 

2554 break 

2555 else: 

2556 loc = match_stringloc + 1 

2557 results = ParseResults([instring[start:loc]]) 

2558 results['original'] = self.match_string 

2559 results['mismatches'] = mismatches 

2560 return loc, results 

2561 

2562 raise ParseException(instring, loc, self.errmsg, self) 

2563 

2564 

2565class Word(Token): 

2566 """ 

2567 Token for matching words composed of allowed character sets. 

2568 Defined with string containing all allowed initial characters, 

2569 an optional string containing allowed body characters (if omitted, 

2570 defaults to the initial character set), and an optional minimum, 

2571 maximum, and/or exact length. The default value for C{min} is 1 (a 

2572 minimum value < 1 is not valid); the default values for C{max} and C{exact} 

2573 are 0, meaning no maximum or exact length restriction. An optional 

2574 C{excludeChars} parameter can list characters that might be found in  

2575 the input C{bodyChars} string; useful to define a word of all printables 

2576 except for one or two characters, for instance. 

2577  

2578 L{srange} is useful for defining custom character set strings for defining  

2579 C{Word} expressions, using range notation from regular expression character sets. 

2580  

2581 A common mistake is to use C{Word} to match a specific literal string, as in  

2582 C{Word("Address")}. Remember that C{Word} uses the string argument to define 

2583 I{sets} of matchable characters. This expression would match "Add", "AAA", 

2584 "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. 

2585 To match an exact literal string, use L{Literal} or L{Keyword}. 

2586 

2587 pyparsing includes helper strings for building Words: 

2588 - L{alphas} 

2589 - L{nums} 

2590 - L{alphanums} 

2591 - L{hexnums} 

2592 - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) 

2593 - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) 

2594 - L{printables} (any non-whitespace character) 

2595 

2596 Example:: 

2597 # a word composed of digits 

2598 integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) 

2599  

2600 # a word with a leading capital, and zero or more lowercase 

2601 capital_word = Word(alphas.upper(), alphas.lower()) 

2602 

2603 # hostnames are alphanumeric, with leading alpha, and '-' 

2604 hostname = Word(alphas, alphanums+'-') 

2605  

2606 # roman numeral (not a strict parser, accepts invalid mix of characters) 

2607 roman = Word("IVXLCDM") 

2608  

2609 # any string of non-whitespace characters, except for ',' 

2610 csv_value = Word(printables, excludeChars=",") 

2611 """ 

2612 def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): 

2613 super(Word,self).__init__() 

2614 if excludeChars: 

2615 initChars = ''.join(c for c in initChars if c not in excludeChars) 

2616 if bodyChars: 

2617 bodyChars = ''.join(c for c in bodyChars if c not in excludeChars) 

2618 self.initCharsOrig = initChars 

2619 self.initChars = set(initChars) 

2620 if bodyChars : 

2621 self.bodyCharsOrig = bodyChars 

2622 self.bodyChars = set(bodyChars) 

2623 else: 

2624 self.bodyCharsOrig = initChars 

2625 self.bodyChars = set(initChars) 

2626 

2627 self.maxSpecified = max > 0 

2628 

2629 if min < 1: 

2630 raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") 

2631 

2632 self.minLen = min 

2633 

2634 if max > 0: 

2635 self.maxLen = max 

2636 else: 

2637 self.maxLen = _MAX_INT 

2638 

2639 if exact > 0: 

2640 self.maxLen = exact 

2641 self.minLen = exact 

2642 

2643 self.name = _ustr(self) 

2644 self.errmsg = "Expected " + self.name 

2645 self.mayIndexError = False 

2646 self.asKeyword = asKeyword 

2647 

2648 if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): 

2649 if self.bodyCharsOrig == self.initCharsOrig: 

2650 self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) 

2651 elif len(self.initCharsOrig) == 1: 

2652 self.reString = "%s[%s]*" % \ 

2653 (re.escape(self.initCharsOrig), 

2654 _escapeRegexRangeChars(self.bodyCharsOrig),) 

2655 else: 

2656 self.reString = "[%s][%s]*" % \ 

2657 (_escapeRegexRangeChars(self.initCharsOrig), 

2658 _escapeRegexRangeChars(self.bodyCharsOrig),) 

2659 if self.asKeyword: 

2660 self.reString = r"\b"+self.reString+r"\b" 

2661 try: 

2662 self.re = re.compile( self.reString ) 

2663 except Exception: 

2664 self.re = None 

2665 

2666 def parseImpl( self, instring, loc, doActions=True ): 

2667 if self.re: 

2668 result = self.re.match(instring,loc) 

2669 if not result: 

2670 raise ParseException(instring, loc, self.errmsg, self) 

2671 

2672 loc = result.end() 

2673 return loc, result.group() 

2674 

2675 if not(instring[ loc ] in self.initChars): 

2676 raise ParseException(instring, loc, self.errmsg, self) 

2677 

2678 start = loc 

2679 loc += 1 

2680 instrlen = len(instring) 

2681 bodychars = self.bodyChars 

2682 maxloc = start + self.maxLen 

2683 maxloc = min( maxloc, instrlen ) 

2684 while loc < maxloc and instring[loc] in bodychars: 

2685 loc += 1 

2686 

2687 throwException = False 

2688 if loc - start < self.minLen: 

2689 throwException = True 

2690 if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: 

2691 throwException = True 

2692 if self.asKeyword: 

2693 if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars): 

2694 throwException = True 

2695 

2696 if throwException: 

2697 raise ParseException(instring, loc, self.errmsg, self) 

2698 

2699 return loc, instring[start:loc] 

2700 

2701 def __str__( self ): 

2702 try: 

2703 return super(Word,self).__str__() 

2704 except Exception: 

2705 pass 

2706 

2707 

2708 if self.strRepr is None: 

2709 

2710 def charsAsStr(s): 

2711 if len(s)>4: 

2712 return s[:4]+"..." 

2713 else: 

2714 return s 

2715 

2716 if ( self.initCharsOrig != self.bodyCharsOrig ): 

2717 self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) 

2718 else: 

2719 self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) 

2720 

2721 return self.strRepr 

2722 

2723 

2724class Regex(Token): 

2725 r""" 

2726 Token for matching strings that match a given regular expression. 

2727 Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. 

2728 If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as  

2729 named parse results. 

2730 

2731 Example:: 

2732 realnum = Regex(r"[+-]?\d+\.\d*") 

2733 date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') 

2734 # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression 

2735 roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") 

2736 """ 

2737 compiledREtype = type(re.compile("[A-Z]")) 

2738 def __init__( self, pattern, flags=0): 

2739 """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags.""" 

2740 super(Regex,self).__init__() 

2741 

2742 if isinstance(pattern, basestring): 

2743 if not pattern: 

2744 warnings.warn("null string passed to Regex; use Empty() instead", 

2745 SyntaxWarning, stacklevel=2) 

2746 

2747 self.pattern = pattern 

2748 self.flags = flags 

2749 

2750 try: 

2751 self.re = re.compile(self.pattern, self.flags) 

2752 self.reString = self.pattern 

2753 except sre_constants.error: 

2754 warnings.warn("invalid pattern (%s) passed to Regex" % pattern, 

2755 SyntaxWarning, stacklevel=2) 

2756 raise 

2757 

2758 elif isinstance(pattern, Regex.compiledREtype): 

2759 self.re = pattern 

2760 self.pattern = \ 

2761 self.reString = str(pattern) 

2762 self.flags = flags 

2763 

2764 else: 

2765 raise ValueError("Regex may only be constructed with a string or a compiled RE object") 

2766 

2767 self.name = _ustr(self) 

2768 self.errmsg = "Expected " + self.name 

2769 self.mayIndexError = False 

2770 self.mayReturnEmpty = True 

2771 

2772 def parseImpl( self, instring, loc, doActions=True ): 

2773 result = self.re.match(instring,loc) 

2774 if not result: 

2775 raise ParseException(instring, loc, self.errmsg, self) 

2776 

2777 loc = result.end() 

2778 d = result.groupdict() 

2779 ret = ParseResults(result.group()) 

2780 if d: 

2781 for k in d: 

2782 ret[k] = d[k] 

2783 return loc,ret 

2784 

2785 def __str__( self ): 

2786 try: 

2787 return super(Regex,self).__str__() 

2788 except Exception: 

2789 pass 

2790 

2791 if self.strRepr is None: 

2792 self.strRepr = "Re:(%s)" % repr(self.pattern) 

2793 

2794 return self.strRepr 

2795 

2796 

2797class QuotedString(Token): 

2798 r""" 

2799 Token for matching strings that are delimited by quoting characters. 

2800  

2801 Defined with the following parameters: 

2802 - quoteChar - string of one or more characters defining the quote delimiting string 

2803 - escChar - character to escape quotes, typically backslash (default=C{None}) 

2804 - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None}) 

2805 - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) 

2806 - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) 

2807 - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) 

2808 - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) 

2809 

2810 Example:: 

2811 qs = QuotedString('"') 

2812 print(qs.searchString('lsjdf "This is the quote" sldjf')) 

2813 complex_qs = QuotedString('{{', endQuoteChar='}}') 

2814 print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) 

2815 sql_qs = QuotedString('"', escQuote='""') 

2816 print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) 

2817 prints:: 

2818 [['This is the quote']] 

2819 [['This is the "quote"']] 

2820 [['This is the quote with "embedded" quotes']] 

2821 """ 

2822 def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): 

2823 super(QuotedString,self).__init__() 

2824 

2825 # remove white space from quote chars - wont work anyway 

2826 quoteChar = quoteChar.strip() 

2827 if not quoteChar: 

2828 warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) 

2829 raise SyntaxError() 

2830 

2831 if endQuoteChar is None: 

2832 endQuoteChar = quoteChar 

2833 else: 

2834 endQuoteChar = endQuoteChar.strip() 

2835 if not endQuoteChar: 

2836 warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) 

2837 raise SyntaxError() 

2838 

2839 self.quoteChar = quoteChar 

2840 self.quoteCharLen = len(quoteChar) 

2841 self.firstQuoteChar = quoteChar[0] 

2842 self.endQuoteChar = endQuoteChar 

2843 self.endQuoteCharLen = len(endQuoteChar) 

2844 self.escChar = escChar 

2845 self.escQuote = escQuote 

2846 self.unquoteResults = unquoteResults 

2847 self.convertWhitespaceEscapes = convertWhitespaceEscapes 

2848 

2849 if multiline: 

2850 self.flags = re.MULTILINE | re.DOTALL 

2851 self.pattern = r'%s(?:[^%s%s]' % \ 

2852 ( re.escape(self.quoteChar), 

2853 _escapeRegexRangeChars(self.endQuoteChar[0]), 

2854 (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) 

2855 else: 

2856 self.flags = 0 

2857 self.pattern = r'%s(?:[^%s\n\r%s]' % \ 

2858 ( re.escape(self.quoteChar), 

2859 _escapeRegexRangeChars(self.endQuoteChar[0]), 

2860 (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) 

2861 if len(self.endQuoteChar) > 1: 

2862 self.pattern += ( 

2863 '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]), 

2864 _escapeRegexRangeChars(self.endQuoteChar[i])) 

2865 for i in range(len(self.endQuoteChar)-1,0,-1)) + ')' 

2866 ) 

2867 if escQuote: 

2868 self.pattern += (r'|(?:%s)' % re.escape(escQuote)) 

2869 if escChar: 

2870 self.pattern += (r'|(?:%s.)' % re.escape(escChar)) 

2871 self.escCharReplacePattern = re.escape(self.escChar)+"(.)" 

2872 self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) 

2873 

2874 try: 

2875 self.re = re.compile(self.pattern, self.flags) 

2876 self.reString = self.pattern 

2877 except sre_constants.error: 

2878 warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, 

2879 SyntaxWarning, stacklevel=2) 

2880 raise 

2881 

2882 self.name = _ustr(self) 

2883 self.errmsg = "Expected " + self.name 

2884 self.mayIndexError = False 

2885 self.mayReturnEmpty = True 

2886 

2887 def parseImpl( self, instring, loc, doActions=True ): 

2888 result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None 

2889 if not result: 

2890 raise ParseException(instring, loc, self.errmsg, self) 

2891 

2892 loc = result.end() 

2893 ret = result.group() 

2894 

2895 if self.unquoteResults: 

2896 

2897 # strip off quotes 

2898 ret = ret[self.quoteCharLen:-self.endQuoteCharLen] 

2899 

2900 if isinstance(ret,basestring): 

2901 # replace escaped whitespace 

2902 if '\\' in ret and self.convertWhitespaceEscapes: 

2903 ws_map = { 

2904 r'\t' : '\t', 

2905 r'\n' : '\n', 

2906 r'\f' : '\f', 

2907 r'\r' : '\r', 

2908 } 

2909 for wslit,wschar in ws_map.items(): 

2910 ret = ret.replace(wslit, wschar) 

2911 

2912 # replace escaped characters 

2913 if self.escChar: 

2914 ret = re.sub(self.escCharReplacePattern,r"\g<1>",ret) 

2915 

2916 # replace escaped quotes 

2917 if self.escQuote: 

2918 ret = ret.replace(self.escQuote, self.endQuoteChar) 

2919 

2920 return loc, ret 

2921 

2922 def __str__( self ): 

2923 try: 

2924 return super(QuotedString,self).__str__() 

2925 except Exception: 

2926 pass 

2927 

2928 if self.strRepr is None: 

2929 self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) 

2930 

2931 return self.strRepr 

2932 

2933 

2934class CharsNotIn(Token): 

2935 """ 

2936 Token for matching words composed of characters I{not} in a given set (will 

2937 include whitespace in matched characters if not listed in the provided exclusion set - see example). 

2938 Defined with string containing all disallowed characters, and an optional 

2939 minimum, maximum, and/or exact length. The default value for C{min} is 1 (a 

2940 minimum value < 1 is not valid); the default values for C{max} and C{exact} 

2941 are 0, meaning no maximum or exact length restriction. 

2942 

2943 Example:: 

2944 # define a comma-separated-value as anything that is not a ',' 

2945 csv_value = CharsNotIn(',') 

2946 print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) 

2947 prints:: 

2948 ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] 

2949 """ 

2950 def __init__( self, notChars, min=1, max=0, exact=0 ): 

2951 super(CharsNotIn,self).__init__() 

2952 self.skipWhitespace = False 

2953 self.notChars = notChars 

2954 

2955 if min < 1: 

2956 raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") 

2957 

2958 self.minLen = min 

2959 

2960 if max > 0: 

2961 self.maxLen = max 

2962 else: 

2963 self.maxLen = _MAX_INT 

2964 

2965 if exact > 0: 

2966 self.maxLen = exact 

2967 self.minLen = exact 

2968 

2969 self.name = _ustr(self) 

2970 self.errmsg = "Expected " + self.name 

2971 self.mayReturnEmpty = ( self.minLen == 0 ) 

2972 self.mayIndexError = False 

2973 

2974 def parseImpl( self, instring, loc, doActions=True ): 

2975 if instring[loc] in self.notChars: 

2976 raise ParseException(instring, loc, self.errmsg, self) 

2977 

2978 start = loc 

2979 loc += 1 

2980 notchars = self.notChars 

2981 maxlen = min( start+self.maxLen, len(instring) ) 

2982 while loc < maxlen and \ 

2983 (instring[loc] not in notchars): 

2984 loc += 1 

2985 

2986 if loc - start < self.minLen: 

2987 raise ParseException(instring, loc, self.errmsg, self) 

2988 

2989 return loc, instring[start:loc] 

2990 

2991 def __str__( self ): 

2992 try: 

2993 return super(CharsNotIn, self).__str__() 

2994 except Exception: 

2995 pass 

2996 

2997 if self.strRepr is None: 

2998 if len(self.notChars) > 4: 

2999 self.strRepr = "!W:(%s...)" % self.notChars[:4] 

3000 else: 

3001 self.strRepr = "!W:(%s)" % self.notChars 

3002 

3003 return self.strRepr 

3004 

3005class White(Token): 

3006 """ 

3007 Special matching class for matching whitespace. Normally, whitespace is ignored 

3008 by pyparsing grammars. This class is included when some whitespace structures 

3009 are significant. Define with a string containing the whitespace characters to be 

3010 matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, 

3011 as defined for the C{L{Word}} class. 

3012 """ 

3013 whiteStrs = { 

3014 " " : "<SPC>", 

3015 "\t": "<TAB>", 

3016 "\n": "<LF>", 

3017 "\r": "<CR>", 

3018 "\f": "<FF>", 

3019 } 

3020 def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): 

3021 super(White,self).__init__() 

3022 self.matchWhite = ws 

3023 self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) ) 

3024 #~ self.leaveWhitespace() 

3025 self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite)) 

3026 self.mayReturnEmpty = True 

3027 self.errmsg = "Expected " + self.name 

3028 

3029 self.minLen = min 

3030 

3031 if max > 0: 

3032 self.maxLen = max 

3033 else: 

3034 self.maxLen = _MAX_INT 

3035 

3036 if exact > 0: 

3037 self.maxLen = exact 

3038 self.minLen = exact 

3039 

3040 def parseImpl( self, instring, loc, doActions=True ): 

3041 if not(instring[ loc ] in self.matchWhite): 

3042 raise ParseException(instring, loc, self.errmsg, self) 

3043 start = loc 

3044 loc += 1 

3045 maxloc = start + self.maxLen 

3046 maxloc = min( maxloc, len(instring) ) 

3047 while loc < maxloc and instring[loc] in self.matchWhite: 

3048 loc += 1 

3049 

3050 if loc - start < self.minLen: 

3051 raise ParseException(instring, loc, self.errmsg, self) 

3052 

3053 return loc, instring[start:loc] 

3054 

3055 

3056class _PositionToken(Token): 

3057 def __init__( self ): 

3058 super(_PositionToken,self).__init__() 

3059 self.name=self.__class__.__name__ 

3060 self.mayReturnEmpty = True 

3061 self.mayIndexError = False 

3062 

3063class GoToColumn(_PositionToken): 

3064 """ 

3065 Token to advance to a specific column of input text; useful for tabular report scraping. 

3066 """ 

3067 def __init__( self, colno ): 

3068 super(GoToColumn,self).__init__() 

3069 self.col = colno 

3070 

3071 def preParse( self, instring, loc ): 

3072 if col(loc,instring) != self.col: 

3073 instrlen = len(instring) 

3074 if self.ignoreExprs: 

3075 loc = self._skipIgnorables( instring, loc ) 

3076 while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : 

3077 loc += 1 

3078 return loc 

3079 

3080 def parseImpl( self, instring, loc, doActions=True ): 

3081 thiscol = col( loc, instring ) 

3082 if thiscol > self.col: 

3083 raise ParseException( instring, loc, "Text not in expected column", self ) 

3084 newloc = loc + self.col - thiscol 

3085 ret = instring[ loc: newloc ] 

3086 return newloc, ret 

3087 

3088 

3089class LineStart(_PositionToken): 

3090 """ 

3091 Matches if current position is at the beginning of a line within the parse string 

3092  

3093 Example:: 

3094  

3095 test = '''\ 

3096 AAA this line 

3097 AAA and this line 

3098 AAA but not this one 

3099 B AAA and definitely not this one 

3100 ''' 

3101 

3102 for t in (LineStart() + 'AAA' + restOfLine).searchString(test): 

3103 print(t) 

3104  

3105 Prints:: 

3106 ['AAA', ' this line'] 

3107 ['AAA', ' and this line']  

3108 

3109 """ 

3110 def __init__( self ): 

3111 super(LineStart,self).__init__() 

3112 self.errmsg = "Expected start of line" 

3113 

3114 def parseImpl( self, instring, loc, doActions=True ): 

3115 if col(loc, instring) == 1: 

3116 return loc, [] 

3117 raise ParseException(instring, loc, self.errmsg, self) 

3118 

3119class LineEnd(_PositionToken): 

3120 """ 

3121 Matches if current position is at the end of a line within the parse string 

3122 """ 

3123 def __init__( self ): 

3124 super(LineEnd,self).__init__() 

3125 self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) 

3126 self.errmsg = "Expected end of line" 

3127 

3128 def parseImpl( self, instring, loc, doActions=True ): 

3129 if loc<len(instring): 

3130 if instring[loc] == "\n": 

3131 return loc+1, "\n" 

3132 else: 

3133 raise ParseException(instring, loc, self.errmsg, self) 

3134 elif loc == len(instring): 

3135 return loc+1, [] 

3136 else: 

3137 raise ParseException(instring, loc, self.errmsg, self) 

3138 

3139class StringStart(_PositionToken): 

3140 """ 

3141 Matches if current position is at the beginning of the parse string 

3142 """ 

3143 def __init__( self ): 

3144 super(StringStart,self).__init__() 

3145 self.errmsg = "Expected start of text" 

3146 

3147 def parseImpl( self, instring, loc, doActions=True ): 

3148 if loc != 0: 

3149 # see if entire string up to here is just whitespace and ignoreables 

3150 if loc != self.preParse( instring, 0 ): 

3151 raise ParseException(instring, loc, self.errmsg, self) 

3152 return loc, [] 

3153 

3154class StringEnd(_PositionToken): 

3155 """ 

3156 Matches if current position is at the end of the parse string 

3157 """ 

3158 def __init__( self ): 

3159 super(StringEnd,self).__init__() 

3160 self.errmsg = "Expected end of text" 

3161 

3162 def parseImpl( self, instring, loc, doActions=True ): 

3163 if loc < len(instring): 

3164 raise ParseException(instring, loc, self.errmsg, self) 

3165 elif loc == len(instring): 

3166 return loc+1, [] 

3167 elif loc > len(instring): 

3168 return loc, [] 

3169 else: 

3170 raise ParseException(instring, loc, self.errmsg, self) 

3171 

3172class WordStart(_PositionToken): 

3173 """ 

3174 Matches if the current position is at the beginning of a Word, and 

3175 is not preceded by any character in a given set of C{wordChars} 

3176 (default=C{printables}). To emulate the C{\b} behavior of regular expressions, 

3177 use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of 

3178 the string being parsed, or at the beginning of a line. 

3179 """ 

3180 def __init__(self, wordChars = printables): 

3181 super(WordStart,self).__init__() 

3182 self.wordChars = set(wordChars) 

3183 self.errmsg = "Not at the start of a word" 

3184 

3185 def parseImpl(self, instring, loc, doActions=True ): 

3186 if loc != 0: 

3187 if (instring[loc-1] in self.wordChars or 

3188 instring[loc] not in self.wordChars): 

3189 raise ParseException(instring, loc, self.errmsg, self) 

3190 return loc, [] 

3191 

3192class WordEnd(_PositionToken): 

3193 """ 

3194 Matches if the current position is at the end of a Word, and 

3195 is not followed by any character in a given set of C{wordChars} 

3196 (default=C{printables}). To emulate the C{\b} behavior of regular expressions, 

3197 use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of 

3198 the string being parsed, or at the end of a line. 

3199 """ 

3200 def __init__(self, wordChars = printables): 

3201 super(WordEnd,self).__init__() 

3202 self.wordChars = set(wordChars) 

3203 self.skipWhitespace = False 

3204 self.errmsg = "Not at the end of a word" 

3205 

3206 def parseImpl(self, instring, loc, doActions=True ): 

3207 instrlen = len(instring) 

3208 if instrlen>0 and loc<instrlen: 

3209 if (instring[loc] in self.wordChars or 

3210 instring[loc-1] not in self.wordChars): 

3211 raise ParseException(instring, loc, self.errmsg, self) 

3212 return loc, [] 

3213 

3214 

3215class ParseExpression(ParserElement): 

3216 """ 

3217 Abstract subclass of ParserElement, for combining and post-processing parsed tokens. 

3218 """ 

3219 def __init__( self, exprs, savelist = False ): 

3220 super(ParseExpression,self).__init__(savelist) 

3221 if isinstance( exprs, _generatorType ): 

3222 exprs = list(exprs) 

3223 

3224 if isinstance( exprs, basestring ): 

3225 self.exprs = [ ParserElement._literalStringClass( exprs ) ] 

3226 elif isinstance( exprs, collections.Iterable ): 

3227 exprs = list(exprs) 

3228 # if sequence of strings provided, wrap with Literal 

3229 if all(isinstance(expr, basestring) for expr in exprs): 

3230 exprs = map(ParserElement._literalStringClass, exprs) 

3231 self.exprs = list(exprs) 

3232 else: 

3233 try: 

3234 self.exprs = list( exprs ) 

3235 except TypeError: 

3236 self.exprs = [ exprs ] 

3237 self.callPreparse = False 

3238 

3239 def __getitem__( self, i ): 

3240 return self.exprs[i] 

3241 

3242 def append( self, other ): 

3243 self.exprs.append( other ) 

3244 self.strRepr = None 

3245 return self 

3246 

3247 def leaveWhitespace( self ): 

3248 """Extends C{leaveWhitespace} defined in base class, and also invokes C{leaveWhitespace} on 

3249 all contained expressions.""" 

3250 self.skipWhitespace = False 

3251 self.exprs = [ e.copy() for e in self.exprs ] 

3252 for e in self.exprs: 

3253 e.leaveWhitespace() 

3254 return self 

3255 

3256 def ignore( self, other ): 

3257 if isinstance( other, Suppress ): 

3258 if other not in self.ignoreExprs: 

3259 super( ParseExpression, self).ignore( other ) 

3260 for e in self.exprs: 

3261 e.ignore( self.ignoreExprs[-1] ) 

3262 else: 

3263 super( ParseExpression, self).ignore( other ) 

3264 for e in self.exprs: 

3265 e.ignore( self.ignoreExprs[-1] ) 

3266 return self 

3267 

3268 def __str__( self ): 

3269 try: 

3270 return super(ParseExpression,self).__str__() 

3271 except Exception: 

3272 pass 

3273 

3274 if self.strRepr is None: 

3275 self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) ) 

3276 return self.strRepr 

3277 

3278 def streamline( self ): 

3279 super(ParseExpression,self).streamline() 

3280 

3281 for e in self.exprs: 

3282 e.streamline() 

3283 

3284 # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d ) 

3285 # but only if there are no parse actions or resultsNames on the nested And's 

3286 # (likewise for Or's and MatchFirst's) 

3287 if ( len(self.exprs) == 2 ): 

3288 other = self.exprs[0] 

3289 if ( isinstance( other, self.__class__ ) and 

3290 not(other.parseAction) and 

3291 other.resultsName is None and 

3292 not other.debug ): 

3293 self.exprs = other.exprs[:] + [ self.exprs[1] ] 

3294 self.strRepr = None 

3295 self.mayReturnEmpty |= other.mayReturnEmpty 

3296 self.mayIndexError |= other.mayIndexError 

3297 

3298 other = self.exprs[-1] 

3299 if ( isinstance( other, self.__class__ ) and 

3300 not(other.parseAction) and 

3301 other.resultsName is None and 

3302 not other.debug ): 

3303 self.exprs = self.exprs[:-1] + other.exprs[:] 

3304 self.strRepr = None 

3305 self.mayReturnEmpty |= other.mayReturnEmpty 

3306 self.mayIndexError |= other.mayIndexError 

3307 

3308 self.errmsg = "Expected " + _ustr(self) 

3309 

3310 return self 

3311 

3312 def setResultsName( self, name, listAllMatches=False ): 

3313 ret = super(ParseExpression,self).setResultsName(name,listAllMatches) 

3314 return ret 

3315 

3316 def validate( self, validateTrace=[] ): 

3317 tmp = validateTrace[:]+[self] 

3318 for e in self.exprs: 

3319 e.validate(tmp) 

3320 self.checkRecursion( [] ) 

3321 

3322 def copy(self): 

3323 ret = super(ParseExpression,self).copy() 

3324 ret.exprs = [e.copy() for e in self.exprs] 

3325 return ret 

3326 

3327class And(ParseExpression): 

3328 """ 

3329 Requires all given C{ParseExpression}s to be found in the given order. 

3330 Expressions may be separated by whitespace. 

3331 May be constructed using the C{'+'} operator. 

3332 May also be constructed using the C{'-'} operator, which will suppress backtracking. 

3333 

3334 Example:: 

3335 integer = Word(nums) 

3336 name_expr = OneOrMore(Word(alphas)) 

3337 

3338 expr = And([integer("id"),name_expr("name"),integer("age")]) 

3339 # more easily written as: 

3340 expr = integer("id") + name_expr("name") + integer("age") 

3341 """ 

3342 

3343 class _ErrorStop(Empty): 

3344 def __init__(self, *args, **kwargs): 

3345 super(And._ErrorStop,self).__init__(*args, **kwargs) 

3346 self.name = '-' 

3347 self.leaveWhitespace() 

3348 

3349 def __init__( self, exprs, savelist = True ): 

3350 super(And,self).__init__(exprs, savelist) 

3351 self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) 

3352 self.setWhitespaceChars( self.exprs[0].whiteChars ) 

3353 self.skipWhitespace = self.exprs[0].skipWhitespace 

3354 self.callPreparse = True 

3355 

3356 def parseImpl( self, instring, loc, doActions=True ): 

3357 # pass False as last arg to _parse for first element, since we already 

3358 # pre-parsed the string as part of our And pre-parsing 

3359 loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False ) 

3360 errorStop = False 

3361 for e in self.exprs[1:]: 

3362 if isinstance(e, And._ErrorStop): 

3363 errorStop = True 

3364 continue 

3365 if errorStop: 

3366 try: 

3367 loc, exprtokens = e._parse( instring, loc, doActions ) 

3368 except ParseSyntaxException: 

3369 raise 

3370 except ParseBaseException as pe: 

3371 pe.__traceback__ = None 

3372 raise ParseSyntaxException._from_exception(pe) 

3373 except IndexError: 

3374 raise ParseSyntaxException(instring, len(instring), self.errmsg, self) 

3375 else: 

3376 loc, exprtokens = e._parse( instring, loc, doActions ) 

3377 if exprtokens or exprtokens.haskeys(): 

3378 resultlist += exprtokens 

3379 return loc, resultlist 

3380 

3381 def __iadd__(self, other ): 

3382 if isinstance( other, basestring ): 

3383 other = ParserElement._literalStringClass( other ) 

3384 return self.append( other ) #And( [ self, other ] ) 

3385 

3386 def checkRecursion( self, parseElementList ): 

3387 subRecCheckList = parseElementList[:] + [ self ] 

3388 for e in self.exprs: 

3389 e.checkRecursion( subRecCheckList ) 

3390 if not e.mayReturnEmpty: 

3391 break 

3392 

3393 def __str__( self ): 

3394 if hasattr(self,"name"): 

3395 return self.name 

3396 

3397 if self.strRepr is None: 

3398 self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}" 

3399 

3400 return self.strRepr 

3401 

3402 

3403class Or(ParseExpression): 

3404 """ 

3405 Requires that at least one C{ParseExpression} is found. 

3406 If two expressions match, the expression that matches the longest string will be used. 

3407 May be constructed using the C{'^'} operator. 

3408 

3409 Example:: 

3410 # construct Or using '^' operator 

3411  

3412 number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) 

3413 print(number.searchString("123 3.1416 789")) 

3414 prints:: 

3415 [['123'], ['3.1416'], ['789']] 

3416 """ 

3417 def __init__( self, exprs, savelist = False ): 

3418 super(Or,self).__init__(exprs, savelist) 

3419 if self.exprs: 

3420 self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) 

3421 else: 

3422 self.mayReturnEmpty = True 

3423 

3424 def parseImpl( self, instring, loc, doActions=True ): 

3425 maxExcLoc = -1 

3426 maxException = None 

3427 matches = [] 

3428 for e in self.exprs: 

3429 try: 

3430 loc2 = e.tryParse( instring, loc ) 

3431 except ParseException as err: 

3432 err.__traceback__ = None 

3433 if err.loc > maxExcLoc: 

3434 maxException = err 

3435 maxExcLoc = err.loc 

3436 except IndexError: 

3437 if len(instring) > maxExcLoc: 

3438 maxException = ParseException(instring,len(instring),e.errmsg,self) 

3439 maxExcLoc = len(instring) 

3440 else: 

3441 # save match among all matches, to retry longest to shortest 

3442 matches.append((loc2, e)) 

3443 

3444 if matches: 

3445 matches.sort(key=lambda x: -x[0]) 

3446 for _,e in matches: 

3447 try: 

3448 return e._parse( instring, loc, doActions ) 

3449 except ParseException as err: 

3450 err.__traceback__ = None 

3451 if err.loc > maxExcLoc: 

3452 maxException = err 

3453 maxExcLoc = err.loc 

3454 

3455 if maxException is not None: 

3456 maxException.msg = self.errmsg 

3457 raise maxException 

3458 else: 

3459 raise ParseException(instring, loc, "no defined alternatives to match", self) 

3460 

3461 

3462 def __ixor__(self, other ): 

3463 if isinstance( other, basestring ): 

3464 other = ParserElement._literalStringClass( other ) 

3465 return self.append( other ) #Or( [ self, other ] ) 

3466 

3467 def __str__( self ): 

3468 if hasattr(self,"name"): 

3469 return self.name 

3470 

3471 if self.strRepr is None: 

3472 self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}" 

3473 

3474 return self.strRepr 

3475 

3476 def checkRecursion( self, parseElementList ): 

3477 subRecCheckList = parseElementList[:] + [ self ] 

3478 for e in self.exprs: 

3479 e.checkRecursion( subRecCheckList ) 

3480 

3481 

3482class MatchFirst(ParseExpression): 

3483 """ 

3484 Requires that at least one C{ParseExpression} is found. 

3485 If two expressions match, the first one listed is the one that will match. 

3486 May be constructed using the C{'|'} operator. 

3487 

3488 Example:: 

3489 # construct MatchFirst using '|' operator 

3490  

3491 # watch the order of expressions to match 

3492 number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) 

3493 print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] 

3494 

3495 # put more selective expression first 

3496 number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) 

3497 print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] 

3498 """ 

3499 def __init__( self, exprs, savelist = False ): 

3500 super(MatchFirst,self).__init__(exprs, savelist) 

3501 if self.exprs: 

3502 self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) 

3503 else: 

3504 self.mayReturnEmpty = True 

3505 

3506 def parseImpl( self, instring, loc, doActions=True ): 

3507 maxExcLoc = -1 

3508 maxException = None 

3509 for e in self.exprs: 

3510 try: 

3511 ret = e._parse( instring, loc, doActions ) 

3512 return ret 

3513 except ParseException as err: 

3514 if err.loc > maxExcLoc: 

3515 maxException = err 

3516 maxExcLoc = err.loc 

3517 except IndexError: 

3518 if len(instring) > maxExcLoc: 

3519 maxException = ParseException(instring,len(instring),e.errmsg,self) 

3520 maxExcLoc = len(instring) 

3521 

3522 # only got here if no expression matched, raise exception for match that made it the furthest 

3523 else: 

3524 if maxException is not None: 

3525 maxException.msg = self.errmsg 

3526 raise maxException 

3527 else: 

3528 raise ParseException(instring, loc, "no defined alternatives to match", self) 

3529 

3530 def __ior__(self, other ): 

3531 if isinstance( other, basestring ): 

3532 other = ParserElement._literalStringClass( other ) 

3533 return self.append( other ) #MatchFirst( [ self, other ] ) 

3534 

3535 def __str__( self ): 

3536 if hasattr(self,"name"): 

3537 return self.name 

3538 

3539 if self.strRepr is None: 

3540 self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}" 

3541 

3542 return self.strRepr 

3543 

3544 def checkRecursion( self, parseElementList ): 

3545 subRecCheckList = parseElementList[:] + [ self ] 

3546 for e in self.exprs: 

3547 e.checkRecursion( subRecCheckList ) 

3548 

3549 

3550class Each(ParseExpression): 

3551 """ 

3552 Requires all given C{ParseExpression}s to be found, but in any order. 

3553 Expressions may be separated by whitespace. 

3554 May be constructed using the C{'&'} operator. 

3555 

3556 Example:: 

3557 color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") 

3558 shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") 

3559 integer = Word(nums) 

3560 shape_attr = "shape:" + shape_type("shape") 

3561 posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") 

3562 color_attr = "color:" + color("color") 

3563 size_attr = "size:" + integer("size") 

3564 

3565 # use Each (using operator '&') to accept attributes in any order  

3566 # (shape and posn are required, color and size are optional) 

3567 shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) 

3568 

3569 shape_spec.runTests(''' 

3570 shape: SQUARE color: BLACK posn: 100, 120 

3571 shape: CIRCLE size: 50 color: BLUE posn: 50,80 

3572 color:GREEN size:20 shape:TRIANGLE posn:20,40 

3573 ''' 

3574 ) 

3575 prints:: 

3576 shape: SQUARE color: BLACK posn: 100, 120 

3577 ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] 

3578 - color: BLACK 

3579 - posn: ['100', ',', '120'] 

3580 - x: 100 

3581 - y: 120 

3582 - shape: SQUARE 

3583 

3584 

3585 shape: CIRCLE size: 50 color: BLUE posn: 50,80 

3586 ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] 

3587 - color: BLUE 

3588 - posn: ['50', ',', '80'] 

3589 - x: 50 

3590 - y: 80 

3591 - shape: CIRCLE 

3592 - size: 50 

3593 

3594 

3595 color: GREEN size: 20 shape: TRIANGLE posn: 20,40 

3596 ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] 

3597 - color: GREEN 

3598 - posn: ['20', ',', '40'] 

3599 - x: 20 

3600 - y: 40 

3601 - shape: TRIANGLE 

3602 - size: 20 

3603 """ 

3604 def __init__( self, exprs, savelist = True ): 

3605 super(Each,self).__init__(exprs, savelist) 

3606 self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) 

3607 self.skipWhitespace = True 

3608 self.initExprGroups = True 

3609 

3610 def parseImpl( self, instring, loc, doActions=True ): 

3611 if self.initExprGroups: 

3612 self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional)) 

3613 opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ] 

3614 opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)] 

3615 self.optionals = opt1 + opt2 

3616 self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] 

3617 self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] 

3618 self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] 

3619 self.required += self.multirequired 

3620 self.initExprGroups = False 

3621 tmpLoc = loc 

3622 tmpReqd = self.required[:] 

3623 tmpOpt = self.optionals[:] 

3624 matchOrder = [] 

3625 

3626 keepMatching = True 

3627 while keepMatching: 

3628 tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired 

3629 failed = [] 

3630 for e in tmpExprs: 

3631 try: 

3632 tmpLoc = e.tryParse( instring, tmpLoc ) 

3633 except ParseException: 

3634 failed.append(e) 

3635 else: 

3636 matchOrder.append(self.opt1map.get(id(e),e)) 

3637 if e in tmpReqd: 

3638 tmpReqd.remove(e) 

3639 elif e in tmpOpt: 

3640 tmpOpt.remove(e) 

3641 if len(failed) == len(tmpExprs): 

3642 keepMatching = False 

3643 

3644 if tmpReqd: 

3645 missing = ", ".join(_ustr(e) for e in tmpReqd) 

3646 raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) 

3647 

3648 # add any unmatched Optionals, in case they have default values defined 

3649 matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt] 

3650 

3651 resultlist = [] 

3652 for e in matchOrder: 

3653 loc,results = e._parse(instring,loc,doActions) 

3654 resultlist.append(results) 

3655 

3656 finalResults = sum(resultlist, ParseResults([])) 

3657 return loc, finalResults 

3658 

3659 def __str__( self ): 

3660 if hasattr(self,"name"): 

3661 return self.name 

3662 

3663 if self.strRepr is None: 

3664 self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}" 

3665 

3666 return self.strRepr 

3667 

3668 def checkRecursion( self, parseElementList ): 

3669 subRecCheckList = parseElementList[:] + [ self ] 

3670 for e in self.exprs: 

3671 e.checkRecursion( subRecCheckList ) 

3672 

3673 

3674class ParseElementEnhance(ParserElement): 

3675 """ 

3676 Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens. 

3677 """ 

3678 def __init__( self, expr, savelist=False ): 

3679 super(ParseElementEnhance,self).__init__(savelist) 

3680 if isinstance( expr, basestring ): 

3681 if issubclass(ParserElement._literalStringClass, Token): 

3682 expr = ParserElement._literalStringClass(expr) 

3683 else: 

3684 expr = ParserElement._literalStringClass(Literal(expr)) 

3685 self.expr = expr 

3686 self.strRepr = None 

3687 if expr is not None: 

3688 self.mayIndexError = expr.mayIndexError 

3689 self.mayReturnEmpty = expr.mayReturnEmpty 

3690 self.setWhitespaceChars( expr.whiteChars ) 

3691 self.skipWhitespace = expr.skipWhitespace 

3692 self.saveAsList = expr.saveAsList 

3693 self.callPreparse = expr.callPreparse 

3694 self.ignoreExprs.extend(expr.ignoreExprs) 

3695 

3696 def parseImpl( self, instring, loc, doActions=True ): 

3697 if self.expr is not None: 

3698 return self.expr._parse( instring, loc, doActions, callPreParse=False ) 

3699 else: 

3700 raise ParseException("",loc,self.errmsg,self) 

3701 

3702 def leaveWhitespace( self ): 

3703 self.skipWhitespace = False 

3704 self.expr = self.expr.copy() 

3705 if self.expr is not None: 

3706 self.expr.leaveWhitespace() 

3707 return self 

3708 

3709 def ignore( self, other ): 

3710 if isinstance( other, Suppress ): 

3711 if other not in self.ignoreExprs: 

3712 super( ParseElementEnhance, self).ignore( other ) 

3713 if self.expr is not None: 

3714 self.expr.ignore( self.ignoreExprs[-1] ) 

3715 else: 

3716 super( ParseElementEnhance, self).ignore( other ) 

3717 if self.expr is not None: 

3718 self.expr.ignore( self.ignoreExprs[-1] ) 

3719 return self 

3720 

3721 def streamline( self ): 

3722 super(ParseElementEnhance,self).streamline() 

3723 if self.expr is not None: 

3724 self.expr.streamline() 

3725 return self 

3726 

3727 def checkRecursion( self, parseElementList ): 

3728 if self in parseElementList: 

3729 raise RecursiveGrammarException( parseElementList+[self] ) 

3730 subRecCheckList = parseElementList[:] + [ self ] 

3731 if self.expr is not None: 

3732 self.expr.checkRecursion( subRecCheckList ) 

3733 

3734 def validate( self, validateTrace=[] ): 

3735 tmp = validateTrace[:]+[self] 

3736 if self.expr is not None: 

3737 self.expr.validate(tmp) 

3738 self.checkRecursion( [] ) 

3739 

3740 def __str__( self ): 

3741 try: 

3742 return super(ParseElementEnhance,self).__str__() 

3743 except Exception: 

3744 pass 

3745 

3746 if self.strRepr is None and self.expr is not None: 

3747 self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) 

3748 return self.strRepr 

3749 

3750 

3751class FollowedBy(ParseElementEnhance): 

3752 """ 

3753 Lookahead matching of the given parse expression. C{FollowedBy} 

3754 does I{not} advance the parsing position within the input string, it only 

3755 verifies that the specified parse expression matches at the current 

3756 position. C{FollowedBy} always returns a null token list. 

3757 

3758 Example:: 

3759 # use FollowedBy to match a label only if it is followed by a ':' 

3760 data_word = Word(alphas) 

3761 label = data_word + FollowedBy(':') 

3762 attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) 

3763  

3764 OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() 

3765 prints:: 

3766 [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] 

3767 """ 

3768 def __init__( self, expr ): 

3769 super(FollowedBy,self).__init__(expr) 

3770 self.mayReturnEmpty = True 

3771 

3772 def parseImpl( self, instring, loc, doActions=True ): 

3773 self.expr.tryParse( instring, loc ) 

3774 return loc, [] 

3775 

3776 

3777class NotAny(ParseElementEnhance): 

3778 """ 

3779 Lookahead to disallow matching with the given parse expression. C{NotAny} 

3780 does I{not} advance the parsing position within the input string, it only 

3781 verifies that the specified parse expression does I{not} match at the current 

3782 position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} 

3783 always returns a null token list. May be constructed using the '~' operator. 

3784 

3785 Example:: 

3786  

3787 """ 

3788 def __init__( self, expr ): 

3789 super(NotAny,self).__init__(expr) 

3790 #~ self.leaveWhitespace() 

3791 self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs 

3792 self.mayReturnEmpty = True 

3793 self.errmsg = "Found unwanted token, "+_ustr(self.expr) 

3794 

3795 def parseImpl( self, instring, loc, doActions=True ): 

3796 if self.expr.canParseNext(instring, loc): 

3797 raise ParseException(instring, loc, self.errmsg, self) 

3798 return loc, [] 

3799 

3800 def __str__( self ): 

3801 if hasattr(self,"name"): 

3802 return self.name 

3803 

3804 if self.strRepr is None: 

3805 self.strRepr = "~{" + _ustr(self.expr) + "}" 

3806 

3807 return self.strRepr 

3808 

3809class _MultipleMatch(ParseElementEnhance): 

3810 def __init__( self, expr, stopOn=None): 

3811 super(_MultipleMatch, self).__init__(expr) 

3812 self.saveAsList = True 

3813 ender = stopOn 

3814 if isinstance(ender, basestring): 

3815 ender = ParserElement._literalStringClass(ender) 

3816 self.not_ender = ~ender if ender is not None else None 

3817 

3818 def parseImpl( self, instring, loc, doActions=True ): 

3819 self_expr_parse = self.expr._parse 

3820 self_skip_ignorables = self._skipIgnorables 

3821 check_ender = self.not_ender is not None 

3822 if check_ender: 

3823 try_not_ender = self.not_ender.tryParse 

3824 

3825 # must be at least one (but first see if we are the stopOn sentinel; 

3826 # if so, fail) 

3827 if check_ender: 

3828 try_not_ender(instring, loc) 

3829 loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) 

3830 try: 

3831 hasIgnoreExprs = (not not self.ignoreExprs) 

3832 while 1: 

3833 if check_ender: 

3834 try_not_ender(instring, loc) 

3835 if hasIgnoreExprs: 

3836 preloc = self_skip_ignorables( instring, loc ) 

3837 else: 

3838 preloc = loc 

3839 loc, tmptokens = self_expr_parse( instring, preloc, doActions ) 

3840 if tmptokens or tmptokens.haskeys(): 

3841 tokens += tmptokens 

3842 except (ParseException,IndexError): 

3843 pass 

3844 

3845 return loc, tokens 

3846 

3847class OneOrMore(_MultipleMatch): 

3848 """ 

3849 Repetition of one or more of the given expression. 

3850  

3851 Parameters: 

3852 - expr - expression that must match one or more times 

3853 - stopOn - (default=C{None}) - expression for a terminating sentinel 

3854 (only required if the sentinel would ordinarily match the repetition  

3855 expression)  

3856 

3857 Example:: 

3858 data_word = Word(alphas) 

3859 label = data_word + FollowedBy(':') 

3860 attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) 

3861 

3862 text = "shape: SQUARE posn: upper left color: BLACK" 

3863 OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] 

3864 

3865 # use stopOn attribute for OneOrMore to avoid reading label string as part of the data 

3866 attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) 

3867 OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] 

3868  

3869 # could also be written as 

3870 (attr_expr * (1,)).parseString(text).pprint() 

3871 """ 

3872 

3873 def __str__( self ): 

3874 if hasattr(self,"name"): 

3875 return self.name 

3876 

3877 if self.strRepr is None: 

3878 self.strRepr = "{" + _ustr(self.expr) + "}..." 

3879 

3880 return self.strRepr 

3881 

3882class ZeroOrMore(_MultipleMatch): 

3883 """ 

3884 Optional repetition of zero or more of the given expression. 

3885  

3886 Parameters: 

3887 - expr - expression that must match zero or more times 

3888 - stopOn - (default=C{None}) - expression for a terminating sentinel 

3889 (only required if the sentinel would ordinarily match the repetition  

3890 expression)  

3891 

3892 Example: similar to L{OneOrMore} 

3893 """ 

3894 def __init__( self, expr, stopOn=None): 

3895 super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) 

3896 self.mayReturnEmpty = True 

3897 

3898 def parseImpl( self, instring, loc, doActions=True ): 

3899 try: 

3900 return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) 

3901 except (ParseException,IndexError): 

3902 return loc, [] 

3903 

3904 def __str__( self ): 

3905 if hasattr(self,"name"): 

3906 return self.name 

3907 

3908 if self.strRepr is None: 

3909 self.strRepr = "[" + _ustr(self.expr) + "]..." 

3910 

3911 return self.strRepr 

3912 

3913class _NullToken(object): 

3914 def __bool__(self): 

3915 return False 

3916 __nonzero__ = __bool__ 

3917 def __str__(self): 

3918 return "" 

3919 

3920_optionalNotMatched = _NullToken() 

3921class Optional(ParseElementEnhance): 

3922 """ 

3923 Optional matching of the given expression. 

3924 

3925 Parameters: 

3926 - expr - expression that must match zero or more times 

3927 - default (optional) - value to be returned if the optional expression is not found. 

3928 

3929 Example:: 

3930 # US postal code can be a 5-digit zip, plus optional 4-digit qualifier 

3931 zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) 

3932 zip.runTests(''' 

3933 # traditional ZIP code 

3934 12345 

3935  

3936 # ZIP+4 form 

3937 12101-0001 

3938  

3939 # invalid ZIP 

3940 98765- 

3941 ''') 

3942 prints:: 

3943 # traditional ZIP code 

3944 12345 

3945 ['12345'] 

3946 

3947 # ZIP+4 form 

3948 12101-0001 

3949 ['12101-0001'] 

3950 

3951 # invalid ZIP 

3952 98765- 

3953 ^ 

3954 FAIL: Expected end of text (at char 5), (line:1, col:6) 

3955 """ 

3956 def __init__( self, expr, default=_optionalNotMatched ): 

3957 super(Optional,self).__init__( expr, savelist=False ) 

3958 self.saveAsList = self.expr.saveAsList 

3959 self.defaultValue = default 

3960 self.mayReturnEmpty = True 

3961 

3962 def parseImpl( self, instring, loc, doActions=True ): 

3963 try: 

3964 loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) 

3965 except (ParseException,IndexError): 

3966 if self.defaultValue is not _optionalNotMatched: 

3967 if self.expr.resultsName: 

3968 tokens = ParseResults([ self.defaultValue ]) 

3969 tokens[self.expr.resultsName] = self.defaultValue 

3970 else: 

3971 tokens = [ self.defaultValue ] 

3972 else: 

3973 tokens = [] 

3974 return loc, tokens 

3975 

3976 def __str__( self ): 

3977 if hasattr(self,"name"): 

3978 return self.name 

3979 

3980 if self.strRepr is None: 

3981 self.strRepr = "[" + _ustr(self.expr) + "]" 

3982 

3983 return self.strRepr 

3984 

3985class SkipTo(ParseElementEnhance): 

3986 """ 

3987 Token for skipping over all undefined text until the matched expression is found. 

3988 

3989 Parameters: 

3990 - expr - target expression marking the end of the data to be skipped 

3991 - include - (default=C{False}) if True, the target expression is also parsed  

3992 (the skipped text and target expression are returned as a 2-element list). 

3993 - ignore - (default=C{None}) used to define grammars (typically quoted strings and  

3994 comments) that might contain false matches to the target expression 

3995 - failOn - (default=C{None}) define expressions that are not allowed to be  

3996 included in the skipped test; if found before the target expression is found,  

3997 the SkipTo is not a match 

3998 

3999 Example:: 

4000 report = ''' 

4001 Outstanding Issues Report - 1 Jan 2000 

4002 

4003 # | Severity | Description | Days Open 

4004 -----+----------+-------------------------------------------+----------- 

4005 101 | Critical | Intermittent system crash | 6 

4006 94 | Cosmetic | Spelling error on Login ('log|n') | 14 

4007 79 | Minor | System slow when running too many reports | 47 

4008 ''' 

4009 integer = Word(nums) 

4010 SEP = Suppress('|') 

4011 # use SkipTo to simply match everything up until the next SEP 

4012 # - ignore quoted strings, so that a '|' character inside a quoted string does not match 

4013 # - parse action will call token.strip() for each matched token, i.e., the description body 

4014 string_data = SkipTo(SEP, ignore=quotedString) 

4015 string_data.setParseAction(tokenMap(str.strip)) 

4016 ticket_expr = (integer("issue_num") + SEP  

4017 + string_data("sev") + SEP  

4018 + string_data("desc") + SEP  

4019 + integer("days_open")) 

4020  

4021 for tkt in ticket_expr.searchString(report): 

4022 print tkt.dump() 

4023 prints:: 

4024 ['101', 'Critical', 'Intermittent system crash', '6'] 

4025 - days_open: 6 

4026 - desc: Intermittent system crash 

4027 - issue_num: 101 

4028 - sev: Critical 

4029 ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] 

4030 - days_open: 14 

4031 - desc: Spelling error on Login ('log|n') 

4032 - issue_num: 94 

4033 - sev: Cosmetic 

4034 ['79', 'Minor', 'System slow when running too many reports', '47'] 

4035 - days_open: 47 

4036 - desc: System slow when running too many reports 

4037 - issue_num: 79 

4038 - sev: Minor 

4039 """ 

4040 def __init__( self, other, include=False, ignore=None, failOn=None ): 

4041 super( SkipTo, self ).__init__( other ) 

4042 self.ignoreExpr = ignore 

4043 self.mayReturnEmpty = True 

4044 self.mayIndexError = False 

4045 self.includeMatch = include 

4046 self.asList = False 

4047 if isinstance(failOn, basestring): 

4048 self.failOn = ParserElement._literalStringClass(failOn) 

4049 else: 

4050 self.failOn = failOn 

4051 self.errmsg = "No match found for "+_ustr(self.expr) 

4052 

4053 def parseImpl( self, instring, loc, doActions=True ): 

4054 startloc = loc 

4055 instrlen = len(instring) 

4056 expr = self.expr 

4057 expr_parse = self.expr._parse 

4058 self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None 

4059 self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None 

4060 

4061 tmploc = loc 

4062 while tmploc <= instrlen: 

4063 if self_failOn_canParseNext is not None: 

4064 # break if failOn expression matches 

4065 if self_failOn_canParseNext(instring, tmploc): 

4066 break 

4067 

4068 if self_ignoreExpr_tryParse is not None: 

4069 # advance past ignore expressions 

4070 while 1: 

4071 try: 

4072 tmploc = self_ignoreExpr_tryParse(instring, tmploc) 

4073 except ParseBaseException: 

4074 break 

4075 

4076 try: 

4077 expr_parse(instring, tmploc, doActions=False, callPreParse=False) 

4078 except (ParseException, IndexError): 

4079 # no match, advance loc in string 

4080 tmploc += 1 

4081 else: 

4082 # matched skipto expr, done 

4083 break 

4084 

4085 else: 

4086 # ran off the end of the input string without matching skipto expr, fail 

4087 raise ParseException(instring, loc, self.errmsg, self) 

4088 

4089 # build up return values 

4090 loc = tmploc 

4091 skiptext = instring[startloc:loc] 

4092 skipresult = ParseResults(skiptext) 

4093 

4094 if self.includeMatch: 

4095 loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) 

4096 skipresult += mat 

4097 

4098 return loc, skipresult 

4099 

4100class Forward(ParseElementEnhance): 

4101 """ 

4102 Forward declaration of an expression to be defined later - 

4103 used for recursive grammars, such as algebraic infix notation. 

4104 When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. 

4105 

4106 Note: take care when assigning to C{Forward} not to overlook precedence of operators. 

4107 Specifically, '|' has a lower precedence than '<<', so that:: 

4108 fwdExpr << a | b | c 

4109 will actually be evaluated as:: 

4110 (fwdExpr << a) | b | c 

4111 thereby leaving b and c out as parseable alternatives. It is recommended that you 

4112 explicitly group the values inserted into the C{Forward}:: 

4113 fwdExpr << (a | b | c) 

4114 Converting to use the '<<=' operator instead will avoid this problem. 

4115 

4116 See L{ParseResults.pprint} for an example of a recursive parser created using 

4117 C{Forward}. 

4118 """ 

4119 def __init__( self, other=None ): 

4120 super(Forward,self).__init__( other, savelist=False ) 

4121 

4122 def __lshift__( self, other ): 

4123 if isinstance( other, basestring ): 

4124 other = ParserElement._literalStringClass(other) 

4125 self.expr = other 

4126 self.strRepr = None 

4127 self.mayIndexError = self.expr.mayIndexError 

4128 self.mayReturnEmpty = self.expr.mayReturnEmpty 

4129 self.setWhitespaceChars( self.expr.whiteChars ) 

4130 self.skipWhitespace = self.expr.skipWhitespace 

4131 self.saveAsList = self.expr.saveAsList 

4132 self.ignoreExprs.extend(self.expr.ignoreExprs) 

4133 return self 

4134 

4135 def __ilshift__(self, other): 

4136 return self << other 

4137 

4138 def leaveWhitespace( self ): 

4139 self.skipWhitespace = False 

4140 return self 

4141 

4142 def streamline( self ): 

4143 if not self.streamlined: 

4144 self.streamlined = True 

4145 if self.expr is not None: 

4146 self.expr.streamline() 

4147 return self 

4148 

4149 def validate( self, validateTrace=[] ): 

4150 if self not in validateTrace: 

4151 tmp = validateTrace[:]+[self] 

4152 if self.expr is not None: 

4153 self.expr.validate(tmp) 

4154 self.checkRecursion([]) 

4155 

4156 def __str__( self ): 

4157 if hasattr(self,"name"): 

4158 return self.name 

4159 return self.__class__.__name__ + ": ..." 

4160 

4161 # stubbed out for now - creates awful memory and perf issues 

4162 self._revertClass = self.__class__ 

4163 self.__class__ = _ForwardNoRecurse 

4164 try: 

4165 if self.expr is not None: 

4166 retString = _ustr(self.expr) 

4167 else: 

4168 retString = "None" 

4169 finally: 

4170 self.__class__ = self._revertClass 

4171 return self.__class__.__name__ + ": " + retString 

4172 

4173 def copy(self): 

4174 if self.expr is not None: 

4175 return super(Forward,self).copy() 

4176 else: 

4177 ret = Forward() 

4178 ret <<= self 

4179 return ret 

4180 

4181class _ForwardNoRecurse(Forward): 

4182 def __str__( self ): 

4183 return "..." 

4184 

4185class TokenConverter(ParseElementEnhance): 

4186 """ 

4187 Abstract subclass of C{ParseExpression}, for converting parsed results. 

4188 """ 

4189 def __init__( self, expr, savelist=False ): 

4190 super(TokenConverter,self).__init__( expr )#, savelist ) 

4191 self.saveAsList = False 

4192 

4193class Combine(TokenConverter): 

4194 """ 

4195 Converter to concatenate all matching tokens to a single string. 

4196 By default, the matching patterns must also be contiguous in the input string; 

4197 this can be disabled by specifying C{'adjacent=False'} in the constructor. 

4198 

4199 Example:: 

4200 real = Word(nums) + '.' + Word(nums) 

4201 print(real.parseString('3.1416')) # -> ['3', '.', '1416'] 

4202 # will also erroneously match the following 

4203 print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] 

4204 

4205 real = Combine(Word(nums) + '.' + Word(nums)) 

4206 print(real.parseString('3.1416')) # -> ['3.1416'] 

4207 # no match when there are internal spaces 

4208 print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) 

4209 """ 

4210 def __init__( self, expr, joinString="", adjacent=True ): 

4211 super(Combine,self).__init__( expr ) 

4212 # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself 

4213 if adjacent: 

4214 self.leaveWhitespace() 

4215 self.adjacent = adjacent 

4216 self.skipWhitespace = True 

4217 self.joinString = joinString 

4218 self.callPreparse = True 

4219 

4220 def ignore( self, other ): 

4221 if self.adjacent: 

4222 ParserElement.ignore(self, other) 

4223 else: 

4224 super( Combine, self).ignore( other ) 

4225 return self 

4226 

4227 def postParse( self, instring, loc, tokenlist ): 

4228 retToks = tokenlist.copy() 

4229 del retToks[:] 

4230 retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) 

4231 

4232 if self.resultsName and retToks.haskeys(): 

4233 return [ retToks ] 

4234 else: 

4235 return retToks 

4236 

4237class Group(TokenConverter): 

4238 """ 

4239 Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions. 

4240 

4241 Example:: 

4242 ident = Word(alphas) 

4243 num = Word(nums) 

4244 term = ident | num 

4245 func = ident + Optional(delimitedList(term)) 

4246 print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] 

4247 

4248 func = ident + Group(Optional(delimitedList(term))) 

4249 print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] 

4250 """ 

4251 def __init__( self, expr ): 

4252 super(Group,self).__init__( expr ) 

4253 self.saveAsList = True 

4254 

4255 def postParse( self, instring, loc, tokenlist ): 

4256 return [ tokenlist ] 

4257 

4258class Dict(TokenConverter): 

4259 """ 

4260 Converter to return a repetitive expression as a list, but also as a dictionary. 

4261 Each element can also be referenced using the first token in the expression as its key. 

4262 Useful for tabular report scraping when the first column can be used as a item key. 

4263 

4264 Example:: 

4265 data_word = Word(alphas) 

4266 label = data_word + FollowedBy(':') 

4267 attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) 

4268 

4269 text = "shape: SQUARE posn: upper left color: light blue texture: burlap" 

4270 attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) 

4271  

4272 # print attributes as plain groups 

4273 print(OneOrMore(attr_expr).parseString(text).dump()) 

4274  

4275 # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names 

4276 result = Dict(OneOrMore(Group(attr_expr))).parseString(text) 

4277 print(result.dump()) 

4278  

4279 # access named fields as dict entries, or output as dict 

4280 print(result['shape'])  

4281 print(result.asDict()) 

4282 prints:: 

4283 ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] 

4284 

4285 [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] 

4286 - color: light blue 

4287 - posn: upper left 

4288 - shape: SQUARE 

4289 - texture: burlap 

4290 SQUARE 

4291 {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} 

4292 See more examples at L{ParseResults} of accessing fields by results name. 

4293 """ 

4294 def __init__( self, expr ): 

4295 super(Dict,self).__init__( expr ) 

4296 self.saveAsList = True 

4297 

4298 def postParse( self, instring, loc, tokenlist ): 

4299 for i,tok in enumerate(tokenlist): 

4300 if len(tok) == 0: 

4301 continue 

4302 ikey = tok[0] 

4303 if isinstance(ikey,int): 

4304 ikey = _ustr(tok[0]).strip() 

4305 if len(tok)==1: 

4306 tokenlist[ikey] = _ParseResultsWithOffset("",i) 

4307 elif len(tok)==2 and not isinstance(tok[1],ParseResults): 

4308 tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) 

4309 else: 

4310 dictvalue = tok.copy() #ParseResults(i) 

4311 del dictvalue[0] 

4312 if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()): 

4313 tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) 

4314 else: 

4315 tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) 

4316 

4317 if self.resultsName: 

4318 return [ tokenlist ] 

4319 else: 

4320 return tokenlist 

4321 

4322 

4323class Suppress(TokenConverter): 

4324 """ 

4325 Converter for ignoring the results of a parsed expression. 

4326 

4327 Example:: 

4328 source = "a, b, c,d" 

4329 wd = Word(alphas) 

4330 wd_list1 = wd + ZeroOrMore(',' + wd) 

4331 print(wd_list1.parseString(source)) 

4332 

4333 # often, delimiters that are useful during parsing are just in the 

4334 # way afterward - use Suppress to keep them out of the parsed output 

4335 wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) 

4336 print(wd_list2.parseString(source)) 

4337 prints:: 

4338 ['a', ',', 'b', ',', 'c', ',', 'd'] 

4339 ['a', 'b', 'c', 'd'] 

4340 (See also L{delimitedList}.) 

4341 """ 

4342 def postParse( self, instring, loc, tokenlist ): 

4343 return [] 

4344 

4345 def suppress( self ): 

4346 return self 

4347 

4348 

4349class OnlyOnce(object): 

4350 """ 

4351 Wrapper for parse actions, to ensure they are only called once. 

4352 """ 

4353 def __init__(self, methodCall): 

4354 self.callable = _trim_arity(methodCall) 

4355 self.called = False 

4356 def __call__(self,s,l,t): 

4357 if not self.called: 

4358 results = self.callable(s,l,t) 

4359 self.called = True 

4360 return results 

4361 raise ParseException(s,l,"") 

4362 def reset(self): 

4363 self.called = False 

4364 

4365def traceParseAction(f): 

4366 """ 

4367 Decorator for debugging parse actions.  

4368  

4369 When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} 

4370 When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. 

4371 

4372 Example:: 

4373 wd = Word(alphas) 

4374 

4375 @traceParseAction 

4376 def remove_duplicate_chars(tokens): 

4377 return ''.join(sorted(set(''.join(tokens))) 

4378 

4379 wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) 

4380 print(wds.parseString("slkdjs sld sldd sdlf sdljf")) 

4381 prints:: 

4382 >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) 

4383 <<leaving remove_duplicate_chars (ret: 'dfjkls') 

4384 ['dfjkls'] 

4385 """ 

4386 f = _trim_arity(f) 

4387 def z(*paArgs): 

4388 thisFunc = f.__name__ 

4389 s,l,t = paArgs[-3:] 

4390 if len(paArgs)>3: 

4391 thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc 

4392 sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) ) 

4393 try: 

4394 ret = f(*paArgs) 

4395 except Exception as exc: 

4396 sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) ) 

4397 raise 

4398 sys.stderr.write( "<<leaving %s (ret: %r)\n" % (thisFunc,ret) ) 

4399 return ret 

4400 try: 

4401 z.__name__ = f.__name__ 

4402 except AttributeError: 

4403 pass 

4404 return z 

4405 

4406# 

4407# global helpers 

4408# 

4409def delimitedList( expr, delim=",", combine=False ): 

4410 """ 

4411 Helper to define a delimited list of expressions - the delimiter defaults to ','. 

4412 By default, the list elements and delimiters can have intervening whitespace, and 

4413 comments, but this can be overridden by passing C{combine=True} in the constructor. 

4414 If C{combine} is set to C{True}, the matching tokens are returned as a single token 

4415 string, with the delimiters included; otherwise, the matching tokens are returned 

4416 as a list of tokens, with the delimiters suppressed. 

4417 

4418 Example:: 

4419 delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] 

4420 delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] 

4421 """ 

4422 dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..." 

4423 if combine: 

4424 return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName) 

4425 else: 

4426 return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName) 

4427 

4428def countedArray( expr, intExpr=None ): 

4429 """ 

4430 Helper to define a counted list of expressions. 

4431 This helper defines a pattern of the form:: 

4432 integer expr expr expr... 

4433 where the leading integer tells how many expr expressions follow. 

4434 The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. 

4435  

4436 If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. 

4437 

4438 Example:: 

4439 countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] 

4440 

4441 # in this parser, the leading integer value is given in binary, 

4442 # '10' indicating that 2 values are in the array 

4443 binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) 

4444 countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] 

4445 """ 

4446 arrayExpr = Forward() 

4447 def countFieldParseAction(s,l,t): 

4448 n = t[0] 

4449 arrayExpr << (n and Group(And([expr]*n)) or Group(empty)) 

4450 return [] 

4451 if intExpr is None: 

4452 intExpr = Word(nums).setParseAction(lambda t:int(t[0])) 

4453 else: 

4454 intExpr = intExpr.copy() 

4455 intExpr.setName("arrayLen") 

4456 intExpr.addParseAction(countFieldParseAction, callDuringTry=True) 

4457 return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...') 

4458 

4459def _flatten(L): 

4460 ret = [] 

4461 for i in L: 

4462 if isinstance(i,list): 

4463 ret.extend(_flatten(i)) 

4464 else: 

4465 ret.append(i) 

4466 return ret 

4467 

4468def matchPreviousLiteral(expr): 

4469 """ 

4470 Helper to define an expression that is indirectly defined from 

4471 the tokens matched in a previous expression, that is, it looks 

4472 for a 'repeat' of a previous expression. For example:: 

4473 first = Word(nums) 

4474 second = matchPreviousLiteral(first) 

4475 matchExpr = first + ":" + second 

4476 will match C{"1:1"}, but not C{"1:2"}. Because this matches a 

4477 previous literal, will also match the leading C{"1:1"} in C{"1:10"}. 

4478 If this is not desired, use C{matchPreviousExpr}. 

4479 Do I{not} use with packrat parsing enabled. 

4480 """ 

4481 rep = Forward() 

4482 def copyTokenToRepeater(s,l,t): 

4483 if t: 

4484 if len(t) == 1: 

4485 rep << t[0] 

4486 else: 

4487 # flatten t tokens 

4488 tflat = _flatten(t.asList()) 

4489 rep << And(Literal(tt) for tt in tflat) 

4490 else: 

4491 rep << Empty() 

4492 expr.addParseAction(copyTokenToRepeater, callDuringTry=True) 

4493 rep.setName('(prev) ' + _ustr(expr)) 

4494 return rep 

4495 

4496def matchPreviousExpr(expr): 

4497 """ 

4498 Helper to define an expression that is indirectly defined from 

4499 the tokens matched in a previous expression, that is, it looks 

4500 for a 'repeat' of a previous expression. For example:: 

4501 first = Word(nums) 

4502 second = matchPreviousExpr(first) 

4503 matchExpr = first + ":" + second 

4504 will match C{"1:1"}, but not C{"1:2"}. Because this matches by 

4505 expressions, will I{not} match the leading C{"1:1"} in C{"1:10"}; 

4506 the expressions are evaluated first, and then compared, so 

4507 C{"1"} is compared with C{"10"}. 

4508 Do I{not} use with packrat parsing enabled. 

4509 """ 

4510 rep = Forward() 

4511 e2 = expr.copy() 

4512 rep <<= e2 

4513 def copyTokenToRepeater(s,l,t): 

4514 matchTokens = _flatten(t.asList()) 

4515 def mustMatchTheseTokens(s,l,t): 

4516 theseTokens = _flatten(t.asList()) 

4517 if theseTokens != matchTokens: 

4518 raise ParseException("",0,"") 

4519 rep.setParseAction( mustMatchTheseTokens, callDuringTry=True ) 

4520 expr.addParseAction(copyTokenToRepeater, callDuringTry=True) 

4521 rep.setName('(prev) ' + _ustr(expr)) 

4522 return rep 

4523 

4524def _escapeRegexRangeChars(s): 

4525 #~ escape these chars: ^-] 

4526 for c in r"\^-]": 

4527 s = s.replace(c,_bslash+c) 

4528 s = s.replace("\n",r"\n") 

4529 s = s.replace("\t",r"\t") 

4530 return _ustr(s) 

4531 

4532def oneOf( strs, caseless=False, useRegex=True ): 

4533 """ 

4534 Helper to quickly define a set of alternative Literals, and makes sure to do 

4535 longest-first testing when there is a conflict, regardless of the input order, 

4536 but returns a C{L{MatchFirst}} for best performance. 

4537 

4538 Parameters: 

4539 - strs - a string of space-delimited literals, or a collection of string literals 

4540 - caseless - (default=C{False}) - treat all literals as caseless 

4541 - useRegex - (default=C{True}) - as an optimization, will generate a Regex 

4542 object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or 

4543 if creating a C{Regex} raises an exception) 

4544 

4545 Example:: 

4546 comp_oper = oneOf("< = > <= >= !=") 

4547 var = Word(alphas) 

4548 number = Word(nums) 

4549 term = var | number 

4550 comparison_expr = term + comp_oper + term 

4551 print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) 

4552 prints:: 

4553 [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] 

4554 """ 

4555 if caseless: 

4556 isequal = ( lambda a,b: a.upper() == b.upper() ) 

4557 masks = ( lambda a,b: b.upper().startswith(a.upper()) ) 

4558 parseElementClass = CaselessLiteral 

4559 else: 

4560 isequal = ( lambda a,b: a == b ) 

4561 masks = ( lambda a,b: b.startswith(a) ) 

4562 parseElementClass = Literal 

4563 

4564 symbols = [] 

4565 if isinstance(strs,basestring): 

4566 symbols = strs.split() 

4567 elif isinstance(strs, collections.Iterable): 

4568 symbols = list(strs) 

4569 else: 

4570 warnings.warn("Invalid argument to oneOf, expected string or iterable", 

4571 SyntaxWarning, stacklevel=2) 

4572 if not symbols: 

4573 return NoMatch() 

4574 

4575 i = 0 

4576 while i < len(symbols)-1: 

4577 cur = symbols[i] 

4578 for j,other in enumerate(symbols[i+1:]): 

4579 if ( isequal(other, cur) ): 

4580 del symbols[i+j+1] 

4581 break 

4582 elif ( masks(cur, other) ): 

4583 del symbols[i+j+1] 

4584 symbols.insert(i,other) 

4585 cur = other 

4586 break 

4587 else: 

4588 i += 1 

4589 

4590 if not caseless and useRegex: 

4591 #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) 

4592 try: 

4593 if len(symbols)==len("".join(symbols)): 

4594 return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols)) 

4595 else: 

4596 return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) 

4597 except Exception: 

4598 warnings.warn("Exception creating Regex for oneOf, building MatchFirst", 

4599 SyntaxWarning, stacklevel=2) 

4600 

4601 

4602 # last resort, just use MatchFirst 

4603 return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols)) 

4604 

4605def dictOf( key, value ): 

4606 """ 

4607 Helper to easily and clearly define a dictionary by specifying the respective patterns 

4608 for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens 

4609 in the proper order. The key pattern can include delimiting markers or punctuation, 

4610 as long as they are suppressed, thereby leaving the significant key text. The value 

4611 pattern can include named results, so that the C{Dict} results can include named token 

4612 fields. 

4613 

4614 Example:: 

4615 text = "shape: SQUARE posn: upper left color: light blue texture: burlap" 

4616 attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) 

4617 print(OneOrMore(attr_expr).parseString(text).dump()) 

4618  

4619 attr_label = label 

4620 attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) 

4621 

4622 # similar to Dict, but simpler call format 

4623 result = dictOf(attr_label, attr_value).parseString(text) 

4624 print(result.dump()) 

4625 print(result['shape']) 

4626 print(result.shape) # object attribute access works too 

4627 print(result.asDict()) 

4628 prints:: 

4629 [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] 

4630 - color: light blue 

4631 - posn: upper left 

4632 - shape: SQUARE 

4633 - texture: burlap 

4634 SQUARE 

4635 SQUARE 

4636 {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} 

4637 """ 

4638 return Dict( ZeroOrMore( Group ( key + value ) ) ) 

4639 

4640def originalTextFor(expr, asString=True): 

4641 """ 

4642 Helper to return the original, untokenized text for a given expression. Useful to 

4643 restore the parsed fields of an HTML start tag into the raw tag text itself, or to 

4644 revert separate tokens with intervening whitespace back to the original matching 

4645 input text. By default, returns astring containing the original parsed text.  

4646  

4647 If the optional C{asString} argument is passed as C{False}, then the return value is a  

4648 C{L{ParseResults}} containing any results names that were originally matched, and a  

4649 single token containing the original matched text from the input string. So if  

4650 the expression passed to C{L{originalTextFor}} contains expressions with defined 

4651 results names, you must set C{asString} to C{False} if you want to preserve those 

4652 results name values. 

4653 

4654 Example:: 

4655 src = "this is test <b> bold <i>text</i> </b> normal text " 

4656 for tag in ("b","i"): 

4657 opener,closer = makeHTMLTags(tag) 

4658 patt = originalTextFor(opener + SkipTo(closer) + closer) 

4659 print(patt.searchString(src)[0]) 

4660 prints:: 

4661 ['<b> bold <i>text</i> </b>'] 

4662 ['<i>text</i>'] 

4663 """ 

4664 locMarker = Empty().setParseAction(lambda s,loc,t: loc) 

4665 endlocMarker = locMarker.copy() 

4666 endlocMarker.callPreparse = False 

4667 matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") 

4668 if asString: 

4669 extractText = lambda s,l,t: s[t._original_start:t._original_end] 

4670 else: 

4671 def extractText(s,l,t): 

4672 t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] 

4673 matchExpr.setParseAction(extractText) 

4674 matchExpr.ignoreExprs = expr.ignoreExprs 

4675 return matchExpr 

4676 

4677def ungroup(expr): 

4678 """ 

4679 Helper to undo pyparsing's default grouping of And expressions, even 

4680 if all but one are non-empty. 

4681 """ 

4682 return TokenConverter(expr).setParseAction(lambda t:t[0]) 

4683 

4684def locatedExpr(expr): 

4685 """ 

4686 Helper to decorate a returned token with its starting and ending locations in the input string. 

4687 This helper adds the following results names: 

4688 - locn_start = location where matched expression begins 

4689 - locn_end = location where matched expression ends 

4690 - value = the actual parsed results 

4691 

4692 Be careful if the input text contains C{<TAB>} characters, you may want to call 

4693 C{L{ParserElement.parseWithTabs}} 

4694 

4695 Example:: 

4696 wd = Word(alphas) 

4697 for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): 

4698 print(match) 

4699 prints:: 

4700 [[0, 'ljsdf', 5]] 

4701 [[8, 'lksdjjf', 15]] 

4702 [[18, 'lkkjj', 23]] 

4703 """ 

4704 locator = Empty().setParseAction(lambda s,l,t: l) 

4705 return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) 

4706 

4707 

4708# convenience constants for positional expressions 

4709empty = Empty().setName("empty") 

4710lineStart = LineStart().setName("lineStart") 

4711lineEnd = LineEnd().setName("lineEnd") 

4712stringStart = StringStart().setName("stringStart") 

4713stringEnd = StringEnd().setName("stringEnd") 

4714 

4715_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) 

4716_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16))) 

4717_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8))) 

4718_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(printables, excludeChars=r'\]', exact=1) | Regex(r"\w", re.UNICODE) 

4719_charRange = Group(_singleChar + Suppress("-") + _singleChar) 

4720_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" 

4721 

4722def srange(s): 

4723 r""" 

4724 Helper to easily define string ranges for use in Word construction. Borrows 

4725 syntax from regexp '[]' string range definitions:: 

4726 srange("[0-9]") -> "0123456789" 

4727 srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" 

4728 srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" 

4729 The input string must be enclosed in []'s, and the returned string is the expanded 

4730 character set joined into a single string. 

4731 The values enclosed in the []'s may be: 

4732 - a single character 

4733 - an escaped character with a leading backslash (such as C{\-} or C{\]}) 

4734 - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character)  

4735 (C{\0x##} is also supported for backwards compatibility)  

4736 - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) 

4737 - a range of any of the above, separated by a dash (C{'a-z'}, etc.) 

4738 - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.) 

4739 """ 

4740 _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1)) 

4741 try: 

4742 return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) 

4743 except Exception: 

4744 return "" 

4745 

4746def matchOnlyAtCol(n): 

4747 """ 

4748 Helper method for defining parse actions that require matching at a specific 

4749 column in the input text. 

4750 """ 

4751 def verifyCol(strg,locn,toks): 

4752 if col(locn,strg) != n: 

4753 raise ParseException(strg,locn,"matched token not at column %d" % n) 

4754 return verifyCol 

4755 

4756def replaceWith(replStr): 

4757 """ 

4758 Helper method for common parse actions that simply return a literal value. Especially 

4759 useful when used with C{L{transformString<ParserElement.transformString>}()}. 

4760 

4761 Example:: 

4762 num = Word(nums).setParseAction(lambda toks: int(toks[0])) 

4763 na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) 

4764 term = na | num 

4765  

4766 OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] 

4767 """ 

4768 return lambda s,l,t: [replStr] 

4769 

4770def removeQuotes(s,l,t): 

4771 """ 

4772 Helper parse action for removing quotation marks from parsed quoted strings. 

4773 

4774 Example:: 

4775 # by default, quotation marks are included in parsed results 

4776 quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] 

4777 

4778 # use removeQuotes to strip quotation marks from parsed results 

4779 quotedString.setParseAction(removeQuotes) 

4780 quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] 

4781 """ 

4782 return t[0][1:-1] 

4783 

4784def tokenMap(func, *args): 

4785 """ 

4786 Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional  

4787 args are passed, they are forwarded to the given function as additional arguments after 

4788 the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the 

4789 parsed data to an integer using base 16. 

4790 

4791 Example (compare the last to example in L{ParserElement.transformString}:: 

4792 hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) 

4793 hex_ints.runTests(''' 

4794 00 11 22 aa FF 0a 0d 1a 

4795 ''') 

4796  

4797 upperword = Word(alphas).setParseAction(tokenMap(str.upper)) 

4798 OneOrMore(upperword).runTests(''' 

4799 my kingdom for a horse 

4800 ''') 

4801 

4802 wd = Word(alphas).setParseAction(tokenMap(str.title)) 

4803 OneOrMore(wd).setParseAction(' '.join).runTests(''' 

4804 now is the winter of our discontent made glorious summer by this sun of york 

4805 ''') 

4806 prints:: 

4807 00 11 22 aa FF 0a 0d 1a 

4808 [0, 17, 34, 170, 255, 10, 13, 26] 

4809 

4810 my kingdom for a horse 

4811 ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] 

4812 

4813 now is the winter of our discontent made glorious summer by this sun of york 

4814 ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] 

4815 """ 

4816 def pa(s,l,t): 

4817 return [func(tokn, *args) for tokn in t] 

4818 

4819 try: 

4820 func_name = getattr(func, '__name__', 

4821 getattr(func, '__class__').__name__) 

4822 except Exception: 

4823 func_name = str(func) 

4824 pa.__name__ = func_name 

4825 

4826 return pa 

4827 

4828upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) 

4829"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" 

4830 

4831downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) 

4832"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" 

4833 

4834def _makeTags(tagStr, xml): 

4835 """Internal helper to construct opening and closing tag expressions, given a tag name""" 

4836 if isinstance(tagStr,basestring): 

4837 resname = tagStr 

4838 tagStr = Keyword(tagStr, caseless=not xml) 

4839 else: 

4840 resname = tagStr.name 

4841 

4842 tagAttrName = Word(alphas,alphanums+"_-:") 

4843 if (xml): 

4844 tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) 

4845 openTag = Suppress("<") + tagStr("tag") + \ 

4846 Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ 

4847 Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") 

4848 else: 

4849 printablesLessRAbrack = "".join(c for c in printables if c not in ">") 

4850 tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) 

4851 openTag = Suppress("<") + tagStr("tag") + \ 

4852 Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ 

4853 Optional( Suppress("=") + tagAttrValue ) ))) + \ 

4854 Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") 

4855 closeTag = Combine(_L("</") + tagStr + ">") 

4856 

4857 openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname) 

4858 closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) 

4859 openTag.tag = resname 

4860 closeTag.tag = resname 

4861 return openTag, closeTag 

4862 

4863def makeHTMLTags(tagStr): 

4864 """ 

4865 Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches 

4866 tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. 

4867 

4868 Example:: 

4869 text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' 

4870 # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple 

4871 a,a_end = makeHTMLTags("A") 

4872 link_expr = a + SkipTo(a_end)("link_text") + a_end 

4873  

4874 for link in link_expr.searchString(text): 

4875 # attributes in the <A> tag (like "href" shown here) are also accessible as named results 

4876 print(link.link_text, '->', link.href) 

4877 prints:: 

4878 pyparsing -> http://pyparsing.wikispaces.com 

4879 """ 

4880 return _makeTags( tagStr, False ) 

4881 

4882def makeXMLTags(tagStr): 

4883 """ 

4884 Helper to construct opening and closing tag expressions for XML, given a tag name. Matches 

4885 tags only in the given upper/lower case. 

4886 

4887 Example: similar to L{makeHTMLTags} 

4888 """ 

4889 return _makeTags( tagStr, True ) 

4890 

4891def withAttribute(*args,**attrDict): 

4892 """ 

4893 Helper to create a validating parse action to be used with start tags created 

4894 with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag 

4895 with a required attribute value, to avoid false matches on common tags such as 

4896 C{<TD>} or C{<DIV>}. 

4897 

4898 Call C{withAttribute} with a series of attribute names and values. Specify the list 

4899 of filter attributes names and values as: 

4900 - keyword arguments, as in C{(align="right")}, or 

4901 - as an explicit dict with C{**} operator, when an attribute name is also a Python 

4902 reserved word, as in C{**{"class":"Customer", "align":"right"}} 

4903 - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) 

4904 For attribute names with a namespace prefix, you must use the second form. Attribute 

4905 names are matched insensitive to upper/lower case. 

4906  

4907 If just testing for C{class} (with or without a namespace), use C{L{withClass}}. 

4908 

4909 To verify that the attribute exists, but without specifying a value, pass 

4910 C{withAttribute.ANY_VALUE} as the value. 

4911 

4912 Example:: 

4913 html = ''' 

4914 <div> 

4915 Some text 

4916 <div type="grid">1 4 0 1 0</div> 

4917 <div type="graph">1,3 2,3 1,1</div> 

4918 <div>this has no type</div> 

4919 </div> 

4920  

4921 ''' 

4922 div,div_end = makeHTMLTags("div") 

4923 

4924 # only match div tag having a type attribute with value "grid" 

4925 div_grid = div().setParseAction(withAttribute(type="grid")) 

4926 grid_expr = div_grid + SkipTo(div | div_end)("body") 

4927 for grid_header in grid_expr.searchString(html): 

4928 print(grid_header.body) 

4929  

4930 # construct a match with any div tag having a type attribute, regardless of the value 

4931 div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) 

4932 div_expr = div_any_type + SkipTo(div | div_end)("body") 

4933 for div_header in div_expr.searchString(html): 

4934 print(div_header.body) 

4935 prints:: 

4936 1 4 0 1 0 

4937 

4938 1 4 0 1 0 

4939 1,3 2,3 1,1 

4940 """ 

4941 if args: 

4942 attrs = args[:] 

4943 else: 

4944 attrs = attrDict.items() 

4945 attrs = [(k,v) for k,v in attrs] 

4946 def pa(s,l,tokens): 

4947 for attrName,attrValue in attrs: 

4948 if attrName not in tokens: 

4949 raise ParseException(s,l,"no matching attribute " + attrName) 

4950 if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: 

4951 raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % 

4952 (attrName, tokens[attrName], attrValue)) 

4953 return pa 

4954withAttribute.ANY_VALUE = object() 

4955 

4956def withClass(classname, namespace=''): 

4957 """ 

4958 Simplified version of C{L{withAttribute}} when matching on a div class - made 

4959 difficult because C{class} is a reserved word in Python. 

4960 

4961 Example:: 

4962 html = ''' 

4963 <div> 

4964 Some text 

4965 <div class="grid">1 4 0 1 0</div> 

4966 <div class="graph">1,3 2,3 1,1</div> 

4967 <div>this &lt;div&gt; has no class</div> 

4968 </div> 

4969  

4970 ''' 

4971 div,div_end = makeHTMLTags("div") 

4972 div_grid = div().setParseAction(withClass("grid")) 

4973  

4974 grid_expr = div_grid + SkipTo(div | div_end)("body") 

4975 for grid_header in grid_expr.searchString(html): 

4976 print(grid_header.body) 

4977  

4978 div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) 

4979 div_expr = div_any_type + SkipTo(div | div_end)("body") 

4980 for div_header in div_expr.searchString(html): 

4981 print(div_header.body) 

4982 prints:: 

4983 1 4 0 1 0 

4984 

4985 1 4 0 1 0 

4986 1,3 2,3 1,1 

4987 """ 

4988 classattr = "%s:class" % namespace if namespace else "class" 

4989 return withAttribute(**{classattr : classname}) 

4990 

4991opAssoc = _Constants() 

4992opAssoc.LEFT = object() 

4993opAssoc.RIGHT = object() 

4994 

4995def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): 

4996 """ 

4997 Helper method for constructing grammars of expressions made up of 

4998 operators working in a precedence hierarchy. Operators may be unary or 

4999 binary, left- or right-associative. Parse actions can also be attached 

5000 to operator expressions. The generated parser will also recognize the use  

5001 of parentheses to override operator precedences (see example below). 

5002  

5003 Note: if you define a deep operator list, you may see performance issues 

5004 when using infixNotation. See L{ParserElement.enablePackrat} for a 

5005 mechanism to potentially improve your parser performance. 

5006 

5007 Parameters: 

5008 - baseExpr - expression representing the most basic element for the nested 

5009 - opList - list of tuples, one for each operator precedence level in the 

5010 expression grammar; each tuple is of the form 

5011 (opExpr, numTerms, rightLeftAssoc, parseAction), where: 

5012 - opExpr is the pyparsing expression for the operator; 

5013 may also be a string, which will be converted to a Literal; 

5014 if numTerms is 3, opExpr is a tuple of two expressions, for the 

5015 two operators separating the 3 terms 

5016 - numTerms is the number of terms for this operator (must 

5017 be 1, 2, or 3) 

5018 - rightLeftAssoc is the indicator whether the operator is 

5019 right or left associative, using the pyparsing-defined 

5020 constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. 

5021 - parseAction is the parse action to be associated with 

5022 expressions matching this operator expression (the 

5023 parse action tuple member may be omitted) 

5024 - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) 

5025 - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) 

5026 

5027 Example:: 

5028 # simple example of four-function arithmetic with ints and variable names 

5029 integer = pyparsing_common.signed_integer 

5030 varname = pyparsing_common.identifier  

5031  

5032 arith_expr = infixNotation(integer | varname, 

5033 [ 

5034 ('-', 1, opAssoc.RIGHT), 

5035 (oneOf('* /'), 2, opAssoc.LEFT), 

5036 (oneOf('+ -'), 2, opAssoc.LEFT), 

5037 ]) 

5038  

5039 arith_expr.runTests(''' 

5040 5+3*6 

5041 (5+3)*6 

5042 -2--11 

5043 ''', fullDump=False) 

5044 prints:: 

5045 5+3*6 

5046 [[5, '+', [3, '*', 6]]] 

5047 

5048 (5+3)*6 

5049 [[[5, '+', 3], '*', 6]] 

5050 

5051 -2--11 

5052 [[['-', 2], '-', ['-', 11]]] 

5053 """ 

5054 ret = Forward() 

5055 lastExpr = baseExpr | ( lpar + ret + rpar ) 

5056 for i,operDef in enumerate(opList): 

5057 opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] 

5058 termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr 

5059 if arity == 3: 

5060 if opExpr is None or len(opExpr) != 2: 

5061 raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") 

5062 opExpr1, opExpr2 = opExpr 

5063 thisExpr = Forward().setName(termName) 

5064 if rightLeftAssoc == opAssoc.LEFT: 

5065 if arity == 1: 

5066 matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) 

5067 elif arity == 2: 

5068 if opExpr is not None: 

5069 matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) 

5070 else: 

5071 matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) 

5072 elif arity == 3: 

5073 matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ 

5074 Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) 

5075 else: 

5076 raise ValueError("operator must be unary (1), binary (2), or ternary (3)") 

5077 elif rightLeftAssoc == opAssoc.RIGHT: 

5078 if arity == 1: 

5079 # try to avoid LR with this extra test 

5080 if not isinstance(opExpr, Optional): 

5081 opExpr = Optional(opExpr) 

5082 matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) 

5083 elif arity == 2: 

5084 if opExpr is not None: 

5085 matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) 

5086 else: 

5087 matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) 

5088 elif arity == 3: 

5089 matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ 

5090 Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) 

5091 else: 

5092 raise ValueError("operator must be unary (1), binary (2), or ternary (3)") 

5093 else: 

5094 raise ValueError("operator must indicate right or left associativity") 

5095 if pa: 

5096 matchExpr.setParseAction( pa ) 

5097 thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) 

5098 lastExpr = thisExpr 

5099 ret <<= lastExpr 

5100 return ret 

5101 

5102operatorPrecedence = infixNotation 

5103"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" 

5104 

5105dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") 

5106sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") 

5107quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| 

5108 Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") 

5109unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") 

5110 

5111def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): 

5112 """ 

5113 Helper method for defining nested lists enclosed in opening and closing 

5114 delimiters ("(" and ")" are the default). 

5115 

5116 Parameters: 

5117 - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression 

5118 - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression 

5119 - content - expression for items within the nested lists (default=C{None}) 

5120 - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) 

5121 

5122 If an expression is not provided for the content argument, the nested 

5123 expression will capture all whitespace-delimited content between delimiters 

5124 as a list of separate values. 

5125 

5126 Use the C{ignoreExpr} argument to define expressions that may contain 

5127 opening or closing characters that should not be treated as opening 

5128 or closing characters for nesting, such as quotedString or a comment 

5129 expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. 

5130 The default is L{quotedString}, but if no expressions are to be ignored, 

5131 then pass C{None} for this argument. 

5132 

5133 Example:: 

5134 data_type = oneOf("void int short long char float double") 

5135 decl_data_type = Combine(data_type + Optional(Word('*'))) 

5136 ident = Word(alphas+'_', alphanums+'_') 

5137 number = pyparsing_common.number 

5138 arg = Group(decl_data_type + ident) 

5139 LPAR,RPAR = map(Suppress, "()") 

5140 

5141 code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) 

5142 

5143 c_function = (decl_data_type("type")  

5144 + ident("name") 

5145 + LPAR + Optional(delimitedList(arg), [])("args") + RPAR  

5146 + code_body("body")) 

5147 c_function.ignore(cStyleComment) 

5148  

5149 source_code = ''' 

5150 int is_odd(int x) {  

5151 return (x%2);  

5152 } 

5153  

5154 int dec_to_hex(char hchar) {  

5155 if (hchar >= '0' && hchar <= '9') {  

5156 return (ord(hchar)-ord('0'));  

5157 } else {  

5158 return (10+ord(hchar)-ord('A')); 

5159 }  

5160 } 

5161 ''' 

5162 for func in c_function.searchString(source_code): 

5163 print("%(name)s (%(type)s) args: %(args)s" % func) 

5164 

5165 prints:: 

5166 is_odd (int) args: [['int', 'x']] 

5167 dec_to_hex (int) args: [['char', 'hchar']] 

5168 """ 

5169 if opener == closer: 

5170 raise ValueError("opening and closing strings cannot be the same") 

5171 if content is None: 

5172 if isinstance(opener,basestring) and isinstance(closer,basestring): 

5173 if len(opener) == 1 and len(closer)==1: 

5174 if ignoreExpr is not None: 

5175 content = (Combine(OneOrMore(~ignoreExpr + 

5176 CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) 

5177 ).setParseAction(lambda t:t[0].strip())) 

5178 else: 

5179 content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS 

5180 ).setParseAction(lambda t:t[0].strip())) 

5181 else: 

5182 if ignoreExpr is not None: 

5183 content = (Combine(OneOrMore(~ignoreExpr + 

5184 ~Literal(opener) + ~Literal(closer) + 

5185 CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) 

5186 ).setParseAction(lambda t:t[0].strip())) 

5187 else: 

5188 content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + 

5189 CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) 

5190 ).setParseAction(lambda t:t[0].strip())) 

5191 else: 

5192 raise ValueError("opening and closing arguments must be strings if no content expression is given") 

5193 ret = Forward() 

5194 if ignoreExpr is not None: 

5195 ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) 

5196 else: 

5197 ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) 

5198 ret.setName('nested %s%s expression' % (opener,closer)) 

5199 return ret 

5200 

5201def indentedBlock(blockStatementExpr, indentStack, indent=True): 

5202 """ 

5203 Helper method for defining space-delimited indentation blocks, such as 

5204 those used to define block statements in Python source code. 

5205 

5206 Parameters: 

5207 - blockStatementExpr - expression defining syntax of statement that 

5208 is repeated within the indented block 

5209 - indentStack - list created by caller to manage indentation stack 

5210 (multiple statementWithIndentedBlock expressions within a single grammar 

5211 should share a common indentStack) 

5212 - indent - boolean indicating whether block must be indented beyond the 

5213 the current level; set to False for block of left-most statements 

5214 (default=C{True}) 

5215 

5216 A valid block must contain at least one C{blockStatement}. 

5217 

5218 Example:: 

5219 data = ''' 

5220 def A(z): 

5221 A1 

5222 B = 100 

5223 G = A2 

5224 A2 

5225 A3 

5226 B 

5227 def BB(a,b,c): 

5228 BB1 

5229 def BBA(): 

5230 bba1 

5231 bba2 

5232 bba3 

5233 C 

5234 D 

5235 def spam(x,y): 

5236 def eggs(z): 

5237 pass 

5238 ''' 

5239 

5240 

5241 indentStack = [1] 

5242 stmt = Forward() 

5243 

5244 identifier = Word(alphas, alphanums) 

5245 funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") 

5246 func_body = indentedBlock(stmt, indentStack) 

5247 funcDef = Group( funcDecl + func_body ) 

5248 

5249 rvalue = Forward() 

5250 funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") 

5251 rvalue << (funcCall | identifier | Word(nums)) 

5252 assignment = Group(identifier + "=" + rvalue) 

5253 stmt << ( funcDef | assignment | identifier ) 

5254 

5255 module_body = OneOrMore(stmt) 

5256 

5257 parseTree = module_body.parseString(data) 

5258 parseTree.pprint() 

5259 prints:: 

5260 [['def', 

5261 'A', 

5262 ['(', 'z', ')'], 

5263 ':', 

5264 [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], 

5265 'B', 

5266 ['def', 

5267 'BB', 

5268 ['(', 'a', 'b', 'c', ')'], 

5269 ':', 

5270 [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], 

5271 'C', 

5272 'D', 

5273 ['def', 

5274 'spam', 

5275 ['(', 'x', 'y', ')'], 

5276 ':', 

5277 [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]]  

5278 """ 

5279 def checkPeerIndent(s,l,t): 

5280 if l >= len(s): return 

5281 curCol = col(l,s) 

5282 if curCol != indentStack[-1]: 

5283 if curCol > indentStack[-1]: 

5284 raise ParseFatalException(s,l,"illegal nesting") 

5285 raise ParseException(s,l,"not a peer entry") 

5286 

5287 def checkSubIndent(s,l,t): 

5288 curCol = col(l,s) 

5289 if curCol > indentStack[-1]: 

5290 indentStack.append( curCol ) 

5291 else: 

5292 raise ParseException(s,l,"not a subentry") 

5293 

5294 def checkUnindent(s,l,t): 

5295 if l >= len(s): return 

5296 curCol = col(l,s) 

5297 if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): 

5298 raise ParseException(s,l,"not an unindent") 

5299 indentStack.pop() 

5300 

5301 NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) 

5302 INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') 

5303 PEER = Empty().setParseAction(checkPeerIndent).setName('') 

5304 UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') 

5305 if indent: 

5306 smExpr = Group( Optional(NL) + 

5307 #~ FollowedBy(blockStatementExpr) + 

5308 INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) 

5309 else: 

5310 smExpr = Group( Optional(NL) + 

5311 (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) 

5312 blockStatementExpr.ignore(_bslash + LineEnd()) 

5313 return smExpr.setName('indented block') 

5314 

5315alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") 

5316punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") 

5317 

5318anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) 

5319_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) 

5320commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") 

5321def replaceHTMLEntity(t): 

5322 """Helper parser action to replace common HTML entities with their special characters""" 

5323 return _htmlEntityMap.get(t.entity) 

5324 

5325# it's easy to get these comment structures wrong - they're very common, so may as well make them available 

5326cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") 

5327"Comment of the form C{/* ... */}" 

5328 

5329htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") 

5330"Comment of the form C{<!-- ... -->}" 

5331 

5332restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") 

5333dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") 

5334"Comment of the form C{// ... (to end of line)}" 

5335 

5336cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") 

5337"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" 

5338 

5339javaStyleComment = cppStyleComment 

5340"Same as C{L{cppStyleComment}}" 

5341 

5342pythonStyleComment = Regex(r"#.*").setName("Python style comment") 

5343"Comment of the form C{# ... (to end of line)}" 

5344 

5345_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + 

5346 Optional( Word(" \t") + 

5347 ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") 

5348commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") 

5349"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. 

5350 This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" 

5351 

5352# some other useful expressions - using lower-case class name since we are really using this as a namespace 

5353class pyparsing_common: 

5354 """ 

5355 Here are some common low-level expressions that may be useful in jump-starting parser development: 

5356 - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>}) 

5357 - common L{programming identifiers<identifier>} 

5358 - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>}) 

5359 - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>} 

5360 - L{UUID<uuid>} 

5361 - L{comma-separated list<comma_separated_list>} 

5362 Parse actions: 

5363 - C{L{convertToInteger}} 

5364 - C{L{convertToFloat}} 

5365 - C{L{convertToDate}} 

5366 - C{L{convertToDatetime}} 

5367 - C{L{stripHTMLTags}} 

5368 - C{L{upcaseTokens}} 

5369 - C{L{downcaseTokens}} 

5370 

5371 Example:: 

5372 pyparsing_common.number.runTests(''' 

5373 # any int or real number, returned as the appropriate type 

5374 100 

5375 -100 

5376 +100 

5377 3.14159 

5378 6.02e23 

5379 1e-12 

5380 ''') 

5381 

5382 pyparsing_common.fnumber.runTests(''' 

5383 # any int or real number, returned as float 

5384 100 

5385 -100 

5386 +100 

5387 3.14159 

5388 6.02e23 

5389 1e-12 

5390 ''') 

5391 

5392 pyparsing_common.hex_integer.runTests(''' 

5393 # hex numbers 

5394 100 

5395 FF 

5396 ''') 

5397 

5398 pyparsing_common.fraction.runTests(''' 

5399 # fractions 

5400 1/2 

5401 -3/4 

5402 ''') 

5403 

5404 pyparsing_common.mixed_integer.runTests(''' 

5405 # mixed fractions 

5406 1 

5407 1/2 

5408 -3/4 

5409 1-3/4 

5410 ''') 

5411 

5412 import uuid 

5413 pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) 

5414 pyparsing_common.uuid.runTests(''' 

5415 # uuid 

5416 12345678-1234-5678-1234-567812345678 

5417 ''') 

5418 prints:: 

5419 # any int or real number, returned as the appropriate type 

5420 100 

5421 [100] 

5422 

5423 -100 

5424 [-100] 

5425 

5426 +100 

5427 [100] 

5428 

5429 3.14159 

5430 [3.14159] 

5431 

5432 6.02e23 

5433 [6.02e+23] 

5434 

5435 1e-12 

5436 [1e-12] 

5437 

5438 # any int or real number, returned as float 

5439 100 

5440 [100.0] 

5441 

5442 -100 

5443 [-100.0] 

5444 

5445 +100 

5446 [100.0] 

5447 

5448 3.14159 

5449 [3.14159] 

5450 

5451 6.02e23 

5452 [6.02e+23] 

5453 

5454 1e-12 

5455 [1e-12] 

5456 

5457 # hex numbers 

5458 100 

5459 [256] 

5460 

5461 FF 

5462 [255] 

5463 

5464 # fractions 

5465 1/2 

5466 [0.5] 

5467 

5468 -3/4 

5469 [-0.75] 

5470 

5471 # mixed fractions 

5472 1 

5473 [1] 

5474 

5475 1/2 

5476 [0.5] 

5477 

5478 -3/4 

5479 [-0.75] 

5480 

5481 1-3/4 

5482 [1.75] 

5483 

5484 # uuid 

5485 12345678-1234-5678-1234-567812345678 

5486 [UUID('12345678-1234-5678-1234-567812345678')] 

5487 """ 

5488 

5489 convertToInteger = tokenMap(int) 

5490 """ 

5491 Parse action for converting parsed integers to Python int 

5492 """ 

5493 

5494 convertToFloat = tokenMap(float) 

5495 """ 

5496 Parse action for converting parsed numbers to Python float 

5497 """ 

5498 

5499 integer = Word(nums).setName("integer").setParseAction(convertToInteger) 

5500 """expression that parses an unsigned integer, returns an int""" 

5501 

5502 hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) 

5503 """expression that parses a hexadecimal integer, returns an int""" 

5504 

5505 signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) 

5506 """expression that parses an integer with optional leading sign, returns an int""" 

5507 

5508 fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") 

5509 """fractional expression of an integer divided by an integer, returns a float""" 

5510 fraction.addParseAction(lambda t: t[0]/t[-1]) 

5511 

5512 mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") 

5513 """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" 

5514 mixed_integer.addParseAction(sum) 

5515 

5516 real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) 

5517 """expression that parses a floating point number and returns a float""" 

5518 

5519 sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) 

5520 """expression that parses a floating point number with optional scientific notation and returns a float""" 

5521 

5522 # streamlining this expression makes the docs nicer-looking 

5523 number = (sci_real | real | signed_integer).streamline() 

5524 """any numeric expression, returns the corresponding Python type""" 

5525 

5526 fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) 

5527 """any int or real number, returned as float""" 

5528 

5529 identifier = Word(alphas+'_', alphanums+'_').setName("identifier") 

5530 """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" 

5531 

5532 ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") 

5533 "IPv4 address (C{0.0.0.0 - 255.255.255.255})" 

5534 

5535 _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") 

5536 _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") 

5537 _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") 

5538 _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) 

5539 _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") 

5540 ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") 

5541 "IPv6 address (long, short, or mixed form)" 

5542 

5543 mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") 

5544 "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" 

5545 

5546 @staticmethod 

5547 def convertToDate(fmt="%Y-%m-%d"): 

5548 """ 

5549 Helper to create a parse action for converting parsed date string to Python datetime.date 

5550 

5551 Params - 

5552 - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) 

5553 

5554 Example:: 

5555 date_expr = pyparsing_common.iso8601_date.copy() 

5556 date_expr.setParseAction(pyparsing_common.convertToDate()) 

5557 print(date_expr.parseString("1999-12-31")) 

5558 prints:: 

5559 [datetime.date(1999, 12, 31)] 

5560 """ 

5561 def cvt_fn(s,l,t): 

5562 try: 

5563 return datetime.strptime(t[0], fmt).date() 

5564 except ValueError as ve: 

5565 raise ParseException(s, l, str(ve)) 

5566 return cvt_fn 

5567 

5568 @staticmethod 

5569 def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): 

5570 """ 

5571 Helper to create a parse action for converting parsed datetime string to Python datetime.datetime 

5572 

5573 Params - 

5574 - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) 

5575 

5576 Example:: 

5577 dt_expr = pyparsing_common.iso8601_datetime.copy() 

5578 dt_expr.setParseAction(pyparsing_common.convertToDatetime()) 

5579 print(dt_expr.parseString("1999-12-31T23:59:59.999")) 

5580 prints:: 

5581 [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] 

5582 """ 

5583 def cvt_fn(s,l,t): 

5584 try: 

5585 return datetime.strptime(t[0], fmt) 

5586 except ValueError as ve: 

5587 raise ParseException(s, l, str(ve)) 

5588 return cvt_fn 

5589 

5590 iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") 

5591 "ISO8601 date (C{yyyy-mm-dd})" 

5592 

5593 iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") 

5594 "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" 

5595 

5596 uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") 

5597 "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" 

5598 

5599 _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() 

5600 @staticmethod 

5601 def stripHTMLTags(s, l, tokens): 

5602 """ 

5603 Parse action to remove HTML tags from web page HTML source 

5604 

5605 Example:: 

5606 # strip HTML links from normal text  

5607 text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' 

5608 td,td_end = makeHTMLTags("TD") 

5609 table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end 

5610  

5611 print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' 

5612 """ 

5613 return pyparsing_common._html_stripper.transformString(tokens[0]) 

5614 

5615 _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') 

5616 + Optional( White(" \t") ) ) ).streamline().setName("commaItem") 

5617 comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") 

5618 """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" 

5619 

5620 upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) 

5621 """Parse action to convert tokens to upper case.""" 

5622 

5623 downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) 

5624 """Parse action to convert tokens to lower case.""" 

5625 

5626 

5627if __name__ == "__main__": 

5628 

5629 selectToken = CaselessLiteral("select") 

5630 fromToken = CaselessLiteral("from") 

5631 

5632 ident = Word(alphas, alphanums + "_$") 

5633 

5634 columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) 

5635 columnNameList = Group(delimitedList(columnName)).setName("columns") 

5636 columnSpec = ('*' | columnNameList) 

5637 

5638 tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) 

5639 tableNameList = Group(delimitedList(tableName)).setName("tables") 

5640 

5641 simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") 

5642 

5643 # demo runTests method, including embedded comments in test string 

5644 simpleSQL.runTests(""" 

5645 # '*' as column list and dotted table name 

5646 select * from SYS.XYZZY 

5647 

5648 # caseless match on "SELECT", and casts back to "select" 

5649 SELECT * from XYZZY, ABC 

5650 

5651 # list of column names, and mixed case SELECT keyword 

5652 Select AA,BB,CC from Sys.dual 

5653 

5654 # multiple tables 

5655 Select A, B, C from Sys.dual, Table2 

5656 

5657 # invalid SELECT keyword - should fail 

5658 Xelect A, B, C from Sys.dual 

5659 

5660 # incomplete command - should fail 

5661 Select 

5662 

5663 # invalid column name - should fail 

5664 Select ^^^ frox Sys.dual 

5665 

5666 """) 

5667 

5668 pyparsing_common.number.runTests(""" 

5669 100 

5670 -100 

5671 +100 

5672 3.14159 

5673 6.02e23 

5674 1e-12 

5675 """) 

5676 

5677 # any int or real number, returned as float 

5678 pyparsing_common.fnumber.runTests(""" 

5679 100 

5680 -100 

5681 +100 

5682 3.14159 

5683 6.02e23 

5684 1e-12 

5685 """) 

5686 

5687 pyparsing_common.hex_integer.runTests(""" 

5688 100 

5689 FF 

5690 """) 

5691 

5692 import uuid 

5693 pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) 

5694 pyparsing_common.uuid.runTests(""" 

5695 12345678-1234-5678-1234-567812345678 

5696 """)