From fa8f26f805154c958ee2ab94debda7d487d9fe08 Mon Sep 17 00:00:00 2001 From: Dmitry Cheryasov Date: Tue, 13 Oct 2009 08:13:07 +0300 Subject: [PATCH] Removed Jython-generated files, updated pyparsing with right EOLs. --- python/lib/Lib/UserDict$py.class | Bin 21347 -> 0 bytes python/lib/Lib/ntpath$py.class | Bin 30004 -> 0 bytes python/lib/Lib/os$py.class | Bin 77675 -> 0 bytes python/lib/Lib/stat$py.class | Bin 7471 -> 0 bytes .../src/com/jetbrains/python/sdk/pyparsing.py | 11121 +++++---------- .../com/jetbrains/python/sdk/pyparsing_py3.py | 11148 ++++++---------- 6 files changed, 7423 insertions(+), 14846 deletions(-) delete mode 100644 python/lib/Lib/UserDict$py.class delete mode 100644 python/lib/Lib/ntpath$py.class delete mode 100644 python/lib/Lib/os$py.class delete mode 100644 python/lib/Lib/stat$py.class diff --git a/python/lib/Lib/UserDict$py.class b/python/lib/Lib/UserDict$py.class deleted file mode 100644 index 9fbff34b1d27008a84777f8729a05f33b78b6c53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21347 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 diff --git a/python/lib/Lib/ntpath$py.class b/python/lib/Lib/ntpath$py.class deleted file mode 100644 index 5bdfbc394110850203771a84094b7688661d1d69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30004 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 F{{?NoTBra3 diff --git a/python/lib/Lib/os$py.class b/python/lib/Lib/os$py.class deleted file mode 100644 index 1046df4ba72fe5b49d16d7b64fe15a72f1425809..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77675 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, !"):: - - - - from pyparsing import Word, alphas - - - - # define grammar of a greeting - - greet = Word( alphas ) + "," + Word( alphas ) + "!" - - - - hello = "Hello, World!" - - print hello, "->", greet.parseString( hello ) - - - -The program outputs the following:: - - - - Hello, World! -> ['Hello', ',', 'World', '!'] - - - -The Python representation of the grammar is quite readable, owing to the self-explanatory - -class names, and the use of '+', '|' and '^' operators. - - - -The parsed results returned from parseString() can be accessed as a nested list, a dictionary, or an - -object with named attributes. - - - -The pyparsing module handles some of the problems that are typically vexing when writing text parsers: - - - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) - - - quoted strings - - - embedded comments - -""" - - - -__version__ = "1.5.2" - -__versionTime__ = "17 February 2009 19:45" - -__author__ = "Paul McGuire " - - - -import string - -from weakref import ref as wkref - -import copy - -import sys - -import warnings - -import re - -import sre_constants - -#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) - - - -__all__ = [ - -'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', - -'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', - -'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', - -'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', - -'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', - -'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', - -'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', - -'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', - -'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', - -'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums', - -'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', - -'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', - -'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', - -'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', - -'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', - -'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', - -'indentedBlock', 'originalTextFor', - -] - - - - - -""" - -Detect if we are running version 3.X and make appropriate changes - -Robert A. Clark - -""" - -if sys.version_info[0] > 2: - - _PY3K = True - - _MAX_INT = sys.maxsize - - basestring = str - -else: - - _PY3K = False - - _MAX_INT = sys.maxint - - - -if not _PY3K: - - def _ustr(obj): - - """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries - - str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It - - then < returns the unicode object | encodes it with the default encoding | ... >. - - """ - - if isinstance(obj,unicode): - - return obj - - - - try: - - # If this works, then _ustr(obj) has the same behaviour as str(obj), so - - # it won't break any existing code. - - return str(obj) - - - - except UnicodeEncodeError: - - # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) - - # state that "The return value must be a string object". However, does a - - # unicode object (being a subclass of basestring) count as a "string - - # object"? - - # If so, then return a unicode object: - - return unicode(obj) - - # Else encode it... but how? There are many choices... :) - - # Replace unprintables with escape codes? - - #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') - - # Replace unprintables with question marks? - - #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') - - # ... - -else: - - _ustr = str - - unichr = chr - - - -if not _PY3K: - - def _str2dict(strg): - - return dict( [(c,0) for c in strg] ) - -else: - - _str2dict = set - - - -def _xml_escape(data): - - """Escape &, <, >, ", ', etc. in a string of data.""" - - - - # ampersand must be replaced first - - from_symbols = '&><"\'' - - to_symbols = ['&'+s+';' for s in "amp gt lt quot apos".split()] - - for from_,to_ in zip(from_symbols, to_symbols): - - data = data.replace(from_, to_) - - return data - - - -class _Constants(object): - - pass - - - -if not _PY3K: - - alphas = string.lowercase + string.uppercase - -else: - - alphas = string.ascii_lowercase + string.ascii_uppercase - -nums = string.digits - -hexnums = nums + "ABCDEFabcdef" - -alphanums = alphas + nums - -_bslash = chr(92) - -printables = "".join( [ c for c in string.printable if c not in string.whitespace ] ) - - - -class ParseBaseException(Exception): - - """base exception class for all parsing runtime exceptions""" - - # Performance tuning: we construct a *lot* of these, so keep this - - # constructor as small and fast as possible - - def __init__( self, pstr, loc=0, msg=None, elem=None ): - - self.loc = loc - - if msg is None: - - self.msg = pstr - - self.pstr = "" - - else: - - self.msg = msg - - self.pstr = pstr - - self.parserElement = elem - - - - def __getattr__( self, aname ): - - """supported attributes by name are: - - - lineno - returns the line number of the exception text - - - col - returns the column number of the exception text - - - line - returns the line containing the exception text - - """ - - if( aname == "lineno" ): - - return lineno( self.loc, self.pstr ) - - elif( aname in ("col", "column") ): - - return col( self.loc, self.pstr ) - - elif( aname == "line" ): - - return line( self.loc, self.pstr ) - - else: - - raise AttributeError(aname) - - - - def __str__( self ): - - return "%s (at char %d), (line:%d, col:%d)" % \ - - ( self.msg, self.loc, self.lineno, self.column ) - - def __repr__( self ): - - return _ustr(self) - - def markInputline( self, markerString = ">!<" ): - - """Extracts the exception line from the input string, and marks - - the location of the exception with a special symbol. - - """ - - line_str = self.line - - line_column = self.column - 1 - - if markerString: - - line_str = "".join( [line_str[:line_column], - - markerString, line_str[line_column:]]) - - return line_str.strip() - - def __dir__(self): - - return "loc msg pstr parserElement lineno col line " \ - - "markInputLine __str__ __repr__".split() - - - -class ParseException(ParseBaseException): - - """exception thrown when parse expressions don't match class; - - supported attributes by name are: - - - lineno - returns the line number of the exception text - - - col - returns the column number of the exception text - - - line - returns the line containing the exception text - - """ - - pass - - - -class ParseFatalException(ParseBaseException): - - """user-throwable exception thrown when inconsistent parse content - - is found; stops all parsing immediately""" - - pass - - - -class ParseSyntaxException(ParseFatalException): - - """just like ParseFatalException, but thrown internally when an - - ErrorStop indicates that parsing is to stop immediately because - - an unbacktrackable syntax error has been found""" - - def __init__(self, pe): - - super(ParseSyntaxException, self).__init__( - - pe.pstr, pe.loc, pe.msg, pe.parserElement) - - - -#~ class ReparseException(ParseBaseException): - - #~ """Experimental class - parse actions can raise this exception to cause - - #~ pyparsing to reparse the input string: - - #~ - with a modified input string, and/or - - #~ - with a modified start location - - #~ Set the values of the ReparseException in the constructor, and raise the - - #~ exception in a parse action to cause pyparsing to use the new string/location. - - #~ Setting the values as None causes no change to be made. - - #~ """ - - #~ def __init_( self, newstring, restartLoc ): - - #~ self.newParseText = newstring - - #~ self.reparseLoc = restartLoc - - - -class RecursiveGrammarException(Exception): - - """exception thrown by validate() if the grammar could be improperly recursive""" - - def __init__( self, parseElementList ): - - self.parseElementTrace = parseElementList - - - - def __str__( self ): - - return "RecursiveGrammarException: %s" % self.parseElementTrace - - - -class _ParseResultsWithOffset(object): - - def __init__(self,p1,p2): - - self.tup = (p1,p2) - - def __getitem__(self,i): - - return self.tup[i] - - def __repr__(self): - - return repr(self.tup) - - def setOffset(self,i): - - self.tup = (self.tup[0],i) - - - -class ParseResults(object): - - """Structured parse results, to provide multiple means of access to the parsed data: - - - as a list (len(results)) - - - by list index (results[0], results[1], etc.) - - - by attribute (results.) - - """ - - __slots__ = ( "__toklist", "__tokdict", "__doinit", "__name", "__parent", "__accumNames", "__weakref__" ) - - def __new__(cls, toklist, name=None, asList=True, modal=True ): - - if isinstance(toklist, cls): - - return toklist - - retobj = object.__new__(cls) - - retobj.__doinit = True - - return retobj - - - - # Performance tuning: we construct a *lot* of these, so keep this - - # constructor as small and fast as possible - - def __init__( self, toklist, name=None, asList=True, modal=True ): - - if self.__doinit: - - self.__doinit = False - - self.__name = None - - self.__parent = None - - self.__accumNames = {} - - if isinstance(toklist, list): - - self.__toklist = toklist[:] - - else: - - self.__toklist = [toklist] - - self.__tokdict = dict() - - - - if name: - - if not modal: - - self.__accumNames[name] = 0 - - if isinstance(name,int): - - name = _ustr(name) # will always return a str, but use _ustr for consistency - - self.__name = name - - if not toklist in (None,'',[]): - - if isinstance(toklist,basestring): - - toklist = [ toklist ] - - if asList: - - if isinstance(toklist,ParseResults): - - self[name] = _ParseResultsWithOffset(toklist.copy(),0) - - else: - - self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) - - self[name].__name = name - - else: - - try: - - self[name] = toklist[0] - - except (KeyError,TypeError,IndexError): - - self[name] = toklist - - - - def __getitem__( self, i ): - - if isinstance( i, (int,slice) ): - - return self.__toklist[i] - - else: - - if i not in self.__accumNames: - - return self.__tokdict[i][-1][0] - - else: - - return ParseResults([ v[0] for v in self.__tokdict[i] ]) - - - - def __setitem__( self, k, v ): - - if isinstance(v,_ParseResultsWithOffset): - - self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] - - sub = v[0] - - elif isinstance(k,int): - - self.__toklist[k] = v - - sub = v - - else: - - self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] - - sub = v - - if isinstance(sub,ParseResults): - - sub.__parent = wkref(self) - - - - def __delitem__( self, i ): - - if isinstance(i,(int,slice)): - - mylen = len( self.__toklist ) - - del self.__toklist[i] - - - - # convert int to slice - - if isinstance(i, int): - - if i < 0: - - i += mylen - - i = slice(i, i+1) - - # get removed indices - - removed = list(range(*i.indices(mylen))) - - removed.reverse() - - # fixup indices in token dictionary - - for name in self.__tokdict: - - occurrences = self.__tokdict[name] - - for j in removed: - - for k, (value, position) in enumerate(occurrences): - - occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) - - else: - - del self.__tokdict[i] - - - - def __contains__( self, k ): - - return k in self.__tokdict - - - - def __len__( self ): return len( self.__toklist ) - - def __bool__(self): return len( self.__toklist ) > 0 - - __nonzero__ = __bool__ - - def __iter__( self ): return iter( self.__toklist ) - - def __reversed__( self ): return iter( reversed(self.__toklist) ) - - def keys( self ): - - """Returns all named result keys.""" - - return self.__tokdict.keys() - - - - def pop( self, index=-1 ): - - """Removes and returns item at specified index (default=last). - - Will work with either numeric indices or dict-key indicies.""" - - ret = self[index] - - del self[index] - - return ret - - - - def get(self, key, defaultValue=None): - - """Returns named result matching the given key, or if there is no - - such name, then returns the given defaultValue or None if no - - defaultValue is specified.""" - - if key in self: - - return self[key] - - else: - - return defaultValue - - - - def insert( self, index, insStr ): - - self.__toklist.insert(index, insStr) - - # fixup indices in token dictionary - - for name in self.__tokdict: - - occurrences = self.__tokdict[name] - - for k, (value, position) in enumerate(occurrences): - - occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) - - - - def items( self ): - - """Returns all named result keys and values as a list of tuples.""" - - return [(k,self[k]) for k in self.__tokdict] - - - - def values( self ): - - """Returns all named result values.""" - - return [ v[-1][0] for v in self.__tokdict.values() ] - - - - def __getattr__( self, name ): - - if name not in self.__slots__: - - if name in self.__tokdict: - - if name not in self.__accumNames: - - return self.__tokdict[name][-1][0] - - else: - - return ParseResults([ v[0] for v in self.__tokdict[name] ]) - - else: - - return "" - - return None - - - - def __add__( self, other ): - - ret = self.copy() - - ret += other - - return ret - - - - def __iadd__( self, other ): - - if other.__tokdict: - - offset = len(self.__toklist) - - addoffset = ( lambda a: (a<0 and offset) or (a+offset) ) - - otheritems = other.__tokdict.items() - - otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) - - for (k,vlist) in otheritems for v in vlist] - - for k,v in otherdictitems: - - self[k] = v - - if isinstance(v[0],ParseResults): - - v[0].__parent = wkref(self) - - - - self.__toklist += other.__toklist - - self.__accumNames.update( other.__accumNames ) - - del other - - return self - - - - def __repr__( self ): - - return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) - - - - def __str__( self ): - - out = "[" - - sep = "" - - for i in self.__toklist: - - if isinstance(i, ParseResults): - - out += sep + _ustr(i) - - else: - - out += sep + repr(i) - - sep = ", " - - out += "]" - - return out - - - - def _asStringList( self, sep='' ): - - out = [] - - for item in self.__toklist: - - if out and sep: - - out.append(sep) - - if isinstance( item, ParseResults ): - - out += item._asStringList() - - else: - - out.append( _ustr(item) ) - - return out - - - - def asList( self ): - - """Returns the parse results as a nested list of matching tokens, all converted to strings.""" - - out = [] - - for res in self.__toklist: - - if isinstance(res,ParseResults): - - out.append( res.asList() ) - - else: - - out.append( res ) - - return out - - - - def asDict( self ): - - """Returns the named parse results as dictionary.""" - - return dict( self.items() ) - - - - def copy( self ): - - """Returns a new copy of a ParseResults object.""" - - ret = ParseResults( self.__toklist ) - - ret.__tokdict = self.__tokdict.copy() - - ret.__parent = self.__parent - - ret.__accumNames.update( self.__accumNames ) - - ret.__name = self.__name - - return ret - - - - def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): - - """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" - - nl = "\n" - - out = [] - - namedItems = dict( [ (v[1],k) for (k,vlist) in self.__tokdict.items() - - for v in vlist ] ) - - nextLevelIndent = indent + " " - - - - # collapse out indents if formatting is not desired - - if not formatted: - - indent = "" - - nextLevelIndent = "" - - nl = "" - - - - selfTag = None - - if doctag is not None: - - selfTag = doctag - - else: - - if self.__name: - - selfTag = self.__name - - - - if not selfTag: - - if namedItemsOnly: - - return "" - - else: - - selfTag = "ITEM" - - - - out += [ nl, indent, "<", selfTag, ">" ] - - - - worklist = self.__toklist - - for i,res in enumerate(worklist): - - if isinstance(res,ParseResults): - - if i in namedItems: - - out += [ res.asXML(namedItems[i], - - namedItemsOnly and doctag is None, - - nextLevelIndent, - - formatted)] - - else: - - out += [ res.asXML(None, - - namedItemsOnly and doctag is None, - - nextLevelIndent, - - formatted)] - - else: - - # individual token, see if there is a name for it - - resTag = None - - if i in namedItems: - - resTag = namedItems[i] - - if not resTag: - - if namedItemsOnly: - - continue - - else: - - resTag = "ITEM" - - xmlBodyText = _xml_escape(_ustr(res)) - - out += [ nl, nextLevelIndent, "<", resTag, ">", - - xmlBodyText, - - "" ] - - - - out += [ nl, indent, "" ] - - return "".join(out) - - - - def __lookup(self,sub): - - for k,vlist in self.__tokdict.items(): - - for v,loc in vlist: - - if sub is v: - - return k - - return None - - - - def getName(self): - - """Returns the results name for this token expression.""" - - if self.__name: - - return self.__name - - elif self.__parent: - - par = self.__parent() - - if par: - - return par.__lookup(self) - - else: - - return None - - elif (len(self) == 1 and - - len(self.__tokdict) == 1 and - - self.__tokdict.values()[0][0][1] in (0,-1)): - - return self.__tokdict.keys()[0] - - else: - - return None - - - - def dump(self,indent='',depth=0): - - """Diagnostic method for listing out the contents of a ParseResults. - - Accepts an optional indent argument so that this string can be embedded - - in a nested display of other data.""" - - out = [] - - out.append( indent+_ustr(self.asList()) ) - - keys = self.items() - - keys.sort() - - for k,v in keys: - - if out: - - out.append('\n') - - out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) - - if isinstance(v,ParseResults): - - if v.keys(): - - #~ out.append('\n') - - out.append( v.dump(indent,depth+1) ) - - #~ out.append('\n') - - else: - - out.append(_ustr(v)) - - else: - - out.append(_ustr(v)) - - #~ out.append('\n') - - return "".join(out) - - - - # add support for pickle protocol - - def __getstate__(self): - - return ( self.__toklist, - - ( self.__tokdict.copy(), - - self.__parent is not None and self.__parent() or None, - - self.__accumNames, - - self.__name ) ) - - - - def __setstate__(self,state): - - self.__toklist = state[0] - - self.__tokdict, \ - - par, \ - - inAccumNames, \ - - self.__name = state[1] - - self.__accumNames = {} - - self.__accumNames.update(inAccumNames) - - if par is not None: - - self.__parent = wkref(par) - - else: - - self.__parent = None - - - - def __dir__(self): - - return dir(super(ParseResults,self)) + self.keys() - - - -def col (loc,strg): - - """Returns current column within a string, counting newlines as line separators. - - The first column is number 1. - - - - Note: the default parsing behavior is to expand tabs in the input string - - before starting the parsing process. See L{I{ParserElement.parseString}} for more information - - on parsing strings containing s, and suggested methods to maintain a - - consistent view of the parsed string, the parse location, and line and column - - positions within the parsed string. - - """ - - return (loc} for more information - - on parsing strings containing s, and suggested methods to maintain a - - consistent view of the parsed string, the parse location, and line and column - - positions within the parsed string. - - """ - - return strg.count("\n",0,loc) + 1 - - - -def line( loc, strg ): - - """Returns the line of text containing loc within a string, counting newlines as line separators. - - """ - - lastCR = strg.rfind("\n", 0, loc) - - nextCR = strg.find("\n", loc) - - if nextCR > 0: - - return strg[lastCR+1:nextCR] - - else: - - return strg[lastCR+1:] - - - -def _defaultStartDebugAction( instring, loc, expr ): - - print ("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) - - - -def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): - - print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) - - - -def _defaultExceptionDebugAction( instring, loc, expr, exc ): - - print ("Exception raised:" + _ustr(exc)) - - - -def nullDebugAction(*args): - - """'Do-nothing' debug action, to suppress debugging output during parsing.""" - - pass - - - -class ParserElement(object): - - """Abstract base level parser element class.""" - - DEFAULT_WHITE_CHARS = " \n\t\r" - - - - def setDefaultWhitespaceChars( chars ): - - """Overrides the default whitespace chars - - """ - - ParserElement.DEFAULT_WHITE_CHARS = chars - - setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars) - - - - def __init__( self, savelist=False ): - - self.parseAction = list() - - self.failAction = None - - #~ self.name = "" # don't define self.name, let subclasses try/except upcall - - self.strRepr = None - - self.resultsName = None - - self.saveAsList = savelist - - self.skipWhitespace = True - - self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS - - self.copyDefaultWhiteChars = True - - self.mayReturnEmpty = False # used when checking for left-recursion - - self.keepTabs = False - - self.ignoreExprs = list() - - self.debug = False - - self.streamlined = False - - self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index - - self.errmsg = "" - - self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) - - self.debugActions = ( None, None, None ) #custom debug actions - - self.re = None - - self.callPreparse = True # used to avoid redundant calls to preParse - - self.callDuringTry = False - - - - def copy( self ): - - """Make a copy of this ParserElement. Useful for defining different parse actions - - for the same parsing pattern, using copies of the original parse element.""" - - cpy = copy.copy( self ) - - cpy.parseAction = self.parseAction[:] - - cpy.ignoreExprs = self.ignoreExprs[:] - - if self.copyDefaultWhiteChars: - - cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS - - return cpy - - - - def setName( self, name ): - - """Define name for this expression, for use in debugging.""" - - self.name = name - - self.errmsg = "Expected " + self.name - - if hasattr(self,"exception"): - - self.exception.msg = self.errmsg - - return self - - - - def setResultsName( self, name, listAllMatches=False ): - - """Define name for referencing matching tokens as a nested attribute - - of the returned parse results. - - NOTE: this returns a *copy* of the original ParserElement object; - - this is so that the client can define a basic element, such as an - - integer, and reference it in multiple places with different names. - - """ - - newself = self.copy() - - newself.resultsName = name - - newself.modalResults = not listAllMatches - - return newself - - - - def setBreak(self,breakFlag = True): - - """Method to invoke the Python pdb debugger when this element is - - about to be parsed. Set breakFlag to True to enable, False to - - disable. - - """ - - if breakFlag: - - _parseMethod = self._parse - - def breaker(instring, loc, doActions=True, callPreParse=True): - - import pdb - - pdb.set_trace() - - return _parseMethod( instring, loc, doActions, callPreParse ) - - breaker._originalParseMethod = _parseMethod - - self._parse = breaker - - else: - - if hasattr(self._parse,"_originalParseMethod"): - - self._parse = self._parse._originalParseMethod - - return self - - - - def _normalizeParseActionArgs( f ): - - """Internal method used to decorate parse actions that take fewer than 3 arguments, - - so that all parse actions can be called as f(s,l,t).""" - - STAR_ARGS = 4 - - - - try: - - restore = None - - if isinstance(f,type): - - restore = f - - f = f.__init__ - - if not _PY3K: - - codeObj = f.func_code - - else: - - codeObj = f.code - - if codeObj.co_flags & STAR_ARGS: - - return f - - numargs = codeObj.co_argcount - - if not _PY3K: - - if hasattr(f,"im_self"): - - numargs -= 1 - - else: - - if hasattr(f,"__self__"): - - numargs -= 1 - - if restore: - - f = restore - - except AttributeError: - - try: - - if not _PY3K: - - call_im_func_code = f.__call__.im_func.func_code - - else: - - call_im_func_code = f.__code__ - - - - # not a function, must be a callable object, get info from the - - # im_func binding of its bound __call__ method - - if call_im_func_code.co_flags & STAR_ARGS: - - return f - - numargs = call_im_func_code.co_argcount - - if not _PY3K: - - if hasattr(f.__call__,"im_self"): - - numargs -= 1 - - else: - - if hasattr(f.__call__,"__self__"): - - numargs -= 0 - - except AttributeError: - - if not _PY3K: - - call_func_code = f.__call__.func_code - - else: - - call_func_code = f.__call__.__code__ - - # not a bound method, get info directly from __call__ method - - if call_func_code.co_flags & STAR_ARGS: - - return f - - numargs = call_func_code.co_argcount - - if not _PY3K: - - if hasattr(f.__call__,"im_self"): - - numargs -= 1 - - else: - - if hasattr(f.__call__,"__self__"): - - numargs -= 1 - - - - - - #~ print ("adding function %s with %d args" % (f.func_name,numargs)) - - if numargs == 3: - - return f - - else: - - if numargs > 3: - - def tmp(s,l,t): - - return f(f.__call__.__self__, s,l,t) - - if numargs == 2: - - def tmp(s,l,t): - - return f(l,t) - - elif numargs == 1: - - def tmp(s,l,t): - - return f(t) - - else: #~ numargs == 0: - - def tmp(s,l,t): - - return f() - - try: - - tmp.__name__ = f.__name__ - - except (AttributeError,TypeError): - - # no need for special handling if attribute doesnt exist - - pass - - try: - - tmp.__doc__ = f.__doc__ - - except (AttributeError,TypeError): - - # no need for special handling if attribute doesnt exist - - pass - - try: - - tmp.__dict__.update(f.__dict__) - - except (AttributeError,TypeError): - - # no need for special handling if attribute doesnt exist - - pass - - return tmp - - _normalizeParseActionArgs = staticmethod(_normalizeParseActionArgs) - - - - def setParseAction( self, *fns, **kwargs ): - - """Define action to perform when successfully matching parse element definition. - - Parse action fn is a callable method with 0-3 arguments, called as fn(s,loc,toks), - - fn(loc,toks), fn(toks), or just fn(), where: - - - s = the original string being parsed (see note below) - - - loc = the location of the matching substring - - - toks = a list of the matched tokens, packaged as a ParseResults object - - If the functions in fns modify the tokens, they can return them as the return - - value from fn, and the modified list of tokens will replace the original. - - Otherwise, fn does not need to return any value. - - - - Note: the default parsing behavior is to expand tabs in the input string - - before starting the parsing process. See L{I{parseString}} for more information - - on parsing strings containing s, and suggested methods to maintain a - - consistent view of the parsed string, the parse location, and line and column - - positions within the parsed string. - - """ - - self.parseAction = list(map(self._normalizeParseActionArgs, list(fns))) - - self.callDuringTry = ("callDuringTry" in kwargs and kwargs["callDuringTry"]) - - return self - - - - def addParseAction( self, *fns, **kwargs ): - - """Add parse action to expression's list of parse actions. See L{I{setParseAction}}.""" - - self.parseAction += list(map(self._normalizeParseActionArgs, list(fns))) - - self.callDuringTry = self.callDuringTry or ("callDuringTry" in kwargs and kwargs["callDuringTry"]) - - return self - - - - def setFailAction( self, fn ): - - """Define action to perform if parsing fails at this expression. - - Fail acton fn is a callable function that takes the arguments - - fn(s,loc,expr,err) where: - - - s = string being parsed - - - loc = location where expression match was attempted and failed - - - expr = the parse expression that failed - - - err = the exception thrown - - The function returns no value. It may throw ParseFatalException - - if it is desired to stop parsing immediately.""" - - self.failAction = fn - - return self - - - - def _skipIgnorables( self, instring, loc ): - - exprsFound = True - - while exprsFound: - - exprsFound = False - - for e in self.ignoreExprs: - - try: - - while 1: - - loc,dummy = e._parse( instring, loc ) - - exprsFound = True - - except ParseException: - - pass - - return loc - - - - def preParse( self, instring, loc ): - - if self.ignoreExprs: - - loc = self._skipIgnorables( instring, loc ) - - - - if self.skipWhitespace: - - wt = self.whiteChars - - instrlen = len(instring) - - while loc < instrlen and instring[loc] in wt: - - loc += 1 - - - - return loc - - - - def parseImpl( self, instring, loc, doActions=True ): - - return loc, [] - - - - def postParse( self, instring, loc, tokenlist ): - - return tokenlist - - - - #~ @profile - - def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): - - debugging = ( self.debug ) #and doActions ) - - - - if debugging or self.failAction: - - #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) - - if (self.debugActions[0] ): - - self.debugActions[0]( instring, loc, self ) - - if callPreParse and self.callPreparse: - - preloc = self.preParse( instring, loc ) - - else: - - preloc = loc - - tokensStart = loc - - try: - - try: - - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - except IndexError: - - raise ParseException( instring, len(instring), self.errmsg, self ) - - except ParseBaseException, err: - - #~ print ("Exception raised:", err) - - if self.debugActions[2]: - - self.debugActions[2]( instring, tokensStart, self, err ) - - if self.failAction: - - self.failAction( instring, tokensStart, self, err ) - - raise - - else: - - if callPreParse and self.callPreparse: - - preloc = self.preParse( instring, loc ) - - else: - - preloc = loc - - tokensStart = loc - - if self.mayIndexError or loc >= len(instring): - - try: - - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - except IndexError: - - raise ParseException( instring, len(instring), self.errmsg, self ) - - else: - - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - - - tokens = self.postParse( instring, loc, tokens ) - - - - retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) - - if self.parseAction and (doActions or self.callDuringTry): - - if debugging: - - try: - - for fn in self.parseAction: - - tokens = fn( instring, tokensStart, retTokens ) - - if tokens is not None: - - retTokens = ParseResults( tokens, - - self.resultsName, - - asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), - - modal=self.modalResults ) - - except ParseBaseException, err: - - #~ print "Exception raised in user parse action:", err - - if (self.debugActions[2] ): - - self.debugActions[2]( instring, tokensStart, self, err ) - - raise - - else: - - for fn in self.parseAction: - - tokens = fn( instring, tokensStart, retTokens ) - - if tokens is not None: - - retTokens = ParseResults( tokens, - - self.resultsName, - - asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), - - modal=self.modalResults ) - - - - if debugging: - - #~ print ("Matched",self,"->",retTokens.asList()) - - if (self.debugActions[1] ): - - self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) - - - - return loc, retTokens - - - - def tryParse( self, instring, loc ): - - try: - - return self._parse( instring, loc, doActions=False )[0] - - except ParseFatalException: - - raise ParseException( instring, loc, self.errmsg, self) - - - - # this method gets repeatedly called during backtracking with the same arguments - - - # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression - - def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): - - lookup = (self,instring,loc,callPreParse,doActions) - - if lookup in ParserElement._exprArgCache: - - value = ParserElement._exprArgCache[ lookup ] - - if isinstance(value,Exception): - - raise value - - return value - - else: - - try: - - value = self._parseNoCache( instring, loc, doActions, callPreParse ) - - ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) - - return value - - except ParseBaseException, pe: - - ParserElement._exprArgCache[ lookup ] = pe - - raise - - - - _parse = _parseNoCache - - - - # argument cache for optimizing repeated calls when backtracking through recursive expressions - - _exprArgCache = {} - - def resetCache(): - - ParserElement._exprArgCache.clear() - - resetCache = staticmethod(resetCache) - - - - _packratEnabled = False - - def enablePackrat(): - - """Enables "packrat" parsing, which adds memoizing to the parsing logic. - - Repeated parse attempts at the same string location (which happens - - often in many complex grammars) can immediately return a cached value, - - instead of re-executing parsing/validating code. Memoizing is done of - - both valid results and parsing exceptions. - - - - This speedup may break existing programs that use parse actions that - - have side-effects. For this reason, packrat parsing is disabled when - - you first import pyparsing. To activate the packrat feature, your - - program must call the class method ParserElement.enablePackrat(). If - - your program uses psyco to "compile as you go", you must call - - enablePackrat before calling psyco.full(). If you do not do this, - - Python will crash. For best results, call enablePackrat() immediately - - after importing pyparsing. - - """ - - if not ParserElement._packratEnabled: - - ParserElement._packratEnabled = True - - ParserElement._parse = ParserElement._parseCache - - enablePackrat = staticmethod(enablePackrat) - - - - def parseString( self, instring, parseAll=False ): - - """Execute the parse expression with the given string. - - This is the main interface to the client code, once the complete - - expression has been built. - - - - If you want the grammar to require that the entire input string be - - successfully parsed, then set parseAll to True (equivalent to ending - - the grammar with StringEnd()). - - - - Note: parseString implicitly calls expandtabs() on the input string, - - in order to report proper column numbers in parse actions. - - If the input string contains tabs and - - the grammar uses parse actions that use the loc argument to index into the - - string being parsed, you can ensure you have a consistent view of the input - - string by: - - - calling parseWithTabs on your grammar before calling parseString - - (see L{I{parseWithTabs}}) - - - define your parse action using the full (s,loc,toks) signature, and - - reference the input string using the parse action's s argument - - - explictly expand the tabs in your input string before calling - - parseString - - """ - - ParserElement.resetCache() - - if not self.streamlined: - - self.streamline() - - #~ self.saveAsList = True - - for e in self.ignoreExprs: - - e.streamline() - - if not self.keepTabs: - - instring = instring.expandtabs() - - try: - - loc, tokens = self._parse( instring, 0 ) - - if parseAll: - - loc = self.preParse( instring, loc ) - - StringEnd()._parse( instring, loc ) - - except ParseBaseException, exc: - - # catch and re-raise exception from here, clears out pyparsing internal stack trace - - raise exc - - else: - - return tokens - - - - def scanString( self, instring, maxMatches=_MAX_INT ): - - """Scan the input string for expression matches. Each match will return the - - matching tokens, start location, and end location. May be called with optional - - maxMatches argument, to clip scanning after 'n' matches are found. - - - - Note that the start and end locations are reported relative to the string - - being parsed. See L{I{parseString}} for more information on parsing - - strings with embedded tabs.""" - - if not self.streamlined: - - self.streamline() - - for e in self.ignoreExprs: - - e.streamline() - - - - if not self.keepTabs: - - instring = _ustr(instring).expandtabs() - - instrlen = len(instring) - - loc = 0 - - preparseFn = self.preParse - - parseFn = self._parse - - ParserElement.resetCache() - - matches = 0 - - try: - - while loc <= instrlen and matches < maxMatches: - - try: - - preloc = preparseFn( instring, loc ) - - nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) - - except ParseException: - - loc = preloc+1 - - else: - - matches += 1 - - yield tokens, preloc, nextLoc - - loc = nextLoc - - except ParseBaseException, pe: - - raise pe - - - - def transformString( self, instring ): - - """Extension to scanString, to modify matching text with modified tokens that may - - be returned from a parse action. To use transformString, define a grammar and - - attach a parse action to it that modifies the returned token list. - - Invoking transformString() on a target string will then scan for matches, - - and replace the matched text patterns according to the logic in the parse - - action. transformString() returns the resulting transformed string.""" - - out = [] - - lastE = 0 - - # force preservation of s, to minimize unwanted transformation of string, and to - - # keep string locs straight between transformString and scanString - - self.keepTabs = True - - try: - - for t,s,e in self.scanString( instring ): - - out.append( instring[lastE:s] ) - - if t: - - if isinstance(t,ParseResults): - - out += t.asList() - - elif isinstance(t,list): - - out += t - - else: - - out.append(t) - - lastE = e - - out.append(instring[lastE:]) - - return "".join(map(_ustr,out)) - - except ParseBaseException, pe: - - raise pe - - - - def searchString( self, instring, maxMatches=_MAX_INT ): - - """Another extension to scanString, simplifying the access to the tokens found - - to match the given parse expression. May be called with optional - - maxMatches argument, to clip searching after 'n' matches are found. - - """ - - try: - - return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) - - except ParseBaseException, pe: - - raise pe - - - - def __add__(self, other ): - - """Implementation of + operator - returns And""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return And( [ self, other ] ) - - - - def __radd__(self, other ): - - """Implementation of + operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other + self - - - - def __sub__(self, other): - - """Implementation of - operator, returns And with error stop""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return And( [ self, And._ErrorStop(), other ] ) - - - - def __rsub__(self, other ): - - """Implementation of - operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other - self - - - - def __mul__(self,other): - - if isinstance(other,int): - - minElements, optElements = other,0 - - elif isinstance(other,tuple): - - other = (other + (None, None))[:2] - - if other[0] is None: - - other = (0, other[1]) - - if isinstance(other[0],int) and other[1] is None: - - if other[0] == 0: - - return ZeroOrMore(self) - - if other[0] == 1: - - return OneOrMore(self) - - else: - - return self*other[0] + ZeroOrMore(self) - - elif isinstance(other[0],int) and isinstance(other[1],int): - - minElements, optElements = other - - optElements -= minElements - - else: - - raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) - - else: - - raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) - - - - if minElements < 0: - - raise ValueError("cannot multiply ParserElement by negative value") - - if optElements < 0: - - raise ValueError("second tuple value must be greater or equal to first tuple value") - - if minElements == optElements == 0: - - raise ValueError("cannot multiply ParserElement by 0 or (0,0)") - - - - if (optElements): - - def makeOptionalList(n): - - if n>1: - - return Optional(self + makeOptionalList(n-1)) - - else: - - return Optional(self) - - if minElements: - - if minElements == 1: - - ret = self + makeOptionalList(optElements) - - else: - - ret = And([self]*minElements) + makeOptionalList(optElements) - - else: - - ret = makeOptionalList(optElements) - - else: - - if minElements == 1: - - ret = self - - else: - - ret = And([self]*minElements) - - return ret - - - - def __rmul__(self, other): - - return self.__mul__(other) - - - - def __or__(self, other ): - - """Implementation of | operator - returns MatchFirst""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return MatchFirst( [ self, other ] ) - - - - def __ror__(self, other ): - - """Implementation of | operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other | self - - - - def __xor__(self, other ): - - """Implementation of ^ operator - returns Or""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return Or( [ self, other ] ) - - - - def __rxor__(self, other ): - - """Implementation of ^ operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other ^ self - - - - def __and__(self, other ): - - """Implementation of & operator - returns Each""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return Each( [ self, other ] ) - - - - def __rand__(self, other ): - - """Implementation of & operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other & self - - - - def __invert__( self ): - - """Implementation of ~ operator - returns NotAny""" - - return NotAny( self ) - - - - def __call__(self, name): - - """Shortcut for setResultsName, with listAllMatches=default:: - - userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") - - could be written as:: - - userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") - - """ - - return self.setResultsName(name) - - - - def suppress( self ): - - """Suppresses the output of this ParserElement; useful to keep punctuation from - - cluttering up returned output. - - """ - - return Suppress( self ) - - - - def leaveWhitespace( self ): - - """Disables the skipping of whitespace before matching the characters in the - - ParserElement's defined pattern. This is normally only used internally by - - the pyparsing module, but may be needed in some whitespace-sensitive grammars. - - """ - - self.skipWhitespace = False - - return self - - - - def setWhitespaceChars( self, chars ): - - """Overrides the default whitespace chars - - """ - - self.skipWhitespace = True - - self.whiteChars = chars - - self.copyDefaultWhiteChars = False - - return self - - - - def parseWithTabs( self ): - - """Overrides default behavior to expand s to spaces before parsing the input string. - - Must be called before parseString when the input grammar contains elements that - - match characters.""" - - self.keepTabs = True - - return self - - - - def ignore( self, other ): - - """Define expression to be ignored (e.g., comments) while doing pattern - - matching; may be called repeatedly, to define multiple comment or other - - ignorable patterns. - - """ - - if isinstance( other, Suppress ): - - if other not in self.ignoreExprs: - - self.ignoreExprs.append( other ) - - else: - - self.ignoreExprs.append( Suppress( other ) ) - - return self - - - - def setDebugActions( self, startAction, successAction, exceptionAction ): - - """Enable display of debugging messages while doing pattern matching.""" - - self.debugActions = (startAction or _defaultStartDebugAction, - - successAction or _defaultSuccessDebugAction, - - exceptionAction or _defaultExceptionDebugAction) - - self.debug = True - - return self - - - - def setDebug( self, flag=True ): - - """Enable display of debugging messages while doing pattern matching. - - Set flag to True to enable, False to disable.""" - - if flag: - - self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) - - else: - - self.debug = False - - return self - - - - def __str__( self ): - - return self.name - - - - def __repr__( self ): - - return _ustr(self) - - - - def streamline( self ): - - self.streamlined = True - - self.strRepr = None - - return self - - - - def checkRecursion( self, parseElementList ): - - pass - - - - def validate( self, validateTrace=[] ): - - """Check defined expressions for valid structure, check for infinite recursive definitions.""" - - self.checkRecursion( [] ) - - - - def parseFile( self, file_or_filename, parseAll=False ): - - """Execute the parse expression on the given file or filename. - - If a filename is specified (instead of a file object), - - the entire file is opened, read, and closed before parsing. - - """ - - try: - - file_contents = file_or_filename.read() - - except AttributeError: - - f = open(file_or_filename, "rb") - - file_contents = f.read() - - f.close() - - try: - - return self.parseString(file_contents, parseAll) - - except ParseBaseException, exc: - - # catch and re-raise exception from here, clears out pyparsing internal stack trace - - raise exc - - - - def getException(self): - - return ParseException("",0,self.errmsg,self) - - - - def __getattr__(self,aname): - - if aname == "myException": - - self.myException = ret = self.getException(); - - return ret; - - else: - - raise AttributeError("no such attribute " + aname) - - - - def __eq__(self,other): - - if isinstance(other, ParserElement): - - return self is other or self.__dict__ == other.__dict__ - - elif isinstance(other, basestring): - - try: - - self.parseString(_ustr(other), parseAll=True) - - return True - - except ParseBaseException: - - return False - - else: - - return super(ParserElement,self)==other - - - - def __ne__(self,other): - - return not (self == other) - - - - def __hash__(self): - - return hash(id(self)) - - - - def __req__(self,other): - - return self == other - - - - def __rne__(self,other): - - return not (self == other) - - - - - -class Token(ParserElement): - - """Abstract ParserElement subclass, for defining atomic matching patterns.""" - - def __init__( self ): - - super(Token,self).__init__( savelist=False ) - - #self.myException = ParseException("",0,"",self) - - - - def setName(self, name): - - s = super(Token,self).setName(name) - - self.errmsg = "Expected " + self.name - - #s.myException.msg = self.errmsg - - return s - - - - - -class Empty(Token): - - """An empty token, will always match.""" - - def __init__( self ): - - super(Empty,self).__init__() - - self.name = "Empty" - - self.mayReturnEmpty = True - - self.mayIndexError = False - - - - - -class NoMatch(Token): - - """A token that will never match.""" - - def __init__( self ): - - super(NoMatch,self).__init__() - - self.name = "NoMatch" - - self.mayReturnEmpty = True - - self.mayIndexError = False - - self.errmsg = "Unmatchable token" - - #self.myException.msg = self.errmsg - - - - def parseImpl( self, instring, loc, doActions=True ): - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - - -class Literal(Token): - - """Token to exactly match a specified string.""" - - def __init__( self, matchString ): - - super(Literal,self).__init__() - - self.match = matchString - - self.matchLen = len(matchString) - - try: - - self.firstMatchChar = matchString[0] - - except IndexError: - - warnings.warn("null string passed to Literal; use Empty() instead", - - SyntaxWarning, stacklevel=2) - - self.__class__ = Empty - - self.name = '"%s"' % _ustr(self.match) - - self.errmsg = "Expected " + self.name - - self.mayReturnEmpty = False - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - - - # Performance tuning: this routine gets called a *lot* - - # if this is a single character match string and the first character matches, - - # short-circuit as quickly as possible, and avoid calling startswith - - #~ @profile - - def parseImpl( self, instring, loc, doActions=True ): - - if (instring[loc] == self.firstMatchChar and - - (self.matchLen==1 or instring.startswith(self.match,loc)) ): - - return loc+self.matchLen, self.match - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - -_L = Literal - - - -class Keyword(Token): - - """Token to exactly match a specified string as a keyword, that is, it must be - - immediately followed by a non-keyword character. Compare with Literal:: - - Literal("if") will match the leading 'if' in 'ifAndOnlyIf'. - - Keyword("if") will not; it will only match the leading 'if in 'if x=1', or 'if(y==2)' - - Accepts two optional constructor arguments in addition to the keyword string: - - identChars is a string of characters that would be valid identifier characters, - - defaulting to all alphanumerics + "_" and "$"; caseless allows case-insensitive - - matching, default is False. - - """ - - DEFAULT_KEYWORD_CHARS = alphanums+"_$" - - - - def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): - - super(Keyword,self).__init__() - - self.match = matchString - - self.matchLen = len(matchString) - - try: - - self.firstMatchChar = matchString[0] - - except IndexError: - - warnings.warn("null string passed to Keyword; use Empty() instead", - - SyntaxWarning, stacklevel=2) - - self.name = '"%s"' % self.match - - self.errmsg = "Expected " + self.name - - self.mayReturnEmpty = False - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.caseless = caseless - - if caseless: - - self.caselessmatch = matchString.upper() - - identChars = identChars.upper() - - self.identChars = _str2dict(identChars) - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.caseless: - - if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and - - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and - - (loc == 0 or instring[loc-1].upper() not in self.identChars) ): - - return loc+self.matchLen, self.match - - else: - - if (instring[loc] == self.firstMatchChar and - - (self.matchLen==1 or instring.startswith(self.match,loc)) and - - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and - - (loc == 0 or instring[loc-1] not in self.identChars) ): - - return loc+self.matchLen, self.match - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - def copy(self): - - c = super(Keyword,self).copy() - - c.identChars = Keyword.DEFAULT_KEYWORD_CHARS - - return c - - - - def setDefaultKeywordChars( chars ): - - """Overrides the default Keyword chars - - """ - - Keyword.DEFAULT_KEYWORD_CHARS = chars - - setDefaultKeywordChars = staticmethod(setDefaultKeywordChars) - - - -class CaselessLiteral(Literal): - - """Token to match a specified string, ignoring case of letters. - - Note: the matched results will always be in the case of the given - - match string, NOT the case of the input text. - - """ - - def __init__( self, matchString ): - - super(CaselessLiteral,self).__init__( matchString.upper() ) - - # Preserve the defining literal. - - self.returnString = matchString - - self.name = "'%s'" % self.returnString - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - - - def parseImpl( self, instring, loc, doActions=True ): - - if instring[ loc:loc+self.matchLen ].upper() == self.match: - - return loc+self.matchLen, self.returnString - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class CaselessKeyword(Keyword): - - def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): - - super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) - - - - def parseImpl( self, instring, loc, doActions=True ): - - if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and - - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): - - return loc+self.matchLen, self.match - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class Word(Token): - - """Token for matching words composed of allowed character sets. - - Defined with string containing all allowed initial characters, - - an optional string containing allowed body characters (if omitted, - - defaults to the initial character set), and an optional minimum, - - maximum, and/or exact length. The default value for min is 1 (a - - minimum value < 1 is not valid); the default values for max and exact - - are 0, meaning no maximum or exact length restriction. - - """ - - def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False ): - - super(Word,self).__init__() - - self.initCharsOrig = initChars - - self.initChars = _str2dict(initChars) - - if bodyChars : - - self.bodyCharsOrig = bodyChars - - self.bodyChars = _str2dict(bodyChars) - - else: - - self.bodyCharsOrig = initChars - - self.bodyChars = _str2dict(initChars) - - - - self.maxSpecified = max > 0 - - - - if min < 1: - - raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") - - - - self.minLen = min - - - - if max > 0: - - self.maxLen = max - - else: - - self.maxLen = _MAX_INT - - - - if exact > 0: - - self.maxLen = exact - - self.minLen = exact - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.asKeyword = asKeyword - - - - if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): - - if self.bodyCharsOrig == self.initCharsOrig: - - self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) - - elif len(self.bodyCharsOrig) == 1: - - self.reString = "%s[%s]*" % \ - - (re.escape(self.initCharsOrig), - - _escapeRegexRangeChars(self.bodyCharsOrig),) - - else: - - self.reString = "[%s][%s]*" % \ - - (_escapeRegexRangeChars(self.initCharsOrig), - - _escapeRegexRangeChars(self.bodyCharsOrig),) - - if self.asKeyword: - - self.reString = r"\b"+self.reString+r"\b" - - try: - - self.re = re.compile( self.reString ) - - except: - - self.re = None - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.re: - - result = self.re.match(instring,loc) - - if not result: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - loc = result.end() - - return loc,result.group() - - - - if not(instring[ loc ] in self.initChars): - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - start = loc - - loc += 1 - - instrlen = len(instring) - - bodychars = self.bodyChars - - maxloc = start + self.maxLen - - maxloc = min( maxloc, instrlen ) - - while loc < maxloc and instring[loc] in bodychars: - - loc += 1 - - - - throwException = False - - if loc - start < self.minLen: - - throwException = True - - if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: - - throwException = True - - if self.asKeyword: - - if (start>0 and instring[start-1] in bodychars) or (loc4: - - return s[:4]+"..." - - else: - - return s - - - - if ( self.initCharsOrig != self.bodyCharsOrig ): - - self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) - - else: - - self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) - - - - return self.strRepr - - - - - -class Regex(Token): - - """Token for matching strings that match a given regular expression. - - Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. - - """ - - def __init__( self, pattern, flags=0): - - """The parameters pattern and flags are passed to the re.compile() function as-is. See the Python re module for an explanation of the acceptable patterns and flags.""" - - super(Regex,self).__init__() - - - - if len(pattern) == 0: - - warnings.warn("null string passed to Regex; use Empty() instead", - - SyntaxWarning, stacklevel=2) - - - - self.pattern = pattern - - self.flags = flags - - - - try: - - self.re = re.compile(self.pattern, self.flags) - - self.reString = self.pattern - - except sre_constants.error: - - warnings.warn("invalid pattern (%s) passed to Regex" % pattern, - - SyntaxWarning, stacklevel=2) - - raise - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - result = self.re.match(instring,loc) - - if not result: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - loc = result.end() - - d = result.groupdict() - - ret = ParseResults(result.group()) - - if d: - - for k in d: - - ret[k] = d[k] - - return loc,ret - - - - def __str__( self ): - - try: - - return super(Regex,self).__str__() - - except: - - pass - - - - if self.strRepr is None: - - self.strRepr = "Re:(%s)" % repr(self.pattern) - - - - return self.strRepr - - - - - -class QuotedString(Token): - - """Token for matching strings that are delimited by quoting characters. - - """ - - def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): - - """ - - Defined with the following parameters: - - - quoteChar - string of one or more characters defining the quote delimiting string - - - escChar - character to escape quotes, typically backslash (default=None) - - - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) - - - multiline - boolean indicating whether quotes can span multiple lines (default=False) - - - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True) - - - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar) - - """ - - super(QuotedString,self).__init__() - - - - # remove white space from quote chars - wont work anyway - - quoteChar = quoteChar.strip() - - if len(quoteChar) == 0: - - warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) - - raise SyntaxError() - - - - if endQuoteChar is None: - - endQuoteChar = quoteChar - - else: - - endQuoteChar = endQuoteChar.strip() - - if len(endQuoteChar) == 0: - - warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) - - raise SyntaxError() - - - - self.quoteChar = quoteChar - - self.quoteCharLen = len(quoteChar) - - self.firstQuoteChar = quoteChar[0] - - self.endQuoteChar = endQuoteChar - - self.endQuoteCharLen = len(endQuoteChar) - - self.escChar = escChar - - self.escQuote = escQuote - - self.unquoteResults = unquoteResults - - - - if multiline: - - self.flags = re.MULTILINE | re.DOTALL - - self.pattern = r'%s(?:[^%s%s]' % \ - - ( re.escape(self.quoteChar), - - _escapeRegexRangeChars(self.endQuoteChar[0]), - - (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) - - else: - - self.flags = 0 - - self.pattern = r'%s(?:[^%s\n\r%s]' % \ - - ( re.escape(self.quoteChar), - - _escapeRegexRangeChars(self.endQuoteChar[0]), - - (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) - - if len(self.endQuoteChar) > 1: - - self.pattern += ( - - '|(?:' + ')|(?:'.join(["%s[^%s]" % (re.escape(self.endQuoteChar[:i]), - - _escapeRegexRangeChars(self.endQuoteChar[i])) - - for i in range(len(self.endQuoteChar)-1,0,-1)]) + ')' - - ) - - if escQuote: - - self.pattern += (r'|(?:%s)' % re.escape(escQuote)) - - if escChar: - - self.pattern += (r'|(?:%s.)' % re.escape(escChar)) - - self.escCharReplacePattern = re.escape(self.escChar)+"(.)" - - self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) - - - - try: - - self.re = re.compile(self.pattern, self.flags) - - self.reString = self.pattern - - except sre_constants.error: - - warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, - - SyntaxWarning, stacklevel=2) - - raise - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None - - if not result: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - loc = result.end() - - ret = result.group() - - - - if self.unquoteResults: - - - - # strip off quotes - - ret = ret[self.quoteCharLen:-self.endQuoteCharLen] - - - - if isinstance(ret,basestring): - - # replace escaped characters - - if self.escChar: - - ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) - - - - # replace escaped quotes - - if self.escQuote: - - ret = ret.replace(self.escQuote, self.endQuoteChar) - - - - return loc, ret - - - - def __str__( self ): - - try: - - return super(QuotedString,self).__str__() - - except: - - pass - - - - if self.strRepr is None: - - self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) - - - - return self.strRepr - - - - - -class CharsNotIn(Token): - - """Token for matching words composed of characters *not* in a given set. - - Defined with string containing all disallowed characters, and an optional - - minimum, maximum, and/or exact length. The default value for min is 1 (a - - minimum value < 1 is not valid); the default values for max and exact - - are 0, meaning no maximum or exact length restriction. - - """ - - def __init__( self, notChars, min=1, max=0, exact=0 ): - - super(CharsNotIn,self).__init__() - - self.skipWhitespace = False - - self.notChars = notChars - - - - if min < 1: - - raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") - - - - self.minLen = min - - - - if max > 0: - - self.maxLen = max - - else: - - self.maxLen = _MAX_INT - - - - if exact > 0: - - self.maxLen = exact - - self.minLen = exact - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - self.mayReturnEmpty = ( self.minLen == 0 ) - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - - - def parseImpl( self, instring, loc, doActions=True ): - - if instring[loc] in self.notChars: - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - start = loc - - loc += 1 - - notchars = self.notChars - - maxlen = min( start+self.maxLen, len(instring) ) - - while loc < maxlen and \ - - (instring[loc] not in notchars): - - loc += 1 - - - - if loc - start < self.minLen: - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - return loc, instring[start:loc] - - - - def __str__( self ): - - try: - - return super(CharsNotIn, self).__str__() - - except: - - pass - - - - if self.strRepr is None: - - if len(self.notChars) > 4: - - self.strRepr = "!W:(%s...)" % self.notChars[:4] - - else: - - self.strRepr = "!W:(%s)" % self.notChars - - - - return self.strRepr - - - -class White(Token): - - """Special matching class for matching whitespace. Normally, whitespace is ignored - - by pyparsing grammars. This class is included when some whitespace structures - - are significant. Define with a string containing the whitespace characters to be - - matched; default is " \\t\\r\\n". Also takes optional min, max, and exact arguments, - - as defined for the Word class.""" - - whiteStrs = { - - " " : "", - - "\t": "", - - "\n": "", - - "\r": "", - - "\f": "", - - } - - def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): - - super(White,self).__init__() - - self.matchWhite = ws - - self.setWhitespaceChars( "".join([c for c in self.whiteChars if c not in self.matchWhite]) ) - - #~ self.leaveWhitespace() - - self.name = ("".join([White.whiteStrs[c] for c in self.matchWhite])) - - self.mayReturnEmpty = True - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - - - self.minLen = min - - - - if max > 0: - - self.maxLen = max - - else: - - self.maxLen = _MAX_INT - - - - if exact > 0: - - self.maxLen = exact - - self.minLen = exact - - - - def parseImpl( self, instring, loc, doActions=True ): - - if not(instring[ loc ] in self.matchWhite): - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - start = loc - - loc += 1 - - maxloc = start + self.maxLen - - maxloc = min( maxloc, len(instring) ) - - while loc < maxloc and instring[loc] in self.matchWhite: - - loc += 1 - - - - if loc - start < self.minLen: - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - return loc, instring[start:loc] - - - - - -class _PositionToken(Token): - - def __init__( self ): - - super(_PositionToken,self).__init__() - - self.name=self.__class__.__name__ - - self.mayReturnEmpty = True - - self.mayIndexError = False - - - -class GoToColumn(_PositionToken): - - """Token to advance to a specific column of input text; useful for tabular report scraping.""" - - def __init__( self, colno ): - - super(GoToColumn,self).__init__() - - self.col = colno - - - - def preParse( self, instring, loc ): - - if col(loc,instring) != self.col: - - instrlen = len(instring) - - if self.ignoreExprs: - - loc = self._skipIgnorables( instring, loc ) - - while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : - - loc += 1 - - return loc - - - - def parseImpl( self, instring, loc, doActions=True ): - - thiscol = col( loc, instring ) - - if thiscol > self.col: - - raise ParseException( instring, loc, "Text not in expected column", self ) - - newloc = loc + self.col - thiscol - - ret = instring[ loc: newloc ] - - return newloc, ret - - - -class LineStart(_PositionToken): - - """Matches if current position is at the beginning of a line within the parse string""" - - def __init__( self ): - - super(LineStart,self).__init__() - - self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) - - self.errmsg = "Expected start of line" - - #self.myException.msg = self.errmsg - - - - def preParse( self, instring, loc ): - - preloc = super(LineStart,self).preParse(instring,loc) - - if instring[preloc] == "\n": - - loc += 1 - - return loc - - - - def parseImpl( self, instring, loc, doActions=True ): - - if not( loc==0 or - - (loc == self.preParse( instring, 0 )) or - - (instring[loc-1] == "\n") ): #col(loc, instring) != 1: - - #~ raise ParseException( instring, loc, "Expected start of line" ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - return loc, [] - - - -class LineEnd(_PositionToken): - - """Matches if current position is at the end of a line within the parse string""" - - def __init__( self ): - - super(LineEnd,self).__init__() - - self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) - - self.errmsg = "Expected end of line" - - #self.myException.msg = self.errmsg - - - - def parseImpl( self, instring, loc, doActions=True ): - - if loc len(instring): - - return loc, [] - - else: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class WordStart(_PositionToken): - - """Matches if the current position is at the beginning of a Word, and - - is not preceded by any character in a given set of wordChars - - (default=printables). To emulate the \b behavior of regular expressions, - - use WordStart(alphanums). WordStart will also match at the beginning of - - the string being parsed, or at the beginning of a line. - - """ - - def __init__(self, wordChars = printables): - - super(WordStart,self).__init__() - - self.wordChars = _str2dict(wordChars) - - self.errmsg = "Not at the start of a word" - - - - def parseImpl(self, instring, loc, doActions=True ): - - if loc != 0: - - if (instring[loc-1] in self.wordChars or - - instring[loc] not in self.wordChars): - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - return loc, [] - - - -class WordEnd(_PositionToken): - - """Matches if the current position is at the end of a Word, and - - is not followed by any character in a given set of wordChars - - (default=printables). To emulate the \b behavior of regular expressions, - - use WordEnd(alphanums). WordEnd will also match at the end of - - the string being parsed, or at the end of a line. - - """ - - def __init__(self, wordChars = printables): - - super(WordEnd,self).__init__() - - self.wordChars = _str2dict(wordChars) - - self.skipWhitespace = False - - self.errmsg = "Not at the end of a word" - - - - def parseImpl(self, instring, loc, doActions=True ): - - instrlen = len(instring) - - if instrlen>0 and loc maxExcLoc: - - maxException = err - - maxExcLoc = err.loc - - except IndexError: - - if len(instring) > maxExcLoc: - - maxException = ParseException(instring,len(instring),e.errmsg,self) - - maxExcLoc = len(instring) - - else: - - if loc2 > maxMatchLoc: - - maxMatchLoc = loc2 - - maxMatchExp = e - - - - if maxMatchLoc < 0: - - if maxException is not None: - - raise maxException - - else: - - raise ParseException(instring, loc, "no defined alternatives to match", self) - - - - return maxMatchExp._parse( instring, loc, doActions ) - - - - def __ixor__(self, other ): - - if isinstance( other, basestring ): - - other = Literal( other ) - - return self.append( other ) #Or( [ self, other ] ) - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + " ^ ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - - - return self.strRepr - - - - def checkRecursion( self, parseElementList ): - - subRecCheckList = parseElementList[:] + [ self ] - - for e in self.exprs: - - e.checkRecursion( subRecCheckList ) - - - - - -class MatchFirst(ParseExpression): - - """Requires that at least one ParseExpression is found. - - If two expressions match, the first one listed is the one that will match. - - May be constructed using the '|' operator. - - """ - - def __init__( self, exprs, savelist = False ): - - super(MatchFirst,self).__init__(exprs, savelist) - - if exprs: - - self.mayReturnEmpty = False - - for e in self.exprs: - - if e.mayReturnEmpty: - - self.mayReturnEmpty = True - - break - - else: - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - maxExcLoc = -1 - - maxException = None - - for e in self.exprs: - - try: - - ret = e._parse( instring, loc, doActions ) - - return ret - - except ParseException, err: - - if err.loc > maxExcLoc: - - maxException = err - - maxExcLoc = err.loc - - except IndexError: - - if len(instring) > maxExcLoc: - - maxException = ParseException(instring,len(instring),e.errmsg,self) - - maxExcLoc = len(instring) - - - - # only got here if no expression matched, raise exception for match that made it the furthest - - else: - - if maxException is not None: - - raise maxException - - else: - - raise ParseException(instring, loc, "no defined alternatives to match", self) - - - - def __ior__(self, other ): - - if isinstance( other, basestring ): - - other = Literal( other ) - - return self.append( other ) #MatchFirst( [ self, other ] ) - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + " | ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - - - return self.strRepr - - - - def checkRecursion( self, parseElementList ): - - subRecCheckList = parseElementList[:] + [ self ] - - for e in self.exprs: - - e.checkRecursion( subRecCheckList ) - - - - - -class Each(ParseExpression): - - """Requires all given ParseExpressions to be found, but in any order. - - Expressions may be separated by whitespace. - - May be constructed using the '&' operator. - - """ - - def __init__( self, exprs, savelist = True ): - - super(Each,self).__init__(exprs, savelist) - - self.mayReturnEmpty = True - - for e in self.exprs: - - if not e.mayReturnEmpty: - - self.mayReturnEmpty = False - - break - - self.skipWhitespace = True - - self.initExprGroups = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.initExprGroups: - - self.optionals = [ e.expr for e in self.exprs if isinstance(e,Optional) ] - - self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] - - self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] - - self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] - - self.required += self.multirequired - - self.initExprGroups = False - - tmpLoc = loc - - tmpReqd = self.required[:] - - tmpOpt = self.optionals[:] - - matchOrder = [] - - - - keepMatching = True - - while keepMatching: - - tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired - - failed = [] - - for e in tmpExprs: - - try: - - tmpLoc = e.tryParse( instring, tmpLoc ) - - except ParseException: - - failed.append(e) - - else: - - matchOrder.append(e) - - if e in tmpReqd: - - tmpReqd.remove(e) - - elif e in tmpOpt: - - tmpOpt.remove(e) - - if len(failed) == len(tmpExprs): - - keepMatching = False - - - - if tmpReqd: - - missing = ", ".join( [ _ustr(e) for e in tmpReqd ] ) - - raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) - - - - # add any unmatched Optionals, in case they have default values defined - - matchOrder += list(e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt) - - - - resultlist = [] - - for e in matchOrder: - - loc,results = e._parse(instring,loc,doActions) - - resultlist.append(results) - - - - finalResults = ParseResults([]) - - for r in resultlist: - - dups = {} - - for k in r.keys(): - - if k in finalResults.keys(): - - tmp = ParseResults(finalResults[k]) - - tmp += ParseResults(r[k]) - - dups[k] = tmp - - finalResults += ParseResults(r) - - for k,v in dups.items(): - - finalResults[k] = v - - return loc, finalResults - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + " & ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - - - return self.strRepr - - - - def checkRecursion( self, parseElementList ): - - subRecCheckList = parseElementList[:] + [ self ] - - for e in self.exprs: - - e.checkRecursion( subRecCheckList ) - - - - - -class ParseElementEnhance(ParserElement): - - """Abstract subclass of ParserElement, for combining and post-processing parsed tokens.""" - - def __init__( self, expr, savelist=False ): - - super(ParseElementEnhance,self).__init__(savelist) - - if isinstance( expr, basestring ): - - expr = Literal(expr) - - self.expr = expr - - self.strRepr = None - - if expr is not None: - - self.mayIndexError = expr.mayIndexError - - self.mayReturnEmpty = expr.mayReturnEmpty - - self.setWhitespaceChars( expr.whiteChars ) - - self.skipWhitespace = expr.skipWhitespace - - self.saveAsList = expr.saveAsList - - self.callPreparse = expr.callPreparse - - self.ignoreExprs.extend(expr.ignoreExprs) - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.expr is not None: - - return self.expr._parse( instring, loc, doActions, callPreParse=False ) - - else: - - raise ParseException("",loc,self.errmsg,self) - - - - def leaveWhitespace( self ): - - self.skipWhitespace = False - - self.expr = self.expr.copy() - - if self.expr is not None: - - self.expr.leaveWhitespace() - - return self - - - - def ignore( self, other ): - - if isinstance( other, Suppress ): - - if other not in self.ignoreExprs: - - super( ParseElementEnhance, self).ignore( other ) - - if self.expr is not None: - - self.expr.ignore( self.ignoreExprs[-1] ) - - else: - - super( ParseElementEnhance, self).ignore( other ) - - if self.expr is not None: - - self.expr.ignore( self.ignoreExprs[-1] ) - - return self - - - - def streamline( self ): - - super(ParseElementEnhance,self).streamline() - - if self.expr is not None: - - self.expr.streamline() - - return self - - - - def checkRecursion( self, parseElementList ): - - if self in parseElementList: - - raise RecursiveGrammarException( parseElementList+[self] ) - - subRecCheckList = parseElementList[:] + [ self ] - - if self.expr is not None: - - self.expr.checkRecursion( subRecCheckList ) - - - - def validate( self, validateTrace=[] ): - - tmp = validateTrace[:]+[self] - - if self.expr is not None: - - self.expr.validate(tmp) - - self.checkRecursion( [] ) - - - - def __str__( self ): - - try: - - return super(ParseElementEnhance,self).__str__() - - except: - - pass - - - - if self.strRepr is None and self.expr is not None: - - self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) - - return self.strRepr - - - - - -class FollowedBy(ParseElementEnhance): - - """Lookahead matching of the given parse expression. FollowedBy - - does *not* advance the parsing position within the input string, it only - - verifies that the specified parse expression matches at the current - - position. FollowedBy always returns a null token list.""" - - def __init__( self, expr ): - - super(FollowedBy,self).__init__(expr) - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - self.expr.tryParse( instring, loc ) - - return loc, [] - - - - - -class NotAny(ParseElementEnhance): - - """Lookahead to disallow matching with the given parse expression. NotAny - - does *not* advance the parsing position within the input string, it only - - verifies that the specified parse expression does *not* match at the current - - position. Also, NotAny does *not* skip over leading whitespace. NotAny - - always returns a null token list. May be constructed using the '~' operator.""" - - def __init__( self, expr ): - - super(NotAny,self).__init__(expr) - - #~ self.leaveWhitespace() - - self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs - - self.mayReturnEmpty = True - - self.errmsg = "Found unwanted token, "+_ustr(self.expr) - - #self.myException = ParseException("",0,self.errmsg,self) - - - - def parseImpl( self, instring, loc, doActions=True ): - - try: - - self.expr.tryParse( instring, loc ) - - except (ParseException,IndexError): - - pass - - else: - - #~ raise ParseException(instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - return loc, [] - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "~{" + _ustr(self.expr) + "}" - - - - return self.strRepr - - - - - -class ZeroOrMore(ParseElementEnhance): - - """Optional repetition of zero or more of the given expression.""" - - def __init__( self, expr ): - - super(ZeroOrMore,self).__init__(expr) - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - tokens = [] - - try: - - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - - while 1: - - if hasIgnoreExprs: - - preloc = self._skipIgnorables( instring, loc ) - - else: - - preloc = loc - - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - - if tmptokens or tmptokens.keys(): - - tokens += tmptokens - - except (ParseException,IndexError): - - pass - - - - return loc, tokens - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "[" + _ustr(self.expr) + "]..." - - - - return self.strRepr - - - - def setResultsName( self, name, listAllMatches=False ): - - ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) - - ret.saveAsList = True - - return ret - - - - - -class OneOrMore(ParseElementEnhance): - - """Repetition of one or more of the given expression.""" - - def parseImpl( self, instring, loc, doActions=True ): - - # must be at least one - - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - - try: - - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - - while 1: - - if hasIgnoreExprs: - - preloc = self._skipIgnorables( instring, loc ) - - else: - - preloc = loc - - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - - if tmptokens or tmptokens.keys(): - - tokens += tmptokens - - except (ParseException,IndexError): - - pass - - - - return loc, tokens - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + _ustr(self.expr) + "}..." - - - - return self.strRepr - - - - def setResultsName( self, name, listAllMatches=False ): - - ret = super(OneOrMore,self).setResultsName(name,listAllMatches) - - ret.saveAsList = True - - return ret - - - -class _NullToken(object): - - def __bool__(self): - - return False - - __nonzero__ = __bool__ - - def __str__(self): - - return "" - - - -_optionalNotMatched = _NullToken() - -class Optional(ParseElementEnhance): - - """Optional matching of the given expression. - - A default return string can also be specified, if the optional expression - - is not found. - - """ - - def __init__( self, exprs, default=_optionalNotMatched ): - - super(Optional,self).__init__( exprs, savelist=False ) - - self.defaultValue = default - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - try: - - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - - except (ParseException,IndexError): - - if self.defaultValue is not _optionalNotMatched: - - if self.expr.resultsName: - - tokens = ParseResults([ self.defaultValue ]) - - tokens[self.expr.resultsName] = self.defaultValue - - else: - - tokens = [ self.defaultValue ] - - else: - - tokens = [] - - return loc, tokens - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "[" + _ustr(self.expr) + "]" - - - - return self.strRepr - - - - - -class SkipTo(ParseElementEnhance): - - """Token for skipping over all undefined text until the matched expression is found. - - If include is set to true, the matched expression is also parsed (the skipped text - - and matched expression are returned as a 2-element list). The ignore - - argument is used to define grammars (typically quoted strings and comments) that - - might contain false matches. - - """ - - def __init__( self, other, include=False, ignore=None, failOn=None ): - - super( SkipTo, self ).__init__( other ) - - self.ignoreExpr = ignore - - self.mayReturnEmpty = True - - self.mayIndexError = False - - self.includeMatch = include - - self.asList = False - - if failOn is not None and isinstance(failOn, basestring): - - self.failOn = Literal(failOn) - - else: - - self.failOn = failOn - - self.errmsg = "No match found for "+_ustr(self.expr) - - #self.myException = ParseException("",0,self.errmsg,self) - - - - def parseImpl( self, instring, loc, doActions=True ): - - startLoc = loc - - instrlen = len(instring) - - expr = self.expr - - failParse = False - - while loc <= instrlen: - - try: - - if self.failOn: - - try: - - self.failOn.tryParse(instring, loc) - - except ParseBaseException: - - pass - - else: - - failParse = True - - raise ParseException(instring, loc, "Found expression " + str(self.failOn)) - - failParse = False - - if self.ignoreExpr is not None: - - while 1: - - try: - - loc = self.ignoreExpr.tryParse(instring,loc) - - print "found ignoreExpr, advance to", loc - - except ParseBaseException: - - break - - expr._parse( instring, loc, doActions=False, callPreParse=False ) - - skipText = instring[startLoc:loc] - - if self.includeMatch: - - loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) - - if mat: - - skipRes = ParseResults( skipText ) - - skipRes += mat - - return loc, [ skipRes ] - - else: - - return loc, [ skipText ] - - else: - - return loc, [ skipText ] - - except (ParseException,IndexError): - - if failParse: - - raise - - else: - - loc += 1 - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class Forward(ParseElementEnhance): - - """Forward declaration of an expression to be defined later - - - used for recursive grammars, such as algebraic infix notation. - - When the expression is known, it is assigned to the Forward variable using the '<<' operator. - - - - Note: take care when assigning to Forward not to overlook precedence of operators. - - Specifically, '|' has a lower precedence than '<<', so that:: - - fwdExpr << a | b | c - - will actually be evaluated as:: - - (fwdExpr << a) | b | c - - thereby leaving b and c out as parseable alternatives. It is recommended that you - - explicitly group the values inserted into the Forward:: - - fwdExpr << (a | b | c) - - """ - - def __init__( self, other=None ): - - super(Forward,self).__init__( other, savelist=False ) - - - - def __lshift__( self, other ): - - if isinstance( other, basestring ): - - other = Literal(other) - - self.expr = other - - self.mayReturnEmpty = other.mayReturnEmpty - - self.strRepr = None - - self.mayIndexError = self.expr.mayIndexError - - self.mayReturnEmpty = self.expr.mayReturnEmpty - - self.setWhitespaceChars( self.expr.whiteChars ) - - self.skipWhitespace = self.expr.skipWhitespace - - self.saveAsList = self.expr.saveAsList - - self.ignoreExprs.extend(self.expr.ignoreExprs) - - return None - - - - def leaveWhitespace( self ): - - self.skipWhitespace = False - - return self - - - - def streamline( self ): - - if not self.streamlined: - - self.streamlined = True - - if self.expr is not None: - - self.expr.streamline() - - return self - - - - def validate( self, validateTrace=[] ): - - if self not in validateTrace: - - tmp = validateTrace[:]+[self] - - if self.expr is not None: - - self.expr.validate(tmp) - - self.checkRecursion([]) - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - self._revertClass = self.__class__ - - self.__class__ = _ForwardNoRecurse - - try: - - if self.expr is not None: - - retString = _ustr(self.expr) - - else: - - retString = "None" - - finally: - - self.__class__ = self._revertClass - - return self.__class__.__name__ + ": " + retString - - - - def copy(self): - - if self.expr is not None: - - return super(Forward,self).copy() - - else: - - ret = Forward() - - ret << self - - return ret - - - -class _ForwardNoRecurse(Forward): - - def __str__( self ): - - return "..." - - - -class TokenConverter(ParseElementEnhance): - - """Abstract subclass of ParseExpression, for converting parsed results.""" - - def __init__( self, expr, savelist=False ): - - super(TokenConverter,self).__init__( expr )#, savelist ) - - self.saveAsList = False - - - -class Upcase(TokenConverter): - - """Converter to upper case all matching tokens.""" - - def __init__(self, *args): - - super(Upcase,self).__init__(*args) - - warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", - - DeprecationWarning,stacklevel=2) - - - - def postParse( self, instring, loc, tokenlist ): - - return list(map( string.upper, tokenlist )) - - - - - -class Combine(TokenConverter): - - """Converter to concatenate all matching tokens to a single string. - - By default, the matching patterns must also be contiguous in the input string; - - this can be disabled by specifying 'adjacent=False' in the constructor. - - """ - - def __init__( self, expr, joinString="", adjacent=True ): - - super(Combine,self).__init__( expr ) - - # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself - - if adjacent: - - self.leaveWhitespace() - - self.adjacent = adjacent - - self.skipWhitespace = True - - self.joinString = joinString - - - - def ignore( self, other ): - - if self.adjacent: - - ParserElement.ignore(self, other) - - else: - - super( Combine, self).ignore( other ) - - return self - - - - def postParse( self, instring, loc, tokenlist ): - - retToks = tokenlist.copy() - - del retToks[:] - - retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) - - - - if self.resultsName and len(retToks.keys())>0: - - return [ retToks ] - - else: - - return retToks - - - -class Group(TokenConverter): - - """Converter to return the matched tokens as a list - useful for returning tokens of ZeroOrMore and OneOrMore expressions.""" - - def __init__( self, expr ): - - super(Group,self).__init__( expr ) - - self.saveAsList = True - - - - def postParse( self, instring, loc, tokenlist ): - - return [ tokenlist ] - - - -class Dict(TokenConverter): - - """Converter to return a repetitive expression as a list, but also as a dictionary. - - Each element can also be referenced using the first token in the expression as its key. - - Useful for tabular report scraping when the first column can be used as a item key. - - """ - - def __init__( self, exprs ): - - super(Dict,self).__init__( exprs ) - - self.saveAsList = True - - - - def postParse( self, instring, loc, tokenlist ): - - for i,tok in enumerate(tokenlist): - - if len(tok) == 0: - - continue - - ikey = tok[0] - - if isinstance(ikey,int): - - ikey = _ustr(tok[0]).strip() - - if len(tok)==1: - - tokenlist[ikey] = _ParseResultsWithOffset("",i) - - elif len(tok)==2 and not isinstance(tok[1],ParseResults): - - tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) - - else: - - dictvalue = tok.copy() #ParseResults(i) - - del dictvalue[0] - - if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.keys()): - - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) - - else: - - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) - - - - if self.resultsName: - - return [ tokenlist ] - - else: - - return tokenlist - - - - - -class Suppress(TokenConverter): - - """Converter for ignoring the results of a parsed expression.""" - - def postParse( self, instring, loc, tokenlist ): - - return [] - - - - def suppress( self ): - - return self - - - - - -class OnlyOnce(object): - - """Wrapper for parse actions, to ensure they are only called once.""" - - def __init__(self, methodCall): - - self.callable = ParserElement._normalizeParseActionArgs(methodCall) - - self.called = False - - def __call__(self,s,l,t): - - if not self.called: - - results = self.callable(s,l,t) - - self.called = True - - return results - - raise ParseException(s,l,"") - - def reset(self): - - self.called = False - - - -def traceParseAction(f): - - """Decorator for debugging parse actions.""" - - f = ParserElement._normalizeParseActionArgs(f) - - def z(*paArgs): - - thisFunc = f.func_name - - s,l,t = paArgs[-3:] - - if len(paArgs)>3: - - thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc - - sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) - - try: - - ret = f(*paArgs) - - except Exception, exc: - - sys.stderr.write( "<", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) - - try: - - if len(symbols)==len("".join(symbols)): - - return Regex( "[%s]" % "".join( [ _escapeRegexRangeChars(sym) for sym in symbols] ) ) - - else: - - return Regex( "|".join( [ re.escape(sym) for sym in symbols] ) ) - - except: - - warnings.warn("Exception creating Regex for oneOf, building MatchFirst", - - SyntaxWarning, stacklevel=2) - - - - - - # last resort, just use MatchFirst - - return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) - - - -def dictOf( key, value ): - - """Helper to easily and clearly define a dictionary by specifying the respective patterns - - for the key and value. Takes care of defining the Dict, ZeroOrMore, and Group tokens - - in the proper order. The key pattern can include delimiting markers or punctuation, - - as long as they are suppressed, thereby leaving the significant key text. The value - - pattern can include named results, so that the Dict results can include named token - - fields. - - """ - - return Dict( ZeroOrMore( Group ( key + value ) ) ) - - - -def originalTextFor(expr, asString=True): - - """Helper to return the original, untokenized text for a given expression. Useful to - - restore the parsed fields of an HTML start tag into the raw tag text itself, or to - - revert separate tokens with intervening whitespace back to the original matching - - input text. Simpler to use than the parse action keepOriginalText, and does not - - require the inspect module to chase up the call stack. By default, returns a - - string containing the original parsed text. - - - - If the optional asString argument is passed as False, then the return value is a - - ParseResults containing any results names that were originally matched, and a - - single token containing the original matched text from the input string. So if - - the expression passed to originalTextFor contains expressions with defined - - results names, you must set asString to False if you want to preserve those - - results name values.""" - - locMarker = Empty().setParseAction(lambda s,loc,t: loc) - - matchExpr = locMarker("_original_start") + expr + locMarker("_original_end") - - if asString: - - extractText = lambda s,l,t: s[t._original_start:t._original_end] - - else: - - def extractText(s,l,t): - - del t[:] - - t.insert(0, s[t._original_start:t._original_end]) - - del t["_original_start"] - - del t["_original_end"] - - matchExpr.setParseAction(extractText) - - return matchExpr - - - -# convenience constants for positional expressions - -empty = Empty().setName("empty") - -lineStart = LineStart().setName("lineStart") - -lineEnd = LineEnd().setName("lineEnd") - -stringStart = StringStart().setName("stringStart") - -stringEnd = StringEnd().setName("stringEnd") - - - -_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) - -_printables_less_backslash = "".join([ c for c in printables if c not in r"\]" ]) - -_escapedHexChar = Combine( Suppress(_bslash + "0x") + Word(hexnums) ).setParseAction(lambda s,l,t:unichr(int(t[0],16))) - -_escapedOctChar = Combine( Suppress(_bslash) + Word("0","01234567") ).setParseAction(lambda s,l,t:unichr(int(t[0],8))) - -_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(_printables_less_backslash,exact=1) - -_charRange = Group(_singleChar + Suppress("-") + _singleChar) - -_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" - - - -_expanded = lambda p: (isinstance(p,ParseResults) and ''.join([ unichr(c) for c in range(ord(p[0]),ord(p[1])+1) ]) or p) - - - -def srange(s): - - r"""Helper to easily define string ranges for use in Word construction. Borrows - - syntax from regexp '[]' string range definitions:: - - srange("[0-9]") -> "0123456789" - - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - - The input string must be enclosed in []'s, and the returned string is the expanded - - character set joined into a single string. - - The values enclosed in the []'s may be:: - - a single character - - an escaped character with a leading backslash (such as \- or \]) - - an escaped hex character with a leading '\0x' (\0x21, which is a '!' character) - - an escaped octal character with a leading '\0' (\041, which is a '!' character) - - a range of any of the above, separated by a dash ('a-z', etc.) - - any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) - - """ - - try: - - return "".join([_expanded(part) for part in _reBracketExpr.parseString(s).body]) - - except: - - return "" - - - -def matchOnlyAtCol(n): - - """Helper method for defining parse actions that require matching at a specific - - column in the input text. - - """ - - def verifyCol(strg,locn,toks): - - if col(locn,strg) != n: - - raise ParseException(strg,locn,"matched token not at column %d" % n) - - return verifyCol - - - -def replaceWith(replStr): - - """Helper method for common parse actions that simply return a literal value. Especially - - useful when used with transformString(). - - """ - - def _replFunc(*args): - - return [replStr] - - return _replFunc - - - -def removeQuotes(s,l,t): - - """Helper parse action for removing quotation marks from parsed quoted strings. - - To use, add this parse action to quoted string using:: - - quotedString.setParseAction( removeQuotes ) - - """ - - return t[0][1:-1] - - - -def upcaseTokens(s,l,t): - - """Helper parse action to convert tokens to upper case.""" - - return [ tt.upper() for tt in map(_ustr,t) ] - - - -def downcaseTokens(s,l,t): - - """Helper parse action to convert tokens to lower case.""" - - return [ tt.lower() for tt in map(_ustr,t) ] - - - -def keepOriginalText(s,startLoc,t): - - """Helper parse action to preserve original parsed text, - - overriding any nested parse actions.""" - - try: - - endloc = getTokensEndLoc() - - except ParseException: - - raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") - - del t[:] - - t += ParseResults(s[startLoc:endloc]) - - return t - - - -def getTokensEndLoc(): - - """Method to be called from within a parse action to determine the end - - location of the parsed tokens.""" - - import inspect - - fstack = inspect.stack() - - try: - - # search up the stack (through intervening argument normalizers) for correct calling routine - - for f in fstack[2:]: - - if f[3] == "_parseNoCache": - - endloc = f[0].f_locals["loc"] - - return endloc - - else: - - raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") - - finally: - - del fstack - - - -def _makeTags(tagStr, xml): - - """Internal helper to construct opening and closing tag expressions, given a tag name""" - - if isinstance(tagStr,basestring): - - resname = tagStr - - tagStr = Keyword(tagStr, caseless=not xml) - - else: - - resname = tagStr.name - - - - tagAttrName = Word(alphas,alphanums+"_-:") - - if (xml): - - tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) - - openTag = Suppress("<") + tagStr + \ - - Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ - - Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") - - else: - - printablesLessRAbrack = "".join( [ c for c in printables if c not in ">" ] ) - - tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) - - openTag = Suppress("<") + tagStr + \ - - Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ - - Optional( Suppress("=") + tagAttrValue ) ))) + \ - - Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") - - closeTag = Combine(_L("") - - - - openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) - - closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % tagStr) - - - - return openTag, closeTag - - - -def makeHTMLTags(tagStr): - - """Helper to construct opening and closing tag expressions for HTML, given a tag name""" - - return _makeTags( tagStr, False ) - - - -def makeXMLTags(tagStr): - - """Helper to construct opening and closing tag expressions for XML, given a tag name""" - - return _makeTags( tagStr, True ) - - - -def withAttribute(*args,**attrDict): - - """Helper to create a validating parse action to be used with start tags created - - with makeXMLTags or makeHTMLTags. Use withAttribute to qualify a starting tag - - with a required attribute value, to avoid false matches on common tags such as - - or
. - - - - Call withAttribute with a series of attribute names and values. Specify the list - - of filter attributes names and values as: - - - keyword arguments, as in (class="Customer",align="right"), or - - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - - For attribute names with a namespace prefix, you must use the second form. Attribute - - names are matched insensitive to upper/lower case. - - - - To verify that the attribute exists, but without specifying a value, pass - - withAttribute.ANY_VALUE as the value. - - """ - - if args: - - attrs = args[:] - - else: - - attrs = attrDict.items() - - attrs = [(k,v) for k,v in attrs] - - def pa(s,l,tokens): - - for attrName,attrValue in attrs: - - if attrName not in tokens: - - raise ParseException(s,l,"no matching attribute " + attrName) - - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - - (attrName, tokens[attrName], attrValue)) - - return pa - -withAttribute.ANY_VALUE = object() - - - -opAssoc = _Constants() - -opAssoc.LEFT = object() - -opAssoc.RIGHT = object() - - - -def operatorPrecedence( baseExpr, opList ): - - """Helper method for constructing grammars of expressions made up of - - operators working in a precedence hierarchy. Operators may be unary or - - binary, left- or right-associative. Parse actions can also be attached - - to operator expressions. - - - - Parameters: - - - baseExpr - expression representing the most basic element for the nested - - - opList - list of tuples, one for each operator precedence level in the - - expression grammar; each tuple is of the form - - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - - opExpr is the pyparsing expression for the operator; - - may also be a string, which will be converted to a Literal; - - if numTerms is 3, opExpr is a tuple of two expressions, for the - - two operators separating the 3 terms - - - numTerms is the number of terms for this operator (must - - be 1, 2, or 3) - - - rightLeftAssoc is the indicator whether the operator is - - right or left associative, using the pyparsing-defined - - constants opAssoc.RIGHT and opAssoc.LEFT. - - - parseAction is the parse action to be associated with - - expressions matching this operator expression (the - - parse action tuple member may be omitted) - - """ - - ret = Forward() - - lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') ) - - for i,operDef in enumerate(opList): - - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - - if arity == 3: - - if opExpr is None or len(opExpr) != 2: - - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - - opExpr1, opExpr2 = opExpr - - thisExpr = Forward()#.setName("expr%d" % i) - - if rightLeftAssoc == opAssoc.LEFT: - - if arity == 1: - - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - - elif arity == 2: - - if opExpr is not None: - - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - - else: - - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - - elif arity == 3: - - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - - else: - - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - elif rightLeftAssoc == opAssoc.RIGHT: - - if arity == 1: - - # try to avoid LR with this extra test - - if not isinstance(opExpr, Optional): - - opExpr = Optional(opExpr) - - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - - elif arity == 2: - - if opExpr is not None: - - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - - else: - - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - - elif arity == 3: - - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - - else: - - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - else: - - raise ValueError("operator must indicate right or left associativity") - - if pa: - - matchExpr.setParseAction( pa ) - - thisExpr << ( matchExpr | lastExpr ) - - lastExpr = thisExpr - - ret << lastExpr - - return ret - - - -dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") - -sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") - -quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") - -unicodeString = Combine(_L('u') + quotedString.copy()) - - - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString): - - """Helper method for defining nested lists enclosed in opening and closing - - delimiters ("(" and ")" are the default). - - - - Parameters: - - - opener - opening character for a nested list (default="("); can also be a pyparsing expression - - - closer - closing character for a nested list (default=")"); can also be a pyparsing expression - - - content - expression for items within the nested lists (default=None) - - - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) - - - - If an expression is not provided for the content argument, the nested - - expression will capture all whitespace-delimited content between delimiters - - as a list of separate values. - - - - Use the ignoreExpr argument to define expressions that may contain - - opening or closing characters that should not be treated as opening - - or closing characters for nesting, such as quotedString or a comment - - expression. Specify multiple expressions using an Or or MatchFirst. - - The default is quotedString, but if no expressions are to be ignored, - - then pass None for this argument. - - """ - - if opener == closer: - - raise ValueError("opening and closing strings cannot be the same") - - if content is None: - - if isinstance(opener,basestring) and isinstance(closer,basestring): - - if len(opener) == 1 and len(closer)==1: - - if ignoreExpr is not None: - - content = (Combine(OneOrMore(~ignoreExpr + - - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - - ).setParseAction(lambda t:t[0].strip())) - - else: - - content = (empty+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - - ).setParseAction(lambda t:t[0].strip())) - - else: - - if ignoreExpr is not None: - - content = (Combine(OneOrMore(~ignoreExpr + - - ~Literal(opener) + ~Literal(closer) + - - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - - ).setParseAction(lambda t:t[0].strip())) - - else: - - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - - ).setParseAction(lambda t:t[0].strip())) - - else: - - raise ValueError("opening and closing arguments must be strings if no content expression is given") - - ret = Forward() - - if ignoreExpr is not None: - - ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - - else: - - ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - - return ret - - - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - - """Helper method for defining space-delimited indentation blocks, such as - - those used to define block statements in Python source code. - - - - Parameters: - - - blockStatementExpr - expression defining syntax of statement that - - is repeated within the indented block - - - indentStack - list created by caller to manage indentation stack - - (multiple statementWithIndentedBlock expressions within a single grammar - - should share a common indentStack) - - - indent - boolean indicating whether block must be indented beyond the - - the current level; set to False for block of left-most statements - - (default=True) - - - - A valid block must contain at least one blockStatement. - - """ - - def checkPeerIndent(s,l,t): - - if l >= len(s): return - - curCol = col(l,s) - - if curCol != indentStack[-1]: - - if curCol > indentStack[-1]: - - raise ParseFatalException(s,l,"illegal nesting") - - raise ParseException(s,l,"not a peer entry") - - - - def checkSubIndent(s,l,t): - - curCol = col(l,s) - - if curCol > indentStack[-1]: - - indentStack.append( curCol ) - - else: - - raise ParseException(s,l,"not a subentry") - - - - def checkUnindent(s,l,t): - - if l >= len(s): return - - curCol = col(l,s) - - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - - raise ParseException(s,l,"not an unindent") - - indentStack.pop() - - - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - - INDENT = Empty() + Empty().setParseAction(checkSubIndent) - - PEER = Empty().setParseAction(checkPeerIndent) - - UNDENT = Empty().setParseAction(checkUnindent) - - if indent: - - smExpr = Group( Optional(NL) + - - FollowedBy(blockStatementExpr) + - - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - - else: - - smExpr = Group( Optional(NL) + - - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - - blockStatementExpr.ignore(_bslash + LineEnd()) - - return smExpr - - - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") - -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - - - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) - -commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() - -_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) - -replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None - - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available - -cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") - - - -htmlComment = Regex(r"") - -restOfLine = Regex(r".*").leaveWhitespace() - -dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") - -cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?" + str(tokenlist)) - - print ("tokens = " + str(tokens)) - - print ("tokens.columns = " + str(tokens.columns)) - - print ("tokens.tables = " + str(tokens.tables)) - - print (tokens.asXML("SQL",True)) - - except ParseBaseException,err: - - print (teststring + "->") - - print (err.line) - - print (" "*(err.column-1) + "^") - - print (err) - - print() - - - - selectToken = CaselessLiteral( "select" ) - - fromToken = CaselessLiteral( "from" ) - - - - ident = Word( alphas, alphanums + "_$" ) - - columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - - columnNameList = Group( delimitedList( columnName ) )#.setName("columns") - - tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - - tableNameList = Group( delimitedList( tableName ) )#.setName("tables") - - simpleSQL = ( selectToken + \ - - ( '*' | columnNameList ).setResultsName( "columns" ) + \ - - fromToken + \ - - tableNameList.setResultsName( "tables" ) ) - - - - test( "SELECT * from XYZZY, ABC" ) - - test( "select * from SYS.XYZZY" ) - - test( "Select A from Sys.dual" ) - - test( "Select AA,BB,CC from Sys.dual" ) - - test( "Select A, B, C from Sys.dual" ) - - test( "Select A, B, C from Sys.dual" ) - - test( "Xelect A, B, C from Sys.dual" ) - - test( "Select A, B, C frox Sys.dual" ) - - test( "Select" ) - - test( "Select ^^^ frox Sys.dual" ) - - test( "Select A, B, C from Sys.dual, Table2 " ) - +# module pyparsing.py +# +# Copyright (c) 2003-2009 Paul T. McGuire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +#from __future__ import generators + +__doc__ = \ +""" +pyparsing module - Classes and methods to define and execute parsing grammars + +The pyparsing module is an alternative approach to creating and executing simple grammars, +vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you +don't need to learn a new syntax for defining grammars or matching expressions - the parsing module +provides a library of classes that you use to construct the grammar directly in Python. + +Here is a program to parse "Hello, World!" (or any greeting of the form ", !"):: + + from pyparsing import Word, alphas + + # define grammar of a greeting + greet = Word( alphas ) + "," + Word( alphas ) + "!" + + hello = "Hello, World!" + print hello, "->", greet.parseString( hello ) + +The program outputs the following:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + +The Python representation of the grammar is quite readable, owing to the self-explanatory +class names, and the use of '+', '|' and '^' operators. + +The parsed results returned from parseString() can be accessed as a nested list, a dictionary, or an +object with named attributes. + +The pyparsing module handles some of the problems that are typically vexing when writing text parsers: + - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) + - quoted strings + - embedded comments +""" + +__version__ = "1.5.2" +__versionTime__ = "17 February 2009 19:45" +__author__ = "Paul McGuire " + +import string +from weakref import ref as wkref +import copy +import sys +import warnings +import re +import sre_constants +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums', +'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', +'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', +'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', +'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', +'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', +'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', +'indentedBlock', 'originalTextFor', +] + + +""" +Detect if we are running version 3.X and make appropriate changes +Robert A. Clark +""" +if sys.version_info[0] > 2: + _PY3K = True + _MAX_INT = sys.maxsize + basestring = str +else: + _PY3K = False + _MAX_INT = sys.maxint + +if not _PY3K: + def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj + + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) + + except UnicodeEncodeError: + # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) + # state that "The return value must be a string object". However, does a + # unicode object (being a subclass of basestring) count as a "string + # object"? + # If so, then return a unicode object: + return unicode(obj) + # Else encode it... but how? There are many choices... :) + # Replace unprintables with escape codes? + #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') + # Replace unprintables with question marks? + #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') + # ... +else: + _ustr = str + unichr = chr + +if not _PY3K: + def _str2dict(strg): + return dict( [(c,0) for c in strg] ) +else: + _str2dict = set + +def _xml_escape(data): + """Escape &, <, >, ", ', etc. in a string of data.""" + + # ampersand must be replaced first + from_symbols = '&><"\'' + to_symbols = ['&'+s+';' for s in "amp gt lt quot apos".split()] + for from_,to_ in zip(from_symbols, to_symbols): + data = data.replace(from_, to_) + return data + +class _Constants(object): + pass + +if not _PY3K: + alphas = string.lowercase + string.uppercase +else: + alphas = string.ascii_lowercase + string.ascii_uppercase +nums = string.digits +hexnums = nums + "ABCDEFabcdef" +alphanums = alphas + nums +_bslash = chr(92) +printables = "".join( [ c for c in string.printable if c not in string.whitespace ] ) + +class ParseBaseException(Exception): + """base exception class for all parsing runtime exceptions""" + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, pstr, loc=0, msg=None, elem=None ): + self.loc = loc + if msg is None: + self.msg = pstr + self.pstr = "" + else: + self.msg = msg + self.pstr = pstr + self.parserElement = elem + + def __getattr__( self, aname ): + """supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + if( aname == "lineno" ): + return lineno( self.loc, self.pstr ) + elif( aname in ("col", "column") ): + return col( self.loc, self.pstr ) + elif( aname == "line" ): + return line( self.loc, self.pstr ) + else: + raise AttributeError(aname) + + def __str__( self ): + return "%s (at char %d), (line:%d, col:%d)" % \ + ( self.msg, self.loc, self.lineno, self.column ) + def __repr__( self ): + return _ustr(self) + def markInputline( self, markerString = ">!<" ): + """Extracts the exception line from the input string, and marks + the location of the exception with a special symbol. + """ + line_str = self.line + line_column = self.column - 1 + if markerString: + line_str = "".join( [line_str[:line_column], + markerString, line_str[line_column:]]) + return line_str.strip() + def __dir__(self): + return "loc msg pstr parserElement lineno col line " \ + "markInputLine __str__ __repr__".split() + +class ParseException(ParseBaseException): + """exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like ParseFatalException, but thrown internally when an + ErrorStop indicates that parsing is to stop immediately because + an unbacktrackable syntax error has been found""" + def __init__(self, pe): + super(ParseSyntaxException, self).__init__( + pe.pstr, pe.loc, pe.msg, pe.parserElement) + +#~ class ReparseException(ParseBaseException): + #~ """Experimental class - parse actions can raise this exception to cause + #~ pyparsing to reparse the input string: + #~ - with a modified input string, and/or + #~ - with a modified start location + #~ Set the values of the ReparseException in the constructor, and raise the + #~ exception in a parse action to cause pyparsing to use the new string/location. + #~ Setting the values as None causes no change to be made. + #~ """ + #~ def __init_( self, newstring, restartLoc ): + #~ self.newParseText = newstring + #~ self.reparseLoc = restartLoc + +class RecursiveGrammarException(Exception): + """exception thrown by validate() if the grammar could be improperly recursive""" + def __init__( self, parseElementList ): + self.parseElementTrace = parseElementList + + def __str__( self ): + return "RecursiveGrammarException: %s" % self.parseElementTrace + +class _ParseResultsWithOffset(object): + def __init__(self,p1,p2): + self.tup = (p1,p2) + def __getitem__(self,i): + return self.tup[i] + def __repr__(self): + return repr(self.tup) + def setOffset(self,i): + self.tup = (self.tup[0],i) + +class ParseResults(object): + """Structured parse results, to provide multiple means of access to the parsed data: + - as a list (len(results)) + - by list index (results[0], results[1], etc.) + - by attribute (results.) + """ + __slots__ = ( "__toklist", "__tokdict", "__doinit", "__name", "__parent", "__accumNames", "__weakref__" ) + def __new__(cls, toklist, name=None, asList=True, modal=True ): + if isinstance(toklist, cls): + return toklist + retobj = object.__new__(cls) + retobj.__doinit = True + return retobj + + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, toklist, name=None, asList=True, modal=True ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + if isinstance(toklist, list): + self.__toklist = toklist[:] + else: + self.__toklist = [toklist] + self.__tokdict = dict() + + if name: + if not modal: + self.__accumNames[name] = 0 + if isinstance(name,int): + name = _ustr(name) # will always return a str, but use _ustr for consistency + self.__name = name + if not toklist in (None,'',[]): + if isinstance(toklist,basestring): + toklist = [ toklist ] + if asList: + if isinstance(toklist,ParseResults): + self[name] = _ParseResultsWithOffset(toklist.copy(),0) + else: + self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) + self[name].__name = name + else: + try: + self[name] = toklist[0] + except (KeyError,TypeError,IndexError): + self[name] = toklist + + def __getitem__( self, i ): + if isinstance( i, (int,slice) ): + return self.__toklist[i] + else: + if i not in self.__accumNames: + return self.__tokdict[i][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[i] ]) + + def __setitem__( self, k, v ): + if isinstance(v,_ParseResultsWithOffset): + self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] + sub = v[0] + elif isinstance(k,int): + self.__toklist[k] = v + sub = v + else: + self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] + sub = v + if isinstance(sub,ParseResults): + sub.__parent = wkref(self) + + def __delitem__( self, i ): + if isinstance(i,(int,slice)): + mylen = len( self.__toklist ) + del self.__toklist[i] + + # convert int to slice + if isinstance(i, int): + if i < 0: + i += mylen + i = slice(i, i+1) + # get removed indices + removed = list(range(*i.indices(mylen))) + removed.reverse() + # fixup indices in token dictionary + for name in self.__tokdict: + occurrences = self.__tokdict[name] + for j in removed: + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + else: + del self.__tokdict[i] + + def __contains__( self, k ): + return k in self.__tokdict + + def __len__( self ): return len( self.__toklist ) + def __bool__(self): return len( self.__toklist ) > 0 + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( reversed(self.__toklist) ) + def keys( self ): + """Returns all named result keys.""" + return self.__tokdict.keys() + + def pop( self, index=-1 ): + """Removes and returns item at specified index (default=last). + Will work with either numeric indices or dict-key indicies.""" + ret = self[index] + del self[index] + return ret + + def get(self, key, defaultValue=None): + """Returns named result matching the given key, or if there is no + such name, then returns the given defaultValue or None if no + defaultValue is specified.""" + if key in self: + return self[key] + else: + return defaultValue + + def insert( self, index, insStr ): + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + for name in self.__tokdict: + occurrences = self.__tokdict[name] + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + + def items( self ): + """Returns all named result keys and values as a list of tuples.""" + return [(k,self[k]) for k in self.__tokdict] + + def values( self ): + """Returns all named result values.""" + return [ v[-1][0] for v in self.__tokdict.values() ] + + def __getattr__( self, name ): + if name not in self.__slots__: + if name in self.__tokdict: + if name not in self.__accumNames: + return self.__tokdict[name][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[name] ]) + else: + return "" + return None + + def __add__( self, other ): + ret = self.copy() + ret += other + return ret + + def __iadd__( self, other ): + if other.__tokdict: + offset = len(self.__toklist) + addoffset = ( lambda a: (a<0 and offset) or (a+offset) ) + otheritems = other.__tokdict.items() + otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) + for (k,vlist) in otheritems for v in vlist] + for k,v in otherdictitems: + self[k] = v + if isinstance(v[0],ParseResults): + v[0].__parent = wkref(self) + + self.__toklist += other.__toklist + self.__accumNames.update( other.__accumNames ) + del other + return self + + def __repr__( self ): + return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) + + def __str__( self ): + out = "[" + sep = "" + for i in self.__toklist: + if isinstance(i, ParseResults): + out += sep + _ustr(i) + else: + out += sep + repr(i) + sep = ", " + out += "]" + return out + + def _asStringList( self, sep='' ): + out = [] + for item in self.__toklist: + if out and sep: + out.append(sep) + if isinstance( item, ParseResults ): + out += item._asStringList() + else: + out.append( _ustr(item) ) + return out + + def asList( self ): + """Returns the parse results as a nested list of matching tokens, all converted to strings.""" + out = [] + for res in self.__toklist: + if isinstance(res,ParseResults): + out.append( res.asList() ) + else: + out.append( res ) + return out + + def asDict( self ): + """Returns the named parse results as dictionary.""" + return dict( self.items() ) + + def copy( self ): + """Returns a new copy of a ParseResults object.""" + ret = ParseResults( self.__toklist ) + ret.__tokdict = self.__tokdict.copy() + ret.__parent = self.__parent + ret.__accumNames.update( self.__accumNames ) + ret.__name = self.__name + return ret + + def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): + """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" + nl = "\n" + out = [] + namedItems = dict( [ (v[1],k) for (k,vlist) in self.__tokdict.items() + for v in vlist ] ) + nextLevelIndent = indent + " " + + # collapse out indents if formatting is not desired + if not formatted: + indent = "" + nextLevelIndent = "" + nl = "" + + selfTag = None + if doctag is not None: + selfTag = doctag + else: + if self.__name: + selfTag = self.__name + + if not selfTag: + if namedItemsOnly: + return "" + else: + selfTag = "ITEM" + + out += [ nl, indent, "<", selfTag, ">" ] + + worklist = self.__toklist + for i,res in enumerate(worklist): + if isinstance(res,ParseResults): + if i in namedItems: + out += [ res.asXML(namedItems[i], + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + out += [ res.asXML(None, + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + # individual token, see if there is a name for it + resTag = None + if i in namedItems: + resTag = namedItems[i] + if not resTag: + if namedItemsOnly: + continue + else: + resTag = "ITEM" + xmlBodyText = _xml_escape(_ustr(res)) + out += [ nl, nextLevelIndent, "<", resTag, ">", + xmlBodyText, + "" ] + + out += [ nl, indent, "" ] + return "".join(out) + + def __lookup(self,sub): + for k,vlist in self.__tokdict.items(): + for v,loc in vlist: + if sub is v: + return k + return None + + def getName(self): + """Returns the results name for this token expression.""" + if self.__name: + return self.__name + elif self.__parent: + par = self.__parent() + if par: + return par.__lookup(self) + else: + return None + elif (len(self) == 1 and + len(self.__tokdict) == 1 and + self.__tokdict.values()[0][0][1] in (0,-1)): + return self.__tokdict.keys()[0] + else: + return None + + def dump(self,indent='',depth=0): + """Diagnostic method for listing out the contents of a ParseResults. + Accepts an optional indent argument so that this string can be embedded + in a nested display of other data.""" + out = [] + out.append( indent+_ustr(self.asList()) ) + keys = self.items() + keys.sort() + for k,v in keys: + if out: + out.append('\n') + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v.keys(): + #~ out.append('\n') + out.append( v.dump(indent,depth+1) ) + #~ out.append('\n') + else: + out.append(_ustr(v)) + else: + out.append(_ustr(v)) + #~ out.append('\n') + return "".join(out) + + # add support for pickle protocol + def __getstate__(self): + return ( self.__toklist, + ( self.__tokdict.copy(), + self.__parent is not None and self.__parent() or None, + self.__accumNames, + self.__name ) ) + + def __setstate__(self,state): + self.__toklist = state[0] + self.__tokdict, \ + par, \ + inAccumNames, \ + self.__name = state[1] + self.__accumNames = {} + self.__accumNames.update(inAccumNames) + if par is not None: + self.__parent = wkref(par) + else: + self.__parent = None + + def __dir__(self): + return dir(super(ParseResults,self)) + self.keys() + +def col (loc,strg): + """Returns current column within a string, counting newlines as line separators. + The first column is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}} for more information + on parsing strings containing s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return (loc} for more information + on parsing strings containing s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return strg.count("\n",0,loc) + 1 + +def line( loc, strg ): + """Returns the line of text containing loc within a string, counting newlines as line separators. + """ + lastCR = strg.rfind("\n", 0, loc) + nextCR = strg.find("\n", loc) + if nextCR > 0: + return strg[lastCR+1:nextCR] + else: + return strg[lastCR+1:] + +def _defaultStartDebugAction( instring, loc, expr ): + print ("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + +def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): + print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) + +def _defaultExceptionDebugAction( instring, loc, expr, exc ): + print ("Exception raised:" + _ustr(exc)) + +def nullDebugAction(*args): + """'Do-nothing' debug action, to suppress debugging output during parsing.""" + pass + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + + def setDefaultWhitespaceChars( chars ): + """Overrides the default whitespace chars + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars) + + def __init__( self, savelist=False ): + self.parseAction = list() + self.failAction = None + #~ self.name = "" # don't define self.name, let subclasses try/except upcall + self.strRepr = None + self.resultsName = None + self.saveAsList = savelist + self.skipWhitespace = True + self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + self.copyDefaultWhiteChars = True + self.mayReturnEmpty = False # used when checking for left-recursion + self.keepTabs = False + self.ignoreExprs = list() + self.debug = False + self.streamlined = False + self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index + self.errmsg = "" + self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) + self.debugActions = ( None, None, None ) #custom debug actions + self.re = None + self.callPreparse = True # used to avoid redundant calls to preParse + self.callDuringTry = False + + def copy( self ): + """Make a copy of this ParserElement. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element.""" + cpy = copy.copy( self ) + cpy.parseAction = self.parseAction[:] + cpy.ignoreExprs = self.ignoreExprs[:] + if self.copyDefaultWhiteChars: + cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + return cpy + + def setName( self, name ): + """Define name for this expression, for use in debugging.""" + self.name = name + self.errmsg = "Expected " + self.name + if hasattr(self,"exception"): + self.exception.msg = self.errmsg + return self + + def setResultsName( self, name, listAllMatches=False ): + """Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original ParserElement object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + """ + newself = self.copy() + newself.resultsName = name + newself.modalResults = not listAllMatches + return newself + + def setBreak(self,breakFlag = True): + """Method to invoke the Python pdb debugger when this element is + about to be parsed. Set breakFlag to True to enable, False to + disable. + """ + if breakFlag: + _parseMethod = self._parse + def breaker(instring, loc, doActions=True, callPreParse=True): + import pdb + pdb.set_trace() + return _parseMethod( instring, loc, doActions, callPreParse ) + breaker._originalParseMethod = _parseMethod + self._parse = breaker + else: + if hasattr(self._parse,"_originalParseMethod"): + self._parse = self._parse._originalParseMethod + return self + + def _normalizeParseActionArgs( f ): + """Internal method used to decorate parse actions that take fewer than 3 arguments, + so that all parse actions can be called as f(s,l,t).""" + STAR_ARGS = 4 + + try: + restore = None + if isinstance(f,type): + restore = f + f = f.__init__ + if not _PY3K: + codeObj = f.func_code + else: + codeObj = f.code + if codeObj.co_flags & STAR_ARGS: + return f + numargs = codeObj.co_argcount + if not _PY3K: + if hasattr(f,"im_self"): + numargs -= 1 + else: + if hasattr(f,"__self__"): + numargs -= 1 + if restore: + f = restore + except AttributeError: + try: + if not _PY3K: + call_im_func_code = f.__call__.im_func.func_code + else: + call_im_func_code = f.__code__ + + # not a function, must be a callable object, get info from the + # im_func binding of its bound __call__ method + if call_im_func_code.co_flags & STAR_ARGS: + return f + numargs = call_im_func_code.co_argcount + if not _PY3K: + if hasattr(f.__call__,"im_self"): + numargs -= 1 + else: + if hasattr(f.__call__,"__self__"): + numargs -= 0 + except AttributeError: + if not _PY3K: + call_func_code = f.__call__.func_code + else: + call_func_code = f.__call__.__code__ + # not a bound method, get info directly from __call__ method + if call_func_code.co_flags & STAR_ARGS: + return f + numargs = call_func_code.co_argcount + if not _PY3K: + if hasattr(f.__call__,"im_self"): + numargs -= 1 + else: + if hasattr(f.__call__,"__self__"): + numargs -= 1 + + + #~ print ("adding function %s with %d args" % (f.func_name,numargs)) + if numargs == 3: + return f + else: + if numargs > 3: + def tmp(s,l,t): + return f(f.__call__.__self__, s,l,t) + if numargs == 2: + def tmp(s,l,t): + return f(l,t) + elif numargs == 1: + def tmp(s,l,t): + return f(t) + else: #~ numargs == 0: + def tmp(s,l,t): + return f() + try: + tmp.__name__ = f.__name__ + except (AttributeError,TypeError): + # no need for special handling if attribute doesnt exist + pass + try: + tmp.__doc__ = f.__doc__ + except (AttributeError,TypeError): + # no need for special handling if attribute doesnt exist + pass + try: + tmp.__dict__.update(f.__dict__) + except (AttributeError,TypeError): + # no need for special handling if attribute doesnt exist + pass + return tmp + _normalizeParseActionArgs = staticmethod(_normalizeParseActionArgs) + + def setParseAction( self, *fns, **kwargs ): + """Define action to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as fn(s,loc,toks), + fn(loc,toks), fn(toks), or just fn(), where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a ParseResults object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}} for more information + on parsing strings containing s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + self.parseAction = list(map(self._normalizeParseActionArgs, list(fns))) + self.callDuringTry = ("callDuringTry" in kwargs and kwargs["callDuringTry"]) + return self + + def addParseAction( self, *fns, **kwargs ): + """Add parse action to expression's list of parse actions. See L{I{setParseAction}}.""" + self.parseAction += list(map(self._normalizeParseActionArgs, list(fns))) + self.callDuringTry = self.callDuringTry or ("callDuringTry" in kwargs and kwargs["callDuringTry"]) + return self + + def setFailAction( self, fn ): + """Define action to perform if parsing fails at this expression. + Fail acton fn is a callable function that takes the arguments + fn(s,loc,expr,err) where: + - s = string being parsed + - loc = location where expression match was attempted and failed + - expr = the parse expression that failed + - err = the exception thrown + The function returns no value. It may throw ParseFatalException + if it is desired to stop parsing immediately.""" + self.failAction = fn + return self + + def _skipIgnorables( self, instring, loc ): + exprsFound = True + while exprsFound: + exprsFound = False + for e in self.ignoreExprs: + try: + while 1: + loc,dummy = e._parse( instring, loc ) + exprsFound = True + except ParseException: + pass + return loc + + def preParse( self, instring, loc ): + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + + if self.skipWhitespace: + wt = self.whiteChars + instrlen = len(instring) + while loc < instrlen and instring[loc] in wt: + loc += 1 + + return loc + + def parseImpl( self, instring, loc, doActions=True ): + return loc, [] + + def postParse( self, instring, loc, tokenlist ): + return tokenlist + + #~ @profile + def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): + debugging = ( self.debug ) #and doActions ) + + if debugging or self.failAction: + #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + if (self.debugActions[0] ): + self.debugActions[0]( instring, loc, self ) + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = loc + try: + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + except ParseBaseException, err: + #~ print ("Exception raised:", err) + if self.debugActions[2]: + self.debugActions[2]( instring, tokensStart, self, err ) + if self.failAction: + self.failAction( instring, tokensStart, self, err ) + raise + else: + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = loc + if self.mayIndexError or loc >= len(instring): + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + else: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + + tokens = self.postParse( instring, loc, tokens ) + + retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) + if self.parseAction and (doActions or self.callDuringTry): + if debugging: + try: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + except ParseBaseException, err: + #~ print "Exception raised in user parse action:", err + if (self.debugActions[2] ): + self.debugActions[2]( instring, tokensStart, self, err ) + raise + else: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + + if debugging: + #~ print ("Matched",self,"->",retTokens.asList()) + if (self.debugActions[1] ): + self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) + + return loc, retTokens + + def tryParse( self, instring, loc ): + try: + return self._parse( instring, loc, doActions=False )[0] + except ParseFatalException: + raise ParseException( instring, loc, self.errmsg, self) + + # this method gets repeatedly called during backtracking with the same arguments - + # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression + def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): + lookup = (self,instring,loc,callPreParse,doActions) + if lookup in ParserElement._exprArgCache: + value = ParserElement._exprArgCache[ lookup ] + if isinstance(value,Exception): + raise value + return value + else: + try: + value = self._parseNoCache( instring, loc, doActions, callPreParse ) + ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) + return value + except ParseBaseException, pe: + ParserElement._exprArgCache[ lookup ] = pe + raise + + _parse = _parseNoCache + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + _exprArgCache = {} + def resetCache(): + ParserElement._exprArgCache.clear() + resetCache = staticmethod(resetCache) + + _packratEnabled = False + def enablePackrat(): + """Enables "packrat" parsing, which adds memoizing to the parsing logic. + Repeated parse attempts at the same string location (which happens + often in many complex grammars) can immediately return a cached value, + instead of re-executing parsing/validating code. Memoizing is done of + both valid results and parsing exceptions. + + This speedup may break existing programs that use parse actions that + have side-effects. For this reason, packrat parsing is disabled when + you first import pyparsing. To activate the packrat feature, your + program must call the class method ParserElement.enablePackrat(). If + your program uses psyco to "compile as you go", you must call + enablePackrat before calling psyco.full(). If you do not do this, + Python will crash. For best results, call enablePackrat() immediately + after importing pyparsing. + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + ParserElement._parse = ParserElement._parseCache + enablePackrat = staticmethod(enablePackrat) + + def parseString( self, instring, parseAll=False ): + """Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set parseAll to True (equivalent to ending + the grammar with StringEnd()). + + Note: parseString implicitly calls expandtabs() on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the loc argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling parseWithTabs on your grammar before calling parseString + (see L{I{parseWithTabs}}) + - define your parse action using the full (s,loc,toks) signature, and + reference the input string using the parse action's s argument + - explictly expand the tabs in your input string before calling + parseString + """ + ParserElement.resetCache() + if not self.streamlined: + self.streamline() + #~ self.saveAsList = True + for e in self.ignoreExprs: + e.streamline() + if not self.keepTabs: + instring = instring.expandtabs() + try: + loc, tokens = self._parse( instring, 0 ) + if parseAll: + loc = self.preParse( instring, loc ) + StringEnd()._parse( instring, loc ) + except ParseBaseException, exc: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + else: + return tokens + + def scanString( self, instring, maxMatches=_MAX_INT ): + """Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + maxMatches argument, to clip scanning after 'n' matches are found. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}} for more information on parsing + strings with embedded tabs.""" + if not self.streamlined: + self.streamline() + for e in self.ignoreExprs: + e.streamline() + + if not self.keepTabs: + instring = _ustr(instring).expandtabs() + instrlen = len(instring) + loc = 0 + preparseFn = self.preParse + parseFn = self._parse + ParserElement.resetCache() + matches = 0 + try: + while loc <= instrlen and matches < maxMatches: + try: + preloc = preparseFn( instring, loc ) + nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) + except ParseException: + loc = preloc+1 + else: + matches += 1 + yield tokens, preloc, nextLoc + loc = nextLoc + except ParseBaseException, pe: + raise pe + + def transformString( self, instring ): + """Extension to scanString, to modify matching text with modified tokens that may + be returned from a parse action. To use transformString, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking transformString() on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. transformString() returns the resulting transformed string.""" + out = [] + lastE = 0 + # force preservation of s, to minimize unwanted transformation of string, and to + # keep string locs straight between transformString and scanString + self.keepTabs = True + try: + for t,s,e in self.scanString( instring ): + out.append( instring[lastE:s] ) + if t: + if isinstance(t,ParseResults): + out += t.asList() + elif isinstance(t,list): + out += t + else: + out.append(t) + lastE = e + out.append(instring[lastE:]) + return "".join(map(_ustr,out)) + except ParseBaseException, pe: + raise pe + + def searchString( self, instring, maxMatches=_MAX_INT ): + """Another extension to scanString, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + maxMatches argument, to clip searching after 'n' matches are found. + """ + try: + return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) + except ParseBaseException, pe: + raise pe + + def __add__(self, other ): + """Implementation of + operator - returns And""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, other ] ) + + def __radd__(self, other ): + """Implementation of + operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other + self + + def __sub__(self, other): + """Implementation of - operator, returns And with error stop""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, And._ErrorStop(), other ] ) + + def __rsub__(self, other ): + """Implementation of - operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other - self + + def __mul__(self,other): + if isinstance(other,int): + minElements, optElements = other,0 + elif isinstance(other,tuple): + other = (other + (None, None))[:2] + if other[0] is None: + other = (0, other[1]) + if isinstance(other[0],int) and other[1] is None: + if other[0] == 0: + return ZeroOrMore(self) + if other[0] == 1: + return OneOrMore(self) + else: + return self*other[0] + ZeroOrMore(self) + elif isinstance(other[0],int) and isinstance(other[1],int): + minElements, optElements = other + optElements -= minElements + else: + raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) + else: + raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) + + if minElements < 0: + raise ValueError("cannot multiply ParserElement by negative value") + if optElements < 0: + raise ValueError("second tuple value must be greater or equal to first tuple value") + if minElements == optElements == 0: + raise ValueError("cannot multiply ParserElement by 0 or (0,0)") + + if (optElements): + def makeOptionalList(n): + if n>1: + return Optional(self + makeOptionalList(n-1)) + else: + return Optional(self) + if minElements: + if minElements == 1: + ret = self + makeOptionalList(optElements) + else: + ret = And([self]*minElements) + makeOptionalList(optElements) + else: + ret = makeOptionalList(optElements) + else: + if minElements == 1: + ret = self + else: + ret = And([self]*minElements) + return ret + + def __rmul__(self, other): + return self.__mul__(other) + + def __or__(self, other ): + """Implementation of | operator - returns MatchFirst""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return MatchFirst( [ self, other ] ) + + def __ror__(self, other ): + """Implementation of | operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other | self + + def __xor__(self, other ): + """Implementation of ^ operator - returns Or""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Or( [ self, other ] ) + + def __rxor__(self, other ): + """Implementation of ^ operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other ^ self + + def __and__(self, other ): + """Implementation of & operator - returns Each""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Each( [ self, other ] ) + + def __rand__(self, other ): + """Implementation of & operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other & self + + def __invert__( self ): + """Implementation of ~ operator - returns NotAny""" + return NotAny( self ) + + def __call__(self, name): + """Shortcut for setResultsName, with listAllMatches=default:: + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + could be written as:: + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + return self.setResultsName(name) + + def suppress( self ): + """Suppresses the output of this ParserElement; useful to keep punctuation from + cluttering up returned output. + """ + return Suppress( self ) + + def leaveWhitespace( self ): + """Disables the skipping of whitespace before matching the characters in the + ParserElement's defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + self.skipWhitespace = False + return self + + def setWhitespaceChars( self, chars ): + """Overrides the default whitespace chars + """ + self.skipWhitespace = True + self.whiteChars = chars + self.copyDefaultWhiteChars = False + return self + + def parseWithTabs( self ): + """Overrides default behavior to expand s to spaces before parsing the input string. + Must be called before parseString when the input grammar contains elements that + match characters.""" + self.keepTabs = True + return self + + def ignore( self, other ): + """Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + """ + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append( other ) + else: + self.ignoreExprs.append( Suppress( other ) ) + return self + + def setDebugActions( self, startAction, successAction, exceptionAction ): + """Enable display of debugging messages while doing pattern matching.""" + self.debugActions = (startAction or _defaultStartDebugAction, + successAction or _defaultSuccessDebugAction, + exceptionAction or _defaultExceptionDebugAction) + self.debug = True + return self + + def setDebug( self, flag=True ): + """Enable display of debugging messages while doing pattern matching. + Set flag to True to enable, False to disable.""" + if flag: + self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) + else: + self.debug = False + return self + + def __str__( self ): + return self.name + + def __repr__( self ): + return _ustr(self) + + def streamline( self ): + self.streamlined = True + self.strRepr = None + return self + + def checkRecursion( self, parseElementList ): + pass + + def validate( self, validateTrace=[] ): + """Check defined expressions for valid structure, check for infinite recursive definitions.""" + self.checkRecursion( [] ) + + def parseFile( self, file_or_filename, parseAll=False ): + """Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. + """ + try: + file_contents = file_or_filename.read() + except AttributeError: + f = open(file_or_filename, "rb") + file_contents = f.read() + f.close() + try: + return self.parseString(file_contents, parseAll) + except ParseBaseException, exc: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def getException(self): + return ParseException("",0,self.errmsg,self) + + def __getattr__(self,aname): + if aname == "myException": + self.myException = ret = self.getException(); + return ret; + else: + raise AttributeError("no such attribute " + aname) + + def __eq__(self,other): + if isinstance(other, ParserElement): + return self is other or self.__dict__ == other.__dict__ + elif isinstance(other, basestring): + try: + self.parseString(_ustr(other), parseAll=True) + return True + except ParseBaseException: + return False + else: + return super(ParserElement,self)==other + + def __ne__(self,other): + return not (self == other) + + def __hash__(self): + return hash(id(self)) + + def __req__(self,other): + return self == other + + def __rne__(self,other): + return not (self == other) + + +class Token(ParserElement): + """Abstract ParserElement subclass, for defining atomic matching patterns.""" + def __init__( self ): + super(Token,self).__init__( savelist=False ) + #self.myException = ParseException("",0,"",self) + + def setName(self, name): + s = super(Token,self).setName(name) + self.errmsg = "Expected " + self.name + #s.myException.msg = self.errmsg + return s + + +class Empty(Token): + """An empty token, will always match.""" + def __init__( self ): + super(Empty,self).__init__() + self.name = "Empty" + self.mayReturnEmpty = True + self.mayIndexError = False + + +class NoMatch(Token): + """A token that will never match.""" + def __init__( self ): + super(NoMatch,self).__init__() + self.name = "NoMatch" + self.mayReturnEmpty = True + self.mayIndexError = False + self.errmsg = "Unmatchable token" + #self.myException.msg = self.errmsg + + def parseImpl( self, instring, loc, doActions=True ): + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + +class Literal(Token): + """Token to exactly match a specified string.""" + def __init__( self, matchString ): + super(Literal,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Literal; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.__class__ = Empty + self.name = '"%s"' % _ustr(self.match) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + #self.myException.msg = self.errmsg + self.mayIndexError = False + + # Performance tuning: this routine gets called a *lot* + # if this is a single character match string and the first character matches, + # short-circuit as quickly as possible, and avoid calling startswith + #~ @profile + def parseImpl( self, instring, loc, doActions=True ): + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) ): + return loc+self.matchLen, self.match + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc +_L = Literal + +class Keyword(Token): + """Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with Literal:: + Literal("if") will match the leading 'if' in 'ifAndOnlyIf'. + Keyword("if") will not; it will only match the leading 'if in 'if x=1', or 'if(y==2)' + Accepts two optional constructor arguments in addition to the keyword string: + identChars is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$"; caseless allows case-insensitive + matching, default is False. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): + super(Keyword,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Keyword; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.name = '"%s"' % self.match + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.caseless = caseless + if caseless: + self.caselessmatch = matchString.upper() + identChars = identChars.upper() + self.identChars = _str2dict(identChars) + + def parseImpl( self, instring, loc, doActions=True ): + if self.caseless: + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and + (loc == 0 or instring[loc-1].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + else: + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and + (loc == 0 or instring[loc-1] not in self.identChars) ): + return loc+self.matchLen, self.match + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + def copy(self): + c = super(Keyword,self).copy() + c.identChars = Keyword.DEFAULT_KEYWORD_CHARS + return c + + def setDefaultKeywordChars( chars ): + """Overrides the default Keyword chars + """ + Keyword.DEFAULT_KEYWORD_CHARS = chars + setDefaultKeywordChars = staticmethod(setDefaultKeywordChars) + +class CaselessLiteral(Literal): + """Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + """ + def __init__( self, matchString ): + super(CaselessLiteral,self).__init__( matchString.upper() ) + # Preserve the defining literal. + self.returnString = matchString + self.name = "'%s'" % self.returnString + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + + def parseImpl( self, instring, loc, doActions=True ): + if instring[ loc:loc+self.matchLen ].upper() == self.match: + return loc+self.matchLen, self.returnString + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class CaselessKeyword(Keyword): + def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + + def parseImpl( self, instring, loc, doActions=True ): + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class Word(Token): + """Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for min is 1 (a + minimum value < 1 is not valid); the default values for max and exact + are 0, meaning no maximum or exact length restriction. + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False ): + super(Word,self).__init__() + self.initCharsOrig = initChars + self.initChars = _str2dict(initChars) + if bodyChars : + self.bodyCharsOrig = bodyChars + self.bodyChars = _str2dict(bodyChars) + else: + self.bodyCharsOrig = initChars + self.bodyChars = _str2dict(initChars) + + self.maxSpecified = max > 0 + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.asKeyword = asKeyword + + if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): + if self.bodyCharsOrig == self.initCharsOrig: + self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) + elif len(self.bodyCharsOrig) == 1: + self.reString = "%s[%s]*" % \ + (re.escape(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + else: + self.reString = "[%s][%s]*" % \ + (_escapeRegexRangeChars(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + if self.asKeyword: + self.reString = r"\b"+self.reString+r"\b" + try: + self.re = re.compile( self.reString ) + except: + self.re = None + + def parseImpl( self, instring, loc, doActions=True ): + if self.re: + result = self.re.match(instring,loc) + if not result: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + loc = result.end() + return loc,result.group() + + if not(instring[ loc ] in self.initChars): + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + start = loc + loc += 1 + instrlen = len(instring) + bodychars = self.bodyChars + maxloc = start + self.maxLen + maxloc = min( maxloc, instrlen ) + while loc < maxloc and instring[loc] in bodychars: + loc += 1 + + throwException = False + if loc - start < self.minLen: + throwException = True + if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + if self.asKeyword: + if (start>0 and instring[start-1] in bodychars) or (loc4: + return s[:4]+"..." + else: + return s + + if ( self.initCharsOrig != self.bodyCharsOrig ): + self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) + else: + self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) + + return self.strRepr + + +class Regex(Token): + """Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + """ + def __init__( self, pattern, flags=0): + """The parameters pattern and flags are passed to the re.compile() function as-is. See the Python re module for an explanation of the acceptable patterns and flags.""" + super(Regex,self).__init__() + + if len(pattern) == 0: + warnings.warn("null string passed to Regex; use Empty() instead", + SyntaxWarning, stacklevel=2) + + self.pattern = pattern + self.flags = flags + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = self.re.match(instring,loc) + if not result: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + loc = result.end() + d = result.groupdict() + ret = ParseResults(result.group()) + if d: + for k in d: + ret[k] = d[k] + return loc,ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + """Token for matching strings that are delimited by quoting characters. + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): + """ + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=None) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) + - multiline - boolean indicating whether quotes can span multiple lines (default=False) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar) + """ + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if len(quoteChar) == 0: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if len(endQuoteChar) == 0: + warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + self.quoteChar = quoteChar + self.quoteCharLen = len(quoteChar) + self.firstQuoteChar = quoteChar[0] + self.endQuoteChar = endQuoteChar + self.endQuoteCharLen = len(endQuoteChar) + self.escChar = escChar + self.escQuote = escQuote + self.unquoteResults = unquoteResults + + if multiline: + self.flags = re.MULTILINE | re.DOTALL + self.pattern = r'%s(?:[^%s%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + else: + self.flags = 0 + self.pattern = r'%s(?:[^%s\n\r%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + if len(self.endQuoteChar) > 1: + self.pattern += ( + '|(?:' + ')|(?:'.join(["%s[^%s]" % (re.escape(self.endQuoteChar[:i]), + _escapeRegexRangeChars(self.endQuoteChar[i])) + for i in range(len(self.endQuoteChar)-1,0,-1)]) + ')' + ) + if escQuote: + self.pattern += (r'|(?:%s)' % re.escape(escQuote)) + if escChar: + self.pattern += (r'|(?:%s.)' % re.escape(escChar)) + self.escCharReplacePattern = re.escape(self.escChar)+"(.)" + self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None + if not result: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + loc = result.end() + ret = result.group() + + if self.unquoteResults: + + # strip off quotes + ret = ret[self.quoteCharLen:-self.endQuoteCharLen] + + if isinstance(ret,basestring): + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) + + # replace escaped quotes + if self.escQuote: + ret = ret.replace(self.escQuote, self.endQuoteChar) + + return loc, ret + + def __str__( self ): + try: + return super(QuotedString,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) + + return self.strRepr + + +class CharsNotIn(Token): + """Token for matching words composed of characters *not* in a given set. + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for min is 1 (a + minimum value < 1 is not valid); the default values for max and exact + are 0, meaning no maximum or exact length restriction. + """ + def __init__( self, notChars, min=1, max=0, exact=0 ): + super(CharsNotIn,self).__init__() + self.skipWhitespace = False + self.notChars = notChars + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = ( self.minLen == 0 ) + #self.myException.msg = self.errmsg + self.mayIndexError = False + + def parseImpl( self, instring, loc, doActions=True ): + if instring[loc] in self.notChars: + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + start = loc + loc += 1 + notchars = self.notChars + maxlen = min( start+self.maxLen, len(instring) ) + while loc < maxlen and \ + (instring[loc] not in notchars): + loc += 1 + + if loc - start < self.minLen: + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(CharsNotIn, self).__str__() + except: + pass + + if self.strRepr is None: + if len(self.notChars) > 4: + self.strRepr = "!W:(%s...)" % self.notChars[:4] + else: + self.strRepr = "!W:(%s)" % self.notChars + + return self.strRepr + +class White(Token): + """Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is " \\t\\r\\n". Also takes optional min, max, and exact arguments, + as defined for the Word class.""" + whiteStrs = { + " " : "", + "\t": "", + "\n": "", + "\r": "", + "\f": "", + } + def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): + super(White,self).__init__() + self.matchWhite = ws + self.setWhitespaceChars( "".join([c for c in self.whiteChars if c not in self.matchWhite]) ) + #~ self.leaveWhitespace() + self.name = ("".join([White.whiteStrs[c] for c in self.matchWhite])) + self.mayReturnEmpty = True + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + def parseImpl( self, instring, loc, doActions=True ): + if not(instring[ loc ] in self.matchWhite): + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + start = loc + loc += 1 + maxloc = start + self.maxLen + maxloc = min( maxloc, len(instring) ) + while loc < maxloc and instring[loc] in self.matchWhite: + loc += 1 + + if loc - start < self.minLen: + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + return loc, instring[start:loc] + + +class _PositionToken(Token): + def __init__( self ): + super(_PositionToken,self).__init__() + self.name=self.__class__.__name__ + self.mayReturnEmpty = True + self.mayIndexError = False + +class GoToColumn(_PositionToken): + """Token to advance to a specific column of input text; useful for tabular report scraping.""" + def __init__( self, colno ): + super(GoToColumn,self).__init__() + self.col = colno + + def preParse( self, instring, loc ): + if col(loc,instring) != self.col: + instrlen = len(instring) + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + thiscol = col( loc, instring ) + if thiscol > self.col: + raise ParseException( instring, loc, "Text not in expected column", self ) + newloc = loc + self.col - thiscol + ret = instring[ loc: newloc ] + return newloc, ret + +class LineStart(_PositionToken): + """Matches if current position is at the beginning of a line within the parse string""" + def __init__( self ): + super(LineStart,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected start of line" + #self.myException.msg = self.errmsg + + def preParse( self, instring, loc ): + preloc = super(LineStart,self).preParse(instring,loc) + if instring[preloc] == "\n": + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + if not( loc==0 or + (loc == self.preParse( instring, 0 )) or + (instring[loc-1] == "\n") ): #col(loc, instring) != 1: + #~ raise ParseException( instring, loc, "Expected start of line" ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + return loc, [] + +class LineEnd(_PositionToken): + """Matches if current position is at the end of a line within the parse string""" + def __init__( self ): + super(LineEnd,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected end of line" + #self.myException.msg = self.errmsg + + def parseImpl( self, instring, loc, doActions=True ): + if loc len(instring): + return loc, [] + else: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class WordStart(_PositionToken): + """Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of wordChars + (default=printables). To emulate the \b behavior of regular expressions, + use WordStart(alphanums). WordStart will also match at the beginning of + the string being parsed, or at the beginning of a line. + """ + def __init__(self, wordChars = printables): + super(WordStart,self).__init__() + self.wordChars = _str2dict(wordChars) + self.errmsg = "Not at the start of a word" + + def parseImpl(self, instring, loc, doActions=True ): + if loc != 0: + if (instring[loc-1] in self.wordChars or + instring[loc] not in self.wordChars): + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + return loc, [] + +class WordEnd(_PositionToken): + """Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of wordChars + (default=printables). To emulate the \b behavior of regular expressions, + use WordEnd(alphanums). WordEnd will also match at the end of + the string being parsed, or at the end of a line. + """ + def __init__(self, wordChars = printables): + super(WordEnd,self).__init__() + self.wordChars = _str2dict(wordChars) + self.skipWhitespace = False + self.errmsg = "Not at the end of a word" + + def parseImpl(self, instring, loc, doActions=True ): + instrlen = len(instring) + if instrlen>0 and loc maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + else: + if loc2 > maxMatchLoc: + maxMatchLoc = loc2 + maxMatchExp = e + + if maxMatchLoc < 0: + if maxException is not None: + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + return maxMatchExp._parse( instring, loc, doActions ) + + def __ixor__(self, other ): + if isinstance( other, basestring ): + other = Literal( other ) + return self.append( other ) #Or( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ^ ".join( [ _ustr(e) for e in self.exprs ] ) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class MatchFirst(ParseExpression): + """Requires that at least one ParseExpression is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the '|' operator. + """ + def __init__( self, exprs, savelist = False ): + super(MatchFirst,self).__init__(exprs, savelist) + if exprs: + self.mayReturnEmpty = False + for e in self.exprs: + if e.mayReturnEmpty: + self.mayReturnEmpty = True + break + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + for e in self.exprs: + try: + ret = e._parse( instring, loc, doActions ) + return ret + except ParseException, err: + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + + # only got here if no expression matched, raise exception for match that made it the furthest + else: + if maxException is not None: + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + def __ior__(self, other ): + if isinstance( other, basestring ): + other = Literal( other ) + return self.append( other ) #MatchFirst( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " | ".join( [ _ustr(e) for e in self.exprs ] ) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class Each(ParseExpression): + """Requires all given ParseExpressions to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the '&' operator. + """ + def __init__( self, exprs, savelist = True ): + super(Each,self).__init__(exprs, savelist) + self.mayReturnEmpty = True + for e in self.exprs: + if not e.mayReturnEmpty: + self.mayReturnEmpty = False + break + self.skipWhitespace = True + self.initExprGroups = True + + def parseImpl( self, instring, loc, doActions=True ): + if self.initExprGroups: + self.optionals = [ e.expr for e in self.exprs if isinstance(e,Optional) ] + self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] + self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] + self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] + self.required += self.multirequired + self.initExprGroups = False + tmpLoc = loc + tmpReqd = self.required[:] + tmpOpt = self.optionals[:] + matchOrder = [] + + keepMatching = True + while keepMatching: + tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired + failed = [] + for e in tmpExprs: + try: + tmpLoc = e.tryParse( instring, tmpLoc ) + except ParseException: + failed.append(e) + else: + matchOrder.append(e) + if e in tmpReqd: + tmpReqd.remove(e) + elif e in tmpOpt: + tmpOpt.remove(e) + if len(failed) == len(tmpExprs): + keepMatching = False + + if tmpReqd: + missing = ", ".join( [ _ustr(e) for e in tmpReqd ] ) + raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) + + # add any unmatched Optionals, in case they have default values defined + matchOrder += list(e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt) + + resultlist = [] + for e in matchOrder: + loc,results = e._parse(instring,loc,doActions) + resultlist.append(results) + + finalResults = ParseResults([]) + for r in resultlist: + dups = {} + for k in r.keys(): + if k in finalResults.keys(): + tmp = ParseResults(finalResults[k]) + tmp += ParseResults(r[k]) + dups[k] = tmp + finalResults += ParseResults(r) + for k,v in dups.items(): + finalResults[k] = v + return loc, finalResults + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " & ".join( [ _ustr(e) for e in self.exprs ] ) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class ParseElementEnhance(ParserElement): + """Abstract subclass of ParserElement, for combining and post-processing parsed tokens.""" + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + expr = Literal(expr) + self.expr = expr + self.strRepr = None + if expr is not None: + self.mayIndexError = expr.mayIndexError + self.mayReturnEmpty = expr.mayReturnEmpty + self.setWhitespaceChars( expr.whiteChars ) + self.skipWhitespace = expr.skipWhitespace + self.saveAsList = expr.saveAsList + self.callPreparse = expr.callPreparse + self.ignoreExprs.extend(expr.ignoreExprs) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr is not None: + return self.expr._parse( instring, loc, doActions, callPreParse=False ) + else: + raise ParseException("",loc,self.errmsg,self) + + def leaveWhitespace( self ): + self.skipWhitespace = False + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + else: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + return self + + def streamline( self ): + super(ParseElementEnhance,self).streamline() + if self.expr is not None: + self.expr.streamline() + return self + + def checkRecursion( self, parseElementList ): + if self in parseElementList: + raise RecursiveGrammarException( parseElementList+[self] ) + subRecCheckList = parseElementList[:] + [ self ] + if self.expr is not None: + self.expr.checkRecursion( subRecCheckList ) + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion( [] ) + + def __str__( self ): + try: + return super(ParseElementEnhance,self).__str__() + except: + pass + + if self.strRepr is None and self.expr is not None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) + return self.strRepr + + +class FollowedBy(ParseElementEnhance): + """Lookahead matching of the given parse expression. FollowedBy + does *not* advance the parsing position within the input string, it only + verifies that the specified parse expression matches at the current + position. FollowedBy always returns a null token list.""" + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + self.expr.tryParse( instring, loc ) + return loc, [] + + +class NotAny(ParseElementEnhance): + """Lookahead to disallow matching with the given parse expression. NotAny + does *not* advance the parsing position within the input string, it only + verifies that the specified parse expression does *not* match at the current + position. Also, NotAny does *not* skip over leading whitespace. NotAny + always returns a null token list. May be constructed using the '~' operator.""" + def __init__( self, expr ): + super(NotAny,self).__init__(expr) + #~ self.leaveWhitespace() + self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs + self.mayReturnEmpty = True + self.errmsg = "Found unwanted token, "+_ustr(self.expr) + #self.myException = ParseException("",0,self.errmsg,self) + + def parseImpl( self, instring, loc, doActions=True ): + try: + self.expr.tryParse( instring, loc ) + except (ParseException,IndexError): + pass + else: + #~ raise ParseException(instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "~{" + _ustr(self.expr) + "}" + + return self.strRepr + + +class ZeroOrMore(ParseElementEnhance): + """Optional repetition of zero or more of the given expression.""" + def __init__( self, expr ): + super(ZeroOrMore,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + tokens = [] + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + while 1: + if hasIgnoreExprs: + preloc = self._skipIgnorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + if tmptokens or tmptokens.keys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]..." + + return self.strRepr + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) + ret.saveAsList = True + return ret + + +class OneOrMore(ParseElementEnhance): + """Repetition of one or more of the given expression.""" + def parseImpl( self, instring, loc, doActions=True ): + # must be at least one + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + while 1: + if hasIgnoreExprs: + preloc = self._skipIgnorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + if tmptokens or tmptokens.keys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + _ustr(self.expr) + "}..." + + return self.strRepr + + def setResultsName( self, name, listAllMatches=False ): + ret = super(OneOrMore,self).setResultsName(name,listAllMatches) + ret.saveAsList = True + return ret + +class _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """Optional matching of the given expression. + A default return string can also be specified, if the optional expression + is not found. + """ + def __init__( self, exprs, default=_optionalNotMatched ): + super(Optional,self).__init__( exprs, savelist=False ) + self.defaultValue = default + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + except (ParseException,IndexError): + if self.defaultValue is not _optionalNotMatched: + if self.expr.resultsName: + tokens = ParseResults([ self.defaultValue ]) + tokens[self.expr.resultsName] = self.defaultValue + else: + tokens = [ self.defaultValue ] + else: + tokens = [] + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]" + + return self.strRepr + + +class SkipTo(ParseElementEnhance): + """Token for skipping over all undefined text until the matched expression is found. + If include is set to true, the matched expression is also parsed (the skipped text + and matched expression are returned as a 2-element list). The ignore + argument is used to define grammars (typically quoted strings and comments) that + might contain false matches. + """ + def __init__( self, other, include=False, ignore=None, failOn=None ): + super( SkipTo, self ).__init__( other ) + self.ignoreExpr = ignore + self.mayReturnEmpty = True + self.mayIndexError = False + self.includeMatch = include + self.asList = False + if failOn is not None and isinstance(failOn, basestring): + self.failOn = Literal(failOn) + else: + self.failOn = failOn + self.errmsg = "No match found for "+_ustr(self.expr) + #self.myException = ParseException("",0,self.errmsg,self) + + def parseImpl( self, instring, loc, doActions=True ): + startLoc = loc + instrlen = len(instring) + expr = self.expr + failParse = False + while loc <= instrlen: + try: + if self.failOn: + try: + self.failOn.tryParse(instring, loc) + except ParseBaseException: + pass + else: + failParse = True + raise ParseException(instring, loc, "Found expression " + str(self.failOn)) + failParse = False + if self.ignoreExpr is not None: + while 1: + try: + loc = self.ignoreExpr.tryParse(instring,loc) + print "found ignoreExpr, advance to", loc + except ParseBaseException: + break + expr._parse( instring, loc, doActions=False, callPreParse=False ) + skipText = instring[startLoc:loc] + if self.includeMatch: + loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) + if mat: + skipRes = ParseResults( skipText ) + skipRes += mat + return loc, [ skipRes ] + else: + return loc, [ skipText ] + else: + return loc, [ skipText ] + except (ParseException,IndexError): + if failParse: + raise + else: + loc += 1 + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class Forward(ParseElementEnhance): + """Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the Forward variable using the '<<' operator. + + Note: take care when assigning to Forward not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the Forward:: + fwdExpr << (a | b | c) + """ + def __init__( self, other=None ): + super(Forward,self).__init__( other, savelist=False ) + + def __lshift__( self, other ): + if isinstance( other, basestring ): + other = Literal(other) + self.expr = other + self.mayReturnEmpty = other.mayReturnEmpty + self.strRepr = None + self.mayIndexError = self.expr.mayIndexError + self.mayReturnEmpty = self.expr.mayReturnEmpty + self.setWhitespaceChars( self.expr.whiteChars ) + self.skipWhitespace = self.expr.skipWhitespace + self.saveAsList = self.expr.saveAsList + self.ignoreExprs.extend(self.expr.ignoreExprs) + return None + + def leaveWhitespace( self ): + self.skipWhitespace = False + return self + + def streamline( self ): + if not self.streamlined: + self.streamlined = True + if self.expr is not None: + self.expr.streamline() + return self + + def validate( self, validateTrace=[] ): + if self not in validateTrace: + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion([]) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + self._revertClass = self.__class__ + self.__class__ = _ForwardNoRecurse + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + self.__class__ = self._revertClass + return self.__class__.__name__ + ": " + retString + + def copy(self): + if self.expr is not None: + return super(Forward,self).copy() + else: + ret = Forward() + ret << self + return ret + +class _ForwardNoRecurse(Forward): + def __str__( self ): + return "..." + +class TokenConverter(ParseElementEnhance): + """Abstract subclass of ParseExpression, for converting parsed results.""" + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +class Upcase(TokenConverter): + """Converter to upper case all matching tokens.""" + def __init__(self, *args): + super(Upcase,self).__init__(*args) + warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", + DeprecationWarning,stacklevel=2) + + def postParse( self, instring, loc, tokenlist ): + return list(map( string.upper, tokenlist )) + + +class Combine(TokenConverter): + """Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying 'adjacent=False' in the constructor. + """ + def __init__( self, expr, joinString="", adjacent=True ): + super(Combine,self).__init__( expr ) + # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself + if adjacent: + self.leaveWhitespace() + self.adjacent = adjacent + self.skipWhitespace = True + self.joinString = joinString + + def ignore( self, other ): + if self.adjacent: + ParserElement.ignore(self, other) + else: + super( Combine, self).ignore( other ) + return self + + def postParse( self, instring, loc, tokenlist ): + retToks = tokenlist.copy() + del retToks[:] + retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) + + if self.resultsName and len(retToks.keys())>0: + return [ retToks ] + else: + return retToks + +class Group(TokenConverter): + """Converter to return the matched tokens as a list - useful for returning tokens of ZeroOrMore and OneOrMore expressions.""" + def __init__( self, expr ): + super(Group,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + return [ tokenlist ] + +class Dict(TokenConverter): + """Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + """ + def __init__( self, exprs ): + super(Dict,self).__init__( exprs ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + for i,tok in enumerate(tokenlist): + if len(tok) == 0: + continue + ikey = tok[0] + if isinstance(ikey,int): + ikey = _ustr(tok[0]).strip() + if len(tok)==1: + tokenlist[ikey] = _ParseResultsWithOffset("",i) + elif len(tok)==2 and not isinstance(tok[1],ParseResults): + tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) + else: + dictvalue = tok.copy() #ParseResults(i) + del dictvalue[0] + if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.keys()): + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) + else: + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) + + if self.resultsName: + return [ tokenlist ] + else: + return tokenlist + + +class Suppress(TokenConverter): + """Converter for ignoring the results of a parsed expression.""" + def postParse( self, instring, loc, tokenlist ): + return [] + + def suppress( self ): + return self + + +class OnlyOnce(object): + """Wrapper for parse actions, to ensure they are only called once.""" + def __init__(self, methodCall): + self.callable = ParserElement._normalizeParseActionArgs(methodCall) + self.called = False + def __call__(self,s,l,t): + if not self.called: + results = self.callable(s,l,t) + self.called = True + return results + raise ParseException(s,l,"") + def reset(self): + self.called = False + +def traceParseAction(f): + """Decorator for debugging parse actions.""" + f = ParserElement._normalizeParseActionArgs(f) + def z(*paArgs): + thisFunc = f.func_name + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) + try: + ret = f(*paArgs) + except Exception, exc: + sys.stderr.write( "<", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) + try: + if len(symbols)==len("".join(symbols)): + return Regex( "[%s]" % "".join( [ _escapeRegexRangeChars(sym) for sym in symbols] ) ) + else: + return Regex( "|".join( [ re.escape(sym) for sym in symbols] ) ) + except: + warnings.warn("Exception creating Regex for oneOf, building MatchFirst", + SyntaxWarning, stacklevel=2) + + + # last resort, just use MatchFirst + return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) + +def dictOf( key, value ): + """Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the Dict, ZeroOrMore, and Group tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the Dict results can include named token + fields. + """ + return Dict( ZeroOrMore( Group ( key + value ) ) ) + +def originalTextFor(expr, asString=True): + """Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. Simpler to use than the parse action keepOriginalText, and does not + require the inspect module to chase up the call stack. By default, returns a + string containing the original parsed text. + + If the optional asString argument is passed as False, then the return value is a + ParseResults containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to originalTextFor contains expressions with defined + results names, you must set asString to False if you want to preserve those + results name values.""" + locMarker = Empty().setParseAction(lambda s,loc,t: loc) + matchExpr = locMarker("_original_start") + expr + locMarker("_original_end") + if asString: + extractText = lambda s,l,t: s[t._original_start:t._original_end] + else: + def extractText(s,l,t): + del t[:] + t.insert(0, s[t._original_start:t._original_end]) + del t["_original_start"] + del t["_original_end"] + matchExpr.setParseAction(extractText) + return matchExpr + +# convenience constants for positional expressions +empty = Empty().setName("empty") +lineStart = LineStart().setName("lineStart") +lineEnd = LineEnd().setName("lineEnd") +stringStart = StringStart().setName("stringStart") +stringEnd = StringEnd().setName("stringEnd") + +_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) +_printables_less_backslash = "".join([ c for c in printables if c not in r"\]" ]) +_escapedHexChar = Combine( Suppress(_bslash + "0x") + Word(hexnums) ).setParseAction(lambda s,l,t:unichr(int(t[0],16))) +_escapedOctChar = Combine( Suppress(_bslash) + Word("0","01234567") ).setParseAction(lambda s,l,t:unichr(int(t[0],8))) +_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(_printables_less_backslash,exact=1) +_charRange = Group(_singleChar + Suppress("-") + _singleChar) +_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" + +_expanded = lambda p: (isinstance(p,ParseResults) and ''.join([ unichr(c) for c in range(ord(p[0]),ord(p[1])+1) ]) or p) + +def srange(s): + r"""Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be:: + a single character + an escaped character with a leading backslash (such as \- or \]) + an escaped hex character with a leading '\0x' (\0x21, which is a '!' character) + an escaped octal character with a leading '\0' (\041, which is a '!' character) + a range of any of the above, separated by a dash ('a-z', etc.) + any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) + """ + try: + return "".join([_expanded(part) for part in _reBracketExpr.parseString(s).body]) + except: + return "" + +def matchOnlyAtCol(n): + """Helper method for defining parse actions that require matching at a specific + column in the input text. + """ + def verifyCol(strg,locn,toks): + if col(locn,strg) != n: + raise ParseException(strg,locn,"matched token not at column %d" % n) + return verifyCol + +def replaceWith(replStr): + """Helper method for common parse actions that simply return a literal value. Especially + useful when used with transformString(). + """ + def _replFunc(*args): + return [replStr] + return _replFunc + +def removeQuotes(s,l,t): + """Helper parse action for removing quotation marks from parsed quoted strings. + To use, add this parse action to quoted string using:: + quotedString.setParseAction( removeQuotes ) + """ + return t[0][1:-1] + +def upcaseTokens(s,l,t): + """Helper parse action to convert tokens to upper case.""" + return [ tt.upper() for tt in map(_ustr,t) ] + +def downcaseTokens(s,l,t): + """Helper parse action to convert tokens to lower case.""" + return [ tt.lower() for tt in map(_ustr,t) ] + +def keepOriginalText(s,startLoc,t): + """Helper parse action to preserve original parsed text, + overriding any nested parse actions.""" + try: + endloc = getTokensEndLoc() + except ParseException: + raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") + del t[:] + t += ParseResults(s[startLoc:endloc]) + return t + +def getTokensEndLoc(): + """Method to be called from within a parse action to determine the end + location of the parsed tokens.""" + import inspect + fstack = inspect.stack() + try: + # search up the stack (through intervening argument normalizers) for correct calling routine + for f in fstack[2:]: + if f[3] == "_parseNoCache": + endloc = f[0].f_locals["loc"] + return endloc + else: + raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") + finally: + del fstack + +def _makeTags(tagStr, xml): + """Internal helper to construct opening and closing tag expressions, given a tag name""" + if isinstance(tagStr,basestring): + resname = tagStr + tagStr = Keyword(tagStr, caseless=not xml) + else: + resname = tagStr.name + + tagAttrName = Word(alphas,alphanums+"_-:") + if (xml): + tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) + openTag = Suppress("<") + tagStr + \ + Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + else: + printablesLessRAbrack = "".join( [ c for c in printables if c not in ">" ] ) + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) + openTag = Suppress("<") + tagStr + \ + Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ + Optional( Suppress("=") + tagAttrValue ) ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + closeTag = Combine(_L("") + + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % tagStr) + + return openTag, closeTag + +def makeHTMLTags(tagStr): + """Helper to construct opening and closing tag expressions for HTML, given a tag name""" + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """Helper to construct opening and closing tag expressions for XML, given a tag name""" + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """Helper to create a validating parse action to be used with start tags created + with makeXMLTags or makeHTMLTags. Use withAttribute to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + or
. + + Call withAttribute with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in (class="Customer",align="right"), or + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + To verify that the attribute exists, but without specifying a value, pass + withAttribute.ANY_VALUE as the value. + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def operatorPrecedence( baseExpr, opList ): + """Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants opAssoc.RIGHT and opAssoc.LEFT. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted) + """ + ret = Forward() + lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward()#.setName("expr%d" % i) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + matchExpr.setParseAction( pa ) + thisExpr << ( matchExpr | lastExpr ) + lastExpr = thisExpr + ret << lastExpr + return ret + +dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") +sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") +quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()) + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString): + """Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default="("); can also be a pyparsing expression + - closer - closing character for a nested list (default=")"); can also be a pyparsing expression + - content - expression for items within the nested lists (default=None) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the ignoreExpr argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an Or or MatchFirst. + The default is quotedString, but if no expressions are to be ignored, + then pass None for this argument. + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=True) + + A valid block must contain at least one blockStatement. + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = Empty() + Empty().setParseAction(checkSubIndent) + PEER = Empty().setParseAction(checkPeerIndent) + UNDENT = Empty().setParseAction(checkUnindent) + if indent: + smExpr = Group( Optional(NL) + + FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) +commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() +_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) +replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") + +htmlComment = Regex(r"") +restOfLine = Regex(r".*").leaveWhitespace() +dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") +cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?" + str(tokenlist)) + print ("tokens = " + str(tokens)) + print ("tokens.columns = " + str(tokens.columns)) + print ("tokens.tables = " + str(tokens.tables)) + print (tokens.asXML("SQL",True)) + except ParseBaseException,err: + print (teststring + "->") + print (err.line) + print (" "*(err.column-1) + "^") + print (err) + print() + + selectToken = CaselessLiteral( "select" ) + fromToken = CaselessLiteral( "from" ) + + ident = Word( alphas, alphanums + "_$" ) + columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) + columnNameList = Group( delimitedList( columnName ) )#.setName("columns") + tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) + tableNameList = Group( delimitedList( tableName ) )#.setName("tables") + simpleSQL = ( selectToken + \ + ( '*' | columnNameList ).setResultsName( "columns" ) + \ + fromToken + \ + tableNameList.setResultsName( "tables" ) ) + + test( "SELECT * from XYZZY, ABC" ) + test( "select * from SYS.XYZZY" ) + test( "Select A from Sys.dual" ) + test( "Select AA,BB,CC from Sys.dual" ) + test( "Select A, B, C from Sys.dual" ) + test( "Select A, B, C from Sys.dual" ) + test( "Xelect A, B, C from Sys.dual" ) + test( "Select A, B, C frox Sys.dual" ) + test( "Select" ) + test( "Select ^^^ frox Sys.dual" ) + test( "Select A, B, C from Sys.dual, Table2 " ) diff --git a/python/src/com/jetbrains/python/sdk/pyparsing_py3.py b/python/src/com/jetbrains/python/sdk/pyparsing_py3.py index 73537c96c2de..a427ac8247e0 100644 --- a/python/src/com/jetbrains/python/sdk/pyparsing_py3.py +++ b/python/src/com/jetbrains/python/sdk/pyparsing_py3.py @@ -1,7432 +1,3716 @@ -# module pyparsing.py - -# - -# Copyright (c) 2003-2009 Paul T. McGuire - -# - -# Permission is hereby granted, free of charge, to any person obtaining - -# a copy of this software and associated documentation files (the - -# "Software"), to deal in the Software without restriction, including - -# without limitation the rights to use, copy, modify, merge, publish, - -# distribute, sublicense, and/or sell copies of the Software, and to - -# permit persons to whom the Software is furnished to do so, subject to - -# the following conditions: - -# - -# The above copyright notice and this permission notice shall be - -# included in all copies or substantial portions of the Software. - -# - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# - -#from __future__ import generators - - - -__doc__ = \ - -""" - -pyparsing module - Classes and methods to define and execute parsing grammars - - - -The pyparsing module is an alternative approach to creating and executing simple grammars, - -vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you - -don't need to learn a new syntax for defining grammars or matching expressions - the parsing module - -provides a library of classes that you use to construct the grammar directly in Python. - - - -Here is a program to parse "Hello, World!" (or any greeting of the form ", !"):: - - - - from pyparsing_py3 import Word, alphas - - - - # define grammar of a greeting - - greet = Word( alphas ) + "," + Word( alphas ) + "!" - - - - hello = "Hello, World!" - - print hello, "->", greet.parseString( hello ) - - - -The program outputs the following:: - - - - Hello, World! -> ['Hello', ',', 'World', '!'] - - - -The Python representation of the grammar is quite readable, owing to the self-explanatory - -class names, and the use of '+', '|' and '^' operators. - - - -The parsed results returned from parseString() can be accessed as a nested list, a dictionary, or an - -object with named attributes. - - - -The pyparsing module handles some of the problems that are typically vexing when writing text parsers: - - - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) - - - quoted strings - - - embedded comments - -""" - - - -__version__ = "1.5.2.Py3" - -__versionTime__ = "9 April 2009 12:21" - -__author__ = "Paul McGuire " - - - -import string - -from weakref import ref as wkref - -import copy - -import sys - -import warnings - -import re - -import sre_constants - -#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) - - - -__all__ = [ - -'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', - -'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', - -'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', - -'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', - -'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', - -'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', - -'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', - -'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', - -'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', - -'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums', - -'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', - -'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', - -'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', - -'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', - -'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', - -'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', - -'indentedBlock', 'originalTextFor', - -] - - - -""" - -Detect if we are running version 3.X and make appropriate changes - -Robert A. Clark - -""" - -_PY3K = sys.version_info[0] > 2 - -if _PY3K: - - _MAX_INT = sys.maxsize - - basestring = str - - unichr = chr - - _ustr = str - - _str2dict = set - - alphas = string.ascii_lowercase + string.ascii_uppercase - -else: - - _MAX_INT = sys.maxint - - - - def _ustr(obj): - - """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries - - str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It - - then < returns the unicode object | encodes it with the default encoding | ... >. - - """ - - if isinstance(obj,unicode): - - return obj - - - - try: - - # If this works, then _ustr(obj) has the same behaviour as str(obj), so - - # it won't break any existing code. - - return str(obj) - - - - except UnicodeEncodeError: - - # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) - - # state that "The return value must be a string object". However, does a - - # unicode object (being a subclass of basestring) count as a "string - - # object"? - - # If so, then return a unicode object: - - return unicode(obj) - - # Else encode it... but how? There are many choices... :) - - # Replace unprintables with escape codes? - - #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') - - # Replace unprintables with question marks? - - #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') - - # ... - - - - def _str2dict(strg): - - return dict( [(c,0) for c in strg] ) - - - - alphas = string.lowercase + string.uppercase - - - - - -def _xml_escape(data): - - """Escape &, <, >, ", ', etc. in a string of data.""" - - - - # ampersand must be replaced first - - from_symbols = '&><"\'' - - to_symbols = ['&'+s+';' for s in "amp gt lt quot apos".split()] - - for from_,to_ in zip(from_symbols, to_symbols): - - data = data.replace(from_, to_) - - return data - - - -class _Constants(object): - - pass - - - -nums = string.digits - -hexnums = nums + "ABCDEFabcdef" - -alphanums = alphas + nums - -_bslash = chr(92) - -printables = "".join( [ c for c in string.printable if c not in string.whitespace ] ) - - - -class ParseBaseException(Exception): - - """base exception class for all parsing runtime exceptions""" - - # Performance tuning: we construct a *lot* of these, so keep this - - # constructor as small and fast as possible - - def __init__( self, pstr, loc=0, msg=None, elem=None ): - - self.loc = loc - - if msg is None: - - self.msg = pstr - - self.pstr = "" - - else: - - self.msg = msg - - self.pstr = pstr - - self.parserElement = elem - - - - def __getattr__( self, aname ): - - """supported attributes by name are: - - - lineno - returns the line number of the exception text - - - col - returns the column number of the exception text - - - line - returns the line containing the exception text - - """ - - if( aname == "lineno" ): - - return lineno( self.loc, self.pstr ) - - elif( aname in ("col", "column") ): - - return col( self.loc, self.pstr ) - - elif( aname == "line" ): - - return line( self.loc, self.pstr ) - - else: - - raise AttributeError(aname) - - - - def __str__( self ): - - return "%s (at char %d), (line:%d, col:%d)" % \ - - ( self.msg, self.loc, self.lineno, self.column ) - - def __repr__( self ): - - return _ustr(self) - - def markInputline( self, markerString = ">!<" ): - - """Extracts the exception line from the input string, and marks - - the location of the exception with a special symbol. - - """ - - line_str = self.line - - line_column = self.column - 1 - - if markerString: - - line_str = "".join( [line_str[:line_column], - - markerString, line_str[line_column:]]) - - return line_str.strip() - - def __dir__(self): - - return "loc msg pstr parserElement lineno col line " \ - - "markInputLine __str__ __repr__".split() - - - -class ParseException(ParseBaseException): - - """exception thrown when parse expressions don't match class; - - supported attributes by name are: - - - lineno - returns the line number of the exception text - - - col - returns the column number of the exception text - - - line - returns the line containing the exception text - - """ - - pass - - - -class ParseFatalException(ParseBaseException): - - """user-throwable exception thrown when inconsistent parse content - - is found; stops all parsing immediately""" - - pass - - - -class ParseSyntaxException(ParseFatalException): - - """just like ParseFatalException, but thrown internally when an - - ErrorStop indicates that parsing is to stop immediately because - - an unbacktrackable syntax error has been found""" - - def __init__(self, pe): - - super(ParseSyntaxException, self).__init__( - - pe.pstr, pe.loc, pe.msg, pe.parserElement) - - - -#~ class ReparseException(ParseBaseException): - - #~ """Experimental class - parse actions can raise this exception to cause - - #~ pyparsing to reparse the input string: - - #~ - with a modified input string, and/or - - #~ - with a modified start location - - #~ Set the values of the ReparseException in the constructor, and raise the - - #~ exception in a parse action to cause pyparsing to use the new string/location. - - #~ Setting the values as None causes no change to be made. - - #~ """ - - #~ def __init_( self, newstring, restartLoc ): - - #~ self.newParseText = newstring - - #~ self.reparseLoc = restartLoc - - - -class RecursiveGrammarException(Exception): - - """exception thrown by validate() if the grammar could be improperly recursive""" - - def __init__( self, parseElementList ): - - self.parseElementTrace = parseElementList - - - - def __str__( self ): - - return "RecursiveGrammarException: %s" % self.parseElementTrace - - - -class _ParseResultsWithOffset(object): - - def __init__(self,p1,p2): - - self.tup = (p1,p2) - - def __getitem__(self,i): - - return self.tup[i] - - def __repr__(self): - - return repr(self.tup) - - def setOffset(self,i): - - self.tup = (self.tup[0],i) - - - -class ParseResults(object): - - """Structured parse results, to provide multiple means of access to the parsed data: - - - as a list (len(results)) - - - by list index (results[0], results[1], etc.) - - - by attribute (results.) - - """ - - __slots__ = ( "__toklist", "__tokdict", "__doinit", "__name", "__parent", "__accumNames", "__weakref__" ) - - def __new__(cls, toklist, name=None, asList=True, modal=True ): - - if isinstance(toklist, cls): - - return toklist - - retobj = object.__new__(cls) - - retobj.__doinit = True - - return retobj - - - - # Performance tuning: we construct a *lot* of these, so keep this - - # constructor as small and fast as possible - - def __init__( self, toklist, name=None, asList=True, modal=True ): - - if self.__doinit: - - self.__doinit = False - - self.__name = None - - self.__parent = None - - self.__accumNames = {} - - if isinstance(toklist, list): - - self.__toklist = toklist[:] - - else: - - self.__toklist = [toklist] - - self.__tokdict = dict() - - - - if name: - - if not modal: - - self.__accumNames[name] = 0 - - if isinstance(name,int): - - name = _ustr(name) # will always return a str, but use _ustr for consistency - - self.__name = name - - if not toklist in (None,'',[]): - - if isinstance(toklist,basestring): - - toklist = [ toklist ] - - if asList: - - if isinstance(toklist,ParseResults): - - self[name] = _ParseResultsWithOffset(toklist.copy(),0) - - else: - - self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) - - self[name].__name = name - - else: - - try: - - self[name] = toklist[0] - - except (KeyError,TypeError,IndexError): - - self[name] = toklist - - - - def __getitem__( self, i ): - - if isinstance( i, (int,slice) ): - - return self.__toklist[i] - - else: - - if i not in self.__accumNames: - - return self.__tokdict[i][-1][0] - - else: - - return ParseResults([ v[0] for v in self.__tokdict[i] ]) - - - - def __setitem__( self, k, v ): - - if isinstance(v,_ParseResultsWithOffset): - - self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] - - sub = v[0] - - elif isinstance(k,int): - - self.__toklist[k] = v - - sub = v - - else: - - self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] - - sub = v - - if isinstance(sub,ParseResults): - - sub.__parent = wkref(self) - - - - def __delitem__( self, i ): - - if isinstance(i,(int,slice)): - - mylen = len( self.__toklist ) - - del self.__toklist[i] - - - - # convert int to slice - - if isinstance(i, int): - - if i < 0: - - i += mylen - - i = slice(i, i+1) - - # get removed indices - - removed = list(range(*i.indices(mylen))) - - removed.reverse() - - # fixup indices in token dictionary - - for name in self.__tokdict: - - occurrences = self.__tokdict[name] - - for j in removed: - - for k, (value, position) in enumerate(occurrences): - - occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) - - else: - - del self.__tokdict[i] - - - - def __contains__( self, k ): - - return k in self.__tokdict - - - - def __len__( self ): return len( self.__toklist ) - - def __bool__(self): return len( self.__toklist ) > 0 - - __nonzero__ = __bool__ - - def __iter__( self ): return iter( self.__toklist ) - - def __reversed__( self ): return iter( reversed(self.__toklist) ) - - def keys( self ): - - """Returns all named result keys.""" - - return self.__tokdict.keys() - - - - def pop( self, index=-1 ): - - """Removes and returns item at specified index (default=last). - - Will work with either numeric indices or dict-key indicies.""" - - ret = self[index] - - del self[index] - - return ret - - - - def get(self, key, defaultValue=None): - - """Returns named result matching the given key, or if there is no - - such name, then returns the given defaultValue or None if no - - defaultValue is specified.""" - - if key in self: - - return self[key] - - else: - - return defaultValue - - - - def insert( self, index, insStr ): - - self.__toklist.insert(index, insStr) - - # fixup indices in token dictionary - - for name in self.__tokdict: - - occurrences = self.__tokdict[name] - - for k, (value, position) in enumerate(occurrences): - - occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) - - - - def items( self ): - - """Returns all named result keys and values as a list of tuples.""" - - return [(k,self[k]) for k in self.__tokdict] - - - - def values( self ): - - """Returns all named result values.""" - - return [ v[-1][0] for v in self.__tokdict.values() ] - - - - def __getattr__( self, name ): - - if name not in self.__slots__: - - if name in self.__tokdict: - - if name not in self.__accumNames: - - return self.__tokdict[name][-1][0] - - else: - - return ParseResults([ v[0] for v in self.__tokdict[name] ]) - - else: - - return "" - - return None - - - - def __add__( self, other ): - - ret = self.copy() - - ret += other - - return ret - - - - def __iadd__( self, other ): - - if other.__tokdict: - - offset = len(self.__toklist) - - addoffset = ( lambda a: (a<0 and offset) or (a+offset) ) - - otheritems = other.__tokdict.items() - - otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) - - for (k,vlist) in otheritems for v in vlist] - - for k,v in otherdictitems: - - self[k] = v - - if isinstance(v[0],ParseResults): - - v[0].__parent = wkref(self) - - - - self.__toklist += other.__toklist - - self.__accumNames.update( other.__accumNames ) - - del other - - return self - - - - def __repr__( self ): - - return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) - - - - def __str__( self ): - - out = "[" - - sep = "" - - for i in self.__toklist: - - if isinstance(i, ParseResults): - - out += sep + _ustr(i) - - else: - - out += sep + repr(i) - - sep = ", " - - out += "]" - - return out - - - - def _asStringList( self, sep='' ): - - out = [] - - for item in self.__toklist: - - if out and sep: - - out.append(sep) - - if isinstance( item, ParseResults ): - - out += item._asStringList() - - else: - - out.append( _ustr(item) ) - - return out - - - - def asList( self ): - - """Returns the parse results as a nested list of matching tokens, all converted to strings.""" - - out = [] - - for res in self.__toklist: - - if isinstance(res,ParseResults): - - out.append( res.asList() ) - - else: - - out.append( res ) - - return out - - - - def asDict( self ): - - """Returns the named parse results as dictionary.""" - - return dict( self.items() ) - - - - def copy( self ): - - """Returns a new copy of a ParseResults object.""" - - ret = ParseResults( self.__toklist ) - - ret.__tokdict = self.__tokdict.copy() - - ret.__parent = self.__parent - - ret.__accumNames.update( self.__accumNames ) - - ret.__name = self.__name - - return ret - - - - def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): - - """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" - - nl = "\n" - - out = [] - - namedItems = dict( [ (v[1],k) for (k,vlist) in self.__tokdict.items() - - for v in vlist ] ) - - nextLevelIndent = indent + " " - - - - # collapse out indents if formatting is not desired - - if not formatted: - - indent = "" - - nextLevelIndent = "" - - nl = "" - - - - selfTag = None - - if doctag is not None: - - selfTag = doctag - - else: - - if self.__name: - - selfTag = self.__name - - - - if not selfTag: - - if namedItemsOnly: - - return "" - - else: - - selfTag = "ITEM" - - - - out += [ nl, indent, "<", selfTag, ">" ] - - - - worklist = self.__toklist - - for i,res in enumerate(worklist): - - if isinstance(res,ParseResults): - - if i in namedItems: - - out += [ res.asXML(namedItems[i], - - namedItemsOnly and doctag is None, - - nextLevelIndent, - - formatted)] - - else: - - out += [ res.asXML(None, - - namedItemsOnly and doctag is None, - - nextLevelIndent, - - formatted)] - - else: - - # individual token, see if there is a name for it - - resTag = None - - if i in namedItems: - - resTag = namedItems[i] - - if not resTag: - - if namedItemsOnly: - - continue - - else: - - resTag = "ITEM" - - xmlBodyText = _xml_escape(_ustr(res)) - - out += [ nl, nextLevelIndent, "<", resTag, ">", - - xmlBodyText, - - "" ] - - - - out += [ nl, indent, "" ] - - return "".join(out) - - - - def __lookup(self,sub): - - for k,vlist in self.__tokdict.items(): - - for v,loc in vlist: - - if sub is v: - - return k - - return None - - - - def getName(self): - - """Returns the results name for this token expression.""" - - if self.__name: - - return self.__name - - elif self.__parent: - - par = self.__parent() - - if par: - - return par.__lookup(self) - - else: - - return None - - elif (len(self) == 1 and - - len(self.__tokdict) == 1 and - - self.__tokdict.values()[0][0][1] in (0,-1)): - - return self.__tokdict.keys()[0] - - else: - - return None - - - - def dump(self,indent='',depth=0): - - """Diagnostic method for listing out the contents of a ParseResults. - - Accepts an optional indent argument so that this string can be embedded - - in a nested display of other data.""" - - out = [] - - out.append( indent+_ustr(self.asList()) ) - - keys = self.items() - - keys.sort() - - for k,v in keys: - - if out: - - out.append('\n') - - out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) - - if isinstance(v,ParseResults): - - if v.keys(): - - out.append( v.dump(indent,depth+1) ) - - else: - - out.append(_ustr(v)) - - else: - - out.append(_ustr(v)) - - return "".join(out) - - - - # add support for pickle protocol - - def __getstate__(self): - - return ( self.__toklist, - - ( self.__tokdict.copy(), - - self.__parent is not None and self.__parent() or None, - - self.__accumNames, - - self.__name ) ) - - - - def __setstate__(self,state): - - self.__toklist = state[0] - - self.__tokdict, \ - - par, \ - - inAccumNames, \ - - self.__name = state[1] - - self.__accumNames = {} - - self.__accumNames.update(inAccumNames) - - if par is not None: - - self.__parent = wkref(par) - - else: - - self.__parent = None - - - - def __dir__(self): - - return dir(super(ParseResults,self)) + self.keys() - - - -def col (loc,strg): - - """Returns current column within a string, counting newlines as line separators. - - The first column is number 1. - - - - Note: the default parsing behavior is to expand tabs in the input string - - before starting the parsing process. See L{I{ParserElement.parseString}} for more information - - on parsing strings containing s, and suggested methods to maintain a - - consistent view of the parsed string, the parse location, and line and column - - positions within the parsed string. - - """ - - return (loc} for more information - - on parsing strings containing s, and suggested methods to maintain a - - consistent view of the parsed string, the parse location, and line and column - - positions within the parsed string. - - """ - - return strg.count("\n",0,loc) + 1 - - - -def line( loc, strg ): - - """Returns the line of text containing loc within a string, counting newlines as line separators. - - """ - - lastCR = strg.rfind("\n", 0, loc) - - nextCR = strg.find("\n", loc) - - if nextCR > 0: - - return strg[lastCR+1:nextCR] - - else: - - return strg[lastCR+1:] - - - -def _defaultStartDebugAction( instring, loc, expr ): - - print ("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) - - - -def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): - - print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) - - - -def _defaultExceptionDebugAction( instring, loc, expr, exc ): - - print ("Exception raised:" + _ustr(exc)) - - - -def nullDebugAction(*args): - - """'Do-nothing' debug action, to suppress debugging output during parsing.""" - - pass - - - -class ParserElement(object): - - """Abstract base level parser element class.""" - - DEFAULT_WHITE_CHARS = " \n\t\r" - - - - def setDefaultWhitespaceChars( chars ): - - """Overrides the default whitespace chars - - """ - - ParserElement.DEFAULT_WHITE_CHARS = chars - - setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars) - - - - def __init__( self, savelist=False ): - - self.parseAction = list() - - self.failAction = None - - #~ self.name = "" # don't define self.name, let subclasses try/except upcall - - self.strRepr = None - - self.resultsName = None - - self.saveAsList = savelist - - self.skipWhitespace = True - - self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS - - self.copyDefaultWhiteChars = True - - self.mayReturnEmpty = False # used when checking for left-recursion - - self.keepTabs = False - - self.ignoreExprs = list() - - self.debug = False - - self.streamlined = False - - self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index - - self.errmsg = "" - - self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) - - self.debugActions = ( None, None, None ) #custom debug actions - - self.re = None - - self.callPreparse = True # used to avoid redundant calls to preParse - - self.callDuringTry = False - - - - def copy( self ): - - """Make a copy of this ParserElement. Useful for defining different parse actions - - for the same parsing pattern, using copies of the original parse element.""" - - cpy = copy.copy( self ) - - cpy.parseAction = self.parseAction[:] - - cpy.ignoreExprs = self.ignoreExprs[:] - - if self.copyDefaultWhiteChars: - - cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS - - return cpy - - - - def setName( self, name ): - - """Define name for this expression, for use in debugging.""" - - self.name = name - - self.errmsg = "Expected " + self.name - - if hasattr(self,"exception"): - - self.exception.msg = self.errmsg - - return self - - - - def setResultsName( self, name, listAllMatches=False ): - - """Define name for referencing matching tokens as a nested attribute - - of the returned parse results. - - NOTE: this returns a *copy* of the original ParserElement object; - - this is so that the client can define a basic element, such as an - - integer, and reference it in multiple places with different names. - - """ - - newself = self.copy() - - newself.resultsName = name - - newself.modalResults = not listAllMatches - - return newself - - - - def setBreak(self,breakFlag = True): - - """Method to invoke the Python pdb debugger when this element is - - about to be parsed. Set breakFlag to True to enable, False to - - disable. - - """ - - if breakFlag: - - _parseMethod = self._parse - - def breaker(instring, loc, doActions=True, callPreParse=True): - - import pdb - - pdb.set_trace() - - return _parseMethod( instring, loc, doActions, callPreParse ) - - breaker._originalParseMethod = _parseMethod - - self._parse = breaker - - else: - - if hasattr(self._parse,"_originalParseMethod"): - - self._parse = self._parse._originalParseMethod - - return self - - - - def _normalizeParseActionArgs( f ): - - """Internal method used to decorate parse actions that take fewer than 3 arguments, - - so that all parse actions can be called as f(s,l,t).""" - - STAR_ARGS = 4 - - - - try: - - restore = None - - if isinstance(f,type): - - restore = f - - f = f.__init__ - - if not _PY3K: - - codeObj = f.func_code - - else: - - codeObj = f.code - - if codeObj.co_flags & STAR_ARGS: - - return f - - numargs = codeObj.co_argcount - - if not _PY3K: - - if hasattr(f,"im_self"): - - numargs -= 1 - - else: - - if hasattr(f,"__self__"): - - numargs -= 1 - - if restore: - - f = restore - - except AttributeError: - - try: - - if not _PY3K: - - call_im_func_code = f.__call__.im_func.func_code - - else: - - call_im_func_code = f.__code__ - - - - # not a function, must be a callable object, get info from the - - # im_func binding of its bound __call__ method - - if call_im_func_code.co_flags & STAR_ARGS: - - return f - - numargs = call_im_func_code.co_argcount - - if not _PY3K: - - if hasattr(f.__call__,"im_self"): - - numargs -= 1 - - else: - - if hasattr(f.__call__,"__self__"): - - numargs -= 0 - - except AttributeError: - - if not _PY3K: - - call_func_code = f.__call__.func_code - - else: - - call_func_code = f.__call__.__code__ - - # not a bound method, get info directly from __call__ method - - if call_func_code.co_flags & STAR_ARGS: - - return f - - numargs = call_func_code.co_argcount - - if not _PY3K: - - if hasattr(f.__call__,"im_self"): - - numargs -= 1 - - else: - - if hasattr(f.__call__,"__self__"): - - numargs -= 1 - - - - - - #~ print ("adding function %s with %d args" % (f.func_name,numargs)) - - if numargs == 3: - - return f - - else: - - if numargs > 3: - - def tmp(s,l,t): - - return f(f.__call__.__self__, s,l,t) - - if numargs == 2: - - def tmp(s,l,t): - - return f(l,t) - - elif numargs == 1: - - def tmp(s,l,t): - - return f(t) - - else: #~ numargs == 0: - - def tmp(s,l,t): - - return f() - - try: - - tmp.__name__ = f.__name__ - - except (AttributeError,TypeError): - - # no need for special handling if attribute doesnt exist - - pass - - try: - - tmp.__doc__ = f.__doc__ - - except (AttributeError,TypeError): - - # no need for special handling if attribute doesnt exist - - pass - - try: - - tmp.__dict__.update(f.__dict__) - - except (AttributeError,TypeError): - - # no need for special handling if attribute doesnt exist - - pass - - return tmp - - _normalizeParseActionArgs = staticmethod(_normalizeParseActionArgs) - - - - def setParseAction( self, *fns, **kwargs ): - - """Define action to perform when successfully matching parse element definition. - - Parse action fn is a callable method with 0-3 arguments, called as fn(s,loc,toks), - - fn(loc,toks), fn(toks), or just fn(), where: - - - s = the original string being parsed (see note below) - - - loc = the location of the matching substring - - - toks = a list of the matched tokens, packaged as a ParseResults object - - If the functions in fns modify the tokens, they can return them as the return - - value from fn, and the modified list of tokens will replace the original. - - Otherwise, fn does not need to return any value. - - - - Note: the default parsing behavior is to expand tabs in the input string - - before starting the parsing process. See L{I{parseString}} for more information - - on parsing strings containing s, and suggested methods to maintain a - - consistent view of the parsed string, the parse location, and line and column - - positions within the parsed string. - - """ - - self.parseAction = list(map(self._normalizeParseActionArgs, list(fns))) - - self.callDuringTry = ("callDuringTry" in kwargs and kwargs["callDuringTry"]) - - return self - - - - def addParseAction( self, *fns, **kwargs ): - - """Add parse action to expression's list of parse actions. See L{I{setParseAction}}.""" - - self.parseAction += list(map(self._normalizeParseActionArgs, list(fns))) - - self.callDuringTry = self.callDuringTry or ("callDuringTry" in kwargs and kwargs["callDuringTry"]) - - return self - - - - def setFailAction( self, fn ): - - """Define action to perform if parsing fails at this expression. - - Fail acton fn is a callable function that takes the arguments - - fn(s,loc,expr,err) where: - - - s = string being parsed - - - loc = location where expression match was attempted and failed - - - expr = the parse expression that failed - - - err = the exception thrown - - The function returns no value. It may throw ParseFatalException - - if it is desired to stop parsing immediately.""" - - self.failAction = fn - - return self - - - - def _skipIgnorables( self, instring, loc ): - - exprsFound = True - - while exprsFound: - - exprsFound = False - - for e in self.ignoreExprs: - - try: - - while 1: - - loc,dummy = e._parse( instring, loc ) - - exprsFound = True - - except ParseException: - - pass - - return loc - - - - def preParse( self, instring, loc ): - - if self.ignoreExprs: - - loc = self._skipIgnorables( instring, loc ) - - - - if self.skipWhitespace: - - wt = self.whiteChars - - instrlen = len(instring) - - while loc < instrlen and instring[loc] in wt: - - loc += 1 - - - - return loc - - - - def parseImpl( self, instring, loc, doActions=True ): - - return loc, [] - - - - def postParse( self, instring, loc, tokenlist ): - - return tokenlist - - - - #~ @profile - - def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): - - debugging = ( self.debug ) #and doActions ) - - - - if debugging or self.failAction: - - #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) - - if (self.debugActions[0] ): - - self.debugActions[0]( instring, loc, self ) - - if callPreParse and self.callPreparse: - - preloc = self.preParse( instring, loc ) - - else: - - preloc = loc - - tokensStart = loc - - try: - - try: - - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - except IndexError: - - raise ParseException( instring, len(instring), self.errmsg, self ) - - except ParseBaseException: - - #~ print ("Exception raised:", err) - - err = None - - if self.debugActions[2]: - - err = sys.exc_info()[1] - - self.debugActions[2]( instring, tokensStart, self, err ) - - if self.failAction: - - if err is None: - - err = sys.exc_info()[1] - - self.failAction( instring, tokensStart, self, err ) - - raise - - else: - - if callPreParse and self.callPreparse: - - preloc = self.preParse( instring, loc ) - - else: - - preloc = loc - - tokensStart = loc - - if self.mayIndexError or loc >= len(instring): - - try: - - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - except IndexError: - - raise ParseException( instring, len(instring), self.errmsg, self ) - - else: - - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - - - tokens = self.postParse( instring, loc, tokens ) - - - - retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) - - if self.parseAction and (doActions or self.callDuringTry): - - if debugging: - - try: - - for fn in self.parseAction: - - tokens = fn( instring, tokensStart, retTokens ) - - if tokens is not None: - - retTokens = ParseResults( tokens, - - self.resultsName, - - asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), - - modal=self.modalResults ) - - except ParseBaseException: - - #~ print "Exception raised in user parse action:", err - - if (self.debugActions[2] ): - - err = sys.exc_info()[1] - - self.debugActions[2]( instring, tokensStart, self, err ) - - raise - - else: - - for fn in self.parseAction: - - tokens = fn( instring, tokensStart, retTokens ) - - if tokens is not None: - - retTokens = ParseResults( tokens, - - self.resultsName, - - asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), - - modal=self.modalResults ) - - - - if debugging: - - #~ print ("Matched",self,"->",retTokens.asList()) - - if (self.debugActions[1] ): - - self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) - - - - return loc, retTokens - - - - def tryParse( self, instring, loc ): - - try: - - return self._parse( instring, loc, doActions=False )[0] - - except ParseFatalException: - - raise ParseException( instring, loc, self.errmsg, self) - - - - # this method gets repeatedly called during backtracking with the same arguments - - - # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression - - def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): - - lookup = (self,instring,loc,callPreParse,doActions) - - if lookup in ParserElement._exprArgCache: - - value = ParserElement._exprArgCache[ lookup ] - - if isinstance(value,Exception): - - raise value - - return value - - else: - - try: - - value = self._parseNoCache( instring, loc, doActions, callPreParse ) - - ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) - - return value - - except ParseBaseException: - - pe = sys.exc_info()[1] - - ParserElement._exprArgCache[ lookup ] = pe - - raise - - - - _parse = _parseNoCache - - - - # argument cache for optimizing repeated calls when backtracking through recursive expressions - - _exprArgCache = {} - - def resetCache(): - - ParserElement._exprArgCache.clear() - - resetCache = staticmethod(resetCache) - - - - _packratEnabled = False - - def enablePackrat(): - - """Enables "packrat" parsing, which adds memoizing to the parsing logic. - - Repeated parse attempts at the same string location (which happens - - often in many complex grammars) can immediately return a cached value, - - instead of re-executing parsing/validating code. Memoizing is done of - - both valid results and parsing exceptions. - - - - This speedup may break existing programs that use parse actions that - - have side-effects. For this reason, packrat parsing is disabled when - - you first import pyparsing_py3 as pyparsing. To activate the packrat feature, your - - program must call the class method ParserElement.enablePackrat(). If - - your program uses psyco to "compile as you go", you must call - - enablePackrat before calling psyco.full(). If you do not do this, - - Python will crash. For best results, call enablePackrat() immediately - - after importing pyparsing. - - """ - - if not ParserElement._packratEnabled: - - ParserElement._packratEnabled = True - - ParserElement._parse = ParserElement._parseCache - - enablePackrat = staticmethod(enablePackrat) - - - - def parseString( self, instring, parseAll=False ): - - """Execute the parse expression with the given string. - - This is the main interface to the client code, once the complete - - expression has been built. - - - - If you want the grammar to require that the entire input string be - - successfully parsed, then set parseAll to True (equivalent to ending - - the grammar with StringEnd()). - - - - Note: parseString implicitly calls expandtabs() on the input string, - - in order to report proper column numbers in parse actions. - - If the input string contains tabs and - - the grammar uses parse actions that use the loc argument to index into the - - string being parsed, you can ensure you have a consistent view of the input - - string by: - - - calling parseWithTabs on your grammar before calling parseString - - (see L{I{parseWithTabs}}) - - - define your parse action using the full (s,loc,toks) signature, and - - reference the input string using the parse action's s argument - - - explictly expand the tabs in your input string before calling - - parseString - - """ - - ParserElement.resetCache() - - if not self.streamlined: - - self.streamline() - - #~ self.saveAsList = True - - for e in self.ignoreExprs: - - e.streamline() - - if not self.keepTabs: - - instring = instring.expandtabs() - - try: - - loc, tokens = self._parse( instring, 0 ) - - if parseAll: - - loc = self.preParse( instring, loc ) - - StringEnd()._parse( instring, loc ) - - except ParseBaseException: - - exc = sys.exc_info()[1] - - # catch and re-raise exception from here, clears out pyparsing internal stack trace - - raise exc - - else: - - return tokens - - - - def scanString( self, instring, maxMatches=_MAX_INT ): - - """Scan the input string for expression matches. Each match will return the - - matching tokens, start location, and end location. May be called with optional - - maxMatches argument, to clip scanning after 'n' matches are found. - - - - Note that the start and end locations are reported relative to the string - - being parsed. See L{I{parseString}} for more information on parsing - - strings with embedded tabs.""" - - if not self.streamlined: - - self.streamline() - - for e in self.ignoreExprs: - - e.streamline() - - - - if not self.keepTabs: - - instring = _ustr(instring).expandtabs() - - instrlen = len(instring) - - loc = 0 - - preparseFn = self.preParse - - parseFn = self._parse - - ParserElement.resetCache() - - matches = 0 - - try: - - while loc <= instrlen and matches < maxMatches: - - try: - - preloc = preparseFn( instring, loc ) - - nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) - - except ParseException: - - loc = preloc+1 - - else: - - if nextLoc > loc: - - matches += 1 - - yield tokens, preloc, nextLoc - - loc = nextLoc - - else: - - loc = preloc+1 - - except ParseBaseException: - - pe = sys.exc_info()[1] - - raise pe - - - - def transformString( self, instring ): - - """Extension to scanString, to modify matching text with modified tokens that may - - be returned from a parse action. To use transformString, define a grammar and - - attach a parse action to it that modifies the returned token list. - - Invoking transformString() on a target string will then scan for matches, - - and replace the matched text patterns according to the logic in the parse - - action. transformString() returns the resulting transformed string.""" - - out = [] - - lastE = 0 - - # force preservation of s, to minimize unwanted transformation of string, and to - - # keep string locs straight between transformString and scanString - - self.keepTabs = True - - try: - - for t,s,e in self.scanString( instring ): - - out.append( instring[lastE:s] ) - - if t: - - if isinstance(t,ParseResults): - - out += t.asList() - - elif isinstance(t,list): - - out += t - - else: - - out.append(t) - - lastE = e - - out.append(instring[lastE:]) - - return "".join(map(_ustr,out)) - - except ParseBaseException: - - pe = sys.exc_info()[1] - - raise pe - - - - def searchString( self, instring, maxMatches=_MAX_INT ): - - """Another extension to scanString, simplifying the access to the tokens found - - to match the given parse expression. May be called with optional - - maxMatches argument, to clip searching after 'n' matches are found. - - """ - - try: - - return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) - - except ParseBaseException: - - pe = sys.exc_info()[1] - - raise pe - - - - def __add__(self, other ): - - """Implementation of + operator - returns And""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return And( [ self, other ] ) - - - - def __radd__(self, other ): - - """Implementation of + operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other + self - - - - def __sub__(self, other): - - """Implementation of - operator, returns And with error stop""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return And( [ self, And._ErrorStop(), other ] ) - - - - def __rsub__(self, other ): - - """Implementation of - operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other - self - - - - def __mul__(self,other): - - if isinstance(other,int): - - minElements, optElements = other,0 - - elif isinstance(other,tuple): - - other = (other + (None, None))[:2] - - if other[0] is None: - - other = (0, other[1]) - - if isinstance(other[0],int) and other[1] is None: - - if other[0] == 0: - - return ZeroOrMore(self) - - if other[0] == 1: - - return OneOrMore(self) - - else: - - return self*other[0] + ZeroOrMore(self) - - elif isinstance(other[0],int) and isinstance(other[1],int): - - minElements, optElements = other - - optElements -= minElements - - else: - - raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) - - else: - - raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) - - - - if minElements < 0: - - raise ValueError("cannot multiply ParserElement by negative value") - - if optElements < 0: - - raise ValueError("second tuple value must be greater or equal to first tuple value") - - if minElements == optElements == 0: - - raise ValueError("cannot multiply ParserElement by 0 or (0,0)") - - - - if (optElements): - - def makeOptionalList(n): - - if n>1: - - return Optional(self + makeOptionalList(n-1)) - - else: - - return Optional(self) - - if minElements: - - if minElements == 1: - - ret = self + makeOptionalList(optElements) - - else: - - ret = And([self]*minElements) + makeOptionalList(optElements) - - else: - - ret = makeOptionalList(optElements) - - else: - - if minElements == 1: - - ret = self - - else: - - ret = And([self]*minElements) - - return ret - - - - def __rmul__(self, other): - - return self.__mul__(other) - - - - def __or__(self, other ): - - """Implementation of | operator - returns MatchFirst""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return MatchFirst( [ self, other ] ) - - - - def __ror__(self, other ): - - """Implementation of | operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other | self - - - - def __xor__(self, other ): - - """Implementation of ^ operator - returns Or""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return Or( [ self, other ] ) - - - - def __rxor__(self, other ): - - """Implementation of ^ operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other ^ self - - - - def __and__(self, other ): - - """Implementation of & operator - returns Each""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return Each( [ self, other ] ) - - - - def __rand__(self, other ): - - """Implementation of & operator when left operand is not a ParserElement""" - - if isinstance( other, basestring ): - - other = Literal( other ) - - if not isinstance( other, ParserElement ): - - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - - SyntaxWarning, stacklevel=2) - - return None - - return other & self - - - - def __invert__( self ): - - """Implementation of ~ operator - returns NotAny""" - - return NotAny( self ) - - - - def __call__(self, name): - - """Shortcut for setResultsName, with listAllMatches=default:: - - userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") - - could be written as:: - - userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") - - """ - - return self.setResultsName(name) - - - - def suppress( self ): - - """Suppresses the output of this ParserElement; useful to keep punctuation from - - cluttering up returned output. - - """ - - return Suppress( self ) - - - - def leaveWhitespace( self ): - - """Disables the skipping of whitespace before matching the characters in the - - ParserElement's defined pattern. This is normally only used internally by - - the pyparsing module, but may be needed in some whitespace-sensitive grammars. - - """ - - self.skipWhitespace = False - - return self - - - - def setWhitespaceChars( self, chars ): - - """Overrides the default whitespace chars - - """ - - self.skipWhitespace = True - - self.whiteChars = chars - - self.copyDefaultWhiteChars = False - - return self - - - - def parseWithTabs( self ): - - """Overrides default behavior to expand s to spaces before parsing the input string. - - Must be called before parseString when the input grammar contains elements that - - match characters.""" - - self.keepTabs = True - - return self - - - - def ignore( self, other ): - - """Define expression to be ignored (e.g., comments) while doing pattern - - matching; may be called repeatedly, to define multiple comment or other - - ignorable patterns. - - """ - - if isinstance( other, Suppress ): - - if other not in self.ignoreExprs: - - self.ignoreExprs.append( other ) - - else: - - self.ignoreExprs.append( Suppress( other ) ) - - return self - - - - def setDebugActions( self, startAction, successAction, exceptionAction ): - - """Enable display of debugging messages while doing pattern matching.""" - - self.debugActions = (startAction or _defaultStartDebugAction, - - successAction or _defaultSuccessDebugAction, - - exceptionAction or _defaultExceptionDebugAction) - - self.debug = True - - return self - - - - def setDebug( self, flag=True ): - - """Enable display of debugging messages while doing pattern matching. - - Set flag to True to enable, False to disable.""" - - if flag: - - self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) - - else: - - self.debug = False - - return self - - - - def __str__( self ): - - return self.name - - - - def __repr__( self ): - - return _ustr(self) - - - - def streamline( self ): - - self.streamlined = True - - self.strRepr = None - - return self - - - - def checkRecursion( self, parseElementList ): - - pass - - - - def validate( self, validateTrace=[] ): - - """Check defined expressions for valid structure, check for infinite recursive definitions.""" - - self.checkRecursion( [] ) - - - - def parseFile( self, file_or_filename, parseAll=False ): - - """Execute the parse expression on the given file or filename. - - If a filename is specified (instead of a file object), - - the entire file is opened, read, and closed before parsing. - - """ - - try: - - file_contents = file_or_filename.read() - - except AttributeError: - - f = open(file_or_filename, "rb") - - file_contents = f.read() - - f.close() - - try: - - return self.parseString(file_contents, parseAll) - - except ParseBaseException: - - # catch and re-raise exception from here, clears out pyparsing internal stack trace - - exc = sys.exc_info()[1] - - raise exc - - - - def getException(self): - - return ParseException("",0,self.errmsg,self) - - - - def __getattr__(self,aname): - - if aname == "myException": - - self.myException = ret = self.getException(); - - return ret; - - else: - - raise AttributeError("no such attribute " + aname) - - - - def __eq__(self,other): - - if isinstance(other, ParserElement): - - return self is other or self.__dict__ == other.__dict__ - - elif isinstance(other, basestring): - - try: - - self.parseString(_ustr(other), parseAll=True) - - return True - - except ParseBaseException: - - return False - - else: - - return super(ParserElement,self)==other - - - - def __ne__(self,other): - - return not (self == other) - - - - def __hash__(self): - - return hash(id(self)) - - - - def __req__(self,other): - - return self == other - - - - def __rne__(self,other): - - return not (self == other) - - - - - -class Token(ParserElement): - - """Abstract ParserElement subclass, for defining atomic matching patterns.""" - - def __init__( self ): - - super(Token,self).__init__( savelist=False ) - - #self.myException = ParseException("",0,"",self) - - - - def setName(self, name): - - s = super(Token,self).setName(name) - - self.errmsg = "Expected " + self.name - - #s.myException.msg = self.errmsg - - return s - - - - - -class Empty(Token): - - """An empty token, will always match.""" - - def __init__( self ): - - super(Empty,self).__init__() - - self.name = "Empty" - - self.mayReturnEmpty = True - - self.mayIndexError = False - - - - - -class NoMatch(Token): - - """A token that will never match.""" - - def __init__( self ): - - super(NoMatch,self).__init__() - - self.name = "NoMatch" - - self.mayReturnEmpty = True - - self.mayIndexError = False - - self.errmsg = "Unmatchable token" - - #self.myException.msg = self.errmsg - - - - def parseImpl( self, instring, loc, doActions=True ): - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - - -class Literal(Token): - - """Token to exactly match a specified string.""" - - def __init__( self, matchString ): - - super(Literal,self).__init__() - - self.match = matchString - - self.matchLen = len(matchString) - - try: - - self.firstMatchChar = matchString[0] - - except IndexError: - - warnings.warn("null string passed to Literal; use Empty() instead", - - SyntaxWarning, stacklevel=2) - - self.__class__ = Empty - - self.name = '"%s"' % _ustr(self.match) - - self.errmsg = "Expected " + self.name - - self.mayReturnEmpty = False - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - - - # Performance tuning: this routine gets called a *lot* - - # if this is a single character match string and the first character matches, - - # short-circuit as quickly as possible, and avoid calling startswith - - #~ @profile - - def parseImpl( self, instring, loc, doActions=True ): - - if (instring[loc] == self.firstMatchChar and - - (self.matchLen==1 or instring.startswith(self.match,loc)) ): - - return loc+self.matchLen, self.match - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - -_L = Literal - - - -class Keyword(Token): - - """Token to exactly match a specified string as a keyword, that is, it must be - - immediately followed by a non-keyword character. Compare with Literal:: - - Literal("if") will match the leading 'if' in 'ifAndOnlyIf'. - - Keyword("if") will not; it will only match the leading 'if in 'if x=1', or 'if(y==2)' - - Accepts two optional constructor arguments in addition to the keyword string: - - identChars is a string of characters that would be valid identifier characters, - - defaulting to all alphanumerics + "_" and "$"; caseless allows case-insensitive - - matching, default is False. - - """ - - DEFAULT_KEYWORD_CHARS = alphanums+"_$" - - - - def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): - - super(Keyword,self).__init__() - - self.match = matchString - - self.matchLen = len(matchString) - - try: - - self.firstMatchChar = matchString[0] - - except IndexError: - - warnings.warn("null string passed to Keyword; use Empty() instead", - - SyntaxWarning, stacklevel=2) - - self.name = '"%s"' % self.match - - self.errmsg = "Expected " + self.name - - self.mayReturnEmpty = False - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.caseless = caseless - - if caseless: - - self.caselessmatch = matchString.upper() - - identChars = identChars.upper() - - self.identChars = _str2dict(identChars) - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.caseless: - - if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and - - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and - - (loc == 0 or instring[loc-1].upper() not in self.identChars) ): - - return loc+self.matchLen, self.match - - else: - - if (instring[loc] == self.firstMatchChar and - - (self.matchLen==1 or instring.startswith(self.match,loc)) and - - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and - - (loc == 0 or instring[loc-1] not in self.identChars) ): - - return loc+self.matchLen, self.match - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - def copy(self): - - c = super(Keyword,self).copy() - - c.identChars = Keyword.DEFAULT_KEYWORD_CHARS - - return c - - - - def setDefaultKeywordChars( chars ): - - """Overrides the default Keyword chars - - """ - - Keyword.DEFAULT_KEYWORD_CHARS = chars - - setDefaultKeywordChars = staticmethod(setDefaultKeywordChars) - - - -class CaselessLiteral(Literal): - - """Token to match a specified string, ignoring case of letters. - - Note: the matched results will always be in the case of the given - - match string, NOT the case of the input text. - - """ - - def __init__( self, matchString ): - - super(CaselessLiteral,self).__init__( matchString.upper() ) - - # Preserve the defining literal. - - self.returnString = matchString - - self.name = "'%s'" % self.returnString - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - - - def parseImpl( self, instring, loc, doActions=True ): - - if instring[ loc:loc+self.matchLen ].upper() == self.match: - - return loc+self.matchLen, self.returnString - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class CaselessKeyword(Keyword): - - def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): - - super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) - - - - def parseImpl( self, instring, loc, doActions=True ): - - if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and - - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): - - return loc+self.matchLen, self.match - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class Word(Token): - - """Token for matching words composed of allowed character sets. - - Defined with string containing all allowed initial characters, - - an optional string containing allowed body characters (if omitted, - - defaults to the initial character set), and an optional minimum, - - maximum, and/or exact length. The default value for min is 1 (a - - minimum value < 1 is not valid); the default values for max and exact - - are 0, meaning no maximum or exact length restriction. - - """ - - def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False ): - - super(Word,self).__init__() - - self.initCharsOrig = initChars - - self.initChars = _str2dict(initChars) - - if bodyChars : - - self.bodyCharsOrig = bodyChars - - self.bodyChars = _str2dict(bodyChars) - - else: - - self.bodyCharsOrig = initChars - - self.bodyChars = _str2dict(initChars) - - - - self.maxSpecified = max > 0 - - - - if min < 1: - - raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") - - - - self.minLen = min - - - - if max > 0: - - self.maxLen = max - - else: - - self.maxLen = _MAX_INT - - - - if exact > 0: - - self.maxLen = exact - - self.minLen = exact - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.asKeyword = asKeyword - - - - if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): - - if self.bodyCharsOrig == self.initCharsOrig: - - self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) - - elif len(self.bodyCharsOrig) == 1: - - self.reString = "%s[%s]*" % \ - - (re.escape(self.initCharsOrig), - - _escapeRegexRangeChars(self.bodyCharsOrig),) - - else: - - self.reString = "[%s][%s]*" % \ - - (_escapeRegexRangeChars(self.initCharsOrig), - - _escapeRegexRangeChars(self.bodyCharsOrig),) - - if self.asKeyword: - - self.reString = r"\b"+self.reString+r"\b" - - try: - - self.re = re.compile( self.reString ) - - except: - - self.re = None - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.re: - - result = self.re.match(instring,loc) - - if not result: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - loc = result.end() - - return loc,result.group() - - - - if not(instring[ loc ] in self.initChars): - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - start = loc - - loc += 1 - - instrlen = len(instring) - - bodychars = self.bodyChars - - maxloc = start + self.maxLen - - maxloc = min( maxloc, instrlen ) - - while loc < maxloc and instring[loc] in bodychars: - - loc += 1 - - - - throwException = False - - if loc - start < self.minLen: - - throwException = True - - if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: - - throwException = True - - if self.asKeyword: - - if (start>0 and instring[start-1] in bodychars) or (loc4: - - return s[:4]+"..." - - else: - - return s - - - - if ( self.initCharsOrig != self.bodyCharsOrig ): - - self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) - - else: - - self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) - - - - return self.strRepr - - - - - -class Regex(Token): - - """Token for matching strings that match a given regular expression. - - Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. - - """ - - def __init__( self, pattern, flags=0): - - """The parameters pattern and flags are passed to the re.compile() function as-is. See the Python re module for an explanation of the acceptable patterns and flags.""" - - super(Regex,self).__init__() - - - - if len(pattern) == 0: - - warnings.warn("null string passed to Regex; use Empty() instead", - - SyntaxWarning, stacklevel=2) - - - - self.pattern = pattern - - self.flags = flags - - - - try: - - self.re = re.compile(self.pattern, self.flags) - - self.reString = self.pattern - - except sre_constants.error: - - warnings.warn("invalid pattern (%s) passed to Regex" % pattern, - - SyntaxWarning, stacklevel=2) - - raise - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - result = self.re.match(instring,loc) - - if not result: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - loc = result.end() - - d = result.groupdict() - - ret = ParseResults(result.group()) - - if d: - - for k in d: - - ret[k] = d[k] - - return loc,ret - - - - def __str__( self ): - - try: - - return super(Regex,self).__str__() - - except: - - pass - - - - if self.strRepr is None: - - self.strRepr = "Re:(%s)" % repr(self.pattern) - - - - return self.strRepr - - - - - -class QuotedString(Token): - - """Token for matching strings that are delimited by quoting characters. - - """ - - def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): - - """ - - Defined with the following parameters: - - - quoteChar - string of one or more characters defining the quote delimiting string - - - escChar - character to escape quotes, typically backslash (default=None) - - - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) - - - multiline - boolean indicating whether quotes can span multiple lines (default=False) - - - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True) - - - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar) - - """ - - super(QuotedString,self).__init__() - - - - # remove white space from quote chars - wont work anyway - - quoteChar = quoteChar.strip() - - if len(quoteChar) == 0: - - warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) - - raise SyntaxError() - - - - if endQuoteChar is None: - - endQuoteChar = quoteChar - - else: - - endQuoteChar = endQuoteChar.strip() - - if len(endQuoteChar) == 0: - - warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) - - raise SyntaxError() - - - - self.quoteChar = quoteChar - - self.quoteCharLen = len(quoteChar) - - self.firstQuoteChar = quoteChar[0] - - self.endQuoteChar = endQuoteChar - - self.endQuoteCharLen = len(endQuoteChar) - - self.escChar = escChar - - self.escQuote = escQuote - - self.unquoteResults = unquoteResults - - - - if multiline: - - self.flags = re.MULTILINE | re.DOTALL - - self.pattern = r'%s(?:[^%s%s]' % \ - - ( re.escape(self.quoteChar), - - _escapeRegexRangeChars(self.endQuoteChar[0]), - - (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) - - else: - - self.flags = 0 - - self.pattern = r'%s(?:[^%s\n\r%s]' % \ - - ( re.escape(self.quoteChar), - - _escapeRegexRangeChars(self.endQuoteChar[0]), - - (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) - - if len(self.endQuoteChar) > 1: - - self.pattern += ( - - '|(?:' + ')|(?:'.join(["%s[^%s]" % (re.escape(self.endQuoteChar[:i]), - - _escapeRegexRangeChars(self.endQuoteChar[i])) - - for i in range(len(self.endQuoteChar)-1,0,-1)]) + ')' - - ) - - if escQuote: - - self.pattern += (r'|(?:%s)' % re.escape(escQuote)) - - if escChar: - - self.pattern += (r'|(?:%s.)' % re.escape(escChar)) - - self.escCharReplacePattern = re.escape(self.escChar)+"(.)" - - self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) - - - - try: - - self.re = re.compile(self.pattern, self.flags) - - self.reString = self.pattern - - except sre_constants.error: - - warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, - - SyntaxWarning, stacklevel=2) - - raise - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None - - if not result: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - loc = result.end() - - ret = result.group() - - - - if self.unquoteResults: - - - - # strip off quotes - - ret = ret[self.quoteCharLen:-self.endQuoteCharLen] - - - - if isinstance(ret,basestring): - - # replace escaped characters - - if self.escChar: - - ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) - - - - # replace escaped quotes - - if self.escQuote: - - ret = ret.replace(self.escQuote, self.endQuoteChar) - - - - return loc, ret - - - - def __str__( self ): - - try: - - return super(QuotedString,self).__str__() - - except: - - pass - - - - if self.strRepr is None: - - self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) - - - - return self.strRepr - - - - - -class CharsNotIn(Token): - - """Token for matching words composed of characters *not* in a given set. - - Defined with string containing all disallowed characters, and an optional - - minimum, maximum, and/or exact length. The default value for min is 1 (a - - minimum value < 1 is not valid); the default values for max and exact - - are 0, meaning no maximum or exact length restriction. - - """ - - def __init__( self, notChars, min=1, max=0, exact=0 ): - - super(CharsNotIn,self).__init__() - - self.skipWhitespace = False - - self.notChars = notChars - - - - if min < 1: - - raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") - - - - self.minLen = min - - - - if max > 0: - - self.maxLen = max - - else: - - self.maxLen = _MAX_INT - - - - if exact > 0: - - self.maxLen = exact - - self.minLen = exact - - - - self.name = _ustr(self) - - self.errmsg = "Expected " + self.name - - self.mayReturnEmpty = ( self.minLen == 0 ) - - #self.myException.msg = self.errmsg - - self.mayIndexError = False - - - - def parseImpl( self, instring, loc, doActions=True ): - - if instring[loc] in self.notChars: - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - start = loc - - loc += 1 - - notchars = self.notChars - - maxlen = min( start+self.maxLen, len(instring) ) - - while loc < maxlen and \ - - (instring[loc] not in notchars): - - loc += 1 - - - - if loc - start < self.minLen: - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - return loc, instring[start:loc] - - - - def __str__( self ): - - try: - - return super(CharsNotIn, self).__str__() - - except: - - pass - - - - if self.strRepr is None: - - if len(self.notChars) > 4: - - self.strRepr = "!W:(%s...)" % self.notChars[:4] - - else: - - self.strRepr = "!W:(%s)" % self.notChars - - - - return self.strRepr - - - -class White(Token): - - """Special matching class for matching whitespace. Normally, whitespace is ignored - - by pyparsing grammars. This class is included when some whitespace structures - - are significant. Define with a string containing the whitespace characters to be - - matched; default is " \\t\\r\\n". Also takes optional min, max, and exact arguments, - - as defined for the Word class.""" - - whiteStrs = { - - " " : "", - - "\t": "", - - "\n": "", - - "\r": "", - - "\f": "", - - } - - def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): - - super(White,self).__init__() - - self.matchWhite = ws - - self.setWhitespaceChars( "".join([c for c in self.whiteChars if c not in self.matchWhite]) ) - - #~ self.leaveWhitespace() - - self.name = ("".join([White.whiteStrs[c] for c in self.matchWhite])) - - self.mayReturnEmpty = True - - self.errmsg = "Expected " + self.name - - #self.myException.msg = self.errmsg - - - - self.minLen = min - - - - if max > 0: - - self.maxLen = max - - else: - - self.maxLen = _MAX_INT - - - - if exact > 0: - - self.maxLen = exact - - self.minLen = exact - - - - def parseImpl( self, instring, loc, doActions=True ): - - if not(instring[ loc ] in self.matchWhite): - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - start = loc - - loc += 1 - - maxloc = start + self.maxLen - - maxloc = min( maxloc, len(instring) ) - - while loc < maxloc and instring[loc] in self.matchWhite: - - loc += 1 - - - - if loc - start < self.minLen: - - #~ raise ParseException( instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - - return loc, instring[start:loc] - - - - - -class _PositionToken(Token): - - def __init__( self ): - - super(_PositionToken,self).__init__() - - self.name=self.__class__.__name__ - - self.mayReturnEmpty = True - - self.mayIndexError = False - - - -class GoToColumn(_PositionToken): - - """Token to advance to a specific column of input text; useful for tabular report scraping.""" - - def __init__( self, colno ): - - super(GoToColumn,self).__init__() - - self.col = colno - - - - def preParse( self, instring, loc ): - - if col(loc,instring) != self.col: - - instrlen = len(instring) - - if self.ignoreExprs: - - loc = self._skipIgnorables( instring, loc ) - - while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : - - loc += 1 - - return loc - - - - def parseImpl( self, instring, loc, doActions=True ): - - thiscol = col( loc, instring ) - - if thiscol > self.col: - - raise ParseException( instring, loc, "Text not in expected column", self ) - - newloc = loc + self.col - thiscol - - ret = instring[ loc: newloc ] - - return newloc, ret - - - -class LineStart(_PositionToken): - - """Matches if current position is at the beginning of a line within the parse string""" - - def __init__( self ): - - super(LineStart,self).__init__() - - self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) - - self.errmsg = "Expected start of line" - - #self.myException.msg = self.errmsg - - - - def preParse( self, instring, loc ): - - preloc = super(LineStart,self).preParse(instring,loc) - - if instring[preloc] == "\n": - - loc += 1 - - return loc - - - - def parseImpl( self, instring, loc, doActions=True ): - - if not( loc==0 or - - (loc == self.preParse( instring, 0 )) or - - (instring[loc-1] == "\n") ): #col(loc, instring) != 1: - - #~ raise ParseException( instring, loc, "Expected start of line" ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - return loc, [] - - - -class LineEnd(_PositionToken): - - """Matches if current position is at the end of a line within the parse string""" - - def __init__( self ): - - super(LineEnd,self).__init__() - - self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) - - self.errmsg = "Expected end of line" - - #self.myException.msg = self.errmsg - - - - def parseImpl( self, instring, loc, doActions=True ): - - if loc len(instring): - - return loc, [] - - else: - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class WordStart(_PositionToken): - - """Matches if the current position is at the beginning of a Word, and - - is not preceded by any character in a given set of wordChars - - (default=printables). To emulate the \b behavior of regular expressions, - - use WordStart(alphanums). WordStart will also match at the beginning of - - the string being parsed, or at the beginning of a line. - - """ - - def __init__(self, wordChars = printables): - - super(WordStart,self).__init__() - - self.wordChars = _str2dict(wordChars) - - self.errmsg = "Not at the start of a word" - - - - def parseImpl(self, instring, loc, doActions=True ): - - if loc != 0: - - if (instring[loc-1] in self.wordChars or - - instring[loc] not in self.wordChars): - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - return loc, [] - - - -class WordEnd(_PositionToken): - - """Matches if the current position is at the end of a Word, and - - is not followed by any character in a given set of wordChars - - (default=printables). To emulate the \b behavior of regular expressions, - - use WordEnd(alphanums). WordEnd will also match at the end of - - the string being parsed, or at the end of a line. - - """ - - def __init__(self, wordChars = printables): - - super(WordEnd,self).__init__() - - self.wordChars = _str2dict(wordChars) - - self.skipWhitespace = False - - self.errmsg = "Not at the end of a word" - - - - def parseImpl(self, instring, loc, doActions=True ): - - instrlen = len(instring) - - if instrlen>0 and loc maxExcLoc: - - maxException = err - - maxExcLoc = err.loc - - except IndexError: - - if len(instring) > maxExcLoc: - - maxException = ParseException(instring,len(instring),e.errmsg,self) - - maxExcLoc = len(instring) - - else: - - if loc2 > maxMatchLoc: - - maxMatchLoc = loc2 - - maxMatchExp = e - - - - if maxMatchLoc < 0: - - if maxException is not None: - - raise maxException - - else: - - raise ParseException(instring, loc, "no defined alternatives to match", self) - - - - return maxMatchExp._parse( instring, loc, doActions ) - - - - def __ixor__(self, other ): - - if isinstance( other, basestring ): - - other = Literal( other ) - - return self.append( other ) #Or( [ self, other ] ) - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + " ^ ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - - - return self.strRepr - - - - def checkRecursion( self, parseElementList ): - - subRecCheckList = parseElementList[:] + [ self ] - - for e in self.exprs: - - e.checkRecursion( subRecCheckList ) - - - - - -class MatchFirst(ParseExpression): - - """Requires that at least one ParseExpression is found. - - If two expressions match, the first one listed is the one that will match. - - May be constructed using the '|' operator. - - """ - - def __init__( self, exprs, savelist = False ): - - super(MatchFirst,self).__init__(exprs, savelist) - - if exprs: - - self.mayReturnEmpty = False - - for e in self.exprs: - - if e.mayReturnEmpty: - - self.mayReturnEmpty = True - - break - - else: - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - maxExcLoc = -1 - - maxException = None - - for e in self.exprs: - - try: - - ret = e._parse( instring, loc, doActions ) - - return ret - - except ParseException as err: - - if err.loc > maxExcLoc: - - maxException = err - - maxExcLoc = err.loc - - except IndexError: - - if len(instring) > maxExcLoc: - - maxException = ParseException(instring,len(instring),e.errmsg,self) - - maxExcLoc = len(instring) - - - - # only got here if no expression matched, raise exception for match that made it the furthest - - else: - - if maxException is not None: - - raise maxException - - else: - - raise ParseException(instring, loc, "no defined alternatives to match", self) - - - - def __ior__(self, other ): - - if isinstance( other, basestring ): - - other = Literal( other ) - - return self.append( other ) #MatchFirst( [ self, other ] ) - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + " | ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - - - return self.strRepr - - - - def checkRecursion( self, parseElementList ): - - subRecCheckList = parseElementList[:] + [ self ] - - for e in self.exprs: - - e.checkRecursion( subRecCheckList ) - - - - - -class Each(ParseExpression): - - """Requires all given ParseExpressions to be found, but in any order. - - Expressions may be separated by whitespace. - - May be constructed using the '&' operator. - - """ - - def __init__( self, exprs, savelist = True ): - - super(Each,self).__init__(exprs, savelist) - - self.mayReturnEmpty = True - - for e in self.exprs: - - if not e.mayReturnEmpty: - - self.mayReturnEmpty = False - - break - - self.skipWhitespace = True - - self.initExprGroups = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.initExprGroups: - - self.optionals = [ e.expr for e in self.exprs if isinstance(e,Optional) ] - - self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] - - self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] - - self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] - - self.required += self.multirequired - - self.initExprGroups = False - - tmpLoc = loc - - tmpReqd = self.required[:] - - tmpOpt = self.optionals[:] - - matchOrder = [] - - - - keepMatching = True - - while keepMatching: - - tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired - - failed = [] - - for e in tmpExprs: - - try: - - tmpLoc = e.tryParse( instring, tmpLoc ) - - except ParseException: - - failed.append(e) - - else: - - matchOrder.append(e) - - if e in tmpReqd: - - tmpReqd.remove(e) - - elif e in tmpOpt: - - tmpOpt.remove(e) - - if len(failed) == len(tmpExprs): - - keepMatching = False - - - - if tmpReqd: - - missing = ", ".join( [ _ustr(e) for e in tmpReqd ] ) - - raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) - - - - # add any unmatched Optionals, in case they have default values defined - - matchOrder += list(e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt) - - - - resultlist = [] - - for e in matchOrder: - - loc,results = e._parse(instring,loc,doActions) - - resultlist.append(results) - - - - finalResults = ParseResults([]) - - for r in resultlist: - - dups = {} - - for k in r.keys(): - - if k in finalResults.keys(): - - tmp = ParseResults(finalResults[k]) - - tmp += ParseResults(r[k]) - - dups[k] = tmp - - finalResults += ParseResults(r) - - for k,v in dups.items(): - - finalResults[k] = v - - return loc, finalResults - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + " & ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - - - return self.strRepr - - - - def checkRecursion( self, parseElementList ): - - subRecCheckList = parseElementList[:] + [ self ] - - for e in self.exprs: - - e.checkRecursion( subRecCheckList ) - - - - - -class ParseElementEnhance(ParserElement): - - """Abstract subclass of ParserElement, for combining and post-processing parsed tokens.""" - - def __init__( self, expr, savelist=False ): - - super(ParseElementEnhance,self).__init__(savelist) - - if isinstance( expr, basestring ): - - expr = Literal(expr) - - self.expr = expr - - self.strRepr = None - - if expr is not None: - - self.mayIndexError = expr.mayIndexError - - self.mayReturnEmpty = expr.mayReturnEmpty - - self.setWhitespaceChars( expr.whiteChars ) - - self.skipWhitespace = expr.skipWhitespace - - self.saveAsList = expr.saveAsList - - self.callPreparse = expr.callPreparse - - self.ignoreExprs.extend(expr.ignoreExprs) - - - - def parseImpl( self, instring, loc, doActions=True ): - - if self.expr is not None: - - return self.expr._parse( instring, loc, doActions, callPreParse=False ) - - else: - - raise ParseException("",loc,self.errmsg,self) - - - - def leaveWhitespace( self ): - - self.skipWhitespace = False - - self.expr = self.expr.copy() - - if self.expr is not None: - - self.expr.leaveWhitespace() - - return self - - - - def ignore( self, other ): - - if isinstance( other, Suppress ): - - if other not in self.ignoreExprs: - - super( ParseElementEnhance, self).ignore( other ) - - if self.expr is not None: - - self.expr.ignore( self.ignoreExprs[-1] ) - - else: - - super( ParseElementEnhance, self).ignore( other ) - - if self.expr is not None: - - self.expr.ignore( self.ignoreExprs[-1] ) - - return self - - - - def streamline( self ): - - super(ParseElementEnhance,self).streamline() - - if self.expr is not None: - - self.expr.streamline() - - return self - - - - def checkRecursion( self, parseElementList ): - - if self in parseElementList: - - raise RecursiveGrammarException( parseElementList+[self] ) - - subRecCheckList = parseElementList[:] + [ self ] - - if self.expr is not None: - - self.expr.checkRecursion( subRecCheckList ) - - - - def validate( self, validateTrace=[] ): - - tmp = validateTrace[:]+[self] - - if self.expr is not None: - - self.expr.validate(tmp) - - self.checkRecursion( [] ) - - - - def __str__( self ): - - try: - - return super(ParseElementEnhance,self).__str__() - - except: - - pass - - - - if self.strRepr is None and self.expr is not None: - - self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) - - return self.strRepr - - - - - -class FollowedBy(ParseElementEnhance): - - """Lookahead matching of the given parse expression. FollowedBy - - does *not* advance the parsing position within the input string, it only - - verifies that the specified parse expression matches at the current - - position. FollowedBy always returns a null token list.""" - - def __init__( self, expr ): - - super(FollowedBy,self).__init__(expr) - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - self.expr.tryParse( instring, loc ) - - return loc, [] - - - - - -class NotAny(ParseElementEnhance): - - """Lookahead to disallow matching with the given parse expression. NotAny - - does *not* advance the parsing position within the input string, it only - - verifies that the specified parse expression does *not* match at the current - - position. Also, NotAny does *not* skip over leading whitespace. NotAny - - always returns a null token list. May be constructed using the '~' operator.""" - - def __init__( self, expr ): - - super(NotAny,self).__init__(expr) - - #~ self.leaveWhitespace() - - self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs - - self.mayReturnEmpty = True - - self.errmsg = "Found unwanted token, "+_ustr(self.expr) - - #self.myException = ParseException("",0,self.errmsg,self) - - - - def parseImpl( self, instring, loc, doActions=True ): - - try: - - self.expr.tryParse( instring, loc ) - - except (ParseException,IndexError): - - pass - - else: - - #~ raise ParseException(instring, loc, self.errmsg ) - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - return loc, [] - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "~{" + _ustr(self.expr) + "}" - - - - return self.strRepr - - - - - -class ZeroOrMore(ParseElementEnhance): - - """Optional repetition of zero or more of the given expression.""" - - def __init__( self, expr ): - - super(ZeroOrMore,self).__init__(expr) - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - tokens = [] - - try: - - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - - while 1: - - if hasIgnoreExprs: - - preloc = self._skipIgnorables( instring, loc ) - - else: - - preloc = loc - - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - - if tmptokens or tmptokens.keys(): - - tokens += tmptokens - - except (ParseException,IndexError): - - pass - - - - return loc, tokens - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "[" + _ustr(self.expr) + "]..." - - - - return self.strRepr - - - - def setResultsName( self, name, listAllMatches=False ): - - ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) - - ret.saveAsList = True - - return ret - - - - - -class OneOrMore(ParseElementEnhance): - - """Repetition of one or more of the given expression.""" - - def parseImpl( self, instring, loc, doActions=True ): - - # must be at least one - - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - - try: - - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - - while 1: - - if hasIgnoreExprs: - - preloc = self._skipIgnorables( instring, loc ) - - else: - - preloc = loc - - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - - if tmptokens or tmptokens.keys(): - - tokens += tmptokens - - except (ParseException,IndexError): - - pass - - - - return loc, tokens - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "{" + _ustr(self.expr) + "}..." - - - - return self.strRepr - - - - def setResultsName( self, name, listAllMatches=False ): - - ret = super(OneOrMore,self).setResultsName(name,listAllMatches) - - ret.saveAsList = True - - return ret - - - -class _NullToken(object): - - def __bool__(self): - - return False - - __nonzero__ = __bool__ - - def __str__(self): - - return "" - - - -_optionalNotMatched = _NullToken() - -class Optional(ParseElementEnhance): - - """Optional matching of the given expression. - - A default return string can also be specified, if the optional expression - - is not found. - - """ - - def __init__( self, exprs, default=_optionalNotMatched ): - - super(Optional,self).__init__( exprs, savelist=False ) - - self.defaultValue = default - - self.mayReturnEmpty = True - - - - def parseImpl( self, instring, loc, doActions=True ): - - try: - - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - - except (ParseException,IndexError): - - if self.defaultValue is not _optionalNotMatched: - - if self.expr.resultsName: - - tokens = ParseResults([ self.defaultValue ]) - - tokens[self.expr.resultsName] = self.defaultValue - - else: - - tokens = [ self.defaultValue ] - - else: - - tokens = [] - - return loc, tokens - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - if self.strRepr is None: - - self.strRepr = "[" + _ustr(self.expr) + "]" - - - - return self.strRepr - - - - - -class SkipTo(ParseElementEnhance): - - """Token for skipping over all undefined text until the matched expression is found. - - If include is set to true, the matched expression is also parsed (the skipped text - - and matched expression are returned as a 2-element list). The ignore - - argument is used to define grammars (typically quoted strings and comments) that - - might contain false matches. - - """ - - def __init__( self, other, include=False, ignore=None, failOn=None ): - - super( SkipTo, self ).__init__( other ) - - self.ignoreExpr = ignore - - self.mayReturnEmpty = True - - self.mayIndexError = False - - self.includeMatch = include - - self.asList = False - - if failOn is not None and isinstance(failOn, basestring): - - self.failOn = Literal(failOn) - - else: - - self.failOn = failOn - - self.errmsg = "No match found for "+_ustr(self.expr) - - #self.myException = ParseException("",0,self.errmsg,self) - - - - def parseImpl( self, instring, loc, doActions=True ): - - startLoc = loc - - instrlen = len(instring) - - expr = self.expr - - failParse = False - - while loc <= instrlen: - - try: - - if self.failOn: - - try: - - self.failOn.tryParse(instring, loc) - - except ParseBaseException: - - pass - - else: - - failParse = True - - raise ParseException(instring, loc, "Found expression " + str(self.failOn)) - - failParse = False - - if self.ignoreExpr is not None: - - while 1: - - try: - - loc = self.ignoreExpr.tryParse(instring,loc) - - # print("found ignoreExpr, advance to", loc) - - except ParseBaseException: - - break - - expr._parse( instring, loc, doActions=False, callPreParse=False ) - - skipText = instring[startLoc:loc] - - if self.includeMatch: - - loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) - - if mat: - - skipRes = ParseResults( skipText ) - - skipRes += mat - - return loc, [ skipRes ] - - else: - - return loc, [ skipText ] - - else: - - return loc, [ skipText ] - - except (ParseException,IndexError): - - if failParse: - - raise - - else: - - loc += 1 - - exc = self.myException - - exc.loc = loc - - exc.pstr = instring - - raise exc - - - -class Forward(ParseElementEnhance): - - """Forward declaration of an expression to be defined later - - - used for recursive grammars, such as algebraic infix notation. - - When the expression is known, it is assigned to the Forward variable using the '<<' operator. - - - - Note: take care when assigning to Forward not to overlook precedence of operators. - - Specifically, '|' has a lower precedence than '<<', so that:: - - fwdExpr << a | b | c - - will actually be evaluated as:: - - (fwdExpr << a) | b | c - - thereby leaving b and c out as parseable alternatives. It is recommended that you - - explicitly group the values inserted into the Forward:: - - fwdExpr << (a | b | c) - - """ - - def __init__( self, other=None ): - - super(Forward,self).__init__( other, savelist=False ) - - - - def __lshift__( self, other ): - - if isinstance( other, basestring ): - - other = Literal(other) - - self.expr = other - - self.mayReturnEmpty = other.mayReturnEmpty - - self.strRepr = None - - self.mayIndexError = self.expr.mayIndexError - - self.mayReturnEmpty = self.expr.mayReturnEmpty - - self.setWhitespaceChars( self.expr.whiteChars ) - - self.skipWhitespace = self.expr.skipWhitespace - - self.saveAsList = self.expr.saveAsList - - self.ignoreExprs.extend(self.expr.ignoreExprs) - - return None - - - - def leaveWhitespace( self ): - - self.skipWhitespace = False - - return self - - - - def streamline( self ): - - if not self.streamlined: - - self.streamlined = True - - if self.expr is not None: - - self.expr.streamline() - - return self - - - - def validate( self, validateTrace=[] ): - - if self not in validateTrace: - - tmp = validateTrace[:]+[self] - - if self.expr is not None: - - self.expr.validate(tmp) - - self.checkRecursion([]) - - - - def __str__( self ): - - if hasattr(self,"name"): - - return self.name - - - - self._revertClass = self.__class__ - - self.__class__ = _ForwardNoRecurse - - try: - - if self.expr is not None: - - retString = _ustr(self.expr) - - else: - - retString = "None" - - finally: - - self.__class__ = self._revertClass - - return self.__class__.__name__ + ": " + retString - - - - def copy(self): - - if self.expr is not None: - - return super(Forward,self).copy() - - else: - - ret = Forward() - - ret << self - - return ret - - - -class _ForwardNoRecurse(Forward): - - def __str__( self ): - - return "..." - - - -class TokenConverter(ParseElementEnhance): - - """Abstract subclass of ParseExpression, for converting parsed results.""" - - def __init__( self, expr, savelist=False ): - - super(TokenConverter,self).__init__( expr )#, savelist ) - - self.saveAsList = False - - - -class Upcase(TokenConverter): - - """Converter to upper case all matching tokens.""" - - def __init__(self, *args): - - super(Upcase,self).__init__(*args) - - warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", - - DeprecationWarning,stacklevel=2) - - - - def postParse( self, instring, loc, tokenlist ): - - return list(map( string.upper, tokenlist )) - - - - - -class Combine(TokenConverter): - - """Converter to concatenate all matching tokens to a single string. - - By default, the matching patterns must also be contiguous in the input string; - - this can be disabled by specifying 'adjacent=False' in the constructor. - - """ - - def __init__( self, expr, joinString="", adjacent=True ): - - super(Combine,self).__init__( expr ) - - # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself - - if adjacent: - - self.leaveWhitespace() - - self.adjacent = adjacent - - self.skipWhitespace = True - - self.joinString = joinString - - - - def ignore( self, other ): - - if self.adjacent: - - ParserElement.ignore(self, other) - - else: - - super( Combine, self).ignore( other ) - - return self - - - - def postParse( self, instring, loc, tokenlist ): - - retToks = tokenlist.copy() - - del retToks[:] - - retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) - - - - if self.resultsName and len(retToks.keys())>0: - - return [ retToks ] - - else: - - return retToks - - - -class Group(TokenConverter): - - """Converter to return the matched tokens as a list - useful for returning tokens of ZeroOrMore and OneOrMore expressions.""" - - def __init__( self, expr ): - - super(Group,self).__init__( expr ) - - self.saveAsList = True - - - - def postParse( self, instring, loc, tokenlist ): - - return [ tokenlist ] - - - -class Dict(TokenConverter): - - """Converter to return a repetitive expression as a list, but also as a dictionary. - - Each element can also be referenced using the first token in the expression as its key. - - Useful for tabular report scraping when the first column can be used as a item key. - - """ - - def __init__( self, exprs ): - - super(Dict,self).__init__( exprs ) - - self.saveAsList = True - - - - def postParse( self, instring, loc, tokenlist ): - - for i,tok in enumerate(tokenlist): - - if len(tok) == 0: - - continue - - ikey = tok[0] - - if isinstance(ikey,int): - - ikey = _ustr(tok[0]).strip() - - if len(tok)==1: - - tokenlist[ikey] = _ParseResultsWithOffset("",i) - - elif len(tok)==2 and not isinstance(tok[1],ParseResults): - - tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) - - else: - - dictvalue = tok.copy() #ParseResults(i) - - del dictvalue[0] - - if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.keys()): - - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) - - else: - - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) - - - - if self.resultsName: - - return [ tokenlist ] - - else: - - return tokenlist - - - - - -class Suppress(TokenConverter): - - """Converter for ignoring the results of a parsed expression.""" - - def postParse( self, instring, loc, tokenlist ): - - return [] - - - - def suppress( self ): - - return self - - - - - -class OnlyOnce(object): - - """Wrapper for parse actions, to ensure they are only called once.""" - - def __init__(self, methodCall): - - self.callable = ParserElement._normalizeParseActionArgs(methodCall) - - self.called = False - - def __call__(self,s,l,t): - - if not self.called: - - results = self.callable(s,l,t) - - self.called = True - - return results - - raise ParseException(s,l,"") - - def reset(self): - - self.called = False - - - -def traceParseAction(f): - - """Decorator for debugging parse actions.""" - - f = ParserElement._normalizeParseActionArgs(f) - - def z(*paArgs): - - thisFunc = f.func_name - - s,l,t = paArgs[-3:] - - if len(paArgs)>3: - - thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc - - sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) - - try: - - ret = f(*paArgs) - - except Exception: - - exc = sys.exc_info()[1] - - sys.stderr.write( "<", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) - - try: - - if len(symbols)==len("".join(symbols)): - - return Regex( "[%s]" % "".join( [ _escapeRegexRangeChars(sym) for sym in symbols] ) ) - - else: - - return Regex( "|".join( [ re.escape(sym) for sym in symbols] ) ) - - except: - - warnings.warn("Exception creating Regex for oneOf, building MatchFirst", - - SyntaxWarning, stacklevel=2) - - - - - - # last resort, just use MatchFirst - - return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) - - - -def dictOf( key, value ): - - """Helper to easily and clearly define a dictionary by specifying the respective patterns - - for the key and value. Takes care of defining the Dict, ZeroOrMore, and Group tokens - - in the proper order. The key pattern can include delimiting markers or punctuation, - - as long as they are suppressed, thereby leaving the significant key text. The value - - pattern can include named results, so that the Dict results can include named token - - fields. - - """ - - return Dict( ZeroOrMore( Group ( key + value ) ) ) - - - -def originalTextFor(expr, asString=True): - - """Helper to return the original, untokenized text for a given expression. Useful to - - restore the parsed fields of an HTML start tag into the raw tag text itself, or to - - revert separate tokens with intervening whitespace back to the original matching - - input text. Simpler to use than the parse action keepOriginalText, and does not - - require the inspect module to chase up the call stack. By default, returns a - - string containing the original parsed text. - - - - If the optional asString argument is passed as False, then the return value is a - - ParseResults containing any results names that were originally matched, and a - - single token containing the original matched text from the input string. So if - - the expression passed to originalTextFor contains expressions with defined - - results names, you must set asString to False if you want to preserve those - - results name values.""" - - locMarker = Empty().setParseAction(lambda s,loc,t: loc) - - matchExpr = locMarker("_original_start") + expr + locMarker("_original_end") - - if asString: - - extractText = lambda s,l,t: s[t._original_start:t._original_end] - - else: - - def extractText(s,l,t): - - del t[:] - - t.insert(0, s[t._original_start:t._original_end]) - - del t["_original_start"] - - del t["_original_end"] - - matchExpr.setParseAction(extractText) - - return matchExpr - - - -# convenience constants for positional expressions - -empty = Empty().setName("empty") - -lineStart = LineStart().setName("lineStart") - -lineEnd = LineEnd().setName("lineEnd") - -stringStart = StringStart().setName("stringStart") - -stringEnd = StringEnd().setName("stringEnd") - - - -_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) - -_printables_less_backslash = "".join([ c for c in printables if c not in r"\]" ]) - -_escapedHexChar = Combine( Suppress(_bslash + "0x") + Word(hexnums) ).setParseAction(lambda s,l,t:unichr(int(t[0],16))) - -_escapedOctChar = Combine( Suppress(_bslash) + Word("0","01234567") ).setParseAction(lambda s,l,t:unichr(int(t[0],8))) - -_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(_printables_less_backslash,exact=1) - -_charRange = Group(_singleChar + Suppress("-") + _singleChar) - -_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" - - - -_expanded = lambda p: (isinstance(p,ParseResults) and ''.join([ unichr(c) for c in range(ord(p[0]),ord(p[1])+1) ]) or p) - - - -def srange(s): - - r"""Helper to easily define string ranges for use in Word construction. Borrows - - syntax from regexp '[]' string range definitions:: - - srange("[0-9]") -> "0123456789" - - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - - The input string must be enclosed in []'s, and the returned string is the expanded - - character set joined into a single string. - - The values enclosed in the []'s may be:: - - a single character - - an escaped character with a leading backslash (such as \- or \]) - - an escaped hex character with a leading '\0x' (\0x21, which is a '!' character) - - an escaped octal character with a leading '\0' (\041, which is a '!' character) - - a range of any of the above, separated by a dash ('a-z', etc.) - - any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) - - """ - - try: - - return "".join([_expanded(part) for part in _reBracketExpr.parseString(s).body]) - - except: - - return "" - - - -def matchOnlyAtCol(n): - - """Helper method for defining parse actions that require matching at a specific - - column in the input text. - - """ - - def verifyCol(strg,locn,toks): - - if col(locn,strg) != n: - - raise ParseException(strg,locn,"matched token not at column %d" % n) - - return verifyCol - - - -def replaceWith(replStr): - - """Helper method for common parse actions that simply return a literal value. Especially - - useful when used with transformString(). - - """ - - def _replFunc(*args): - - return [replStr] - - return _replFunc - - - -def removeQuotes(s,l,t): - - """Helper parse action for removing quotation marks from parsed quoted strings. - - To use, add this parse action to quoted string using:: - - quotedString.setParseAction( removeQuotes ) - - """ - - return t[0][1:-1] - - - -def upcaseTokens(s,l,t): - - """Helper parse action to convert tokens to upper case.""" - - return [ tt.upper() for tt in map(_ustr,t) ] - - - -def downcaseTokens(s,l,t): - - """Helper parse action to convert tokens to lower case.""" - - return [ tt.lower() for tt in map(_ustr,t) ] - - - -def keepOriginalText(s,startLoc,t): - - """Helper parse action to preserve original parsed text, - - overriding any nested parse actions.""" - - try: - - endloc = getTokensEndLoc() - - except ParseException: - - raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") - - del t[:] - - t += ParseResults(s[startLoc:endloc]) - - return t - - - -def getTokensEndLoc(): - - """Method to be called from within a parse action to determine the end - - location of the parsed tokens.""" - - import inspect - - fstack = inspect.stack() - - try: - - # search up the stack (through intervening argument normalizers) for correct calling routine - - for f in fstack[2:]: - - if f[3] == "_parseNoCache": - - endloc = f[0].f_locals["loc"] - - return endloc - - else: - - raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") - - finally: - - del fstack - - - -def _makeTags(tagStr, xml): - - """Internal helper to construct opening and closing tag expressions, given a tag name""" - - if isinstance(tagStr,basestring): - - resname = tagStr - - tagStr = Keyword(tagStr, caseless=not xml) - - else: - - resname = tagStr.name - - - - tagAttrName = Word(alphas,alphanums+"_-:") - - if (xml): - - tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) - - openTag = Suppress("<") + tagStr + \ - - Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ - - Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") - - else: - - printablesLessRAbrack = "".join( [ c for c in printables if c not in ">" ] ) - - tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) - - openTag = Suppress("<") + tagStr + \ - - Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ - - Optional( Suppress("=") + tagAttrValue ) ))) + \ - - Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") - - closeTag = Combine(_L("") - - - - openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) - - closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % tagStr) - - - - return openTag, closeTag - - - -def makeHTMLTags(tagStr): - - """Helper to construct opening and closing tag expressions for HTML, given a tag name""" - - return _makeTags( tagStr, False ) - - - -def makeXMLTags(tagStr): - - """Helper to construct opening and closing tag expressions for XML, given a tag name""" - - return _makeTags( tagStr, True ) - - - -def withAttribute(*args,**attrDict): - - """Helper to create a validating parse action to be used with start tags created - - with makeXMLTags or makeHTMLTags. Use withAttribute to qualify a starting tag - - with a required attribute value, to avoid false matches on common tags such as - - or
. - - - - Call withAttribute with a series of attribute names and values. Specify the list - - of filter attributes names and values as: - - - keyword arguments, as in (class="Customer",align="right"), or - - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - - For attribute names with a namespace prefix, you must use the second form. Attribute - - names are matched insensitive to upper/lower case. - - - - To verify that the attribute exists, but without specifying a value, pass - - withAttribute.ANY_VALUE as the value. - - """ - - if args: - - attrs = args[:] - - else: - - attrs = attrDict.items() - - attrs = [(k,v) for k,v in attrs] - - def pa(s,l,tokens): - - for attrName,attrValue in attrs: - - if attrName not in tokens: - - raise ParseException(s,l,"no matching attribute " + attrName) - - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - - (attrName, tokens[attrName], attrValue)) - - return pa - -withAttribute.ANY_VALUE = object() - - - -opAssoc = _Constants() - -opAssoc.LEFT = object() - -opAssoc.RIGHT = object() - - - -def operatorPrecedence( baseExpr, opList ): - - """Helper method for constructing grammars of expressions made up of - - operators working in a precedence hierarchy. Operators may be unary or - - binary, left- or right-associative. Parse actions can also be attached - - to operator expressions. - - - - Parameters: - - - baseExpr - expression representing the most basic element for the nested - - - opList - list of tuples, one for each operator precedence level in the - - expression grammar; each tuple is of the form - - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - - opExpr is the pyparsing expression for the operator; - - may also be a string, which will be converted to a Literal; - - if numTerms is 3, opExpr is a tuple of two expressions, for the - - two operators separating the 3 terms - - - numTerms is the number of terms for this operator (must - - be 1, 2, or 3) - - - rightLeftAssoc is the indicator whether the operator is - - right or left associative, using the pyparsing-defined - - constants opAssoc.RIGHT and opAssoc.LEFT. - - - parseAction is the parse action to be associated with - - expressions matching this operator expression (the - - parse action tuple member may be omitted) - - """ - - ret = Forward() - - lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') ) - - for i,operDef in enumerate(opList): - - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - - if arity == 3: - - if opExpr is None or len(opExpr) != 2: - - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - - opExpr1, opExpr2 = opExpr - - thisExpr = Forward()#.setName("expr%d" % i) - - if rightLeftAssoc == opAssoc.LEFT: - - if arity == 1: - - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - - elif arity == 2: - - if opExpr is not None: - - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - - else: - - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - - elif arity == 3: - - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - - else: - - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - elif rightLeftAssoc == opAssoc.RIGHT: - - if arity == 1: - - # try to avoid LR with this extra test - - if not isinstance(opExpr, Optional): - - opExpr = Optional(opExpr) - - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - - elif arity == 2: - - if opExpr is not None: - - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - - else: - - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - - elif arity == 3: - - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - - else: - - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - else: - - raise ValueError("operator must indicate right or left associativity") - - if pa: - - matchExpr.setParseAction( pa ) - - thisExpr << ( matchExpr | lastExpr ) - - lastExpr = thisExpr - - ret << lastExpr - - return ret - - - -dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") - -sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") - -quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") - -unicodeString = Combine(_L('u') + quotedString.copy()) - - - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString): - - """Helper method for defining nested lists enclosed in opening and closing - - delimiters ("(" and ")" are the default). - - - - Parameters: - - - opener - opening character for a nested list (default="("); can also be a pyparsing expression - - - closer - closing character for a nested list (default=")"); can also be a pyparsing expression - - - content - expression for items within the nested lists (default=None) - - - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) - - - - If an expression is not provided for the content argument, the nested - - expression will capture all whitespace-delimited content between delimiters - - as a list of separate values. - - - - Use the ignoreExpr argument to define expressions that may contain - - opening or closing characters that should not be treated as opening - - or closing characters for nesting, such as quotedString or a comment - - expression. Specify multiple expressions using an Or or MatchFirst. - - The default is quotedString, but if no expressions are to be ignored, - - then pass None for this argument. - - """ - - if opener == closer: - - raise ValueError("opening and closing strings cannot be the same") - - if content is None: - - if isinstance(opener,basestring) and isinstance(closer,basestring): - - if len(opener) == 1 and len(closer)==1: - - if ignoreExpr is not None: - - content = (Combine(OneOrMore(~ignoreExpr + - - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - - ).setParseAction(lambda t:t[0].strip())) - - else: - - content = (empty+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - - ).setParseAction(lambda t:t[0].strip())) - - else: - - if ignoreExpr is not None: - - content = (Combine(OneOrMore(~ignoreExpr + - - ~Literal(opener) + ~Literal(closer) + - - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - - ).setParseAction(lambda t:t[0].strip())) - - else: - - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - - ).setParseAction(lambda t:t[0].strip())) - - else: - - raise ValueError("opening and closing arguments must be strings if no content expression is given") - - ret = Forward() - - if ignoreExpr is not None: - - ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - - else: - - ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - - return ret - - - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - - """Helper method for defining space-delimited indentation blocks, such as - - those used to define block statements in Python source code. - - - - Parameters: - - - blockStatementExpr - expression defining syntax of statement that - - is repeated within the indented block - - - indentStack - list created by caller to manage indentation stack - - (multiple statementWithIndentedBlock expressions within a single grammar - - should share a common indentStack) - - - indent - boolean indicating whether block must be indented beyond the - - the current level; set to False for block of left-most statements - - (default=True) - - - - A valid block must contain at least one blockStatement. - - """ - - def checkPeerIndent(s,l,t): - - if l >= len(s): return - - curCol = col(l,s) - - if curCol != indentStack[-1]: - - if curCol > indentStack[-1]: - - raise ParseFatalException(s,l,"illegal nesting") - - raise ParseException(s,l,"not a peer entry") - - - - def checkSubIndent(s,l,t): - - curCol = col(l,s) - - if curCol > indentStack[-1]: - - indentStack.append( curCol ) - - else: - - raise ParseException(s,l,"not a subentry") - - - - def checkUnindent(s,l,t): - - if l >= len(s): return - - curCol = col(l,s) - - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - - raise ParseException(s,l,"not an unindent") - - indentStack.pop() - - - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - - INDENT = Empty() + Empty().setParseAction(checkSubIndent) - - PEER = Empty().setParseAction(checkPeerIndent) - - UNDENT = Empty().setParseAction(checkUnindent) - - if indent: - - smExpr = Group( Optional(NL) + - - FollowedBy(blockStatementExpr) + - - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - - else: - - smExpr = Group( Optional(NL) + - - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - - blockStatementExpr.ignore(_bslash + LineEnd()) - - return smExpr - - - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") - -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - - - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) - -commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() - -_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) - -replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None - - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available - -cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") - - - -htmlComment = Regex(r"") - -restOfLine = Regex(r".*").leaveWhitespace() - -dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") - -cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?" + str(tokenlist)) - - print ("tokens = " + str(tokens)) - - print ("tokens.columns = " + str(tokens.columns)) - - print ("tokens.tables = " + str(tokens.tables)) - - print (tokens.asXML("SQL",True)) - - except ParseBaseException: - - err = sys.exc_info()[1] - - print (teststring + "->") - - print (err.line) - - print (" "*(err.column-1) + "^") - - print (err) - - print() - - - - selectToken = CaselessLiteral( "select" ) - - fromToken = CaselessLiteral( "from" ) - - - - ident = Word( alphas, alphanums + "_$" ) - - columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - - columnNameList = Group( delimitedList( columnName ) )#.setName("columns") - - tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - - tableNameList = Group( delimitedList( tableName ) )#.setName("tables") - - simpleSQL = ( selectToken + \ - - ( '*' | columnNameList ).setResultsName( "columns" ) + \ - - fromToken + \ - - tableNameList.setResultsName( "tables" ) ) - - - - test( "SELECT * from XYZZY, ABC" ) - - test( "select * from SYS.XYZZY" ) - - test( "Select A from Sys.dual" ) - - test( "Select AA,BB,CC from Sys.dual" ) - - test( "Select A, B, C from Sys.dual" ) - - test( "Select A, B, C from Sys.dual" ) - - test( "Xelect A, B, C from Sys.dual" ) - - test( "Select A, B, C frox Sys.dual" ) - - test( "Select" ) - - test( "Select ^^^ frox Sys.dual" ) - - test( "Select A, B, C from Sys.dual, Table2 " ) - +# module pyparsing.py +# +# Copyright (c) 2003-2009 Paul T. McGuire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +#from __future__ import generators + +__doc__ = \ +""" +pyparsing module - Classes and methods to define and execute parsing grammars + +The pyparsing module is an alternative approach to creating and executing simple grammars, +vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you +don't need to learn a new syntax for defining grammars or matching expressions - the parsing module +provides a library of classes that you use to construct the grammar directly in Python. + +Here is a program to parse "Hello, World!" (or any greeting of the form ", !"):: + + from pyparsing_py3 import Word, alphas + + # define grammar of a greeting + greet = Word( alphas ) + "," + Word( alphas ) + "!" + + hello = "Hello, World!" + print hello, "->", greet.parseString( hello ) + +The program outputs the following:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + +The Python representation of the grammar is quite readable, owing to the self-explanatory +class names, and the use of '+', '|' and '^' operators. + +The parsed results returned from parseString() can be accessed as a nested list, a dictionary, or an +object with named attributes. + +The pyparsing module handles some of the problems that are typically vexing when writing text parsers: + - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) + - quoted strings + - embedded comments +""" + +__version__ = "1.5.2.Py3" +__versionTime__ = "9 April 2009 12:21" +__author__ = "Paul McGuire " + +import string +from weakref import ref as wkref +import copy +import sys +import warnings +import re +import sre_constants +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums', +'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', +'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', +'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', +'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', +'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', +'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', +'indentedBlock', 'originalTextFor', +] + +""" +Detect if we are running version 3.X and make appropriate changes +Robert A. Clark +""" +_PY3K = sys.version_info[0] > 2 +if _PY3K: + _MAX_INT = sys.maxsize + basestring = str + unichr = chr + _ustr = str + _str2dict = set + alphas = string.ascii_lowercase + string.ascii_uppercase +else: + _MAX_INT = sys.maxint + + def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj + + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) + + except UnicodeEncodeError: + # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) + # state that "The return value must be a string object". However, does a + # unicode object (being a subclass of basestring) count as a "string + # object"? + # If so, then return a unicode object: + return unicode(obj) + # Else encode it... but how? There are many choices... :) + # Replace unprintables with escape codes? + #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') + # Replace unprintables with question marks? + #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') + # ... + + def _str2dict(strg): + return dict( [(c,0) for c in strg] ) + + alphas = string.lowercase + string.uppercase + + +def _xml_escape(data): + """Escape &, <, >, ", ', etc. in a string of data.""" + + # ampersand must be replaced first + from_symbols = '&><"\'' + to_symbols = ['&'+s+';' for s in "amp gt lt quot apos".split()] + for from_,to_ in zip(from_symbols, to_symbols): + data = data.replace(from_, to_) + return data + +class _Constants(object): + pass + +nums = string.digits +hexnums = nums + "ABCDEFabcdef" +alphanums = alphas + nums +_bslash = chr(92) +printables = "".join( [ c for c in string.printable if c not in string.whitespace ] ) + +class ParseBaseException(Exception): + """base exception class for all parsing runtime exceptions""" + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, pstr, loc=0, msg=None, elem=None ): + self.loc = loc + if msg is None: + self.msg = pstr + self.pstr = "" + else: + self.msg = msg + self.pstr = pstr + self.parserElement = elem + + def __getattr__( self, aname ): + """supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + if( aname == "lineno" ): + return lineno( self.loc, self.pstr ) + elif( aname in ("col", "column") ): + return col( self.loc, self.pstr ) + elif( aname == "line" ): + return line( self.loc, self.pstr ) + else: + raise AttributeError(aname) + + def __str__( self ): + return "%s (at char %d), (line:%d, col:%d)" % \ + ( self.msg, self.loc, self.lineno, self.column ) + def __repr__( self ): + return _ustr(self) + def markInputline( self, markerString = ">!<" ): + """Extracts the exception line from the input string, and marks + the location of the exception with a special symbol. + """ + line_str = self.line + line_column = self.column - 1 + if markerString: + line_str = "".join( [line_str[:line_column], + markerString, line_str[line_column:]]) + return line_str.strip() + def __dir__(self): + return "loc msg pstr parserElement lineno col line " \ + "markInputLine __str__ __repr__".split() + +class ParseException(ParseBaseException): + """exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like ParseFatalException, but thrown internally when an + ErrorStop indicates that parsing is to stop immediately because + an unbacktrackable syntax error has been found""" + def __init__(self, pe): + super(ParseSyntaxException, self).__init__( + pe.pstr, pe.loc, pe.msg, pe.parserElement) + +#~ class ReparseException(ParseBaseException): + #~ """Experimental class - parse actions can raise this exception to cause + #~ pyparsing to reparse the input string: + #~ - with a modified input string, and/or + #~ - with a modified start location + #~ Set the values of the ReparseException in the constructor, and raise the + #~ exception in a parse action to cause pyparsing to use the new string/location. + #~ Setting the values as None causes no change to be made. + #~ """ + #~ def __init_( self, newstring, restartLoc ): + #~ self.newParseText = newstring + #~ self.reparseLoc = restartLoc + +class RecursiveGrammarException(Exception): + """exception thrown by validate() if the grammar could be improperly recursive""" + def __init__( self, parseElementList ): + self.parseElementTrace = parseElementList + + def __str__( self ): + return "RecursiveGrammarException: %s" % self.parseElementTrace + +class _ParseResultsWithOffset(object): + def __init__(self,p1,p2): + self.tup = (p1,p2) + def __getitem__(self,i): + return self.tup[i] + def __repr__(self): + return repr(self.tup) + def setOffset(self,i): + self.tup = (self.tup[0],i) + +class ParseResults(object): + """Structured parse results, to provide multiple means of access to the parsed data: + - as a list (len(results)) + - by list index (results[0], results[1], etc.) + - by attribute (results.) + """ + __slots__ = ( "__toklist", "__tokdict", "__doinit", "__name", "__parent", "__accumNames", "__weakref__" ) + def __new__(cls, toklist, name=None, asList=True, modal=True ): + if isinstance(toklist, cls): + return toklist + retobj = object.__new__(cls) + retobj.__doinit = True + return retobj + + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, toklist, name=None, asList=True, modal=True ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + if isinstance(toklist, list): + self.__toklist = toklist[:] + else: + self.__toklist = [toklist] + self.__tokdict = dict() + + if name: + if not modal: + self.__accumNames[name] = 0 + if isinstance(name,int): + name = _ustr(name) # will always return a str, but use _ustr for consistency + self.__name = name + if not toklist in (None,'',[]): + if isinstance(toklist,basestring): + toklist = [ toklist ] + if asList: + if isinstance(toklist,ParseResults): + self[name] = _ParseResultsWithOffset(toklist.copy(),0) + else: + self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) + self[name].__name = name + else: + try: + self[name] = toklist[0] + except (KeyError,TypeError,IndexError): + self[name] = toklist + + def __getitem__( self, i ): + if isinstance( i, (int,slice) ): + return self.__toklist[i] + else: + if i not in self.__accumNames: + return self.__tokdict[i][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[i] ]) + + def __setitem__( self, k, v ): + if isinstance(v,_ParseResultsWithOffset): + self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] + sub = v[0] + elif isinstance(k,int): + self.__toklist[k] = v + sub = v + else: + self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] + sub = v + if isinstance(sub,ParseResults): + sub.__parent = wkref(self) + + def __delitem__( self, i ): + if isinstance(i,(int,slice)): + mylen = len( self.__toklist ) + del self.__toklist[i] + + # convert int to slice + if isinstance(i, int): + if i < 0: + i += mylen + i = slice(i, i+1) + # get removed indices + removed = list(range(*i.indices(mylen))) + removed.reverse() + # fixup indices in token dictionary + for name in self.__tokdict: + occurrences = self.__tokdict[name] + for j in removed: + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + else: + del self.__tokdict[i] + + def __contains__( self, k ): + return k in self.__tokdict + + def __len__( self ): return len( self.__toklist ) + def __bool__(self): return len( self.__toklist ) > 0 + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( reversed(self.__toklist) ) + def keys( self ): + """Returns all named result keys.""" + return self.__tokdict.keys() + + def pop( self, index=-1 ): + """Removes and returns item at specified index (default=last). + Will work with either numeric indices or dict-key indicies.""" + ret = self[index] + del self[index] + return ret + + def get(self, key, defaultValue=None): + """Returns named result matching the given key, or if there is no + such name, then returns the given defaultValue or None if no + defaultValue is specified.""" + if key in self: + return self[key] + else: + return defaultValue + + def insert( self, index, insStr ): + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + for name in self.__tokdict: + occurrences = self.__tokdict[name] + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + + def items( self ): + """Returns all named result keys and values as a list of tuples.""" + return [(k,self[k]) for k in self.__tokdict] + + def values( self ): + """Returns all named result values.""" + return [ v[-1][0] for v in self.__tokdict.values() ] + + def __getattr__( self, name ): + if name not in self.__slots__: + if name in self.__tokdict: + if name not in self.__accumNames: + return self.__tokdict[name][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[name] ]) + else: + return "" + return None + + def __add__( self, other ): + ret = self.copy() + ret += other + return ret + + def __iadd__( self, other ): + if other.__tokdict: + offset = len(self.__toklist) + addoffset = ( lambda a: (a<0 and offset) or (a+offset) ) + otheritems = other.__tokdict.items() + otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) + for (k,vlist) in otheritems for v in vlist] + for k,v in otherdictitems: + self[k] = v + if isinstance(v[0],ParseResults): + v[0].__parent = wkref(self) + + self.__toklist += other.__toklist + self.__accumNames.update( other.__accumNames ) + del other + return self + + def __repr__( self ): + return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) + + def __str__( self ): + out = "[" + sep = "" + for i in self.__toklist: + if isinstance(i, ParseResults): + out += sep + _ustr(i) + else: + out += sep + repr(i) + sep = ", " + out += "]" + return out + + def _asStringList( self, sep='' ): + out = [] + for item in self.__toklist: + if out and sep: + out.append(sep) + if isinstance( item, ParseResults ): + out += item._asStringList() + else: + out.append( _ustr(item) ) + return out + + def asList( self ): + """Returns the parse results as a nested list of matching tokens, all converted to strings.""" + out = [] + for res in self.__toklist: + if isinstance(res,ParseResults): + out.append( res.asList() ) + else: + out.append( res ) + return out + + def asDict( self ): + """Returns the named parse results as dictionary.""" + return dict( self.items() ) + + def copy( self ): + """Returns a new copy of a ParseResults object.""" + ret = ParseResults( self.__toklist ) + ret.__tokdict = self.__tokdict.copy() + ret.__parent = self.__parent + ret.__accumNames.update( self.__accumNames ) + ret.__name = self.__name + return ret + + def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): + """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" + nl = "\n" + out = [] + namedItems = dict( [ (v[1],k) for (k,vlist) in self.__tokdict.items() + for v in vlist ] ) + nextLevelIndent = indent + " " + + # collapse out indents if formatting is not desired + if not formatted: + indent = "" + nextLevelIndent = "" + nl = "" + + selfTag = None + if doctag is not None: + selfTag = doctag + else: + if self.__name: + selfTag = self.__name + + if not selfTag: + if namedItemsOnly: + return "" + else: + selfTag = "ITEM" + + out += [ nl, indent, "<", selfTag, ">" ] + + worklist = self.__toklist + for i,res in enumerate(worklist): + if isinstance(res,ParseResults): + if i in namedItems: + out += [ res.asXML(namedItems[i], + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + out += [ res.asXML(None, + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + # individual token, see if there is a name for it + resTag = None + if i in namedItems: + resTag = namedItems[i] + if not resTag: + if namedItemsOnly: + continue + else: + resTag = "ITEM" + xmlBodyText = _xml_escape(_ustr(res)) + out += [ nl, nextLevelIndent, "<", resTag, ">", + xmlBodyText, + "" ] + + out += [ nl, indent, "" ] + return "".join(out) + + def __lookup(self,sub): + for k,vlist in self.__tokdict.items(): + for v,loc in vlist: + if sub is v: + return k + return None + + def getName(self): + """Returns the results name for this token expression.""" + if self.__name: + return self.__name + elif self.__parent: + par = self.__parent() + if par: + return par.__lookup(self) + else: + return None + elif (len(self) == 1 and + len(self.__tokdict) == 1 and + self.__tokdict.values()[0][0][1] in (0,-1)): + return self.__tokdict.keys()[0] + else: + return None + + def dump(self,indent='',depth=0): + """Diagnostic method for listing out the contents of a ParseResults. + Accepts an optional indent argument so that this string can be embedded + in a nested display of other data.""" + out = [] + out.append( indent+_ustr(self.asList()) ) + keys = self.items() + keys.sort() + for k,v in keys: + if out: + out.append('\n') + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v.keys(): + out.append( v.dump(indent,depth+1) ) + else: + out.append(_ustr(v)) + else: + out.append(_ustr(v)) + return "".join(out) + + # add support for pickle protocol + def __getstate__(self): + return ( self.__toklist, + ( self.__tokdict.copy(), + self.__parent is not None and self.__parent() or None, + self.__accumNames, + self.__name ) ) + + def __setstate__(self,state): + self.__toklist = state[0] + self.__tokdict, \ + par, \ + inAccumNames, \ + self.__name = state[1] + self.__accumNames = {} + self.__accumNames.update(inAccumNames) + if par is not None: + self.__parent = wkref(par) + else: + self.__parent = None + + def __dir__(self): + return dir(super(ParseResults,self)) + self.keys() + +def col (loc,strg): + """Returns current column within a string, counting newlines as line separators. + The first column is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}} for more information + on parsing strings containing s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return (loc} for more information + on parsing strings containing s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return strg.count("\n",0,loc) + 1 + +def line( loc, strg ): + """Returns the line of text containing loc within a string, counting newlines as line separators. + """ + lastCR = strg.rfind("\n", 0, loc) + nextCR = strg.find("\n", loc) + if nextCR > 0: + return strg[lastCR+1:nextCR] + else: + return strg[lastCR+1:] + +def _defaultStartDebugAction( instring, loc, expr ): + print ("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + +def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): + print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) + +def _defaultExceptionDebugAction( instring, loc, expr, exc ): + print ("Exception raised:" + _ustr(exc)) + +def nullDebugAction(*args): + """'Do-nothing' debug action, to suppress debugging output during parsing.""" + pass + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + + def setDefaultWhitespaceChars( chars ): + """Overrides the default whitespace chars + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars) + + def __init__( self, savelist=False ): + self.parseAction = list() + self.failAction = None + #~ self.name = "" # don't define self.name, let subclasses try/except upcall + self.strRepr = None + self.resultsName = None + self.saveAsList = savelist + self.skipWhitespace = True + self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + self.copyDefaultWhiteChars = True + self.mayReturnEmpty = False # used when checking for left-recursion + self.keepTabs = False + self.ignoreExprs = list() + self.debug = False + self.streamlined = False + self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index + self.errmsg = "" + self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) + self.debugActions = ( None, None, None ) #custom debug actions + self.re = None + self.callPreparse = True # used to avoid redundant calls to preParse + self.callDuringTry = False + + def copy( self ): + """Make a copy of this ParserElement. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element.""" + cpy = copy.copy( self ) + cpy.parseAction = self.parseAction[:] + cpy.ignoreExprs = self.ignoreExprs[:] + if self.copyDefaultWhiteChars: + cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + return cpy + + def setName( self, name ): + """Define name for this expression, for use in debugging.""" + self.name = name + self.errmsg = "Expected " + self.name + if hasattr(self,"exception"): + self.exception.msg = self.errmsg + return self + + def setResultsName( self, name, listAllMatches=False ): + """Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original ParserElement object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + """ + newself = self.copy() + newself.resultsName = name + newself.modalResults = not listAllMatches + return newself + + def setBreak(self,breakFlag = True): + """Method to invoke the Python pdb debugger when this element is + about to be parsed. Set breakFlag to True to enable, False to + disable. + """ + if breakFlag: + _parseMethod = self._parse + def breaker(instring, loc, doActions=True, callPreParse=True): + import pdb + pdb.set_trace() + return _parseMethod( instring, loc, doActions, callPreParse ) + breaker._originalParseMethod = _parseMethod + self._parse = breaker + else: + if hasattr(self._parse,"_originalParseMethod"): + self._parse = self._parse._originalParseMethod + return self + + def _normalizeParseActionArgs( f ): + """Internal method used to decorate parse actions that take fewer than 3 arguments, + so that all parse actions can be called as f(s,l,t).""" + STAR_ARGS = 4 + + try: + restore = None + if isinstance(f,type): + restore = f + f = f.__init__ + if not _PY3K: + codeObj = f.func_code + else: + codeObj = f.code + if codeObj.co_flags & STAR_ARGS: + return f + numargs = codeObj.co_argcount + if not _PY3K: + if hasattr(f,"im_self"): + numargs -= 1 + else: + if hasattr(f,"__self__"): + numargs -= 1 + if restore: + f = restore + except AttributeError: + try: + if not _PY3K: + call_im_func_code = f.__call__.im_func.func_code + else: + call_im_func_code = f.__code__ + + # not a function, must be a callable object, get info from the + # im_func binding of its bound __call__ method + if call_im_func_code.co_flags & STAR_ARGS: + return f + numargs = call_im_func_code.co_argcount + if not _PY3K: + if hasattr(f.__call__,"im_self"): + numargs -= 1 + else: + if hasattr(f.__call__,"__self__"): + numargs -= 0 + except AttributeError: + if not _PY3K: + call_func_code = f.__call__.func_code + else: + call_func_code = f.__call__.__code__ + # not a bound method, get info directly from __call__ method + if call_func_code.co_flags & STAR_ARGS: + return f + numargs = call_func_code.co_argcount + if not _PY3K: + if hasattr(f.__call__,"im_self"): + numargs -= 1 + else: + if hasattr(f.__call__,"__self__"): + numargs -= 1 + + + #~ print ("adding function %s with %d args" % (f.func_name,numargs)) + if numargs == 3: + return f + else: + if numargs > 3: + def tmp(s,l,t): + return f(f.__call__.__self__, s,l,t) + if numargs == 2: + def tmp(s,l,t): + return f(l,t) + elif numargs == 1: + def tmp(s,l,t): + return f(t) + else: #~ numargs == 0: + def tmp(s,l,t): + return f() + try: + tmp.__name__ = f.__name__ + except (AttributeError,TypeError): + # no need for special handling if attribute doesnt exist + pass + try: + tmp.__doc__ = f.__doc__ + except (AttributeError,TypeError): + # no need for special handling if attribute doesnt exist + pass + try: + tmp.__dict__.update(f.__dict__) + except (AttributeError,TypeError): + # no need for special handling if attribute doesnt exist + pass + return tmp + _normalizeParseActionArgs = staticmethod(_normalizeParseActionArgs) + + def setParseAction( self, *fns, **kwargs ): + """Define action to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as fn(s,loc,toks), + fn(loc,toks), fn(toks), or just fn(), where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a ParseResults object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}} for more information + on parsing strings containing s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + self.parseAction = list(map(self._normalizeParseActionArgs, list(fns))) + self.callDuringTry = ("callDuringTry" in kwargs and kwargs["callDuringTry"]) + return self + + def addParseAction( self, *fns, **kwargs ): + """Add parse action to expression's list of parse actions. See L{I{setParseAction}}.""" + self.parseAction += list(map(self._normalizeParseActionArgs, list(fns))) + self.callDuringTry = self.callDuringTry or ("callDuringTry" in kwargs and kwargs["callDuringTry"]) + return self + + def setFailAction( self, fn ): + """Define action to perform if parsing fails at this expression. + Fail acton fn is a callable function that takes the arguments + fn(s,loc,expr,err) where: + - s = string being parsed + - loc = location where expression match was attempted and failed + - expr = the parse expression that failed + - err = the exception thrown + The function returns no value. It may throw ParseFatalException + if it is desired to stop parsing immediately.""" + self.failAction = fn + return self + + def _skipIgnorables( self, instring, loc ): + exprsFound = True + while exprsFound: + exprsFound = False + for e in self.ignoreExprs: + try: + while 1: + loc,dummy = e._parse( instring, loc ) + exprsFound = True + except ParseException: + pass + return loc + + def preParse( self, instring, loc ): + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + + if self.skipWhitespace: + wt = self.whiteChars + instrlen = len(instring) + while loc < instrlen and instring[loc] in wt: + loc += 1 + + return loc + + def parseImpl( self, instring, loc, doActions=True ): + return loc, [] + + def postParse( self, instring, loc, tokenlist ): + return tokenlist + + #~ @profile + def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): + debugging = ( self.debug ) #and doActions ) + + if debugging or self.failAction: + #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + if (self.debugActions[0] ): + self.debugActions[0]( instring, loc, self ) + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = loc + try: + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + except ParseBaseException: + #~ print ("Exception raised:", err) + err = None + if self.debugActions[2]: + err = sys.exc_info()[1] + self.debugActions[2]( instring, tokensStart, self, err ) + if self.failAction: + if err is None: + err = sys.exc_info()[1] + self.failAction( instring, tokensStart, self, err ) + raise + else: + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = loc + if self.mayIndexError or loc >= len(instring): + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + else: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + + tokens = self.postParse( instring, loc, tokens ) + + retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) + if self.parseAction and (doActions or self.callDuringTry): + if debugging: + try: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + except ParseBaseException: + #~ print "Exception raised in user parse action:", err + if (self.debugActions[2] ): + err = sys.exc_info()[1] + self.debugActions[2]( instring, tokensStart, self, err ) + raise + else: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + + if debugging: + #~ print ("Matched",self,"->",retTokens.asList()) + if (self.debugActions[1] ): + self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) + + return loc, retTokens + + def tryParse( self, instring, loc ): + try: + return self._parse( instring, loc, doActions=False )[0] + except ParseFatalException: + raise ParseException( instring, loc, self.errmsg, self) + + # this method gets repeatedly called during backtracking with the same arguments - + # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression + def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): + lookup = (self,instring,loc,callPreParse,doActions) + if lookup in ParserElement._exprArgCache: + value = ParserElement._exprArgCache[ lookup ] + if isinstance(value,Exception): + raise value + return value + else: + try: + value = self._parseNoCache( instring, loc, doActions, callPreParse ) + ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) + return value + except ParseBaseException: + pe = sys.exc_info()[1] + ParserElement._exprArgCache[ lookup ] = pe + raise + + _parse = _parseNoCache + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + _exprArgCache = {} + def resetCache(): + ParserElement._exprArgCache.clear() + resetCache = staticmethod(resetCache) + + _packratEnabled = False + def enablePackrat(): + """Enables "packrat" parsing, which adds memoizing to the parsing logic. + Repeated parse attempts at the same string location (which happens + often in many complex grammars) can immediately return a cached value, + instead of re-executing parsing/validating code. Memoizing is done of + both valid results and parsing exceptions. + + This speedup may break existing programs that use parse actions that + have side-effects. For this reason, packrat parsing is disabled when + you first import pyparsing_py3 as pyparsing. To activate the packrat feature, your + program must call the class method ParserElement.enablePackrat(). If + your program uses psyco to "compile as you go", you must call + enablePackrat before calling psyco.full(). If you do not do this, + Python will crash. For best results, call enablePackrat() immediately + after importing pyparsing. + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + ParserElement._parse = ParserElement._parseCache + enablePackrat = staticmethod(enablePackrat) + + def parseString( self, instring, parseAll=False ): + """Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set parseAll to True (equivalent to ending + the grammar with StringEnd()). + + Note: parseString implicitly calls expandtabs() on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the loc argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling parseWithTabs on your grammar before calling parseString + (see L{I{parseWithTabs}}) + - define your parse action using the full (s,loc,toks) signature, and + reference the input string using the parse action's s argument + - explictly expand the tabs in your input string before calling + parseString + """ + ParserElement.resetCache() + if not self.streamlined: + self.streamline() + #~ self.saveAsList = True + for e in self.ignoreExprs: + e.streamline() + if not self.keepTabs: + instring = instring.expandtabs() + try: + loc, tokens = self._parse( instring, 0 ) + if parseAll: + loc = self.preParse( instring, loc ) + StringEnd()._parse( instring, loc ) + except ParseBaseException: + exc = sys.exc_info()[1] + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + else: + return tokens + + def scanString( self, instring, maxMatches=_MAX_INT ): + """Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + maxMatches argument, to clip scanning after 'n' matches are found. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}} for more information on parsing + strings with embedded tabs.""" + if not self.streamlined: + self.streamline() + for e in self.ignoreExprs: + e.streamline() + + if not self.keepTabs: + instring = _ustr(instring).expandtabs() + instrlen = len(instring) + loc = 0 + preparseFn = self.preParse + parseFn = self._parse + ParserElement.resetCache() + matches = 0 + try: + while loc <= instrlen and matches < maxMatches: + try: + preloc = preparseFn( instring, loc ) + nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) + except ParseException: + loc = preloc+1 + else: + if nextLoc > loc: + matches += 1 + yield tokens, preloc, nextLoc + loc = nextLoc + else: + loc = preloc+1 + except ParseBaseException: + pe = sys.exc_info()[1] + raise pe + + def transformString( self, instring ): + """Extension to scanString, to modify matching text with modified tokens that may + be returned from a parse action. To use transformString, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking transformString() on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. transformString() returns the resulting transformed string.""" + out = [] + lastE = 0 + # force preservation of s, to minimize unwanted transformation of string, and to + # keep string locs straight between transformString and scanString + self.keepTabs = True + try: + for t,s,e in self.scanString( instring ): + out.append( instring[lastE:s] ) + if t: + if isinstance(t,ParseResults): + out += t.asList() + elif isinstance(t,list): + out += t + else: + out.append(t) + lastE = e + out.append(instring[lastE:]) + return "".join(map(_ustr,out)) + except ParseBaseException: + pe = sys.exc_info()[1] + raise pe + + def searchString( self, instring, maxMatches=_MAX_INT ): + """Another extension to scanString, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + maxMatches argument, to clip searching after 'n' matches are found. + """ + try: + return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) + except ParseBaseException: + pe = sys.exc_info()[1] + raise pe + + def __add__(self, other ): + """Implementation of + operator - returns And""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, other ] ) + + def __radd__(self, other ): + """Implementation of + operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other + self + + def __sub__(self, other): + """Implementation of - operator, returns And with error stop""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, And._ErrorStop(), other ] ) + + def __rsub__(self, other ): + """Implementation of - operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other - self + + def __mul__(self,other): + if isinstance(other,int): + minElements, optElements = other,0 + elif isinstance(other,tuple): + other = (other + (None, None))[:2] + if other[0] is None: + other = (0, other[1]) + if isinstance(other[0],int) and other[1] is None: + if other[0] == 0: + return ZeroOrMore(self) + if other[0] == 1: + return OneOrMore(self) + else: + return self*other[0] + ZeroOrMore(self) + elif isinstance(other[0],int) and isinstance(other[1],int): + minElements, optElements = other + optElements -= minElements + else: + raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) + else: + raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) + + if minElements < 0: + raise ValueError("cannot multiply ParserElement by negative value") + if optElements < 0: + raise ValueError("second tuple value must be greater or equal to first tuple value") + if minElements == optElements == 0: + raise ValueError("cannot multiply ParserElement by 0 or (0,0)") + + if (optElements): + def makeOptionalList(n): + if n>1: + return Optional(self + makeOptionalList(n-1)) + else: + return Optional(self) + if minElements: + if minElements == 1: + ret = self + makeOptionalList(optElements) + else: + ret = And([self]*minElements) + makeOptionalList(optElements) + else: + ret = makeOptionalList(optElements) + else: + if minElements == 1: + ret = self + else: + ret = And([self]*minElements) + return ret + + def __rmul__(self, other): + return self.__mul__(other) + + def __or__(self, other ): + """Implementation of | operator - returns MatchFirst""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return MatchFirst( [ self, other ] ) + + def __ror__(self, other ): + """Implementation of | operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other | self + + def __xor__(self, other ): + """Implementation of ^ operator - returns Or""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Or( [ self, other ] ) + + def __rxor__(self, other ): + """Implementation of ^ operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other ^ self + + def __and__(self, other ): + """Implementation of & operator - returns Each""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Each( [ self, other ] ) + + def __rand__(self, other ): + """Implementation of & operator when left operand is not a ParserElement""" + if isinstance( other, basestring ): + other = Literal( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other & self + + def __invert__( self ): + """Implementation of ~ operator - returns NotAny""" + return NotAny( self ) + + def __call__(self, name): + """Shortcut for setResultsName, with listAllMatches=default:: + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + could be written as:: + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + return self.setResultsName(name) + + def suppress( self ): + """Suppresses the output of this ParserElement; useful to keep punctuation from + cluttering up returned output. + """ + return Suppress( self ) + + def leaveWhitespace( self ): + """Disables the skipping of whitespace before matching the characters in the + ParserElement's defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + self.skipWhitespace = False + return self + + def setWhitespaceChars( self, chars ): + """Overrides the default whitespace chars + """ + self.skipWhitespace = True + self.whiteChars = chars + self.copyDefaultWhiteChars = False + return self + + def parseWithTabs( self ): + """Overrides default behavior to expand s to spaces before parsing the input string. + Must be called before parseString when the input grammar contains elements that + match characters.""" + self.keepTabs = True + return self + + def ignore( self, other ): + """Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + """ + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append( other ) + else: + self.ignoreExprs.append( Suppress( other ) ) + return self + + def setDebugActions( self, startAction, successAction, exceptionAction ): + """Enable display of debugging messages while doing pattern matching.""" + self.debugActions = (startAction or _defaultStartDebugAction, + successAction or _defaultSuccessDebugAction, + exceptionAction or _defaultExceptionDebugAction) + self.debug = True + return self + + def setDebug( self, flag=True ): + """Enable display of debugging messages while doing pattern matching. + Set flag to True to enable, False to disable.""" + if flag: + self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) + else: + self.debug = False + return self + + def __str__( self ): + return self.name + + def __repr__( self ): + return _ustr(self) + + def streamline( self ): + self.streamlined = True + self.strRepr = None + return self + + def checkRecursion( self, parseElementList ): + pass + + def validate( self, validateTrace=[] ): + """Check defined expressions for valid structure, check for infinite recursive definitions.""" + self.checkRecursion( [] ) + + def parseFile( self, file_or_filename, parseAll=False ): + """Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. + """ + try: + file_contents = file_or_filename.read() + except AttributeError: + f = open(file_or_filename, "rb") + file_contents = f.read() + f.close() + try: + return self.parseString(file_contents, parseAll) + except ParseBaseException: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + exc = sys.exc_info()[1] + raise exc + + def getException(self): + return ParseException("",0,self.errmsg,self) + + def __getattr__(self,aname): + if aname == "myException": + self.myException = ret = self.getException(); + return ret; + else: + raise AttributeError("no such attribute " + aname) + + def __eq__(self,other): + if isinstance(other, ParserElement): + return self is other or self.__dict__ == other.__dict__ + elif isinstance(other, basestring): + try: + self.parseString(_ustr(other), parseAll=True) + return True + except ParseBaseException: + return False + else: + return super(ParserElement,self)==other + + def __ne__(self,other): + return not (self == other) + + def __hash__(self): + return hash(id(self)) + + def __req__(self,other): + return self == other + + def __rne__(self,other): + return not (self == other) + + +class Token(ParserElement): + """Abstract ParserElement subclass, for defining atomic matching patterns.""" + def __init__( self ): + super(Token,self).__init__( savelist=False ) + #self.myException = ParseException("",0,"",self) + + def setName(self, name): + s = super(Token,self).setName(name) + self.errmsg = "Expected " + self.name + #s.myException.msg = self.errmsg + return s + + +class Empty(Token): + """An empty token, will always match.""" + def __init__( self ): + super(Empty,self).__init__() + self.name = "Empty" + self.mayReturnEmpty = True + self.mayIndexError = False + + +class NoMatch(Token): + """A token that will never match.""" + def __init__( self ): + super(NoMatch,self).__init__() + self.name = "NoMatch" + self.mayReturnEmpty = True + self.mayIndexError = False + self.errmsg = "Unmatchable token" + #self.myException.msg = self.errmsg + + def parseImpl( self, instring, loc, doActions=True ): + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + +class Literal(Token): + """Token to exactly match a specified string.""" + def __init__( self, matchString ): + super(Literal,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Literal; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.__class__ = Empty + self.name = '"%s"' % _ustr(self.match) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + #self.myException.msg = self.errmsg + self.mayIndexError = False + + # Performance tuning: this routine gets called a *lot* + # if this is a single character match string and the first character matches, + # short-circuit as quickly as possible, and avoid calling startswith + #~ @profile + def parseImpl( self, instring, loc, doActions=True ): + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) ): + return loc+self.matchLen, self.match + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc +_L = Literal + +class Keyword(Token): + """Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with Literal:: + Literal("if") will match the leading 'if' in 'ifAndOnlyIf'. + Keyword("if") will not; it will only match the leading 'if in 'if x=1', or 'if(y==2)' + Accepts two optional constructor arguments in addition to the keyword string: + identChars is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$"; caseless allows case-insensitive + matching, default is False. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): + super(Keyword,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Keyword; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.name = '"%s"' % self.match + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.caseless = caseless + if caseless: + self.caselessmatch = matchString.upper() + identChars = identChars.upper() + self.identChars = _str2dict(identChars) + + def parseImpl( self, instring, loc, doActions=True ): + if self.caseless: + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and + (loc == 0 or instring[loc-1].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + else: + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and + (loc == 0 or instring[loc-1] not in self.identChars) ): + return loc+self.matchLen, self.match + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + def copy(self): + c = super(Keyword,self).copy() + c.identChars = Keyword.DEFAULT_KEYWORD_CHARS + return c + + def setDefaultKeywordChars( chars ): + """Overrides the default Keyword chars + """ + Keyword.DEFAULT_KEYWORD_CHARS = chars + setDefaultKeywordChars = staticmethod(setDefaultKeywordChars) + +class CaselessLiteral(Literal): + """Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + """ + def __init__( self, matchString ): + super(CaselessLiteral,self).__init__( matchString.upper() ) + # Preserve the defining literal. + self.returnString = matchString + self.name = "'%s'" % self.returnString + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + + def parseImpl( self, instring, loc, doActions=True ): + if instring[ loc:loc+self.matchLen ].upper() == self.match: + return loc+self.matchLen, self.returnString + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class CaselessKeyword(Keyword): + def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + + def parseImpl( self, instring, loc, doActions=True ): + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class Word(Token): + """Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for min is 1 (a + minimum value < 1 is not valid); the default values for max and exact + are 0, meaning no maximum or exact length restriction. + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False ): + super(Word,self).__init__() + self.initCharsOrig = initChars + self.initChars = _str2dict(initChars) + if bodyChars : + self.bodyCharsOrig = bodyChars + self.bodyChars = _str2dict(bodyChars) + else: + self.bodyCharsOrig = initChars + self.bodyChars = _str2dict(initChars) + + self.maxSpecified = max > 0 + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.asKeyword = asKeyword + + if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): + if self.bodyCharsOrig == self.initCharsOrig: + self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) + elif len(self.bodyCharsOrig) == 1: + self.reString = "%s[%s]*" % \ + (re.escape(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + else: + self.reString = "[%s][%s]*" % \ + (_escapeRegexRangeChars(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + if self.asKeyword: + self.reString = r"\b"+self.reString+r"\b" + try: + self.re = re.compile( self.reString ) + except: + self.re = None + + def parseImpl( self, instring, loc, doActions=True ): + if self.re: + result = self.re.match(instring,loc) + if not result: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + loc = result.end() + return loc,result.group() + + if not(instring[ loc ] in self.initChars): + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + start = loc + loc += 1 + instrlen = len(instring) + bodychars = self.bodyChars + maxloc = start + self.maxLen + maxloc = min( maxloc, instrlen ) + while loc < maxloc and instring[loc] in bodychars: + loc += 1 + + throwException = False + if loc - start < self.minLen: + throwException = True + if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + if self.asKeyword: + if (start>0 and instring[start-1] in bodychars) or (loc4: + return s[:4]+"..." + else: + return s + + if ( self.initCharsOrig != self.bodyCharsOrig ): + self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) + else: + self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) + + return self.strRepr + + +class Regex(Token): + """Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + """ + def __init__( self, pattern, flags=0): + """The parameters pattern and flags are passed to the re.compile() function as-is. See the Python re module for an explanation of the acceptable patterns and flags.""" + super(Regex,self).__init__() + + if len(pattern) == 0: + warnings.warn("null string passed to Regex; use Empty() instead", + SyntaxWarning, stacklevel=2) + + self.pattern = pattern + self.flags = flags + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = self.re.match(instring,loc) + if not result: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + loc = result.end() + d = result.groupdict() + ret = ParseResults(result.group()) + if d: + for k in d: + ret[k] = d[k] + return loc,ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + """Token for matching strings that are delimited by quoting characters. + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): + """ + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=None) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) + - multiline - boolean indicating whether quotes can span multiple lines (default=False) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar) + """ + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if len(quoteChar) == 0: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if len(endQuoteChar) == 0: + warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + self.quoteChar = quoteChar + self.quoteCharLen = len(quoteChar) + self.firstQuoteChar = quoteChar[0] + self.endQuoteChar = endQuoteChar + self.endQuoteCharLen = len(endQuoteChar) + self.escChar = escChar + self.escQuote = escQuote + self.unquoteResults = unquoteResults + + if multiline: + self.flags = re.MULTILINE | re.DOTALL + self.pattern = r'%s(?:[^%s%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + else: + self.flags = 0 + self.pattern = r'%s(?:[^%s\n\r%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + if len(self.endQuoteChar) > 1: + self.pattern += ( + '|(?:' + ')|(?:'.join(["%s[^%s]" % (re.escape(self.endQuoteChar[:i]), + _escapeRegexRangeChars(self.endQuoteChar[i])) + for i in range(len(self.endQuoteChar)-1,0,-1)]) + ')' + ) + if escQuote: + self.pattern += (r'|(?:%s)' % re.escape(escQuote)) + if escChar: + self.pattern += (r'|(?:%s.)' % re.escape(escChar)) + self.escCharReplacePattern = re.escape(self.escChar)+"(.)" + self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None + if not result: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + loc = result.end() + ret = result.group() + + if self.unquoteResults: + + # strip off quotes + ret = ret[self.quoteCharLen:-self.endQuoteCharLen] + + if isinstance(ret,basestring): + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) + + # replace escaped quotes + if self.escQuote: + ret = ret.replace(self.escQuote, self.endQuoteChar) + + return loc, ret + + def __str__( self ): + try: + return super(QuotedString,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) + + return self.strRepr + + +class CharsNotIn(Token): + """Token for matching words composed of characters *not* in a given set. + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for min is 1 (a + minimum value < 1 is not valid); the default values for max and exact + are 0, meaning no maximum or exact length restriction. + """ + def __init__( self, notChars, min=1, max=0, exact=0 ): + super(CharsNotIn,self).__init__() + self.skipWhitespace = False + self.notChars = notChars + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = ( self.minLen == 0 ) + #self.myException.msg = self.errmsg + self.mayIndexError = False + + def parseImpl( self, instring, loc, doActions=True ): + if instring[loc] in self.notChars: + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + start = loc + loc += 1 + notchars = self.notChars + maxlen = min( start+self.maxLen, len(instring) ) + while loc < maxlen and \ + (instring[loc] not in notchars): + loc += 1 + + if loc - start < self.minLen: + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(CharsNotIn, self).__str__() + except: + pass + + if self.strRepr is None: + if len(self.notChars) > 4: + self.strRepr = "!W:(%s...)" % self.notChars[:4] + else: + self.strRepr = "!W:(%s)" % self.notChars + + return self.strRepr + +class White(Token): + """Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is " \\t\\r\\n". Also takes optional min, max, and exact arguments, + as defined for the Word class.""" + whiteStrs = { + " " : "", + "\t": "", + "\n": "", + "\r": "", + "\f": "", + } + def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): + super(White,self).__init__() + self.matchWhite = ws + self.setWhitespaceChars( "".join([c for c in self.whiteChars if c not in self.matchWhite]) ) + #~ self.leaveWhitespace() + self.name = ("".join([White.whiteStrs[c] for c in self.matchWhite])) + self.mayReturnEmpty = True + self.errmsg = "Expected " + self.name + #self.myException.msg = self.errmsg + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + def parseImpl( self, instring, loc, doActions=True ): + if not(instring[ loc ] in self.matchWhite): + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + start = loc + loc += 1 + maxloc = start + self.maxLen + maxloc = min( maxloc, len(instring) ) + while loc < maxloc and instring[loc] in self.matchWhite: + loc += 1 + + if loc - start < self.minLen: + #~ raise ParseException( instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + + return loc, instring[start:loc] + + +class _PositionToken(Token): + def __init__( self ): + super(_PositionToken,self).__init__() + self.name=self.__class__.__name__ + self.mayReturnEmpty = True + self.mayIndexError = False + +class GoToColumn(_PositionToken): + """Token to advance to a specific column of input text; useful for tabular report scraping.""" + def __init__( self, colno ): + super(GoToColumn,self).__init__() + self.col = colno + + def preParse( self, instring, loc ): + if col(loc,instring) != self.col: + instrlen = len(instring) + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + thiscol = col( loc, instring ) + if thiscol > self.col: + raise ParseException( instring, loc, "Text not in expected column", self ) + newloc = loc + self.col - thiscol + ret = instring[ loc: newloc ] + return newloc, ret + +class LineStart(_PositionToken): + """Matches if current position is at the beginning of a line within the parse string""" + def __init__( self ): + super(LineStart,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected start of line" + #self.myException.msg = self.errmsg + + def preParse( self, instring, loc ): + preloc = super(LineStart,self).preParse(instring,loc) + if instring[preloc] == "\n": + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + if not( loc==0 or + (loc == self.preParse( instring, 0 )) or + (instring[loc-1] == "\n") ): #col(loc, instring) != 1: + #~ raise ParseException( instring, loc, "Expected start of line" ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + return loc, [] + +class LineEnd(_PositionToken): + """Matches if current position is at the end of a line within the parse string""" + def __init__( self ): + super(LineEnd,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected end of line" + #self.myException.msg = self.errmsg + + def parseImpl( self, instring, loc, doActions=True ): + if loc len(instring): + return loc, [] + else: + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class WordStart(_PositionToken): + """Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of wordChars + (default=printables). To emulate the \b behavior of regular expressions, + use WordStart(alphanums). WordStart will also match at the beginning of + the string being parsed, or at the beginning of a line. + """ + def __init__(self, wordChars = printables): + super(WordStart,self).__init__() + self.wordChars = _str2dict(wordChars) + self.errmsg = "Not at the start of a word" + + def parseImpl(self, instring, loc, doActions=True ): + if loc != 0: + if (instring[loc-1] in self.wordChars or + instring[loc] not in self.wordChars): + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + return loc, [] + +class WordEnd(_PositionToken): + """Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of wordChars + (default=printables). To emulate the \b behavior of regular expressions, + use WordEnd(alphanums). WordEnd will also match at the end of + the string being parsed, or at the end of a line. + """ + def __init__(self, wordChars = printables): + super(WordEnd,self).__init__() + self.wordChars = _str2dict(wordChars) + self.skipWhitespace = False + self.errmsg = "Not at the end of a word" + + def parseImpl(self, instring, loc, doActions=True ): + instrlen = len(instring) + if instrlen>0 and loc maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + else: + if loc2 > maxMatchLoc: + maxMatchLoc = loc2 + maxMatchExp = e + + if maxMatchLoc < 0: + if maxException is not None: + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + return maxMatchExp._parse( instring, loc, doActions ) + + def __ixor__(self, other ): + if isinstance( other, basestring ): + other = Literal( other ) + return self.append( other ) #Or( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ^ ".join( [ _ustr(e) for e in self.exprs ] ) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class MatchFirst(ParseExpression): + """Requires that at least one ParseExpression is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the '|' operator. + """ + def __init__( self, exprs, savelist = False ): + super(MatchFirst,self).__init__(exprs, savelist) + if exprs: + self.mayReturnEmpty = False + for e in self.exprs: + if e.mayReturnEmpty: + self.mayReturnEmpty = True + break + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + for e in self.exprs: + try: + ret = e._parse( instring, loc, doActions ) + return ret + except ParseException as err: + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + + # only got here if no expression matched, raise exception for match that made it the furthest + else: + if maxException is not None: + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + def __ior__(self, other ): + if isinstance( other, basestring ): + other = Literal( other ) + return self.append( other ) #MatchFirst( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " | ".join( [ _ustr(e) for e in self.exprs ] ) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class Each(ParseExpression): + """Requires all given ParseExpressions to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the '&' operator. + """ + def __init__( self, exprs, savelist = True ): + super(Each,self).__init__(exprs, savelist) + self.mayReturnEmpty = True + for e in self.exprs: + if not e.mayReturnEmpty: + self.mayReturnEmpty = False + break + self.skipWhitespace = True + self.initExprGroups = True + + def parseImpl( self, instring, loc, doActions=True ): + if self.initExprGroups: + self.optionals = [ e.expr for e in self.exprs if isinstance(e,Optional) ] + self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] + self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] + self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] + self.required += self.multirequired + self.initExprGroups = False + tmpLoc = loc + tmpReqd = self.required[:] + tmpOpt = self.optionals[:] + matchOrder = [] + + keepMatching = True + while keepMatching: + tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired + failed = [] + for e in tmpExprs: + try: + tmpLoc = e.tryParse( instring, tmpLoc ) + except ParseException: + failed.append(e) + else: + matchOrder.append(e) + if e in tmpReqd: + tmpReqd.remove(e) + elif e in tmpOpt: + tmpOpt.remove(e) + if len(failed) == len(tmpExprs): + keepMatching = False + + if tmpReqd: + missing = ", ".join( [ _ustr(e) for e in tmpReqd ] ) + raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) + + # add any unmatched Optionals, in case they have default values defined + matchOrder += list(e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt) + + resultlist = [] + for e in matchOrder: + loc,results = e._parse(instring,loc,doActions) + resultlist.append(results) + + finalResults = ParseResults([]) + for r in resultlist: + dups = {} + for k in r.keys(): + if k in finalResults.keys(): + tmp = ParseResults(finalResults[k]) + tmp += ParseResults(r[k]) + dups[k] = tmp + finalResults += ParseResults(r) + for k,v in dups.items(): + finalResults[k] = v + return loc, finalResults + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " & ".join( [ _ustr(e) for e in self.exprs ] ) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class ParseElementEnhance(ParserElement): + """Abstract subclass of ParserElement, for combining and post-processing parsed tokens.""" + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + expr = Literal(expr) + self.expr = expr + self.strRepr = None + if expr is not None: + self.mayIndexError = expr.mayIndexError + self.mayReturnEmpty = expr.mayReturnEmpty + self.setWhitespaceChars( expr.whiteChars ) + self.skipWhitespace = expr.skipWhitespace + self.saveAsList = expr.saveAsList + self.callPreparse = expr.callPreparse + self.ignoreExprs.extend(expr.ignoreExprs) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr is not None: + return self.expr._parse( instring, loc, doActions, callPreParse=False ) + else: + raise ParseException("",loc,self.errmsg,self) + + def leaveWhitespace( self ): + self.skipWhitespace = False + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + else: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + return self + + def streamline( self ): + super(ParseElementEnhance,self).streamline() + if self.expr is not None: + self.expr.streamline() + return self + + def checkRecursion( self, parseElementList ): + if self in parseElementList: + raise RecursiveGrammarException( parseElementList+[self] ) + subRecCheckList = parseElementList[:] + [ self ] + if self.expr is not None: + self.expr.checkRecursion( subRecCheckList ) + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion( [] ) + + def __str__( self ): + try: + return super(ParseElementEnhance,self).__str__() + except: + pass + + if self.strRepr is None and self.expr is not None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) + return self.strRepr + + +class FollowedBy(ParseElementEnhance): + """Lookahead matching of the given parse expression. FollowedBy + does *not* advance the parsing position within the input string, it only + verifies that the specified parse expression matches at the current + position. FollowedBy always returns a null token list.""" + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + self.expr.tryParse( instring, loc ) + return loc, [] + + +class NotAny(ParseElementEnhance): + """Lookahead to disallow matching with the given parse expression. NotAny + does *not* advance the parsing position within the input string, it only + verifies that the specified parse expression does *not* match at the current + position. Also, NotAny does *not* skip over leading whitespace. NotAny + always returns a null token list. May be constructed using the '~' operator.""" + def __init__( self, expr ): + super(NotAny,self).__init__(expr) + #~ self.leaveWhitespace() + self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs + self.mayReturnEmpty = True + self.errmsg = "Found unwanted token, "+_ustr(self.expr) + #self.myException = ParseException("",0,self.errmsg,self) + + def parseImpl( self, instring, loc, doActions=True ): + try: + self.expr.tryParse( instring, loc ) + except (ParseException,IndexError): + pass + else: + #~ raise ParseException(instring, loc, self.errmsg ) + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "~{" + _ustr(self.expr) + "}" + + return self.strRepr + + +class ZeroOrMore(ParseElementEnhance): + """Optional repetition of zero or more of the given expression.""" + def __init__( self, expr ): + super(ZeroOrMore,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + tokens = [] + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + while 1: + if hasIgnoreExprs: + preloc = self._skipIgnorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + if tmptokens or tmptokens.keys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]..." + + return self.strRepr + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) + ret.saveAsList = True + return ret + + +class OneOrMore(ParseElementEnhance): + """Repetition of one or more of the given expression.""" + def parseImpl( self, instring, loc, doActions=True ): + # must be at least one + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + while 1: + if hasIgnoreExprs: + preloc = self._skipIgnorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + if tmptokens or tmptokens.keys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + _ustr(self.expr) + "}..." + + return self.strRepr + + def setResultsName( self, name, listAllMatches=False ): + ret = super(OneOrMore,self).setResultsName(name,listAllMatches) + ret.saveAsList = True + return ret + +class _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """Optional matching of the given expression. + A default return string can also be specified, if the optional expression + is not found. + """ + def __init__( self, exprs, default=_optionalNotMatched ): + super(Optional,self).__init__( exprs, savelist=False ) + self.defaultValue = default + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + except (ParseException,IndexError): + if self.defaultValue is not _optionalNotMatched: + if self.expr.resultsName: + tokens = ParseResults([ self.defaultValue ]) + tokens[self.expr.resultsName] = self.defaultValue + else: + tokens = [ self.defaultValue ] + else: + tokens = [] + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]" + + return self.strRepr + + +class SkipTo(ParseElementEnhance): + """Token for skipping over all undefined text until the matched expression is found. + If include is set to true, the matched expression is also parsed (the skipped text + and matched expression are returned as a 2-element list). The ignore + argument is used to define grammars (typically quoted strings and comments) that + might contain false matches. + """ + def __init__( self, other, include=False, ignore=None, failOn=None ): + super( SkipTo, self ).__init__( other ) + self.ignoreExpr = ignore + self.mayReturnEmpty = True + self.mayIndexError = False + self.includeMatch = include + self.asList = False + if failOn is not None and isinstance(failOn, basestring): + self.failOn = Literal(failOn) + else: + self.failOn = failOn + self.errmsg = "No match found for "+_ustr(self.expr) + #self.myException = ParseException("",0,self.errmsg,self) + + def parseImpl( self, instring, loc, doActions=True ): + startLoc = loc + instrlen = len(instring) + expr = self.expr + failParse = False + while loc <= instrlen: + try: + if self.failOn: + try: + self.failOn.tryParse(instring, loc) + except ParseBaseException: + pass + else: + failParse = True + raise ParseException(instring, loc, "Found expression " + str(self.failOn)) + failParse = False + if self.ignoreExpr is not None: + while 1: + try: + loc = self.ignoreExpr.tryParse(instring,loc) + # print("found ignoreExpr, advance to", loc) + except ParseBaseException: + break + expr._parse( instring, loc, doActions=False, callPreParse=False ) + skipText = instring[startLoc:loc] + if self.includeMatch: + loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) + if mat: + skipRes = ParseResults( skipText ) + skipRes += mat + return loc, [ skipRes ] + else: + return loc, [ skipText ] + else: + return loc, [ skipText ] + except (ParseException,IndexError): + if failParse: + raise + else: + loc += 1 + exc = self.myException + exc.loc = loc + exc.pstr = instring + raise exc + +class Forward(ParseElementEnhance): + """Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the Forward variable using the '<<' operator. + + Note: take care when assigning to Forward not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the Forward:: + fwdExpr << (a | b | c) + """ + def __init__( self, other=None ): + super(Forward,self).__init__( other, savelist=False ) + + def __lshift__( self, other ): + if isinstance( other, basestring ): + other = Literal(other) + self.expr = other + self.mayReturnEmpty = other.mayReturnEmpty + self.strRepr = None + self.mayIndexError = self.expr.mayIndexError + self.mayReturnEmpty = self.expr.mayReturnEmpty + self.setWhitespaceChars( self.expr.whiteChars ) + self.skipWhitespace = self.expr.skipWhitespace + self.saveAsList = self.expr.saveAsList + self.ignoreExprs.extend(self.expr.ignoreExprs) + return None + + def leaveWhitespace( self ): + self.skipWhitespace = False + return self + + def streamline( self ): + if not self.streamlined: + self.streamlined = True + if self.expr is not None: + self.expr.streamline() + return self + + def validate( self, validateTrace=[] ): + if self not in validateTrace: + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion([]) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + self._revertClass = self.__class__ + self.__class__ = _ForwardNoRecurse + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + self.__class__ = self._revertClass + return self.__class__.__name__ + ": " + retString + + def copy(self): + if self.expr is not None: + return super(Forward,self).copy() + else: + ret = Forward() + ret << self + return ret + +class _ForwardNoRecurse(Forward): + def __str__( self ): + return "..." + +class TokenConverter(ParseElementEnhance): + """Abstract subclass of ParseExpression, for converting parsed results.""" + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +class Upcase(TokenConverter): + """Converter to upper case all matching tokens.""" + def __init__(self, *args): + super(Upcase,self).__init__(*args) + warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", + DeprecationWarning,stacklevel=2) + + def postParse( self, instring, loc, tokenlist ): + return list(map( string.upper, tokenlist )) + + +class Combine(TokenConverter): + """Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying 'adjacent=False' in the constructor. + """ + def __init__( self, expr, joinString="", adjacent=True ): + super(Combine,self).__init__( expr ) + # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself + if adjacent: + self.leaveWhitespace() + self.adjacent = adjacent + self.skipWhitespace = True + self.joinString = joinString + + def ignore( self, other ): + if self.adjacent: + ParserElement.ignore(self, other) + else: + super( Combine, self).ignore( other ) + return self + + def postParse( self, instring, loc, tokenlist ): + retToks = tokenlist.copy() + del retToks[:] + retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) + + if self.resultsName and len(retToks.keys())>0: + return [ retToks ] + else: + return retToks + +class Group(TokenConverter): + """Converter to return the matched tokens as a list - useful for returning tokens of ZeroOrMore and OneOrMore expressions.""" + def __init__( self, expr ): + super(Group,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + return [ tokenlist ] + +class Dict(TokenConverter): + """Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + """ + def __init__( self, exprs ): + super(Dict,self).__init__( exprs ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + for i,tok in enumerate(tokenlist): + if len(tok) == 0: + continue + ikey = tok[0] + if isinstance(ikey,int): + ikey = _ustr(tok[0]).strip() + if len(tok)==1: + tokenlist[ikey] = _ParseResultsWithOffset("",i) + elif len(tok)==2 and not isinstance(tok[1],ParseResults): + tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) + else: + dictvalue = tok.copy() #ParseResults(i) + del dictvalue[0] + if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.keys()): + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) + else: + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) + + if self.resultsName: + return [ tokenlist ] + else: + return tokenlist + + +class Suppress(TokenConverter): + """Converter for ignoring the results of a parsed expression.""" + def postParse( self, instring, loc, tokenlist ): + return [] + + def suppress( self ): + return self + + +class OnlyOnce(object): + """Wrapper for parse actions, to ensure they are only called once.""" + def __init__(self, methodCall): + self.callable = ParserElement._normalizeParseActionArgs(methodCall) + self.called = False + def __call__(self,s,l,t): + if not self.called: + results = self.callable(s,l,t) + self.called = True + return results + raise ParseException(s,l,"") + def reset(self): + self.called = False + +def traceParseAction(f): + """Decorator for debugging parse actions.""" + f = ParserElement._normalizeParseActionArgs(f) + def z(*paArgs): + thisFunc = f.func_name + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) + try: + ret = f(*paArgs) + except Exception: + exc = sys.exc_info()[1] + sys.stderr.write( "<", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) + try: + if len(symbols)==len("".join(symbols)): + return Regex( "[%s]" % "".join( [ _escapeRegexRangeChars(sym) for sym in symbols] ) ) + else: + return Regex( "|".join( [ re.escape(sym) for sym in symbols] ) ) + except: + warnings.warn("Exception creating Regex for oneOf, building MatchFirst", + SyntaxWarning, stacklevel=2) + + + # last resort, just use MatchFirst + return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) + +def dictOf( key, value ): + """Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the Dict, ZeroOrMore, and Group tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the Dict results can include named token + fields. + """ + return Dict( ZeroOrMore( Group ( key + value ) ) ) + +def originalTextFor(expr, asString=True): + """Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. Simpler to use than the parse action keepOriginalText, and does not + require the inspect module to chase up the call stack. By default, returns a + string containing the original parsed text. + + If the optional asString argument is passed as False, then the return value is a + ParseResults containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to originalTextFor contains expressions with defined + results names, you must set asString to False if you want to preserve those + results name values.""" + locMarker = Empty().setParseAction(lambda s,loc,t: loc) + matchExpr = locMarker("_original_start") + expr + locMarker("_original_end") + if asString: + extractText = lambda s,l,t: s[t._original_start:t._original_end] + else: + def extractText(s,l,t): + del t[:] + t.insert(0, s[t._original_start:t._original_end]) + del t["_original_start"] + del t["_original_end"] + matchExpr.setParseAction(extractText) + return matchExpr + +# convenience constants for positional expressions +empty = Empty().setName("empty") +lineStart = LineStart().setName("lineStart") +lineEnd = LineEnd().setName("lineEnd") +stringStart = StringStart().setName("stringStart") +stringEnd = StringEnd().setName("stringEnd") + +_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) +_printables_less_backslash = "".join([ c for c in printables if c not in r"\]" ]) +_escapedHexChar = Combine( Suppress(_bslash + "0x") + Word(hexnums) ).setParseAction(lambda s,l,t:unichr(int(t[0],16))) +_escapedOctChar = Combine( Suppress(_bslash) + Word("0","01234567") ).setParseAction(lambda s,l,t:unichr(int(t[0],8))) +_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(_printables_less_backslash,exact=1) +_charRange = Group(_singleChar + Suppress("-") + _singleChar) +_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" + +_expanded = lambda p: (isinstance(p,ParseResults) and ''.join([ unichr(c) for c in range(ord(p[0]),ord(p[1])+1) ]) or p) + +def srange(s): + r"""Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be:: + a single character + an escaped character with a leading backslash (such as \- or \]) + an escaped hex character with a leading '\0x' (\0x21, which is a '!' character) + an escaped octal character with a leading '\0' (\041, which is a '!' character) + a range of any of the above, separated by a dash ('a-z', etc.) + any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) + """ + try: + return "".join([_expanded(part) for part in _reBracketExpr.parseString(s).body]) + except: + return "" + +def matchOnlyAtCol(n): + """Helper method for defining parse actions that require matching at a specific + column in the input text. + """ + def verifyCol(strg,locn,toks): + if col(locn,strg) != n: + raise ParseException(strg,locn,"matched token not at column %d" % n) + return verifyCol + +def replaceWith(replStr): + """Helper method for common parse actions that simply return a literal value. Especially + useful when used with transformString(). + """ + def _replFunc(*args): + return [replStr] + return _replFunc + +def removeQuotes(s,l,t): + """Helper parse action for removing quotation marks from parsed quoted strings. + To use, add this parse action to quoted string using:: + quotedString.setParseAction( removeQuotes ) + """ + return t[0][1:-1] + +def upcaseTokens(s,l,t): + """Helper parse action to convert tokens to upper case.""" + return [ tt.upper() for tt in map(_ustr,t) ] + +def downcaseTokens(s,l,t): + """Helper parse action to convert tokens to lower case.""" + return [ tt.lower() for tt in map(_ustr,t) ] + +def keepOriginalText(s,startLoc,t): + """Helper parse action to preserve original parsed text, + overriding any nested parse actions.""" + try: + endloc = getTokensEndLoc() + except ParseException: + raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") + del t[:] + t += ParseResults(s[startLoc:endloc]) + return t + +def getTokensEndLoc(): + """Method to be called from within a parse action to determine the end + location of the parsed tokens.""" + import inspect + fstack = inspect.stack() + try: + # search up the stack (through intervening argument normalizers) for correct calling routine + for f in fstack[2:]: + if f[3] == "_parseNoCache": + endloc = f[0].f_locals["loc"] + return endloc + else: + raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") + finally: + del fstack + +def _makeTags(tagStr, xml): + """Internal helper to construct opening and closing tag expressions, given a tag name""" + if isinstance(tagStr,basestring): + resname = tagStr + tagStr = Keyword(tagStr, caseless=not xml) + else: + resname = tagStr.name + + tagAttrName = Word(alphas,alphanums+"_-:") + if (xml): + tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) + openTag = Suppress("<") + tagStr + \ + Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + else: + printablesLessRAbrack = "".join( [ c for c in printables if c not in ">" ] ) + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) + openTag = Suppress("<") + tagStr + \ + Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ + Optional( Suppress("=") + tagAttrValue ) ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + closeTag = Combine(_L("") + + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % tagStr) + + return openTag, closeTag + +def makeHTMLTags(tagStr): + """Helper to construct opening and closing tag expressions for HTML, given a tag name""" + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """Helper to construct opening and closing tag expressions for XML, given a tag name""" + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """Helper to create a validating parse action to be used with start tags created + with makeXMLTags or makeHTMLTags. Use withAttribute to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + or
. + + Call withAttribute with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in (class="Customer",align="right"), or + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + To verify that the attribute exists, but without specifying a value, pass + withAttribute.ANY_VALUE as the value. + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def operatorPrecedence( baseExpr, opList ): + """Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants opAssoc.RIGHT and opAssoc.LEFT. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted) + """ + ret = Forward() + lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward()#.setName("expr%d" % i) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + matchExpr.setParseAction( pa ) + thisExpr << ( matchExpr | lastExpr ) + lastExpr = thisExpr + ret << lastExpr + return ret + +dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") +sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") +quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()) + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString): + """Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default="("); can also be a pyparsing expression + - closer - closing character for a nested list (default=")"); can also be a pyparsing expression + - content - expression for items within the nested lists (default=None) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the ignoreExpr argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an Or or MatchFirst. + The default is quotedString, but if no expressions are to be ignored, + then pass None for this argument. + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=True) + + A valid block must contain at least one blockStatement. + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = Empty() + Empty().setParseAction(checkSubIndent) + PEER = Empty().setParseAction(checkPeerIndent) + UNDENT = Empty().setParseAction(checkUnindent) + if indent: + smExpr = Group( Optional(NL) + + FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) +commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() +_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) +replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") + +htmlComment = Regex(r"") +restOfLine = Regex(r".*").leaveWhitespace() +dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") +cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?" + str(tokenlist)) + print ("tokens = " + str(tokens)) + print ("tokens.columns = " + str(tokens.columns)) + print ("tokens.tables = " + str(tokens.tables)) + print (tokens.asXML("SQL",True)) + except ParseBaseException: + err = sys.exc_info()[1] + print (teststring + "->") + print (err.line) + print (" "*(err.column-1) + "^") + print (err) + print() + + selectToken = CaselessLiteral( "select" ) + fromToken = CaselessLiteral( "from" ) + + ident = Word( alphas, alphanums + "_$" ) + columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) + columnNameList = Group( delimitedList( columnName ) )#.setName("columns") + tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) + tableNameList = Group( delimitedList( tableName ) )#.setName("tables") + simpleSQL = ( selectToken + \ + ( '*' | columnNameList ).setResultsName( "columns" ) + \ + fromToken + \ + tableNameList.setResultsName( "tables" ) ) + + test( "SELECT * from XYZZY, ABC" ) + test( "select * from SYS.XYZZY" ) + test( "Select A from Sys.dual" ) + test( "Select AA,BB,CC from Sys.dual" ) + test( "Select A, B, C from Sys.dual" ) + test( "Select A, B, C from Sys.dual" ) + test( "Xelect A, B, C from Sys.dual" ) + test( "Select A, B, C frox Sys.dual" ) + test( "Select" ) + test( "Select ^^^ frox Sys.dual" ) + test( "Select A, B, C from Sys.dual, Table2 " )