~ibmcharmers/charms/xenial/ibm-cinder-storwize-svc/trunk

« back to all changes in this revision

Viewing changes to .tox/py35/lib/python3.5/site-packages/pip/_vendor/pyparsing.py

  • Committer: Ankammarao
  • Date: 2017-03-06 05:11:42 UTC
  • Revision ID: achittet@in.ibm.com-20170306051142-dpg27z4es1k56hfn
Marked tests folder executable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
"""
 
27
pyparsing module - Classes and methods to define and execute parsing grammars
 
28
 
 
29
The pyparsing module is an alternative approach to creating and executing simple grammars,
 
30
vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
 
31
don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
 
32
provides a library of classes that you use to construct the grammar directly in Python.
 
33
 
 
34
Here is a program to parse "Hello, World!" (or any greeting of the form 
 
35
C{"<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
 
37
L{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
 
 
47
The program outputs the following::
 
48
 
 
49
    Hello, World! -> ['Hello', ',', 'World', '!']
 
50
 
 
51
The Python representation of the grammar is quite readable, owing to the self-explanatory
 
52
class names, and the use of '+', '|' and '^' operators.
 
53
 
 
54
The L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.parseString>} can be accessed as a nested list, a dictionary, or an
 
55
object with named attributes.
 
56
 
 
57
The 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
 
 
67
import string
 
68
from weakref import ref as wkref
 
69
import copy
 
70
import sys
 
71
import warnings
 
72
import re
 
73
import sre_constants
 
74
import collections
 
75
import pprint
 
76
import traceback
 
77
import types
 
78
from datetime import datetime
 
79
 
 
80
try:
 
81
    from _thread import RLock
 
82
except ImportError:
 
83
    from threading import RLock
 
84
 
 
85
try:
 
86
    from collections import OrderedDict as _OrderedDict
 
87
except 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
 
 
116
system_version = tuple(sys.version_info)[:3]
 
117
PY_3 = system_version[0] == 3
 
118
if 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
 
 
127
else:
 
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('&#\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
 
 
162
def _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
 
 
172
class _Constants(object):
 
173
    pass
 
174
 
 
175
alphas     = string.ascii_uppercase + string.ascii_lowercase
 
176
nums       = "0123456789"
 
177
hexnums    = nums + "ABCDEFabcdef"
 
178
alphanums  = alphas + nums
 
179
_bslash    = chr(92)
 
180
printables = "".join(c for c in string.printable if c not in string.whitespace)
 
181
 
 
182
class 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
 
 
238
class 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
 
 
259
class ParseFatalException(ParseBaseException):
 
260
    """user-throwable exception thrown when inconsistent parse content
 
261
       is found; stops all parsing immediately"""
 
262
    pass
 
263
 
 
264
class 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
 
 
283
class 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
 
 
291
class _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
 
 
301
class 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
        """
 
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
 
 
943
collections.MutableMapping.register(ParseResults)
 
944
 
 
945
def 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
 
 
958
def 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
 
 
970
def 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
 
 
980
def _defaultStartDebugAction( instring, loc, expr ):
 
981
    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
 
982
 
 
983
def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
 
984
    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
 
985
 
 
986
def _defaultExceptionDebugAction( instring, loc, expr, exc ):
 
987
    print ("Exception raised:" + _ustr(exc))
 
988
 
 
989
def 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'
 
1016
def _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
 
 
1080
class 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
        
 
2323
class 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
 
 
2331
class 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
 
 
2342
class 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
 
 
2357
class 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
 
2396
ParserElement._literalStringClass = Literal
 
2397
 
 
2398
class 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
 
 
2463
class 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
 
 
2486
class 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
 
 
2504
class 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
 
 
2565
class 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
 
 
2724
class Regex(Token):
 
2725
    """
 
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
 
 
2797
class 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,"\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
 
 
2934
class 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
 
 
3005
class 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
 
 
3056
class _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
 
 
3063
class 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
 
 
3089
class 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
 
 
3119
class 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
 
 
3139
class 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
 
 
3154
class 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
 
 
3172
class 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
 
 
3192
class 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
 
 
3215
class 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
 
 
3327
class 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
 
 
3403
class 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
 
 
3482
class 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
 
 
3550
class 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
 
 
3674
class 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
 
 
3751
class 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
 
 
3777
class 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
 
 
3809
class _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
        
 
3847
class 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
 
 
3882
class 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
 
 
3913
class _NullToken(object):
 
3914
    def __bool__(self):
 
3915
        return False
 
3916
    __nonzero__ = __bool__
 
3917
    def __str__(self):
 
3918
        return ""
 
3919
 
 
3920
_optionalNotMatched = _NullToken()
 
3921
class 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
 
 
3985
class 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
 
 
4100
class 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
 
 
4181
class _ForwardNoRecurse(Forward):
 
4182
    def __str__( self ):
 
4183
        return "..."
 
4184
 
 
4185
class 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
 
 
4193
class 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
 
 
4237
class 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
 
 
4258
class 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
 
 
4323
class 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
 
 
4349
class 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
 
 
4365
def 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
#
 
4409
def 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
 
 
4428
def 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
 
 
4459
def _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
 
 
4468
def 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
 
 
4496
def 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
 
 
4524
def _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
 
 
4532
def 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
 
 
4605
def 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
 
 
4640
def 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
 
 
4677
def 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
 
 
4684
def 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
 
4709
empty       = Empty().setName("empty")
 
4710
lineStart   = LineStart().setName("lineStart")
 
4711
lineEnd     = LineEnd().setName("lineEnd")
 
4712
stringStart = StringStart().setName("stringStart")
 
4713
stringEnd   = 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
 
 
4722
def 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
 
 
4746
def 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
 
 
4756
def 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
 
 
4770
def 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
 
 
4784
def 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
 
 
4828
upcaseTokens = 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
 
 
4831
downcaseTokens = 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
    
 
4834
def _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
 
 
4863
def 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
 
 
4882
def 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
 
 
4891
def 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
 
4954
withAttribute.ANY_VALUE = object()
 
4955
 
 
4956
def 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
 
 
4991
opAssoc = _Constants()
 
4992
opAssoc.LEFT = object()
 
4993
opAssoc.RIGHT = object()
 
4994
 
 
4995
def 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
 
 
5102
operatorPrecedence = infixNotation
 
5103
"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release."""
 
5104
 
 
5105
dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes")
 
5106
sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes")
 
5107
quotedString = 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")
 
5109
unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal")
 
5110
 
 
5111
def 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
 
 
5201
def 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
 
 
5315
alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]")
 
5316
punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]")
 
5317
 
 
5318
anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag'))
 
5319
_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\''))
 
5320
commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity")
 
5321
def 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
 
5326
cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment")
 
5327
"Comment of the form C{/* ... */}"
 
5328
 
 
5329
htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment")
 
5330
"Comment of the form C{<!-- ... -->}"
 
5331
 
 
5332
restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line")
 
5333
dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment")
 
5334
"Comment of the form C{// ... (to end of line)}"
 
5335
 
 
5336
cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment")
 
5337
"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}"
 
5338
 
 
5339
javaStyleComment = cppStyleComment
 
5340
"Same as C{L{cppStyleComment}}"
 
5341
 
 
5342
pythonStyleComment = 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")
 
5348
commaSeparatedList = 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
 
5353
class 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
 
 
5627
if __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
        """)