pax_global_header00006660000000000000000000000064136533316050014517gustar00rootroot0000000000000052 comment=9d42c96656d5733882fcbb5c2264ff075a8699e0 pyparsing2-2.4.7/000077500000000000000000000000001365333160500136275ustar00rootroot00000000000000pyparsing2-2.4.7/CHANGES000066400000000000000000003704561365333160500146410ustar00rootroot00000000000000========== Change Log ========== Version 2.4.7 - March, 2020 --------------------------- - Backport of selected fixes from 3.0.0 work: . Each bug with Regex expressions . And expressions not properly constructing with generator . Traceback abbreviation . Bug in delta_time example . Fix regexen in pyparsing_common.real and .sci_real . Avoid FutureWarning on Python 3.7 or later . Cleanup output in runTests if comments are embedded in test string Version 2.4.6 - December, 2019 ------------------------------ - Fixed typos in White mapping of whitespace characters, to use correct "\u" prefix instead of "u\". - Fix bug in left-associative ternary operators defined using infixNotation. First reported on StackOverflow by user Jeronimo. - Backport of pyparsing_test namespace from 3.0.0, including TestParseResultsAsserts mixin class defining unittest-helper methods: . def assertParseResultsEquals( self, result, expected_list=None, expected_dict=None, msg=None) . def assertParseAndCheckList( self, expr, test_string, expected_list, msg=None, verbose=True) . def assertParseAndCheckDict( self, expr, test_string, expected_dict, msg=None, verbose=True) . def assertRunTestResults( self, run_tests_report, expected_parse_results=None, msg=None) . def assertRaisesParseException(self, exc_type=ParseException, msg=None) To use the methods in this mixin class, declare your unittest classes as: from pyparsing import pyparsing_test as ppt class MyParserTest(ppt.TestParseResultsAsserts, unittest.TestCase): ... Version 2.4.5 - November, 2019 ------------------------------ - Fixed encoding when setup.py reads README.rst to include the project long description when uploading to PyPI. A stray unicode space in README.rst prevented the source install on systems whose default encoding is not 'utf-8'. Version 2.4.4 - November, 2019 -------------------------------- - Unresolved symbol reference in 2.4.3 release was masked by stdout buffering in unit tests, thanks for the prompt heads-up, Ned Batchelder! Version 2.4.3 - November, 2019 ------------------------------ - Fixed a bug in ParserElement.__eq__ that would for some parsers create a recursion error at parser definition time. Thanks to Michael Clerx for the assist. (Addresses issue #123) - Fixed bug in indentedBlock where a block that ended at the end of the input string could cause pyaprsing to loop forever. Raised as part of discussion on StackOverflow with geckos. - Backports from pyparsing 3.0.0: . __diag__.enable_all_warnings() . Fixed bug in PrecededBy which caused infinite recursion, issue #127 . support for using regex-compiled RE to construct Regex expressions Version 2.4.2 - July, 2019 -------------------------- - Updated the shorthand notation that has been added for repetition expressions: expr[min, max], with '...' valid as a min or max value: - expr[...] and expr[0, ...] are equivalent to ZeroOrMore(expr) - expr[1, ...] is equivalent to OneOrMore(expr) - expr[n, ...] or expr[n,] is equivalent to expr*n + ZeroOrMore(expr) (read as "n or more instances of expr") - expr[..., n] is equivalent to expr*(0, n) - expr[m, n] is equivalent to expr*(m, n) Note that expr[..., n] and expr[m, n] do not raise an exception if more than n exprs exist in the input stream. If this behavior is desired, then write expr[..., n] + ~expr. Better interpretation of [...] as ZeroOrMore raised by crowsonkb, thanks for keeping me in line! If upgrading from 2.4.1 or 2.4.1.1 and you have used `expr[...]` for `OneOrMore(expr)`, it must be updated to `expr[1, ...]`. - The defaults on all the `__diag__` switches have been set to False, to avoid getting alarming warnings. To use these diagnostics, set them to True after importing pyparsing. Example: import pyparsing as pp pp.__diag__.warn_multiple_tokens_in_named_alternation = True - Fixed bug introduced by the use of __getitem__ for repetition, overlooking Python's legacy implementation of iteration by sequentially calling __getitem__ with increasing numbers until getting an IndexError. Found during investigation of problem reported by murlock, merci! Version 2.4.2a1 - July, 2019 ---------------------------- It turns out I got the meaning of `[...]` absolutely backwards, so I've deleted 2.4.1 and am repushing this release as 2.4.2a1 for people to give it a try before I can call it ready to go. The `expr[...]` notation was pushed out to be synonymous with `OneOrMore(expr)`, but this is really counter to most Python notations (and even other internal pyparsing notations as well). It should have been defined to be equivalent to ZeroOrMore(expr). - Changed [...] to emit ZeroOrMore instead of OneOrMore. - Removed code that treats ParserElements like iterables. - Change all __diag__ switches to False. Version 2.4.1.1 - July 24, 2019 ------------------------------- This is a re-release of version 2.4.1 to restore the release history in PyPI, since the 2.4.1 release was deleted. There are 3 known issues in this release, which are fixed in the upcoming 2.4.2: - API change adding support for `expr[...]` - the original code in 2.4.1 incorrectly implemented this as OneOrMore. Code using this feature under this relase should explicitly use `expr[0, ...]` for ZeroOrMore and `expr[1, ...]` for OneOrMore. In 2.4.2 you will be able to write `expr[...]` equivalent to `ZeroOrMore(expr)`. - Bug if composing And, Or, MatchFirst, or Each expressions using an expression. This only affects code which uses explicit expression construction using the And, Or, etc. classes instead of using overloaded operators '+', '^', and so on. If constructing an And using a single expression, you may get an error that "cannot multiply ParserElement by 0 or (0, 0)" or a Python `IndexError`. Change code like cmd = Or(Word(alphas)) to cmd = Or([Word(alphas)]) (Note that this is not the recommended style for constructing Or expressions.) - Some newly-added `__diag__` switches are enabled by default, which may give rise to noisy user warnings for existing parsers. You can disable them using: import pyparsing as pp pp.__diag__.warn_multiple_tokens_in_named_alternation = False pp.__diag__.warn_ungrouped_named_tokens_in_collection = False pp.__diag__.warn_name_set_on_empty_Forward = False pp.__diag__.warn_on_multiple_string_args_to_oneof = False pp.__diag__.enable_debug_on_named_expressions = False In 2.4.2 these will all be set to False by default. Version 2.4.1 - July, 2019 -------------------------- - NOTE: Deprecated functions and features that will be dropped in pyparsing 2.5.0 (planned next release): . support for Python 2 - ongoing users running with Python 2 can continue to use pyparsing 2.4.1 . ParseResults.asXML() - if used for debugging, switch to using ParseResults.dump(); if used for data transfer, use ParseResults.asDict() to convert to a nested Python dict, which can then be converted to XML or JSON or other transfer format . operatorPrecedence synonym for infixNotation - convert to calling infixNotation . commaSeparatedList - convert to using pyparsing_common.comma_separated_list . upcaseTokens and downcaseTokens - convert to using pyparsing_common.upcaseTokens and downcaseTokens . __compat__.collect_all_And_tokens will not be settable to False to revert to pre-2.3.1 results name behavior - review use of names for MatchFirst and Or expressions containing And expressions, as they will return the complete list of parsed tokens, not just the first one. Use __diag__.warn_multiple_tokens_in_named_alternation (described below) to help identify those expressions in your parsers that will have changed as a result. - A new shorthand notation has been added for repetition expressions: expr[min, max], with '...' valid as a min or max value: - expr[...] is equivalent to OneOrMore(expr) - expr[0, ...] is equivalent to ZeroOrMore(expr) - expr[1, ...] is equivalent to OneOrMore(expr) - expr[n, ...] or expr[n,] is equivalent to expr*n + ZeroOrMore(expr) (read as "n or more instances of expr") - expr[..., n] is equivalent to expr*(0, n) - expr[m, n] is equivalent to expr*(m, n) Note that expr[..., n] and expr[m, n] do not raise an exception if more than n exprs exist in the input stream. If this behavior is desired, then write expr[..., n] + ~expr. - '...' can also be used as short hand for SkipTo when used in adding parse expressions to compose an And expression. Literal('start') + ... + Literal('end') And(['start', ..., 'end']) are both equivalent to: Literal('start') + SkipTo('end')("_skipped*") + Literal('end') The '...' form has the added benefit of not requiring repeating the skip target expression. Note that the skipped text is returned with '_skipped' as a results name, and that the contents of `_skipped` will contain a list of text from all `...`s in the expression. - '...' can also be used as a "skip forward in case of error" expression: expr = "start" + (Word(nums).setName("int") | ...) + "end" expr.parseString("start 456 end") ['start', '456', 'end'] expr.parseString("start 456 foo 789 end") ['start', '456', 'foo 789 ', 'end'] - _skipped: ['foo 789 '] expr.parseString("start foo end") ['start', 'foo ', 'end'] - _skipped: ['foo '] expr.parseString("start end") ['start', '', 'end'] - _skipped: ['missing '] Note that in all the error cases, the '_skipped' results name is present, showing a list of the extra or missing items. This form is only valid when used with the '|' operator. - Improved exception messages to show what was actually found, not just what was expected. word = pp.Word(pp.alphas) pp.OneOrMore(word).parseString("aaa bbb 123", parseAll=True) Former exception message: pyparsing.ParseException: Expected end of text (at char 8), (line:1, col:9) New exception message: pyparsing.ParseException: Expected end of text, found '1' (at char 8), (line:1, col:9) - Added diagnostic switches to help detect and warn about common parser construction mistakes, or enable additional parse debugging. Switches are attached to the pyparsing.__diag__ namespace object: - warn_multiple_tokens_in_named_alternation - flag to enable warnings when a results name is defined on a MatchFirst or Or expression with one or more And subexpressions (default=True) - warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results name is defined on a containing expression with ungrouped subexpressions that also have results names (default=True) - warn_name_set_on_empty_Forward - flag to enable warnings whan a Forward is defined with a results name, but has no contents defined (default=False) - warn_on_multiple_string_args_to_oneof - flag to enable warnings whan oneOf is incorrectly called with multiple str arguments (default=True) - enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent calls to ParserElement.setName() (default=False) warn_multiple_tokens_in_named_alternation is intended to help those who currently have set __compat__.collect_all_And_tokens to False as a workaround for using the pre-2.3.1 code with named MatchFirst or Or expressions containing an And expression. - Added ParseResults.from_dict classmethod, to simplify creation of a ParseResults with results names using a dict, which may be nested. This makes it easy to add a sub-level of named items to the parsed tokens in a parse action. - Added asKeyword argument (default=False) to oneOf, to force keyword-style matching on the generated expressions. - ParserElement.runTests now accepts an optional 'file' argument to redirect test output to a file-like object (such as a StringIO, or opened file). Default is to write to sys.stdout. - conditionAsParseAction is a helper method for constructing a parse action method from a predicate function that simply returns a boolean result. Useful for those places where a predicate cannot be added using addCondition, but must be converted to a parse action (such as in infixNotation). May be used as a decorator if default message and exception types can be used. See ParserElement.addCondition for more details about the expected signature and behavior for predicate condition methods. - While investigating issue #93, I found that Or and addCondition could interact to select an alternative that is not the longest match. This is because Or first checks all alternatives for matches without running attached parse actions or conditions, orders by longest match, and then rechecks for matches with conditions and parse actions. Some expressions, when checking with conditions, may end up matching on a shorter token list than originally matched, but would be selected because of its original priority. This matching code has been expanded to do more extensive searching for matches when a second-pass check matches a smaller list than in the first pass. - Fixed issue #87, a regression in indented block. Reported by Renz Bagaporo, who submitted a very nice repro example, which makes the bug-fixing process a lot easier, thanks! - Fixed MemoryError issue #85 and #91 with str generation for Forwards. Thanks decalage2 and Harmon758 for your patience. - Modified setParseAction to accept None as an argument, indicating that all previously-defined parse actions for the expression should be cleared. - Modified pyparsing_common.real and sci_real to parse reals without leading integer digits before the decimal point, consistent with Python real number formats. Original PR #98 submitted by ansobolev. - Modified runTests to call postParse function before dumping out the parsed results - allows for postParse to add further results, such as indications of additional validation success/failure. - Updated statemachine example: refactored state transitions to use overridden classmethods; added Mixin class to simplify definition of application classes that "own" the state object and delegate to it to model state-specific properties and behavior. - Added example nested_markup.py, showing a simple wiki markup with nested markup directives, and illustrating the use of '...' for skipping over input to match the next expression. (This example uses syntax that is not valid under Python 2.) - Rewrote delta_time.py example (renamed from deltaTime.py) to fix some omitted formats and upgrade to latest pyparsing idioms, beginning with writing an actual BNF. - With the help and encouragement from several contributors, including Matěj Cepl and Cengiz Kaygusuz, I've started cleaning up the internal coding styles in core pyparsing, bringing it up to modern coding practices from pyparsing's early development days dating back to 2003. Whitespace has been largely standardized along PEP8 guidelines, removing extra spaces around parentheses, and adding them around arithmetic operators and after colons and commas. I was going to hold off on doing this work until after 2.4.1, but after cleaning up a few trial classes, the difference was so significant that I continued on to the rest of the core code base. This should facilitate future work and submitted PRs, allowing them to focus on substantive code changes, and not get sidetracked by whitespace issues. Version 2.4.0 - April, 2019 --------------------------- - Well, it looks like the API change that was introduced in 2.3.1 was more drastic than expected, so for a friendlier forward upgrade path, this release: . Bumps the current version number to 2.4.0, to reflect this incompatible change. . Adds a pyparsing.__compat__ object for specifying compatibility with future breaking changes. . Conditionalizes the API-breaking behavior, based on the value pyparsing.__compat__.collect_all_And_tokens. By default, this value will be set to True, reflecting the new bugfixed behavior. To set this value to False, add to your code: import pyparsing pyparsing.__compat__.collect_all_And_tokens = False . User code that is dependent on the pre-bugfix behavior can restore it by setting this value to False. In 2.5 and later versions, the conditional code will be removed and setting the flag to True or False in these later versions will have no effect. - Updated unitTests.py and simple_unit_tests.py to be compatible with "python setup.py test". To run tests using setup, do: python setup.py test python setup.py test -s unitTests.suite python setup.py test -s simple_unit_tests.suite Prompted by issue #83 and PR submitted by bdragon28, thanks. - Fixed bug in runTests handling '\n' literals in quoted strings. - Added tag_body attribute to the start tag expressions generated by makeHTMLTags, so that you can avoid using SkipTo to roll your own tag body expression: a, aEnd = pp.makeHTMLTags('a') link = a + a.tag_body("displayed_text") + aEnd for t in s.searchString(html_page): print(t.displayed_text, '->', t.startA.href) - indentedBlock failure handling was improved; PR submitted by TMiguelT, thanks! - Address Py2 incompatibility in simpleUnitTests, plus explain() and Forward str() cleanup; PRs graciously provided by eswald. - Fixed docstring with embedded '\w', which creates SyntaxWarnings in Py3.8, issue #80. - Examples: - Added example parser for rosettacode.org tutorial compiler. - Added example to show how an HTML table can be parsed into a collection of Python lists or dicts, one per row. - Updated SimpleSQL.py example to handle nested selects, reworked 'where' expression to use infixNotation. - Added include_preprocessor.py, similar to macroExpander.py. - Examples using makeHTMLTags use new tag_body expression when retrieving a tag's body text. - Updated examples that are runnable as unit tests: python setup.py test -s examples.antlr_grammar_tests python setup.py test -s examples.test_bibparse Version 2.3.1 - January, 2019 ----------------------------- - POSSIBLE API CHANGE: this release fixes a bug when results names were attached to a MatchFirst or Or object containing an And object. Previously, a results name on an And object within an enclosing MatchFirst or Or could return just the first token in the And. Now, all the tokens matched by the And are correctly returned. This may result in subtle changes in the tokens returned if you have this condition in your pyparsing scripts. - New staticmethod ParseException.explain() to help diagnose parse exceptions by showing the failing input line and the trace of ParserElements in the parser leading up to the exception. explain() returns a multiline string listing each element by name. (This is still an experimental method, and the method signature and format of the returned string may evolve over the next few releases.) Example: # define a parser to parse an integer followed by an # alphabetic word expr = pp.Word(pp.nums).setName("int") + pp.Word(pp.alphas).setName("word") try: # parse a string with a numeric second value instead of alpha expr.parseString("123 355") except pp.ParseException as pe: print(pp.ParseException.explain(pe)) Prints: 123 355 ^ ParseException: Expected word (at char 4), (line:1, col:5) __main__.ExplainExceptionTest pyparsing.And - {int word} pyparsing.Word - word explain() will accept any exception type and will list the function names and parse expressions in the stack trace. This is especially useful when an exception is raised in a parse action. Note: explain() is only supported under Python 3. - Fix bug in dictOf which could match an empty sequence, making it infinitely loop if wrapped in a OneOrMore. - Added unicode sets to pyparsing_unicode for Latin-A and Latin-B ranges. - Added ability to define custom unicode sets as combinations of other sets using multiple inheritance. class Turkish_set(pp.pyparsing_unicode.Latin1, pp.pyparsing_unicode.LatinA): pass turkish_word = pp.Word(Turkish_set.alphas) - Updated state machine import examples, with state machine demos for: . traffic light . library book checkin/checkout . document review/approval In the traffic light example, you can use the custom 'statemachine' keyword to define the states for a traffic light, and have the state classes auto-generated for you: statemachine TrafficLightState: Red -> Green Green -> Yellow Yellow -> Red Similar for state machines with named transitions, like the library book state example: statemachine LibraryBookState: New -(shelve)-> Available Available -(reserve)-> OnHold OnHold -(release)-> Available Available -(checkout)-> CheckedOut CheckedOut -(checkin)-> Available Once the classes are defined, then additional Python code can reference those classes to add class attributes, instance methods, etc. See the examples in examples/statemachine - Added an example parser for the decaf language. This language is used in CS compiler classes in many colleges and universities. - Fixup of docstrings to Sphinx format, inclusion of test files in the source package, and convert markdown to rst throughout the distribution, great job by Matěj Cepl! - Expanded the whitespace characters recognized by the White class to include all unicode defined spaces. Suggested in Issue #51 by rtkjbillo. - Added optional postParse argument to ParserElement.runTests() to add a custom callback to be called for test strings that parse successfully. Useful for running tests that do additional validation or processing on the parsed results. See updated chemicalFormulas.py example. - Removed distutils fallback in setup.py. If installing the package fails, please update to the latest version of setuptools. Plus overall project code cleanup (CRLFs, whitespace, imports, etc.), thanks Jon Dufresne! - Fix bug in CaselessKeyword, to make its behavior consistent with Keyword(caseless=True). Fixes Issue #65 reported by telesphore. Version 2.3.0 - October, 2018 ----------------------------- - NEW SUPPORT FOR UNICODE CHARACTER RANGES This release introduces the pyparsing_unicode namespace class, defining a series of language character sets to simplify the definition of alphas, nums, alphanums, and printables in the following language sets: . Arabic . Chinese . Cyrillic . Devanagari . Greek . Hebrew . Japanese (including Kanji, Katakana, and Hirigana subsets) . Korean . Latin1 (includes 7 and 8-bit Latin characters) . Thai . CJK (combination of Chinese, Japanese, and Korean sets) For example, your code can define words using: korean_word = Word(pyparsing_unicode.Korean.alphas) See their use in the updated examples greetingInGreek.py and greetingInKorean.py. This namespace class also offers access to these sets using their unicode identifiers. - POSSIBLE API CHANGE: Fixed bug where a parse action that explicitly returned the input ParseResults could add another nesting level in the results if the current expression had a results name. vals = pp.OneOrMore(pp.pyparsing_common.integer)("int_values") def add_total(tokens): tokens['total'] = sum(tokens) return tokens # this line can be removed vals.addParseAction(add_total) print(vals.parseString("244 23 13 2343").dump()) Before the fix, this code would print (note the extra nesting level): [244, 23, 13, 2343] - int_values: [244, 23, 13, 2343] - int_values: [244, 23, 13, 2343] - total: 2623 - total: 2623 With the fix, this code now prints: [244, 23, 13, 2343] - int_values: [244, 23, 13, 2343] - total: 2623 This fix will change the structure of ParseResults returned if a program defines a parse action that returns the tokens that were sent in. This is not necessary, and statements like "return tokens" in the example above can be safely deleted prior to upgrading to this release, in order to avoid the bug and get the new behavior. Reported by seron in Issue #22, nice catch! - POSSIBLE API CHANGE: Fixed a related bug where a results name erroneously created a second level of hierarchy in the returned ParseResults. The intent for accumulating results names into ParseResults is that, in the absence of Group'ing, all names get merged into a common namespace. This allows us to write: key_value_expr = (Word(alphas)("key") + '=' + Word(nums)("value")) result = key_value_expr.parseString("a = 100") and have result structured as {"key": "a", "value": "100"} instead of [{"key": "a"}, {"value": "100"}]. However, if a named expression is used in a higher-level non-Group expression that *also* has a name, a false sub-level would be created in the namespace: num = pp.Word(pp.nums) num_pair = ("[" + (num("A") + num("B"))("values") + "]") U = num_pair.parseString("[ 10 20 ]") print(U.dump()) Since there is no grouping, "A", "B", and "values" should all appear at the same level in the results, as: ['[', '10', '20', ']'] - A: '10' - B: '20' - values: ['10', '20'] Instead, an extra level of "A" and "B" show up under "values": ['[', '10', '20', ']'] - A: '10' - B: '20' - values: ['10', '20'] - A: '10' - B: '20' This bug has been fixed. Now, if this hierarchy is desired, then a Group should be added: num_pair = ("[" + pp.Group(num("A") + num("B"))("values") + "]") Giving: ['[', ['10', '20'], ']'] - values: ['10', '20'] - A: '10' - B: '20' But in no case should "A" and "B" appear in multiple levels. This bug-fix fixes that. If you have current code which relies on this behavior, then add or remove Groups as necessary to get your intended results structure. Reported by Athanasios Anastasiou. - IndexError's raised in parse actions will get explicitly reraised as ParseExceptions that wrap the original IndexError. Since IndexError sometimes occurs as part of pyparsing's normal parsing logic, IndexErrors that are raised during a parse action may have gotten silently reinterpreted as parsing errors. To retain the information from the IndexError, these exceptions will now be raised as ParseExceptions that reference the original IndexError. This wrapping will only be visible when run under Python3, since it emulates "raise ... from ..." syntax. Addresses Issue #4, reported by guswns0528. - Added Char class to simplify defining expressions of a single character. (Char("abc") is equivalent to Word("abc", exact=1)) - Added class PrecededBy to perform lookbehind tests. PrecededBy is used in the same way as FollowedBy, passing in an expression that must occur just prior to the current parse location. For fixed-length expressions like a Literal, Keyword, Char, or a Word with an `exact` or `maxLen` length given, `PrecededBy(expr)` is sufficient. For varying length expressions like a Word with no given maximum length, `PrecededBy` must be constructed with an integer `retreat` argument, as in `PrecededBy(Word(alphas, nums), retreat=10)`, to specify the maximum number of characters pyparsing must look backward to make a match. pyparsing will check all the values from 1 up to retreat characters back from the current parse location. When stepping backwards through the input string, PrecededBy does *not* skip over whitespace. PrecededBy can be created with a results name so that, even though it always returns an empty parse result, the result *can* include named results. Idea first suggested in Issue #30 by Freakwill. - Updated FollowedBy to accept expressions that contain named results, so that results names defined in the lookahead expression will be returned, even though FollowedBy always returns an empty list. Inspired by the same feature implemented in PrecededBy. Version 2.2.2 - September, 2018 ------------------------------- - Fixed bug in SkipTo, if a SkipTo expression that was skipping to an expression that returned a list (such as an And), and the SkipTo was saved as a named result, the named result could be saved as a ParseResults - should always be saved as a string. Issue #28, reported by seron. - Added simple_unit_tests.py, as a collection of easy-to-follow unit tests for various classes and features of the pyparsing library. Primary intent is more to be instructional than actually rigorous testing. Complex tests can still be added in the unitTests.py file. - New features added to the Regex class: - optional asGroupList parameter, returns all the capture groups as a list - optional asMatch parameter, returns the raw re.match result - new sub(repl) method, which adds a parse action calling re.sub(pattern, repl, parsed_result). Simplifies creating Regex expressions to be used with transformString. Like re.sub, repl may be an ordinary string (similar to using pyparsing's replaceWith), or may contain references to capture groups by group number, or may be a callable that takes an re match group and returns a string. For instance: expr = pp.Regex(r"([Hh]\d):\s*(.*)").sub(r"<\1>\2") expr.transformString("h1: This is the title") will return

This is the title

- Fixed omission of LICENSE file in source tarball, also added CODE_OF_CONDUCT.md per GitHub community standards. Version 2.2.1 - September, 2018 ------------------------------- - Applied changes necessary to migrate hosting of pyparsing source over to GitHub. Many thanks for help and contributions from hugovk, jdufresne, and cngkaygusuz among others through this transition, sorry it took me so long! - Fixed import of collections.abc to address DeprecationWarnings in Python 3.7. - Updated oc.py example to support function calls in arithmetic expressions; fixed regex for '==' operator; and added packrat parsing. Raised on the pyparsing wiki by Boris Marin, thanks! - Fixed bug in select_parser.py example, group_by_terms was not reported. Reported on SF bugs by Adam Groszer, thanks Adam! - Added "Getting Started" section to the module docstring, to guide new users to the most common starting points in pyparsing's API. - Fixed bug in Literal and Keyword classes, which erroneously raised IndexError instead of ParseException. Version 2.2.0 - March, 2017 --------------------------- - Bumped minor version number to reflect compatibility issues with OneOrMore and ZeroOrMore bugfixes in 2.1.10. (2.1.10 fixed a bug that was introduced in 2.1.4, but the fix could break code written against 2.1.4 - 2.1.9.) - Updated setup.py to address recursive import problems now that pyparsing is part of 'packaging' (used by setuptools). Patch submitted by Joshua Root, much thanks! - Fixed KeyError issue reported by Yann Bizeul when using packrat parsing in the Graphite time series database, thanks Yann! - Fixed incorrect usages of '\' in literals, as described in https://docs.python.org/3/whatsnew/3.6.html#deprecated-python-behavior Patch submitted by Ville Skyttä - thanks! - Minor internal change when using '-' operator, to be compatible with ParserElement.streamline() method. - Expanded infixNotation to accept a list or tuple of parse actions to attach to an operation. - New unit test added for dill support for storing pyparsing parsers. Ordinary Python pickle can be used to pickle pyparsing parsers as long as they do not use any parse actions. The 'dill' module is an extension to pickle which *does* support pickling of attached parse actions. Version 2.1.10 - October, 2016 ------------------------------- - Fixed bug in reporting named parse results for ZeroOrMore expressions, thanks Ethan Nash for reporting this! - Fixed behavior of LineStart to be much more predictable. LineStart can now be used to detect if the next parse position is col 1, factoring in potential leading whitespace (which would cause LineStart to fail). Also fixed a bug in col, which is used in LineStart, where '\n's were erroneously considered to be column 1. - Added support for multiline test strings in runTests. - Fixed bug in ParseResults.dump when keys were not strings. Also changed display of string values to show them in quotes, to help distinguish parsed numeric strings from parsed integers that have been converted to Python ints. Version 2.1.9 - September, 2016 ------------------------------- - Added class CloseMatch, a variation on Literal which matches "close" matches, that is, strings with at most 'n' mismatching characters. - Fixed bug in Keyword.setDefaultKeywordChars(), reported by Kobayashi Shinji - nice catch, thanks! - Minor API change in pyparsing_common. Renamed some of the common expressions to PEP8 format (to be consistent with the other pyparsing_common expressions): . signedInteger -> signed_integer . sciReal -> sci_real Also, in trying to stem the API bloat of pyparsing, I've copied some of the global expressions and helper parse actions into pyparsing_common, with the originals to be deprecated and removed in a future release: . commaSeparatedList -> pyparsing_common.comma_separated_list . upcaseTokens -> pyparsing_common.upcaseTokens . downcaseTokens -> pyparsing_common.downcaseTokens (I don't expect any other expressions, like the comment expressions, quotedString, or the Word-helping strings like alphas, nums, etc. to migrate to pyparsing_common - they are just too pervasive. As for the PEP8 vs camelCase naming, all the expressions are PEP8, while the parse actions in pyparsing_common are still camelCase. It's a small step - when pyparsing 3.0 comes around, everything will change to PEP8 snake case.) - Fixed Python3 compatibility bug when using dict keys() and values() in ParseResults.getName(). - After some prodding, I've reworked the unitTests.py file for pyparsing over the past few releases. It uses some variations on unittest to handle my testing style. The test now: . auto-discovers its test classes (while maintining their order of definition) . suppresses voluminous 'print' output for tests that pass Version 2.1.8 - August, 2016 ---------------------------- - Fixed issue in the optimization to _trim_arity, when the full stacktrace is retrieved to determine if a TypeError is raised in pyparsing or in the caller's parse action. Code was traversing the full stacktrace, and potentially encountering UnicodeDecodeError. - Fixed bug in ParserElement.inlineLiteralsUsing, causing infinite loop with Suppress. - Fixed bug in Each, when merging named results from multiple expressions in a ZeroOrMore or OneOrMore. Also fixed bug when ZeroOrMore expressions were erroneously treated as required expressions in an Each expression. - Added a few more inline doc examples. - Improved use of runTests in several example scripts. Version 2.1.7 - August, 2016 ---------------------------- - Fixed regression reported by Andrea Censi (surfaced in PyContracts tests) when using ParseSyntaxExceptions (raised when using operator '-') with packrat parsing. - Minor fix to oneOf, to accept all iterables, not just space-delimited strings and lists. (If you have a list or set of strings, it is not necessary to concat them using ' '.join to pass them to oneOf, oneOf will accept the list or set or generator directly.) Version 2.1.6 - August, 2016 ---------------------------- - *Major packrat upgrade*, inspired by patch provided by Tal Einat - many, many, thanks to Tal for working on this! Tal's tests show faster parsing performance (2X in some tests), *and* memory reduction from 3GB down to ~100MB! Requires no changes to existing code using packratting. (Uses OrderedDict, available in Python 2.7 and later. For Python 2.6 users, will attempt to import from ordereddict backport. If not present, will implement pure-Python Fifo dict.) - Minor API change - to better distinguish between the flexible numeric types defined in pyparsing_common, I've changed "numeric" (which parsed numbers of different types and returned int for ints, float for floats, etc.) and "number" (which parsed numbers of int or float type, and returned all floats) to "number" and "fnumber" respectively. I hope the "f" prefix of "fnumber" will be a better indicator of its internal conversion of parsed values to floats, while the generic "number" is similar to the flexible number syntax in other languages. Also fixed a bug in pyparsing_common.numeric (now renamed to pyparsing_common.number), integers were parsed and returned as floats instead of being retained as ints. - Fixed bug in upcaseTokens and downcaseTokens introduced in 2.1.5, when the parse action was used in conjunction with results names. Reported by Steven Arcangeli from the dql project, thanks for your patience, Steven! - Major change to docs! After seeing some comments on reddit about general issue with docs of Python modules, and thinking that I'm a little overdue in doing some doc tuneup on pyparsing, I decided to following the suggestions of the redditor and add more inline examples to the pyparsing reference documentation. I hope this addition will clarify some of the more common questions people have, especially when first starting with pyparsing/Python. - Deprecated ParseResults.asXML. I've never been too happy with this method, and it usually forces some unnatural code in the parsers in order to get decent tag names. The amount of guesswork that asXML has to do to try to match names with values should have been a red flag from day one. If you are using asXML, you will need to implement your own ParseResults->XML serialization. Or consider migrating to a more current format such as JSON (which is very easy to do: results_as_json = json.dumps(parse_result.asDict()) Hopefully, when I remove this code in a future version, I'll also be able to simplify some of the craziness in ParseResults, which IIRC was only there to try to make asXML work. - Updated traceParseAction parse action decorator to show the repr of the input and output tokens, instead of the str format, since str has been simplified to just show the token list content. (The change to ParseResults.__str__ occurred in pyparsing 2.0.4, but it seems that didn't make it into the release notes - sorry! Too many users, especially beginners, were confused by the "([token_list], {names_dict})" str format for ParseResults, thinking they were getting a tuple containing a list and a dict. The full form can be seen if using repr().) For tracing tokens in and out of parse actions, the more complete repr form provides important information when debugging parse actions. Verison 2.1.5 - June, 2016 ------------------------------ - Added ParserElement.split() generator method, similar to re.split(). Includes optional arguments maxsplit (to limit the number of splits), and includeSeparators (to include the separating matched text in the returned output, default=False). - Added a new parse action construction helper tokenMap, which will apply a function and optional arguments to each element in a ParseResults. So this parse action: def lowercase_all(tokens): return [str(t).lower() for t in tokens] OneOrMore(Word(alphas)).setParseAction(lowercase_all) can now be written: OneOrMore(Word(alphas)).setParseAction(tokenMap(str.lower)) Also simplifies writing conversion parse actions like: integer = Word(nums).setParseAction(lambda t: int(t[0])) to just: integer = Word(nums).setParseAction(tokenMap(int)) If additional arguments are necessary, they can be included in the call to tokenMap, as in: hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16)) - Added more expressions to pyparsing_common: . IPv4 and IPv6 addresses (including long, short, and mixed forms of IPv6) . MAC address . ISO8601 date and date time strings (with named fields for year, month, etc.) . UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) . hex integer (returned as int) . fraction (integer '/' integer, returned as float) . mixed integer (integer '-' fraction, or just fraction, returned as float) . stripHTMLTags (parse action to remove tags from HTML source) . parse action helpers convertToDate and convertToDatetime to do custom parse time conversions of parsed ISO8601 strings - runTests now returns a two-tuple: success if all tests succeed, and an output list of each test and its output lines. - Added failureTests argument (default=False) to runTests, so that tests can be run that are expected failures, and runTests' success value will return True only if all tests *fail* as expected. Also, parseAll now defaults to True. - New example numerics.py, shows samples of parsing integer and real numbers using locale-dependent formats: 4.294.967.295,000 4 294 967 295,000 4,294,967,295.000 Version 2.1.4 - May, 2016 ------------------------------ - Split out the '==' behavior in ParserElement, now implemented as the ParserElement.matches() method. Using '==' for string test purposes will be removed in a future release. - Expanded capabilities of runTests(). Will now accept embedded comments (default is Python style, leading '#' character, but customizable). Comments will be emitted along with the tests and test output. Useful during test development, to create a test string consisting only of test case description comments separated by blank lines, and then fill in the test cases. Will also highlight ParseFatalExceptions with "(FATAL)". - Added a 'pyparsing_common' class containing common/helpful little expressions such as integer, float, identifier, etc. I used this class as a sort of embedded namespace, to contain these helpers without further adding to pyparsing's namespace bloat. - Minor enhancement to traceParseAction decorator, to retain the parse action's name for the trace output. - Added optional 'fatal' keyword arg to addCondition, to indicate that a condition failure should halt parsing immediately. Version 2.1.3 - May, 2016 ------------------------------ - _trim_arity fix in 2.1.2 was very version-dependent on Py 3.5.0. Now works for Python 2.x, 3.3, 3.4, 3.5.0, and 3.5.1 (and hopefully beyond). Version 2.1.2 - May, 2016 ------------------------------ - Fixed bug in _trim_arity when pyparsing code is included in a PyInstaller, reported by maluwa. - Fixed catastrophic regex backtracking in implementation of the quoted string expressions (dblQuotedString, sglQuotedString, and quotedString). Reported on the pyparsing wiki by webpentest, good catch! (Also tuned up some other expressions susceptible to the same backtracking problem, such as cStyleComment, cppStyleComment, etc.) Version 2.1.1 - March, 2016 --------------------------- - Added support for assigning to ParseResults using slices. - Fixed bug in ParseResults.toDict(), in which dict values were always converted to dicts, even if they were just unkeyed lists of tokens. Reported on SO by Gerald Thibault, thanks Gerald! - Fixed bug in SkipTo when using failOn, reported by robyschek, thanks! - Fixed bug in Each introduced in 2.1.0, reported by AND patch and unit test submitted by robyschek, well done! - Removed use of functools.partial in replaceWith, as this creates an ambiguous signature for the generated parse action, which fails in PyPy. Reported by Evan Hubinger, thanks Evan! - Added default behavior to QuotedString to convert embedded '\t', '\n', etc. characters to their whitespace counterparts. Found during Q&A exchange on SO with Maxim. Version 2.1.0 - February, 2016 ------------------------------ - Modified the internal _trim_arity method to distinguish between TypeError's raised while trying to determine parse action arity and those raised within the parse action itself. This will clear up those confusing "() takes exactly 1 argument (0 given)" error messages when there is an actual TypeError in the body of the parse action. Thanks to all who have raised this issue in the past, and most recently to Michael Cohen, who sent in a proposed patch, and got me to finally tackle this problem. - Added compatibility for pickle protocols 2-4 when pickling ParseResults. In Python 2.x, protocol 0 was the default, and protocol 2 did not work. In Python 3.x, protocol 3 is the default, so explicitly naming protocol 0 or 1 was required to pickle ParseResults. With this release, all protocols 0-4 are supported. Thanks for reporting this on StackOverflow, Arne Wolframm, and for providing a nice simple test case! - Added optional 'stopOn' argument to ZeroOrMore and OneOrMore, to simplify breaking on stop tokens that would match the repetition expression. It is a common problem to fail to look ahead when matching repetitive tokens if the sentinel at the end also matches the repetition expression, as when parsing "BEGIN aaa bbb ccc END" with: "BEGIN" + OneOrMore(Word(alphas)) + "END" Since "END" matches the repetition expression "Word(alphas)", it will never get parsed as the terminating sentinel. Up until now, this has to be resolved by the user inserting their own negative lookahead: "BEGIN" + OneOrMore(~Literal("END") + Word(alphas)) + "END" Using stopOn, they can more easily write: "BEGIN" + OneOrMore(Word(alphas), stopOn="END") + "END" The stopOn argument can be a literal string or a pyparsing expression. Inspired by a question by Lamakaha on StackOverflow (and many previous questions with the same negative-lookahead resolution). - Added expression names for many internal and builtin expressions, to reduce name and error message overhead during parsing. - Converted helper lambdas to functions to refactor and add docstring support. - Fixed ParseResults.asDict() to correctly convert nested ParseResults values to dicts. - Cleaned up some examples, fixed typo in fourFn.py identified by aristotle2600 on reddit. - Removed keepOriginalText helper method, which was deprecated ages ago. Superceded by originalTextFor. - Same for the Upcase class, which was long ago deprecated and replaced with the upcaseTokens method. Version 2.0.7 - December, 2015 ------------------------------ - Simplified string representation of Forward class, to avoid memory and performance errors while building ParseException messages. Thanks, Will McGugan, Andrea Censi, and Martijn Vermaat for the bug reports and test code. - Cleaned up additional issues from enhancing the error messages for Or and MatchFirst, handling Unicode values in expressions. Fixes Unicode encoding issues in Python 2, thanks to Evan Hubinger for the bug report. - Fixed implementation of dir() for ParseResults - was leaving out all the defined methods and just adding the custom results names. - Fixed bug in ignore() that was introduced in pyparsing 1.5.3, that would not accept a string literal as the ignore expression. - Added new example parseTabularData.py to illustrate parsing of data formatted in columns, with detection of empty cells. - Updated a number of examples to more current Python and pyparsing forms. Version 2.0.6 - November, 2015 ------------------------------ - Fixed a bug in Each when multiple Optional elements are present. Thanks for reporting this, whereswalden on SO. - Fixed another bug in Each, when Optional elements have results names or parse actions, reported by Max Rothman - thank you, Max! - Added optional parseAll argument to runTests, whether tests should require the entire input string to be parsed or not (similar to parseAll argument to parseString). Plus a little neaten-up of the output on Python 2 (no stray ()'s). - Modified exception messages from MatchFirst and Or expressions. These were formerly misleading as they would only give the first or longest exception mismatch error message. Now the error message includes all the alternatives that were possible matches. Originally proposed by a pyparsing user, but I've lost the email thread - finally figured out a fairly clean way to do this. - Fixed a bug in Or, when a parse action on an alternative raises an exception, other potentially matching alternatives were not always tried. Reported by TheVeryOmni on the pyparsing wiki, thanks! - Fixed a bug to dump() introduced in 2.0.4, where list values were shown in duplicate. Version 2.0.5 - October, 2015 ----------------------------- - (&$(@#&$(@!!!! Some "print" statements snuck into pyparsing v2.0.4, breaking Python 3 compatibility! Fixed. Reported by jenshn, thanks! Version 2.0.4 - October, 2015 ----------------------------- - Added ParserElement.addCondition, to simplify adding parse actions that act primarily as filters. If the given condition evaluates False, pyparsing will raise a ParseException. The condition should be a method with the same method signature as a parse action, but should return a boolean. Suggested by Victor Porton, nice idea Victor, thanks! - Slight mod to srange to accept unicode literals for the input string, such as "[а-яА-Я]" instead of "[\u0430-\u044f\u0410-\u042f]". Thanks to Alexandr Suchkov for the patch! - Enhanced implementation of replaceWith. - Fixed enhanced ParseResults.dump() method when the results consists only of an unnamed array of sub-structure results. Reported by Robin Siebler, thanks for your patience and persistence, Robin! - Fixed bug in fourFn.py example code, where pi and e were defined using CaselessLiteral instead of CaselessKeyword. This was not a problem until adding a new function 'exp', and the leading 'e' of 'exp' was accidentally parsed as the mathematical constant 'e'. Nice catch, Tom Grydeland - thanks! - Adopt new-fangled Python features, like decorators and ternary expressions, per suggestions from Williamzjc - thanks William! (Oh yeah, I'm not supporting Python 2.3 with this code any more...) Plus, some additional code fixes/cleanup - thanks again! - Added ParserElement.runTests, a little test bench for quickly running an expression against a list of sample input strings. Basically, I got tired of writing the same test code over and over, and finally added it as a test point method on ParserElement. - Added withClass helper method, a simplified version of withAttribute for the common but annoying case when defining a filter on a div's class - made difficult because 'class' is a Python reserved word. Version 2.0.3 - October, 2014 ----------------------------- - Fixed escaping behavior in QuotedString. Formerly, only quotation marks (or characters designated as quotation marks in the QuotedString constructor) would be escaped. Now all escaped characters will be escaped, and the escaping backslashes will be removed. - Fixed regression in ParseResults.pop() - pop() was pretty much broken after I added *improvements* in 2.0.2. Reported by Iain Shelvington, thanks Iain! - Fixed bug in And class when initializing using a generator. - Enhanced ParseResults.dump() method to list out nested ParseResults that are unnamed arrays of sub-structures. - Fixed UnboundLocalError under Python 3.4 in oneOf method, reported on Sourceforge by aldanor, thanks! - Fixed bug in ParseResults __init__ method, when returning non-ParseResults types from parse actions that implement __eq__. Raised during discussion on the pyparsing wiki with cyrfer. Version 2.0.2 - April, 2014 --------------------------- - Extended "expr(name)" shortcut (same as "expr.setResultsName(name)") to accept "expr()" as a shortcut for "expr.copy()". - Added "locatedExpr(expr)" helper, to decorate any returned tokens with their location within the input string. Adds the results names locn_start and locn_end to the output parse results. - Added "pprint()" method to ParseResults, to simplify troubleshooting and prettified output. Now instead of importing the pprint module and then writing "pprint.pprint(result)", you can just write "result.pprint()". This method also accepts addtional positional and keyword arguments (such as indent, width, etc.), which get passed through directly to the pprint method (see https://docs.python.org/2/library/pprint.html#pprint.pprint). - Removed deprecation warnings when using '<<' for Forward expression assignment. '<<=' is still preferred, but '<<' will be retained for cases where '<<=' operator is not suitable (such as in defining lambda expressions). - Expanded argument compatibility for classes and functions that take list arguments, to now accept generators as well. - Extended list-like behavior of ParseResults, adding support for append and extend. NOTE: if you have existing applications using these names as results names, you will have to access them using dict-style syntax: res["append"] and res["extend"] - ParseResults emulates the change in list vs. iterator semantics for methods like keys(), values(), and items(). Under Python 2.x, these methods will return lists, under Python 3.x, these methods will return iterators. - ParseResults now has a method haskeys() which returns True or False depending on whether any results names have been defined. This simplifies testing for the existence of results names under Python 3.x, which returns keys() as an iterator, not a list. - ParseResults now supports both list and dict semantics for pop(). If passed no argument or an integer argument, it will use list semantics and pop tokens from the list of parsed tokens. If passed a non-integer argument (most likely a string), it will use dict semantics and pop the corresponding value from any defined results names. A second default return value argument is supported, just as in dict.pop(). - Fixed bug in markInputline, thanks for reporting this, Matt Grant! - Cleaned up my unit test environment, now runs with Python 2.6 and 3.3. Version 2.0.1 - July, 2013 -------------------------- - Removed use of "nonlocal" that prevented using this version of pyparsing with Python 2.6 and 2.7. This will make it easier to install for packages that depend on pyparsing, under Python versions 2.6 and later. Those using older versions of Python will have to manually install pyparsing 1.5.7. - Fixed implementation of <<= operator to return self; reported by Luc J. Bourhis, with patch fix by Mathias Mamsch - thanks, Luc and Mathias! Version 2.0.0 - November, 2012 ------------------------------ - Rather than release another combined Python 2.x/3.x release I've decided to start a new major version that is only compatible with Python 3.x (and consequently Python 2.7 as well due to backporting of key features). This version will be the main development path from now on, with little follow-on development on the 1.5.x path. - Operator '<<' is now deprecated, in favor of operator '<<=' for attaching parsing expressions to Forward() expressions. This is being done to address precedence of operations problems with '<<'. Operator '<<' will be removed in a future version of pyparsing. Version 1.5.7 - November, 2012 ----------------------------- - NOTE: This is the last release of pyparsing that will try to maintain compatibility with Python versions < 2.6. The next release of pyparsing will be version 2.0.0, using new Python syntax that will not be compatible for Python version 2.5 or older. - An awesome new example is included in this release, submitted by Luca DellOlio, for parsing ANTLR grammar definitions, nice work Luca! - Fixed implementation of ParseResults.__str__ to use Pythonic ''.join() instead of repeated string concatenation. This purportedly has been a performance issue under PyPy. - Fixed bug in ParseResults.__dir__ under Python 3, reported by Thomas Kluyver, thank you Thomas! - Added ParserElement.inlineLiteralsUsing static method, to override pyparsing's default behavior of converting string literals to Literal instances, to use other classes (such as Suppress or CaselessLiteral). - Added new operator '<<=', which will eventually replace '<<' for storing the contents of a Forward(). '<<=' does not have the same operator precedence problems that '<<' does. - 'operatorPrecedence' is being renamed 'infixNotation' as a better description of what this helper function creates. 'operatorPrecedence' is deprecated, and will be dropped entirely in a future release. - Added optional arguments lpar and rpar to operatorPrecedence, so that expressions that use it can override the default suppression of the grouping characters. - Added support for using single argument builtin functions as parse actions. Now you can write 'expr.setParseAction(len)' and get back the length of the list of matched tokens. Supported builtins are: sum, len, sorted, reversed, list, tuple, set, any, all, min, and max. A script demonstrating this feature is included in the examples directory. - Improved linking in generated docs, proposed on the pyparsing wiki by techtonik, thanks! - Fixed a bug in the definition of 'alphas', which was based on the string.uppercase and string.lowercase "constants", which in fact *aren't* constant, but vary with locale settings. This could make parsers locale-sensitive in a subtle way. Thanks to Kef Schecter for his diligence in following through on reporting and monitoring this bugfix! - Fixed a bug in the Py3 version of pyparsing, during exception handling with packrat parsing enabled, reported by Catherine Devlin - thanks Catherine! - Fixed typo in ParseBaseException.__dir__, reported anonymously on the SourceForge bug tracker, thank you Pyparsing User With No Name. - Fixed bug in srange when using '\x###' hex character codes. - Addeed optional 'intExpr' argument to countedArray, so that you can define your own expression that will evaluate to an integer, to be used as the count for the following elements. Allows you to define a countedArray with the count given in hex, for example, by defining intExpr as "Word(hexnums).setParseAction(int(t[0],16))". Version 1.5.6 - June, 2011 ---------------------------- - Cleanup of parse action normalizing code, to be more version-tolerant, and robust in the face of future Python versions - much thanks to Raymond Hettinger for this rewrite! - Removal of exception cacheing, addressing a memory leak condition in Python 3. Thanks to Michael Droettboom and the Cape Town PUG for their analysis and work on this problem! - Fixed bug when using packrat parsing, where a previously parsed expression would duplicate subsequent tokens - reported by Frankie Ribery on stackoverflow, thanks! - Added 'ungroup' helper method, to address token grouping done implicitly by And expressions, even if only one expression in the And actually returns any text - also inspired by stackoverflow discussion with Frankie Ribery! - Fixed bug in srange, which accepted escaped hex characters of the form '\0x##', but should be '\x##'. Both forms will be supported for backwards compatibility. - Enhancement to countedArray, accepting an optional expression to be used for matching the leading integer count - proposed by Mathias on the pyparsing mailing list, good idea! - Added the Verilog parser to the provided set of examples, under the MIT license. While this frees up this parser for any use, if you find yourself using it in a commercial purpose, please consider making a charitable donation as described in the parser's header. - Added the excludeChars argument to the Word class, to simplify defining a word composed of all characters in a large range except for one or two. Suggested by JesterEE on the pyparsing wiki. - Added optional overlap parameter to scanString, to return overlapping matches found in the source text. - Updated oneOf internal regular expression generation, with improved parse time performance. - Slight performance improvement in transformString, removing empty strings from the list of string fragments built while scanning the source text, before calling ''.join. Especially useful when using transformString to strip out selected text. - Enhanced form of using the "expr('name')" style of results naming, in lieu of calling setResultsName. If name ends with an '*', then this is equivalent to expr.setResultsName('name',listAllMatches=True). - Fixed up internal list flattener to use iteration instead of recursion, to avoid stack overflow when transforming large files. - Added other new examples: . protobuf parser - parses Google's protobuf language . btpyparse - a BibTex parser contributed by Matthew Brett, with test suite test_bibparse.py (thanks, Matthew!) . groupUsingListAllMatches.py - demo using trailing '*' for results names Version 1.5.5 - August, 2010 ---------------------------- - Typo in Python3 version of pyparsing, "builtin" should be "builtins". (sigh) Version 1.5.4 - August, 2010 ---------------------------- - Fixed __builtins__ and file references in Python 3 code, thanks to Greg Watson, saulspatz, sminos, and Mark Summerfield for reporting their Python 3 experiences. - Added new example, apicheck.py, as a sample of scanning a Tcl-like language for functions with incorrect number of arguments (difficult to track down in Tcl languages). This example uses some interesting methods for capturing exceptions while scanning through source code. - Added new example deltaTime.py, that takes everyday time references like "an hour from now", "2 days ago", "next Sunday at 2pm". Version 1.5.3 - June, 2010 -------------------------- - ======= NOTE: API CHANGE!!!!!!! =============== With this release, and henceforward, the pyparsing module is imported as "pyparsing" on both Python 2.x and Python 3.x versions. - Fixed up setup.py to auto-detect Python version and install the correct version of pyparsing - suggested by Alex Martelli, thanks, Alex! (and my apologies to all those who struggled with those spurious installation errors caused by my earlier fumblings!) - Fixed bug on Python3 when using parseFile, getting bytes instead of a str from the input file. - Fixed subtle bug in originalTextFor, if followed by significant whitespace (like a newline) - discovered by Francis Vidal, thanks! - Fixed very sneaky bug in Each, in which Optional elements were not completely recognized as optional - found by Tal Weiss, thanks for your patience. - Fixed off-by-1 bug in line() method when the first line of the input text was an empty line. Thanks to John Krukoff for submitting a patch! - Fixed bug in transformString if grammar contains Group expressions, thanks to patch submitted by barnabas79, nice work! - Fixed bug in originalTextFor in which trailing comments or otherwised ignored text got slurped in with the matched expression. Thanks to michael_ramirez44 on the pyparsing wiki for reporting this just in time to get into this release! - Added better support for summing ParseResults, see the new example, parseResultsSumExample.py. - Added support for composing a Regex using a compiled RE object; thanks to my new colleague, Mike Thornton! - In version 1.5.2, I changed the way exceptions are raised in order to simplify the stacktraces reported during parsing. An anonymous user posted a bug report on SF that this behavior makes it difficult to debug some complex parsers, or parsers nested within parsers. In this release I've added a class attribute ParserElement.verbose_stacktrace, with a default value of False. If you set this to True, pyparsing will report stacktraces using the pre-1.5.2 behavior. - New examples: . pymicko.py, a MicroC compiler submitted by Zarko Zivanov. (Note: this example is separately licensed under the GPLv3, and requires Python 2.6 or higher.) Thank you, Zarko! . oc.py, a subset C parser, using the BNF from the 1996 Obfuscated C Contest. . stateMachine2.py, a modified version of stateMachine.py submitted by Matt Anderson, that is compatible with Python versions 2.7 and above - thanks so much, Matt! . select_parser.py, a parser for reading SQLite SELECT statements, as specified at https://www.sqlite.org/lang_select.html this goes into much more detail than the simple SQL parser included in pyparsing's source code . excelExpr.py, a *simplistic* first-cut at a parser for Excel expressions, which I originally posted on comp.lang.python in January, 2010; beware, this parser omits many common Excel cases (addition of numbers represented as strings, references to named ranges) . cpp_enum_parser.py, a nice little parser posted my Mark Tolonen on comp.lang.python in August, 2009 (redistributed here with Mark's permission). Thanks a bunch, Mark! . partial_gene_match.py, a sample I posted to Stackoverflow.com, implementing a special variation on Literal that does "close" matching, up to a given number of allowed mismatches. The application was to find matching gene sequences, with allowance for one or two mismatches. . tagCapture.py, a sample showing how to use a Forward placeholder to enforce matching of text parsed in a previous expression. . matchPreviousDemo.py, simple demo showing how the matchPreviousLiteral helper method is used to match a previously parsed token. Version 1.5.2 - April, 2009 ------------------------------ - Added pyparsing_py3.py module, so that Python 3 users can use pyparsing by changing their pyparsing import statement to: import pyparsing_py3 Thanks for help from Patrick Laban and his friend Geremy Condra on the pyparsing wiki. - Removed __slots__ declaration on ParseBaseException, for compatibility with IronPython 2.0.1. Raised by David Lawler on the pyparsing wiki, thanks David! - Fixed bug in SkipTo/failOn handling - caught by eagle eye cpennington on the pyparsing wiki! - Fixed second bug in SkipTo when using the ignore constructor argument, reported by Catherine Devlin, thanks! - Fixed obscure bug reported by Eike Welk when using a class as a ParseAction with an errant __getitem__ method. - Simplified exception stack traces when reporting parse exceptions back to caller of parseString or parseFile - thanks to a tip from Peter Otten on comp.lang.python. - Changed behavior of scanString to avoid infinitely looping on expressions that match zero-length strings. Prompted by a question posted by ellisonbg on the wiki. - Enhanced classes that take a list of expressions (And, Or, MatchFirst, and Each) to accept generator expressions also. This can be useful when generating lists of alternative expressions, as in this case, where the user wanted to match any repetitions of '+', '*', '#', or '.', but not mixtures of them (that is, match '+++', but not '+-+'): codes = "+*#." format = MatchFirst(Word(c) for c in codes) Based on a problem posed by Denis Spir on the Python tutor list. - Added new example eval_arith.py, which extends the example simpleArith.py to actually evaluate the parsed expressions. Version 1.5.1 - October, 2008 ------------------------------- - Added new helper method originalTextFor, to replace the use of the current keepOriginalText parse action. Now instead of using the parse action, as in: fullName = Word(alphas) + Word(alphas) fullName.setParseAction(keepOriginalText) (in this example, we used keepOriginalText to restore any white space that may have been skipped between the first and last names) You can now write: fullName = originalTextFor(Word(alphas) + Word(alphas)) The implementation of originalTextFor is simpler and faster than keepOriginalText, and does not depend on using the inspect or imp modules. - Added optional parseAll argument to parseFile, to be consistent with parseAll argument to parseString. Posted by pboucher on the pyparsing wiki, thanks! - Added failOn argument to SkipTo, so that grammars can define literal strings or pyparsing expressions which, if found in the skipped text, will cause SkipTo to fail. Useful to prevent SkipTo from reading past terminating expression. Instigated by question posed by Aki Niimura on the pyparsing wiki. - Fixed bug in nestedExpr if multi-character expressions are given for nesting delimiters. Patch provided by new pyparsing user, Hans-Martin Gaudecker - thanks, H-M! - Removed dependency on xml.sax.saxutils.escape, and included internal implementation instead - proposed by Mike Droettboom on the pyparsing mailing list, thanks Mike! Also fixed erroneous mapping in replaceHTMLEntity of " to ', now correctly maps to ". (Also added support for mapping ' to '.) - Fixed typo in ParseResults.insert, found by Alejandro Dubrovsky, good catch! - Added __dir__() methods to ParseBaseException and ParseResults, to support new dir() behavior in Py2.6 and Py3.0. If dir() is called on a ParseResults object, the returned list will include the base set of attribute names, plus any results names that are defined. - Fixed bug in ParseResults.asXML(), in which the first named item within a ParseResults gets reported with an tag instead of with the correct results name. - Fixed bug in '-' error stop, when '-' operator is used inside a Combine expression. - Reverted generator expression to use list comprehension, for better compatibility with old versions of Python. Reported by jester/artixdesign on the SourceForge pyparsing discussion list. - Fixed bug in parseString(parseAll=True), when the input string ends with a comment or whitespace. - Fixed bug in LineStart and LineEnd that did not recognize any special whitespace chars defined using ParserElement.setDefault- WhitespaceChars, found while debugging an issue for Marek Kubica, thanks for the new test case, Marek! - Made Forward class more tolerant of subclassing. Version 1.5.0 - June, 2008 -------------------------- This version of pyparsing includes work on two long-standing FAQ's: support for forcing parsing of the complete input string (without having to explicitly append StringEnd() to the grammar), and a method to improve the mechanism of detecting where syntax errors occur in an input string with various optional and alternative paths. This release also includes a helper method to simplify definition of indentation-based grammars. With these changes (and the past few minor updates), I thought it was finally time to bump the minor rev number on pyparsing - so 1.5.0 is now available! Read on... - AT LAST!!! You can now call parseString and have it raise an exception if the expression does not parse the entire input string. This has been an FAQ for a LONG time. The parseString method now includes an optional parseAll argument (default=False). If parseAll is set to True, then the given parse expression must parse the entire input string. (This is equivalent to adding StringEnd() to the end of the expression.) The default value is False to retain backward compatibility. Inspired by MANY requests over the years, most recently by ecir-hana on the pyparsing wiki! - Added new operator '-' for composing grammar sequences. '-' behaves just like '+' in creating And expressions, but '-' is used to mark grammar structures that should stop parsing immediately and report a syntax error, rather than just backtracking to the last successful parse and trying another alternative. For instance, running the following code: port_definition = Keyword("port") + '=' + Word(nums) entity_definition = Keyword("entity") + "{" + Optional(port_definition) + "}" entity_definition.parseString("entity { port 100 }") pyparsing fails to detect the missing '=' in the port definition. But, since this expression is optional, pyparsing then proceeds to try to match the closing '}' of the entity_definition. Not finding it, pyparsing reports that there was no '}' after the '{' character. Instead, we would like pyparsing to parse the 'port' keyword, and if not followed by an equals sign and an integer, to signal this as a syntax error. This can now be done simply by changing the port_definition to: port_definition = Keyword("port") - '=' + Word(nums) Now after successfully parsing 'port', pyparsing must also find an equals sign and an integer, or it will raise a fatal syntax exception. By judicious insertion of '-' operators, a pyparsing developer can have their grammar report much more informative syntax error messages. Patches and suggestions proposed by several contributors on the pyparsing mailing list and wiki - special thanks to Eike Welk and Thomas/Poldy on the pyparsing wiki! - Added indentedBlock helper method, to encapsulate the parse actions and indentation stack management needed to keep track of indentation levels. Use indentedBlock to define grammars for indentation-based grouping grammars, like Python's. indentedBlock takes up to 3 parameters: - blockStatementExpr - expression defining syntax of statement that is repeated within the indented block - indentStack - list created by caller to manage indentation stack (multiple indentedBlock expressions within a single grammar should share a common indentStack) - indent - boolean indicating whether block must be indented beyond the current level; set to False for block of left-most statements (default=True) A valid block must contain at least one indented statement. - Fixed bug in nestedExpr in which ignored expressions needed to be set off with whitespace. Reported by Stefaan Himpe, nice catch! - Expanded multiplication of an expression by a tuple, to accept tuple values of None: . expr*(n,None) or expr*(n,) is equivalent to expr*n + ZeroOrMore(expr) (read as "at least n instances of expr") . expr*(None,n) is equivalent to expr*(0,n) (read as "0 to n instances of expr") . expr*(None,None) is equivalent to ZeroOrMore(expr) . expr*(1,None) is equivalent to OneOrMore(expr) Note that expr*(None,n) does not raise an exception if more than n exprs exist in the input stream; that is, expr*(None,n) does not enforce a maximum number of expr occurrences. If this behavior is desired, then write expr*(None,n) + ~expr - Added None as a possible operator for operatorPrecedence. None signifies "no operator", as in multiplying m times x in "y=mx+b". - Fixed bug in Each, reported by Michael Ramirez, in which the order of terms in the Each affected the parsing of the results. Problem was due to premature grouping of the expressions in the overall Each during grammar construction, before the complete Each was defined. Thanks, Michael! - Also fixed bug in Each in which Optional's with default values were not getting the defaults added to the results of the overall Each expression. - Fixed a bug in Optional in which results names were not assigned if a default value was supplied. - Cleaned up Py3K compatibility statements, including exception construction statements, and better equivalence between _ustr and basestring, and __nonzero__ and __bool__. Version 1.4.11 - February, 2008 ------------------------------- - With help from Robert A. Clark, this version of pyparsing is compatible with Python 3.0a3. Thanks for the help, Robert! - Added WordStart and WordEnd positional classes, to support expressions that must occur at the start or end of a word. Proposed by piranha on the pyparsing wiki, good idea! - Added matchOnlyAtCol helper parser action, to simplify parsing log or data files that have optional fields that are column dependent. Inspired by a discussion thread with hubritic on comp.lang.python. - Added withAttribute.ANY_VALUE as a match-all value when using withAttribute. Used to ensure that an attribute is present, without having to match on the actual attribute value. - Added get() method to ParseResults, similar to dict.get(). Suggested by new pyparsing user, Alejandro Dubrovksy, thanks! - Added '==' short-cut to see if a given string matches a pyparsing expression. For instance, you can now write: integer = Word(nums) if "123" == integer: # do something print [ x for x in "123 234 asld".split() if x==integer ] # prints ['123', '234'] - Simplified the use of nestedExpr when using an expression for the opening or closing delimiters. Now the content expression will not have to explicitly negate closing delimiters. Found while working with dfinnie on GHOP Task #277, thanks! - Fixed bug when defining ignorable expressions that are later enclosed in a wrapper expression (such as ZeroOrMore, OneOrMore, etc.) - found while working with Prabhu Gurumurthy, thanks Prahbu! - Fixed bug in withAttribute in which keys were automatically converted to lowercase, making it impossible to match XML attributes with uppercase characters in them. Using with- Attribute requires that you reference attributes in all lowercase if parsing HTML, and in correct case when parsing XML. - Changed '<<' operator on Forward to return None, since this is really used as a pseudo-assignment operator, not as a left-shift operator. By returning None, it is easier to catch faulty statements such as a << b | c, where precedence of operations causes the '|' operation to be performed *after* inserting b into a, so no alternation is actually implemented. The correct form is a << (b | c). With this change, an error will be reported instead of silently clipping the alternative term. (Note: this may break some existing code, but if it does, the code had a silent bug in it anyway.) Proposed by wcbarksdale on the pyparsing wiki, thanks! - Several unit tests were added to pyparsing's regression suite, courtesy of the Google Highly-Open Participation Contest. Thanks to all who administered and took part in this event! Version 1.4.10 - December 9, 2007 --------------------------------- - Fixed bug introduced in v1.4.8, parse actions were called for intermediate operator levels, not just the deepest matching operation level. Again, big thanks to Torsten Marek for helping isolate this problem! Version 1.4.9 - December 8, 2007 -------------------------------- - Added '*' multiplication operator support when creating grammars, accepting either an integer, or a two-integer tuple multiplier, as in: ipAddress = Word(nums) + ('.'+Word(nums))*3 usPhoneNumber = Word(nums) + ('-'+Word(nums))*(1,2) If multiplying by a tuple, the two integer values represent min and max multiples. Suggested by Vincent of eToy.com, great idea, Vincent! - Fixed bug in nestedExpr, original version was overly greedy! Thanks to Michael Ramirez for raising this issue. - Fixed internal bug in ParseResults - when an item was deleted, the key indices were not updated. Thanks to Tim Mitchell for posting a bugfix patch to the SF bug tracking system! - Fixed internal bug in operatorPrecedence - when the results of a right-associative term were sent to a parse action, the wrong tokens were sent. Reported by Torsten Marek, nice job! - Added pop() method to ParseResults. If pop is called with an integer or with no arguments, it will use list semantics and update the ParseResults' list of tokens. If pop is called with a non-integer (a string, for instance), then it will use dict semantics and update the ParseResults' internal dict. Suggested by Donn Ingle, thanks Donn! - Fixed quoted string built-ins to accept '\xHH' hex characters within the string. Version 1.4.8 - October, 2007 ----------------------------- - Added new helper method nestedExpr to easily create expressions that parse lists of data in nested parentheses, braces, brackets, etc. - Added withAttribute parse action helper, to simplify creating filtering parse actions to attach to expressions returned by makeHTMLTags and makeXMLTags. Use withAttribute to qualify a starting tag with one or more required attribute values, to avoid false matches on common tags such as or
. - Added new examples nested.py and withAttribute.py to demonstrate the new features. - Added performance speedup to grammars using operatorPrecedence, instigated by Stefan Reichör - thanks for the feedback, Stefan! - Fixed bug/typo when deleting an element from a ParseResults by using the element's results name. - Fixed whitespace-skipping bug in wrapper classes (such as Group, Suppress, Combine, etc.) and when using setDebug(), reported by new pyparsing user dazzawazza on SourceForge, nice job! - Added restriction to prevent defining Word or CharsNotIn expressions with minimum length of 0 (should use Optional if this is desired), and enhanced docstrings to reflect this limitation. Issue was raised by Joey Tallieu, who submitted a patch with a slightly different solution. Thanks for taking the initiative, Joey, and please keep submitting your ideas! - Fixed bug in makeHTMLTags that did not detect HTML tag attributes with no '= value' portion (such as ""), reported by hamidh on the pyparsing wiki - thanks! - Fixed minor bug in makeHTMLTags and makeXMLTags, which did not accept whitespace in closing tags. Version 1.4.7 - July, 2007 -------------------------- - NEW NOTATION SHORTCUT: ParserElement now accepts results names using a notational shortcut, following the expression with the results name in parentheses. So this: stats = "AVE:" + realNum.setResultsName("average") + \ "MIN:" + realNum.setResultsName("min") + \ "MAX:" + realNum.setResultsName("max") can now be written as this: stats = "AVE:" + realNum("average") + \ "MIN:" + realNum("min") + \ "MAX:" + realNum("max") The intent behind this change is to make it simpler to define results names for significant fields within the expression, while keeping the grammar syntax clean and uncluttered. - Fixed bug when packrat parsing is enabled, with cached ParseResults being updated by subsequent parsing. Reported on the pyparsing wiki by Kambiz, thanks! - Fixed bug in operatorPrecedence for unary operators with left associativity, if multiple operators were given for the same term. - Fixed bug in example simpleBool.py, corrected precedence of "and" vs. "or" operations. - Fixed bug in Dict class, in which keys were converted to strings whether they needed to be or not. Have narrowed this logic to convert keys to strings only if the keys are ints (which would confuse __getitem__ behavior for list indexing vs. key lookup). - Added ParserElement method setBreak(), which will invoke the pdb module's set_trace() function when this expression is about to be parsed. - Fixed bug in StringEnd in which reading off the end of the input string raises an exception - should match. Resolved while answering a question for Shawn on the pyparsing wiki. Version 1.4.6 - April, 2007 --------------------------- - Simplified constructor for ParseFatalException, to support common exception construction idiom: raise ParseFatalException, "unexpected text: 'Spanish Inquisition'" - Added method getTokensEndLoc(), to be called from within a parse action, for those parse actions that need both the starting *and* ending location of the parsed tokens within the input text. - Enhanced behavior of keepOriginalText so that named parse fields are preserved, even though tokens are replaced with the original input text matched by the current expression. Also, cleaned up the stack traversal to be more robust. Suggested by Tim Arnold - thanks, Tim! - Fixed subtle bug in which countedArray (and similar dynamic expressions configured in parse actions) failed to match within Or, Each, FollowedBy, or NotAny. Reported by Ralf Vosseler, thanks for your patience, Ralf! - Fixed Unicode bug in upcaseTokens and downcaseTokens parse actions, scanString, and default debugging actions; reported (and patch submitted) by Nikolai Zamkovoi, spasibo! - Fixed bug when saving a tuple as a named result. The returned token list gave the proper tuple value, but accessing the result by name only gave the first element of the tuple. Reported by Poromenos, nice catch! - Fixed bug in makeHTMLTags/makeXMLTags, which failed to match tag attributes with namespaces. - Fixed bug in SkipTo when setting include=True, to have the skipped-to tokens correctly included in the returned data. Reported by gunars on the pyparsing wiki, thanks! - Fixed typobug in OnceOnly.reset method, omitted self argument. Submitted by eike welk, thanks for the lint-picking! - Added performance enhancement to Forward class, suggested by akkartik on the pyparsing Wiki discussion, nice work! - Added optional asKeyword to Word constructor, to indicate that the given word pattern should be matched only as a keyword, that is, it should only match if it is within word boundaries. - Added S-expression parser to examples directory. - Added macro substitution example to examples directory. - Added holaMundo.py example, excerpted from Marco Alfonso's blog - muchas gracias, Marco! - Modified internal cyclic references in ParseResults to use weakrefs; this should help reduce the memory footprint of large parsing programs, at some cost to performance (3-5%). Suggested by bca48150 on the pyparsing wiki, thanks! - Enhanced the documentation describing the vagaries and idiosyncracies of parsing strings with embedded tabs, and the impact on: . parse actions . scanString . col and line helper functions (Suggested by eike welk in response to some unexplained inconsistencies between parsed location and offsets in the input string.) - Cleaned up internal decorators to preserve function names, docstrings, etc. Version 1.4.5 - December, 2006 ------------------------------ - Removed debugging print statement from QuotedString class. Sorry for not stripping this out before the 1.4.4 release! - A significant performance improvement, the first one in a while! For my Verilog parser, this version of pyparsing is about double the speed - YMMV. - Added support for pickling of ParseResults objects. (Reported by Jeff Poole, thanks Jeff!) - Fixed minor bug in makeHTMLTags that did not recognize tag attributes with embedded '-' or '_' characters. Also, added support for passing expressions to makeHTMLTags and makeXMLTags, and used this feature to define the globals anyOpenTag and anyCloseTag. - Fixed error in alphas8bit, I had omitted the y-with-umlaut character. - Added punc8bit string to complement alphas8bit - it contains all the non-alphabetic, non-blank 8-bit characters. - Added commonHTMLEntity expression, to match common HTML "ampersand" codes, such as "<", ">", "&", " ", and """. This expression also defines a results name 'entity', which can be used to extract the entity field (that is, "lt", "gt", etc.). Also added built-in parse action replaceHTMLEntity, which can be attached to commonHTMLEntity to translate "<", ">", "&", " ", and """ to "<", ">", "&", " ", and "'". - Added example, htmlStripper.py, that strips HTML tags and scripts from HTML pages. It also translates common HTML entities to their respective characters. Version 1.4.4 - October, 2006 ------------------------------- - Fixed traceParseAction decorator to also trap and record exception returns from parse actions, and to handle parse actions with 0, 1, 2, or 3 arguments. - Enhanced parse action normalization to support using classes as parse actions; that is, the class constructor is called at parse time and the __init__ function is called with 0, 1, 2, or 3 arguments. If passing a class as a parse action, the __init__ method must use one of the valid parse action parameter list formats. (This technique is useful when using pyparsing to compile parsed text into a series of application objects - see the new example simpleBool.py.) - Fixed bug in ParseResults when setting an item using an integer index. (Reported by Christopher Lambacher, thanks!) - Fixed whitespace-skipping bug, patch submitted by Paolo Losi - grazie, Paolo! - Fixed bug when a Combine contained an embedded Forward expression, reported by cie on the pyparsing wiki - good catch! - Fixed listAllMatches bug, when a listAllMatches result was nested within another result. (Reported by don pasquale on comp.lang.python, well done!) - Fixed bug in ParseResults items() method, when returning an item marked as listAllMatches=True - Fixed bug in definition of cppStyleComment (and javaStyleComment) in which '//' line comments were not continued to the next line if the line ends with a '\'. (Reported by eagle-eyed Ralph Corderoy!) - Optimized re's for cppStyleComment and quotedString for better re performance - also provided by Ralph Corderoy, thanks! - Added new example, indentedGrammarExample.py, showing how to define a grammar using indentation to show grouping (as Python does for defining statement nesting). Instigated by an e-mail discussion with Andrew Dalke, thanks Andrew! - Added new helper operatorPrecedence (based on e-mail list discussion with Ralph Corderoy and Paolo Losi), to facilitate definition of grammars for expressions with unary and binary operators. For instance, this grammar defines a 6-function arithmetic expression grammar, with unary plus and minus, proper operator precedence,and right- and left-associativity: expr = operatorPrecedence( operand, [("!", 1, opAssoc.LEFT), ("^", 2, opAssoc.RIGHT), (oneOf("+ -"), 1, opAssoc.RIGHT), (oneOf("* /"), 2, opAssoc.LEFT), (oneOf("+ -"), 2, opAssoc.LEFT),] ) Also added example simpleArith.py and simpleBool.py to provide more detailed code samples using this new helper method. - Added new helpers matchPreviousLiteral and matchPreviousExpr, for creating adaptive parsing expressions that match the same content as was parsed in a previous parse expression. For instance: first = Word(nums) matchExpr = first + ":" + matchPreviousLiteral(first) will match "1:1", but not "1:2". Since this matches at the literal level, this will also match the leading "1:1" in "1:10". In contrast: first = Word(nums) matchExpr = first + ":" + matchPreviousExpr(first) will *not* match the leading "1:1" in "1:10"; the expressions are evaluated first, and then compared, so "1" is compared with "10". - Added keepOriginalText parse action. Sometimes pyparsing's whitespace-skipping leaves out too much whitespace. Adding this parse action will restore any internal whitespace for a parse expression. This is especially useful when defining expressions for scanString or transformString applications. - Added __add__ method for ParseResults class, to better support using Python sum built-in for summing ParseResults objects returned from scanString. - Added reset method for the new OnlyOnce class wrapper for parse actions (to allow a grammar to be used multiple times). - Added optional maxMatches argument to scanString and searchString, to short-circuit scanning after 'n' expression matches are found. Version 1.4.3 - July, 2006 ------------------------------ - Fixed implementation of multiple parse actions for an expression (added in 1.4.2). . setParseAction() reverts to its previous behavior, setting one (or more) actions for an expression, overwriting any action or actions previously defined . new method addParseAction() appends one or more parse actions to the list of parse actions attached to an expression Now it is harder to accidentally append parse actions to an expression, when what you wanted to do was overwrite whatever had been defined before. (Thanks, Jean-Paul Calderone!) - Simplified interface to parse actions that do not require all 3 parse action arguments. Very rarely do parse actions require more than just the parsed tokens, yet parse actions still require all 3 arguments including the string being parsed and the location within the string where the parse expression was matched. With this release, parse actions may now be defined to be called as: . fn(string,locn,tokens) (the current form) . fn(locn,tokens) . fn(tokens) . fn() The setParseAction and addParseAction methods will internally decorate the provided parse actions with compatible wrappers to conform to the full (string,locn,tokens) argument sequence. - REMOVED SUPPORT FOR RETURNING PARSE LOCATION FROM A PARSE ACTION. I announced this in March, 2004, and gave a final warning in the last release. Now you can return a tuple from a parse action, and it will be treated like any other return value (i.e., the tuple will be substituted for the incoming tokens passed to the parse action, which is useful when trying to parse strings into tuples). - Added setFailAction method, taking a callable function fn 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 values. It may throw ParseFatalException if it is desired to stop parsing immediately. (Suggested by peter21081944 on wikispaces.com) - Added class OnlyOnce as helper wrapper for parse actions. OnlyOnce only permits a parse action to be called one time, after which all subsequent calls throw a ParseException. - Added traceParseAction decorator to help debug parse actions. Simply insert "@traceParseAction" ahead of the definition of your parse action, and each invocation will be displayed, along with incoming arguments, and returned value. - Fixed bug when copying ParserElements using copy() or setResultsName(). (Reported by Dan Thill, great catch!) - Fixed bug in asXML() where token text contains <, >, and & characters - generated XML now escapes these as <, > and &. (Reported by Jacek Sieka, thanks!) - Fixed bug in SkipTo() when searching for a StringEnd(). (Reported by Pete McEvoy, thanks Pete!) - Fixed "except Exception" statements, the most critical added as part of the packrat parsing enhancement. (Thanks, Erick Tryzelaar!) - Fixed end-of-string infinite looping on LineEnd and StringEnd expressions. (Thanks again to Erick Tryzelaar.) - Modified setWhitespaceChars to return self, to be consistent with other ParserElement modifiers. (Suggested by Erick Tryzelaar.) - Fixed bug/typo in new ParseResults.dump() method. - Fixed bug in searchString() method, in which only the first token of an expression was returned. searchString() now returns a ParseResults collection of all search matches. - Added example program removeLineBreaks.py, a string transformer that converts text files with hard line-breaks into one with line breaks only between paragraphs. - Added example program listAllMatches.py, to illustrate using the listAllMatches option when specifying results names (also shows new support for passing lists to oneOf). - Added example program linenoExample.py, to illustrate using the helper methods lineno, line, and col, and returning objects from a parse action. - Added example program parseListString.py, to which can parse the string representation of a Python list back into a true list. Taken mostly from my PyCon presentation examples, but now with support for tuple elements, too! Version 1.4.2 - April 1, 2006 (No foolin'!) ------------------------------------------- - Significant speedup from memoizing nested expressions (a technique known as "packrat parsing"), thanks to Chris Lesniewski-Laas! Your mileage may vary, but my Verilog parser almost doubled in speed to over 600 lines/sec! 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. - Added new helper method countedArray(expr), for defining patterns that start with a leading integer to indicate the number of array elements, followed by that many elements, matching the given expr parse expression. For instance, this two-liner: wordArray = countedArray(Word(alphas)) print wordArray.parseString("3 Practicality beats purity")[0] returns the parsed array of words: ['Practicality', 'beats', 'purity'] The leading token '3' is suppressed, although it is easily obtained from the length of the returned array. (Inspired by e-mail discussion with Ralf Vosseler.) - Added support for attaching multiple parse actions to a single ParserElement. (Suggested by Dan "Dang" Griffith - nice idea, Dan!) - Added support for asymmetric quoting characters in the recently-added QuotedString class. Now you can define your own quoted string syntax like "<>". To define this custom form of QuotedString, your code would define: dblAngleQuotedString = QuotedString('<<',endQuoteChar='>>') QuotedString also supports escaped quotes, escape character other than '\', and multiline. - Changed the default value returned internally by Optional, so that None can be used as a default value. (Suggested by Steven Bethard - I finally saw the light!) - Added dump() method to ParseResults, to make it easier to list out and diagnose values returned from calling parseString. - A new example, a search query string parser, submitted by Steven Mooij and Rudolph Froger - a very interesting application, thanks! - Added an example that parses the BNF in Python's Grammar file, in support of generating Python grammar documentation. (Suggested by J H Stovall.) - A new example, submitted by Tim Cera, of a flexible parser module, using a simple config variable to adjust parsing for input formats that have slight variations - thanks, Tim! - Added an example for parsing Roman numerals, showing the capability of parse actions to "compile" Roman numerals into their integer values during parsing. - Added a new docs directory, for additional documentation or help. Currently, this includes the text and examples from my recent presentation at PyCon. - Fixed another typo in CaselessKeyword, thanks Stefan Behnel. - Expanded oneOf to also accept tuples, not just lists. This really should be sufficient... - Added deprecation warnings when tuple is returned from a parse action. Looking back, I see that I originally deprecated this feature in March, 2004, so I'm guessing people really shouldn't have been using this feature - I'll drop it altogether in the next release, which will allow users to return a tuple from a parse action (which is really handy when trying to reconstuct tuples from a tuple string representation!). Version 1.4.1 - February, 2006 ------------------------------ - Converted generator expression in QuotedString class to list comprehension, to retain compatibility with Python 2.3. (Thanks, Titus Brown for the heads-up!) - Added searchString() method to ParserElement, as an alternative to using "scanString(instring).next()[0][0]" to search through a string looking for a substring matching a given parse expression. (Inspired by e-mail conversation with Dave Feustel.) - Modified oneOf to accept lists of strings as well as a single string of space-delimited literals. (Suggested by Jacek Sieka - thanks!) - Removed deprecated use of Upcase in pyparsing test code. (Also caught by Titus Brown.) - Removed lstrip() call from Literal - too aggressive in stripping whitespace which may be valid for some grammars. (Point raised by Jacek Sieka). Also, made Literal more robust in the event of passing an empty string. - Fixed bug in replaceWith when returning None. - Added cautionary documentation for Forward class when assigning a MatchFirst expression, as in: fwdExpr << a | b | c Precedence of operators causes this to be evaluated as: (fwdExpr << a) | b | c thereby leaving b and c out as parseable alternatives. Users must explicitly group the values inserted into the Forward: fwdExpr << (a | b | c) (Suggested by Scot Wilcoxon - thanks, Scot!) Version 1.4 - January 18, 2006 ------------------------------ - Added Regex class, to permit definition of complex embedded expressions using regular expressions. (Enhancement provided by John Beisley, great job!) - Converted implementations of Word, oneOf, quoted string, and comment helpers to utilize regular expression matching. Performance improvements in the 20-40% range. - Added QuotedString class, to support definition of non-standard quoted strings (Suggested by Guillaume Proulx, thanks!) - Added CaselessKeyword class, to streamline grammars with, well, caseless keywords (Proposed by Stefan Behnel, thanks!) - Fixed bug in SkipTo, when using an ignoreable expression. (Patch provided by Anonymous, thanks, whoever-you-are!) - Fixed typo in NoMatch class. (Good catch, Stefan Behnel!) - Fixed minor bug in _makeTags(), using string.printables instead of pyparsing.printables. - Cleaned up some of the expressions created by makeXXXTags helpers, to suppress extraneous <> characters. - Added some grammar definition-time checking to verify that a grammar is being built using proper ParserElements. - Added examples: . LAparser.py - linear algebra C preprocessor (submitted by Mike Ellis, thanks Mike!) . wordsToNum.py - converts word description of a number back to the original number (such as 'one hundred and twenty three' -> 123) . updated fourFn.py to support unary minus, added BNF comments Version 1.3.3 - September 12, 2005 ---------------------------------- - Improved support for Unicode strings that would be returned using srange. Added greetingInKorean.py example, for a Korean version of "Hello, World!" using Unicode. (Thanks, June Kim!) - Added 'hexnums' string constant (nums+"ABCDEFabcdef") for defining hexadecimal value expressions. - NOTE: ===THIS CHANGE MAY BREAK EXISTING CODE=== Modified tag and results definitions returned by makeHTMLTags(), to better support the looseness of HTML parsing. Tags to be parsed are now caseless, and keys generated for tag attributes are now converted to lower case. Formerly, makeXMLTags("XYZ") would return a tag with results name of "startXYZ", this has been changed to "startXyz". If this tag is matched against '', the matched keys formerly would be "Abc", "DEF", and "ghi"; keys are now converted to lower case, giving keys of "abc", "def", and "ghi". These changes were made to try to address the lax case sensitivity agreement between start and end tags in many HTML pages. No changes were made to makeXMLTags(), which assumes more rigorous parsing rules. Also, cleaned up case-sensitivity bugs in closing tags, and switched to using Keyword instead of Literal class for tags. (Thanks, Steve Young, for getting me to look at these in more detail!) - Added two helper parse actions, upcaseTokens and downcaseTokens, which will convert matched text to all uppercase or lowercase, respectively. - Deprecated Upcase class, to be replaced by upcaseTokens parse action. - Converted messages sent to stderr to use warnings module, such as when constructing a Literal with an empty string, one should use the Empty() class or the empty helper instead. - Added ' ' (space) as an escapable character within a quoted string. - Added helper expressions for common comment types, in addition to the existing cStyleComment (/*...*/) and htmlStyleComment () . dblSlashComment = // ... (to end of line) . cppStyleComment = cStyleComment or dblSlashComment . javaStyleComment = cppStyleComment . pythonStyleComment = # ... (to end of line) Version 1.3.2 - July 24, 2005 ----------------------------- - Added Each class as an enhanced version of And. 'Each' requires that all given expressions be present, but may occur in any order. Special handling is provided to group ZeroOrMore and OneOrMore elements that occur out-of-order in the input string. You can also construct 'Each' objects by joining expressions with the '&' operator. When using the Each class, results names are strongly recommended for accessing the matched tokens. (Suggested by Pradam Amini - thanks, Pradam!) - Stricter interpretation of 'max' qualifier on Word elements. If the 'max' attribute is specified, matching will fail if an input field contains more than 'max' consecutive body characters. For example, previously, Word(nums,max=3) would match the first three characters of '0123456', returning '012' and continuing parsing at '3'. Now, when constructed using the max attribute, Word will raise an exception with this string. - Cleaner handling of nested dictionaries returned by Dict. No longer necessary to dereference sub-dictionaries as element [0] of their parents. === NOTE: THIS CHANGE MAY BREAK SOME EXISTING CODE, BUT ONLY IF PARSING NESTED DICTIONARIES USING THE LITTLE-USED DICT CLASS === (Prompted by discussion thread on the Python Tutor list, with contributions from Danny Yoo, Kent Johnson, and original post by Liam Clarke - thanks all!) Version 1.3.1 - June, 2005 ---------------------------------- - Added markInputline() method to ParseException, to display the input text line location of the parsing exception. (Thanks, Stefan Behnel!) - Added setDefaultKeywordChars(), so that Keyword definitions using a custom keyword character set do not all need to add the keywordChars constructor argument (similar to setDefaultWhitespaceChars()). (suggested by rzhanka on the SourceForge pyparsing forum.) - Simplified passing debug actions to setDebugAction(). You can now pass 'None' for a debug action if you want to take the default debug behavior. To suppress a particular debug action, you can pass the pyparsing method nullDebugAction. - Refactored parse exception classes, moved all behavior to ParseBaseException, and the former ParseException is now a subclass of ParseBaseException. Added a second subclass, ParseFatalException, as a subclass of ParseBaseException. User-defined parse actions can raise ParseFatalException if a data inconsistency is detected (such as a begin-tag/end-tag mismatch), and this will stop all parsing immediately. (Inspired by e-mail thread with Michele Petrazzo - thanks, Michelle!) - Added helper methods makeXMLTags and makeHTMLTags, that simplify the definition of XML or HTML tag parse expressions for a given tagname. Both functions return a pair of parse expressions, one for the opening tag (that is, '') and one for the closing tag (''). The opening tagame also recognizes any attribute definitions that have been included in the opening tag, as well as an empty tag (one with a trailing '/', as in '' which is equivalent to ''). makeXMLTags uses stricter XML syntax for attributes, requiring that they be enclosed in double quote characters - makeHTMLTags is more lenient, and accepts single-quoted strings or any contiguous string of characters up to the next whitespace character or '>' character. Attributes can be retrieved as dictionary or attribute values of the returned results from the opening tag. - Added example minimath2.py, a refinement on fourFn.py that adds an interactive session and support for variables. (Thanks, Steven Siew!) - Added performance improvement, up to 20% reduction! (Found while working with Wolfgang Borgert on performance tuning of his TTCN3 parser.) - And another performance improvement, up to 25%, when using scanString! (Found while working with Henrik Westlund on his C header file scanner.) - Updated UML diagrams to reflect latest class/method changes. Version 1.3 - March, 2005 ---------------------------------- - Added new Keyword class, as a special form of Literal. Keywords must be followed by whitespace or other non-keyword characters, to distinguish them from variables or other identifiers that just happen to start with the same characters as a keyword. For instance, the input string containing "ifOnlyIfOnly" will match a Literal("if") at the beginning and in the middle, but will fail to match a Keyword("if"). Keyword("if") will match only strings such as "if only" or "if(only)". (Proposed by Wolfgang Borgert, and Berteun Damman separately requested this on comp.lang.python - great idea!) - Added setWhitespaceChars() method to override the characters to be skipped as whitespace before matching a particular ParseElement. Also added the class-level method setDefaultWhitespaceChars(), to allow users to override the default set of whitespace characters (space, tab, newline, and return) for all subsequently defined ParseElements. (Inspired by Klaas Hofstra's inquiry on the Sourceforge pyparsing forum.) - Added helper parse actions to support some very common parse action use cases: . replaceWith(replStr) - replaces the matching tokens with the provided replStr replacement string; especially useful with transformString() . removeQuotes - removes first and last character from string enclosed in quotes (note - NOT the same as the string strip() method, as only a single character is removed at each end) - Added copy() method to ParseElement, to make it easier to define different parse actions for the same basic parse expression. (Note, copy is implicitly called when using setResultsName().) (The following changes were posted to CVS as Version 1.2.3 - October-December, 2004) - Added support for Unicode strings in creating grammar definitions. (Big thanks to Gavin Panella!) - Added constant alphas8bit to include the following 8-bit characters: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ - Added srange() function to simplify definition of Word elements, using regexp-like '[A-Za-z0-9]' syntax. This also simplifies referencing common 8-bit characters. - Fixed bug in Dict when a single element Dict was embedded within another Dict. (Thanks Andy Yates for catching this one!) - Added 'formatted' argument to ParseResults.asXML(). If set to False, suppresses insertion of whitespace for pretty-print formatting. Default equals True for backward compatibility. - Added setDebugActions() function to ParserElement, to allow user-defined debugging actions. - Added support for escaped quotes (either in \', \", or doubled quote form) to the predefined expressions for quoted strings. (Thanks, Ero Carrera!) - Minor performance improvement (~5%) converting "char in string" tests to "char in dict". (Suggested by Gavin Panella, cool idea!) Version 1.2.2 - September 27, 2004 ---------------------------------- - Modified delimitedList to accept an expression as the delimiter, instead of only accepting strings. - Modified ParseResults, to convert integer field keys to strings (to avoid confusion with list access). - Modified Combine, to convert all embedded tokens to strings before combining. - Fixed bug in MatchFirst in which parse actions would be called for expressions that only partially match. (Thanks, John Hunter!) - Fixed bug in fourFn.py example that fixes right-associativity of ^ operator. (Thanks, Andrea Griffini!) - Added class FollowedBy(expression), to look ahead in the input string without consuming tokens. - Added class NoMatch that never matches any input. Can be useful in debugging, and in very specialized grammars. - Added example pgn.py, for parsing chess game files stored in Portable Game Notation. (Thanks, Alberto Santini!) Version 1.2.1 - August 19, 2004 ------------------------------- - Added SkipTo(expression) token type, simplifying grammars that only want to specify delimiting expressions, and want to match any characters between them. - Added helper method dictOf(key,value), making it easier to work with the Dict class. (Inspired by Pavel Volkovitskiy, thanks!). - Added optional argument listAllMatches (default=False) to setResultsName(). Setting listAllMatches to True overrides the default modal setting of tokens to results names; instead, the results name acts as an accumulator for all matching tokens within the local repetition group. (Suggested by Amaury Le Leyzour - thanks!) - Fixed bug in ParseResults, throwing exception when trying to extract slice, or make a copy using [:]. (Thanks, Wilson Fowlie!) - Fixed bug in transformString() when the input string contains 's (Thanks, Rick Walia!). - Fixed bug in returning tokens from un-Grouped And's, Or's and MatchFirst's, where too many tokens would be included in the results, confounding parse actions and returned results. - Fixed bug in naming ParseResults returned by And's, Or's, and Match First's. - Fixed bug in LineEnd() - matching this token now correctly consumes and returns the end of line "\n". - Added a beautiful example for parsing Mozilla calendar files (Thanks, Petri Savolainen!). - Added support for dynamically modifying Forward expressions during parsing. Version 1.2 - 20 June 2004 -------------------------- - Added definition for htmlComment to help support HTML scanning and parsing. - Fixed bug in generating XML for Dict classes, in which trailing item was duplicated in the output XML. - Fixed release bug in which scanExamples.py was omitted from release files. - Fixed bug in transformString() when parse actions are not defined on the outermost parser element. - Added example urlExtractor.py, as another example of using scanString and parse actions. Version 1.2beta3 - 4 June 2004 ------------------------------ - Added White() token type, analogous to Word, to match on whitespace characters. Use White in parsers with significant whitespace (such as configuration file parsers that use indentation to indicate grouping). Construct White with a string containing the whitespace characters to be matched. Similar to Word, White also takes optional min, max, and exact parameters. - As part of supporting whitespace-signficant parsing, added parseWithTabs() method to ParserElement, to override the default behavior in parseString of automatically expanding tabs to spaces. To retain tabs during parsing, call parseWithTabs() before calling parseString(), parseFile() or scanString(). (Thanks, Jean-Guillaume Paradis for catching this, and for your suggestions on whitespace-significant parsing.) - Added transformString() method to ParseElement, as a complement to scanString(). To use transformString, define a grammar and attach a parse action to the overall grammar 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. (Note: transformString() does *not* automatically expand tabs to spaces.) Also added scanExamples.py to the examples directory to show sample uses of scanString() and transformString(). - Removed group() method that was introduced in beta2. This turns out NOT to be equivalent to nesting within a Group() object, and I'd prefer not to sow more seeds of confusion. - Fixed behavior of asXML() where tags for groups were incorrectly duplicated. (Thanks, Brad Clements!) - Changed beta version message to display to stderr instead of stdout, to make asXML() easier to use. (Thanks again, Brad.) Version 1.2beta2 - 19 May 2004 ------------------------------ - *** SIMPLIFIED API *** - Parse actions that do not modify the list of tokens no longer need to return a value. This simplifies those parse actions that use the list of tokens to update a counter or record or display some of the token content; these parse actions can simply end without having to specify 'return toks'. - *** POSSIBLE API INCOMPATIBILITY *** - Fixed CaselessLiteral bug, where the returned token text was not the original string (as stated in the docs), but the original string converted to upper case. (Thanks, Dang Griffith!) **NOTE: this may break some code that relied on this erroneous behavior. Users should scan their code for uses of CaselessLiteral.** - *** POSSIBLE CODE INCOMPATIBILITY *** - I have renamed the internal attributes on ParseResults from 'dict' and 'list' to '__tokdict' and '__toklist', to avoid collisions with user-defined data fields named 'dict' and 'list'. Any client code that accesses these attributes directly will need to be modified. Hopefully the implementation of methods such as keys(), items(), len(), etc. on ParseResults will make such direct attribute accessess unnecessary. - Added asXML() method to ParseResults. This greatly simplifies the process of parsing an input data file and generating XML-structured data. - Added getName() method to ParseResults. This method is helpful when a grammar specifies ZeroOrMore or OneOrMore of a MatchFirst or Or expression, and the parsing code needs to know which expression matched. (Thanks, Eric van der Vlist, for this idea!) - Added items() and values() methods to ParseResults, to better support using ParseResults as a Dictionary. - Added parseFile() as a convenience function to parse the contents of an entire text file. Accepts either a file name or a file object. (Thanks again, Dang!) - Added group() method to And, Or, and MatchFirst, as a short-cut alternative to enclosing a construct inside a Group object. - Extended fourFn.py to support exponentiation, and simple built-in functions. - Added EBNF parser to examples, including a demo where it parses its own EBNF! (Thanks to Seo Sanghyeon!) - Added Delphi Form parser to examples, dfmparse.py, plus a couple of sample Delphi forms as tests. (Well done, Dang!) - Another performance speedup, 5-10%, inspired by Dang! Plus about a 20% speedup, by pre-constructing and cacheing exception objects instead of constructing them on the fly. - Fixed minor bug when specifying oneOf() with 'caseless=True'. - Cleaned up and added a few more docstrings, to improve the generated docs. Version 1.1.2 - 21 Mar 2004 --------------------------- - Fixed minor bug in scanString(), so that start location is at the start of the matched tokens, not at the start of the whitespace before the matched tokens. - Inclusion of HTML documentation, generated using Epydoc. Reformatted some doc strings to better generate readable docs. (Beautiful work, Ed Loper, thanks for Epydoc!) - Minor performance speedup, 5-15% - And on a process note, I've used the unittest module to define a series of unit tests, to help avoid the embarrassment of the version 1.1 snafu. Version 1.1.1 - 6 Mar 2004 -------------------------- - Fixed critical bug introduced in 1.1, which broke MatchFirst(!) token matching. **THANK YOU, SEO SANGHYEON!!!** - Added "from future import __generators__" to permit running under pre-Python 2.3. - Added example getNTPservers.py, showing how to use pyparsing to extract a text pattern from the HTML of a web page. Version 1.1 - 3 Mar 2004 ------------------------- - ***Changed API*** - While testing out parse actions, I found that the value of loc passed in was not the starting location of the matched tokens, but the location of the next token in the list. With this version, the location passed to the parse action is now the starting location of the tokens that matched. A second part of this change is that the return value of parse actions no longer needs to return a tuple containing both the location and the parsed tokens (which may optionally be modified); parse actions only need to return the list of tokens. Parse actions that return a tuple are deprecated; they will still work properly for conversion/compatibility, but this behavior will be removed in a future version. - Added validate() method, to help diagnose infinite recursion in a grammar tree. validate() is not 100% fool-proof, but it can help track down nasty infinite looping due to recursively referencing the same grammar construct without some intervening characters. - Cleaned up default listing of some parse element types, to more closely match ordinary BNF. Instead of the form :[contents-list], some changes are: . And(token1,token2,token3) is "{ token1 token2 token3 }" . Or(token1,token2,token3) is "{ token1 ^ token2 ^ token3 }" . MatchFirst(token1,token2,token3) is "{ token1 | token2 | token3 }" . Optional(token) is "[ token ]" . OneOrMore(token) is "{ token }..." . ZeroOrMore(token) is "[ token ]..." - Fixed an infinite loop in oneOf if the input string contains a duplicated option. (Thanks Brad Clements) - Fixed a bug when specifying a results name on an Optional token. (Thanks again, Brad Clements) - Fixed a bug introduced in 1.0.6 when I converted quotedString to use CharsNotIn; I accidentally permitted quoted strings to span newlines. I have fixed this in this version to go back to the original behavior, in which quoted strings do *not* span newlines. - Fixed minor bug in HTTP server log parser. (Thanks Jim Richardson) Version 1.0.6 - 13 Feb 2004 ---------------------------- - Added CharsNotIn class (Thanks, Lee SangYeong). This is the opposite of Word, in that it is constructed with a set of characters *not* to be matched. (This enhancement also allowed me to clean up and simplify some of the definitions for quoted strings, cStyleComment, and restOfLine.) - **MINOR API CHANGE** - Added joinString argument to the __init__ method of Combine (Thanks, Thomas Kalka). joinString defaults to "", but some applications might choose some other string to use instead, such as a blank or newline. joinString was inserted as the second argument to __init__, so if you have code that specifies an adjacent value, without using 'adjacent=', this code will break. - Modified LineStart to recognize the start of an empty line. - Added optional caseless flag to oneOf(), to create a list of CaselessLiteral tokens instead of Literal tokens. - Added some enhancements to the SQL example: . Oracle-style comments (Thanks to Harald Armin Massa) . simple WHERE clause - Minor performance speedup - 5-15% Version 1.0.5 - 19 Jan 2004 ---------------------------- - Added scanString() generator method to ParseElement, to support regex-like pattern-searching - Added items() list to ParseResults, to return named results as a list of (key,value) pairs - Fixed memory overflow in asList() for deeply nested ParseResults (Thanks, Sverrir Valgeirsson) - Minor performance speedup - 10-15% Version 1.0.4 - 8 Jan 2004 --------------------------- - Added positional tokens StringStart, StringEnd, LineStart, and LineEnd - Added commaSeparatedList to pre-defined global token definitions; also added commasep.py to the examples directory, to demonstrate the differences between parsing comma-separated data and simple line-splitting at commas - Minor API change: delimitedList does not automatically enclose the list elements in a Group, but makes this the responsibility of the caller; also, if invoked using 'combine=True', the list delimiters are also included in the returned text (good for scoped variables, such as a.b.c or a::b::c, or for directory paths such as a/b/c) - Performance speed-up again, 30-40% - Added httpServerLogParser.py to examples directory, as this is a common parsing task Version 1.0.3 - 23 Dec 2003 --------------------------- - Performance speed-up again, 20-40% - Added Python distutils installation setup.py, etc. (thanks, Dave Kuhlman) Version 1.0.2 - 18 Dec 2003 --------------------------- - **NOTE: Changed API again!!!** (for the last time, I hope) + Renamed module from parsing to pyparsing, to better reflect Python linkage. - Also added dictExample.py to examples directory, to illustrate usage of the Dict class. Version 1.0.1 - 17 Dec 2003 --------------------------- - **NOTE: Changed API!** + Renamed 'len' argument on Word.__init__() to 'exact' - Performance speed-up, 10-30% Version 1.0.0 - 15 Dec 2003 --------------------------- - Initial public release Version 0.1.1 thru 0.1.17 - October-November, 2003 -------------------------------------------------- - initial development iterations: - added Dict, Group - added helper methods oneOf, delimitedList - added helpers quotedString (and double and single), restOfLine, cStyleComment - added MatchFirst as an alternative to the slower Or - added UML class diagram - fixed various logic bugs pyparsing2-2.4.7/CODE_OF_CONDUCT.rst000066400000000000000000000064151365333160500166440ustar00rootroot00000000000000Contributor Covenant Code of Conduct ==================================== Our Pledge ---------- In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. Our Standards ------------- Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others’ private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting Our Responsibilities -------------------- Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. Scope ----- This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. Enforcement ----------- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at pyparsing@mail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. Attribution ----------- This Code of Conduct is adapted from the `Contributor Covenant `__, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html pyparsing2-2.4.7/CONTRIBUTING.md000066400000000000000000000134201365333160500160600ustar00rootroot00000000000000# CONTRIBUTING Thank you for your interest in working on pyparsing! Pyparsing has become a popular module for creating simple text parsing and data scraping applications. It has been incorporated in several widely-used packages, and is often used by beginners as part of their first Python project. ## Raising questions / asking for help If you have a question on using pyparsing, there are a number of resources available online. - [StackOverflow](https://stackoverflow.com/questions/tagged/pyparsing) - about 10 years of SO questions and answers can be searched on StackOverflow, tagged with the `pyparsing` tag. Note that some of the older posts will refer to features in Python 2, or to versions and coding practices for pyparsing that have been replaced by newer classes and coding idioms. - [pyparsing sub-reddit](https://www.reddit.com/r/pyparsing/) - still very lightly attended, but open to anyone wishing to post questions or links related to pyparsing. An alternative channel to StackOverflow for asking questions. - [online docs](https://pyparsing-docs.readthedocs.io/en/latest/index.html) and a separately maintained set of class library docs [here](https://pyparsing-doc.neocities.org/) - These docs are auto-generated from the docstrings embedded in the pyparsing classes, so they can also be viewed in the interactive Python console's and Jupyter Notebook's `help` commands. - [the pyparsing Wikispaces archive](https://github.com/pyparsing/wikispaces_archive) - Before hosting on GitHub, pyparsing had a separate wiki on the wikispaces.com website. In 2018 this page was discontinued. The discussion content archive has been reformatted into Markdown and can be viewed by year at the GitHub repository. Just as with some of the older questions on StackOverflow, some of these older posts may reflect out-of-date pyparsing and Python features. - [submit an issue](https://github.com/pyparsing/pyparsing/issues) - If you have a problem with pyparsing that looks like an actual bug, or have an idea for a feature to add to pyaprsing please submit an issue on GitHub. Some pyparsing behavior may be counter-intuitive, so try to review some of the other resources first, or some of the other open and closed issues. Or post your question on SO or reddit. But don't wait until you are desperate and frustrated - just ask! :) ## Submitting changes If you are considering proposing updates to pyparsing, please bear in mind the following guidelines. Please review [_The Zen of Pyparsing_ and _The Zen of Pyparsing Development_](https://github.com/pyparsing/pyparsing/wiki/Zen) article on the pyparsing wiki, to get a general feel for the historical and future approaches to pyparsing's design, and intended developer experience as an embedded DSL. ## Some design points - Minimize additions to the module namespace. Over time, pyparsing's namespace has acquired a *lot* of names. New features have been encapsulated into namespace classes to try to hold back the name flooding when importing pyparsing. - New operator overloads will need to show broad applicability. - Performance tuning should focus on parse time performance. Optimizing parser definition performance is secondary. - New external dependencies will require substantial justification, and if included, will need to be guarded for `ImportError`s raised if the external module is not installed. ## Some coding points These coding styles are encouraged whether submitting code for core pyparsing or for submitting an example. - PEP8 - at this time, pyparsing is very non-compliant with many PEP8 guidelines, especially those regarding name casing. I had just finished several years of Java and Smalltalk development, and camel case seemed to be the future trend in coding styles. There are plans to convert these names to PEP8-conformant snake case, but this will be done over several releases to provide a migration path for current pyparsing-dependent applications. See more information at the [PEP8 wiki page](https://github.com/pyparsing/pyparsing/wiki/PEP-8-planning). If you wish to submit a new example, please follow PEP8 name and coding guidelines. Example code must be available for distribution with the rest of pyparsing under the MIT open source license. - No backslashes for line continuations. Continuation lines for expressions in ()'s should start with the continuing operator: really_long_line = (something + some_other_long_thing + even_another_long_thing) - Changes to core pyparsing must be compatible back to Py3.5 without conditionalizing. Later Py3 features may be used in examples by way of illustration. - str.format() statements should use named format arguments (unless this proves to be a slowdown at parse time). - List, tuple, and dict literals should include a trailing comma after the last element, which reduces changeset clutter when another element gets added to the end. - Examples should import pyparsing and the common namespace classes as: import pyparsing as pp # if necessary ppc = pp.pyparsing_common ppu = pp.pyparsing_unicode - Where possible use operators to create composite parse expressions: expr = expr_a + expr_b | expr_c instead of: expr = pp.MatchFirst([pp.And([expr_a, expr_b]), expr_c]) Exception: if using a generator to create an expression: import keyword python_keywords = keyword.kwlist any_keyword = pp.MatchFirst(pp.Keyword(kw) for kw in python_keywords)) - Learn [The Classic Blunders](https://github.com/pyparsing/pyparsing/wiki/The-Classic-Blunders) and how to avoid them when developing new examples. - New features should be accompanied with updates to unitTests.py and a bullet in the CHANGES file. pyparsing2-2.4.7/LICENSE000066400000000000000000000017771365333160500146500ustar00rootroot00000000000000Permission 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. pyparsing2-2.4.7/MANIFEST.in000066400000000000000000000006371365333160500153730ustar00rootroot00000000000000include pyparsing.py include HowToUsePyparsing.rst pyparsingClassDiagram.* include README.md CODE_OF_CONDUCT.rst CHANGES LICENSE CONTRIBUTING.md modules.rst include examples/*.py examples/Setup.ini examples/*.dfm examples/*.ics examples/*.html examples/*.h examples/*.g examples/statemachine/* recursive-include docs * prune docs/_build/* recursive-include test * include setup.py simple_unit_tests.py unitTests.py pyparsing2-2.4.7/PKG-INFO000066400000000000000000000102171365333160500147250ustar00rootroot00000000000000Metadata-Version: 1.2 Name: pyparsing Version: 2.4.7 Summary: Python parsing module Home-page: https://github.com/pyparsing/pyparsing/ Author: Paul McGuire Author-email: ptmcg@users.sourceforge.net License: MIT License Download-URL: https://pypi.org/project/pyparsing/ Description: PyParsing -- A Python Parsing Module ==================================== |Build Status| Introduction ============ 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. The pyparsing module provides a library of classes that client code uses to construct the grammar directly in Python code. *[Since first writing this description of pyparsing in late 2003, this technique for developing parsers has become more widespread, under the name Parsing Expression Grammars - PEGs. See more information on PEGs at* https://en.wikipedia.org/wiki/Parsing_expression_grammar *.]* Here is a program to parse ``"Hello, World!"`` (or any greeting of the form ``"salutation, addressee!"``): .. code:: python from pyparsing import Word, alphas 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 '^' operator definitions. 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 The examples directory includes a simple SQL parser, simple CORBA IDL parser, a config file parser, a chemical formula parser, and a four- function algebraic notation parser, among many others. Documentation ============= There are many examples in the online docstrings of the classes and methods in pyparsing. You can find them compiled into online docs at https://pyparsing-docs.readthedocs.io/en/latest/. Additional documentation resources and project info are listed in the online GitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An entire directory of examples is at https://github.com/pyparsing/pyparsing/tree/master/examples. License ======= MIT License. See header of pyparsing.py History ======= See CHANGES file. .. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master :target: https://travis-ci.org/pyparsing/pyparsing Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.* pyparsing2-2.4.7/README.rst000066400000000000000000000047071365333160500153260ustar00rootroot00000000000000PyParsing -- A Python Parsing Module ==================================== |Build Status| Introduction ============ 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. The pyparsing module provides a library of classes that client code uses to construct the grammar directly in Python code. *[Since first writing this description of pyparsing in late 2003, this technique for developing parsers has become more widespread, under the name Parsing Expression Grammars - PEGs. See more information on PEGs at* https://en.wikipedia.org/wiki/Parsing_expression_grammar *.]* Here is a program to parse ``"Hello, World!"`` (or any greeting of the form ``"salutation, addressee!"``): .. code:: python from pyparsing import Word, alphas 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 '^' operator definitions. 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 The examples directory includes a simple SQL parser, simple CORBA IDL parser, a config file parser, a chemical formula parser, and a four- function algebraic notation parser, among many others. Documentation ============= There are many examples in the online docstrings of the classes and methods in pyparsing. You can find them compiled into online docs at https://pyparsing-docs.readthedocs.io/en/latest/. Additional documentation resources and project info are listed in the online GitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An entire directory of examples is at https://github.com/pyparsing/pyparsing/tree/master/examples. License ======= MIT License. See header of pyparsing.py History ======= See CHANGES file. .. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master :target: https://travis-ci.org/pyparsing/pyparsing pyparsing2-2.4.7/docs/000077500000000000000000000000001365333160500145575ustar00rootroot00000000000000pyparsing2-2.4.7/docs/CODE_OF_CONDUCT.rst000066400000000000000000000000441365333160500175640ustar00rootroot00000000000000.. include:: ../CODE_OF_CONDUCT.rst pyparsing2-2.4.7/docs/HowToUsePyparsing.rst000066400000000000000000001204171365333160500207300ustar00rootroot00000000000000========================== Using the pyparsing module ========================== :author: Paul McGuire :address: ptmcg@users.sourceforge.net :revision: 2.0.1a :date: July, 2013 (minor update August, 2018) :copyright: Copyright |copy| 2003-2013 Paul McGuire. .. |copy| unicode:: 0xA9 :abstract: This document provides how-to instructions for the pyparsing library, an easy-to-use Python module for constructing and executing basic text parsers. The pyparsing module is useful for evaluating user-definable expressions, processing custom application language commands, or extracting data from formatted reports. .. sectnum:: :depth: 4 .. contents:: :depth: 4 Note: While this content is still valid, there are more detailed descriptions and examples at the online doc server at https://pythonhosted.org/pyparsing/pyparsing-module.html Steps to follow =============== To parse an incoming data string, the client code must follow these steps: 1. First define the tokens and patterns to be matched, and assign this to a program variable. Optional results names or parsing actions can also be defined at this time. 2. Call ``parseString()`` or ``scanString()`` on this variable, passing in the string to be parsed. During the matching process, whitespace between tokens is skipped by default (although this can be changed). When token matches occur, any defined parse action methods are called. 3. Process the parsed results, returned as a list of strings. Matching results may also be accessed as named attributes of the returned results, if names are defined in the definition of the token pattern, using ``setResultsName()``. Hello, World! ------------- The following complete Python program will parse the greeting "Hello, World!", or any other greeting of the form ", !":: from pyparsing import Word, alphas greet = Word(alphas) + "," + Word(alphas) + "!" greeting = greet.parseString("Hello, World!") print greeting The parsed tokens are returned in the following form:: ['Hello', ',', 'World', '!'] Usage notes ----------- - The pyparsing module can be used to interpret simple command strings or algebraic expressions, or can be used to extract data from text reports with complicated format and structure ("screen or report scraping"). However, it is possible that your defined matching patterns may accept invalid inputs. Use pyparsing to extract data from strings assumed to be well-formatted. - To keep up the readability of your code, use operators_ such as ``+``, ``|``, ``^``, and ``~`` to combine expressions. You can also combine string literals with ParseExpressions - they will be automatically converted to Literal objects. For example:: integer = Word(nums) # simple unsigned integer variable = Word(alphas, max=1) # single letter variable, such as x, z, m, etc. arithOp = Word("+-*/", max=1) # arithmetic operators equation = variable + "=" + integer + arithOp + integer # will match "x=2+2", etc. In the definition of ``equation``, the string ``"="`` will get added as a ``Literal("=")``, but in a more readable way. - The pyparsing module's default behavior is to ignore whitespace. This is the case for 99% of all parsers ever written. This allows you to write simple, clean, grammars, such as the above ``equation``, without having to clutter it up with extraneous ``ws`` markers. The ``equation`` grammar will successfully parse all of the following statements:: x=2+2 x = 2+2 a = 10 * 4 r= 1234/ 100000 Of course, it is quite simple to extend this example to support more elaborate expressions, with nesting with parentheses, floating point numbers, scientific notation, and named constants (such as ``e`` or ``pi``). See ``fourFn.py``, included in the examples directory. - To modify pyparsing's default whitespace skipping, you can use one or more of the following methods: - use the static method ``ParserElement.setDefaultWhitespaceChars`` to override the normal set of whitespace chars (' \t\n'). For instance when defining a grammar in which newlines are significant, you should call ``ParserElement.setDefaultWhitespaceChars(' \t')`` to remove newline from the set of skippable whitespace characters. Calling this method will affect all pyparsing expressions defined afterward. - call ``leaveWhitespace()`` on individual expressions, to suppress the skipping of whitespace before trying to match the expression - use ``Combine`` to require that successive expressions must be adjacent in the input string. For instance, this expression:: real = Word(nums) + '.' + Word(nums) will match "3.14159", but will also match "3 . 12". It will also return the matched results as ['3', '.', '14159']. By changing this expression to:: real = Combine(Word(nums) + '.' + Word(nums)) it will not match numbers with embedded spaces, and it will return a single concatenated string '3.14159' as the parsed token. - Repetition of expressions can be indicated using ``*`` or ``[]`` notation. An expression may be multiplied by an integer value (to indicate an exact repetition count), or indexed with a tuple, representing min and max repetitions (with ``...`` representing no min or no max, depending whether it is the first or second tuple element). See the following examples, where n is used to indicate an integer value: - ``expr*3`` is equivalent to ``expr + expr + expr`` - ``expr[2, 3]`` is equivalent to ``expr + expr + Optional(expr)`` - ``expr[n, ...]`` or ``expr[n,]`` is equivalent to ``expr*n + ZeroOrMore(expr)`` (read as "at least n instances of expr") - ``expr[... ,n]`` is equivalent to ``expr*(0, n)`` (read as "0 to n instances of expr") - ``expr[...]`` and ``expr[0, ...]`` are equivalent to ``ZeroOrMore(expr)`` - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)`` Note that ``expr[..., n]`` does not raise an exception if more than n exprs exist in the input stream; that is, ``expr[..., n]`` does not enforce a maximum number of expr occurrences. If this behavior is desired, then write ``expr[..., n] + ~expr``. - ``MatchFirst`` expressions are matched left-to-right, and the first match found will skip all later expressions within, so be sure to define less-specific patterns after more-specific patterns. If you are not sure which expressions are most specific, use Or expressions (defined using the ``^`` operator) - they will always match the longest expression, although they are more compute-intensive. - ``Or`` expressions will evaluate all of the specified subexpressions to determine which is the "best" match, that is, which matches the longest string in the input data. In case of a tie, the left-most expression in the ``Or`` list will win. - If parsing the contents of an entire file, pass it to the ``parseFile`` method using:: expr.parseFile(sourceFile) - ``ParseExceptions`` will report the location where an expected token or expression failed to match. For example, if we tried to use our "Hello, World!" parser to parse "Hello World!" (leaving out the separating comma), we would get an exception, with the message:: pyparsing.ParseException: Expected "," (6), (1,7) In the case of complex expressions, the reported location may not be exactly where you would expect. See more information under ParseException_ . - Use the ``Group`` class to enclose logical groups of tokens within a sublist. This will help organize your results into more hierarchical form (the default behavior is to return matching tokens as a flat list of matching input strings). - Punctuation may be significant for matching, but is rarely of much interest in the parsed results. Use the ``suppress()`` method to keep these tokens from cluttering up your returned lists of tokens. For example, ``delimitedList()`` matches a succession of one or more expressions, separated by delimiters (commas by default), but only returns a list of the actual expressions - the delimiters are used for parsing, but are suppressed from the returned output. - Parse actions can be used to convert values from strings to other data types (ints, floats, booleans, etc.). - Results names are recommended for retrieving tokens from complex expressions. It is much easier to access a token using its field name than using a positional index, especially if the expression contains optional elements. You can also shortcut the ``setResultsName`` call:: stats = ("AVE:" + realNum.setResultsName("average") + "MIN:" + realNum.setResultsName("min") + "MAX:" + realNum.setResultsName("max")) can now be written as this:: stats = ("AVE:" + realNum("average") + "MIN:" + realNum("min") + "MAX:" + realNum("max")) - Be careful when defining parse actions that modify global variables or data structures (as in ``fourFn.py``), especially for low level tokens or expressions that may occur within an ``And`` expression; an early element of an ``And`` may match, but the overall expression may fail. Classes ======= Classes in the pyparsing module ------------------------------- ``ParserElement`` - abstract base class for all pyparsing classes; methods for code to use are: - ``parseString(sourceString, parseAll=False)`` - only called once, on the overall matching pattern; returns a ParseResults_ object that makes the matched tokens available as a list, and optionally as a dictionary, or as an object with named attributes; if parseAll is set to True, then parseString will raise a ParseException if the grammar does not process the complete input string. - ``parseFile(sourceFile)`` - a convenience function, that accepts an input file object or filename. The file contents are passed as a string to ``parseString()``. ``parseFile`` also supports the ``parseAll`` argument. - ``scanString(sourceString)`` - generator function, used to find and extract matching text in the given source string; for each matched text, returns a tuple of: - matched tokens (packaged as a ParseResults_ object) - start location of the matched text in the given source string - end location in the given source string ``scanString`` allows you to scan through the input source string for random matches, instead of exhaustively defining the grammar for the entire source text (as would be required with ``parseString``). - ``transformString(sourceString)`` - convenience wrapper function for ``scanString``, to process the input source string, and replace matching text with the tokens returned from parse actions defined in the grammar (see setParseAction_). - ``searchString(sourceString)`` - another convenience wrapper function for ``scanString``, returns a list of the matching tokens returned from each call to ``scanString``. - ``setName(name)`` - associate a short descriptive name for this element, useful in displaying exceptions and trace information - ``setResultsName(string, listAllMatches=False)`` - name to be given to tokens matching the element; if multiple tokens within a repetition group (such as ``ZeroOrMore`` or ``delimitedList``) the default is to return only the last matching token - if listAllMatches is set to True, then a list of all the matching tokens is returned. (New in 1.5.6 - a results name with a trailing '*' character will be interpreted as setting listAllMatches to True.) Note: ``setResultsName`` returns a *copy* of the element so that a single basic element can be referenced multiple times and given different names within a complex grammar. .. _setParseAction: - ``setParseAction(*fn)`` - specify one or more functions to call after successful matching of the element; each function is defined as ``fn(s, loc, toks)``, where: - ``s`` is the original parse string - ``loc`` is the location in the string where matching started - ``toks`` is the list of the matched tokens, packaged as a ParseResults_ object Multiple functions can be attached to a ParserElement by specifying multiple arguments to setParseAction, or by calling setParseAction multiple times. Each parse action function can return a modified ``toks`` list, to perform conversion, or string modifications. For brevity, ``fn`` may also be a lambda - here is an example of using a parse action to convert matched integer tokens from strings to integers:: intNumber = Word(nums).setParseAction(lambda s,l,t: [int(t[0])]) If ``fn`` does not modify the ``toks`` list, it does not need to return anything at all. - ``setBreak(breakFlag=True)`` - if breakFlag is True, calls pdb.set_break() as this expression is about to be parsed - ``copy()`` - returns a copy of a ParserElement; can be used to use the same parse expression in different places in a grammar, with different parse actions attached to each - ``leaveWhitespace()`` - change default behavior of skipping whitespace before starting matching (mostly used internally to the pyparsing module, rarely used by client code) - ``setWhitespaceChars(chars)`` - define the set of chars to be ignored as whitespace before trying to match a specific ParserElement, in place of the default set of whitespace (space, tab, newline, and return) - ``setDefaultWhitespaceChars(chars)`` - class-level method to override the default set of whitespace chars for all subsequently created ParserElements (including copies); useful when defining grammars that treat one or more of the default whitespace characters as significant (such as a line-sensitive grammar, to omit newline from the list of ignorable whitespace) - ``suppress()`` - convenience function to suppress the output of the given element, instead of wrapping it with a Suppress object. - ``ignore(expr)`` - function to specify parse expression to be ignored while matching defined patterns; can be called repeatedly to specify multiple expressions; useful to specify patterns of comment syntax, for example - ``setDebug(dbgFlag=True)`` - function to enable/disable tracing output when trying to match this element - ``validate()`` - function to verify that the defined grammar does not contain infinitely recursive constructs .. _parseWithTabs: - ``parseWithTabs()`` - function to override default behavior of converting tabs to spaces before parsing the input string; rarely used, except when specifying whitespace-significant grammars using the White_ class. - ``enablePackrat()`` - a class-level static method to enable a memoizing performance enhancement, known as "packrat parsing". packrat parsing is disabled by default, since it may conflict with some user programs that use parse actions. To activate the packrat feature, your program must call the class method ParserElement.enablePackrat(). For best results, call enablePackrat() immediately after importing pyparsing. Basic ParserElement subclasses ------------------------------ - ``Literal`` - construct with a string to be matched exactly - ``CaselessLiteral`` - construct with a string to be matched, but without case checking; results are always returned as the defining literal, NOT as they are found in the input string - ``Keyword`` - similar to Literal, but must be immediately followed by whitespace, punctuation, or other non-keyword characters; prevents accidental matching of a non-keyword that happens to begin with a defined keyword - ``CaselessKeyword`` - similar to Keyword, but with caseless matching behavior .. _Word: - ``Word`` - one or more contiguous characters; construct with a string containing the set of allowed initial characters, and an optional second string of allowed body characters; for instance, a common Word construct is to match a code identifier - in C, a valid identifier must start with an alphabetic character or an underscore ('_'), followed by a body that can also include numeric digits. That is, ``a``, ``i``, ``MAX_LENGTH``, ``_a1``, ``b_109_``, and ``plan9FromOuterSpace`` are all valid identifiers; ``9b7z``, ``$a``, ``.section``, and ``0debug`` are not. To define an identifier using a Word, use either of the following:: - Word(alphas+"_", alphanums+"_") - Word(srange("[a-zA-Z_]"), srange("[a-zA-Z0-9_]")) If only one string given, it specifies that the same character set defined for the initial character is used for the word body; for instance, to define an identifier that can only be composed of capital letters and underscores, use:: - Word("ABCDEFGHIJKLMNOPQRSTUVWXYZ_") - Word(srange("[A-Z_]")) A Word may also be constructed with any of the following optional parameters: - ``min`` - indicating a minimum length of matching characters - ``max`` - indicating a maximum length of matching characters - ``exact`` - indicating an exact length of matching characters If ``exact`` is specified, it will override any values for ``min`` or ``max``. New in 1.5.6 - Sometimes you want to define a word using all characters in a range except for one or two of them; you can do this with the new ``excludeChars`` argument. This is helpful if you want to define a word with all printables except for a single delimiter character, such as '.'. Previously, you would have to create a custom string to pass to Word. With this change, you can just create ``Word(printables, excludeChars='.')``. - ``CharsNotIn`` - similar to Word_, but matches characters not in the given constructor string (accepts only one string for both initial and body characters); also supports ``min``, ``max``, and ``exact`` optional parameters. - ``Regex`` - a powerful construct, that accepts a regular expression to be matched at the current parse position; accepts an optional ``flags`` parameter, corresponding to the flags parameter in the re.compile method; if the expression includes named sub-fields, they will be represented in the returned ParseResults_ - ``QuotedString`` - supports the definition of custom quoted string formats, in addition to pyparsing's built-in ``dblQuotedString`` and ``sglQuotedString``. ``QuotedString`` allows you to specify 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) - ``SkipTo`` - skips ahead in the input string, accepting any characters up to the specified pattern; may be constructed with the following optional parameters: - ``include`` - if set to true, also consumes the match expression (default is false) - ``ignore`` - allows the user to specify patterns to not be matched, to prevent false matches - ``failOn`` - if a literal string or expression is given for this argument, it defines an expression that should cause the ``SkipTo`` expression to fail, and not skip over that expression .. _White: - ``White`` - also similar to Word_, but matches whitespace characters. Not usually needed, as whitespace is implicitly ignored by pyparsing. However, some grammars are whitespace-sensitive, such as those that use leading tabs or spaces to indicating grouping or hierarchy. (If matching on tab characters, be sure to call parseWithTabs_ on the top-level parse element.) - ``Empty`` - a null expression, requiring no characters - will always match; useful for debugging and for specialized grammars - ``NoMatch`` - opposite of Empty, will never match; useful for debugging and for specialized grammars Expression subclasses --------------------- - ``And`` - construct with a list of ParserElements, all of which must match for And to match; can also be created using the '+' operator; multiple expressions can be Anded together using the '*' operator as in:: ipAddress = Word(nums) + ('.' + Word(nums)) * 3 A tuple can be used as the multiplier, indicating a min/max:: usPhoneNumber = Word(nums) + ('-' + Word(nums)) * (1,2) A special form of ``And`` is created if the '-' operator is used instead of the '+' operator. In the ipAddress example above, if no trailing '.' and Word(nums) are found after matching the initial Word(nums), then pyparsing will back up in the grammar and try other alternatives to ipAddress. However, if ipAddress is defined as:: strictIpAddress = Word(nums) - ('.'+Word(nums))*3 then no backing up is done. If the first Word(nums) of strictIpAddress is matched, then any mismatch after that will raise a ParseSyntaxException, which will halt the parsing process immediately. By careful use of the '-' operator, grammars can provide meaningful error messages close to the location where the incoming text does not match the specified grammar. - ``Or`` - construct with a list of ParserElements, any of which must match for Or to match; if more than one expression matches, the expression that makes the longest match will be used; can also be created using the '^' operator - ``MatchFirst`` - construct with a list of ParserElements, any of which must match for MatchFirst to match; matching is done left-to-right, taking the first expression that matches; can also be created using the '|' operator - ``Each`` - similar to And, in that all of the provided expressions must match; however, Each permits matching to be done in any order; can also be created using the '&' operator - ``Optional`` - construct with a ParserElement, but this element is not required to match; can be constructed with an optional ``default`` argument, containing a default string or object to be supplied if the given optional parse element is not found in the input string; parse action will only be called if a match is found, or if a default is specified - ``ZeroOrMore`` - similar to Optional, but can be repeated - ``OneOrMore`` - similar to ZeroOrMore, but at least one match must be present - ``FollowedBy`` - a lookahead expression, requires matching of the given expressions, but does not advance the parsing position within the input string - ``NotAny`` - a negative lookahead expression, prevents matching of named expressions, does not advance the parsing position within the input string; can also be created using the unary '~' operator .. _operators: Expression operators -------------------- - ``~`` - creates NotAny using the expression after the operator - ``+`` - creates And using the expressions before and after the operator - ``|`` - creates MatchFirst (first left-to-right match) using the expressions before and after the operator - ``^`` - creates Or (longest match) using the expressions before and after the operator - ``&`` - creates Each using the expressions before and after the operator - ``*`` - creates And by multiplying the expression by the integer operand; if expression is multiplied by a 2-tuple, creates an And of (min,max) expressions (similar to "{min,max}" form in regular expressions); if min is None, intepret as (0,max); if max is None, interpret as expr*min + ZeroOrMore(expr) - ``-`` - like ``+`` but with no backup and retry of alternatives - ``*`` - repetition of expression - ``==`` - matching expression to string; returns True if the string matches the given expression - ``<<=`` - inserts the expression following the operator as the body of the Forward expression before the operator Positional subclasses --------------------- - ``StringStart`` - matches beginning of the text - ``StringEnd`` - matches the end of the text - ``LineStart`` - matches beginning of a line (lines delimited by ``\n`` characters) - ``LineEnd`` - matches the end of a line - ``WordStart`` - matches a leading word boundary - ``WordEnd`` - matches a trailing word boundary Converter subclasses -------------------- - ``Combine`` - joins all matched tokens into a single string, using specified joinString (default ``joinString=""``); expects all matching tokens to be adjacent, with no intervening whitespace (can be overridden by specifying ``adjacent=False`` in constructor) - ``Suppress`` - clears matched tokens; useful to keep returned results from being cluttered with required but uninteresting tokens (such as list delimiters) Special subclasses ------------------ - ``Group`` - causes the matched tokens to be enclosed in a list; useful in repeated elements like ``ZeroOrMore`` and ``OneOrMore`` to break up matched tokens into groups for each repeated pattern - ``Dict`` - like ``Group``, but also constructs a dictionary, using the [0]'th elements of all enclosed token lists as the keys, and each token list as the value - ``SkipTo`` - catch-all matching expression that accepts all characters up until the given pattern is found to match; useful for specifying incomplete grammars - ``Forward`` - placeholder token used to define recursive token patterns; when defining the actual expression later in the program, insert it into the ``Forward`` object using the ``<<`` operator (see ``fourFn.py`` for an example). Other classes ------------- .. _ParseResults: - ``ParseResults`` - class used to contain and manage the lists of tokens created from parsing the input using the user-defined parse expression. ParseResults can be accessed in a number of ways: - as a list - total list of elements can be found using len() - individual elements can be found using [0], [1], [-1], etc. - elements can be deleted using ``del`` - the -1th element can be extracted and removed in a single operation using ``pop()``, or any element can be extracted and removed using ``pop(n)`` - as a dictionary - if ``setResultsName()`` is used to name elements within the overall parse expression, then these fields can be referenced as dictionary elements or as attributes - the Dict class generates dictionary entries using the data of the input text - in addition to ParseResults listed as ``[ [ a1, b1, c1, ...], [ a2, b2, c2, ...] ]`` it also acts as a dictionary with entries defined as ``{ a1 : [ b1, c1, ... ] }, { a2 : [ b2, c2, ... ] }``; this is especially useful when processing tabular data where the first column contains a key value for that line of data - list elements that are deleted using ``del`` will still be accessible by their dictionary keys - supports ``get()``, ``items()`` and ``keys()`` methods, similar to a dictionary - a keyed item can be extracted and removed using ``pop(key)``. Here key must be non-numeric (such as a string), in order to use dict extraction instead of list extraction. - new named elements can be added (in a parse action, for instance), using the same syntax as adding an item to a dict (``parseResults["X"] = "new item"``); named elements can be removed using ``del parseResults["X"]`` - as a nested list - results returned from the Group class are encapsulated within their own list structure, so that the tokens can be handled as a hierarchical tree ParseResults can also be converted to an ordinary list of strings by calling ``asList()``. Note that this will strip the results of any field names that have been defined for any embedded parse elements. (The ``pprint`` module is especially good at printing out the nested contents given by ``asList()``.) Finally, ParseResults can be viewed by calling ``dump()``. ``dump()` will first show the ``asList()`` output, followed by an indented structure listing parsed tokens that have been assigned results names. Exception classes and Troubleshooting ------------------------------------- .. _ParseException: - ``ParseException`` - exception returned when a grammar parse fails; ParseExceptions have attributes loc, msg, line, lineno, and column; to view the text line and location where the reported ParseException occurs, use:: except ParseException, err: print err.line print " " * (err.column - 1) + "^" print err - ``RecursiveGrammarException`` - exception returned by ``validate()`` if the grammar contains a recursive infinite loop, such as:: badGrammar = Forward() goodToken = Literal("A") badGrammar <<= Optional(goodToken) + badGrammar - ``ParseFatalException`` - exception that parse actions can raise to stop parsing immediately. Should be used when a semantic error is found in the input text, such as a mismatched XML tag. - ``ParseSyntaxException`` - subclass of ``ParseFatalException`` raised when a syntax error is found, based on the use of the '-' operator when defining a sequence of expressions in an ``And`` expression. You can also get some insights into the parsing logic using diagnostic parse actions, and setDebug(), or test the matching of expression fragments by testing them using scanString(). Miscellaneous attributes and methods ==================================== Helper methods -------------- - ``delimitedList(expr, delim=',')`` - convenience function for matching one or more occurrences of expr, separated by delim. By default, the delimiters are suppressed, so the returned results contain only the separate list elements. Can optionally specify ``combine=True``, indicating that the expressions and delimiters should be returned as one combined value (useful for scoped variables, such as ``"a.b.c"``, or ``"a::b::c"``, or paths such as ``"a/b/c"``). - ``countedArray(expr)`` - convenience function for a pattern where an list of instances of the given expression are preceded by an integer giving the count of elements in the list. Returns an expression that parses the leading integer, reads exactly that many expressions, and returns the array of expressions in the parse results - the leading integer is suppressed from the results (although it is easily reconstructed by using len on the returned array). - ``oneOf(string, caseless=False)`` - convenience function for quickly declaring an alternative set of ``Literal`` tokens, by splitting the given string on whitespace boundaries. The tokens are sorted so that longer matches are attempted first; this ensures that a short token does not mask a longer one that starts with the same characters. If ``caseless=True``, will create an alternative set of CaselessLiteral tokens. - ``dictOf(key, value)`` - convenience function for quickly declaring a dictionary pattern of ``Dict(ZeroOrMore(Group(key + value)))``. - ``makeHTMLTags(tagName)`` and ``makeXMLTags(tagName)`` - convenience functions to create definitions of opening and closing tag expressions. Returns a pair of expressions, for the corresponding and strings. Includes support for attributes in the opening tag, such as - attributes are returned as keyed tokens in the returned ParseResults. ``makeHTMLTags`` is less restrictive than ``makeXMLTags``, especially with respect to case sensitivity. - ``infixNotation(baseOperand, operatorList)`` - (formerly named ``operatorPrecedence``) convenience function to define a grammar for parsing infix notation expressions with a hierarchical precedence of operators. To use the ``infixNotation`` helper: 1. Define the base "atom" operand term of the grammar. For this simple grammar, the smallest operand is either and integer or a variable. This will be the first argument to the ``infixNotation`` method. 2. Define a list of tuples for each level of operator precendence. Each tuple is of the form ``(opExpr, numTerms, rightLeftAssoc, parseAction)``, where: - ``opExpr`` - the pyparsing expression for the operator; may also be a string, which will be converted to a Literal; if None, indicates an empty operator, such as the implied multiplication operation between 'm' and 'x' in "y = mx + b". - ``numTerms`` - 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 ``parseAction`` tuple member may be omitted) 3. Call ``infixNotation`` passing the operand expression and the operator precedence list, and save the returned value as the generated pyparsing expression. You can then use this expression to parse input strings, or incorporate it into a larger, more complex grammar. - ``matchPreviousLiteral`` and ``matchPreviousExpr`` - function to define and expression that matches the same content as was parsed in a previous parse expression. For instance:: first = Word(nums) matchExpr = first + ":" + matchPreviousLiteral(first) will match "1:1", but not "1:2". Since this matches at the literal level, this will also match the leading "1:1" in "1:10". In contrast:: first = Word(nums) matchExpr = first + ":" + matchPreviousExpr(first) will *not* match the leading "1:1" in "1:10"; the expressions are evaluated first, and then compared, so "1" is compared with "10". - ``nestedExpr(opener, closer, content=None, ignoreExpr=quotedString)`` - method for defining nested lists enclosed in opening and closing delimiters. - ``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. - ``indentedBlock(statementExpr, indentationStackVar, indent=True)`` - function to define an indented block of statements, similar to indentation-based blocking in Python source code: - ``statementExpr`` - the expression defining a statement that will be found in the indented block; a valid ``indentedBlock`` must contain at least 1 matching ``statementExpr`` - ``indentationStackVar`` - a Python list variable; this variable should be common to all ``indentedBlock`` expressions defined within the same grammar, and should be reinitialized to [1] each time the grammar is to be used - ``indent`` - a boolean flag indicating whether the expressions within the block must be indented from the current parse location; if using ``indentedBlock`` to define the left-most statements (all starting in column 1), set ``indent`` to False .. _originalTextFor: - ``originalTextFor(expr)`` - helper function to preserve the originally parsed text, regardless of any token processing or conversion done by the contained expression. For instance, the following expression:: fullName = Word(alphas) + Word(alphas) will return the parse of "John Smith" as ['John', 'Smith']. In some applications, the actual name as it was given in the input string is what is desired. To do this, use ``originalTextFor``:: fullName = originalTextFor(Word(alphas) + Word(alphas)) - ``ungroup(expr)`` - function to "ungroup" returned tokens; useful to undo the default behavior of And to always group the returned tokens, even if there is only one in the list. (New in 1.5.6) - ``lineno(loc, string)`` - function to give the line number of the location within the string; the first line is line 1, newlines start new rows - ``col(loc, string)`` - function to give the column number of the location within the string; the first column is column 1, newlines reset the column number to 1 - ``line(loc, string)`` - function to retrieve the line of text representing ``lineno(loc, string)``; useful when printing out diagnostic messages for exceptions - ``srange(rangeSpec)`` - function to define a string of characters, given a string of the form used by regexp string ranges, such as ``"[0-9]"`` for all numeric digits, ``"[A-Z_]"`` for uppercase characters plus underscore, and so on (note that rangeSpec does not include support for generic regular expressions, just string range specs) - ``getTokensEndLoc()`` - function to call from within a parse action to get the ending location for the matched tokens - ``traceParseAction(fn)`` - decorator function to debug parse actions. Lists each call, called arguments, and return value or exception Helper parse actions -------------------- - ``removeQuotes`` - removes the first and last characters of a quoted string; useful to remove the delimiting quotes from quoted strings - ``replaceWith(replString)`` - returns a parse action that simply returns the replString; useful when using transformString, or converting HTML entities, as in:: nbsp = Literal(" ").setParseAction( replaceWith("") ) - ``keepOriginalText``- (deprecated, use originalTextFor_ instead) restores any internal whitespace or suppressed text within the tokens for a matched parse expression. This is especially useful when defining expressions for scanString or transformString applications. - ``withAttribute( *args, **kwargs )`` - 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 ``
``. ``withAttribute`` can be called with: - keyword arguments, as in ``(class="Customer", align="right")``, or - a list of name-value tuples, as in ``(("ns1:class", "Customer"), ("ns2:align", "right"))`` An attribute can be specified to have the special value ``withAttribute.ANY_VALUE``, which will match any value - use this to ensure that an attribute is present but any attribute value is acceptable. - ``downcaseTokens`` - converts all matched tokens to lowercase - ``upcaseTokens`` - converts all matched tokens to uppercase - ``matchOnlyAtCol(columnNumber)`` - a parse action that verifies that an expression was matched at a particular column, raising a ParseException if matching at a different column number; useful when parsing tabular data Common string and token constants --------------------------------- - ``alphas`` - same as ``string.letters`` - ``nums`` - same as ``string.digits`` - ``alphanums`` - a string containing ``alphas + nums`` - ``alphas8bit`` - a string containing alphabetic 8-bit characters:: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ - ``printables`` - same as ``string.printable``, minus the space (``' '``) character - ``empty`` - a global ``Empty()``; will always match - ``sglQuotedString`` - a string of characters enclosed in 's; may include whitespace, but not newlines - ``dblQuotedString`` - a string of characters enclosed in "s; may include whitespace, but not newlines - ``quotedString`` - ``sglQuotedString | dblQuotedString`` - ``cStyleComment`` - a comment block delimited by ``'/*'`` and ``'*/'`` sequences; can span multiple lines, but does not support nesting of comments - ``htmlComment`` - a comment block delimited by ``''`` sequences; can span multiple lines, but does not support nesting of comments - ``commaSeparatedList`` - similar to ``delimitedList``, except that the list expressions can be any text value, or a quoted string; quoted strings can safely include commas without incorrectly breaking the string into two tokens - ``restOfLine`` - all remaining printable characters up to but not including the next newline pyparsing2-2.4.7/docs/Makefile000066400000000000000000000011361365333160500162200ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = PyParsing SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)pyparsing2-2.4.7/docs/_static/000077500000000000000000000000001365333160500162055ustar00rootroot00000000000000pyparsing2-2.4.7/docs/_static/pyparsingClassDiagram.jpg000066400000000000000000007155621365333160500232160ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222{" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Aloou;ޱwKQ@T(T :U|-@$چ"W.' 8viaeVm|sPhM n OS[U>NBa<8Qn?Zoqvuro G"UgcAa$YSN4K}'ɎjV860WLkVw!_k=ֹ=Y8ռbz=v`y[CNXt+ =~[t^G[!}|j.ya?Ǿ,x眚Mֹ=G!_k=6)7閡ƬLHcjw@#RT>-A5?]y-] ui+ni %`z~'un?ZoW}\gXytf`2c jxv^$;I~Q6ҩ66U2 -mu@z!_k=Տ? YX ;qn-!E3<D%cc#u(t.uȎ 7;1 wn2\64rR!LFI2A Dn?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAEsQn?ZotP?n?ZoWAE|K>#N=r'ˋRwۺc9'O&%{]'w>$O[ +Jj'2lD 9#Jg?oEWA@@%{]'>6^I㢀>o+lW?WR٫((((((((((((((((((((((((KMxWW-6k@JD,+*E5CZ <kC<[TV<ӕ_?5oJ?UE9QZ <kC<[TQÕ_?5oJ?UE9QZ <kC<[TQÕ_?5oJ?UE9QZ <kC<[TQÕ_?5oJ?UE9QZ <kC{ v.)bO ΋Z\P̕!Cs@@/G4?Ty~0ևy**1|kC@%{]'>6^I㢀>o+lW?WR٫((j(ItѾ>A'woVnCŵFv*Pڮ/7=VI}oo5{UO>Ě*MiѴ,({ O5Ο0$BLC"4_$$1_ đ$a"9Ö{WD[IJ4 HiPw_9|Ҹvg8YX%HWC ׃ &l%p~pa:~QDVVv{2p=k*I $9$,@KZ^e;$'NUpڒMoIi5K$E\ i9' úcBc+Xm㶅a[rYZL'n8jSnVHa-=̖acTK둃].$߽^_f] #6ڶ>} 9QQ}Qu1H.cbR1Gl_\j$s,ڪL+XKpElQQRG Hδ4 =E) S2v9ٜOj:UΗu#ٽe*VC?'7> }N3I|J#n fwN4_^By6tmEn-+`_Roe+PVkqHPXUOc[_[/yL[ιtNݼcORѵ+Bk=Nt{FL)|08s[g8Ry&tXfbi5Lwq0A.\! uK$Ce= V.VYjKjf[‘m ?&rpH țzՄ֚UbNqj'tFǒ- e8IIqJV`uB7˭iѯ` `A^i:e<]jKyG-+"A<G>NUF$bYč*ףnS+jr8gc$l{"77a^>ةk߿F3Eq M $R(dt`UGQOm~yHv1­V.aER(((((((((OzL]owjL4G{ƨV&g#TaϮ;5Eޢ0?[ 3}u, >iQ`2JY^}d(FLw2s~- ޡ`:Zl>t;bPG0tiQ >;t sN:PI-FBgc|1r6VmÍZDӬ`w1 0|l|T)OL]owjL4G{ƩޢI-WPssuj Ρqqlb,JʻULVuGNi-DE"2:qoL4G{ƨL]owj #c-Ž!2·gYUB\ oy1֟nm2YiA{L&a 򍨋bH溏L4G{ƨL]owjX}nq_9B'n'iRXe o䳂I$ֿ|>Y]m&+4 |Ĕ36w`1laϮ;5G&g#Ts;²oQX?iQ >MoQX?iQ >zL]owjL4G{ƨV&g#TaϮ;5Eޢ0?[ 3}u, >iQ`7L4G{ƨL]owjE`aϮ;5G&g#TX J? Ewuƚ+:,çqҽ [Š( (/]]hnyol3"sl@p˝z[x@0SNh/=[-o=zZzܢ?=[-o=zZzܢ0oea9krտr͗#ߛ/G(V[6_GC~l( ?=[-o=zZzܢ0oea9krտr͗#ߛ/G(V[6_GC~l( ?=[-o=zZzܢ0oea9krտr͗#ߛ/G(_Wa4vS\FAfT!`-Gohz^X_pq&K6@%{]'>6^I㢀>o+lW?WR٫((o]ꩬs$i$M4Z%Z0 7c=9WSPj H1P}%Gե)O¢=ZoC֗G-h˾#6's""ּc{"z#?47Sϔf qѾjQ/c\EI6 #pǮiBҬE럳VOAWBA4 zWЛ;Y3okEeq qnJːwیTsx Ih-}V*ŽOs[9]M#߉/ngDd!ыƀީNxcX-U4Mf3u\5Z1`k_ izrZsZ[3xڴN`&؝/1/G9qӣOz(FWQo|Os I,bm>)N2l [qG{h$f)q08ꆑ Iu!Z}Sb 0;f,P\7D͞>oƅ:]cuNֹ$٤M5: D|a۪sӁ]=St9#K>b #X!T`q>r(/%- ((((((((((((((Oa;B͞*<86qt&%A*N:t..GuEq_5MJ8O iFƍcɝG9qbAl-c rcRn}уckEq뷧WMAmnƙm+HPd)kzxHѦTݢs5 UwHwiQA:GsEq#?,,TwoCRb5ˌ1>ˎ4ډp&&wA8ƕ\:Z+MJPPFѤ t< J1sn_j<\Rs0HZEce A܏AMŧa)~'WEq:!85-6TvpǻRp2i%8'&g"O6Hny a#hS]էH#6Iqۼй22mCu[+[mIZpm<ϒ`>NHRuirM3VIt!a,Qۈ;1l/zV\zic=Ν V'Da; TX%5΢Ov$6\q7/C#9a&QEHŠ(((((((((CA/A]pA u(b ( i ?)l5=OaM;KauI5t*[Ky M4B.*I9'wF_ZIot-fLӼLYxFQ~lb=WβE.[{ A78sIE j b'2GbOy8"F,;xYkXs@(!u?< %,;Cd AГ8gLYn-˅d~Vg.RHJdU It=l.mG_M>}D%ꌶI`X3t'tUј&E4 "trwwR旫 aЎq"$BȊd^O?[9I6^fh0@qK_kvN6y%Xah[͑C?zEtWv[_=m-*O$~|Uc=#@wAc*$KXK{ZX"(W{,lKm.\l # >P 4[ky}"!-w32*F.J<`0[: }_AmLLo(@>^v#9:׉-4=-Nx]Ānչ89: 6^I㢏Wwx ; _[5t7@w?Կj(((]~ ,c$X#SIֵoTmٿF~mvsqҗ7:WԴ]~YL$s2szwbumKJ?&>|w3D_<a|_e%,~ȱHHC$OGS<7qM-YY AМ\kWIFYnfǧxCZinxA(Xf_0y>}M"}FS0 |KCWn;ZO=.;f/Wۻofs9Y;mR.- 'P@RHn=3 j~ptGm͌fF>.>kŕ-~cY#'!'y'+Nӵ{|ɥI"ŔEpJ'jfF%bkvj槖=:x^ &WiL"9Ä߂]3U`ڒMOe Flmǘ逤qtՙeMi-aYF#HG_j>0Ƌ^$q낳*(%<m=jg++moīkzEw*z<{췑N<ͻ)`NGo○SVNxkj"yAȶ+zfUGIEfkV1B&7&N0'j95f7rܕ)'lضi*sz6ETQEQEQEQEQEQEQEQEQEQEQEQEQEW?'%Uj+*E5iI"CI#"c֝Y֏OAC3ǃx%AFbIMskrZ+?ZQ5BYX1"9@`J1@zW3ǙBbl')O^<Zi'&W;ju}mdg’vz!k:uuuebt#s8g%C&nG~4yn-&ivX?^KgPFGJ̽4M*6sLN@,QH_JH,t%XZF<Ty=: 籣uد˩'ĩܰrw`\d^N7[($k"0d` 9.)}7M+e+FVye6M}ܚO]$dAcw=ٶbʂ$ JsEryܐI95˘t7ftMd̀==HtpyPU6+*(9zaV NTbS\λ)jt |[R:xb"vģQAڥ5)~|Nڏn#Yvg}9<> #1ىcc#$#;msJ{]G$#V ?pN? O\T^ Qw3GZM71F?-CxmOX"[n(̠)uYiKQof8Wjg<Ēy&((((((((((((x PWw\&%_"lf (( i[ t5x@0SNh>^,QZϹ-4DZ,78<꾽D$ѭ:4P2(%ƫVIO24Sza!z8)Z0."ICwK1d"E ֎mYeDhc8sۜl3$Wֲ$k#K*\FV-wLdehP~$wvqjoUn-:}Cz Mu,O,5#1ˉV.p>xɩc|Cq j3N]9Fx8.1Z2@ %`F"س3<2\ZоN6E*p1׭GiM"Pk op|>\nۙH5upq"myB3%h0Eei+g/crتέH%t|\uw'մP4B%,[z6 A˭鱩@<)`:*?35}cJ+[QKxdxc(geRRNJ[k޷qwoikm<piraF8UBHqI *C) zѳl k[A F*gҬ\Eik5Ͷ(Pd+8^83 NĎ{$ mH F-(tx\Zěuerr*&qǭv:fy +4r6V8`AU{Č-!u39tn맮oN.FII-(EPEPEPEPEPEPkG+E0\(GUI[c 1 Z`d۷~vnv܌--1BGDog'`sbT FPT#T DIx[qn pqkivB >Aq=6 bힼSEh^qYz^k$I݅Vp,e9IUW𕃇['F5Ӫqkz^Wax'Mڅ{tLv@8rrt&VԯQxcY1 I8/sE?k>05֣w7ni0KC*+}sV:Eiz,涷#2Dk45}Š(QEQEQEQEQEQEQEQEQEQEQEQEQEIu w$Ŀ M@ֵ]=yp^Lrvs\.xĊ2mG)FFȊXdW9(kb0ܫA!GԐ+-g}B,IjmzyqǠ`?O?lx&7̄I&!zkNTkbh;Bv}W?bǠ1@dU._L(V1e@##T>#P<gQ*#Xfˠ)K3Jʓ1Ce[J+[֐!E -7.䏼1io#; 'K˜ґ568дLӆZ9!ÂF8I.vXK)hh5vuےr 0y9&i$в$~[ 2b0y#H=ANz=C7m,x X%ٶBܷb~r0*_j3-Eg\Mpp~r:rZw+<1Y?q4jk%-pY͞E#Z+XbmqJ:l$*0\hڏxn//c)ǘfR yq;ӧdUk-HbH!Iyc1׍J( ];J`w]%szw kv6JB ( ( ( ( ( ( (>@%{]'>6^I㢀>o+lW?WR٫((((((((((((((((((((((((sӉ33VA.%! X#HEIN,gw$P p7mBxM.9mR93 `͜. pp;qi-Kn'UP=W<~8ﰚW}KSd 4N*2U2 lw:ZvW1Gk*$*!fEqN:0h[_;;V!wȻ"*)_sb3\hmc ~9R2ݬ@#]ֶȱ]jLcYO<*6vpp'ۥG7dYduVKeeYsk`>lO8۾H.|Is ~so4ΤlV!C E'Ԯq;${uϔS\8=JTVB4em)/&iJ`p0ݧ=Ĭx|u,pp0H>X]glbX! r9!մ 1jnyn&_1\4A$\YiniEQEQEQEQEQEQEQEQErA x PWw[ESQEW=OaM;KacC#ii1~y!YA# AV A#:cC*UrpS@>NDoo㾺짼mKmѤQβOk{<wlŦ iN6"nP7 dG{bi$0]+ŪE~,/ !T0Gq=sM uik}:0Ҵ 2rHCxbѴ !Ml'"A{wɒF;aϠZZzݽ&>UL wP|< Y.B$'ˍ/?h`s`8#`QwaԴ[ mNnFCp͸cto}z[ >V1 }v\·Gi}=M~C>F/1zsj> .O ۴"#Cm?'dݴt>(,63MAe~%t{"FyjI_ޛio;xDPc%C̀>\1.ybxZEP #ʯY7m#XwO}Ev 鏩i6h6}]78l?E-6?%eQCC#8<`z?+hn$k# ;!߸nB9@OqcURd.Z[dGonbH 6Nԥ]sMGPPJKفYnV3c'>/hsD:>j3zT}5ͥƬ#y#W*aHָ)4ˡG;p 8B~V7y<ۥm/{"?g)}}.#NMuF[++i$!A>OӢYڤ 0D@Bp)c\#>+m ;xoZ2 4`29@r1f,>Ǥ0KsnX1A(gzuߵb\[߸O\U7YDZtuHQBZ7z %Ym.g(2HVx$t&,5[i%W QYE DU @r>QOgo':MG=*sɷDˎp:[آI ,7`=wrH\N@] Jͼ"-D#:C 2Tr9tbO[Mqm|.s7D_ FHANُ҅þX&cѴ +f֎:$9KW(\)Ӏp}?>tJ;-ZAH,w6G\Rx&w7&k";',L@ `u7;*(w kv6J(?mt ( ( ( ( ( ( (>@%{]'>6^I㢀>o+lW?WR٫(((((((((((((((((((((((( >[.Q5+%[ "UT1}0kP֡C$o'n8d@%RuԂk{Vɽ#H.@ &v{'Mr$j-8XP#Oآzޝ,hCF7'ctN#G|T6!nu'K`X=G+͒8N:I<#lc/V56cr\-ē*У:Mnh$tbF,!R' ׽ Ucu$Rj(f,dfU@3+qzpiL̻A'\[>) 36dOR91Yx6ndV/݄bά@A] n? ē*>%[lO'4?K:iLZgۻhnۜkV5E∦[]+r.c+<'$ "F$FeP 2 E ((((((((ӿk_ ѷuW7@ֿan뤤HQEQEQEQEQEQEQE+]jr*T;(beB9J% -Kk0$J̞V2Pq=˷'}ѿo݆r!a#hr73{f Es*r޸e*Os?$9gJ{ҢX+lYںúĂdGÎ[AY:}w0Ikm,s6n#hՄs{M!kmhm&8gܕi㎆Rx!Ido-ȁF߻ 1S5gkuPm39N˿`Ro[_tZnjikkco"bL`v,u(o![L6&d#[ sntd{E>m܍>ǒ8Mucm鳬ڥ7,s"1WEo'vv871ʸu6Qciye@r ҕ9I=;J}鐉m"-yv:מ$ƎU+0Odd/Uy'F3pcM)p1S[h/#xgQO|}Kg4[Bc*J ( ( ( ( ( ( ( ( (9 J? Ewuh?P] #wGOUw6xs#UҤʯ0P)q"ɒ[Hn9sYTc,0JF:QEx@0SNk{Ɵšw@QE2(((((((((((( ];J`w]%szw kv6JDQ@Q@Q@Q@Q@Q@Q@ |mۿEk@O7@w?Կj+o+lPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\xW_)OPn*Dlu ؊H]lixF żRVwmnb8hӃxSԮ-fOKh#d|-Թ }ђHp'ev$HbydpfcM.eإkor"%B3.i0~P37c5];hVKpL2H'5;X3/e%?ʿWUU* $FiC HnHݠqZ)ܓs̰$uHXk:.׷R:rʠ{?[k{]9 PHZKȮ$rO}deq嫗>c)xF6Ws,,ƈIS&7bV(5]3iepʛg#[\EspxA\r~a(_ZuMj[;GUHP=vb`uw]{6߼n{m..u{ mJ}_xT s rY|fpɥHen*63CsOG\ͭi.USex$ 4'ڴUֱ.aN,#ha"nQ\9-SmlfXd8 ^c>U;^Qnl2Cqn,G@rïR=ҳrIբ) (((((((4(  @H[Š( (((((((((((((((((((((((Wwxow:(º]s u/- ((((((((((((((((((((((((+*E5tIu4&ц #Nz RK4 'Hu}:ۢQR}Gk/l0LӪao?Sl3,4AT/̫a=E3ij[%$F#anT>sP2>b$v&rj-|7Z2qN 2K.ݧ*8ӥ/#Yf EEk hMTg'理K8ƣ^_:Mʒ Hb1+_{AmNѺl9(zM7fJqt>x; XnFqp8hZr +B%+ 6B->czds0h%9,LdVq{zE(e41!$1h3ҡݍO',rn.%XdrVڵݮlͨLkt[=أ79<'#Oʶa𱈝xȀQk{k[7t &q`C;x3}+ˉ;XF^LB'$+Ix1%jgLIx )xֽ4Y14"<(dYxz=W!a1 mh@ˆkmqŷ{}PuOާ\M(l@|ÿ~r;`s/}im{e!y)T;Pgk|2MJ-= )5Բ$X]0N P]vC F G5V  u}+kd#NZKq n!v] [ -Kf`R~cFwMy^d{ILar~\tEaʻ1-n rKLd XH9ϽIi]Os$W;sc8ErERQEQEQEQEQEQEQEh?P)WZoݧ8m ~Pӗ.sٱִF2P: Tl<7lz۝]+)X ٌA.Ӛσj6mpjDX0޻ c5CUuxl^w\!@l+H6O9s*ishĜ}tëe FӌR}HGwMI"TYuI"z`W9GZ'RG鯮Eu)YQ `=rzvlǩZWkήdC6FsB'F/;hI5:%ZW ebv3Nj̾f1沤6G$d3Z tH=LOk eRh2="3xЫE6FאOѪ7?TDڭPn￞cT!Piu)#iF䜡MGv+ǯIu /́yxn;;Dϱ&E4ɨ٤ iT*3U{]o2[N6M%IBd+еkkY`kipPz(WZ_ i_$P[Gyx+%rzdwYnһ4gE`L# 6[y~etUGÞNE[ԼyqkOG.aE"xR ĶbBʤS-?1N:Y Z/[km՟s;ʁB#r0TxAähmḗVH'ϕ#\Y0pvYxb;xF(-v )eCFGL;Olqu ̥yUpNz^WX}$3"(ʉ&ThЖ6xS*[J2yL\f<ٰ||-xMFE!oBxQ$rrjlGooppqpe?k 6%:m7VDhn0j&I${إ7A{Cꦷm<9ao{5Ɂ|ǖ{ΝM݋V0A{m!cʡvzơ}wH5[f@ElJʹk1N![x m ˜OڥAYL7L0o>ʅ$``:9qY[t{CH%L)l\de Ѿ{o6n}.? <[p ]_O@ݶp/ko&Ƅ~bs Dc^)cHC#`zGQY>#-IYHYیmGh^7%T`b;:cq,xPA6uYH'{O4ە6#zukhM8ϵE&mg_.đx|V$ZE'EZIn>o[@6ʹ>`>qVt ZCΣgk=6ƞd(Mĕlqvݪܛcqm#PıUG{8o{Tk'N0rwM;FB8=^*W6Qi32+yM)IY6"($ ϵT̈o -PJ(I`(c\ճj7Mq..@1Oz'g,-4Z'dvo/l("0}lZz]4{-Epm6oz5$Ɩ ţQ)œ<=|:'}|~_";sQim ¶KuT`3V[I"e!TL0PI۔N[uۼ>|4 ¸IF!4*t6qۏjJ[{\}kcT KƘ zQ۟OX1ʹ\I.P LpGV^yal-H5 e'1q'vZA"g`ێ6#W_ (O|ܠxo'>!彨2jx-7IK,ct=) ѨH$ -ți\¯m\U["]%V|+"8Y1ؚtxN_'%lc?Uk_ ǣr,Z#':'Ir>l:Lۢh±}e*Xc8oJߕI 34WWSħu8xgR΃n<+tv@AS ZTqEmᴎ$]M{=օV'[h,\[bC, #ݎI<8\Q7mdiH ه85)_BdwmۜvN+"zm~odi|)K( |йny{i5,v2@vKX֒̄%ߘZ%fO+l (8bOCgms) #WP1ʊH xEbpUۆ/*0Rp9ϰ8iZQ15_=@d1-E*7J4HbY)98zu^L K]de7ny{7j>*Ů`{d*7[j *'+a^ͼw0ۼӶ-lsN0:W=wg s[2G6Wo!#fNB3玁~,P,HF8?l>m}?,I%/x cWC+[WVZy ]AfwJ ޸Q bW#9}{MJAd#U,Oe9K}ȃ|c>\Q8:D>I0}c( <;`\mm0yѲȇ(pWZtTx}g>nluznZw̟ؒ_`U&gpBVp"xri+C(hD1J4w9^'@.\˹ER([VJ'i At ( ( ( ( ( ( ( ( ( (9 J? Ewuh?Pz`?O~3_XYDNEh&@V wqU}$` Haweq;ԙ"=:Nxk @?}zH*Y6%9}\N*ދ̯x gc >`09z^=tqlۖ "W$H'pc(~ߠ^:/SItIg &GUq ǜhZE8Qvs\_rZGpd`iDqzEBd uTmҏ6լ1'uۆRBpw>6\]G(HfV]%9ڣ\o[fk1E2L(=ANKy与@"J8 @+~R:rxzY-ۿwN]:'r4YI8un* QEQEQEQEQEQEQEQEQEQEh?PQHzӤ^Ěvw3 ke.Gl;Fyvɝ!e32PFۻG"I>ڳN"v0|EcAB-:ⴕٌINwL~s]K4Wx~jo(pK>].uY^Ӛى$򋎪$۰Gj ?Hlm+%ϔnǟvyc'WGmgZX'FBIl-T-#;)|;d$I95?oHLY(JB63Y/0Icv~tգӝe #Bd 8,9=sk's6ևS4I* 0O>ƒxmfjY-Vr][ZE d1,T{vo4̇E*As\$]0ۼ Ǯ6WZخgus##JH,t%XZF<Ty=: 籥}u8KG 's"g`1 9kI;!ڹ$n`d/ʅ'nzx{8$. c?W)g{a(-t2w]1ʮ>P|S~c:Olu Uκ.v[N˹*R}(,(((((((((4(  @H[Š( (((((((((((((((((((((((Wwxow:(º]s u/- ((((((((((((((((((((((((+*E5tIukh{K ,0>犹o:][d chVS&ц #NsMnPlxm^E pk&6)yqgu#4 n aAs5yg[1V$d e :?cVP#=,OcM[dm8V By_iUC/7o3f}kmm_RGfbO^ID%E;ytM"ݼvi98!9s7 rvAHdWpdJrĞ8I9'='Š8#7V$mmx/1G2O`k0ŵϙ]E,Is&"D̻r@9kƑ 59ؓjٲ*i]!ȺYP9d9p2~POC^YH⌢줒?3N2`RyW=[[uiMŢ.*ni@띹;\85 =]9'-ɏ|;W^3~leG?ߥ8 Q궊ii'?\7 n ԒpqU%m泗UtndhdD,Tdc>WlqyQOn8nEF%S!q!R"opqq;HlAw,  0pOyTs, kuGgPGLwPiVrN{ܙcL[a$7=9gWjɫ,.z|`f#kyQIF Vث5jҋo"TIEGG?ߥIdT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yPT~p~yߥrA x PWw[ESQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE+Bvl"Ύ?'N?$Q >Bvl"::+Q;\_G$ .s}GsE?'k( 3IeOН/H,Ύ?'N?$Q >Bvl"::+5KSӖV)^F`왈ݺ5eu # -Q@Q@Q@Q@s;y_]覮H~y#P#&3p0Q(ONݣ׮FBygh\L dda8W]7Yp ~[5Rpyl.Ƌ\"Asa;т`,pU[^ V.)켞b#bpqǧ\NM "6 ȃhU2pBQNG O,34xc&yYcrc)Re70,BGqyziH~+B/n$o5]OcS$#E,M6}KK 9|,fR1k[oҝiGEI7T/+=6X7m$YRI2HN#!mYnKg+%/>qR#PQIuipVTXفUV|(:)b 4I* S5X6,r$@3ŭb ˟-eQ9$GoҀ#GoҀ#GoҀ#GoҀ#GoҀ#GoҀ#GoҀ#o~/?ߥH#7@%_"4( c7QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|JnO|mۿE}?WR٫WA@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s5ӿk բ+((OǺ4Z冗g&+p WVw <9g]ͶOv5r=Q 8nFcclrq5g`NER Q[$BI.+urO+XlD,QEdQ@Q@Q@s;y_]覮KͬWW5kxBBt* '>+kZNC /%$i?Jghc 8#!GZJG%$i?>X?tO@F]8=T)_ִ5I&m5BxrI<9nz? G?OGJCRcV_Ӈöf4q 6A!Iʞ=XJmGQ"$G3 b c;<` G?OGJM)!8Cy˹.5bv yg Fڸ``1Vl|!0kaS"FDFv}ixW z?('4I{ְ!{CPM#Fɍ 2'XIsmxgHlnIiIV[8Ab[yl t4QEQ!EPEPEPEPEP\޶%EoQ_l{eȤ$ezJuz֥RH|טC-vU Z`HWnц e-}@n( ZjІ$01'9B]^O[ޯ5+{sm}%v].|I0)(((((.+}2d(.E'#* 74i2m൫(hHv-bNs1fuz֥RH|טC-vU Z`HWnц k2x|9[[k.f+7$Jjn YY%78T /,)ee9nQ#ԼsizHKH,g杞iaPAp)W+921ƺB8v/֥_IW ɑEkG+p}9*7~,Ai8VvmK{hc8*p+;7vp,dI:L))#p%@ zY+[\;}Vm$p6nx ڟ6[ͨG2Dfhe #%)=|%[8Ăyr@E%P)IVF8a fCa3U|qUYw&_&/X^{M:8Ryg 䴃$N3ZqzTjVx̒9wƸ,#TggQ"%UURr@s׽f뛋۩n5%eL%l 9 )@BF^%΍5&[vSNT;f  9 Ty@Zʲg$`rx =XZ4 S`6#!Cݎ*+O^i3Ar_&-!(%DN34x,zޕ`A}B &Bn~KbmM6f zcg5ol2)`Ĝ9'4e%ii͍Jg<Us 4wv&W%{XGD%K;q}5>e5cdPCP@ r2zGgndI}B/#3[\:6^I㢏Wwx ; _[5t7@w?Կj(((((((((((((oH\K{4w"ȤnV_5Ep>-Uv?fɪ߻3]vp>-Uv?fɪ߻3]Y`MW񚩪YxH/u;k$V%8O\ j~!پcm#h=B/#0?d0+#M3"dgZ~jvYc5D!") ;޺ɪ߻3Ro|E5XK, #iq&EWmIYwG 5_cho 5N.ſ2j5_ck L;tnok sop96+/~? \ \/~(/~? \ \/~(/~? \ \/~(/~? \1隈It4*zbVPEPEPU:O.'gNPKN=*s;y_]覠I}oWjMtE{ƫV˜S+]'}^?5[Z(S+]'}^?5[Z(S+]'}^gu?vzܗWZ}0%1`dֺ(S+]'}^?5[Z(S+]'}^?5[Z(S+]'}^?5[Z(S+]'}^?5[Z(S+]'}^?5[Z(R;Z7K5#͜ HH%+Jak%QLG?'%Uj+/ĺlυu}.ݣYl$(gBqj%;y4GNacv@@(r(<RA\FѬt}Fh,㷍,U(' gPYEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%PAEsct?MT}M7%P'%UjԮ?Ɩ*_5HrdHQ| nHqk+2(((((((/֥_I]mr^K,#+R>6^I㢏Wwx ; _[5t7@w?Կj(((((.6]_TX{+)#Y*YdzԢ >& >& >& >& >& >& nГ<j[ /PyO vg1Oɩ)mF{QDrL۱`0TץJg;}OVmWK)̵R%vg|xWEE7pJERJak_*ׅop)*9[{ PȡԌA8IEs7O*m2[߃6q4Ygc$Ï29 m (F? dӧٟcՏ1Ѕmc0eR@`!6]i?o1|k3@,GzonS:ӓz \ʷe.m "nۺ#vSw$z._D?{{hKNsv'4Ơgdۏ9y{c-mDc8yIxO(Gvqr]ӿ\-;5ځyh vs8Eg( Bp k,/D?{{k&[^0RHl.̱^j(YdHna]q(D}w -(LŠ((( k>vg6 p Obox$1S^LdM'ӱQOi_6ğҿm'#}E.D('4IQOi_6뾢D('4IQOi_6뾢D('4IQOi_6뾢D('4IQOi_6뾢D('4IQOi_6뾢D('4IQOi_6뾢D('4IROkj^t\wE?yrC&&X5?ZEz׺z"H`VcMB8bbdxW z6\ '[H\;Hb8+xn⸷9$l]H #ԕI%l(b>@%{]'>6^I㢀>o+lW?WR٫((ֵFX4:kyoE\ $Ga?^-i(oKſ?s/"h9is :Jw$Ŀ MQixZ'eZGujzO>ۭ%mЮ}g2(AfvW7h9hoOȴs :J+oOȴixZ'eZ9Y%ixZ'eZMi n&dRH Cd,>dgWE67YcYXu1殞!IҴ"%x?<(u/ D̿J(oKſ?s/"h9is :Je ;o [ Gh9k?Y-c"wmsZVϓ2Kfvc=zQ̂(oKſ?s/"h9hAftW7h9hoOȴs :J+oOȵ'-}7\Ӭli9RSAOCN酙QEs u/-  ; _[5tQ\T:TzψH=pĐj0"'m#W;"Zwk=.iΦӿ\-;5\9Y\"Zwk=%;G:Vu5Iu%;Q"[{ I+ .#Q΃m%;G"Zwk=sgSEri?o9Y\"Zwk=%;G:Vu4WoMC\yõ6*pe7_ڴW&_)((((+*E5tIu*ַ;G!gk<㵽Es?LovCy{kz.!gk<ַ;[Qp0?[3~^ޢ~?Z/\ Cy{h?Lov`ַ;G!gk<㵽E3~^??[(?~?Z/oQE4[tv.Mܷ :p38u5%Bo5VQE1Q@Q@Q@"?ng$_ Zvr|RpX8pXo^i?'lGVlG~14̤AEG[qo,sA*H2AG9sD)[U5 whi79C ܜ,'bmW3O∵$zvrRA,,Z[y/T)cJ屹{ڛ*y<{R^>DĿG[@M5b˹4~O /섳|v=󎵛?m oѮ&c(AR @aA$j[@M5ko4OKެ gX6]_DUJJLrcKk1ʿiW|;I(@@aVh8_ _Fb"ZU5jkTGy :g:s~hcH=30Ky2ykc[ko4OK}ſ?q/#Qw{Ag:YxXsKVbAhKu'&_:u[\_Aut%s t$]PĂrz:_ko4OK}ſ?q/#Rolt7WW[&Y%e&S+)v`~EQNiumcs,G+2<<q8kW~-&'%j5.\$913b5+;KБך/,X/o/#ў7p76 |'9h8_ _FVEXAG@8Le29KzWQX?ko4OK}ſ?q/#PoWOZ_ _FsVWZO4/췏>[g;LvOLr㸥QElfQEQEQEQEQEQEQEQEQEQEQEcu<-äb {g\=W6ғI; ȯ4ii+Cg?ن `lmo/q:( (Wwxow:(º]s u/- ((/(/mlV>%E]{G`*FWxMYw5֝m=Hn!`26ȩϬAwX>֨FكAbG$c=Z\=R[HQ I `/ݸ+:Յ~׍f-\[&(< IwR¸>FZe\o?r?{-h-(WA\sa]KKf9C+Em[uHn`*FGV Ѵ{p4bH`uAԎ2>zGIhEr4H^`R+;quhMkVO6\1pϽ26I,#ԌZ-bB'Bsұu^݆aud^͑q66֑˳yji֚mPLJ.%lXMȢ}p8dcgA L H4f%A֭_j,fX (u9R>t*o%^[z+S\34̒fݛbHgh%} Z(aEP?w_)K+7OI`եI]O6"FQVHQEQEQEQEW?'%Uj+*E5jQEjQEQEQEQEQEQEQEQEqP,?uw%Bo5VQE1Q@Q@Q@Q@s'WaHںz4O‘L*;tQEbXQEQEQEQEQEQEQEWOZ쫍GZ'eT7;(3 (8χWWC\y_36H3U/O7%T~襭Ǚr?Ty~0ևy**1|kCg伎A溉JG;^B]L N<%CN?8 :' "z@`q/tOEW?_zmkNu&v$`e!I8䀹 Q;uIXIX*\4ozccV^_ڮ-8Fq DŽi?aRG%WU[Gc Ӣ/*N<%CNޢf'D_UxKUE`q/tOEQ DŽi?a[P'D_UxKUE`q/tOEW9kF-'+T3 .m'z q(D}}EVg?WR٫WA@-^'Rvse`c*ch8_ _F,k2dA+ʄ@H#_ _F~-&4aԺYA#4_ _F~-& 7x:mevU$)vϨ+7OI`եI]O6"FQVHQEQEQEQEV !ILHK3I'VǷDb`pAѯ2CMyƫGF1,NƛϿ>QȊg= Q WCϿ>G&>9yj&TȾ zd/r(fBSH 2UA"G&>>D<5_^?<5_^4hӿo39O4?MWןjO4?MWןjM;}?4hA{U5U5N}ƏM;}9s3a Miض-!-%.X"8=kz=R,H`w~5z+QE0 ( ( ( ( 4O‘th_#+jTw6袊İ(((((((79hіWOZn)lwQElfQEr襭>oMF1np = L4G{ƫM+ 3}u?0?[`7L4G{ƨL]owjE`aϮ;5G&g#TXxKQ{ç]Ѣ3*8in]?Ku5Ԯcy =´lT9߼L]owjL4G{ƪOĴMo 㿼]X\Zhy7 h.};Z+ akhV$r+LpAPXIFp_ >iQv$8'SJbt;y٦_R>nw ,o P;N9L]owjL4G{ƨm7L4G{ƨL]owj7L4G{ƨL]owjE`aϮ;5G&g#TX |Q#`kZiW9kڷE|VyV3[2[twtq+QKch(ow:(%{]'WA\sa]KKf ( (9KJ`iCjk4Frm6ono 7mw95/(/mlVRJ+ծugֶo#ӆfpKzꪅjs B=Hzd)َIdgI+xiJ,{ #ߒ.3\j%ENɎfcuA!#~b s9 ;x#n]ӍTg΁DES?s^j]oa}i$EoJHvYCG< 6I,w4ݹ*}~[:5ۢDiUV26K`׿0t Y Hڥ,G&]*K 7vzUEDT@T`T[RNחkGZ'evUG-2֜7;(39º]s u/- `[]h TQGQmRMޛ=jvSwOZ%% -Kk0$J̞V2Pq=me*Os?$9gJ{һj*y\5il$5 ij36S8O覭/ :O?'SV'u?V؉QEY!EcLng'ukM klTp@T?5oeHi\cV[6_G&C~l=bj9kտr͗#΃=bj9kտr͗#΃=bj9kտr͗#΃=bj9kտr͗#΃=bj9kտr͗#΃QixK5]V&%K|@r|?]NII&qX⾈$Q `@ؚZz~7lu3 ]~"Bg=$#hNG:jt'R-lu Fe5K,BaFC#<5uy>%$;d*cr'-dl2Wey[Iiu'R߼ vX\s9.O_[zueI4/8<:Պ[@-2-^-dgIEs^-/ D̿G2 3/ D̿G_EQ\_EKſ?s/"̂(oKſ?s/"h9hAftW7h9hoOȴs :J+oOȴixZ'eZ9Y%ixZ'eZ?[@-2-,Β[@-2-^-dgIEs^-/ D̿G2 3cD)[S[@-2-`.xVMk@["fXמnTr:¬GG'se¬GG* /Jtt{0:+Y_.U^auW'  ҿ]<7@+Ø(Og:?VxozW ه1WOZg:eIf3X[Z˰hWʚs* (9_?Rr{h a S\ *jK)^[lV֏-U1tPTr݇'*>ViL[(c9Vc72Z0C$3Ac!y<1 IE]uK{S-Q̸'q1 dMNF^OuzxHбU2 Or:#;+ cP_&?,Ds3.XFIw#u9mUyv1u=z=xPot*IflBev #|3ȌBrpKm78'͔u$-yPJK*09GQ\& WRV?,,H C$s=NΘǣtKd.5 VdA7øQ_ V^ 4ѭXDD^H\vy{+Zk"I"w kwb ;Km*mv)M7duW.o@%{]'>6^I㢀>o+lW?WR٫((oR.FVU׼9xbԴ;>S۬<=+Y_.)X(Og:?VxozW uW'  ҿ]<7@+Ø(Og:?VxozW ه1Q\* /Jtt¬GGcS7aFKOU^k_%\Yv #.FpHf'#urO*G^EQgv c8*9 ѷuXonu6tZXb dC#)lA̍Ў++ſ2jrnY}`MW?|[C&~.FCſ2j5_cha̎?|[C&~>-Uv?fFCſ2j5_cha̎>FZ|[C&~1<+{,0 BBrvsN0i(9º]s u/- m|G'UiS6w7I AAև'D_Uo__.a=#vCJFo'D_UxKUݟG#v.@38 :' "_40/F ?C(c7 Ӣ/*N<%CNVqh\nEIHdDA󟳺`[VU t2n1T )|ixYKi~۰#Nn2dҍ+[ EVӇnn|.1S_^x6VP\OiVkwf>`ɶp9%ĥ{y_j(Š(( J? Euh?Pщag.-"*nGW?`=_YH6 wLv$c^-:f]>r{}j&vUKcU#zwEYXm(b ( ( ( ( ( (9_?R`IkG5W *"FԔPEPQp.]>IEQEQEQEQEGVј]YNKIܚ((79hіWOZn)lwQElfQE|JnO|mۿE}?WR٫WA@Q@Q@Q@Q@Q@Q@WWڗKC\ 5,p%䞔ҹW-?oi򳩢oD?{{hKNsvth[-;5?ӿ\+:+KNsvD?{{hAΦӿ\-;59roD?{{hKNsvth[-;5?ӿ\+:+KNsvD?{{hAΦӿ\-;59rσsa]KKf(|[!H&bBS[PY%XOrI?ӿ\+:+KNsvD?{{hAΦӿ\-;59r o xMK"2y\\ek#GwI\MX>7L6t,,R8erd6:LHU5/;YtgQ:1Ƀ(&F2 1Y hhvY,"RUV/B;Euo4MIkj1\Tq)(p5(Q@Q@Q@Q@xW_)$ 䊵'%UjԬ\LH5?eAП/H+2H5?eAП/H( OoYEj'+zS?[_G$ ޢ0 O$Q B~" H5?eAП/H( OoYEq iH"fB>SӂG55_ eGV&GE\-=*:fԣu#bc8<?? GV3ب3 Ӣ/*N<%CNޢ,_40 Ӣ/* N<%CN?8 :' "z_40 Ӣ/* N<%CN?8 :' "z_40 Ӣ/* N<%CN?8 :' "z_40 Ӣ/* N<%CN?8 :' "z_40 Ӣ/* N<%CNMr i5h$8 »j4( pܙlwtQEj@QEQEu+;KU7=nɀoA%B_ "ºM[z-A%B? ' jψG+Ky%AVr`;Tg$`Nl^ROkqu%ūH[i"H '#@$ _A%B? ' j1=(3V8]L#Fтe%LV*,/v]n@X 5@I?V?_G [D|_MS?Wشm-<2ڬ{lqiYNW="($(ow:(%{]'WA\sa]KKf ( ( ( ( ( ( |$ K]Ur襬z( ( ( ( (0iE-hbܨW ۗ,2G'qxGA1H.3' Cb=G^mMypꭻ<`K'9\-i{k[U*=2Xc8|+oqso>E=oZG̼QNѵ=zK-O$rTH*q}+xaP$i|5C ( ( ( (8>FZ}\?r?{-k9ºo]s2t- Q@Q@Q@Q@Q@xW_)u5K.Tn"Xe!iMY𮯥۴k=񴄅 TN2} sxİ$M%jc ׵Dvլ:umndI()>I#5K\{IFKȆ?M, u+ğҿm'# G?OG}0koO\buX ;"

ki!Vuh:Vn :} 2Wty8OJWCs־02h+ưyh \K<3@U@=s[&i+mC#wxK0999Tji w-I;um Vs^0M=\̑JN\g*&OS2U&="&2IHkg9ﶵ4i){tc|Y%1xF9a' RRC/>l'{e-͐s#93Q7ilNYE9d C~az[_%mvEcI.c< 䄢RT \R-6IZ"#`? PZڥlYHIc@*zDh?PuMH# 9GW3qKZQ ]E<\m zV<[AMdoO4-6^NRr$Ũ²C"vO>1X!5N$K{/坷*!}` cw& 'h'7_I CR mɘ۔e%y; 9߆:UOơ=K<[sm *A' 7b> 'h'7_Iw ǂ']fƕ7G$B0rcjH (Gsް 'h'7_I{\%_"~ 'k‰w5ſ F7&$?pc YPR*( (uoźkC*HKWR2",Gz]'}^']Zެ25[]'}^բ8r_?Q >jG8r_?Q >jG8r_?Q >jG8r_?Q >jG8r_?Q >jG8r_?Q >jG8r_?Q >jG8r_?Q >jG8r_?V}i|CyZUXmiyv8Q]-q(D}Sl($(ow:(%{]'WA\sa]KKf ( ( ( ( ( ( ]x|-mo&*3/%EN=Mz-r襨Q'%j>h8z$o e< ̳~-&'%j7M97)'#TDĿG[@M5m3UI uKd.=0}~-&'%j2Ƴ,FD,O$ d2?1O '%j>h8zPc[@M5ko4OKն%Љʪ?0 =iDĿG[@M5oS"9Iadet9 B(~-&'%jVW#5%GDfp%N8 4DĿG[@M5nR7 d 4̱Jಡ<1b}ſ?q/#QDĿ["GN3dEt`e9z_ _F~-&[@M5ko4OKսEy6/[\i_o}߼wD뜞kGZ'ewմv3?/WMk_AT (((((/:.o-f8ĥ"V zV_E@SNkVRh^-/ D̿[TsQh9hoOȵE9Qh9hoOȵE9Qh9hoOȵE9Qh9hoOȵE9Qh9hoOȵE9Qh9k'᷈eP垹 $r$1U vu_ eGUM%chButx55ԓRF4ATCz+Р}s~lQɍU1A&4fm/ i__ ?>g>߱SȇCiVO?G&Эv~YϤQ ?9s3_ +kݧ}} bD?6mom/ iA~`H?أ39M[[?KB}g>߱G,(ACiVO?G&Эv~YϤQ ?9s3_ +kݧ}} bD?6mom/ iA~`H?أ39M[[?KB}g>߱G,(ACiVO?G&Эv~YϤQ ?9s3_o ϊ;짳[6(hG}n}} b8xFP)&MEU((% ?Y5nͽvԢy08nqI >-Uv?fpw-I}`MW?|[C&~.FCſ2j5_cha̎?|[C&~>-Uv?fFCſ2j5_cha̎ J? E>-Uv?fs×^5R\fy[yK5Q"QLM(B(WO7`_X>襭fEPEPEP ڶ;_jF)9r~߅<sFDV3_j]=ۿHmk g-&F-F}6[F"xAb2:4fcXom˛d:9>մu9ԚH ^+!YLr59##sɠi677 ukp%Hz-2R3]Ҵl-L_8rd۷vq*4;$-4m:81C'ڜZ[O#;=ގbx9FA](lx]uvw' eILǸ?/Z#Jӗl-F x'׽vi4e,;hV5-dgϵ-tQE ( ( |Q#`k]q(D}}EVaEPkG+hCw-mbH+ ~W+']Z΢.=voiqw%ԑS#j푟ZQ7mͰ8iceSq* 8c/.pDr5`9{U{}+W&MV2Ћ6!vBQqaF+"]4oϦjkkeY{$sn#o_ۆOӵ; ^H&mdC6m ~g<֊j%% -Kk0$J̞V2Pq=me*Os?$9gJ{һj)s1+m.,rvx+V"2BZk"#e N37rSH3^ Eo%c7ͻ #:`dkK-u 'Dݔ,_54S\^_%y"h9UڅcyA9}Ar #V{kin&P[Z zLc;QK|Y2i5h^eY<*d;2`tn=ao5Ri.ɯw>W*;AE/C-/4EnngiD`$`rᕋcc75mJOT\'ӳa9{s[TQ}nh^M{kgQ=̅!]T62x+[@ 4[rF4c2ĠJ֕^J);)QEq(D}>FZ}[Cc9ns2t- e ;o [ tB ( ( ( ( (9 i[ j7' qq*C ZI#TQy $w8 :' "9z_40 Ӣ/*F DŽi?aG'D_Uz_Y7fqdb)o0lEbO cO ֻKyRhe$l]HG޴2=v( l9hрM R m998?7tM+ X2ʁo`?bsGW3~^??[xM+Cq#G$E?pXqWܳm!@#5o!gk<ַ;[RCy{kմ{m'ZO٥7O1-yzxA^\o?r?{-j;(3 (>@%{]'>6^I㢀>o+lW?WR٫((((((_?RU\m/ \\D*.JFNMEr<7@+  ҿ]O+(Og:?VxozW ه1Q\* /Jtt¬GGc?U^Y_.fYEr<7@+  ҿ]9VxozW g:=se¬GG* /Jtt{0:+Y_.U^auW'  ҿ]<7@+Ø(:}IYt1u{KP   ҿ]9VxozW g:=se¬GG* /Jtt{0!GZ'ewY|=tٌV/##򮾭++_A\ma]7Ka((((({ƿšwZ_AaM;Kak,[LeDfrcu99㷭g=ZE'd٭E5+(Hc#^}S*R];]u2m720voemp:!8l  oc&42 S0>'5\OReEpJ rڇu M&&o.=$e|24HFߏ7}YЬEӠvG*904I3sa4R衵z-c66R|8F2Cuj[-߇ :+\1q4gBpO WUuiWMeq*Mn$eRHO@*;w6qkTu` (# 0OAF SZC<ddc?3vCXj<޳Gm#2nlO86oM:fvC1h#·''֚^aj_0B8\qM:[6OqYG#ha/+<5G0$qVosuVCd3[DҬr NM73wt0EIR "%UEfڊ v`S*c񞄎_AY_hQ@Q@rA x PUreEEPEP-/]WQɫv% ?Y5nsUv8'8|MM*{=@h".~*\c 7\RE,ku AJ_Uu#ibXdn_P{h[zUGg+:wxzLxr<p |==G{/ i2)f5)7y Otm{52//O޶1o Zd 1TF!;;iVϞ X$o*|ѱݖ<`}Zk="/wu!v"qXgT 1f4αugG-ԆK&@D*Luy 9!ktny%aȐȇ Kp:tZzY\o|GmX$[Z&U!I]rt5i5Xot{i P3†##N@h?P6^I㢏Wwx ; _[5t7@w?Կj(((((((ס* |$ KS)Xi\ ߃kcG'~C?UjQS 2; z a51J(ew?Q ߃kcZQ51ס*() ߃kcG'~C?UjQGS/ס*NC^Ԣh_'~C?U?0UE9LNC^?; z aR=r?0Tw?V{@8 ¶=O]':{kO-Щ[?0UE9LNC^?; z aR=r?0Tw?V{@+%u6uBJrHRN2@ϸJ/Uxkc???F fa b>ZGwgkc???F 9o'D_UxKUݟG#v.@38 :' "_40/F ?C(c7 Ӣ/*N<%CNnzO}???Ž@38 :' "⫌U<7Z.-IWeu AGz_F >:|*MOQP }=* }QT AmS=?[3~^ڹJeOx "_`9}A*{?~?Z/+뗊 PK!Fm{pWʨ3Ul V->:'B.T_j^Cr [n8Qz{fqZ?[3~^ڲ͵el.7\rF9cFr/Cy{h?LovnW62*Y??,m NѦ0gs8.tڹ J? ET7&[QZQEQEyE%Xj\U6G>$?&XNZ'7_I.+dջX7'7_I-މy&q?doO4}ſ?O/$\ xz' > 'kz.<[AMdoO5E-މy&(q\[j|BbC01G]rA nL;( (|$ K[i1,-4{;6;xmbYUTo羗y"e#7?K<GH̎Š}/Eo羗y"F(?O?$Q{'(a̎Š}/Eo羗y"F(?O?$Q{'(a̎Š}/Eo羗y"F(?O?$Q{'(a̎Š}/Eo羗y"F(?O?$Q{'(a̎¸>FZӼ}/EdN!񦘺fuZ ̶ݝ>1Ŧ&GEEPkG+Xĥ؏ 7?0G皥=.4{k H.h׵Eߛ/GN~_xlt#;ɅM[-o=ؚZzӼ̱F]c ٘*$ MBK/e2JKmP2NI(adfbj9kտr͗#՝W^TkטMgQɭC"]6@''D_UxKTF+a0GTQfm>eh̃aW-Ǔ =G݉/n<J DŽi?aG'D_U{Q>ҭ1t*@ V5qG-кIenҸ$!M*~R B*'D_UxKTF+cOLk:(H2N ,lV Ӣ/*N<%CNƬ4(  DŽi?aXԼi]]Aum&6Mѱg8aUCqKcШԀ((ʹ^NuxS?sZB/&-3<ہ{ſ2j߃sa]KKfſ2j5_ck 8Oɪ߻3Gdݏ,?|[C&~>-Uv?f( o 4`MW.ſ2jտxrKfK9qo)cF1Qһ (Š(EPEro&6U[ϧ3`RK ucMZ 䪗$ftW3Z <kC6^I㢏Wwx ; _[5t7@w?Կj(((((((:vM}DV >nqZ@c_n}IEO,YtbOZΣ*;S[6_G#zۆd9mȡdTWiaa=܈deLn dƣRS[6_G#z km6mY\<2یۜ摵k$w1bp xTѨ]8kS[6_VGFMVѼ:*8JZ@_ClqGʇއ~$ ԥC~lG?5eΗ>f\(xΠʧQ{.g@˲F jFG#z?p͗#3O o=8kz.#z?p͗#\ G?5e?ߛ/G(?8kS[6_[Qp0p͗#C~l`?ߛ/GO o=oQE[ObQߧmRB^$Ɲs=1}6>FZ}[Gc9ns2t- e ;o [ tB ( ( ( ( (9_Ÿ#zĶ9$]U*ג:zG$ {+ OoYEj'*,QoZtڿ=6ݑfi qϥr moVkveV.vRrAO< OoYEj')8hjc|d쉵m)w$ yWlpK ЗZzźAG5ݽBI/qQd@ /J O$Q B~"z_<& ]4j{ZM%/L`8##9=x}^uH")u+̰NT$ֶ O$Q B~"m[:[|bo`y2lrcL~Z'%ۀ~^Mn7EӴmc!G#rOIS?[_G$ .o:ޢ O$Q B~"7̾/ OoYEq iH"fB>SӂG5 QEh@W'i<֑vjVW?x±XnzO}???¶( F ?C+bnzO}???¶( F ?C+bnzO}???¶( F ?C+bnzO}???¶( F ?C+bnzO}???¶( F ?C+bnzO}tKkK7#EQEQEQEp^ +`.gj +R0,䞾V&C~l/]WQɫvrw4IXտr͗#ߛ/G+m2K|xOr*xJ[mbٔqyd G3,&C~lM[-o=mkBKX%S!IKc y fغ2W^Z2JnȪ21{s$ Pe%eA*sBW _3(;lzް&m.&2C*cDWo])QEQEWOZ쫍GZ'eT7;(3 (>@%{]'>6^I㢀>o+lW?WR٫(((((( Zj |=k{m%@>Rf:|$ KYبzngXgcp@='Ԭ]Y3і+ޭQPn줒VG3s?.-7ӣ%F0Fl:G<2s}b/$X.$q=/n+'ǡ˯Vd:;ɗ`$ی*c 'xn]?H%xLe] otR{ʯn.w_aw1cjצ|;6t϶s{֭"8U%_BpGM{fA0zzRʯp)QEQEQEQEQEQEq(D}>FZ}[Cc9ns2t- e ;o [ tB ( ( ( ( (9_Ÿ#z?)aW;MJ[U+5c۳z"tk)RvFW7Ap5?Q жAx(RS~r*ŷesKi_oWwi+ 瘁#")Hwuր73ɨq-##"F//9v+ivWhwWe#:n,R&_1oR =Emks$w~P]3%Gm?۰SrZUܹF~9z?E%y|Qc%_ eGUrdzQZsC乓JH$d6omfw{SwJ ' h_+h/uo.CLm!K+ʹm*F7.O! ڟ%%R㑡|ZTH# v@Olk%f7 Э/&A%B U[7SC/$c Fz՞_+h/ Э/&p0 ' h_+h/(?Km4/O\ A%Bӭ-4z=6-,V$j͹ >»j7xX뚸nL;( ((% ?Y5nb|7^F#Lx295 B~"kSEV/~nne "lA[؟8 eF@3tq B~"H5?e ͤ0DEs %0w\A;5TZè-\:B!1uPN$ ? O$S_!Y_-X?WCWS47AFXqI$Dl7llcL{ ~|S B~"H5?e~97Iߧ숻]`C?+ W$ ? O$Soq$\|T& sZ_j'+{߉\iZ{;\Ly09\xN[EVQ@-gP\Cm\.>l9hр6^I㢏Wwx ; _[5t7@w?Կj(((((((WO7`_Wi'֑aɣVUdPP g*&بQOi_6ğҿm'#r2Q\o%$i?JKcN= Q WCϿ>G&>.D>fsh~ ?h~ ?t?ؚw4biȃ'k5G'k5]&>ؚw4r g= S[2&YNT ~?M;}?4hA{U5o(Cbp+ӿoN}ƎD<5_^?<5_^4hӿo39O4?MWןj_fu ֿ{4&X2x9r8 ȯKӿo_֑isNbE5 ҚBnUEU+?? GV k/Tf[Cdyط <3بuV<[AMdoO5fOoD[3}7m3R}ſ?O/$OoDS 'h'7_I}@Oh44&,E$BEX?doO4}ſ?O/$zE`ſ?O/$OoD\ckmt8m/ < .Lg.HtwbGF0;]}{YXn¬PB}F'ooQCj A^$ux"m8M%ɋRT Ȯ+:|^Žtim v>bJgQo*L]\ ʜ | QEAQEWOZ쫍GZ'eT7;(3 (>@%{]'>6^I㢀>o+lW?WR٫_5l1C tojulnS 髖.+d2vCJ?'k(Q;\_ZTsQ >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9Q >Bvl"IeE9QOOϑoesnfZGc6y:Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k)~+Yx"KX$'{rȬW\o?r?{-im/WMk_AZQEQEQEQEQEr!a-Vay3%d In?oK@m+bw.+C -;5?ӿ\*nʲ0ӿ\-;5ݢ0ӿ\-;5ڗV5M>-.PIQ-LCjPę񎙩ئ.T4v7W,' w>REadi?oIxGu9M鰴6̊x?\U NUm71A*GT{KNsvD?{{kv.da%;G"Zwk=㵻Eada%;\nGdG+ Is,IJ/?2K*2GE\Ɖ!R?"}Ck-SQGF?14O%cKY/I|){dz\^bMH-%L, BU{S8%NˢimBm eF$3`t9c؜Qd>g/k=5XRV8Fj ~} -k, g@U8>`餍pI I^U$c Wj$M떕 @IAz2#,.in\< }-+"I s'\G_jXrOĦ ᣍ۰ Yk妇OyݛYE9[{)ڤ ( vp]<аIRIPr=~u ]Mح`hV(JŪYaԱclԚׅ5e{s,J8Q0HI! 6)K; +[ƍ#*vn#+1j-y|RcH ۃ.$e{\|彎[] 9QyF=A4nCm%7HbDe miqenv>Kݙ.~' ezH9o:*((>_*ׅ¸M^:[QZQ@Q@/ 5=E~R1ZIuV^nzO}???¨~0ևy*/O7%Q΃C(nzO}>_?5oJ?TseF ?C* h'aAU9Y{???F Z <kCFZCqKc+c0(Wwxow:(º]s u/- +.+d-/]WQɪ'Qݢ+"Š((((((/zV:՞-g孼ۅ۲3*J!wws_%G%mΡIpau$Ă~c$28b&.T {SM=-K8&8OIZavw|1D< 綷fG6wbpn~iUqs\#m4[iZ9,mD=<[z8Vr0Z={]oM{g}bl/.ɡW-XʤK848#МZ/k=Z}OHZ,UOʸry=Z[LlA&H_a/^n5"[㻞J" fzU42HQDff$31,ĒNI&(@QEQEQEQEQEG-2ֻ*|Q#`kU -ºo]s2t- (((((/(/mlW795qkks7mdh/ie9y㙾h8)\v7'%j>h8,QE`ſ?q/#QDĿEE^nik;L=MSE5Z;AK;[Y!Z7 ͷiŒ8|c+[~-&'%jiks"sisjЋ9}LŤ-h8 6z9=J4I<C"I^so]m.ms!;K!Ŵfr< t:?p{_{-bn&|dI{oRK׶W:<L`sOּ1k:mnHMo4I&d$`C K`]mK:-oM{?-f]êr0xjԮB}wR&ks玠کEM愿@z6z4\4G*lc`YN w;Tx+MYἷ侸#a+y . 8#<W%/ԿP+K_Urdw4QEj@QEu϶]iZk7ɥINb2p+/g:(g:?VxozW VxozW g:(g:?VxozW VxozW g:(g:?VxozW VxozW g:(g:?VxozW VxozW g:(g:?VxozW VxozW ^:bᴴsoj-_ɯH~J`i@Q@Q@ |mۿEk@O7@w?Կj+o+lP^ wR45]C|ؼ <? 5:[h8j"8?& ?@*'Q [D|_MKm5sE"֭xk #HzӥhK '!^+6ܨ[Km4/O"uxMٲ0Hw X=ʽsnO[H,Q"FH=iًoX?V?_G [D|_MG7"aS0NwFG9넲񕎣6Vy>H ܲ?ϵ{;/O?V?_T'-. i-ĥOp퓕&3@|_HQ{C> 2DѲxR1OYd/ [D|_MKm5NJ3\ZCi%ݴ"Ļ`M|n,hÿ ' h_+h/1dldϘF۳l,Vz7q+s uW`ij42 ' h_+h/TSHt~";d#`L&VC1CA!]y6pq\ Э/&A%B1,`/DYxTGF*K|<:Sq+~3֓WiKm4/OU2 V3C4 J$ :sgn2q'Y%ŴҮ%IU .8n22)۰sGA%B? ' j+oi7w6;B8)$NdErEm5 UC9$wߝBbĿKm4/Oֵ_w+2!*5߼*$xKR ' h_+h/O+!]ᡂH ȼy88{İyLydyQ-6'қMh.h [D|_Msh27O>noĖ3igzw0:儒<> ?\׊?r?{-iڃi{_A\ma]7Ka (((((/(/mlV>%E]jCV/ŷ{񟽜e/ }dVROp'_WVՓS]Y $ݧNYY '1jRJU2FA FYYW$ űGVamm Q9xmMB.xb].#a-Yا pWr9cd ?RԊtr2Du T9>+[ +vų#Y#/O=ǭkmْI+ұSNvQ|,v9┑+z%s>KfX^Nܤw-Ąsa:jRV] (Š(( /?2K*2=f(H =!V?"bO/@Ե:le{pm>dszcZs}GsE?'k)]s}GsE?'k( 3IeOН/H,Ύ?'N?$Q >Bvl"::+Q;\_G$ .s}GsE?'k( 3IeOН/H,Ύ?'N?$Q >Bvl"::+Q;\_G$ .s}GsE?'k( 3Ie>hJNӯ4˸#_.&ES1AEЬnQE((h5F5]C& *d9SױZiRKA_jݬc 3}u?0?[) L]owjL4G{ƫzL]owjL4G{ƫzL]owjL4G{ƫzL]owj>̷ }: %6FyV ޻ZY%idw4QEh@QEICap-Τ𳱆9r`p%cOoOȴC+Em[uLcKſ?s/"h9kbv>TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ>,liw{k$z瞘笮79hіQlM$( (>@%{]'>6^I㢀>o+lW?WR٫o "ºM]MrEu,{͸Hdh' 02NONȳ/ hj }ё- Bn?.1a$mkY)ʄb@#i[S,{cgfF$QynKe9lGJC<ۈX޸WGC|_r8  jErDZҝbUh8Aa :+a =OYGޤ,&$8\(U(Uأa 818o{4{w,$2,qƪ$ ˕*+S2-ȞY (CJtM9XQc 7ݐr (TbZ-ǘ$i?y|z1рzbvô/˟/bszU(^Qp ܬgn_ "2H| ؓEaʎs[ 쁄a;Üuѷ/5ē'3$p\h\)~Ee{2tu+3۝[-WgF14E7"/|ۏVRsw[Fh.Hscn ~Gb+jM>f2O $gVU{?gvܓyqݍwo3R47"*'Uœ kV.dӦP6OXXb͖FI8(ӅNeݿR7mݎ3}kNW*R]$k #Z7 F^{v>f$K6Rq$YS pr8 )݅gain`Y۷wj8<;#"C)Rc Iq#.;TсJբc,wNc>Vd |7]jRZh6NL+渒V-Iv$ (Uحmc~5*rwr}S>.A!w 0ql7yo JP #h')վ6|Mm|mG2j,'կJwGQ\o h6ג~ƁĬ@yizl.-n~τa4[Dsda[va̭4z%&vj6pi_*ׅ¸M^:[QZQ@Q@ua]GK&4hz֛=zIlGr/&<0>UU^7 uW'  ҿ]<7@+uW'  ҿ]<7@+Ø(Og:?VxozW ه1Q\* /Jtt¬GGcK_R¬G[-%8 0#UuUMڢ* (9C+Em[uCO}OZӬmJ7Xn6+;aϱN<%CNKSEE`q/tOEQ DŽi?aSaV'D_UxKTX + Ӣ/*N<%CN,`xk@%{]'>6^I㢀>o+lW?WR٫o "ºM]Mrx>KJa4I%$jd4o\* /Jtt¬GS:+Y_.U^auW'  ҿ]<7@+Ø(Og:?VxozW ه1Q\* /Jtt¬GGc?U^Y_.fYEr<7@+  ҿ]9VxozW g:=se¬GG* /Jtt{0:+Y_.U^auW'  ҿ]<7@+Ø(Og:?VxozW ه1Q\* /Jtt¬GGc?U^Y_.fYEr<7@+  ҿ]97,%MQN׺q[?<7@+Ø(Og:?VxozW ه1Q\* /Jtt¬GGc79hі7* /Jtuffax,5 ;ºo]s2t- B(((KMxWW-6k@JD,+*E5CZ <kC<[TV<ӕ_?5oJ?UE9QZ <kC<[TQÕ_?5oJ?UlzHՆuK!tBy7}=:Ut6FTE:haʌ/O7%QZ ՆH'V$CЊi*1|kCSaʌ_/O7%QzRU滴ֶlY܃#a[Uh?PU=sQ_.=Rk8gtǴpA'x^=zMN֊nIs\AIy/0&TA|ݺ~T׮PyL+U S<0'o8'9_>u{-]xʀi&[yf#mZ8XUtkײ_yq鱋c0I%Y)͑iYޢt=N?m1v q3 syvL(C ( |Q#`k]q(D}}EVaEPkG+˻kҶ|]0Ҵ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;TcixZ'eZ?[@-2-lQG;Ts׾%7:\$YV=M#u0O+,M=W t:'F"Jma]7Kaºo]P(((iQ`7L4G{ƨL]owjYʗÿLYCx5:?+oo#n2͊:GL3 JvH8BL]owjL4G{ƪs#I"ŇMhe-@Y<ьJ #dz%ʩu`| 68ɮ 3}u?0?[OVGyxP׷Y]Ckwcy[=6-;Ev=kђ$aT|V/&g#TaϮ;5S?+ 3}u?0?[`7L4G{ƨL]owj^eF_uiWe|I: ']D0ʰO QEh@W/0]wŬs!dǕj+?? GVبMj]GQnf .zu޵iFX*ḿ)՛JN^O=sۙz\HG*cէ: :ݗ+lD\ dIgc5tSe-ywBIq%2MW,,I"ViWo,IjwDEeE*ijcDr{yßqUm`֦czXm} zU} ķZ^*79TcV&g#TaϮ;5BZm{ޓ6I1G#j? XRZyĤ%(q#9L]owjL4G{Ʃ_o/8׈ Ɵy&{:tIg6Ҽ  r ms^"jvfV/&0Y čwc׵L]owjL4G{Ʃ;²>g5v-cy"n %$c:L]owjL4G{ƪ]݇\%_"0?[ ^EMrc}@`OpQaӸU ɖǡQEEP1Hn4O‘m2lJ KML8]rrd*FRh\lnָRaM|OJHtm>ګj#X r=~zwb+^:";{%Aj9tm.{so6g$ )S2qWh;"Tkw&d(VfK08]Et{"@fG*jѢعWbFsn3E$*HA>->V(lBv@p8VXԧ,¨Y$f`3ؓ?8 :' "_40}O{V'D_UxKU6z_40 Ӣ/*E`q/tOEQ DŽi?aEޢ8 :' "_40A,?tOЍp/Wu- ;K KP .' O»@#Zb%/WMk_AVHQEQEQEW?'%Uj+*E5jQY+6Mp*GqWvF8#H.1W=_[YCYe?1>dGq.,xF,JYvnA8?6x˥οtW'K-&KٴZq,,*ۤ%gj^"V9>y(LupNhw{Dw4W4R\ ຎ4[2pGOy$GQR))2K t8['R۝xR-\ldY 3ə^uRTt+kHl.&y`YQ:F=[~^c;MPyhu:8H4gIyCZ+׾!͸k+ChH,|4;qv }᳻mSV;|Ę;y $9ٽ59KkhԞ%.l]Bd5ݴZz- y210hgE(if_SWjW2$K$"BqЖ~A#fqUM\:| _Opc6)vd0y*"U )5hOEQC0۴@-Uv?fFCſ2j5_cha̎?|[C&~>-Uv?fFCſ2j5_cha̎?|[C&~>-Uv?fFCſ2j5_cha̎?|[C&~>-Uv?fFGZ'e?ſ2j[LNh]Imyhr: ;QEh@QE+(l/qep27eqa)+^ZD2 e`dӶ2lYyxR3OK$`(>G888n'hDPU( >T>mgwmsKɧ8R(乜@(Hnɤ Ԡc5Q+c|EO ~+jieƠ+k5P\$]a-+2 8=~;?|!cf4c>xq)E#An%nu$>Bp=9Ey*}}F p J G+ttU-^鬴[ݺy$!IF~ gn@š|-l'0Hr-ٍ9ϵ!CCЬn5wW$g*.Y ۿs!nIrOZ|ek5_]^]BxXaVK1|{ CWQFhK)+)+[M4'DVV)?wy?wyÜq.pIZur_OA]Sw]OA]Sw]>FhK)+)+[Er_OA]Sw]OA]Sw]uW%?wy?wySqwVA2E*2Qy_!] SWTUl7Q!K3<#E'dM\ca7shBx;G?R kϾ+5G&O"T\5[]'}^.CEs?Q >MtE{ƨI}oWj\&O"TkϾ+5Eh{]'}^?5[t4W= >?Qp:+I}oWjMtE{ƨ sPo_JOMtE{ƫ>Z>!e<*wڬge<];ggp;( (>@%{]'>6^I㢀>o+lW?WR٫(((((( ѷuW3=_V?6{;TQEbhTK/K#S"}6b0A =Պ( u)[]B+m6p9<4;;+kd@B1* ?JEDYCٗo!s*E42RB"_7gs\ E;*i,$K>@1T 9j$($[t7} qWpi2!mn%2"_`s]gӊȽ{[_k}#Dor%K( |kPi5f@vm% 0m='zw٠|q}*Z) iX6@ PbQ4gj7b8? 1~gSEOt.%01k1 TdV gjgC#Hv~SؒG5-\,WZUFaʈx8EDT@T`R@X((y"] x?_{ǟ+X\5-ºo]s2t- B(((|_\v] Φ7ndEÀZ6VSD?{{j\W:+KNsvD?{{is򳩢oD?{{hKNsvth[-;5?ӿ\+:+KNsvD?{{hAΦӿ\-;59roD?{{hKNsvth[-;5?ӿ\+:+KNsvD?{{hAΦºUӿ\^l Y MnAfbxbI=&thMMW]֒ɲk+2;q!\Vԣ.i%ޫwGV?u+[7\1=Io "ºMJnȨGbj9kտr͗#3*տr͗#ߛ/G(adbbj9kտr͗#s01?5oebj9kn9YؚZz?5oe>-/A򿴮Z/43 UmQ8=jնiy5Vyj9SPt`xZ|,M[-o=ؚZzׂttu 0X7nOci=+(xg@ 䃑EYbj9kտr͗#YؚZz?5oeE,OM[-o=ؚZzۢfF'&C~lM[-o=mG3 #V[6_TKQ|awjWZ-ki4OrD$PFzZ돗JajbV;( ((% ?Y5nua]GK&j ȏT_sXErsI"Nq Zy:UMNQvk[V,pdGu0W i+yv9;8'H>"OX&จ;IaMvmj h%ˉbzQ۶)xoL s+}Uݙhlf==-_Ah^8B}sV}V-w-s0#V&v]'R:0 ߻ϙ.,lghpXbߵ5[I\^Y* qbº=O5eđpX!_ rC/O_MR40B}$U>Jӭ,mGn-GAI?Y}+x PW_\%_"-(H (9C+Em[uHn`*FQEQEW u^6+5܏٢KtXwyrDˑ˴r >#Z\Csgcr{B|\AM&o_ 3Z&jo!W >Sv:6oov&Kyb&w2,(h6*IFЕ]G÷1ZQt%ʤ1qrAdAOŤMl!m\ˉǼf>0d K0S)QEQEWOZ쫍GZ'eT7;(3 (>@%{]'>6^I㢀>o+lW?WR٫((((((OUBM)t.\\ƌgPwtޠ$sRjN) Q WCϿ>G&>O"39O4?MWןjO4?MWןjM;}?4hA{U5U5N}ƏM;}9s3CMyƨCMyƫӿoN}ƎD<5_^?<5_^4hӿo39O4?MWןjO4?MWןjM;}?4hA{U5U5N}ƏM;}9s3CMyƨCMyƫӿoN}ƎD<5_^?<5_^4hӿo393BS2dE G>>0|ذ۩=obiȃ'k5G'k5]&>ؚw4r g= Q WCϿ>G&>9vcc}f>sK݉f(TOjOЍؚw5r#bvϽRVw0e ;o [ t@v)(((}z[/d i5([[kQ%7@`?`xl7E /,%L6H"'tNr^IqW$vR6'b_7&V%JovQ"y>*-CWK}m>m-!)ˈ̌r_,W$n&z$R4I,NFCЃS4TE 0)jXטQEQE%Bo5W/ G\y[c7W-/]WQɫ?}kg} >/#]WPIj&<[Ӛ8vV$ ? O$VV,ޢ O$Q B~"E`AП/H OoYEzS?[_G$ ,REORw&kY݀rl8U~$伂6(0J uX2k_ OoYEj')kjRjIc-=5.<,6ϙR29z}O—בkAE`[yYsƉRl=cZj'(S?[_M]}H{idbbVlА>Qm$ ? O$Rzޢ O$Q B~"oQX?j'(S?[_Eޢ O$Q B~"\|T& sZ_j'+{߉\iZ{;\Ly09\xW ɖǤQEEPEPGjqb#( ^Ik?ߛ/G.+dջX7S[6_G#zޢ`?ߛ/GO o=oQES[6_G#zޢC~lG?5eEO o=cR[_ky=䋨 8@"Ӏ:z]rA nL;( (4O‘m#k'Tj(F[jW돱V7PO=q??[eޢ?[3~^ڑV!gk<ַ;@V!gk<ַ;@'FΨ5Cٝ@ k11Z͹_4:]m4S?-1pIsK~?Z/ZM{9_M0ZA$ ٷzӓKӦl-^+hTe>n ?LovCy{hg@MM{[f--iH9%WROXΨ5O?6!|1oq:w!gk<ַ;@V!gk<ַ;H +3~^??[ +3~^??[ |Q#`kZ9hOK|nyW\c[t񞂪;[EVaEPkG+gI.K yYHMtE{ƫV9ÔI}oWjMtE{ƫV9ÔI}oWjMtE{ƫV9ÔxL5aoM2֟qob \?r?{-k-Yma]7Kaºo]1Q@Q@Q@; 5Or5V,?}ſ?q/#T'?*GsZ.;?ko4OK}ſ?q/#VS/giggg'Xc2FzԕDĿG[@M5n#*r7V'\68lg ch~-&'%jޢ?ko4OK}ſ?q/#VVZxPɑ€QXz~I[]BA4s/~-&'%jֻ5/49>ƒQ3^[Di ULך2h8_ _F) _ _F~-&kF0U$lG VyR "c~-&'%jޢ'%j>h8zx$G*91 08{95~$\6kkm7BN|K4hAxaQEphWL`h3>bn(^>^vRA %DN ]@FZŵ߈ń2M$͸DQ 69l oOv ]B,Jv-[&wqQ:t;+,_g\}r$ort#cS hֱE46kn|1$ cߏQ{K& +t@kQP<$T+E"IlNє"gQӧnvO477Zxby pAmC+EmZ[JѮ+9l ^V$~[}=ⳏkyf ?9H1+' ^ePʖ2#m,@)! ft{tK#tDlK8L2I<֠i13P YP mF#TgS f(m.G?xHswwYVO2fbm );GCs-?4yTK<ȆY 1S8g=1w6f JrR]u`12cMRȌA>x.Ş&Db6ɶ@R<'?/9Go_D㲳Tk fvU`ݹI\`7O]Ŭ\y,Q#ޤp@#oӬ/oe+y.ʬ$B9koq4ƯH[ٖEe>w`"oi*@O!1Ck,uO':xSr`2˸_y`;KÜU&TrM2"G8S;FB; bO0߭[A$۟12F{禄W~6.;g&I5;2,>Uh#}+T.7VeVyBcW%UN+:KiّoX2v6=1Qj>DO# ǎ)05] ru^1MԱ'$%_F!wa~RH H^)[ĚbHwȶs3ӂY@vQmK[UM?tۗU 2zlU=Sf"1a(wm 2q6ggޗq^>!EnRFB7i;y94iZj,D-^`*H' 0@#/p5$z5[v@ ҡtv!4V-hv9O֢*K |Q#`k]q(D}}EVaEPkG+FZ}[Cc9ns2t- e ;o [ tB ( ( (9_Ÿ#ԴVZ(">nK S¯w5YMٔՂ6X (㷑Z[q$BVx ܶsmiEyWyل;pPŻgrַ;Zv)c >iGOT6ED/eK|0mrka#Ldyk9LF@YHW~GI89?Yѵ,Og/LyU!F#x2=w+`G&;/xA$7D#;h rx`K3OޅɉFm;syD9h/f8f7qYBIk5巛OVo㰩5}7]Kd"Ovq$u#^4x):wCn੤sH9..u2%(>V%%VF8fsTմpe%FTQ31ffWqE\6NiL &a'Ao-٠ӄw),cnr dq!p:9ȿ[|T& s]prP,?uwv%xKA_jk~xwT{BZVk٤rI,N9'_*ׅ?VxozW 'np%ᴵ˫8X9t19\(B((((((((((cD)[Vbh_#+j۬e(Q@Q@Q@Q@Q@Q@Q@Q@q(D}ʸ>FZCqKc+c0(Wwxow:(º]s u/- ((((((+?;뙧Gu 5[Pba$GN߭zr^@h=SRgF >7$D*;?LovCy{jiݳOC:#o.Eb982= ouRU33Y+=OCy{h?Lov J3x:K y|8Y?8#M]9w+$rD$كd.HCv?LovCy{joI,[Kky8Kidi @EU%gOS6HK'vbPX?<-C=~?Z/KlKV) `i|< #6RK#.#AA+ƢCA< vsG!gk<ַ;Vlyf3˳lr[ . htr(u##"1??[3~^ޢ`ַ;G!gk<㵽E3~^??[(?~?Z/oQE?LovCy{kz.!gk<ַ;[Qp0?[3~^ޢڶmI4}51%O1o^+kGZ'ewմv3?/WMk_AT (((dԶI9v n*[Fqުyj&Uȋ,mzT4j\Sv9O4?MWןjO4?MWןjM;}?4ir!3CMyƨCMyƫӿoN}ƎD<5_^?<5_^4hӿo39O4?MWןjO4?MWןjM;}?4hA{U5U5N}ƏM;}9s3CMyƨCMyƫӿoN}ƎD<5_^?<5_^4hӿo39O4?MWןjO4?MWןjM;}?4hA{U5U5N}ƏM;}9s3a Miض-!-%.X"8=kz=R,H`w~5z+QE0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8KL=C\ }6I&Xbe=;[O?$WyE.T;|s=y9_ (ʂ}/Eo羗y"(Avp~o羗y"7?K<]r8?7?K<GH9P]HO?$WyE.O?$Q{'+Tg{'(|s=QG* |s=y9_ (9_ <}/EwQʂ}/EUm3wͶ2X#OKHǔ=:j) ((>6^I㢏Wwx ; _[5t7@w?Կj((((((([_WS\ua]GK&Gsf xbBP-Uv?f( o 4`MW.ſ2j5_ck 8Oɪ߻3Gdݏ,?|[C&~>-Uv?f( o 4`MW.ſ2j5_ck </K;0Soiopvdy8?jo 5g%YU 8Oɪ߻3Gdݏ,?|[C&~>-Uv?f( O m}^ 2CꨮP\ݯ\}Es2t- e ;o [ tQEQEQEQEQEh_#+j۬MC+Em[u4[QR0(((((]ʶtwh6P⎶^mx ֋i\Cd+[t.D/\S.w{'.oΧ%jrlA)"@0w28 pFsM=YIA-gKziw @;ņl;2]/(M&IhX|ms(2uOMov}H`ZT6Ӄ^*G5_6]:Wm]DBA]DCZEy|z%]N\I b*az A7:nl1KBI`ʊDà?r>oVo;(aEPEPEPEPEPEP5,?tOЍs<\BJᮇD</ְ؉nhQEdQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ |mۿEk@O7@w?Կj+o+lPEPEPEPEPEPEP^_j^/ rK&hԱzWW+']Zӿ\-;5ݢ.KNsvD?{{kv.KNsvD?{{kv.KNsvD?{{kv.KNsvD?{{kv[Ŗ_o+{#f;Xad\'"Zwk=㵻Eada%;G"Zwk=㵻Eada%;G"Zwk=㵻EadqŘީ M[XuKt6Ӷ <TwV4ְtV8+ Q[$Br+l5W?/WMkQEQEQEQEQECO}OZӬmJ7Xn6+;aϱN<%CN~!R?"$ݱնCXsE DŽi?aG'D_UoTmYv38 :' "_40( DŽi?aG'D_Umy T\djJ4 LN<%CN?8 :' "niIpƥݏ@4<Wj<8u8E01?8 :' "_40}Z=܅#PHBK'4M#+ Ӣ/*N<%CNа5)8>В RHP$U${FN_40 Ӣ/*.i76hդ#f\,IOC @'*q/tOEQ DŽi?a[QN<%CN?8 :' "VP9-f,iʬM;3 Ӣ/*N<%CNޢN<%CN?8 :' "z_40 Ӣ/* N<%CN?8 :' "z_40 Ӣ/* N<%CN?8 :' "z_40 Ӣ/**GiawW2j;!7vDAWqkW t:'FKsB($((((_r^_ik7<4\X2HXNC^?; z aNC^?; z aNC^?; z aNC^?; z aNC^?; z aNC^?; z aNC^?; z aNC^?; z a|YN;;iWwRgd0^#' N'(((((((((((ow:(%{]'WA\sa]KKf ( ( ( ( ( ( |$ K]Uy׃PGi7U\܀q'eDh'%j>h8+oQX?ko4OK}ſ?q/#Q`7'%j>h8V[@M5ko4OKX .| ֯mz^Aun1(#h+9}+G~-&'%jjY|0Mlſ?q/#QDĿNZ_xDҮu;n/I\laEc9[uDĿG[@M5!V[@M5ko4OKԬDĿG[@M5z_ _F~-&E`ſ?q/#QDĿEo%uIyg%zvm{k}キ 1sqa oV؉nc@v++l5UQEQEQEQEQEr:'kĞɊb5S!9D2H;O͹saȨeơ-f1Hdf( p)prkI%ZJGx1K}N9mELm֌f=ڱFyx6omjK$ c2!;g#Vgc*$Vq,rl0OE`z0AH% X׌u<a5KC9Ms1sח1db&r]Vxr8̜xaazgy2Fќ` g]=Q~v&Ii[i(ˡmP09 ǎEI7ep|"B( AQEcuםm"LϱX.T6H 'J]]3ɺX.F]`+hI^#m"z)r*J< >wa(OY<-$Rq̾;0{Z4 a ڮrƺj(Cu>zei!@3ʒwMP@5o^²&IW`D>YPpyctP`yKsgMZiJ,-mV)2E9 Rð-#8"H]`$¦MJEPdQEQEQEQEQEQEs^<\BJᮇD</=,?tOЍk QVHQEQEQEW=_AaM;Ka i[ Z(juKeh@JU$d?6Ji1FRE;um5`eԃ*~BGZOz,z^/^XњktT1˴=62FqO~gErD_%?jY|Hwٿ>m x]Ֆnme{6kAUeK,2dPwWm#MK->iMAo%%7,=.5xI9,B6ӈÙ$`r9PAsea³p)<ȱƀ;=voiqw%ԑS#j푟Znm5恨[[.tI,ACI;-v\@co\gI\V}͵K;uYdxˁH#-ZuX#&n'#0c IJe\MF9;J+ZU b fȇto9bvryf͸Rn%~PF6n>N/oH/gy { #q1xdefX%pYPHd~b\^d$i-d+2yX`aA8'5_=@d1-E*7J,{F:P (QkpOR㎝k=a %9&KU޹y:^$ )].WyI%?LovCy{kW.-b N *aq p{jv%#3~^??[*nQ~?Z/\ Cy{h?Lov`ַ;G!gk<㵽E3~^??[(?~?Z/oQE?LovCy{kz.!gk<ַ;[Qp0?[3~^ޢ~?Z/\ Cy{h?Lov`ַ;G!gk<㵽E3~^??[(7caGs HsuBqֻ@#\?WҸk? B56"[QEY!EPEPEP\4-yMᄍ&x]-=VX–Bo!FG^A٪څ\X^Fb7ܤ`++O o=8k,Wϛ6q$.BɂsR]4iشѥ99 ?ߛ/GO o=0,2 Ihf~]!"snZ z {h:cx "j#z?p͗#ѭfxi+$2fıq{z Y-]]\[tz+xK|8vu%C=ʶlKYظnj麝c ;`⥺KجQU,~I>ÓO8b]ơg8`UMb6H`s"l1". ~Cjr# 9d ͬ';wR_JiZtr3do-qs=\j%<1(GGc6}7oy2x2;֯~ vxeV* w$uSHUm]f\KsGı&yfXU~@A:CڲI "rttɑهHuNPY eX =hyHFhXxݏ=+?ֲF [!f}|޾RH.P[xbȲβ́6n?GL*1`8">ךa/)*ބ9AzJ+7c ZV6s-Zys~x늜piv(Bvgc== sZ?u<5E!Y$eFF $^jVn<-[+.,If#,:< rr>A8$ïKaeI|yR;9?FȾH043d)bg#8 +{0C6'Rv(hityIGVjvSZ ]CB!U1.I=r9Q궭qA+3NӎAC\ɳ`٠x9BF2gY?2XlsҷOd27^&*I㎝ $%&ّo%uIr6_ 7\W_a oUas_A\ma]7Ka(((((?? GVկSn1?8}UuDGح֗+F]>lN0>Q88&{"`r"Mze&aymI (^8r灒j̚4WqC?Vx DTO1ٓd3E]H%X /S~srskWMmVMa;`Hs =jyqt ǜcc2 <閷TQ3*NX a)g.C~}+]PKOѭ7[!|)Y YUdU0xpp WZ'ԝ(6) qkmp01ܱhv8څQHaEPEPEPEPEPEPEPEPEPEPEPEP5,?tOЍs<\BJᮇD</ְ؉nhQEdQ@Q@Q@s5ӿk MjYeZ[N* 36OUQKr<7n8=:ѻwKC'_meY$WZ( n^`e^6⋋"7kydL>zogq{3Co-r׸;k68±ҟ4)* BcKMl}սNCL?t}2 k+'fbdvRGN}c^Ҭj#Cq:)v*0]-sc ֺ*^7)ǪjoZXmyMscӽn[mlr@|9<UCu V戦t/P̄U$Erwr=f#}+9%"QP; p5tkɮu^)ʝ6B 4HO\Ð (EJw( ((S7aFK?m\ NsC#)^ 4gdݏv×VQVǦ}m q(bIF 'dݏſ2jwE;!]'dݏſ2jօ|E[ Kkh-dGo*WQnd8SixZ'eZ^2?|[C&~>-Uv?f[@-2-^->-Uv?fɪ߻3Z^-/ D̿E>-Uv?fɪ߻3Z^-/ D̿E>-Uv?fɪ߻3Z^-/ D̿E>-Uv?fɪ߻3Z^-/ D̿E>-Uv?fɪ߻3Z^-/ D̿E>-Uv?fɪ߻3Z^-/ D̿Eo]z{WP[ILE0UQ'dj~z7eTB`ixZ'eZ?[@-2->h̓_A\^<[mEw&dn>:{g</ D̿G2 3/ D̿Q}B]u:n-)o^%*A144YEQLAEPEPEP/HRjp:+jFծ)KX%ح+Ds8;Tz'WaHڶ.H+Zǥ4#dia=s31]EgF/;"|2f\?%n} z.}9bJWeV0@Uq;?kv *"*2@zXˣxnY6M.q/FLWWjX;q:tQ/9iZǶtfx{{8.$D,K7$ =q]I/ ^2ch\޷um.ȣT+urarǾP~o$NlYY.##x\ݏZfutL+·~wu?$R$`n< Gnd|zC"6d9',pxN/?yt61?ۭB-R5%+jfxCeKKkH1G.Uzu[:tIͽ*SJX61NjoIE0.T{s5SRMU;'&dHFPpw }F9%u=jQXk diWp%ѷ&1/|Ac=|VrEPHQEQEx(Iq]}!\o:'6"[(B((((((((((>6^I㢏Wwx ; _[5t7@w?Կj(((((((((-Eu;2Q6/$;6ZF]'(K?u"T  T߾GF<\LW9}:˯G$Xpg8\sşHkAp)xKA_jݬ/ "ºM[lQE ( +m--5 O4K]w!.P P ot_Zv]JXaK2|ڶm8P ,(^]{xKshyKF`rqFONoEN3s5N'c2܈ۧC[N}SŰ_x}ݧ&umim"%U0}EW.|Iu/M>[d0$8M5/O+$ӭo˝dG_617\gWA-@I7*K hn~H]lM_}C0hs\KqΥN p3g5ycY0M1op>_'vQIبQHaEPEPEPEPEPEPEP5,?tOЍs<\BJᮇD</ְ؉nhQEdQ@Q@Q@s5ӿk xLoEm03:udV*YT%/9j䁀OҹѻVKR) ISsd䍬U>3}j:5obX.8XPuWwo H]` $ ǂW6W :34i%|$ǘ~IiYWzd_+#ݵ*K%Wg!pv2Gpyt92_זC2ڼ9j_=mtɺ-_[ sۛS^ H8*,j z{.KoaÝ|Tڿ`:sj7è%:068݁s]E4LN9tIwī02qW(op(QEQEx(Iq]}!\o:'6"[(B((((((((((>6^I㢏Wwx ; _[5t7@w?Կj((((((((((FZGqKc+c0(((?? GVկP42}p|[ډN?btkjiiok."1N1G^}r1beQoZݢ(.aY>qN;q[7oRKeu"E3P0HKy %~&gGmp.%2');6tU zU8im <+k&R6H->e{F;)(休Ȕsq9$q)}C%9]>KVb/#=9iߵRVYIpʒKrywlh1ӷ[[[[xuUP}sHP19mtlNa}GFK&ei dFV}n;Od⛴ybC4ybQ#ff.o&m)Üqڷ.4}2:m31qY47H#mh)]qcZIǷo!i멀'b[h&I$W1yr0?4@d?tǴlSϒ WKuo/ zw:JT2EdO֬}E9,pGI#ҋZ] |Mz[[kª;gN3_sZOwj2>Up~_~U}ꮵk lg/X4$[ozӷIBŦ`N{=KyX*K ( |Q#`k]q(D}}EVaEPEPEP1He$I ubs~Eb]j88?mާt(4{ ]K(Q1czXƭdXiv%KRY$€~c+Z ]f&"~џF%z#n=z-:y㶈+mA$4Pmhpek`7Ă%A rXÏCC u[cuZgG9,pr ❕r{Dr,YC̅NACێQQap Gd}v\[Kl/ BEݱz0rJx綾e n%7F9B /e95{#zqq ahh) 8'?".^O*1Q6 g{~6?]]4G`AGR*Ke?//mfhCCО:uz}r%Hɴ/icsdcۻ$5E:H/av`n^f;ID$YyF1ZWI6D6噥AX[_^wb'sIh\Ciw؅0pOGNhB8e HRAW1ImWB?%-"O*W}C9A@sHbt yᇆ˕u2X S]{QPXQEQEQEQEQEQEQEQExEqa +@#\?WҸk? B56"[QEY!EPEPEP\4-y7^K{heԴ92A# + Э/&A%B57A%B? ' hzg/(c,3]x4Џ h&UP̟ap 68?CE`/O?V?_H +Ox2W)4`ʭ$9K<2Z b$AS ' j8ə/hH~_c訬A%Bo!>.Sp#gϱgfRxkAu TAw  [D|_MR Q[$Bľ{i.e3jL%F~ppJq8VIk (((((((((((Wwxow:(º]s u/- (((((((((=!V?"sşHk(?|IcAycCp5+0Uӫ#J2 zO4?MWןj{:A$o`6?Pbi8"yj&Tyj&UbiϿ>G"fsh~ ?h~ ?t?ؚw4biȃ'k5G'k5]&>ؚw4r g= Q WCϿ>G&>9yj&Tyj&UbiϿ>G"fsh~ ?h~ ?t?ؚw4biȃ'k5G'k5]&>ؚw4r g= Q WCϿ>G&>9yj&Tyj&UbiϿ>G"fsh~ ?a:Ů2kLV7I+c4 ܨ̈'ctN}ƏM;}j ; ((((ј.92۵Oj]GQnf .zuޡ?? GVղe"v}O{W9/R]n[* xfɠ鲕S+|ӹ3=)!C6vW+Y\ҘIYd(ݽ=G%1Kw1we9`֕ihP˫\]1RFӂ{*&,-TgV9ݱYC ]Eoq?_ٷ?gvn= c4+u)t5l/dbhJN<`0m~AkiڕH]cƠoe,}w6v iifIB{I-on`q+4$2 y kyOpחƵS]Y@cq+2]T(䓒sryMJ Wͱ%s0BaUOZ4koyw?m${~)o4K[-yLVPl2<6IK MqK)ne Hwb˞d%Ԯ^zVᥤqq\2p wcŽK`qZ 64 B# F hvW%'fk?Xďmks342‡-$*FؓG ͸f pN7i)-[6jI̙'̒Y%v㎼qQ1ªݣn,eKV>0pI /-D&mdk$RB1#MʬORǚ-KO#w(ޯ*EY,వKku+g$I$I$rjzNХ{jQE!Q@Q@Q@Q@Q@Q@׏?WҸk? B5xEqa +@#Zb%EUQEQEQEx@SNk{ƿšwC5&Tf,QcXl09F8ZQTr>S{cKks3V[8[Čg'h]ݎaKkPi23C]>ȡx# ))CuWr&7 ]PWz ۧ\}QلiVxbU!lH`ۀ*#1|M+U՞Vj KQwvpXORO?tXz-&kKV?/ mIE+f?CRL!=BUk( bGFvK˒>`F3cBvl"IeE9QiZcCY)z!E1a:Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕ?'k(Q;\_ZQÕcizrJ=ÈL1X۷\WFdx"I.+urO*j̱EU(((((m]nj& ڥѭg:ݼ񴹈E'x5!R?"x"Xq6EbeWruyV;88V,L|PpJ~H=e(؋c-U dx=qIVjqBksl|ОY8j>`0vg= [Fcwzfe!m9;vBvGVmp~a yǮj״Mi:c@֌ ri(kK4.fPЅ69r3]28E;J|8`p3ia|8x?M߆jX Օư9U\HTO=t**+ZE#Aa(.M” # /~-xIdH4ci ptcV."uY$Ivx񎻨66N&K^;weHL?C#$qOW5O-ƥir[`ܱ&efo<@|Š(0(( Q[$BI.+urO+XlD,QEdQ@Q@Q@Q@Q@Ue:#VkKKq.aonX (qzUtbW]i XQSmRڵޥ=\iwkhxc<1ŻY\׮D!H9#x3+(ml[m ]\(ese#yFVoaNi+ ("ҥ%٭#d<0O=ㅳ5 !Ys+C*YBvwm)XnbmokcG.ëuQבZռRtɮgW`~@.7a*}z-~lZZH ŗ%\q~;}g-ݙǓ)׾qך˟wHMko6\,L'n ```el 5e<7RV9`F=яCNqqkYc!n!kU +m$?b`|k^Kd+iiQXG<3VhSC 5y;VYYr }G,sKVu:Z*us{X]kq4*#lHϠU~Vv);QH((((((((((((kǟ+X\5y"] x?_aТ* ( ( ( ( ( ( ( ּ>uoe fB+,n7 ;l@%> |Umkv-5FCnAɕ1ǯgak:y$^uM $5x±tZQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|JnO|mۿE}?WR٫WA@Q@Q@Q@Q@Q@Q@qWCԿt+Jn̷YqWk\PXۺl8G?V?_G [D|_MXu9 WA%ydtխYjZi`/O?V?_RV{WaE:1y0c6kgY$,1E2Z{'+|QiztB%OڌX)&4`2Q >KHwf'Hx)⸇B; 2IB1}t?Q >]V) V q*4aA(VǟdwB,M<;BیIkI}oWjMtE{Ʃ俱|Bl#f@FF6'f<>I& ɣ͓ 8ɮ]'}^?5[-cd`H?OݱbMvW`LTOC\r G)e܅,A2I'5kϾ+5G&O"T[G8 _h!\-eQ.1֦|s= >?Qdf'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Ef'HO?$V&O"TkϾ+5Eg3W{/ gq*9Y f`2Pw:t SSsYEtP? Bf"HuO5eQ@$: ?!?L?$WAEs&k(T3\_]CЙ/HSsYEtP? Bf"i: :(d0!))ulW?g%YU((((((((((>6^I㢏Wwx ; _[5t7@w?Կj(((((((f?(zmt%WUÎF"`0:d&EswhdgbLt/;\XTk5$D[g>[ن-o2֢عV2SÚlMDQy2o9|0I2xw}RĻl0$IqZtQv/tW7IpTq(8jdѴ%d2fTv*)WcZEaʻgC;;Y"2H8ݘ]N=A9(xMXdhdDUIUeCmLJ4cKw V9eb )?r=V.Õv2dH$RgghRk B:Ɓt<"dICLA-Uv?fɪ߻3G#duW!5_cho 4r0G_Er>-Uv?fɪ߻3G#duW!5_cho 4r0G_Er>-Uv?fɪ߻3G#duW!5_cho 4r0G_Er>-Uv?fɪ߻3G#duW!5_cho 4r0G_Er>-Uv?fɪ߻3G#duW!5_cho 4r0G_Er>-Uv?fɪ߻3G#dIo%uIp]z{WP[ILE0UQ'dj~z7eTB\U-ݓQEB ( ( ( ( (9 ѷu\m|CԾ-yZU߲MouuӾq:~?Z/c-#E`ַ;G!gk<#7Cy{h?Lov7Cy{h?Lov7Cy{h?Lov7Cy{h?Lov7Cy{h?Lov7Cy{h?Lov7Cy{h?LovLqGys4QƮc'pI2q*yU"n49hò犥~?Z/f]O{pZ F8xJjxsCK#t{{-.ク|~?Z/@hPSeU.@18W3~^??[ +3~^??[~?Z/~?Z/~?Z/~?Z/~?Z/~?Z/~?Z/<\BJᮇD</ hQ>&c>u2D9G{D</ְ؉nQֵFX4:kyoE\ $Ga?^-KJ`i[&M^-/ D̿[TKſ?s/"h9kb9rKſ?s/"~<[}cgݵiOen~矻EsF?_EKſ?s/"sF?_EKſ?s/"sF?_EKſ?s/"sG7>{xGhl丑c%,U6gֺkn2@q$ğ MZ^t!ZEL(oWCKy{+ w"Ơne_\4- >Bvl"IeEeFG$ ?'N?$VsFG$ ?'N?$VsFG$ ˃UV75"Mg4o;1?%\sG;TdOН/H}GsEkG;TdOН/H}GsEkG;TdOН/H}GsEkG;TdOН/HGW:uΝy]Rw 2,JpyOZЮ>_*ׅbqQEdQ@Q@Q@Q@Q@ |mۿEk@O7@w?Կj+o+lPEPEPEPEPEPEPEp>\ 5,p%䞕?zo\":w=#޹;@Xc;~?ӿ\,tW1?zo:z+GNsvDt?{{h =#޹;G":w=?ӿ\;~OEs?zocDt?{{hGNsv\":w=#޹;@Xc;~?ӿ\,tW1?zo:z+GNsvDt?{{h =#޹;G":w=?ӿ\;~OEs?zocDt?{{hGNsv\=m?[#h_nupynhpN kٮ]2AIQPeQEQE%WUXqWR^Zb|ݔLwjlPXۺIy[2Fqz*Q%rrj:ƷGO߲EhFayX?69{gUS C?mۻj띹+9._O㴻VLIo @g+%0rAz%yI$Dqn-U,$wA'b\.qsoI$QK",23@=RZkew f]$e@9=-[JrN{H:p]_[f쏴y @>qjڬ1޽[d%wo"RjH״w5ki<U5z9U-%ISpGA)NU,'◆PkPevJMv((cu*wrr`AY6j$H?9w#9w ٧<#tEgB۸7͑Hm.%Z5Ԗ:Nv2Fd`}V9:ZMn&70[[+Pl ܨ:o?nװ&/Jy{8ϵ_HdцUi[NOfֲ6F3}(,~{z#.} "Mߐ ]~PG]UF`5 +azWi/Gg[[Z)Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@׏?WҸk? B5xEqa +@#Zb%@m+bvSY.fXԷhp 3x57'D_UL֣ܠ\Tטc·^,pinK7r3ZYuې s]'D_UxKU*ʺ96wgm5ΧlY[}&Kty16+}KZGs~o;1n7Y*x Ӣ/*N<%CNoHoƹhskuPٺ'*C@ #W<֚}ZDÐ 3]GeiF]"F88_40 Ӣ/*1A ѣl:x4y9=ż=OF"y&UcVDeHi | w DŽi?aQO"Lj ]]VKXS1ׁ{hM.pL6$ں :Oآ* k ?)l5=_AaM;Ka\:>34vV2 ^b$$`6g(Xcq wlAcw$ۄ9v r+֞=Ě~oa[)a/;1!E,xQiyq$ nm\<ݱ%<Cc;GoMk_Csky7srG (.Sí2׈4>5Ĉe]$BI?tWG5Iq6pGnks~^9wLE&ӿ]Dx/^h]sMɧ4%Lj/\KIj ﵲ:8Mk4&i/~9n]r\FH*)vXi!k iq츂@]!vdbBg&#2 ^uuӄè~ʒ1.A pEzVtcog+ػOvbM}|cy?48_VRMLasI]%0E6ZO>fh†\5SW \|T& s]qP,?u8n((((((ow:(%{]'WA\sa]KKf ( ( ( ( ( (9o?Rq.4ViIHci HѢڠ&xPK ay_K-Qde1]Ldp)gmŬrZYC /~r7Һ071[z{iw eou;󰸺0(E*FeԎWyY-%er(p7[:Ͷ]s2P0!)nvFįox%g- ^c.3 2P+Bq[V!y|1>ua5vyTSk!'{W''5n}#Mtj [g<2e=엣c{(eQ0Gc{R5/u7H-Go;\[GY 1$(ÉP:d &qv-#x* w@)]ܢKEr-A-k=>;ƍdX avrO,O&~&T5FgkK.w~Sb{ѻ=/GE`Y^^e6TqycL} 5~{&{۸""(H'M&OͤډD/hVY\"balHWݣl+O~OEh#`xr}AؑOtvW%u m&GW p iLţ'q†4,@`{h16guÝ*y<'Q\Z-=#Cvv]c*p ߒG]hp (Q@Q@Q@*ԼZG\O ) ѰQ䎵]>ynOaxRVVA𖹩_yk66)IYB_1`0O\ޝ%Z]!EP3=_V?6aͨZJ0G ) gPG%WUXwzՔMռ0+4 qa?ޟ7+6+ \IEQJK$$x'rq]sx$z_؉G1-ʗ?>8M߻[tRv춢fI@is<;ʆ#p 10ϼ3;%u[;ipL \xʏL[x$YbA(˓hV].r q9ZUy6;}WqDgd, L8Nr `V^7vqk(9EpA*Z$*(QEQEQEQEQEQEQEQEQEQEQEQExEqa +@#\?WҸk? B56"[z w6Ҷ+R.FVD*;QR0(((((OI`եI]O'$]j #b($+xa-"IRHPo!x =_AaM;Ka[ Э/&A%Bޢj`/O?V?_[Qp0 ' h_+h/(?Km4/O\ A%B? ' kz. [D|_MKm5E Э/&A%Bޢ?V?_X6zVH,-lk;hXԷp2B39W/ G\reQEEPEPEPEPEPkG+->D6}>5ͳe 9=?{+ ܴV63y(DFۓx$i.S[Ծy&QtMrKB0۾fq]QtzL.e$l=z)Wj:FDjzuhۑ.`YBP=46ݲ|-Y8Dܠn䏔,$hqa6ь 'y#4;Yn.c4褟&yVɜq=NsWKybegPcߌ~sEZRۻf7O;snv-rQ&MH,a$XaILӲòuqJ?SÚͬ6|B7 7Vl >VV6֜(]q<5:jE$Lh͟b]`,v )'q&fH} mij[jkv\A*\5Ĝ>0Ϗ7mN/p1h|qAE@#qR6PjOad5х|Ӂ~3tjXu}LX[4Y[tB;x`rMa=(]Ms "iLnjG݆ x 8l{i^,/WRKFe. o8 w2 `?KzK([ +؀3#_nq+NS}>K~Lo0q9u S-q%n; }c~{ A.-Vs:OBt/aI[X0Vg#0 qjQ-?bYtذ:7n#y 4҅smg3ʳNۢ`y}j[hwvPY\t֖`8R0? 㮴 SN:H#H0+ss2JIn.7wIe#B (Ivv:.ۛK2yX_pF3׮)dд]*ա;B Y84M.6Ow%"y9vpr21Zz ktza3uT.[88N_\ڇIm}P,fB2rWci=U(aEPEPEPEPEP.%Z];J`w]%"B(f?(zmmV,PXۺuwi]^˫]\C0Pȱz3?\3`Sڇu[\y ܶ3Uk=bMB ZgaG~#XB`hS`|FW-hz/DFmնuywiҢF%'$iZ+Es׾,v̀#jGwo]+RIieV!6G E(y?SӬm6mCyh&H#r$2;0[Pb @I?.M;Zژ~21 0b '<(͎,ޯZGK"3=[}c';N31BOK\eӬgk;y.E3ĥ2=7M1eirp88qpG hO~^L٣TYt`8y=|=LӼ7lp(/ v#尪H $ xkj5p0>S3oHgʮ Zji,3QbS3)i q;r{ݎ̰JYP@H#\H kƯfd6M0[(3ʒ).}gyIi3- 2ecI/khf!EPEPEPEPEPEPEPEPEPEPEP5,?tOЍs<\BJᮇD</ְ؉ne_P4_Jج}KJ`i[ܨQEHŠ((((( ?'SV'u?Vo?w_)K+XlD({ƿšw] s5ӿQEsQ@Q@Q@Q@Q@Q@qP,?uv%Bo5pR((((((ow:(%{]'WA\sa]KKf ( ( ( ( ( (8=VESح Ӎ>1}/j՗m 9V^8犞Pqሦg 3 g(ïl<#d2,Fˏ"ahv(zNK4/R$6˫ *0$7t#\i YOk0hfe_d`8xᴹ0B鼸`;l1*TsjՏVwu+ȶwˉvn:s_Z.|kͥ9e#m,B1c9 UnVVoiFBNwb-M;H 0xw*sibfa^hQ)ԯ4գm<ʭ:ȱePъp>'P D0o0Bӂi/ g'g S~]'$ bTE,/<k[Mwr 2yk+`Qщs׽1|-'oL4ofm=GD>mt>R+gyI@j,q12*K閫I|C E@w۵'P}j0n}/S༗X\$$J$„Up0~dQ9%ot=Z{ǀز}|WD""Oy)[mݷ۳v9ۜT6r[]2R7T yDϾTBi5 v"w3}|ݸwnyot [Y."EV_ICJLJW2ؠlӇ<^7kZʬx+Xg3[KlukXDi)p@E1a-9> 0<d1 q!cS9g4Nj4b[lDsD0]YQ9 о1ZMrJ p5)#U4UAmq:(KćaTT|皳mXa)'ԯghtrmA%:W;|r3E24dRF'-Nq9<QEQEQE@ֿan뤮oN.FIH(J`wSHu Yh\s9#*FֲAO$ʸ@q3an]I+&VV2O["R|eg۸&w.s֬\\"z䷷cRw0w4-⍧%KIf-eѼPd`62qӜZzKIb@*@y"NGy8?GÂDjܱmˈCG \nԚllr\]8XpEê,W`=h_sGry|G`q*HUFǪ],͔vv5rV-O^FM.5*$TܹUlx|m]ôar6B}K)'涢jɋ'4I.$Q fr*1u۔ q#8oikp3!c$c0#۸= qm€hip yO0dc;zzΞ'^ȭ4ah畑y3 l1IMIpEfc5 ߅4=, PuGǗRz=^,n-Uv?fFCſ2j&(;+OIuF2I1 I$Xc,F .2Wr0GQ\dݏſ2jÙ}`MW?|[C&~9\dݏſ2jÙ}qP,?u/dݏ4 궺ԯ類CIp`c5QFMTbފ($(((((ow:(%{]'WA\sa]KKf ( ( ( ( ( (&[:X[)7p7s16t[Y-&- Qv;# H]WhK4y⺶eH<#q>m,ehЅ -؅.si 4N :gF,KzI ;[9$9aLKan_crш4BsRp8sVhz/5]f;My&$[r2n-@x+TZe4(esz褹LJ0y dԴt;fүGl-Tmݴ.wg?]-.1F)҅l ֻJBuH~C-|W^]m ?̼P1ܿ(AΡ5Z_[vFIhO%r9qJfHc 4X:wd֍ED'ygh&V] b9ېjM?wj5ji16/6n ˅bxN1ZBfF pO$:{R~!cfw4jAcC1c0 ޳6]cLK;ˉb_,+*VPeVێw*H'g4pU 'z%+ p~˦i=%,r$s`20rV_j>!2]YKHQerO^9\0")f8Zl}d<Tc]Fp:U7y/]Kikb/#I"IPacW¶;t-:V}z7K8VG!,#rU}tkx|o3;Ե=WKm5 s`F,1摸wuRTӭ"$LGdL$޻-hft̐ќ ?5->:ώu}+K2}b Y2t ă =j'VMoIF5di[.wIQx]}ilEn]>ơ Ȑܡ8NI +9+Nj7[wTJ5Kؖɛ0FssTf4kiYn#.>XR0p 0${Lhi\$qwc2MS4UV_q̿a.L.Ip\V>"]·#5dGu]k1չ#ЊRǠ5->䪐8[_]ܴXE ;Z$5w8IN{ S7u֯j#i@ߨc ؓ 2g' 'nO[k:- 属$e:9MU|68L^7[ZpF]sHCwA.r*o}W첛'oG$S ?x }@18<K*}XH}{xA lI9o"l,%'w$ e@9[E'21~:OjlF#?y'j mH(QEŠ((((((((((((kǟ+X\5y"] x?_aТ* ( ( ( ( ( ( ( ( ( ( ( ( ,--F5W6pw"+IIT^:;]I7XrWxl-K4MSD,!S~IrKq^O|fIՎ·%ͽ}ܛ@O R1th}_6&mf*ȫ#Jcy`$T (((((((((Wwxow:(º]s u/- ((((((?K>v!6/nA , ƞ6C>iW#n3 ;7 .E-om,Q?oaN4[ѐ[S$cgׇ<1Vppa2̙˷19Ǔ޺*+}3Rkŷ42H%8yLqjl ɂ WUuIY 4QeUq yE%(m,s~'Цզ43SHڌĊB?0G@y H.JI5^O:O42y5s| Zm+z1!B*]Kdg&< pm0N֕#`v8AX [Ri ?Gy.fӥwul#7V-F[IdYͼ.9GcJ>BӴ$:WJp_j9q$in>q93Z3$ebPcYPG-7UI-&RlmÍ۱QM_0Zlpڏ bkY 1MXB>T`A)WwH˕y1,00G$gkyAhr^gKm="H #PJNy^-[Kp Wo;o8ۛ,ȃ~NyvQՌKmGG6Am(#kV0%q\ymEeC9#Ȼ_(|6Tu:)[[]nVR>$?s6l##*hX?2Bpc.KJ q[+J:Mſ˷ K #?nQ0VĊDL&B##M~y#85h=JZtݯ#pv[:%y@7`t)hF2T̟_C9M67F[y9Bvl"ҼM\:uwqK<$ȲU*c68==h(Q@Q@Q@Q@Q@ |mۿEk@O7@w?Կj+o+lPEPEPEPEPEPEP ˽oG𾑦\x?Xi졷̩d@JտM$WMEnj&(տM$WMEsտM$QЛ/H3Л/HsV7[_]5gsV7[_GBn"j( Bn"[oYEtP9[oYEۚ 騠.s?ۚ ?5oueQ@\5ouenj&+nj&(տM$WMEsտM$QЛ/H3Л/HsV7[_]5gsV7[_GBn"j( Bn"[oYEtP9[oYEۚ 騠.s?ۚ ?5oueQ@\5ouenj&+nj&(տM$WMEsSQFZtZ[ķ2B3H JH]5P (J`w[4WgN:f*FӬmo/-d`ZGn{&F;ϵc? تZ;1xCn A}sxJ\ص.&8&(aXPJ`91+[x{OmDޯy4 0=1Ѭ!0dc1e' zqҹ9uҬwڄKVlC1f)mɟr1c'p4]Vis"m7qŴnPʧc{f/_ےM_ikq,DkWy T$vU4Y٣m/Y]ٳ ^lm'WlG0) /iK{X`XK5Y.YOéA@V<i' 7w/gEc˶MՋ'y ,Ic 2SDK3"I5of @aa؆x7g~(6ɰ d:(\I2O4K&a mc 8૚BTԵ3M0;%ȓVBKIe7xXpܗ;( (((((ow:(%{]'WA\sa]KKf ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9 ѷu dU=_V?6aͨZJ0G ) gX5u4l?-~~/g;s}^v>ke9sT;*<ܞU%2 *" "G=j3"iw׎:|<'{N=*>V;N9|jvH% X=d] P$+h=1ѤK/t96J[[%YI >psK]sM2w a]q{ZƟC-AbYě;9QrV򩇆.m促8Ei$y M؜i[G_CfT#i//V4ʀ1z*+f[$g.9#@8T{;3\u-,U*`F.~ɑ֞Itu..V-"HF q'$c1cPm4,̗SEiI )9>C:,Ua2pp{y.mD7C|w?ܬ_p0N27<_p'%xR&ww=ޢQeƗjVi=u = ڭ"Juu*r2"al1dXܾqozڌ8S2dc<>)GB\nś\Yȃil@(_guA 8٧LBOg9ž5Fo-_D-ܬF2 O7}ߘml㊣?;;X.(cDsrLwjiM'}י>kqOi줄U u3Z+u QDFݰ8ʶ38sLW[i%ߐ2_qzMF_ƶfyx߻{nsޞ(ӽ?\\. RHmȗV/8A4homYDMЌH?@2H xxIafrW# #q&[FݚgA 7E71'<-5%K5+miN35%e@g1_2 }2Zo O*I5IRVa{n;s[6bpیa=)+"J fY)Q@Q@Q@Q@Q@Q@Q@Q@׏?WҸk? B5xEqa +@#Zb%EUQEQEQEQEQEQEQEQEx@SNkkǐCuᄷ&]KOI#C+A;P٢ ' h_+h/sSz_+h/ Э/&7iiw}mt#9!s FO$ ' h_+h/?V?_G [D|_M 7A%B? ' hz\6ž[9 661VBx<ßZ_+h/ Э/&V [D|_MKm4ޮ>_*ׅ/A%BҴEžake YػGm ƥ끒ϰc(Ԁ(((((Wwxow:(º]s u/- ((((((((((((((((((((((((((c*FӬu{j]&BFsl8=_V?6FiG`GTh _cYͿofcwgڶ?wfv7}qjʋJW1$i4Ao*g H88p{vz#!&+p0#bisIf8bEnIg#6#&)#V =rpOӚv_cю4WNHpHe*;z UmSXOm-^+r8zWᷲ-8'ʹ!@Oj9-4۝I&[d ȭ,JA}phw{nZw,biAB !U\ʜ= zeݜ:n@*cAj7y ݴ0\Z#u5NQ\Jèִ/El?hmmܑ,e**tw=I5&Љʪ0Cfs3}QAEPEPEPEPEPEPEPEPEPEPEPEPEP5,?tOЍs<\BJᮇD</ְ؉nhQEdQ@Q@Q@Q@Q@Q@Q@Q@s5ӿk բ+((((((+Jak>_*ׅyEVaEPEPEPEPEPkG+e岼Z 9D LadcINГSsn`x.4/F&I .\Mooɮml0呷099>?ZQQ,bhVPFˀ2xG:[I4ΥIԪq?YRZӾmZHY =h%:mp_Z+7[~+mہWz`-UcukIVdP @-$X_im,ҩ]EVv+ 0ВS{'EW:)8٨>3/p;s]A(;֫i5Id,/`pӡreVnVMY!ES2 ( ( ( ( ( ( ( ( ( ( ( (9Ȯ?!apCkW t:'FKsB($((((((((+ C\4-Q\EPEPEPEPEPEP\|T& s]qP,?u\7;(3 ( ( ( ( (>@%{]'>6^I㢀>o+lW?WR٫((((((((((((((((((((((((((f?(zmk$L$9?d%WUXkrkWQ^Yb| kzqXhLu n/'h^s2Z_/81wgkf$ o/,!2@c%:0zdzHzٙmnX i-|dd7o6Isirf(&푓~qo m/5 h%iI/Z?%Y3mmr6acI{۫h!T ;e[.<_HchF!Hw Ǡ%tw:M6RQ+Fo&5$;إu}{:wmm6u<ƞhLgQ0?$4+}KSG fۣEH*\t `-u}FM~k!襄 ?NJZdӴmI9$w29|zb{=u~F|^M[ d-MԠ5orrp >A!*v`0{wl-Uv?f( o 4`MW.ſ2j5_ck 8Oɪ߻3Gdݏ,?|[C&~>-Uv?f( o 5&V\:ci. lqF:݉ފ,p)QEQEQEQEQE+6MI\ny Yibd1x^HOS3Pݍ)Iٻ|GoJ#Kk::eA<kNk`ͼYC%G*P 9N08i]*pV5{q[mP۝8Ta*$ۃ3W*/L_,K!bEeFv1j΅sM56{'% |dv*7u\!}UUپ  YY7.v(2nc ;8F*܂E{#JnP1V%P!@=*S!aEP (((((((((((((W t:'FȮ?!apCkXlD4(B+gFYZt2]J۪#1+t ?5oebj9k9\cV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltcV[6_G&C~ltG:Z}y>kW3hj ( ( ( ( ( ( (>@%{]'>6^I㢀>o+lW?WR٫((((((((((((((((((((((((((f?(zmKej|p^#%~f@N9E=_V?6hC70tc?ua?-1<[f}rAkv? A| Ey!fid o<k7~>'̌fʦıI%IeHb8#jZq].x^E{H&FLYp r16K+ w*u8*Ų ݷ30?[ 3}u]cLi ]JlNQ԰>#{a@Y)?wg]_iQ >hG5/,?P3~CA=‹'dTr(@`9R~4X.L4G{ƨL]owjı  >Syh6‰87gBPC@7miQ >]:=+*\Fwߦ .9m}JdP!AOB?0X.iQ >oQHf&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[P&g#TaϮ;5[PXQC>c>u ]Dyw(uD</=,?tOЍk QVHW;qojt1[4Jy18\8]sz w6Ҕ܇ OoYEj'*5hZ^rnqt:;j㹃 B~"H5?ev\/Ŕ8eM ]*XoM.7WT3[Hi4]ϳ+AП/H OoYEY_ g0,O+nW$x9, \$Sk- ht t̊"$ ? O$S!\aV C)p_d9N*I|OO-Ĩ$VmMȪp(FKFqwWkAП/H OoYEYM95Ğ_,2`ci<0-0B2sp<|sE{9eoH5?eAП/HIw~}.{v f)l=^mvؼ7(T*Ax V)oH5?eAП/H/=2;h2N.*ZλN܀ `1NuYBuud:n$Eн{2$ ? O$V@%{]'>6^I㢀>o+lW?WR٫((((((((((((((((((((((((((f?(zmMi&TQ`BG\@Ny 5UB%di&?Y{9&[dGە`!+Ԓ k? K2c ;R5ҭ[H՝WBΒwCiIY!jahG $9'ixuW J⮗D</ְ؉nhQEdsz w6ҺJ/(/mL#7"oB s~%V&=Ɔz2N,a+]?>T35M<\\<ӆVbl]%Uf9隍<]maGN<7tA8[N͠Y=ɼ9D[z}s] (f+ QJs$EI*D1қ g+RxZN{$H HsvƊ5Ej0 i 1)6xYPO*ſ->Ύ6B*q\*QyEBWؐ7{6c%r%_wMt}F+y{<a{me*AZPՁTwoH,/,[Ǚn`s Y@3 1<h]X쌋 &{?1r q}wؼ_eHDXC.r'1] 5ubc9GfT> iNc-ot"O#UM2 +&%HBXNN~cԞrbsVBZmRiivRٿ'-zR^x;MYnIB eC\.1l}v+KZi0t |fa"r9Fxd&.OdkȰWnAX2Okzvso[mQFɝ2xX2hة] ]GH)$'9iVWSA:[xnq5qEqr.O9FHǷ =*(I%an핬=:&8C(9ǹ9&E2oVrA x PWw[ESQEQEQEQEQEQE+rsI\7/ܾy<]imǦ],WmdG w#7۞&z.-پN(8  9nn_Qѹ}GX^3V5iSM]ۙBO:ga]R}Ph{`Pr﵆}:E>V%8[n_Qѹ}GXzR?e8mX3f3ۏz|>*o,e(#}pF.V&~u.ሳx&2 rV!KktҮKʳˆ\8s8Sr΍?:nt׺|SHed :lV2[r΍?:EVܾrά@/ܾ4Pm?:7/[r΍?:EVܾrά@/ܾ4Pm?:7/[r΍?:Er6 dqWMkW t:'FKsB($+Կh ѶW7@m*eQXQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%_"4( c7QEQEQEQEQEQEQE|JnO|mۿE}?WR٫WA@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@PXۺPfL<PXۺuԷwXn2Nx[ӲFe^xsJ.kwx0 0d`sZofeUwVB,@ ~Q֥n9Zڛ+t{o7v6߿# OXGPbMr6W:i4Z\HV<2'BIg8yD_lR+D󉏖+ч\|͜繫>Ո6d!c6iBMeXe\yxwLܴ2@ߴ67ϽgEY.Qox=Ȇ  Nk Ėˤ3[ɗlāmtty&L%(Z;KO&(lJqpG=):!f đ60N`y/f]FYڼ gcJݺԍ^ tPy2atWSN/KHZݠ+ĝ 2sŝ?Mi/( 23dn<0]kZGsw6m.[1v27ޡsD.}?uU>bx8 |5V$ixFe{c#_#ݍn83K'٤F$$o<5ېϽsxE -Fݠwd u-n5 {WHٱrc|^JOEprQmYZ2FF,qw,IN ګ20dF @qڹ[^+Z[t1pO ll3%Jn-@YМxj_as-,FpY##cd)8EKegqōIAױ'#▼ı#ˌ{qr MӕXPpF3)YV۶NYI?Y]پnceUNE)XU#pǧkEc":Aqu}n쏴1ZKܝB3(S6؜۷6vjoE%Q@Q@Q@Q@׏?WҸk? B5xEqa +@#Zb%EU k"FH&kr cav/=mQ@7 SWTG SWT] ?wy?wysE  ;  ;빢?uOt?uOuE???hpOA]Sw]OA]Sw]w4Q`8o@.@.(7 SWTG SWT]X)+)+, ?wy?wysE  ;  ;빢?uOt?uOuE???hpOA]Sw]OA]Sw]w4Q`9}‡CiBdY[f`@KHI<*uQL(((((((>6^I㢏Wwx ; _[5t7@w?Կj(((((((((((((((((((((((((((J`wZqIe+:qd%WU<;j6}WRHQHbbEU_[ e֦D&?esu#J }+k6H]c eu=Nme .!X(l&n%Z,MnmI cqZC-&u!vm${cұχZЬ6^I㢏Wwx ; _[5t7@w?Կj(((((((((((((((((((((((((((J`wN˫t!Td'qSc*Fc-i] @_ m] t5Q} 3gv13Io|4α].qWrqNw#MiL*J '!q<'Dfy4'%nz魦hZŽv'4﹇?5*Xt& }ϙVF?tv: ͭi5ṵ{#nS[{g[1E$ֱGg U 8 ;/xNm>y7[k#ۦLJqAOIoPm$6L;ZK0JQKwqxڂ)>TBoOV',2Vju( Ӈ^0?#(]Ugr+)ogb}2X4/^__?*ie<|''Gr$7{ ɿyV²ymO=Qѐ=W#(7/xs n,4Iې9)Ѽo8±Ȭ@+0qmhf# VʖfyS7.9j4! $(qRw}i5PdyHQNi'3jut7fB\(/Gҵ屴r%^0Sӌ w;w5FX@օmF|76\GutO9^30J*89 Տmmtm`71&Hg {֝agqZAndD%PAF}zN,Z}xaT/ߒ4] ҺԹETQEQEQEQEQEQEQEQEQEQEs^<\BJᮇD</=,?tOЍk QVHQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE+ӕ8lo.IeLӚ7Җ31dR,bn:oJ~Шo!Up.' xO6iV?},AڪH^603ﱛ7t^ g M K#;tA+hGJugL^\|+"gNj&O^\D<]ccbv=>2d@Lk {y޾D:D,Akk(!&Hq!|E[kny6]ZRG^qސxL*Ud{y2v;s6g "I+͸mٳ8pG3^<6kl5[,/.uS 4>b>݉< kx _ǧ]K6l4{cBa0@ZkV7']1Hvܫ#NH'k2wٮ1:@d۵IC96bTcAa "Q'l}3wQ)ɢQ fcap 208TtwiLeXok{*6DyI+-ڪąf;pH8-{Q$pRr+e{fF  ,X8\jzT/_a$l>\غMY\YfOYy䁑zb|] Xp]5;Bbz($!]]9RPxfSPm$4lPRF^Ea̻C ( (9Ȯ?!apCkW t:'FKsB($(((((((((((((((((((((((ow:(%{]'WA\sa]KKf ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9 ѷu%ēYO gLPXۺuƶWvvتp1F8[ӲFQ\f&rnq cLίW96a-vm# w`D^f#o+}&.|ausi4Dj! )3f@bI\IHYs'̄cw pa]L2ӤM4XsaHd'Cc]>Z3 <\I|ӱm9l&12nb%9bI'П5ue²3 *=JWڮ|;u,W׏ő&>rsߦ#ҭ( 0}HTri\EsGE <_\\]#o0{SՏZ<5d-p8eyqVg$AEA9E8Q7s\ZV|eE_ū_XJ'pml׶sҪ/4tTx$}Χl`V~<86qt&%A*N:t查TԣQ{lh=lsq*zf[7_ê>֚۬+baSo|-gA"_O5̒GfYR&9Oz:q\Ğ<QE&/~ۧ8Y/."[[[O! cct'<8+G c𭍝٤R8\x돮hռ)k;A"96xY;M]nl$[K; V#:X'c${ڼ1Go.I(܊w#܍;_4VݷGa(D *QNpQ`A;o\r2I`n~ezrY\\ʓ9F1ݪUB`QӌW>im-ıKXKeN=j+ĚQͧ 1}*J|{~vTW9jw۫ai-H;8fc@y㞎'p) (y"] x?_{ǟ+X\5- ((((((((((((((((((((((((Wwxow:(º]s u/- ((((((((((((((((((((((((((c*FF:P (P;ps'G!vmt>gK-SL]O *@M|H+g#ߩ'4Yw|.UEyGI%-Y*ks`cIky <>nFB#F&ԮkzhIC# r=_sRmA`-ܒT<Tk.s!whB˾h8L)5;zk6zlCqUfDr@N#S\!zl&ymT#E8cy#jO @m/P̻HCIn oAO_qsɭPˉbDѰV$CG*xUj>5 Y@xgn g6a+dP[L]GlddLbW!A8J._%SI .態'3ԕ x$NKHEC),)9>xK1E%V]A,13m` :c.+Q΅p꤂?E{%r' #ILH7H`4[6Y[E?8+v#s'g,]ʱ`ʞbmnn\+7RQnHI,lQd ؈?y˹.5bv yg Fڸ``1EZvWWPZMup! ' OTfF|^Dț,f@ރ<ӧz]lJp2x*R$ 1mdH#3[{rPmN3MF=_qsI:+^jD9STGqxX#cJnk8-m\\nH f-XO56CRn5W(LFҞ ;Wq_ ŵxxL|eg f.d/2h9"R8EUBF@6TFGJ*mHDf MY՞E]QHg5,?tOЍs<\BJᮇD</ְ؉nhQEdQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ |mۿEk@O7@w?Կj+o+lPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP3=_V?6u9 WA%ydtPXۺPfL2ݖeaՔi~²y&߽یfk=KMO8Q&9Mr߃qIRy"[s$`s]rٱs w*qi vꥂ7q jN6b8Lj 2,9'$I'O<܉P20oo̠qSN7W2%3kXn%ѩiDE*C2G>x9'P`'|6ݟ3f0GgڭNh#oX d ܁*i({[yF7˷:0Pi]ұ=똶X.-\8i?3Oob,E#2 \cQ2iG|"-19c;xUɬei!0ugcr=vR_f(^m&$ c P'm6sK<CBN$fx=~Vtm8ۉ3w@46縉 RZ2[dbw?# O` L:jiki'#_6P(?z|i6d/s3r#BF(ZB)I؋#ŀqf%U1]AtfQRXQEQEQEQEQEQEQEQEQEQExEqa +@#\?WҸk? B56"[QEY!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPkG+Yg- i_8]ጒ' 硋YҖx-<~R\d0T@ Ec;yD2 z|Ñ;oWב\1vb>Csoz5=ֹrWdhEij,M0c9k9kX}_iҞXo3MXI,vȐ8[vXI]>/sZn'KNv;]܃y{%EKpbFc#}gCkBj]Lr_K 2*ʛ.̸Wq֋vg%^ yγ@lq 0Dśxi;1kvE s&P44¤BCCbxl.$ñ;?)Wt>+?6n?=wD=Xg:ѶWwR9B#X ;Fcԑ(t^y}3Km?P%$/;HcCҵgycP2"LfNAps>?iK} Ϫr+ۜӡR0B]7!a,Wrgө&it$AŜ nsW\ntKYV)EEȮs4;ZW4hRk:\-˩YJ :s9>k +o9+ p$#] *dwEPXӨp;s*M>SEd•u{h(((y"] x?_{ǟ+X\5- ((((((((((((((((((((((((Wwxow:(º]s u/- ((((((((((((((((((((((((((c*F֞luIm>+F (u yX eX?d-$ҹ=`~{l/A&Ϳ7coV%5s4kdȞh e!^ӷ_Ix[8 @ޙnx'?s|BO"1_|<,ߌ&Dp}8 GпJBEj2!s$Oz,LO,DRB]0:gzwk^-߅}LO@02 oiȎDo{̖67kf8 ~K1''96'L}?¶Io>ͼC j,C.KyQ3xZk[MB-KN݇{+{s|B |HPe,r$Y3ͤ"|&ZOio<1cw)nQ!J@_a\xZi-5,nURSey%8$qc肺 7?)1{(>gקI`Z_4eUFO0cq5!Z)w[>r$"3q WI!F.g"f~+y"utH>OI'Nzc޳%cdQ!FFpl<`G2(Q!OūXʟâ[6nBYǜU>;뻛"J+$[nRQWSҗWDRM1={-C0am-S) #?s|BqOs__ ( E]v݌E,d$jw?(?qFܽ t%%-GI=][Ҭk9I~bFP0 =ޥ?s|B=OEA!FOEA!FOEA!F`Eqa +@#\׍ُ'?0Ҹ? B56"[QEY!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPkG+P?| ȶ\k+$2$J\</`0HʹCVcL. Ds $1[k<8뎼g-uV`G򂁵O'8CZEƯ,?gڪtBq~[O֒r[Y\aU|UwrK |8>$_)JizbC,"FKO=O,Jktf צ53̼\'ɍs]Woc24'$s`D=8.g^o o [w'9[_E1ܼ6i'XRԄ6qor玚]G[<")&"X$d 9Vs5қg|r9?)^6,Ot&kt+*ĕ8R1~|K^IyP` p7e=2vEecKr΍?:g՝CBV6;r歏[\MifG9vU8=QlH_r΍?:/iP[YM"[ȋ 풢Lрlg2xY GovES9ѭ}GFe?bX<_<| TyK[HA2WiA}GFdAhm]̲"bS%L݊/me[W)q,A\G*~ys"m?:7/1NjP Tl"49c)Tl H+=wv-~ 䓱}GF,KsmmqnΡmr 3ֲ5Ŧ]m$pZʯ'K#HE5/ܾB&/\>wiQfʌ?2pGaHd>*Xw27t[pw\ܾrΛ_jfMWh9 :HVܾrά@/ܾ4Pm?:7/[r΍?:EVܾrά@/ܾ4Pm?:7/؃ᓃ/]6 x?_{ǟ+X\5- ((((((((((((((((((((((((Wwxow:(º]s u/- ((((((((((((((((((((((((((c*FF"`0:d&*FӬ.ŕ|KvS] 2vLVRݖ6+>EGWT-)3nHa1qZ^#\m%фHb.T%fĂJtԭ I][*i6i͒?;re${桺y)A䘮$ |jb5na.0KnF G nu>Eyuy n31L*Ly 91UqC}Oikd/4">͙ȅāFeqGSkUuvk|g[Afi6|7g*֘n$[cx]d"?(brhvx7kh-`8m[~WGduҷ@V@qFJHM4_<>N?^C8?ei YI.%7®7gF:U ynnZCM{: z3BOS`I6}RV]k5XryB'$$sUw{W41ڨrVC{ҳ-l-'[>fw9{_IY8,ggF7L?xܓI(wՖJ(@QEQEQEQEQEQEs^<\BJᮇD</=,?tOЍk QVHQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE+ 8sެVׅܐ*?$\Iw}FKBiAYFEh}^E<95a<_{ MBϐ n$3$6^I㢏Wwx ; _[5t7@w?Կj(((((((((((((((((((((((((((*G]֮dXmŕL/@e!Qde(v ]=#.0(r1ؒ==?s|B3ۖ4K+m$ې?n] t)-bA\L<,A bQׯ7?(?Txv2+e{vqϑtN/]$vil)mٍ>>tQOD.u ~r}?tqƂPuSɏ=w 1X ][T}8<woF4u?s|B`e=w/۟nSw/ܾnQ!Mܾr΀!F7r΍?:v?:7/۟nSw/ܾnQ!Mܾr΀!F7r΍?:v?:7/۟nSw/ܾnQ!Mܾr΀!F7r΍?:v?:7/۟nSw/ܾnQ!Mܾr΀!F7r΍?:n|0A9]. x?_g *OЍk QVHQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE+UA;MnQ|xܻP6 r:9R;⣗ZmY]=YchxɗcZxgK !X]U We rYm#"ƙ$B9ngKJ{g:qUͪљz5KSڥM2tڊ̻XHAMo{ =`h?&7ISrogoonN̤ĖI-Z8b nnӕsҒkgљxJ $Y'0rZ@G*ԗ6$aQ4y_,I# iё1y_~^O*]r FV@zϭAi3uo=Wo UmFvӖ FHO+$ы+[D)d9fi=K8,2] .3F@,E9.ys*ɸ L ;@{&tvX񄰽2dU@ `\541os$ǀw$*a&$܄$RNzd2H)ʴ$=qѽ-.QRXQEQEQEQEQEQEQEQEQEQEQEQExEqa +@#\?WҸk? B56"[QEY!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPkG+@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!Vn>@tCI[o?J  ?%o+]a+7G.߈0䕿诐?vC$~!VnWwx?[ڵ/KSv 8P?pyparsing2-2.4.7/docs/_static/pyparsingClassDiagram.png000066400000000000000000004240521365333160500232110ustar00rootroot00000000000000PNG  IHDR{ 7 IDATx]lSg/7RF}HCA4g"v.HL!RIDEQgii*pa*(&QIR&$e.M9)PBNHH_ey~T鄵dN7zADDDDDDˉ2)13 """"Z ?+цLh1#"""""ZnĈ[vm,n҆8mTjZ,iqDDDDDiČ 8^>Ըv8':hREcWOt>gq`FDDDD̴3X'9#UR=fAX,{,rJK+:,A!>4@#3%G 3/ު+몽=k3a}nKlӚݑۘ-?LLnPj8}i\$j3Oܾ2Wxw(}D91#t<\}o.6zᰫ.7.-|Չ[ũ.9͆Թ6>iQn5Q4hhdbGf$!:טb6oL_Je,3 =&b 4fov"M[eVOgֆDW4v&LÈVF&f,͉͂nB)D.Fj4jIJJ43{{{D,2Εmqc'ު|gcou]1L<0 #""""ZAZO'ݶ; so+a>d$j%]ϡjz%A05`TzZ3ٙr3h 3OWc~ qgŽj )N`i\nvqы )Uhd&FDDDDAЭl>0/drϡLS4h-oMVDDDDD+NuegJi*Ll`FDDDDz0* l*j&ƿ->HDDDDDܘ-7fbDDDDDDˍrc&FDDDDDܘ-7fbDDDDDDˍrc&FDDDDDܘ-7fbDDDDDDˍrc&FDDDDDt+ђaC W:"0#uk旿tDtz}C "31"""Z?w*+ Qf|Oh1#"""""ZnĈ31"""""Lh1#"""""ZnĈ31"""""LOCS^I_տsEf MDD31""Zsc}旿 iru3,L/jBa)&"-23WZj-kͯ^׿jݯ^\$;yEkM6[k4\ܯFDD47ĈhFWCm0+ūqSMi:gzUR \j5ǜa}%Pr9F˔ԾC_3|nÝ\ץJG/Gf~_IDDkn " خ}oKZsӀ݇t@}CkPquhRAk9EQQWT@E9?nҡ~H@ tn2a&FDDKOɈx/nfg.S>W{}&/k ć;c/kŷO<D(?S0Q ܉>!ID =Xt]̈́p8eߤj o6{9g^ѐD0~(\ik?7=/󷞆?Th7Ra旿tDtz]-+?W+V4l -}}`۹a6kbloiXfsIu&M&5m]_b5<4 ?6""""ZrZk3k׶ {,f kCn=A'X&}Ssւ7͂K\B٥<@i#]?X>YP7&YEio?-͟sY`TRǚTAFC={ מpx/|mF8'3ft#0o?62yt}oC}Gw+qE5~d9q.= s6Åp$dz襴L-(=Ƹ O|ԌMYYsTc_\pz{h!Bk5^9w(]~{mc5|ӫXOߓ[&fj =[̇;KI͇>ºG2&!f7] K|m1eHcFxhttȞX꣱<7!o'O?c`1Ӏ)^xdsP9WFDDDD dlY`|O9UϸM_ݩ=",z xG#Uji()r㛞5<.iM&'̅;"+zb3:G Rb˴p""""ZLVs[BUeOv&>2F&&e,{b@X ڷVy)ذu/i60VnF_%}IQlyӘi9Uu='4PK[:HǔgU_2=s%J0ClNDDDDKO_Xi|o,V穵WwF\%VslDDDD+ckbD>mL)9Y,`[ N$htz}C Z-hD"""""LhL7i@/Nꗘr»#ҊM,2T[5k3, ȳ&""""31տ|yսsWpcJL\b;{#_T DDD^Ksx%liL!+Wb8YN17aSz}Β0rk3"7Gd?)W|ouˤOqmQ7iyu"R7ktc'{)}=uo{Wmoem?4^97%L6<oڡ~j{s[#Tn~t}o*_V^6z~[.޾JmL"""Zc3t/BgߏOu3AtEv+ :xZ|j0q|Q&qwǣS&zpUiz7^(9.12E"?EWtEq$NPkޔA2nTҭS7i]ZXӉa%;2滶 F/B+Fv8P6= ,R|]mTKjУ<{mFk}Ap _آ2R=+a^͇>΢[_xO*L/5::; g/Ph:4IHjӡ _P͓]ƕ]MjG^62MlwGCӡY-5AcPh:d-ćwk=YKwK?N|c;G5BĘa&L7O97i}ZX [k̥Leԣ'r[~=T#ON&4c om4܊*W[/Uֽd,zq]}P΢ۢW&"Zݩa9ODDDPx|v18*[2Kq-v%3nO3ᆳy#]Vs꓏oye7ZjHk{w9 qj&`H:8έ}^SlOfդ a5vhdbW>ى'>{>ĸv;?z:Ɔ-u=ʕ`c-O,#^,ӳ:כFXjyO 4o%/mg*R)smC93 GG_"8t"ڰײ)R tq7n=pV`B!HvWpw[nFQh0n=2lfM#Qi:zuL򝒦'M?0NZۿ4󦶌ȰQ mH`p~7 ?m}yOܞVK7y7=7h:*ck$ur|Κ[6WL¦@Dْm>rޚWW|0Q]ɂޚ㓟H tH^X]kKföM+;^,gaw;79O@c_ T~CopJG /d? v56NZ(78pcy/RZfD7jĝ!pXp8ґۢ3V|E$UF_|uqr s&+}6+N,pFY!]#-9{Sm_v4皿-ԋ~|[V~0Wh,!4C>G᳀#:y:q FD׉:^L]z9U,JVC[Ntnz˰F<hzkWr_jSټW#H5#zklm5.~=̀tt@/堷&2l(S#"ߞ ݽ~ΘhUa&FD?::FO{{Fr\_SᘑgSMOjPt%T;#5`(pb}bӔ_j;kpH3RWR/~W]%)SU >m`${:]w?-jSR@x> kMvikbej`wáYJ k2Ç?wæzk=1"+Oψs|:1ehӉ@%`"0,WIи@ݰT){egT5:> uj)xs'QNAd]70r'?2`+S_5NNa`+Bd`W~ 1uVJ߲Zn`R-Uj V=Ĉ)x-`2vO,ɫ!u\wPXըfb.G^6YautHTd͌ p208g^bL 'g+_h x_?l|ǜq,DDD͚ܖo9g\ γYPxyֆ 2, W&"7)AvN`m|ٹzτM#hi6(`w+B[zl+YU%;\Z u4劣ρK!2?{OtT*ڊīʵ~-(a#rTEiCPd*L)PlЏ@\:؋ī֒S'hQaP\aͲ6kDpW?5߻~(.1[q{`Lg,9uu-h>;b=5s@z+ 4z`k2}|e</HTK.'%^,>jxv Zg';|~f+BO|o-o,?K:LF{&-o- }9pթ1%4O$RI: )=ߓ%n˞w+015@軽MWEaCq_ZkK:D*?Ӱպ4k0=!{߶o{C9~b2xw:ތgEiOpNx\=FАjK['ݏ̘fd7 bDLjsVڰ(t#PO9z}^j@(9w[w36*^jUGts]Ɓ.( :xZ|j)0oRgX{0تIJ ؜SbWB|+˅$o\6o0n9<9sj31+_EO>zA$mVqCB?ztOYb#i?>h>;F\͕ƢWt_nvEQ$""Z5Z.nCQwѷCg}z MѱuT-mu(Ҭ/Hkhy"L?l{м6*aKXf7>ʖ' :gݒcKM؈Mp GP ׾7#ٞaK95/ 9ԭHAH擦b=5$5oloeb35w}ysA/s{G+¯h7{"4{ì4/<M<Ȱք#׉h z[=ٿ( V@°tE"-酲59EbG ~5xJ38y#T(N9ꭓ7BQ9 ґG${b3ImŇnFK4=})GKp/i[֞!/ ӯY{X'?V/h~2֤4!uhS쉈}{h1EkW$R>&tƗz8~*[WT#tֲدy=rKm:^'l^? ;?:G`]#y[a@[AA-3VV5ۭeQ~f0ZqkD"KmE3.UMAN٘t@PنLjuN~"uGٿhWi<(.{DF/i(I":r2eۅJT;Q";.]>p/]8kpHJ[KND!=?,q0Ԗ1n`, IDATQ#-\ɃlVWOi @K:]˶ cMvDDS҈:sE4ĈhC(Oψs|:$*)twB`g'F M|)NE_ ʟㅩU m:(2^UO}}v]کq*L{؈Ԏq–ʼ7-4aDDD-fbDDilǟ'Ei/- ɻQDTV.8vS(}0&m)W%˫!U#q<@EaUcKe~)MK`v ""k=1ߤEx&gl"'}J柖#$o,z L4 rP ';͈rslCgh)myծ"%藺Pޒ{{)T&bN?kp(W Zv|~TOleeX2tubN0j-Ww,$`W9jQNbgL^-8""l(7߻WmOs-xv Zg'tѯ?:IYWu 7?ux0jV>Rg[[Ax&/ל VƢi\ˇ^\WVu>籲G{\|t.~Qs&sn|.rjlD~Mw|})+=#Ma˞WDDkQZ=גڕYS^S,;>J꧳i[ZxJ -_O(0otŀÖpxO86*yw:l+<ƅg<%hjx oQ[v>>`?U^}c<։g?;_6nˬ(Fܹ;4?o۰2Զ"):g_˫nsT$ml+>GwSz;mUލ]3w[+]ݓ \â>u0qy{Ro8tԀS[O `kN @xX+c $O1H:5Ӛ&ݦũ&Ji"p+n[F֞.”jήeeGk֦ڬL?6 =ScўE E;7O!aEWToKUQrpVS4v_\FG3=+P}G/?|R[':_=m/=ym76x4h ŕFm:M ѥU>v=Cւ{*jLBӡ8ztxk=4}q!%ֻ4mݑ?z~AaNe"~@JSʘ|nA5H)DO`I(%8js\IBYVdo C7g0akvԀrn涴LLaW+Z; xO `,ȣ_,Z, m1&VNERe݋' /gܧ,|e=klz^d9q۫՝jM,fJ-(}[IO G$3έ}^SlQ~w^oq@'`8:Ƥq|ߺդ|X_tQN A1q8e `(oP.F'C6em[' >!ԹgOad[ڷ J nQ9:^k. Q W>+u$``|˼6*Ös{r3ԺSߓxuPbEg*r$㛮g3p|/zΜ,Ɔ:L#-b\~sMp%iJӡcWc[; +bj&4p5HZ-e: wF%\̞al&_Lz|3:VbkʚF<{nr.s.f=sƆ7<^̟MOjw㛞𛉗^jX5 u~$É+e=:h8"~mN zk_1'zOYrh,-%IZr7_ii,Gv͏]8 8y˱Xɏ?SԿ>ڔpJ{Z$8{^:y#tfh`[Y숳2ɓZ51=s㛡@O0F:dO>z%2GUt#kJFc[H=D1]S^}tYfq"k{"̴O31Z%6)sqUο䲨rtİ'ӜvO'"Zדzs[[r%6%JW9?2x+Km56SzNGӫE2@neEELζADk3"elK(g5娃/Pm`ĈhCP3r_]'HSt9kpH3REM`\R丄 }F2ѫ0}{Fex^-TNK3)S:V }Lzڙjg)MvۖƚJ|ה v=OK4>].vWM]΅ nj=cGOh5{f_,(udͽ'FD4qnO'NNUS Єqy.CNT'-T?l.3BiS K$*),?P*ԣx,K+2+?2K߲Zn`2*Uje1""9aMHPXըfnMyKDmQ/~T~(ܔ;g`kZ p2R|>9[Dca;f .'rkÚ&FDIiÍ,T?qDM)mvR*UjWMUgJ6ծ"*l{egŠWlcG 2VwGB;.*;?9e9rԐ({<;'7%?;hXg'[?wj_>t'eĎ:e;,27CI,A2U͓ߍ[?-~]3NO*/iAo缔3/BxF_[8>Q:%_vwXbKz8~)Ɋm O_A<Ş(hqfM).[L[ckI,dH 9/t[N[jW4cf k$o:rdi֕vxz9VV<É{XU{njODkV} pZWцtbi׉sz/]pH3[^.sI3{?3R)jgv֠t3 'axMբ3Ew\b.޳PKJޢ 3RMSa#̉&f;'-Rd wHv㤵-5(]ph$nҜF]u־%o=} 5oth^&Zx=mqO'Nw)]J,Uj}ƔW&ɀAc.3PexMg~ūqʏH [>Kz0ehӉ@%5jt|2lqIUel)ysSr<omϫje1""Zgd(|89\`Ӈ4:FKKCLƺW5&5l Ay[lF..$˫!U#q<@E~)3ƙOx>ه̯shw~Nvۉ.fbD6(UDM mUiG׉*L)?qFnM v2] x-YO\ZaOďjx5FǍHKx2=5xvO0Q <sb.oSSv""5g'}viX.+m֌ {v.#[s<3Zfe\.b/} ǏKp+^w 0=_ى+g'ͮ@qdCľέ}^SlO9][oP?\n8}[{j*V*6MP#8 V#㵽;}m1ccӸaC2 0XcS۽36ѪQPk[网#  Nγ!-ГS؄CґPoE"St(؅k_!eL I-VY+CNٚ;!ʼô1#"ZzAoXS#M#-[~"M:r0^lN/Ei|LLVH4쮙Mْќ#%Ɣ-Lu:-VMT q4Ňjͤit;Fig5eR:)O>#{b}+Ѧ>G:zf/ CU_aoGS4"[-Y߫g7v-v&Ms6}f8U"[ލ{NƏ &Cсv\niǷ zkplcy,ίO7󒰥Uu6.pZS޵(/.^$1dw[q+k8O_A<Ş(awmI,΄"_jDxz9,2W[ckI(dEkng ʊg8>^7H[=*aG{GDnM︚ǑhV:"ڊ:e]'Tc:'?Y2ݾ{D)\T}ڙvbTbn&'J FUN`@ Le6hݯ#jxUyc.ʖU<ݰ dEG/;{D~Z|? ~:=#{hc&FDBxzFӉU8g'PZ2D{egT5:> uAIMx25ˏ*^S(uO(Jڥv쬯7ĸUǚE Єq`"0LR՛|0`·tuKE**MU $W>~FҒvqV;\SU06}JP]Wl⌃DMJ ZeuF҆! ySuWÎyzȆKFϾDZ2ىD>i8'+ubLcgPiYg'fVى =pJt]imى+g'fYӵ uw:^':* SSGD߻,(/\NGDL(҆-W< :g[ݢհWD 9]aQT>|nxUrjJ`੃őp5!Q<,sZ3L@a#­D/ 0#"""Zjӡ _VӨe:ֻ?yrkXk8Y woma 4;-l)WIQ1#"""ZP]p ꓏dzRݡn'\?՘y:v(Pnx^QFĈ K1kJm j zҕ GP Y2n=IND L69/ Dns$Ao(;"1"&#;.g3"ҝJ}`hS^wl=0] kq^Alo->{k^]_s8kl=ň O'IȩӍ8o=,y IDAT uCo4#-N7 =zZ?V)skU)mǭґt/ N鈁'ݯc<~{" FZziӮ妌dg}uZ5Km:^2ulӉˑ[5ʹAc.Rƒ[[Xy/;ubRӘh2Pex -~@Ad3hF!Rx5҃Q滳Dd6&vq4"ZDĈh#(iTUg: f55>;pI3z6O(ϴzO?>h81#;~{FRHOԖF<}fKdS'hYwUډ&sDYe^Ύtr@{-LJ.~8l C,vV3V~ MBӡ块hЭtDD`EtteϺfO~ER ?|p.1ZU;"ӡ-[&OnU:5'-M@00&'g};-Ըc~'ڕ,nS~LO (7"""ZNFP~[cFk4_؝UMlH5uquWY_}蘑g$ ś?O.uϔ}Q작gJ|ϔ$|ϔWG 4&R6瓈IYni|o_T8}ŏP+lꭠ9D4%:,1Ĉhcx>j`j Ŭz'U&X>q(%u Xyد,@ub^Gɀ?@<ܒBs>[JPS<bHsyiKPΥ?u"[퉷'~$3"HDىs$;=eKpV\& zkwc N$ZWV=jg'-6xv"Q:#>e92nkym aD"KDÚO њ'6W bMl&FDDDDDK$b&FDDD47|rKm:^eX(K^B-g?h"M%][ИS',D'lIQhΑy[c0"ZĈh#&X AN٘gG.x$+i:u1i>ʼ1KߩԖ ޸31" 3"0쮙|'LQ`.3BiЦKJk=^!P$0guFBi.P.udg}!Mdx-ynS^0Lw)aXzd`W~n 3[[>.7ߝ٤H-xDJ5=t{B|=N$"JٗGZߙM9G%q"O ;|(aDX#FW&b}RZ)6R)UQi);\Z g.Ru6MESGj GR )SmUMȍ# Ole qFZlIԡvwg6ōR?T O2Ƽ:؋īaG,`րDN׉k 1=qDDDX#ijbDְ&F4' Ĉ !zL("L(0"ZĈl5b6#-[VI^r9حi֚f]EYVbaGŖI1 #eL6IBdsUe?'QKʩ#;i6H3s=#uhS1R:E&""`&FDưF(eHJvF S~֦rlT.{84`"uus%xzHku^ן>Y*r++HKD E<ׯ1W&6u٦:>}&""ڐцٻF^^n^ͦ&At' 3ta.VA1!6,$- |q, .Fq.  4qZL3\E"6Ŧ= "^7)QMC//c=RBr@Vƒ˅1%)-iX@@S^ʮJL!_ZkJ|SB ְ5%o]e([!uݷܾBuRK w: 1}>kwޯe05Qmk%3,sGEEtlԘՍ}kJ\ `-ɦ]^Y"u31"lҜĵkJ|C2wc _m=$n8lzg(IҨP(^ 2kCrrGEủ\!/݊l}txKw .c&FDVr~fOMCz uŻĵ~ 뀞n/|lB>^-WΦe0k\UAN*_9fѝq[#1r:P~+ɚ]YLÈ^@DEΩc/yߝzz/L k5yc^i} kd""":31" 2A/̩ Ghg*5* rXR} ߜ\ӥ,+"<]ƽB޽"ѐ VjǩȨ\{;R] Tnȧl/ѕa2zy%tWR'E{l]kfLQjb1)q-d}i(}mRZ~ZO<Ky/N #47iFf*YȺr`:)%F uoWrN=b+d lHF60o]3"vKBtU.Dt 2A) B/Q,;=3C}0OFД) ̺f,@{yGH94ݻIfucP#hF[NrI}nX:&sHǚ.d_"c}DW0"lX#1@kJ|Z\ZwTKJxu̿SB>\;B4S͗ ɦRNY591p)ߵgkjc:*_rcbČ_~|=3iAs/aT،'Kt1 #ˆ51" ϶1 u#W{x,g:0q@{CR@X08Ýdr5xÀ6s#.s[KSܞXQ27iLfL1tLH kڹD8:- Φ;|[ ""DtL&ͫFۻO-z?wY"d[wU[t4NL_(}Y@L&ĈZl&ű^GQayw"\M,`fiQǮ?7X#51"rjb .-;v54.9fbDDDt݈*2 #K]73DD31"1 _4 0*D1R:wpS/JY1#pl:6n?x~YsePpaL29>\=odyeJ< DDDtM J~aNM.?TG;Yc>%k\bO RLh3FC~]5V|f|ߝ߬eW[ńONZVVddwɶ[LnǼ^+n)qm"^[ݒ˅1%)-)iOSJh6_u`tdF15%]%}KqMrͣwl#Tm瘯^tY~{kKFIar5wXSZksl'LD7A&(#$UY> L%.J<ƃd2 ,Wm}f R07 ZxxOF!?otqaynGuLANˮ)Ԇ]> z`O<)ňzxd)F&-uKnI^! uu3=!$K~SB1)f"?{P(&|p7Jpఐ:fןF;=>IeTJ2I{M "" {g:K$o8+gb6iI/ǑCm#.<ҋ]8[j?4N/GG5`+Tk\LP.l#ĢS[hVso4eL$ʞN7BOD׏d޼:阯0^WҊ| 2%V_Ʉ[w*OsBDt;v]B@RB]'t51"jX#51"""""`&FDDDDDmĈ[VLk9%$H~O0m\TDU&(LTJդ m _yRA rt0ZљODDtFĈ&$bk_kJ\S>d69__Z $){5+6tOl?WOlvAy;'W]_i 5/茘Ֆhc]JN jQ 6TxR)y227i GVqʥb'|u#Vd}X0Z)ŗka11֮^FC+jo%Hh;z>NFWys'%"":uCN .TalVSb)<]5ŬL; h(OW@qm9SB?"1%>ҊwP,Ao'fl@gfP֍q٬z#t"f x8,n"zM%>Q0kP v?(wqGh:(>؏Qlj4t$B<'YSB j>cJTFkDDt1#&i '`:}#C3~?H&²g5$Ha~z pn>XD0q`W5) :1N-V3B60WB~i]xD.qPBS&Vn19$ zlSA6FNv=!""O'an(qm_:nߐ9%3ƷwK]:&S#c 7hDn-՛fl$ˁ)՘4[ՌVBmc?v K{sArh'& 9643!볋׏Oyǒ?Y 80#o!H WˮV\i5~Oj~Ω^ٛ{Rl,Uf)ؓAKՇ'Q #tttH.d޼uo^#ڐ`)S"J+ȱ1<̞It­;-vDD=b2x:I3A Rxy2пy_"5E5""51"wMZbM ĈzQ1#"""""6fbDDg*b'Ohئu:;4R:kLPg9b>)5~nfbDt\Ljln_zc]kΑPZ ƺuFyGq+m=Q#fbDDmcz+5Z.|E3h8:uxx/W _r"bAJ}Xlٽ&E 2{r"fvLx2lULDDXyur⠳Oc&FD7BN _SAwRSSŸgJ9%` 녯}z0 q6 }?heC4\.)qMo!L1<]s뽆`=Qk{f FRv|VLgEvSIc>,?ȽH }Cyu%O~""kNuDD`4 H|A2<%8[qoTS~xG0Ct  c W˃u!/pJ{YC ,o-60i9,\))`}I,FDD7kbDDmXJ\[|헰xrBu{kR*[@f3 vnA/Wl~k\jwWs<9%3FMnM8,TrL7;"p| 巌dMhSjFICqE/H[r(>VedX0R9iIC{]7jP* I3^2׀-OmSb53#}F Wָ\?hq.&ÖCyuDDD7d2:"d27zu dΘ2%"?ރԄ[wU[t4"L&N$",lv@5f o^؝O)|m|9.kbDt=]ŚkbDtU&FDDDDDĈQ1#":#UI>/B+61ߙoQLzb$mvhhYu;?2/` r g K83'"<1"I1_asO'ʃfPL G\PJb+8Ǚf( cCٝ%g\s|tο=4"aMtL//Fh GV.Oآ=eJ1+UҊՀfs9k?ʌ-ODt 1#usNM.:ZvDD31"lҜĵkJG~A?~l@O?|[he׺xX3Drl`_1ѯe.Dq"'r pk+kC4+etu.{r>o5@cحKXN6+&[O_Ԕj-wGL:PԸNu|˾4 cq+; Qr:1ڭndKKkBVфK '3x-ODt-Dt86U$ȡ"$-9w{2@2,2,-HsO Мޝ@`PZD6ifB(_)|_ LUB]lj;4(׶M)<(c?R.h8RNծ֗@5`Is[FFHhd+ƥjs2A8SsKTͅEA?X4]K#tttH.d޼uSB1!LP{0WZD:wA7q 6sU=8&=c&FDUĀҊxm3DŻػ / p.L fbDtm]L΅]&w """""6fbDDDDDDLۘ$r ?V6GOhP#bj,jܱI5DE3w)%Jcl-4>i9Օ,5 xz-f~:DDDc&FD7.ln_VZ{.1l&-eng[!^<ֵ%8,n;ۚeRCہ`%}=SBB, rL94OFzbk!A!_ه8+˩vT MTZbAcWͺ0luVˬǗ'U"""jLn#Sˁٹcb\S3}Z׾u=8>؟u4ҋWCRbbz -]?T[ue([!uݷܾBu@|DbԮ@r05%p0 4YL9] ۆd2AigL=&]~­;-vdD""̓s9j,O!łF|ߝ߬ 2lV15.jÉnfbDt#䔐 95$'15_ZkJ|SB ְ5%o]!gzcj hJ\SKu=I.Ɣ0ķϊhXo7P;cXr=ӧ>5>0F ۈk7U[sGEtlXE| T:~kIi.Dt#ؤ9M1_a|_kGg:$Le-t ΆЎl?YϬ!A ^ uݷT{f!?onaCvXo7k<` e:<띖܎:e똂]SnąBu-'bDDDŚQ`%il)ֹsj $c5'coo-bȝ0LlVjVWftgwz\lHT=9_~f|"""kbDt86kYmQVɡ"U)9w{2.l@NF\xdpxD6iI}H̵1ؐgrMDc\5.@&(g(F\˩E38R<flܖsu|"""d2:"d27zřVߥP/mHf vƔ)c]~­;-vDD=b2x:2O7q}43$ H?_sFDDDƚ]OW&FDg]31"""""nc&FDDDDDmĈHU9ńO ?rTjI1ĩ ۬yؠRFZ)%Lq33瘉Mr1).]~y~sO~:Bq*S;J_Z $3L[ܤ ijs 9b.;fbDDmcz+:_B4@#{L sJ');, }03;<\^rJRs[N]-5SL䐯LW|fȡJ1'5Kͽ.dj닇V1;S"tI]BĈF0҉95$'15_ZkJ|SB ְQg[,Nv=(&Дz)S8vX9exmpLbk\s[>NKF[Lϊaـ qm}ׅL F\ڬ1j~]ևf^>Z5wXSZk?F""KJuDD`4 H| I}#C3~?H&²ggMӻ XϬ!A ^z"t°.D#'f^aq[y!+eהK\(-60q ^ 1uG܎:e똂4lyó!,'bDDtE&FDԆŻĵ~ g(^iJ\.cƨ=Ͱ靈'Pe_}8˗ ѝlH⤦Ԗߺ[hf76.bj8,TrL7;ڭڇMDDt&FD7sjyr+&@EQ oɡ[h6#5 {ChK8K81>sUږ2)eGpNmxh`_?RL* ʞ6`KgLP.m#Ģfܖi&""LGGG&ב]$ɤy(.Ҋ| ғpjY[wU[t4"L&N$"!̓ iLLDDty&FDkbD7kbDtU&FDDDDDĈQ1#":#UI>/TD3g,bj _]jL-aM7h1Ōj4DDDW31"I.&Ue6/b/eϗ3 [n~9^;paͳ;敎>~7HDDte1#"j#\zJUh8:>;jh_7Z1+wlTYJaSDUZdظr>,V=|rHbjBULQ3,O!ł*ͺ 3AV]o15:uCN .ϫ2@ e439ȽH }0gKU.GV=\DDDW 31"lҜ&阯0~bw-#D3[O3kH%c[5ǓC~8T&Ƙ8!1ze+/H~AtnѴJOqcf~:DDD 31"I.7~]fsV|y!uE  pkk17y7wE&(5?9<3Zi%\k|ur*+s|bzN;#Q1#"j#ӋHzޢFP`4cq2y{xhePS=W dddm*ݛG7)`큜jfihSwg&-z[d$>o<@!!ZR}XjJ!_DY(4jVf]~ uEceˉs ""zэ`*sjr9P=w,5_ZkJ|SB ְ5%o]!gX2Y}{(dQ:Z'1%)%>9&W-*.O3kxmبmyguy֒ ""э`4%DtXSZw= dz 'HBm"]6Kj0[6ۈ 6g#m؛G7$_GSP9cƨ\h:o\QZ ^( VT8;gffuX8|嘙oFwvEokhԎ_~f|""KFuDD]pvVG Ӌ_P| ؏xm#.,-a"prͭ}Ic6|ͤMZԻx75GW:{i%\k-tOl?W; ] |3Qkj4C$jٷ?FrϺL8cxڬń/A-ThG9%dBhy5ȡCK}VNZ'#smԟ J+rQ?7k qgL;4 x2_toc'""ĈF0ͩ ;Y=M Rv|LgEb\S3}%q6 CRtlBX?gh $ cJ\S{@\S[gEPq:QfZGUu]09;)qM` 4y1ۈk7☯M.z:""k6iNSZHL5%x<mxY/Hz`<`}# vˡ]nTLj|u7N'ƷnH +#T +uyH+-<ۘtDDD31"Nc5mYJ\[|헕RrHM$ ٢ӎev뀞ͤqUg 'ԫ.v:""#9QL&Ĉ4 "5VL, )qM /e[)OaMk%Mـ|#aMkJ|ߺLXoCF\|1ڎ /5&o!ZDEDDD';э`4 H|jvYl.[g֐ KU) IDATzdC.3XO<dxf1t)P\/eה %8GEDDDƚQ;ftgwz\rJ1fցQl&c{٭i&) lźI嫺lPSjmFkbDt86'vSf5%V/HDكVeG@z   3kÃsj+Ujd2:"d27ziV)NAuG\_EwOc!":dD"<TJH?_sĈz51":Ĉ`Mu31"""""n]o7N]_zEzѩt"Q1#"""""6fbDDooz]6Ĉު};&J?/C;yeS|_mMQկ`BM4K7>ӋOwm31"QgGO}_~w[wYQ } ?M|~嗍m ;jo0J(~#_/~b/>EO_{~t;feDDD731"9R?JG_ˡT%ajLPS-&/ޝ̵5f:+ovޑGŻ#R1Q7':N?JuMкYSmѸ}#""jmu[@.ţ4W1x۵5%){V2c4 pa~v>EݩOO@+oRv}x1{r ,7h߈b&FDF:&H~ASBjo@1CH-̓I h8:䔐O RLE&(#Vd}X0Z)ŗka> }w~3]L]e6vI6nZg}ńW52ძ.͞T;qO'эPsPPxLM%_>v+s6 G @@S@iE~RtL2gCiE~Y;in(7; hq6 g3(I{Zd6s֍p5wXLP} 9&`q~r21?%XVU3ݸ葨P%Μtqӎ> *=͂jh6VAW[5^Ӿ\IU6э`4D] tx<ԆQ" v v_c^Ԩߧ7)G\z1+͍oNTZ}7}fm =8ɀ\uw2AHoMk;rr6ݞT:ӉDDmXJ\[|헹~l4cMwtODŽ3~d3vnAe6`Wjc;ߘpXHQ?o+9%v7ju3.+,^;?(-op9MpvVG 6 CSy⃅Gzf@@_ .LVelO&L,#0ņ̹r욪i řWycPo):rJk79ˆsOꛍj?jmU{2k6q--؏XFDDt #gec2xnQ?WnʯkWH5mH暛VxQcw,;K㫻'3HDWdD"K&Ll/Hq̓s8Řkn}cwq̟giѾŚ]Rנ}Z+Dt?DtU&FTp{4_qDDDDDL xy!:>DDDDD31O}i'_@a;­;HT~#ܺBBDDDD31\߀<`?' ͫ~Orꏿy5'୿؍B{j??$/7XbĈ6FO__.^_~YY^U[ԗ[' !pNC """jQEA5tv$K_~%_ݤ?1j.JY跄V3bm>$*_n_ɏznE~+gm??x?~+,7';Z#Q=>O.)> fݩ*jo^?U>)AL&SCɤ`b&vzE*ܺv7;(&:Wd2{bDD+2nxwtaQUT9nsĈ.t7;(fb&FDDDDDmĈήximwG%31"""""nc&FDDDDDmĈΨxwG%Ş./֝^@DDDDV0ˋϙ9-G!u7;1"D" CV] >XR|GUOr-۴~2rd% fbDDDDDזW/敶[vߵ~D[++E/V bvfbDDv*wG%Qo= JU-dS x~ VޫTR6V>ZD'O~IkMns'~kv^O{bDDgou7;(/7?5'?_DW/ ꏿT_FUor/6 +}o @k[n;Zdb~WD1'MӭD_]H[?o: INk*Rl#ZE#2Cls#MZ$ǢTh;Hq!XuBdv" mJ"i[7;1ok^H^kwbZD&o|[bщlQTgG3_J(? }ߦ`b_@8znzn{&96.h,!vP*V|٭FM=\8偔a G'_)} 3҇ #K"÷Vo-Z6sP 34^0^1znYFbATB_|$[xxH?u@5ZsjMh[XwyymGX5BquZS˃LO*Ǜ=fNi&ttC\p(?6 4x# י/%,~)D]KR1'FDDDDDmĈQ1#"""""6FbDDDDDDH(e#1"""""lSDDDDDH KĈr@ud7xD"""""lc$FDDDDDmĈQ1#"""""6FbDDDDDDH(e#1"""""lc$FDDDDT:V?^P{VEτx괧#z&t`$FDDDDV&lޝ >Ļ5u޴>φ98rƦWROQ^8kSj\!i斾- FbDDDDDI:6{KE[/wE۠{\B,sT]JLw&Nπ>1Nҁ̕y]C VI'S"-=u;@6O/B: """"*u!@M~y{u6ӆ8-cʄozwP>';[-? :lur L#t[2as5Ťc. ]y(#,lJXv&<-/^O7oiOvVxT(46N#1""""3%;2a4# 6 wk]r8tIkv b퓼Sѵu`uk8!6@{eY@tE5cZ(>T]oFw6-}XHVEgXN$""""*@+bQjS _-!;-ީ=׈/8Ņה;ПA I=5A,L dYqosrwLlaN(?;T(k4ڧ->sc{4MwGV}SJ~aeT!I:PM˯Sy`2W&ROM`[i^y6p%E4]ޞJuOT*iw;׽(0jNCsNRrAeّ̗JٿꈞӴؑŊ.~+ yFҫvߵT*G'И?OwCtkv8:Ը5Gʞvwaر1'FDDDDDmĈ.QRktB܉DDDD)yRuG3N񀇈xQ1#:.9!&/۱,8:Z>}#:JuΪNDDT#*|?(fDDTKߥmFRoi"ʉlFL.'FDDDDDmĈ+q(uYpt"Jyb"X"*MĈNQѻ:~R %}8"(QSTHDt#""i+9c 0'FtJuH'&b$FtzHDya$FDD"4u_% Ҭ69&3,\<”7}bDQ:4 Dl}t];O%iϡWҐxBжº Zu.sbDGH{O;H" %7-61\tjtΩFn -rZSkOcͦؔ"JJvfCnL6oֲO;Bt!#:Ly#:'?࿣Q;l\Үدt6 2y;/‘;kR64u׆f3\/3[s_SgbLw!@AgjuK%:%ɏꟵn+~姙c zηalVs(QsK#1""x-Zq[*cAգGxy #?wu޾`}n=tPhs]lbxy&+f%'vf n /bB_|i}<.h*cD'0'1D""/> ۦ[F҇VڨEW+uv#0|kyö-nK25 Tyj205ΐxf%7 wCkWr8^8#" `xdь-ď4i|+o߫{_IB_|8KVb~ LQmF崘J?/TE&ou~ Y0/'!E/ZVB$D5l~FT*N$"".Y$pt"ُ|)=(oeXFb(yc{]cDD܉GڣsR4hNH#"'xEZ3$د6 454Nr i]ުg<24QNẻQH~ls@^FAΓ mG' mKww ~˗S8S "Ĉ(>?KyqpsP,u_YnN":TCcFْTXt%_e'+zK)3Xf%m%[:5:ԌM#S8w DtFbDDD8 #w͋vnY=omo"([mSBF-5nV2([iá5y+Ď,pD.a3ˌ8Az_)- )tv׆Q͝5[iw{ÃN:)嫌&fs"#1"""#CkͶn䅼iȫ]iRrD¡5 ؏[ҫT~q5e+i]^5G?*˳ECk0rrr+{Oᐓ:)AxQRktсCo! CahOqKbŇʀq%zXҫV`ŀ}H_خ=6j/gRЩO՛4ábG Wi[+-^H96'uS [;#1"@t:%BN:5]'WRVYFb{-O7ڴ4MΟf*ےV}p,2MVH>AƿDD 1cM^Gbs)c""/?{$Fqux1(_;@DDDDDTre#1"""""lc$FDDDDDmJZ,C%:sPrjG'Ss.'l4' +N&"""BO֎H$hC\w0#*IJF+2v2 _?CS͐S<u ]Fb[kFNu<UЖZ3$f,\Iy+y!|h^ԋ{ IDAT-#]mi_w7nkw[ݖ>|ۜ2qe dbض- 5m9\ur$Qs-ޖtaѧXbQGmie}[!0?LTXh*>cPY\jS폎k %s[}GR"m 0|kE%ᅿWΉe{tRSňΦ3xѮz:ڊ~tgii ]O:ıP<@{ WY>n.f;rh~e<=%qGX2Wv%m;{70- %vǞV$"""*I9_^I L_|Bz}O=cc3FD.$^mw_|Q! ƫIy""""RʑX|:V'>ktM!ScvjM/~e2 r_ICAޞJuO2PT仞B{BDDDDDG'D]t m/m#"""< aDUVʸ#1"""" S,5g҇WN.z;W|!:%N$""""*&7RJZ)AK >}d.n?>EL1#F.Q 0|kاGh#p5cla_l?Ճ :5wznỎQT)C#1""iw[R!NQ>1""ʒqrJOADES+S1#*E0 VwF{د.{Y6ieiOn(Prde9C.imޝHcjԔfpP)"""bǜQRT K=5Xwauہ]DoXPӍA X 7Xt6{v&}5\`{yY9<6ׄZ) w`ADDt^='][ ?Ε!zC49Q86{&zE5隬ḼfEg5}cq\w@moU}kurBO򽍞w~XNK k%o]|>;-yǷ^'_#@ Ǧ%tx-$Ln (BY|)% 7MkWb 0;6Ru4+6bbH D *zuEq䝖a;}^Sm7Nmo|` V]N^ E2ٮVI PVaT+4ZTq1""ʄͻTRd;VUX %R{VaJ~ HNDoPVo/-ϠdٰwrJfrecᾌq 7MPe5%-#$wCͭc`n ȍC,'CB`' [T A5՗g%'G3_oٚF/BplDVA:;g@!P%fX[oǒ\~@mT'5s!ַ  LDW )\^9ԩqy]CϴԓPvp5Ju啪lR7NJ =bˀU=vFYNʑ>FADDt )',1(&/7<4 cPzj;'3hXz*՛ a!cpI?(neB=L9fI_Uާ]8ì! 9a+s?;j89*cDn?>Vj<䒵p7Ͱn+KZʐ*0'1BO7,kT 34u[tpX@}fRCO .Yꗵ7}E*=u, q"7s] 2qg1W\ F'!ʏ͂-bvx1OR?/ wOP@<X섐O0LYf27^Q{P _ F1#""w9s XyyS#O|/>do0ȋ:pI_-@D9q3bKFma>N Wkfc%߽Ú yuF}zNR8z`a@^EQ-RxO #,sI?oʽUiZA-^ K_.>+~Vf[I_)V`|ҟ/G—2f\b8}ig]y_8ʚ|^"YO}1'B+',άOu7ܬ9vY)D"""" pǣ,S\.BĈr#8yގ􇈲Q1#"""+CTd|_˳o໽$\wN!#1~" / HB| *!qgizVgprP,o;l,f+P>ӑBS+{>%z_IC7tj;!Yxv/iO)(ŸQH,Hd?]ʂ߃:Ǫmi(YX7n9;]~!n.f;)Cåti1"3)H.o /(OM]9Obvd*Iyoy2bp71I7z-/!qDpaV^h鯗V̰_GB5X!Cgl&Rd1tӍ/u4TD6-uZ+܉1?,#c_ eh0!K{hDDtL=;%}hZ+y S\?Qĩ@)R#""" **] J0#""" LȸFbD%' ADD#Nގ&%:FbD7%N$"(w,ѹc$FDtf["""FbD%ȡLQ1aGDDQiwS*LfR?H|P1XL<2^0"""Oa||*ABH,{YPgQa$FT<&Q`$FDDDT~emZbB۵l*W7Wv&"""*p'#қ._s2 ,TݓDGl616FbDDDDE 0+FP/hG&ON 5:F P5CvL6Ms.l8TթgB۔ܷ *Ĉ 3s+Yi^f[|ܖv_90Дݖtf&-}xj-".Ǜmi~,]BH(+6xg`2rzwUR7 w1`e9Q1P"=$4Wk\H>[/lhZS_{;@D#1";y#ڊ''39i%lqy;b 򬙵M-9D" 2j-1$֎Ѭد_ӆi0#"ʂՅ{=7"1`jbv&MY !%'2aJ*PVo/d]"+?Փ7{MdftfoM W&X:Ditіa}uf)[+8(={.=Ck2fԚ;n:USk-Nߘ.UjtꟾT[Rm>1q{"y*ˁ~A^ @ף+jͦgAv&Kц?GgÒ E[Z~FA1n/G(cslE{jn~[EmyoYE@m:~8U]uڇ10-*V'))Bu`N+`zܩQ w<:,x~ܓ=\Bou!n;Pšh_o'nߝ߽+?loKۨvM&vr AmL*FDa$FDtd?z>t-?]``,7@YVxT M  Wk3Br(xӢCL:10^"czjBށ~owaڃX3zwUim9 }sk }?k1 ,"bcGsSO|V?Uur 7}Plyp{ʥzs8?tԷcF]N;CT8:/2٬w.5}ec :%c+>;-/G3 4+Ay ZZY ii~kF|+w@moU}1=}aW:n_;Wglʄʫǰs%C m*#(Uƛ*П(EZ2kX13{OǷ^C MKK;Zr),3W|iNK> O~ gr\Bs\O1(N_Bݖ^DYe;>mhtsn}Ocߙ5<.˯} AUm 8/iȐ'beDSGzy:U2!}RVxS?WZJIxQc"3xz֜o6z;b%SZJC !_0g}P1ЁO-s1MRT_~hKaPT"Fb?K(669~/8~6u@+iȔ0^˿[~bՇ(_6v7(AAy0-$qy囔bJָ.ؙ q%ie2,fWXt'r!ջbP6uF`S;'V\de=wdh-6yxz% y[;Ѥ} Dma>?')|$_#>ZHrT]Ce O*,mo7BS-#]~Aؒ:[]~!ӻ#327kn:~!:a2Sĺ|o=X[c`;e0'qy'j%:`nֺ-c AbSqti خ̪PۀN ėgw'fO~L%\wK`U{{{*joo/;+Y*kItjSiwI쿿__g+i(+K?hǶ Sso]8FщHO=Rzv&scB-27koFDT8:rxs`zY=`h{hԦE6nY4c}bDD [CIGNT!\a$Fah{!/4 u +E/1It݈dԥH0}tx\ DDDe#1"""""lD"ׅJsbDDDDDDHglZSktȉ/5Cbj'?JHglUٖvOs˹|t#hןC(#'=\T\whT*y"":8<}V iV_gL=Z>?tG~t]||Ҭtw\ˇ!m8z#]i-g#rt_^]KADcshJoSsv3@˿CCۏ>ٖ=cht+Kle鯗:Xչ- ; ƭxˑ[mW]Vγ0#"܈}A(qIn"*+fqs? `jsW\!*CJzڊҏ Jhr% IDATfLy]UǴ؉>1""""fGaF\#K"F`r-_c "Qx tw%O91"""";k:˽UԽ)WR64ux+_t8xDC[B˱;T{{{m: T*6 ?Dzpͽ \xTpwhgO9y1'FDDD8 QIa$FDDD8 QIDDDDDDH(8:(_dg"̉ 30Dmǎ[pzwRzSYu'ij?zڱ.ʄ-zXNQO_ gڪsF`<v&#䍫 w׸Z>ȹ7~Hc]|ڦxPD(ϐqtb04ښ0~!{V0Cj}ROM;0ߺmtuV&ԃ"tcpؽ|C:VƂ]C.چX|o`Τ&5\`{yZw/q:Q%dV9<+;kR@h~fkkc$!@M~$UhX2u斾XO=Εngl5!.Ctօr;>VrE`uaئgAv&Kц?Gg6괇C^hɒǏZ8i姆=wLK0W}{ 蘸sI(щaJii@>GaPhl[3_7^X"aI``+Y-VElWϤ PVaDRif6~Hf-c6ͨ?qٖ1FbĨ?U 7M\ˈA1U*bV| ˡ=sΏxJ 63Hu'Q1˩ڎe&囑˱er;~ j{} خwSL @ꮗ`ұzLݰwĪ3y m/+JJKeG,A!NNb7V9fΕ$Db/K`$FDDDDd1+26-y%pKϹrԸ_#ggr8(|ކW9"f%b+[]6 =T>1""""3 y_=v#j}RO V&lmn10\k_:pކ{;^8jt6S[5ޘ?)CށXEiWƴ_iZSOp3^S.ԛã+qjFbDD^DD&| Ϗi6ʄ}iuLc N(ƦrhCX;hΤct)P-{\q!uJK{2D(=ٙtNFkYr׈r;!hJݱb.>Il?!_3K'؊v04ښ㿂a8E`|KD`ʄ-t A_6V8* ;L$<@ &AtT_H|R813ˆM`++l1 j0b\IWk'`}08xƤ3PR FbDDDDDgS{zpt`{9YVjkA:(K(A% (q%9L`z3{b-˷x]"r4KNCU8Wvor L Hn;щ9ܟu>SlZ,2=J9J1Nt(?>:?s/<:+.YoMݯz`u3ksսXoӆ?|#[J ;'dBd*3阫pBɻzܦLPfhс'S,5C~ZSkD<թGMXus@oGOi0b^WW6m?N.i6$}=b.ȓ"%ww7% t^Nkxvl<xn ~IZ̑O9cZ@h.@ 断rB)5ޘ?$ljAJbc/RډO"/݀c`׾Rt1Jnyp{ʥzsxT8DDEw&@T?5qXAU"jmRV;hΤct)P-\>,k]|o~[EmMHA+Iչ2 +XYi<|o]Id>֔n/G(?& ySOG{]XuG16-C dJKWϱ`]cDgWK\Ĉ샒8G~-¡HXyd,7@YVxS?WvZRתf[D|۱+0u$~B{a@xBLOZ;i&[-—R$QIa$FDDI>6vgDs[hjA;9;7jY9<}&VjkAKe:Jjȗ(xn[&Ē7{] 9Pj?3ԟNIF|8%DDQf**](f*J,j;OpQ;+~˖c`Zm@r݉$Nvܟ8ʭt`݀`DD%E?%FRI۹E-o?!L@D)"oD*Suwmiht9! ')he}څ>͚c)Fb#ZدSktjND&ujN|ߦ93YR˻JSyV@DTxe٘Ej.nd-v3k CW]c/CUS""":/GT:Quw!1?L>~x](e\u&Cۏ;nӍ#%""""7HPnۖL+f,^ݣJeL#Q`$F! r`[H[?o ׹ul0!:mAh.$kch*ap]C=67BM?$%RmR2NHaFXN;*oX+h) 4Yy׻ޕjG;485JsTS"pFUr/O,<^qJ/ ?42ǎ=Qe*%R3)]u 8 \c{qU/tUq|V/w+jTYV<1s5u""":?#"*/m blp@0f5ͼ+\p}8<&uOtT 5!""*#1"2~ (}jֽ) UR knۧ  ) i5x0!&ܼ`$(5x𯝞TU_Q2txxjBDDTi8:܄̚ʌKHm;ToKkQV+2erܲK]K"o 7尽KLŠa^8JaDDD}bq2Dtu㻝Ϸdl5J]:`Rh̍ ׵ґo?olnL#R*Ꙙ].=FbuQB1ԯf/CAHĉjK/k"lW^֝^b `ȕUl_U!Q-`$FDT~·VS$#1[+f7hCF&x\ c/j1_jH=> (Q~IqS* ^4BB)U Dvi ~l/k)ޱ{9N%e) ADDDDDTjĈ.]%۠Tne`$FDDDDt) ;%{ 7kBH tW;4!e+YJM84C~T vU}]g92G%#1""""Kb>9j'۵KL3^my>vDEg^љ1#""""R'˽ [Au-aC}IfjnB\[]F6"u]3`fM LפTv@4.]Y#UΊG|l}H5'F[ϱro_9-uDҩMoش[~ T1gu/gWHTuWDDDINN<3b$VV' W׾o?v\.wg͕F,[ZcޟDKqaQb |=7{8HS{ 5*FgщUO+-JKҳ ٤-sv/PFd߈Y}-ؘ7 z\Ϛ<ls(-s"69@|ls m0DW6u ;!G!MR[\-|z0v/}Bň}b;D ቦD="~ǴƕIu7ZGb ش[@Bon'F[;4_Y`="}m7>2 ˀ'n6WM?Oj=Zwv{Po[?oPB(w H֊D"ЬEK-z"hvG=2:0"1vzn  -$psg-ytm1,nK$ #Z&SNܴ#v;IѝTakN2h3m zDbu{RdqXg-kQ9%"*;.LLsS]h\. y ae^93 !k~Yj` L h+iZyi-z[&Ն)eQ&&tו/H˖:q+\ `` :Uk]; DNҠX=J-$r7bh-xRw;-|Ѩ9jE5ڌ1) ^C9"}JU~rmDTjJk MtoAG'V=atAHHdl3lq@-H A]њ]_p@ҚatR(|+?i] 5{k{5@+to7y˔g-bQEƒہ&xf=VoR݇j3Eq') Z  Ef,l ;AO䔪ЕSU IDATt ܋cc[s;x/8ET׋/A-+[NPOt/fNӥNpж嶳THDDDTje$ĈZs" _5[9ENOX4jgUZG*5WY (ߘj<FbDD'qZpt+ůmNM GNռsź&p1Y7DDDt](u!hzzy#,J&rx& C6/w{|rV0J˜1Jn9fQPhG,T㉈\1#"*M'ʜ1} og4~-K_VWas tI%^F6\щc w!_c PXۣWMҩ1)jNWͮ7i՚> cXSY|SAW\BIZH%ԉj #1"8U<7-k 짶:|f>gM6l;Mg!O|#~cl-6  ww"N|xXvfl'3)?^}xC\ km>!Sʛy3XZh0EbJ_".ƠjPP%E;]nx~q?>RUXXwScw_>r pd)]7KPJS('FDT;+ů¿h.=tjZەaC} 7A`fMQ.\Z[lu0" Hw:_km Pii:Ju +2ӵt{WV^>}Xh?K9#K')+H|G|lIW~tjWUd W#8yKQ5wɷOc^A{ 'F[; G.6\#""u>M/-!CiWN'JK=cz-lYHؘ3BN먱'2c|&?YSr aSHR\kv'w!_;RuۚYʮV=(r 1 BP^. KCx-atvh+Û'xU.:@ATaɄؘ3ǺO0iv7AuA wH)#MD2Rza:+0ݕWKDmQ'zJ|q(-.@lat HSC짊6uB;-U}GӺFv|j#1"#1 @}]=Wm-ת5𦳭[L6W=Bo"Nm> GF:`g{Ӟe'fMwG2Cʈsirwː(|4-q1#"*LʴCn%$CHm!"hmC'$.ӯw ZX`8i<^7Y$2b,t׵ Ĵam&#:l B[gNF2k,ut׵,L>9O@B~Ɇmw'2: k3;sEcf H"DbL%ե3;-5r5Ty13W\[K|щDDǩ/>DDY([N8a$FDD\!"JHt*bU /LDXpˇѩq"""""RcQEJHV%!'2FbDD(r7cVq""qĈ {CQyFaq/Q ?Dk'yqZ՚jys6Q9щVATi8!fkH@Aѿtj U},xζ;YYWlgۦ{׉Y4ؘ3Ǻ-up"K.rۘ3OPjtڳB+-g-&L(>WKX/$ ¶!|DM=FF `$Вx""rĈyyi&z~_ z\Ϛmse-OAH|ls z f&ou_ G7!pls(-sƜ]b:Ŝ1g}t .pvg]8DD]~CiWN'JK=cZK&3♍9#ƥΥ \ 㶫wG[D/N-ZGf!5Zw›"[3gM9i [_}@@ד!.$ #W#~Ǵ%w%CG]mNlVx'.B?G_HZpܳx63%"=]~ZdfRҳDbQ~Hz@ ӱ}m>zF[wuuS @WFvջ$+O4]~Du=s;kJ 8 0i> |ѝ@`\Dtw?UpI;a-;;-|%숈hĈ6 UkMg[+1,臀bMV鼒_]]!ȃNjVlmÈq# =zs߉Fݑ3/tqx1@%(W,{Ѹ/FbDDT3چO^0:n ~`OH}xdJ?Mke(i*uC5LGt$= B['!3hFH[rɰh*8ѷ3tPʑ"N4gy<.\CyHW֦|_hHz쾃[z4aHA8Cnnmy^KF==ZªbEOf%^Yv_48K^C@viy>VqUݰ>PKwYwz>l]`_r|>_4VI#M;VCb USbY'iJՔ=kjJ>sUDtU` ZߤjPc>U J-؋*G'V3"JE2^:|7i$?ܰڇ}nnNkn?,5PB9a025ZP;|:}ج)Q4nmHLnKS{"Vr"2P gX5!E{G¥v߇0!k Ѭ ~c8ta%ӄ<;;p]yFN<`DDDDD5X;^PU a6i>ɿrځSGn[RfFUzwdnTr#Uok}bDDD I.ճ>L.K,BryYt"*Ud6!@d|@FIԑ9'9A=ro0@y3jۨɟ!l(/9;(}—?Fig9b$vyh. GZRL`Ƈ-KDZZH_zEO$GFjPHL3ksc8 &+Sʛy3H&2ّ a$F%ўQ)2C+_;]h _u@r|ǡaaAk###"""4Yl}B廝hKf&:H SD}bDDqlU}ح_vkXX2f8=YӿtG 2]();yv|oSeGr[Dk( #"""""*siDDDDDDtΑ{Չ* !"""@HDDDDDTjĈJAP T[\򠆕e^=#1"""5($%KdHbWg 6ǂ~_mh+o.,Ugn̙c˖s8"*-vȀ(FbDDE;isζS{֣\.w'wrKߚWls% ӒhɍӈpD"=JKүlJ["~Ŷwou_ Yul-s"9iGDYg0DW6JzRDDDtR#"d *tUT؇gMg;B?$W1>#_uBm~ .>ۣx4_^ Gg h+77іoFDDD6-JGߢ~cx8#ߞ8Ej w ""JQa7~ߺ;⬗i-hƐp%q|?9Xok>1ښﳍޙ%!P7a[^iƇRSuB׿ěE6攓"c+|:OEST;侈|pUj30Dm@|tvmC'zF't׵q6h|lp$ A~J(-z Zló&ݝEm%$M "JH*4[}\+1gZ@79cZJ67ZGb UK?$-Bje@דp+C'ə%Ħ@r.G3OV $Ld{$濬+mrV0 fSIAsߙFe2aIFS7&wwID^21yct!1گ_p/riT1.N6:U xi Emz{2ڍKlw;r%L \9C(Mr:e$m9j)W0酭~x|Ul3.˥np\R0E&'L!1$!DD@ŭu˓qu;kQJj;,/-hMib?oR4& ˻cc.Z G;벛$o9j,z+Q9忈'zY ̷)NE4|jN ;'1kbZg""*l}JjHfU/Zxqȇ.~ɦU:AWo<I6WJyL CͱL-ݷc0u-g4|ZHM쑬39̍S։M->1Ѩz+`}K"zYC佬mGVj39)a L}O5n.>ep)WC \*/,[:wlKӯ.XDU~6x)U DAȍ;Ǽ3ώ 8bX 񕪉`}Jyr$wusRۧ ؋Ĕsc80頦p) [;zdtMtvpqAlugl.[3 <KsjL}BqF1 U9]0\Zݠ{a0g[}R<7[9e{O:Q'GvحS\/_j@ADDDDD%]_ zujV@خj0ֶ>1""""5-۫A Z}"8 ?:"1Fs} H0%"˦}"q01cDaj/1U!:wĈ d "CH-QED)0#""lSʛ=!lW«=g H]d>DDё*33 &c(ez՞D/TRƇ/ok;Zڑ &,'FDT3Ups0Xku ]XR\k ;۱2] ` ܼ-'v}nnDĈ*a^`/q0!C@5{i ju-zlH45|RՠlȨ\1]F1"H0&@xek9]c-CGK'~>K}h:hdOqt"Q< ξU@~8|?0B uw ^= |k&}6cHN_nU^iQ b"*%B2眵."qwz)U MOt"%x$qŰ\vnԲ_" G'#1"""""R<1" E@@,|p@]Z_8' ^$DP`$FDD|T 8O9N^""/*yFb*7qD@HDP&FDTi8:09v7߻C;;gp{u_/]6 IDAT7hkB&qR`s,8ێ.UҜ1g[kY*N@}~|^HخbdC\ó&3;:W1>#_zV. #ߏMk\ rFHәBfE@IM zZ㷼W';GmVo[?oOM`$FDDDDKkLX9s1d7d[|P@$mQ'Ov=w>8q`|@^?mLw];򉼓~XjӍQ nAQQl+^ u:8OvUR.'N@Y狞hgܝOخjgl8u@/SXrKQ5YVWw\Q#F^ccN]UkbG0/$٫ُw.;tו V|;Ɏ'* c"Q'FDT2a֖ub@ 2פ Lק34nDt2}>=c}t02i O#+֋+VuچO4)RGw2wHW,& jam&#:l B[']c}ј'2ߧT5(s\9΅zqIEO2ڕPe]4v߇QՠTkXڮx*[N'I ]d .A'R<բ mt+Fb˥_rHځl':a} :b;tɺk̬^)\Fhh{[D:CIV2+sROl/q'N i-@.;ʀSHoR/fe$iHk&`|=Fs}x-=x|`Ty`Eb@څoߤf Cy;"q*LW5C6햗׽v[|ݹ{TUMN:+XIvc.T:#i6zY8Xօ%yE8g!o4>nG4ɻdUpq"QqbG)8.I^5}֧7QxK+vddhP\,9K9eh+ӿ_9=RH=Ĝh?'3Ǻyc~yTNJɎadRN~Y_MHfV jo*>1""N4"1 za:NFfDÛ"u7ӢNNjW""'FDTnZ^Zޔna1+pw彨^}GqXudU+tjWB%"#"q yɔD"U: kQqQ8:2 6i+ԮB%"Ĉ \&.$26j-2Bgm9U_.ht qt"Q%Hve.T">1""P8J""#"""""*5ѥ7<)jJ_TjC/zC3ѿ9v>fK2g;Tkoش[s?l̙,LDTy>77oy8K}rtySƙs;]6e:^>[vS[}X/$ ¶!|N3ڄyK$Вx""EĥH;@s{`.?2!uIYa{L<+Y I>:NmnQv\'NdB}TĶ>TMٳMDTѓlR jܒW&wmeN{H5ɷ+Mze)Z(leqט>?}-.p|ls3:2CteWZ1s|]K@ƠL.YSr aSHRptֻ/\?͉ (??|! !o[͓DDՄ+v9]3|Ѩɺk6Cp]4767 5x0!#߭wv߇01G+DTkɄؘ3ǺOĢ6t[ ӱ}a+\Fwqy}&):|q(-.@5JiJ\Y5[hNE[szgݙTHGO[]׃cN`I=U}Gz-5SDDinS<0ܜZf#8K'Vaj/qP#K;Wf̚1"sUkn1,J1O &Ei4uQexF.eB?W/{VيٹwLzwzOxǜ XvU-y^~ ss2y^;ہ`L֊M0ZmП q|=7]cpvi}Q5në9="fmCů96 +-x ؀Ig6m'Y9F{k*5LGɆmwn͑1t/ W#7P8o@G!o :q)g{dI%ܝGCW Ndt]VCBqxxX Bl!tqt >YYdRWOB'X>R;cv%q!ucR/A-+NPpt"QW\[K|3"ˋ1+q-#Io3vg 6TIbNg5*y&"'FDd6 6y_e./8bjs% V<sźܝg-^;:/GwO[wOKBIcJ6~ߒVe mu %sLi<1*z\p{[~ "He_5@: -z; :`!vt1Sm\mƇ?}yg~~Z; GKDI8ʌJD" H웕 U˙rffJݛǶ`]3]v#pľr Qt#'/F/7?qhQMa$FDt[&grXM_ Bo"NW?֊t\Ļu(g Q߹D|zCHv[G',P8~da@[l uxxȡqeWIJ3+ET6'JV+'BjHeMI™%W.y^fv.5& )Y+EzKX^'U>|bSoD }bDDnp\m ,Ң5!R5bAT;Ɇ8V}RU4OQ5*DDDDDD>1""8Ћ*ĈJQqt"QE`˚a(#1"""M *FbDtir"*Juwc$Vft׵lC|sh멪= ӿ_pJ#WuN-6߻k3;mrǔckv;1gu/_4}+AXVW sf~ѥ7pw!_H]qَ~5:AW>vu$;:O1ں{CKQHD/ Kݩ.~_i[DOR;62'nINX2'W5elUat9|ܽ@#KW]!r`j G'YhzOC ?E]L]'}GkANcA.k|@^hWZ1/Emz9B2GĢRXяa7 2[L|&rw/V)"""*VĪP-|L\Mnq# 'X}粿UM$cP0o Vq7mİآd.~K}g45`s% c421@*z mC'Y|;ECox2+-Ϗ9`g-Z0P%Keĸ<ó ®@5Vw81e7$w}bCnd)B$"""jC_Ɏ8<~n .1Ruֳ>_JUCjqbOeQ7Tu.79e/A-+NPpt"qݿ/62kW>]ZSt7pECFDG_qTQEa$F窾i9ujLi -JMD)GY&OخW{ssRۧ">77dleVUረĈ&b'0[dFD;.s֧)DRՠTMQ۠YCcA߭ReH>8xawe>5x8x=-T&4ܜ Ϥ5x QD"""Ecư! dTy_(0:1RhDZmE72bQ]3R^`/1?3k"hN}bDTFyU;jDg<#i7$Ĭ F''˘q8Hͩ2eщDDTqBbH+wCDa^50jO.B%ZchH<ȉDU uiK'#A-y Bb$FDDDDT4{  8x=k) l Ša^(Hʤ~6ǀ)[d^Dt)h&o.XQ꿺6>i;l 77(QЊ_xX^_1KΧ1$կE`e r|@ģnMG21@*KHX(AqxxP(e$HSS'P(iO)o"?(U qN;}R/A-+NPODFC&k-hADDQ  6^ԞGY'i^ѿtj7̱eKQJleu( Z+ھL>W݉xV;:H漂l0bں{Z-#Q(n |A9DDDT6kzuI]H[ywZnLPNibN`P2i*J)S#ZRVb!I%bK)ՌRh΃UIB o!$j~ыxc>n#'.D_=s]<]pdbK }^}~z㦉IWaCs 7t1؜9нG&K+Ӭ%ܪD_]Sc%`{Jihe3aܺ[IeY->;m.;rխ W]cq&NpgtcZa(!lst"9<6< yzN$uq*cqYw ;3^?D:OƦGRi?gr;T&3pBפ4qZʚja$FDDGÎ'{ԬKQlt+,TqFѡ0:NVAi IDATũ0;6(%Tkj {&=FD&۠~5z=Mx*>uT4fWtyڹMKEە #1""|r&{9ݲK@ rfwlT^Τ :껦 3w-Gl{z0P+<P!gSiV6+Gm_{Wkq/#1zn67b}u(ܰS[zRkˈHȢڸJP(8ѿ QVTׅN<:{3gzod3 ty}u<f'FC&Kl6|qaD?gt$貆JOzlCMmD_<_ ϕPoDN3:Q!c@BVaPJ^]uN^]Fmy핺2'?vCtvΰبeuzKq]P(#"Cn&{\0.{vGAR gt.P[=1!0e#1""y'{qAh]A=ri?ਯC2AtTxMaTnݜ ;!Kmr#''E@TDDG牕О{Y]% ~[,=206jy2jTk[ǚ=\뜈b$FAh^狓ZЬ~`U}=>tftiǻGR泄]W΋C'wq෭Өε>CS3d,N]ިMn,^oqPss""C3*&hN*O/pV\O՝=ݾhkzDF[· eovHRljDwHEe @O>e3nVVh06\ ǹ9!UKK&ߞzG]'|A]y ~k;RDgЮ1 Œx߱ 뀭bOW-(+*\JOw/?I 7e>a:AMoHMܪP019ߌD*꓍aˋAZ}b-o[~fpG&|7+sD%/=Iet`;GeENDDcX:=щth9qnwDG'R^o(D"""""#QqNFDDĈJQ1#"""""*5FbDDDDDD;J; 8a$FDDDD&#1""""":*bQO0#!""TFBDDQX&QщDDDDDDH8:6#!""}bDDDDDDH ݶ( 5 -`kr'XQ7*4zbeOSNDDDDDXm'n|?k<4I^t 0rQgP`nTQoi LLDDDDt$1;HtN]>vm R(*Yiz,t-=unRJ#uE>>0".=6ŦFTXkLjZ2Gr]eSchfw`$V6AM)C`.gL: "0kR"jjTY\'Df7ADDDDDʮ1@4*$'/ۡy`.'g6jW i6'`0 (""""FP(ݒOP$r"Kl2U*x]AzJ `\tw\&GYQ%K^1JOPO숫P`>1=-j a>ٗ}btxO`Q0#"""""*5FbDDDDDDH#1"""""RSDDB(w(?AD #1"lhRVT DD s뉈-#"""""*5FbDDDDDDH8O8ۘHwS@^XVAщDDDDDDH8:Ըʡ_%}bDDDDDDH$TQLk}ܕb*6T>qP~ADD+Q5>zNÉ3֍.㋧}0wYDDD)Ĉh_[}\vAA> .&_$)jޮ6X[d00}}4DDDE`$FDDe4}F l>P:ԝ729 "C}ޅucWs%> 7Kҙ[79%)jcѻNh3ĈRÉɫZ@p]2i޾9}JyJ:O㋇/zX!o R\.""ĈhjhY%ۓ0<3꓍ {F?Uà&\"'* Jhݘ-@DDTFbDDVmE6ajWh;&|7c+;g 7%. Mi?̐=O,(5BV$\.R(xܭ lʊ*_QXֶB`Q)݀BP \DDDD#x'kʊr7 HPDADDDqQ1#"""""*5FbDDDDDDybDDٸ 5FbDDٸ)>G$Dtpt"Q1#"""""*5FbDDDDDDHў++" 8֮Ηbtu}saGC[BteӏBO'G,VyAnUZ|1t^_kV۔UڠRr<Ѯb$F{o=slF&V)+UA?m2EOG[Rܵ%V)[iU hkJbyyXd"[ʘ˿Ce">dJ3k / 4l87p<ԋ޴^?߾k$*'K~_Ёho ^q7~a~|C;sW;|}ha׏~Gfn@:Zqtt5Hmf_/>=5+eN;.ܮDZ?wߟ <7u߻4 a#m]`y4ry˃W~Am7pN~3sX}~>hc9uLNTTƿ*{[z,2|GUߜ6yMK/OkCO'1Hyſ}z>'.hQ|ՍbV;ӷ<֐s텕;{|~ޡ"ϼ޽Ͼ3ØORqWqTw^wǟ.|Azj>O/vBA: r|P>zdzn.FV k<>t3ݿ @s=~UU{8?YhTƾ./Ӷ\OCu}g^Ogt:]-9_o&չY6.q̚}9Z{_cW;Ht;0{Ӏe]DC#Td#^v055UΆ1-:&;y߶$ ڬ.&|zR࿿~{}rOcwLտOݯKg|ޠ_~\t^@_&ln,% hPgum6sZ{ ԟ:/;qBxAn_Njڠ˽msm Ů{۽]8Qq8OJA}׀G~mDŽpszrYԥiꦯ/{uǣ*?J|uDyhqn''?uJNa w0(4X 8iQ[mDZ{x?ť\ON6ߪt+(~vhroiAޫ/‰b홆D1W!KDN*n=)wV|g߿goڔΆR>>*᫭pqUjtuɡϹ򵡶*.GQJàcثbmmMP%Bڼc}s}ƔUDu矋{Dj扽<%;IEP(2"*O _""b$FDD!$"""p"""""c$FDDDDDTjĈJ5oWxV)+O7bS*jb+RLl Ivd*wV\y0QG&:5rQ7viTm|VJO0+A{>m&\R6]Edn泄]@jH>9Eە=V+vޮ4lyv !O#v奟 sdtiNj痦Mtj>2V=щDDDDp4̭ٛ tx8'5^s_wĻgnX{_R0+Lt ZRߺ_,`NfؔˆبŬJwE yy=)uZzRddL~zMI H)XoM3+EGvg%lG޺r^ډjK| utvS~/YX=;gS).9._}u&xx|xآ]@S9rf[vљpU"52!9?hxS`4F{2t3u=CZF-^FMz+GqJPbqt2N2~Jo<ѷ>e4xB׫ C o\L_K+ Mop ;֭?^;Oٗ*jH9 >sCwMopZa6+zKN6WP GWƹMDy,q-XzRkYhW( Z[r)ҌzlhxSuBEJLN(+F}c!Y矫d$6jqu/">u$IEP(;l@vQݽ@D'[qI ^=ezDDD_0#"U*Xrg%|ADxy">X[*Ǟ~qN/p"""n>1)w#Eg0/Pڶ #1"""X{UZ~҅N$,I!SoEս*|ukNOd \~o}9_+?nR IDAT}È!zAQ)RF7N"AUjxSk^nQ"M͸@'&)e1-}[[1}yjL""$94{*  =}C7p$l_qArڟ}|n|D#ڝo\a@yXg"03ybF-I<Ofs6,dlP+Vlc59<GW67%QۭK6 ӏHODбxEϧWp{zFbH>˂%'Y]C*mCh%Azˆs*)&) K SZD&w"} XWC7+豙,60""6j1+]={ce+U kI}~M]cF3_a^fZWQˆUWMu|7-/Ҝ}lvI%ߩGEF[ODG30/7ӏ!?z},4p~IHt0+Ǧ!q@zݦDx3{<h7eͤuˉk9SCt;:Nn>-=aS]kkYQQ,/7 #r}pJlukU-m O y|t .J*")f^E&Ìz#BtIuukc;y')i%>+A0+-ԃ|1Vс0XPoDsWSIS.A\yؐn}WQu%nѬMmw2(\ž<]N0\w(˧ c`N]*CM)85 '|=苍ZF.=UVD`A ھ=D.W_,.u;OVC+guR#o+VYPcр:= +\Tfm|e9MʲNJQQh,۪e'ߖ'$;wJ/>|ܝ|s: 6jy0 wf?v`J1[ iv,'W7Мh/>ZF]zM=hm'XDGp=+/L/d 7$L&4'jj{t*4rû~>Ce'a-45rS3¨ΪI=蕻N$F٤ĵz3oyklj ͸,^oPk[R7՟kÛTЪ5d355Sɖԟ?A.6ty;1EF)[p[f55r Cd۲;@%M/ zt2>L*R^eR0xv5S3~A9miAтP|VC+rba '""cXYoc'V19b6+7۸XzRk0Tk iCst:j,P @oZ_QZˮ1 Mskr f>^zfFC۠`XQaY",bxG}ß۠|"""*1ښBX[[+wK9B݊rzMN<\QTU /B% o.z;.s'UDuP]kzM=OfYn?;Bz\ҩB[(s<9?#8]'-:AzȑD z!tPj)QZqFIEPH_Ufc}})굾>(l^>Q!RyyJޠ_NngkDZKZS=Uʜ*c}bo;?9u_O~ꎠחCD`$V"Bs\Yj>*co'sxrf+:#]FSPIDON6~v(#pt"f`V f; #ҏօVaD\Lt;]KOjD>>ʦo06-ݍuL~B'VQǦ6]q;~'=pA i8y Ew0Q˃h]?hxS`4I>1#ohI tPsr; (41[Bs.O/&7I9:cgIP(xe}XKbels@=aaycܭ!""]HhSB%lC3":R 41.sOGNol>o)Zξ6˿QhX?ʔUnxy">Xz:)~|ɫǀ*}gv I5ɫǶSO~{B}h9_BDt|kWQu}_ܫ[R˥x?ꯘzDZvWpo 0:zN\Xh zlJ76<1D&lFm4 DךX*ʓ,`FĢ0bʩt91?Ťc49)WQ]Fr|\/9":9\AE%yhF ; ٹ)1L '6*#6\^#0+Y%[2nBU?lB;_X/^qDG#1"'JJw:Jl]0t;`tkXQ}14zg4m}~}ZzR=6ŦFTPkLR{بEjmnT[kU^W#ބ#yVٔ 5Kpϯj,7,c_>{Uщٕ "%Z*[ ЖChC)>^0\wn|{ O<C;UFc@%eݝPf  pͼ1Xm߀e5WlukUfyt=CZF-^FM΂+?n}DH_ns^2oe?5>zN{7ÉGٓTNo} .'/XkWVT)ܒ\}sL̕3vQᆛ+_t1$/ϩy`JO%Wn6@6ݍ^''ux;TVavDt菇_|~w qϿ/{o5{|p,Yr'x#*Z}wԹzjCeY\JÉx8f̼ISDdzSFՕGD0ްT.WYa<36Nu ۔we$:Ji,AJGu wzM=NxM߫n#!-SCSݸ#H\o4]T^r!*17ifxlD_]zSCQب^gn|,7h8|6ߜTB>1"hgӉL7;5" 2b$FS2DDeq9>|~ޡ2}u~o,]ǟ.|A1/_T~o3N Ůط{/Ӷ\OCu}gbn-DGG'D&aDLt Qzkİ0bĶN۪5yD菇_|~7y`sg}P-WQBcT- ̸9=\@i۫a$FDT4[ %_qvب3rQD}KܔX\du.gOtthylUi62~NlÅv]l2н@o1N]TAtgD_vrևIB#c]ĬX1Eg?q?ז]Fmn1`t%2jSFC"m_nղff4>iociez\&G`{.{6'W[xq*f[| u=fyљpI[$pAƙTJZ}ho_QZˆ钋z` bѺN`qg܁Ux 2jlzS8"МJ^-~@}]uuW]fkEx hLjgr53鴺!-b/ƦٗIDC:lKOr֦@sݞ2QfPBFL-c0jhNJYw0VfgMǐ-XFTƇRpjɹZާƒUT˺;=nHAfA*-TTc%ea[Oݯ.,Fb38u՚o|~s,,:=Mx*>-.|Al!2MG #^mP$E}a2|Bq"w!@+ (RxtdvH:bV Z-TH%\᱔/~\#!.s8bRmV feϓ_VuDDDT8ښBX[[+wKhXPp?LYQ%K^7ږݱԣ5-H\,ڃQTK~er>mv!9 vVm ם24s@m-{S>L""izMɱUڴg^eMlsf}uўNx3>qr[*o\&сH†6,}s ȳh mےWw홻-zhb$FL.^M [JwF2W;>a !wRnkI;[Zc3YlJ9m}^}^T%$Ģ^& tQTmBڢwl1*&]V6}mMAσl3n{1#}&'-wzMY{ 﫻{lKDDDDhig~0~:w^ǒ;ێ'vKeԦ,(Dre9.i|3ԢEl"u-ЩC*_GN]0ntYZlhNQ2/qk^5W@tV.{̨PVT D 2N{[ό}~23+$ YH3.폤.mhzڠ:׶:%n}KRed!=dCM9EĊ(d¡}w ``x<趽; [7350:k]lc/C&b݃ז];*EmyǁY2kHYmݓ>yalLa$FR=sp^ڄVJˆ)RYY p=5 ['tMt|i.5_qrNRYƦGdv8 X-r{r '"" ѱo~IKpw] <Y6K F&۠i|SgdS6y<.l87U0P+<ɜKusU?N_8X[[S(kkkn =+ X?ʔUDuo>xKF-S53V!{P QߧMqge㛓P(=~S9)pQfM+I9U-DDDG#1"Get:H}Wӕg3=":DٚsJ_zMoꉕc{]Hg~cJI璞0t=qd3#Osc."3c$ _YpHc&L cY\ӑ`C˚FΔvk&3Ui:,qvLʃ>!~piORys]:W[-=ɨ__ҽg[ `% H_[٣JhGQXrX*٣J:ND^٪JARZŚW5b%$4c9g_6W|2X37ֶߗm[ =Um{|M)rxs$W\"ZX)*=<EM2vvcB.Sˍyۍm3$ޟƼrez:o3۾$"3GH]|{r?1JV;v[iX -~dn7m7&DI/v[GΓJ']Iq/*='L `Ņ79! _/X\HotCNn{g#ԽLQľ7{2J˞ݘ?[Rvܒ),O[+A%{Tɶx!6d";G/ї<1)>TGsN=zɞZVQ%{QqϏ+RP{ 7L K[B$8"{-{nxυf9<̇Z"Ip!bwR>O˵ `~cf=zknLx{/6&6Cf3L"VܳݘK~\q7?@,db+NHυ>!yBBy>ȯNMqH EH)+^9(BQ֓PwF_|eYt\UGf,~xe*Q\ ~k-{K%To}Llofp_h¯o?cVK"d+A zTk2=T^"""PJoyz椱@wOhpChADn+ "ZqH~!bܳ}!  m7ډ(zJ7Ҷ㠂}0TvD -oh%g~QN21xS D41=>is,kU47u~cYʎFй%꿮?zB8|1 ySyySM{%|(|ɨ3Vt1grHFeQY˼hy椱V]#l%6[}V{C˞<,J);niO(1!HmLNR{΅صVܳUЌML@@CI)U$ ryu"ś,c‚?Ki7Ȭ6Ivd7JVv<2qP[+Y"J頱5=L nf8R4.&-/D8|x49x\^zxkIIjM~9eD^9쪠e$ʮ/FGl{ljW%;uJ"ŷte۷';B`^b)/3 鵝}};9KPa$tsWzӘKM)qqIX2; b ˠP(pDU~bB|ޒ=W{\??[j+Mf8؆ y{3ꄲV!9aPdbL ݐV6 [mZW LV'ɕPn+ "1:>ZInWvT88$/ԛOVZ) @&ℛh:[;SY˼_gpi9h&;*)9HsіTK2J䍘c'P" UO& )_ y1鉬KB؀$"Cyʎ[Qyz椱k;5c 7kn}\NtKۍvoe/S䰞}0St|+ygf?1ѴO>NByTA{"9CJ+Nf+@"Z<ԣMsS( ?l* x~pO_=ATvE4*=I%N\ 2tS(j\H3{u?=zk=[ث,?'Yڵ}"*n`Ie|)sShv-JƋosiܗ֣J*|F{3ʚNm!"k!i<1P4.N&Q4P(:XXXP(;zWqVهtz󝜎/J6([Oij P(p?1l-CYz|Vx^}8:`uZrXe=p|lL21tC&i"{U$Ō 0hkۅ%6cokUŚ"a{; H5dbIH*5zs-Jv :ph#^ثۏ*M@!H %rX*٣J:NҊٌ:N[[pkK-" kj\bMLat6:=X98@6 \ l)""r(Rq=v0Q൝۶Z>N 9܃Wo|3􋟘']bĂ8 n.Jkli9i\ d jb7P(ZS85׹vw)m""(՚V=7o1ev#4h/U^z⟥/_e*wQɧ[GцGsd'_4j衉(F T<]߽Bm O8|7XLg 29eOFo{Vzr@;mg{#?KO[6$]b_S5*uܹ_z$0G*Vɔ/A|:τl.ܭ;O I"CìQPa= W'+V"jXHɧ[;䄚p25}-:X]6q{\^zlaŷuH  YCW r5.Lnv9s =p;H_&h\B=&_VhwS$M;. StMtNm40Y3DwZՇXvQ?soVmbUpj{8sY!WY"5{NN% _Aa#S(A\R<͟C̑/N(w. ~>F]({NRp%)3DDTzltC_6ͣwOpo3uSϊu'6?z96{5-!"W6l:8I~+`Wk;wjMÈh-CYz|VtO ĥ-9P6z2 nJiuìɽ}`2zLlY7{ŧpI|>SvSTwgO#_6}cuN6qV,E2\"lږWL8C,xx+щy۹Ƶݲ!`m; >ȽTSÿKʬ .U1_791JDD& I[?\S&"рD ݝb\ąKO\qϷɌRR^_z?띧7Rj:ak=ƛ!Wn)UwSiq<'nk<5%VdNXN+(ld 51X?}5Ʊ2ÒL 1'뇶>W,jX^{m_&=pq1T-Jv :ph#^ `I0""W5۽{.G3*PƏ =ím/!"ڲz,bMLat67j>.8O l)""r(Rqˢח/3X}3j^o|} %쉼_3yqJ7w[;3KoݭqU v-h(U[gN)L -{n%z| {|}94tQEsVZ(J®-ʫeg?]QqKqd/>c%PeL `<>xJOh'6y&vzKZOOiSvddWqVA3*Gvm!"dm K + X(՚LOhv-vU-c\9eDP8!je%*i.#"Ҳ羴U7*GK D[_Hom9Q?+Uŷ4 ni,n{"Vtz󝜎/J6 [62Bk'![>o;dz6l8: ?op DsBB)GˆS6 dbkKH7&db2pe6H)\; ݐ21tC&6ثأJ=Ze]VVc6۽OQu|YC'a={}F5f7dhi{r ` [ֻuߘݘY>Nd9G7im_n|r|(nO/J ;7 [{'O~i7wE*PƗ_:F  vwX3*z3_oexkk;[|V^sJ>0ím/jxmu\Qث 5C'd u-{g<\>üWáJe-?2>Ȏi3cZmDכ/_)qsWo|3^˼m ?bP嵝T){1Ԭ۽?}qU}@n/sc*=N /zR|@Z0[>sq6|Gvb|WӡO~!9$[gN 6w$ /vNKכ/?->&}JDݭqڕ1 [ojL,ôy*dr0"f3OKl<3]Y%k!"+D%꿮?zKnUGY3M풎w(N><4}A4tw'2t[qq>}5໔KQ@hkŗe)"3Oc wmhK6ƤʲdV6/S7Tk2=Dpt\,\ʂf[,_?6I̱+ۉhk=ۍ?SnHSm$DO'wі;~?lv%DCӃ \] ~Vf3n I!-mcrMCBgE^cۘ!=`=s2=kV'gTצrˈ~!ߏ'"j>{dWY*:RY^Zܗ*֣c-#)w5;xϯ-V-m!-Ƣ%]_^e-gTX\FDD&8ii^|Lŋ=y_1&baaAP@Q({:*+nz󝜎/cUicNJ\\D ql)z.%;zWvR(8:VvVY>ԲRim%{ neOkQGE8f[JVjKXo:):.%;zWvBM 21tC&n ;@:'զT9` H7db4JFiRkZ]fRQ5J[|Bqݪ#dbyZO֏|l)T?n6_:\?t7xx&ҷ| ewHJ\F)qqIqF粶sf[Շ鮯)_ζ^݀ jb^¡ IDATƹڔǨ\L ָB|I, 21 Ekk8 dbTP5tmeRkwt^|c~i9b䛴.>6K&詯)Oh^).5s7_yVKOfZтj?KRV.&^Z(21Elo o>l jdz9?NS矣7[?㳚_DD{sʏ=_IFN(2xW<;5j]GYk9/毦eooL4#] o_RQ<}_Loc|8vbFp61RFsb:a…ͣ{JF~BWQvN_Ǩqo ?sry$FTNY:&RqX2db+u_]jZ[_v w =&"rX  ^ q7j㭼b!Lf2ìɽ}`".נM<\-#h9:&"h4_ O=ؿm+KDDKm֣Pk.!"Y`RqxShNFDDJqOZөnO>'p/5j;a BQk:h2po7\{d}x6;ܱ&wEr)Sscw%q8Nܯn&'q/zS921)dbKdNil}$dbD6t-BPfzBּuBs ^x=۝I@B| s8\0SXTY*VR WU}2o`5B221X]2涫&epfw*싺Z}9@z!ku%c>'$W xL56,M;u}xLjM?Q酺#b:ww}n#bG4>|QѬmz57  }?׉ݸ'l$J&S vO^LjӘ7l 84ye|*&W%D ?2o Sn^s~"}Š^vIIMm7CyA=EM:Z=[3D'p܁*2"M67.̑y5DDTQCs~5%"~+~{{}漘 Wvdߞ]~{7%C<8O ֆub^ܥ)譯n iLwM& ^ $x+i,8KK ˂>L\SlM%hN#H%6翼ּ+WD'׷':oF{ V'1vdwMFφ_ Ep.Ya,4Akge)ܲӅ=؎K,ڧTk;% Z#% zS(L?Zqm>|=db$SUVe0TYQi{з;ӓ8ptC&n XZp=lə\['2+kq%O<.K\)zYk ϯe,YnmRv 7ov/)֤Yөtko W[?p}KXL3B&dIcR Ρi貨s$@ZTL!6۳ u_y*^D~YlP(X19.VY6KR%TLUI;Ϩ8D$mMr&9ϔ_mНqr,{Ue\TkI*ĆL#\KY&Sd12\tޞ+^M+ kXtFջ\m}Mg Bfhwi슌)&͇=cv8i$x뾚~CMm2T 5?2l$XqKCE ։n\088z՞\07տx 炞A|g1&5bTsa*7e-y -q]@ra o@NDD08lrE_ *|UDD#-ATy]|Y%ZiDnsĪ#F*Kra.S$‚p=Q]xd#[FH}5jMW=jMR1nM7Ï}25|,KoLw^r2'詯)Ȏ=jMgum1(D#H4=sSt5~<_z6|M+HX9#T,8rbꔟ19DD??!b&" k{W,f},2z:U>T,& S*K+3uAQ OtPr1z]dEU9 /$昭PO3\P7pjӎchAwpO ԠT_U](fV $:l4`Y u1 VBbd_ǘRe,y+|lyFt{8ȡe6ɜPhNFDDJqOZөnO>'9]Sq=pvNy#tW8zp3蘣{ '& Il[2ɃD+XXXP( :P(sLd%)1i\S\&v!n)RBR?YIjjjv"&J\\Rej?X/ «y:B҄9f5ԩDD:]gi$"l}X!``[ِmr$RhY?o݃ݙZ @!H7dbiT579S1:%_T=.3R:MK_L.+b)Hj=eo>+G1<∸`B&jNS>{Td,}CeQE2A,iJ_S >'{@]:k}~Foc}0&چL,\CVOF= K/A6DB`aqJuIDYV w&io2OLgx6=ؿk&u_MS6i|= gfbMIbƽ4:L,9ervF[ҪcmsAOB`p"+ ~=s\pM.zYK/y,GE t-OtMs-W.WE2g.K쬸^`'8 /-iDnm8\%E,c_*jr OL Dpo a5rݯ{:՚*cRU]l >25nVk |!p q~Gs&ΩyN@l(Wn sc6|B]>6VOæ"yOS_S?H_1xdӛBk2brvLLE `_;Mc6WyUJkY=>km/#sZM*D;PQapJ2~9h.ۗc9ӱ2u-BM#jTT)9f㫔RD$j0u2/\VMM_ևM_Mm}/p}5_:'τJ#Tk:{~+},|O;՚XpBmcBDcoo{8٩Tk:]AuXV0%)Igrim2TF y3i#y'o>P m#G.ڝҾ2љ:\089.˙!a'.Mvi\09;bdDqzx/ldXmpĝ8xLp,뾚.TJ-b>i+q>dB%Ebq/1Gj‹Q)3=uJUxjx|L"YY]Vl˲C2ѾSD^"ml},r"`!QJ%l=u/.D 4PKogT嗄8 9iAwC?1N@tTCY広G̬نgľ7l4`Y qѾ|1K5B-oCh 1įёqN!9[ 51EԸ#kzԧ ȬTQͦBe]pSڹHDoBsth l}XSHTد G]_w՚W6P&!=cȵLOДfSpҬ]:n"S?p"l0B陬 "8,64*e;9MYCo~N5d+!,UJk)/u3?YRUJ\\R:ȯɿ+fFd^ N9f5O*XUpt"?L?u+-cV%Z @!H7dbL,Ÿ6KJ. 2؞8rmz3gT*9MYWvyy\=Je<.3q_扗W˚\D4?Jʰ^7DTk;ao@Znm2^ n_Od3gXdbWzjЗx}b_;5;n'q8E]})We<;xyW*g>"?ΚG_,%sw=nﱧ8 2zb  3b]댓g/68eM*ǐ)XBцeك*t&WD䐨!&C\{eI8 $7~I*t̊9(4eLX451ecfLu6&ǠQfz.ۓxaMfxMDw^ o݃4qt=id@- 2i_f7Wkn&WQ(ӌ\m}vdL#8@_9X:a$"{wg u-O3KGE_ *|UD\3 =Ճn]J% ~T.z$%8w]\A˅#D q5""2X aTsa*7e-y@ɳD5A\H;3)LE1wkE^54"D6sb1tb"!G6rՃ$9W=()U߹( ?;#ܶ^k瞸받^;]{swfWnX:7{e|=k ܤtB*opD|Mѝ\xD#xm6,O,-O Ϻs';:k*'"}]^U7_ڙc6RNOB1q8}yQQC@i5PVL~5hhE,SGF.TV Ŝ#r +]9´(/N)"GvTCյ4F"]نcRQe%qL)njTIbaC#9A67?Idy_Rg5DD_M5}kjL$za|o{ c.MuO8$ߚr3Dny{Oh\J@q7GZ|&"鷤`MR5l8҃z;N|ed 1D\AcDsw!"I׉}-K6WG\qrF Quq#?9#&bi;oj;4HgF¢P%ϜMqTĐscNHGDEϊwΏ.MviLL\dK1r[ؑx }DN>A}:]kl""W[n*P')"-(Tn~1ӯ*$1+lmQInNT?ޏB=Mw-feK%392=X abǠa.Ն@;rǾSD^"hAw.5 $&͚NaIp?1L±21Ua>S( 2z,S 3*SK;{{—s&.ۛEuqaiZ:sE`\#r#cM/dr^'9}5( 2+Y۾sdIc^v.*:/sU++K!J]=5|M_'!@Ob#+ܤ:4eXX,Q*KR8yhNH؜CϺa8=Ȳ|=i˪"[J ;ql7e\zY 29%;kI,Q̬BYƄL#\Y1fYgcr :>=OII&I V #S4 ~%ZwwT6vpqbPQk,n"4qt8bFٻw3j 5PM7 1mvaJnaDA&̱|Kvi88 䢢Ovy*""Bc:1 ֹ`pEqTT5aw IQ16xzxdt=DD(R2+yyzɨQ{;>5 tG[!.z՞\07k!F䖈نcURTº:?T4)<UNrz^F#w2TkkT?l .G| 25|,K>Pz{[[yſNˍj&Iܶ^kӿq=jy]rs` [(sSt5)# ? ܐ`鐉G{T.ASbTTqjbJ8#T,8rb19DD|bx6$gqGeC՟sf5ϢRt bP EY|dĨk/:H<#VETToܡReGE˱Y|d0T]K|xӈm8&U9ֹ`pųCR Qt$*'‚0u2]V¦nfæ6{.=+^| m~'9DkӶb܌z$c|ة~΁H_!cpTV9O""n""~+?rH},J8@q@&.燩]<8~QODDB#8o蒿IЙ @beRed*qYod#q(<:\089.˙!a'.Mvi\L"!"*:? 4{GA]a%zV^!sM79h}۸"vTϼ%A6m{O 䭼b>Qfy$lx/_ ҧSTWBiX?ڠ;h2u>[U "1Z҄oLzPǟATE]$\|xg鬘cBF<*@sA=$ڴa_a{~@ ,*kB1bžFO#p['3Ir|1KRY|Jlmt6[Vdz>kjb+,}51 51Xjb% bjbTk2=$db[qj nRak\ >A/6+eb{wsg/Lv)q,Re)ڔ2X7X&xixn_<}B) Xvy[xrNCE ;v <]φRˏEjk0eJ/ԉwvn *fY  ‘μCâZFz؃,/wqcg\Pyheك*Bu-=bYq=fSѳ +F&&,b,)tZ"**K7XP)K2=9&1A քIX2Dn.T N'dx|=fY,mqfo2C&z щBV`_lpD\/yD|NNEP)851|dĨDqdbJDdSB 0CR4ڗc9ӱaF`!5pL*Nѝ1dO&b%E$8rT5ݭl}f/c|֩~fgFޗ=)o,` uLm_hÈ͹S5(, 2 zY#YJH)iut[,_L9e*:TB#8w0ga|MHghwJDglsDWcg|$/gz'o87AE!"fr1(\Ɩfrv.ˈ8,NH֐h궋?prmVPBr22(𐉥^щ*c>6P$1z%bՃB*!n]ѡi|!1l-uuh.8LҾv4K?1C(7W|s&.6zp['SlL塊0ǞJGce_ǘRe,yċ,N׍ZizdaM{w՚NƝPkMK?J'qtGQ&=(<" Ep.YzC4e UW.;Q]PeR[KP|?Y4?EH#Z?..)\BPfzTF"}VL@뾚#}?QFhP[az@ &ko( 21tC&nR73=ME]mWMfh2 21CZ~q;}E921y`o,ދy,v~> 5.ڤgvnƴu./f8֐"4MvvOM,A!\ȍѮAiEc9%/*rNΈA( qQ<7p2 #g^XX!`Cg=y\(uߘgOzt@'5 N!"|lK_w~KĈX4]o9Q@d3#rh}jkL CO 6#f t4*_Rח5Ѱ~܊a yyǟ<ת&kPfe ~rQ 6%'Ǧͣ ?ƙU/#Qq1$'+K–L(V]s_Ŷ=x ~:!Gb3:Ecf[e.MKGǡ'ǦMuL׍8m5]8t] >M$.c m. ׍0+$]F?"""KXZZaiiԑ%A.uDoOA ,mj?'vo'oHL~]_%551""/&r紾muĈp90[!ѦLlwDDDDDbODDDDDTlĈ6@he ~nJ\+]qeDKDDDkL0>eDgVسwl1ӻn`XUDD[hQ%km,^i TXӴJ@$4(/8$Z?Ĉ6=X2/ۉGm~a%/r߸&aqWZv4M9Ă ]8Jz?a-#cD^}EñekpI{A866n-(/jDX4]@WD|;R~=Sa8hrȕ}/b%쌆O0oeUKB@swOT&6X\/~:%z4uYh SgĉWZ[,4=09v/ap,jӅ|7'WcF&hM&XtRjN'*Ǵ|A\Exיp,:bAKs*#:[uحt\)f>{<˥V_h1EBˑ']5bt"29Y fbDDOҟ=Fʪ>|tWݾ,Elъ)UhbID%mSI&WvN$5%jԙJ{^`W/WL7 Y꒲)][LB)pG[LB3"!Lk{1bGt{:su9rOC+OoQaloLv^/3W'{OszrlԪ@̀b-ˍZ?*P[.#YTԊx,UUyR6%AhԷYsvWf3f5)7htL2 ʎb2jox[=ܸsګϴpvTdԦ 7sטRCs Z˱h}vQX^G2t>dƟ1#"JL-? MrldTq[Ad-4UO58u6?fbDDDDDCꊵ:lhcTcQ1#"""""*6fbDDа+~syX5Z>a4hvAMf*,uPy7""7L0o܈MOڹ9'vK<裶}gOoeLh]FTv)UVj֜{:$$yQYŃ["[49q޷x4yO#98zdjCXϴ f c]BDDT$w"QS>G;ʝӅUiF]e7ZkW?< bkL 1O^?Dz `Sz|a:3fcQx;]eAwCut2*M㸸 n:5F?ɱt0Bîu\Pq#@RCΊV \V>&#+KQAQ2>kUۣ.Ŷ=xmVux}wCvd3*;OsddbNmT UwS|$>X\|c_ŧis+ݟڐ6VS[Cլ97ZBJB΍ٳeial\h(=~׆aPRCMF[N˧ݾUηh_͉(W=Oڕ;lҢ(XV_<ೄU/Ĉ Sg3ս媺]8t] ><'\eZ]!^=sOsZwJx&romq?ms;6=f.O\7hVg3܍.s\~1xp,ㅲVA ~yץS\DXZZaiiԑm l-qtZ_۹oBE&WzZP" jb(wNK PDDD눙[IQ;^ Ka JDDnx"""""bc&FDDDDDTlĈ["08r% VL0>k0R Vv+F;mVĈ6@he4J\0/gl~ŀt?$v!Wz|Ƥ>C:1⒚l'Mv^M.!5`NnQq1#"cq+G^rȕWƚF'{#y|g4l߆Ѱaw l#F?\fO5^ڜ^ K fqش96Gjւ 31"]nk9 qћb&FD|vGtȕe49@vM.qxNhsY-]@'*VQ2Gr96* Tj.Fh W5!W:4CSb+OTizW+ǽ+EY8_?^%.f繕 ""\m|ݾxRA8X}ç.hQ2*j6E!eVgmu6S*y>.WʝӦ:,vdu#N~ۄA(C~΍")kuilJ\vxx]t\œѰy?6WK7?3'""" Lha\+6?]аhrmR8hr$Ψ?(OtG4/x!+`;h=i.v-_4&HIJosv-^#""7L();:C,2h pժh4lN! 4<~d4lO֙ϯ˕'횕쮿xҮ4l>A TG垯հOMas0.2{"""ʃQe.s4lo+wNaSʝ_Mmr$oRKm q96' 63MO*r75ʁ~:JfUIDDD1#"/o-}R0gJ >))GR'}*m \pfS4l~pqylƳjJQg3խyrK9mP ;ݹJL6CtvRXpnĜdu#N+`3!W__Sh`&FDT51n64<~d4ltYf=o:ODD0#"FCt%а_K6l'Mv^S^Jubɱ!HQ;U-8hrM=]FK.uܧB{rDDDxD"<MŇ9QAcQBîu&[#ݍsT `Sz|a}dAfq䯙6G\FfST+boFvMu ~rQ ߯x/IYejUl;; {X{DDDTܝHDT:жe.Mh-Tup躼sLpWl1Ov^7hr=ַ iNΙ;6iSRcٽxў'chsY%""5AXZZ*u$DD hxz Lϸj3 iݦVsbvvɕV-i,DDA`MTCsZ_x)[ߚkbD ;v31"""""bc&FDDDDDTlĈJ)0x&XQ)>! lJt7J}z$5#""Qa|v`d;?%xYzyn)?fjՙj<іLh-\FX .|v[ۉGm~rNOt>F^B.% 'FDDDL(ACt{~!WX{ksԶm䯙+]'kas4|P쮿xҮY{J|yYՇ#f_j: ӟ`yhsLDGY 쮹/LkT^?zyIzz2rgDɿW'9mjQ᫹;_{@m^g4g" :#KV-wvoͦh3Bî[;!OUXڠ_oUu-D&W:w31"Lu[)kuilJ\vxx]t\?&;qrYoT: H.ύT_ALyN[HMҒ ,M%&dr{x\2P2Ax"""""bc&FDDDDDTlĈQ1#"""""*6fbDDcCJ,H`px1o~MQ_#"" L0>k0ޝ=A U$Bg '4<~dThbzكF3"""Ĉ6ϞT}7J%/OBovV%}W2{B._<(jlҙ `;ht׉&ǞKC+jG]M.!5аhrɥN\c_#""!;C,2as4|PY>96møU3jﳻ1" 8>Ifq䯙j5y|X[<GT5_) [/|mN5O0L\27hp40ߺj]?%+k3 ֶ>b&FDGY ʝhԪ{I'{r܋OfԞ>̻r3ݟj?mK)1Etȕۧ^EGT5kG݉aN5*٣.Ŷ=(fADFd4cduM3oG]_fo9cp3{ލ ګ% Ǣg*ׇ]Ĉ6 \-\g51~x/iA/ŤKʪ>|t7_n_rF+◩UFogw$̚` &)~K@=g|v\LVL\^{0|6eB8?8]/ھtH%hfALnj K1{ .KX%+l15 kbYj[1z0#"*LjH]Ě=W4*GGRu]輞|ÌbˍMv^7եyz(exOo㯙N5+2 ~Nh9qnAS%P6} 1߈\::[uحtw]%>)L3c}З6@{K4hU#K'w Ǣ#٠%mR@DvXը{fGhѰ>pm>.,K)1Z]ִA=4W/5Q hit>hS STOz}lc7Ub.1P5s~LD[`kݽ[S n1|PChKASuxt* ^Or[խ_[O_ *җ{7sRڎPؓu1t 9> *9Ĉ6ZR5+[o, us=jU5ڤcvJʋč|B͋Pʞd:{geݙ cۙIia戨;lA_}|_pތ!6eXzD﹑=6?%2)jD^h@ߙ&7!s)~R KKK ,--:"Bi,\+ku"_]v){5P2A;ظ;h5lhc51"""""bc&FDDDDDTlĈQ1#"""""*6;h}R@D[31",L[OWrĈSD[$M#"""""*6fbDDDDDDLؘADDD vL:{<{vFm0``px[ks8ڗ|~xX_@b:%y]_<9jj7g*ĈyսFª=VCw#4>k0R/ KDgu6s4lF4,Nkp#W:J/ ؔ3&>x,IC{|FCL}842\6ۉGs s8M= GnHZB.!۰UQɳ+ɚb\tMC̈́6Lkl)L 3H G;ʝ+ /۟ka=)=^=Lj9"!=_G5ZCAfUz|.wfH-/ȭx6?f4Vh:u!rh}j~NIY"$d4/yq/U ƥ>4;\l6KEcF31"ZcrOmjT4*;5O^^:hOtz?l_<4>PX Qb.d^5*PeL'Ǿ,ku*cխ"H^Sklͬvɕ/䁫VZdVR'f۸]s#jOM*VL(|7F`"g m-g=W{Pz;M*P /Ћ}qGF3kR SFZ$ 祐勄-bks۷SiB,Ԥ4 ao}8-wv{+RV޷ke: }avIkتN?( -[K8XPh拃뷚ҋ~0#"n===?isAwW8sW5UvY `v}*ՎgS2U sd+L$hXHh-lܶ|qXթM+FA|o)ݗ`x6C.{<;1LTҤRl&;MtA]cxmh8}"u~1x{&I!)jGõȞdVRL[ܚU􄥥%AJ f!B,~>' 4ݘ@s}OWnz/+W`J*8ߝsbɑtzzw~>_C¬59"!Bdr{xiWl>Wj2P2A%[?JHYZ- Xv-*͋ʘ ܝHD g[O5ުսS6.e$DV4Ĉҕ&FDeKĈ5Ov&"""""*6fbDDDDDDLx":"Z_ץ(?fbDD+կ~Uh:"pw"Q1#"""""*6fbDDkjG=c#"""bADfCa||yrÇUCHqG%=$!"""b&FDTGTN{O:?`_O1WcӳKw'gT_Ö?>7/n4-LOZ04T;n; \/_~}ǘto^!)Ma=NUo+n!|n8bMQ^Ds4)7ijn{ҹ:m9Hoқ]Wڔ.L۵ wx݉uaKG ~[yTriIo/2_zܝHDDVAXZZ*u$DD h_W?~ÿemh @&Wz':mN 3BA]쉈V}z?;֩+"""z+0#"IDDDD;ؘ31"qv ǿ4o8^"K|YGR`)Z3fov76f""`&FDT)?{gjuxƾ\x8z"G {(ZW5nck5{\k@]gJDD.DDy c`1t[ܶh˯bNYٮT@Ŏ*$U0z11!&}pza.b*4_k/z+69{@!VI+.*ʏύy;TGC9<ЫW'Gyygg m-g=dIns 4nҡ IDATIjWkHDD0#"+Lh>ݧ9sAWcv}RgU &'A^az:bRu\џ* gc `arm?nPY g*z{{~63K}Ƚ0K*WlADDT<Ĉ s-Q6&Cp GG4ٍj I\X '3 ]8NsDޟV6 TBm00zW =˼p%~B Ow@ԀSrg7+ {n[ 3ͽ6Yg4JkG$dќւhR@D[$R#!",AEå=="\=+qLm$+{c.%6̔w<%3B"Ax"wZ%kH|pŝ[3c7ϛm{jbDo#Ĉ ǚX ωEKDD"""""bc&FDDDDDTlĈ659WVC7`mnOA=Zscu/`hh%;Yn -֙!B*[7 iq-<)ɠDDT2Ĉ v罻z7@ֱ}. DJy=sC{R1Pwh[*Y!&sԘQQᘉmTMz?ukAbLcE=x=nT;gue c%R}xc]Ytw5Zd~oRo+5˷j3ZiA2{-]IEJmIqW)E6[sV;hM}3=8K`;36 fbDDy]2CwZRHe,|Ss:psB䂪G*1F9b9;p2:b*?ugv9 վ{ >snTVOi*0﹀{(:7*&!UƘz:S.۴,9$zjo~y ~rR\^dȽ&cf륥}fuȶiPj~gK;hS]쉈P Lc鶸s/Ћ} ̅^LLtȮI N/ Ƽ3DZ@B[tmt2 S'@oqz;K̙ƛe6ȹ YTUbG08R\^d?0kD:Bd!Es +I?,+_dhsb&FDT;zw&3Tv4>CutJmƞ)Q+T?Ep {tw}1w0`7h3[㌐{]97 fm_y" PӜRCkrIͽ~L`s!"b&FDTn|{ Ef4Hg nVFތ3nH vl.TcVCBdTmbf7=R;|"ԒT%ʽYV-gjqY?YxՆ-"$y֚9Q;Ԥ?m36`dFDD$R#!",AEå$,c;zm˒,XmfO֯7wk'+{c<1c&FDĀyG݅}0`8 zfV9w31"̘eNgbDo fbD 31"""""bc&FDDDDDTlĈ650`jfmÅK ~-7:[dy`cu/`ѽgyO!ѕ@1Q10#"*L!Jwa|Δt`5t<{(u^ʚfL&翬\1T@1o:47Į@DDDELh#ī[ r=*Uπٍj,f~t ]p/x=?ެX;3Z-ZoB/Rrc ""ba&FDN\Z ݗoj{bXnN6jJpCs>Eà ]{I)ޓ^ONwd*?"""L(|=5 yx\腔VUiH}=6TJ(T?ERr`$mZVv?"""*fbDDJWYIԏN™~]&W_S}j?Gz]ɼœpr+"""*Y ""} +p~"3Yjb+ڮj nCO^}g";Kwx_/@`$dќJzmj?AqG&k;Oy)w +lq$""u$,-- TH6 Abpȴ0`{J|@D9J^XӖ4"t pw"VQe{3c7MRGCDDDo51"t&FDmf31"""""bc&FDDDDDTlĈJlY~XyOs6LВe=Fwgey`h{םJf~""""dHb t{[|Nݾ1ojعVga'rPMǓa ""z;1#"3nU5- XZdY*u+QIŮKlՂ)hCwּx|gouv /Ӭ37>@ĸYkj}v9\L,WlA6)fbDDy]2CwZ,m_n"GC1w_FP=4Y7{.#{΍"BX {( F==yƭUꦚ[}VcL7s}?GPǍݫW'}k<оDO>`!""dhS Lc鶡NSs2JUq{:̅^LLtȮ%:Y@CG,x-wVsc^"8l\>8GF_4Z { OQ肬,\:BdYHDDTZĈ* gc `arY*z{{~Ŀ?G&Pg }S? z5{iN 2݉DD9pj]u:d*QmԆǧ3?H|jƾӫW $LHȢIy91)'XqH}h|ehZ.z^!""ڄ%AJ f!B,.uX%mfOR^_=[>XU$*L~:mIc!"J'w'QNjcZ {_Cau/c7MіŚQ:Ĉf&?'FDDDDDTlĈQ1#"*1s]{-{ -v32C<0iL}L.ot/yսqs3naӬ]SDDDD[31";|@JzGNݾ1ojؙbݤ{(J~`Wz"G 8ta9Q|Um8MqCG;buksGe<3i:旋i`ǛUpb\ICΞg74gXLg]""31"<.Ev;qC'0k#/c(ߛ=s=WFW!cWwUO=sTm47?yZOY]nVaݗojš?x}C1s*iu|ܸݽzu)f$zIs̝';6HSRI*M:hpe +.* 4t Tֶ7nC}JGƼxEHq؀{UFs4i}>\hk94>Es_Ybet݅[%s̝ĚQQ]_T54сyTv4B1mC0`A)ww>e`am'Y[MU?Er @{(9~B92QHs>]~orvmDDDo-Ĉ s"V6Tpo-2ճ7=NM[-t,SxH3Ft˶K03/ g[3:Kx-bC=TFBͩfFޏp#'W tO is_۝EaiiIRGBDYKEUۆ `jH6HX=[>XUn$Z3\+kuڒBDNĈ514NսS6.~|DDDoĈҽ51 kbD ;v31"""""bc&FDDDDDTlĈJN~JXܼ9~"K|Y]Y߻5XgV݃ս*N5^-2;0x;S8kv~?+jBlՎz =#z̿aj߯M05仡4e""Z?mF*+,)3^p:{zݨvR'?XRFKs<`hj&i-oU3+(-2leHxc]J fr6nOv5=Ƥw!8 IYYkaU-;e""Z7Ĉ򐲣މk2CK ik#/c(ߛ=s=WFk<о󞯦4P=&pDl =1zv:!zJsP`srw]PU43> x߿ߒ'3R Cɚky}C13]$zzt'MyO10ޓ͌?cA")`^ۗNƀJһ~ZOdwf2N$"Cm83zwb&xSs2JUq5#6ޞ4R;B-}OZhP`.bbBz43 0|l9w.,SNY>\hk94]}//Jg<`$O_~-xar d.,{qYE}R N Lp6,S^{_?4fe5T J\Nt;Zv 1{g>*z{{~ fpaKssW&>#YIJ  ӻ*tYrn$K{92[dLDD넙Qa}`Qj& vXJٍj +ڮj q{hU""M(ɽ>> ZiB6BcȡF4 #ܕfD0 7¥7J&:9:~RmVVdQBw1#Y-)%~BGC~fčZ,Mߙ U'ȃ*9+sNWFQ65r!1G64=2vǑ|QDPˊ v%V} Js^n4JezZadci r2vB^tlt#Sv# |ә Y6]]WC1x}?~n\* ;vq3[כi_]q~|EDD"Ĉ*'Ft'FD'{bDDDDDDJX+1"HLdh.Q3ȵf&/\ T$ wZa%_ ?&_jx3e""j'FDԜ뛘g:%zfԾIP$_CvIIȼ+ { nU,mZĞ1/ё.eMmЯKr\@R wH5LjpHEFR@U*Y3fK:*ՌPW<$g+b\eb|qDPTrwj^vDDĈH]fZg\iq]HQ3Hy<,?f7?%y~)ws/e&Llvi!( KM=Ҳѝ*/r)9լ͸l~HܜK>~9mGkF|xx2-z |ӂd氫BI58[$Đ UlRzkʳ(!Kϖs <u_W ][?$,6!fBU ɳe""j +1"tNjDxᙿ],IRK]D`³$]ڍFbYHWljɔtJvE8΢foO9špsPb}Rq쒒 y%7 !x $zhw:V :G[$ӟ +1"c )1lFqoy_4Mk`x0*!$.>t]P]ߘt(m]X$&۶5f;0tPk쥐DٓMKUBC(9-q֖̌#X!w:"Bܟu:wsص%y;7ӛ3kHo4W,u.VbDt#",TbD=VbDt +1"""""vc%FDDDDDnĈN3IE63I lM$HJ6_ X)|eF>TS2[جA#BĈLΩ\׌$7雖쒒]b}"NߌÝ#ZaޒaZ,XwUUkQD&"":X mAȐ4Z@ة5OͪYmN A&-jg+"$qhsJh{g.TCr$-jRE=2Sd,Dm_i1I5h*[iN "":aX5`ZtoH5ݞ (qW9Uy؏$MHSֲ~j Ne꘸-z SÓ]Yq/_ݙu|)U,:ha~V雩aGaXI ƻȬS D&6];ݱ҈b$*՗bNN;S ":tNjtJvE8΢ pX`v`KF~ӂ13[m[,8aRMx*T- z6{˕U vS ?ozۂ&It?,{XZ&2X x:z;%"":#":zv-T2i1ӥsغ=JٯW{⅂Ş۝(Xkj8`ZmD@b~:ZwF);>v1K L=_uYҋ/X+NN8Ĉ[s6UǟsUKw c9qͨʒ3MvY{9ߵ1ˎ{dle&Gd' 3.PnFxlv`@g'H^fha;ݩ"yA|3!":)A[)$v/b&D@z:d2}vM "6W'"~;L.MlM$)["{X<9U:=1"j{bDt'FDDDDDĈڍQ#":45ZȐ3ȍƷ\pFo/ J" $Ңv$j^ݘ_z)J9rb}QGw q&NKJzPb1%\6ghQP\Վ:-"":dg"cQ|$1tW&2İ-7 y⸺l!k`VjwƖ4^WS⋦cܩ8da%FDA]ʹϸs_N5ʭ@<<DwLtp֦TD 7 D_B}D%y~)ws/e&#Κ^.POjp|ĦkG6j'i3^Oz~ŭBfd"C0rI2.%ynj&A+F g"{(lP >|iA2M^9@(GWHxYx4W-*n^2!gSAl`%FDԀ]I@|q(=ܸ$)5]l: +<wRѨߴ`Lgxlo $Y8/|Zk47Rk eR.Olf.aہ] &*{׷ ^w}xƕ;^Ͱ5Pkrt kRla%FDtV[(qbZk @tYR 8,-jEp9լDN7V2V|ghe/vJGAVjqV: g]SDgp=!wmIRIͷRAĈ[kg<αϤ7=<HOHq;s1 Of]beX;*1T܌{,\'naT 7wDS'HWJFajWb{(|Tɖ N/P~cyZ:Xd^\H&zQ$"A΄!oGϺ쒼i48a?~n\* Qo'om0 .PU:{bDt'FDDDDDĈڍQ#"jD@ZZI @V)M$d"C%sH# يY&9l6j쥥ciqf6~LDD5nC7Q}α:$A;'71Y|.Tf`ȋ pqtpP=K!8G[դFwLDDJX9I58mCnL%6Q>>}R2!%(^95؀FhΒJ+ko Mk0(}X6PڻwXթaoCӴ]7u5sj87 F_JzVIu~ŭpNhu}5S2Y#pRqγ6=xx88ހ[1q/_9U ;M~? cu=fDRsjx+[[ᔄ5_W{BzP T?#cS-={k+(Y֖#"j.M֮NLSkL-tXR.=ZߠnEnvXmA6J`/M _=Y8[*G7I>NZ 'P}M]$u-Wg\S[ԿTƦ|5`9tpWթ=2VbDDGnbg-ƐuopzպJ('0S@vIo,KJl$ZQ"^twHvsIZkȐ ܒj T{|x#q?5-QG#"jslxTqFg*ÉdU%f&Gd!ʞ.w`W vi\!Vʚ< Y#cJ]V^+$ $âӋ“Q{-_UjOO{bDt'FDDDDDĈڍQ.DDuNZGu:"X>t DԂ7ot:"D"""""vc%FDDDDDnĈN+N'CDDDo)3ťʍ˝ͅADV?˪)S+3yKKOTn\VWڽ4Xcآ>I OX٫ܸ\=#`m6 5Q~y䏘zDwo/Unk^n~sBv҈NVbDDx݋/^\˿E_e{~": y}F^]ϋՈk\>g\U/pe@ojo_={Y֫4>,s[9w7 c{%#"":X5H/G-___n-ç(Vs7^o~W>dxj Ԉ ?P:]Oˍ˗PHHZQ[#"j9Ǐfݽ-X^,!|2__>UWOo*/ G/t2.Gܛwa_ 31,D:\^W%Z:7/LN Ar?jzۗ5[[DAo޼裏>TItNDDTEŞxu"a޼ySJDDDt(VbDDu89^HDDDDDnĈڍiH>gQkeF͌DjD6~YCUwGDDTQsZ|[$sj8wSZ( .)Ahnߌ:&6tC7g酉Ud|wDDDXzJ+4^E.V ]}&gL`m.d"Cr$Y#ӭɛc]4@&2( R\ m/+4g+bŹ%;-t2!98$Mi1PQK0VyZ˻^ǻ;""JĈHA3MkI5:ȩc58kSq хΩ~LΩ G _>xJ eX^TEKSֲnֽau)|qtg/YV>dWi.eow*-溯s3H+n=UD9k5T/I{k+VZ̈́&&VbDD ٥EϸSk]7Yk$LIS˨7DyU>Ր_ :kD[muWEmV7xAuXk$&{oVﴌbsÈ_HoٕOoI$z+1"cu*n }-Z@xj}X3j?~ߎ@ٙ9α5g˓9I2*3.+ sN@n!6\;oF$~%7ueieO^ ;]]^fmM$farDp 93Y~}a<-fLr'E:%JV&p#""jL NgBDtRNgqrd90'0&D@z:Vbw!jNӇj?ikDUMDF^#"*JR&xreՒ]_mg\ivG)8>u;;dĈj`%Ft#LޱX+1"""""vc%FDqLm.d"CRk9, \2D&P+ m? NZzVPU; VbDDi_m}H>zl+O}3d`CC #Z +1"TCr$-j63I>3 Sr0T;r$Y5ڊ%ϒKmN m/J!lqU_KvQe"Crp4KbؠTwzynJ0SryxwGDD6X5`BZto|MvƢ/rC ڔΩ@ A5SP%D&2 N T LlvX䫌feH,>Cz=[ 8/L@z|~i.b95<ٕ-B,ۙ~I:Sh3S6,F%Y,G fʟ5Qvi:sA3p.KI,n<"@t;I$I 46<7Hşn؍Ћ_6WHSV3k$&E^a/G=MKK'J9lzѫ39-I58I'veEa0ע氥f3 *{ =ު.0[mF)h܃у`Lwר^+{QscMvJ٥;)N Ǒ|&7kSf%]0Z^:>`wzP.l8 1_r\]V^[˽_z{MmprdSO)QϴgxV$@2]7V: ! ; I!Bߎ+z|q(=E.ɫݡs[<TD$rfZMxYx-0}vBDTISs "2[jV|.M~xƕ 5uro7 ffcvGDDt4#"t=1"j Ĉ$#"""""j7VbDDDDDDJXM" -jM$d"Cs3H/yQ38fHEjV3nː=J9GPHAOo6Na׃ك'c6uKJzX\Վq""ӄё.zG+K5l9%ctÞ'5<87:9uw[/jpHE<3BKn\p)SVWj$/jrnI2%Yh10\ŒPܩcj6ĈHA3MkI5+[>R|f8/L@j8*1/_z7҉QnXt!=sjxJ+ ӓjp֦pN ~ Ƹtd"E.46Ys,9=Kq+n=UD@>dWߵu5&Ov&"j.M$ 8^k={kD҆/]+u3Pމ2w; |!ӋI^c<©]l:vQ<$RVckexuwa޷M~X^jץ<[ϸ&?/""{bDDredD:2p XpN o8盾/2*=-e^& hH3WYvm+m`x2wT4zb+Vxp(6%!""z#"jsɆٙ?)Q>N@D+1"|NZ͛N@D^HDDDDDnĈڍѻz=16_}T""":XLl=t DDDtlx"Z_io=^^z*1oͬW^itM?z“]z^+1"&1f)%IDATǕTno_v/z?WڽG/xsyvo+%Ͽ=;ūܾכBv/$m?v -o^x^~êTa%FDԌڽmsp1 \88/Ž%>p^2z߿,~_nԛo{a{DDDtdX5H/Gɓr%0_ |2ZE9JW>qWaY~9tq""":X5kG_z7~ 迩ܸro߅7\W.U: *8ՙ{ܾqgN< rW'"B>!w:"Bܟ?>NCD-xG}O[tw2'"* .DDDDDDƫ͛N@DDDg+1"kN$"""""j7VbDDDDDDJXhj$!2!g*9sQy!Daaj!5n/X52#y^Q95S;;9˽֍SLoF]R҃US$""z?DDGOSBYvl'6ݶmc]dW6%kGxj]_Êm!xX. zӰ]?b:y""3=1"tNC5t^XmAO=l/,][ Ƴ*4JU),!^w}xƕ;hZL-TecY8X59ϟ{dl]7e8i naL1^gjD3P.9hgߥ(%^.2꺤 )F9kՇ]V^fЈB>!w:"Bܟu:S$$văDgS'M݇%"j7Ax""zG~뺚E|u=,v:""S=1"JIƞQ#"""""j7VbDDDDDDJ<1"L~,c%FDTI즶DՉDDDDDDJdg"""""ⓝ:Q#"""""j7VbDDDDDDJX+1"""""vc%FDDDDDnĈڍQ#"""""j7VbDDDDDDJX+1"""""vc%FDDDDDnĈڍQ#"""""j7VbDDDDDDm;<.qjIENDB`pyparsing2-2.4.7/docs/conf.py000066400000000000000000000124351365333160500160630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) from pyparsing import __version__ as pyparsing_version # -- Project information ----------------------------------------------------- project = 'PyParsing' copyright = '2018, Paul T. McGuire' author = 'Paul T. McGuire' # The short X.Y version version = pyparsing_version # The full version, including alpha/beta/rc tags release = pyparsing_version # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path . exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'PyParsingdoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'PyParsing.tex', 'PyParsing Documentation', 'Paul T. McGuire', 'manual'), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'pyparsing', 'PyParsing Documentation', [author], 1) ] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'PyParsing', 'PyParsing Documentation', author, 'PyParsing', 'One line description of project.', 'Miscellaneous'), ] # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # -- Extension configuration ------------------------------------------------- pyparsing2-2.4.7/docs/index.rst000066400000000000000000000010031365333160500164120ustar00rootroot00000000000000.. PyParsing documentation master file, created by sphinx-quickstart on Mon Nov 19 15:06:52 2018. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to PyParsing's documentation! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Release v\ |version| .. toctree:: :maxdepth: 2 :caption: Contents: HowToUsePyparsing modules CODE_OF_CONDUCT Indices and tables ~~~~~~~~~~~~~~~~~~ * :ref:`genindex` * :ref:`modindex` * :ref:`search` pyparsing2-2.4.7/docs/modules.rst000066400000000000000000000001001365333160500167500ustar00rootroot00000000000000pyparsing ========= .. toctree:: :maxdepth: 4 pyparsing pyparsing2-2.4.7/docs/pyparsing.rst000066400000000000000000000001661365333160500173300ustar00rootroot00000000000000pyparsing module ================ .. automodule:: pyparsing :members: :undoc-members: :show-inheritance: pyparsing2-2.4.7/examples/000077500000000000000000000000001365333160500154455ustar00rootroot00000000000000pyparsing2-2.4.7/examples/0README.html000066400000000000000000000257461365333160500173660ustar00rootroot00000000000000 pyparsing Examples

pyparsing Examples

This directory contains a number of Python scripts that can get you started in learning to use pyparsing.

  • greeting.py
    Parse "Hello, World!".
  • greetingInKorean.py ~ submission by June Kim
    Unicode example to parse "Hello, World!" in Korean.
  • greetingInGreek.py ~ submission by ???
    Unicode example to parse "Hello, World!" in Greek.
  • holaMundo.py ~ submission by Marco Alfonso
    "Hello, World!" example translated to Spanish, from Marco Alfonso's blog.
  • chemicalFormulas.py
    Simple example to demonstrate the use of ParseResults returned from parseString(). Parses a chemical formula (such as "H2O" or "C6H5OH"), and walks the returned list of tokens to calculate the molecular weight.
  • wordsToNum.py
    A sample program that reads a number in words (such as "fifteen hundred and sixty four"), and returns the actual number (1564). Also demonstrates some processing of ParseExceptions, including marking where the parse failure was found.
  • pythonGrammarparser.py ~ suggested by JH Stovall
    A sample program that parses the EBNF used in the Python source code to define the Python grammar. From this parser, one can generate Python grammar documentation tools, such as railroad track diagrams. Also demonstrates use of Dict class.
  • commasep.py
    Demonstration of the use of the commaSeparatedList helper. Shows examples of proper handling of commas within quotes, trimming of whitespace around delimited entries, and handling of consecutive commas (null arguments). Includes comparison with simple string.split(',').
  • dictExample.py
    A demonstration of using the Dict class, to parse a table of ASCII tabulated data.
  • dictExample2.py ~ submission by Mike Kelly
    An extended version of dictExample.py, in which Mike Kelly also parses the column headers, and generates a transposed version of the original table!
  • scanExamples.py
    Some examples of using scanString and transformString, as alternative parsing methods to parseString, to do macro substitution, and selection and/or removal of matching strings within a source file.
  • urlExtractorNew.py
    A sample program showing sample definitions and applications of HTML tag expressions created using makeHTMLTags helper function. Very useful for scraping data from HTML pages.
  • fourFn.py
    A simple algebraic expression parser, that performs +,-,*,/, and ^ arithmetic operations. (With suggestions and bug-fixes graciously offered by Andrea Griffini.)
  • SimpleCalc.py ~ submission by Steven Siew
    An interactive version of fourFn.py, with support for variables.
  • LAParser.py ~ submission by Mike Ellis
    An interactive Linear Algebra Parser, an extension of SimpleCalc.py. Supports linear algebra (LA) notation for vectors, matrices, and scalars, including matrix operations such as inversion and determinants. Converts LA expressions to C code - uses a separate C library for runtime evaluation of results.
  • configParse.py
    A simple alternative to Python's ConfigParse module, demonstrating the use of the Dict class to return nested dictionary access to configuration values.
  • getNTPserversNew.py
    Yet another scanString example, to read/extract the list of NTP servers from NIST's web site. Uses the new makeHTMLTags() method.
  • httpServerLogParser.py
    Parser for Apache server log files.
  • idlParse.py
    Parser for CORBA IDL files.
  • mozillaCalendarParser.py ~ submission by Petri Savolainen
    Parser for Mozilla calendar (*.ics) files.
  • pgn.py ~ submission by Alberto Santini
    Parser for PGN (Portable Game Notation) files, the standard form for documenting the moves in chess games.
  • simpleSQL.py
    A simple parser that will extract table and column names from SQL SELECT statements..
  • dfmparse.py ~ submission by Dan Griffith
    Parser for Delphi forms.
  • ebnf.py / ebnftest.py ~ submission by Seo Sanghyeon
    An EBNF-compiler that reads EBNF and generates a pyparsing grammar! Including a test that compiles... EBNF itself!
  • searchparser.py ~ submission by Steven Mooij and Rudolph Froger
    An expression parser that parses search strings, with special keyword and expression operations using (), not, and, or, and quoted strings.
  • sparser.py ~ submission by Tim Cera
    A configurable parser module that can be configured with a list of tuples, giving a high-level definition for parsing common sets of water table data files. Tim had to contend with several different styles of data file formats, each with slight variations of its own. Tim created a configurable parser (or "SPECIFIED parser" - hence the name "sparser"), that simply works from a config variable listing the field names and data types, and implicitly, their order in the source data file.

    See mayport_florida_8720220_data_def.txt for an example configuration file.

  • romanNumerals.py
    A Roman numeral generator and parser example, showing the power of parse actions to compile Roman numerals into their integer values.
  • removeLineBreaks.py
    A string transformer that converts text files with hard line-breaks into one with line breaks only between paragraphs. Useful when converting downloads from Project Gutenberg to import to word processing apps that can reformat paragraphs once hard line-breaks are removed, or for loading into your Palm Pilot for portable perusal.

    See Successful Methods of Public Speaking.txt and Successful Methods of Public Speaking(2).txt for a sample before and after (text file courtesy of Project Gutenberg).

  • listAllMatches.py
    An example program showing the utility of the listAllMatches option when specifying results naming.
  • linenoExample.py
    An example program showing how to use the string location to extract line and column numbers, or the source line of text.
  • parseListString.py
    An example program showing a progression of steps, how to parse a string representation of a Python list back into a true list.
  • parsePythonValue.py
    An extension of parseListString.py to parse tuples and dicts, including nested values, returning a Python value of the original type.
  • indentedGrammarExample.py
    An example program showing how to parse a grammar using indentation for grouping, such as is done in Python.
  • simpleArith.py
    An example program showing how to use the new operatorPrecedence helper method to define a 6-function (+, -, *, /, ^, and !) arithmetic expression parser, with unary plus and minus signs.
  • simpleBool.py
    An example program showing how to use the new operatorPrecedence helper method to define a boolean expression parser, with parse actions associated with each operator to "compile" the expression into a data structure that will evaluate the expression's boolean value.
  • simpleWiki.py
    An example program showing how to use transformString to implement a simple Wiki markup parser.
  • sql2dot.py~ submission by EnErGy [CSDX]
    A nice graphing program that generates schema diagrams from SQL table definition statements.
  • htmlStripper.py
    An example implementation of a common application, removing HTML markup tags from an HTML page, leaving just the text content.
  • macroExpansion.py
    An example implementation of a simple preprocessor, that will read embedded macro definitions and replace macro references with the defined substitution string.
  • sexpParser.py
    A parser that uses a recursive grammar to parse S-expressions.
  • nested.py
    An example using nestedExpr, a helper method to simplify definitions of expressions of nested lists.
  • withAttribute.py
    An example using withAttribute, a helper method to define parse actions to validate matched HTML tags using additional attributes. Especially helpful for matching common tags such as <DIV> and <TD>.
  • stackish.py
    A parser for the data representation format, Stackish.
  • builtin_parse_action_demo.py
    New in version 1.5.7
    Demonstration of using builtins (min, max, sum, len, etc.) as parse actions.
  • antlr_grammar.py~ submission by Luca DellOlio
    New in version 1.5.7
    Pyparsing example parsing ANTLR .a files and generating a working pyparsing parser.
  • shapes.py
    New in version 1.5.7
    Parse actions example simple shape definition syntax, and returning the matched tokens as domain objects instead of just strings.
  • datetimeParseActions.py
    New in version 1.5.7
    Parse actions example showing a parse action returning a datetime object instead of string tokens, and doing validation of the tokens, raising a ParseException if the given YYYY/MM/DD string does not represent a valid date.
  • position.py
    New in version 1.5.7
    Demonstration of a couple of different ways to capture the location a particular expression was found within the overall input string.
pyparsing2-2.4.7/examples/AcManForm.dfm000066400000000000000000001143411365333160500177440ustar00rootroot00000000000000object Form1: TForm1 Left = 193 Top = 105 Width = 696 Height = 480 Caption = 'AcManTest' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object RichEdit1: TRichEdit Left = 0 Top = 107 Width = 688 Height = 346 Align = alClient Lines.Strings = ( 'RichEdit1') TabOrder = 0 end object ActionToolBar1: TActionToolBar Left = 0 Top = 25 Width = 688 Height = 28 ActionManager = ActionManager1 Caption = 'ActionToolBar1' ColorMap.HighlightColor = 14410210 ColorMap.BtnSelectedColor = clBtnFace ColorMap.UnusedColor = 14410210 EdgeBorders = [ebTop, ebBottom] Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] ParentFont = False ParentShowHint = False ShowHint = True Spacing = 0 end object ActionMainMenuBar1: TActionMainMenuBar Left = 0 Top = 0 Width = 688 Height = 25 UseSystemFont = False ActionManager = ActionManager1 AnimationStyle = asSlide Caption = 'ActionMainMenuBar1' ColorMap.HighlightColor = 14410210 ColorMap.BtnSelectedColor = clBtnFace ColorMap.UnusedColor = 14410210 EdgeBorders = [ebTop, ebBottom] EdgeOuter = esNone Font.Charset = ANSI_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] ParentShowHint = False ShowHint = True Spacing = 0 end object ActionToolBar2: TActionToolBar Left = 0 Top = 53 Width = 688 Height = 28 ActionManager = ActionManager1 Caption = 'ActionToolBar2' ColorMap.HighlightColor = 14410210 ColorMap.BtnSelectedColor = clBtnFace ColorMap.UnusedColor = 14410210 EdgeBorders = [ebTop, ebBottom] Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] ParentFont = False ParentShowHint = False ShowHint = True Spacing = 0 end object ActionToolBar3: TActionToolBar Left = 0 Top = 81 Width = 688 Height = 26 ActionManager = ActionManager1 Caption = 'ActionToolBar3' ColorMap.HighlightColor = 14410210 ColorMap.BtnSelectedColor = clBtnFace ColorMap.UnusedColor = 14410210 Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] ParentFont = False Spacing = 0 end object ActionManager1: TActionManager FileName = 'settings' ActionBars.SessionCount = 4 ActionBars = < item Items = < item Action = EditUndo1 ImageIndex = 3 ShortCut = 16474 end item Action = EditCut1 ImageIndex = 0 ShortCut = 16472 end item Action = EditCopy1 ImageIndex = 1 ShortCut = 16451 end item Action = EditPaste1 ImageIndex = 2 ShortCut = 16470 end item Action = SearchFind1 ImageIndex = 15 ShortCut = 16454 end item Action = SearchReplace1 ImageIndex = 17 end> ActionBar = ActionToolBar1 AutoSize = False end item Items = < item Items = < item Action = FileOpen1 ImageIndex = 12 ShortCut = 16463 end item Action = FileSaveAs1 ImageIndex = 13 end item Action = FilePrintSetup1 end item Action = FileRun1 end item Action = FileExit1 ImageIndex = 14 LastSession = -1 UsageCount = -1 end> Caption = '&File' end item Items = < item Action = EditCut1 ImageIndex = 0 ShortCut = 16472 end item Action = EditCopy1 ImageIndex = 1 ShortCut = 16451 end item Action = EditPaste1 ImageIndex = 2 ShortCut = 16470 end item Action = EditSelectAll1 ShortCut = 16449 end item Action = EditUndo1 ImageIndex = 3 ShortCut = 16474 end item Action = EditDelete1 ImageIndex = 4 ShortCut = 46 end> Caption = '&Edit' end item Items = < item Action = RichEditBold1 ImageIndex = 5 ShortCut = 16450 end item Action = RichEditItalic1 ImageIndex = 6 ShortCut = 16457 end item Action = RichEditUnderline1 ImageIndex = 7 ShortCut = 16469 end item Action = RichEditStrikeOut1 end item Action = RichEditBullets1 ImageIndex = 8 end item Action = RichEditAlignLeft1 ImageIndex = 9 end item Action = RichEditAlignRight1 ImageIndex = 10 end item Action = RichEditAlignCenter1 ImageIndex = 11 end> Caption = 'F&ormat' end item Items = < item Action = SearchFind1 ImageIndex = 15 ShortCut = 16454 end item Action = SearchFindNext1 ImageIndex = 16 ShortCut = 114 end item Action = SearchReplace1 ImageIndex = 17 end item Action = SearchFindFirst1 end> Caption = '&Search' end item Items = < item Action = CustomizeActionBars1 end> Caption = '&Tools' end item Items = < item Action = HelpContents1 ImageIndex = 18 end> Caption = '&Help' end> ActionBar = ActionMainMenuBar1 AutoSize = False end item Items = < item Action = RichEditBold1 ImageIndex = 5 ShortCut = 16450 end item Action = RichEditItalic1 ImageIndex = 6 ShortCut = 16457 end item Action = RichEditUnderline1 ImageIndex = 7 ShortCut = 16469 end item Action = RichEditBullets1 Caption = 'Bull&ets' ImageIndex = 8 end item Action = RichEditAlignLeft1 ImageIndex = 9 end item Action = RichEditAlignRight1 ImageIndex = 10 end item Action = RichEditAlignCenter1 ImageIndex = 11 end> ActionBar = ActionToolBar2 AutoSize = False end item AutoSize = False end item AutoSize = False end item Items = < item Action = FileSaveAs1 ImageIndex = 13 LastSession = 2 end item Action = CustomizeActionBars1 end item Action = FileExit1 ImageIndex = 14 end item Action = HelpContents1 Caption = 'C&ontents' ImageIndex = 18 end item Action = ActionShowStatus Caption = '&ShowStatus' end> ActionBar = ActionToolBar3 AutoSize = False end> Images = ImageList1 Left = 88 Top = 136 StyleName = 'XP Style' object EditCut1: TEditCut Category = 'Edit' Caption = 'Cu&t' Hint = 'Cut|Cuts the selection and puts it on the Clipboard' ImageIndex = 0 ShortCut = 16472 end object EditCopy1: TEditCopy Category = 'Edit' Caption = '&Copy' Hint = 'Copy|Copies the selection and puts it on the Clipboard' ImageIndex = 1 ShortCut = 16451 end object EditPaste1: TEditPaste Category = 'Edit' Caption = '&Paste' Hint = 'Paste|Inserts Clipboard contents' ImageIndex = 2 ShortCut = 16470 end object EditSelectAll1: TEditSelectAll Category = 'Edit' Caption = 'Select &All' Hint = 'Select All|Selects the entire document' ShortCut = 16449 end object EditUndo1: TEditUndo Category = 'Edit' Caption = '&Undo' Hint = 'Undo|Reverts the last action' ImageIndex = 3 ShortCut = 16474 end object EditDelete1: TEditDelete Category = 'Edit' Caption = '&Delete' Hint = 'Delete|Erases the selection' ImageIndex = 4 ShortCut = 46 end object RichEditBold1: TRichEditBold Category = 'Format' AutoCheck = True Caption = '&Bold' Hint = 'Bold' ImageIndex = 5 ShortCut = 16450 end object RichEditItalic1: TRichEditItalic Category = 'Format' AutoCheck = True Caption = '&Italic' Hint = 'Italic' ImageIndex = 6 ShortCut = 16457 end object RichEditUnderline1: TRichEditUnderline Category = 'Format' AutoCheck = True Caption = '&Underline' Hint = 'Underline' ImageIndex = 7 ShortCut = 16469 end object RichEditStrikeOut1: TRichEditStrikeOut Category = 'Format' AutoCheck = True Caption = '&Strikeout' Hint = 'Strikeout' end object RichEditBullets1: TRichEditBullets Category = 'Format' AutoCheck = True Caption = '&Bullets' Hint = 'Bullets|Inserts a bullet on the current line' ImageIndex = 8 end object RichEditAlignLeft1: TRichEditAlignLeft Category = 'Format' AutoCheck = True Caption = 'Align &Left' Hint = 'Align Left|Aligns text at the left indent' ImageIndex = 9 end object RichEditAlignRight1: TRichEditAlignRight Category = 'Format' AutoCheck = True Caption = 'Align &Right' Hint = 'Align Right|Aligns text at the right indent' ImageIndex = 10 end object RichEditAlignCenter1: TRichEditAlignCenter Category = 'Format' AutoCheck = True Caption = '&Center' Hint = 'Center|Centers text between margins' ImageIndex = 11 end object FileOpen1: TFileOpen Category = 'File' Caption = '&Open...' Hint = 'Open|Opens an existing file' ImageIndex = 12 ShortCut = 16463 end object FileSaveAs1: TFileSaveAs Category = 'File' Caption = 'Save &As...' Hint = 'Save As|Saves the active file with a new name' ImageIndex = 13 end object FilePrintSetup1: TFilePrintSetup Category = 'File' Caption = 'Print Set&up...' Hint = 'Print Setup' end object FileRun1: TFileRun Category = 'File' Browse = False BrowseDlg.Title = 'Run' Caption = '&Run...' Hint = 'Run|Runs an application' Operation = 'open' ShowCmd = scShowNormal end object FileExit1: TFileExit Category = 'File' Caption = 'E&xit' Hint = 'Exit|Quits the application' ImageIndex = 14 end object SearchFind1: TSearchFind Category = 'Search' Caption = '&Find...' Hint = 'Find|Finds the specified text' ImageIndex = 15 ShortCut = 16454 end object SearchFindNext1: TSearchFindNext Category = 'Search' Caption = 'Find &Next' Enabled = False Hint = 'Find Next|Repeats the last find' ImageIndex = 16 ShortCut = 114 end object SearchReplace1: TSearchReplace Category = 'Search' Caption = '&Replace' Hint = 'Replace|Replaces specific text with different text' ImageIndex = 17 end object SearchFindFirst1: TSearchFindFirst Category = 'Search' Caption = 'F&ind First' Hint = 'Find First|Finds the first occurance of specified text' end object CustomizeActionBars1: TCustomizeActionBars Category = 'Tools' Caption = '&Customize' CustomizeDlg.StayOnTop = False end object HelpContents1: THelpContents Category = 'Help' Caption = '&Contents' Enabled = False Hint = 'Help Contents' ImageIndex = 18 end object ActionShowStatus: TAction Category = 'Tools' Caption = 'ShowStatus' OnExecute = ActionShowStatusExecute end end object ImageList1: TImageList Left = 168 Top = 136 Bitmap = { 494C010113001400040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000400000005000000001001000000000000028 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 1040104010420000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000010401040 FF7FFF7F18631042000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000010401040FF7FFF7F 0000000018631863104200000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000104210401040FF7FFF7F00000000 1040104000001863186310420000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000001863000000000000 0000000000000000186300000000000000000000000000000000000000000000 00000000000000000000000000000000000010421040FF7F0000000010401040 1040104010400000186318631042000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000010420000 0000000010420000000000000000000000000000000000001863000000000000 0000000000000000186300000000000000001042000000001040104010400042 E07F104010401040000018631863104200000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000010420000 0000000010420000000000000000000000001042104010401040104010401040 0042104010401040104000001863000000000000000000000000000000000000 0000000000000000000000000000000000000000000000001863000000000000 0000186300000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000001040FF7F1040104010401040 1040E07FE07F1040104010400000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000001863000000000000 0000186300000000000000000000000000000000000000001863000000000000 000018630000000000000000000000000000000000001040FF7F104010401040 104010400042E07FE07F10401040000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000001863000000000000 0000186300000000000000000000000000000000000000001040FF7F10401040 104000421040E07FE07F10401040104000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 1042000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000001040FF7F1040 1040E07FE07FE07F104010401040000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 104200000000000000000000000000000000000000000000000000001040FF7F 1040104010401040104000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000001040 FF7F104010400000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 1040104000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000001042104210421042104210421042 104210421042FF7F186310421863FF7F18630000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000001042104210421042104210421042 1042104210421042FF7F1042FF7F104210420000000000000000000000000000 0000000000000000000000000000000000000000000000420042000000000000 0000000000000000000000000042000000000000000000000000000000000000 0000000000000000000000000000000000001000100010001000000000001042 10421042FF7FFF7FFF7F10001000100010000000000000000000000000000000 0000000000000000000000000000000000000000000000420042000000000000 0000000000000000000000000042000000000000000000000000004200420000 00000000000018630000004200000000000000000000000010001F0010000000 00001042FF7FFF7FFF7F10000000000000000000FF7F00000000000000000000 0000000000000000FF7F00000000000000000000000000420042000000000000 0000000000000000000000000042000000000000000000000000004200420000 000000000000186300000042000000000000000000000000100010001F001000 0000FF7FFF7FFF7FFF7F10000000000000000000FF7F00000000000000000000 0000000000000000FF7F00000000000000000000000000420042000000000000 0000000000000000000000000042000000000000000000000000004200420000 00000000000000000000004200000000000000000000000010001F0010001F00 0000FF7FFF7FFF7FFF7F10000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000420042004200420042 0042004200420042004200420042000000000000000000000000004200420042 004200420042004200420042000000000000000000000000100010001F001000 0000FF7FFF03FF7FFF03100000000000000000000000FF7F0000000000000000 00000000FF7F0000000000000000000000000000000000420042000000000000 0000000000000000000000420042000000000000000000000000004200420000 00000000000000000042004200000000000000000000000010001F0010001F00 0000FF03FF7FFF03FF7F100000000000000000000000FF7F0000000000001863 00000000FF7F0000000000000000000000000000000000420000000000000000 0000000000000000000000000042000000000000000000000000004200001863 186318631863186300000042000000000000000000000000100010001F001000 0000FF7FFF03FF7FFF03100000000000000000000000FF7F0000000000001863 00000000FF7F0000000000000000000000000000000000420000000000000000 0000000000000000000000000042000000000000000000000000004200001863 18631863186318630000004200000000000000000000000010001F0010001F00 0000FF03FF7FFF03FF7F10000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000420000000000000000 0000000000000000000000000042000000000000000000000000004200001863 1863186318631863000000000000000000000000000000001000100010001000 100010001000100010001000000000000000000000000000FF7F000000000000 00000000FF7F0000000000000000000000000000000000420000000000000000 0000000000000000000000000042000000000000000000000000004200001863 1863186318631863000018630000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000420000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000420000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000002 0002000200020000000000000000000000000000000000000000FF7F00000000 000000000000FF7F000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000100010001000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000010001000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000001000 1000100010001000100010001000100010000000000000000000000000000000 0000000000000000000000000000000000000000000000000000100000000000 1000000000001000100000000000000000000000000000000000000000000000 1000100010001000100010001000100010000000000000000000000000001000 FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F10000000000000000000000000000000 0000000000000000000000000000000000000000000000000000100000000000 1000000010000000000010000000000000000000000000000000000000000000 1000FF7FFF7FFF7FFF7FFF7FFF7FFF7F10000000104200421042004210421000 FF7F000000000000000000000000FF7F10000000000000000000000000000000 0000000000000000000000000000000000000000000000000000100000000000 1000000010000000000010000000000000000000000000000000000000000000 1000FF7F00000000000000000000FF7F10000000004210420042104200421000 FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F10000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000010001000 1000000010000000000010000000000000000000000000000000000000000000 1000FF7FFF7FFF7FFF7FFF7FFF7FFF7F10000000104200421042004210421000 FF7F000000000000FF7F10001000100010000000000000000000000000000000 0000000000000000000010000000000000000000000000000000000000000000 10000000100010001000000000000000000000000000FF7FFF7FFF7FFF7FFF7F 1000FF7F00000000000000000000FF7F10000000004210420042104200421000 FF7FFF7FFF7FFF7FFF7F1000FF7F100000000000000010001000100010001000 0000000000000000000010000000000000000000000000000000000000000000 10000000100000000000000000000000000000000000FF7F0000000000000000 1000FF7FFF7FFF7FFF7FFF7FFF7FFF7F10000000104200421042004210421000 FF7FFF7FFF7FFF7FFF7F10001000000000000000000010001000100010000000 0000000000000000000000001000000000000000000000000000000000000000 00000000000000000000000000000000000000000000FF7FFF7FFF7FFF7FFF7F 1000FF7F00000000FF7F10001000100010000000004210420042104200421000 1000100010001000100010000000000000000000000010001000100000000000 0000000000000000000000001000000000000000000000000000000000000000 00000000000000000000000000000000000000000000FF7F0000000000000000 1000FF7FFF7FFF7FFF7F1000FF7F100000000000104200421042004210420042 1042004210420042104200420000000000000000000010001000000010000000 0000000000000000000000001000000000000000000000000000000000000000 00000000000000000000000000000000000000000000FF7FFF7FFF7FFF7FFF7F 1000FF7FFF7FFF7FFF7F10001000000000000000004210420000000000000000 0000000000000000104210420000000000000000000010000000000000001000 1000000000000000000010000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000FF7F00000000FF7F0000 1000100010001000100010000000000000000000104210420000000000000000 0000000000000000104200420000000000000000000000000000000000000000 0000100010001000100000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000FF7FFF7FFF7FFF7F0000 FF7F0000000000000000000000000000000000000042104200420000E07F0000 0000E07F00001042004210420000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000FF7FFF7FFF7FFF7F0000 000000000000000000000000000000000000000000000000000000000000E07F E07F000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000424D3E000000000000003E000000 2800000040000000500000000100010000000000800200000000000000000000 000000000000000000000000FFFFFF00FFFFB6E7FFFF0000FE49B76BFE3F0000 FE498427F81F0000FFFFB76BE00F0000FFFFCEE780070000C7C7FFFF00030000 C7C7C7C700010000C387C7C700000000C007C38700010000C007C00780010000 C007C007C0010000C007C007E0000000C007C007F0000000F39FC007F8030000 F39FF39FFC0F0000F39FF39FFE3F0000FFFFFF7E0000FFFFC001BFFF0000FFFF 8031F003000007C18031E003E00707C18031E003E00707C18001E003E0070101 8001E003E007000180012003E00700018FF1E002E00700018FF1E003E0078003 8FF1E003E007C1078FF1E003FFFFC1078FF1E003F81FE38F8FF5FFFFF81FE38F 8001BF7DF81FE38FFFFF7F7EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 8FFFFFFFFFFFFFFF8C03C007C007C0078FFFFFFFFFFFFFFFFFFFC03FF807F83F FFFFFFFFFFFFFFFF8FFFC007C007C0078C03FFFFFFFFFFFF8FFFC03FF807F01F FFFFFFFFFFFFFFFFFFFFC007C007C0078FFFFFFFFFFFFFFF8C03C03FF807F83F 8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF EFFDFFFFFFFFE00FC7FFFFFFFFFFFFFFC3FBF00F81FFF83FE3F7F8C7E3FFF39F F1E7F8C7F1FFF39FF8CFF8C7F8FFF39FFC1FF80FFC7FF39FFE3FF8C7FE3FF39F FC1FF8C7FF1FF39FF8CFF8C7FF8FF39FE1E7F00FFF03E10FC3F3FFFFFFFFFFFF C7FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9FFFFFFFC00FFFF F6CFFE008000FFFFF6B7FE000000FFFFF6B7FE000000FFFFF8B780000000FFF7 FE8F80000001C1F7FE3F80000003C3FBFF7F80000003C7FBFE3F80010003CBFB FEBF80030003DCF7FC9F80070FC3FF0FFDDF807F0003FFFFFDDF80FF8007FFFF FDDF81FFF87FFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 000000000000} end end pyparsing2-2.4.7/examples/LAparser.py000066400000000000000000000461001365333160500175310ustar00rootroot00000000000000""" Purpose: Linear Algebra Parser Based on: SimpleCalc.py example (author Paul McGuire) in pyparsing-1.3.3 Author: Mike Ellis Copyright: Ellis & Grant, Inc. 2005 License: You may freely use, modify, and distribute this software. Warranty: THIS SOFTWARE HAS NO WARRANTY WHATSOEVER. USE AT YOUR OWN RISK. Notes: Parses infix linear algebra (LA) notation for vectors, matrices, and scalars. Output is C code function calls. The parser can be run as an interactive interpreter or included as module to use for in-place substitution into C files containing LA equations. Supported operations are: OPERATION: INPUT OUTPUT Scalar addition: "a = b+c" "a=(b+c)" Scalar subtraction: "a = b-c" "a=(b-c)" Scalar multiplication: "a = b*c" "a=b*c" Scalar division: "a = b/c" "a=b/c" Scalar exponentiation: "a = b^c" "a=pow(b,c)" Vector scaling: "V3_a = V3_b * c" "vCopy(a,vScale(b,c))" Vector addition: "V3_a = V3_b + V3_c" "vCopy(a,vAdd(b,c))" Vector subtraction: "V3_a = V3_b - V3_c" "vCopy(a,vSubtract(b,c))" Vector dot product: "a = V3_b * V3_c" "a=vDot(b,c)" Vector outer product: "M3_a = V3_b @ V3_c" "a=vOuterProduct(b,c)" Vector magn. squared: "a = V3_b^Mag2" "a=vMagnitude2(b)" Vector magnitude: "a = V3_b^Mag" "a=sqrt(vMagnitude2(b))" Matrix scaling: "M3_a = M3_b * c" "mCopy(a,mScale(b,c))" Matrix addition: "M3_a = M3_b + M3_c" "mCopy(a,mAdd(b,c))" Matrix subtraction: "M3_a = M3_b - M3_c" "mCopy(a,mSubtract(b,c))" Matrix multiplication: "M3_a = M3_b * M3_c" "mCopy(a,mMultiply(b,c))" Matrix by vector mult.: "V3_a = M3_b * V3_c" "vCopy(a,mvMultiply(b,c))" Matrix inversion: "M3_a = M3_b^-1" "mCopy(a,mInverse(b))" Matrix transpose: "M3_a = M3_b^T" "mCopy(a,mTranspose(b))" Matrix determinant: "a = M3_b^Det" "a=mDeterminant(b)" The parser requires the expression to be an equation. Each non-scalar variable must be prefixed with a type tag, 'M3_' for 3x3 matrices and 'V3_' for 3-vectors. For proper compilation of the C code, the variables need to be declared without the prefix as float[3] for vectors and float[3][3] for matrices. The operations do not modify any variables on the right-hand side of the equation. Equations may include nested expressions within parentheses. The allowed binary operators are '+-*/^' for scalars, and '+-*^@' for vectors and matrices with the meanings defined in the table above. Specifying an improper combination of operands, e.g. adding a vector to a matrix, is detected by the parser and results in a Python TypeError Exception. The usual cause of this is omitting one or more tag prefixes. The parser knows nothing about a a variable's C declaration and relies entirely on the type tags. Errors in C declarations are not caught until compile time. Usage: To process LA equations embedded in source files, import this module and pass input and output file objects to the fprocess() function. You can can also invoke the parser from the command line, e.g. 'python LAparser.py', to run a small test suite and enter an interactive loop where you can enter LA equations and see the resulting C code. """ import re,sys from pyparsing import Word, alphas, ParseException, Literal, CaselessLiteral \ , Combine, Optional, nums, Forward, ZeroOrMore, \ StringEnd, alphanums # Debugging flag can be set to either "debug_flag=True" or "debug_flag=False" debug_flag=False #---------------------------------------------------------------------------- # Variables that hold intermediate parsing results and a couple of # helper functions. exprStack = [] # Holds operators and operands parsed from input. targetvar = None # Holds variable name to left of '=' sign in LA equation. def _pushFirst( str, loc, toks ): if debug_flag: print("pushing ", toks[0], "str is ", str) exprStack.append( toks[0] ) def _assignVar( str, loc, toks ): global targetvar targetvar = toks[0] #----------------------------------------------------------------------------- # The following statements define the grammar for the parser. point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) integer = Combine( Optional(plusorminus) + number ) floatnumber = Combine( integer + Optional( point + Optional(number) ) + Optional( e + integer ) ) lbracket = Literal("[") rbracket = Literal("]") ident = Forward() ## The definition below treats array accesses as identifiers. This means your expressions ## can include references to array elements, rows and columns, e.g., a = b[i] + 5. ## Expressions within []'s are not presently supported, so a = b[i+1] will raise ## a ParseException. ident = Combine(Word(alphas + '-',alphanums + '_') + \ ZeroOrMore(lbracket + (Word(alphas + '-',alphanums + '_')|integer) + rbracket) \ ) plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) outer = Literal( "@" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div | outer expop = Literal( "^" ) assignop = Literal( "=" ) expr = Forward() atom = ( ( e | floatnumber | integer | ident ).setParseAction(_pushFirst) | ( lpar + expr.suppress() + rpar ) ) factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( _pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( _pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( _pushFirst ) ) equation = (ident + assignop).setParseAction(_assignVar) + expr + StringEnd() # End of grammar definition #----------------------------------------------------------------------------- ## The following are helper variables and functions used by the Binary Infix Operator ## Functions described below. vprefix = 'V3_' vplen = len(vprefix) mprefix = 'M3_' mplen = len(mprefix) ## We don't support unary negation for vectors and matrices class UnaryUnsupportedError(Exception): pass def _isvec(ident): if ident[0] == '-' and ident[1:vplen+1] == vprefix: raise UnaryUnsupportedError else: return ident[0:vplen] == vprefix def _ismat(ident): if ident[0] == '-' and ident[1:mplen+1] == mprefix: raise UnaryUnsupportedError else: return ident[0:mplen] == mprefix def _isscalar(ident): return not (_isvec(ident) or _ismat(ident)) ## Binary infix operator (BIO) functions. These are called when the stack evaluator ## pops a binary operator like '+' or '*". The stack evaluator pops the two operand, a and b, ## and calls the function that is mapped to the operator with a and b as arguments. Thus, ## 'x + y' yields a call to addfunc(x,y). Each of the BIO functions checks the prefixes of its ## arguments to determine whether the operand is scalar, vector, or matrix. This information ## is used to generate appropriate C code. For scalars, this is essentially the input string, e.g. ## 'a + b*5' as input yields 'a + b*5' as output. For vectors and matrices, the input is translated to ## nested function calls, e.g. "V3_a + V3_b*5" yields "V3_vAdd(a,vScale(b,5)". Note that prefixes are ## stripped from operands and function names within the argument list to the outer function and ## the appropriate prefix is placed on the outer function for removal later as the stack evaluation ## recurses toward the final assignment statement. def _addfunc(a,b): if _isscalar(a) and _isscalar(b): return "(%s+%s)"%(a,b) if _isvec(a) and _isvec(b): return "%svAdd(%s,%s)"%(vprefix,a[vplen:],b[vplen:]) if _ismat(a) and _ismat(b): return "%smAdd(%s,%s)"%(mprefix,a[mplen:],b[mplen:]) else: raise TypeError def _subfunc(a,b): if _isscalar(a) and _isscalar(b): return "(%s-%s)"%(a,b) if _isvec(a) and _isvec(b): return "%svSubtract(%s,%s)"%(vprefix,a[vplen:],b[vplen:]) if _ismat(a) and _ismat(b): return "%smSubtract(%s,%s)"%(mprefix,a[mplen:],b[mplen:]) else: raise TypeError def _mulfunc(a,b): if _isscalar(a) and _isscalar(b): return "%s*%s"%(a,b) if _isvec(a) and _isvec(b): return "vDot(%s,%s)"%(a[vplen:],b[vplen:]) if _ismat(a) and _ismat(b): return "%smMultiply(%s,%s)"%(mprefix,a[mplen:],b[mplen:]) if _ismat(a) and _isvec(b): return "%smvMultiply(%s,%s)"%(vprefix,a[mplen:],b[vplen:]) if _ismat(a) and _isscalar(b): return "%smScale(%s,%s)"%(mprefix,a[mplen:],b) if _isvec(a) and _isscalar(b): return "%svScale(%s,%s)"%(vprefix,a[mplen:],b) else: raise TypeError def _outermulfunc(a,b): ## The '@' operator is used for the vector outer product. if _isvec(a) and _isvec(b): return "%svOuterProduct(%s,%s)"%(mprefix,a[vplen:],b[vplen:]) else: raise TypeError def _divfunc(a,b): ## The '/' operator is used only for scalar division if _isscalar(a) and _isscalar(b): return "%s/%s"%(a,b) else: raise TypeError def _expfunc(a,b): ## The '^' operator is used for exponentiation on scalars and ## as a marker for unary operations on vectors and matrices. if _isscalar(a) and _isscalar(b): return "pow(%s,%s)"%(str(a),str(b)) if _ismat(a) and b=='-1': return "%smInverse(%s)"%(mprefix,a[mplen:]) if _ismat(a) and b=='T': return "%smTranspose(%s)"%(mprefix,a[mplen:]) if _ismat(a) and b=='Det': return "mDeterminant(%s)"%(a[mplen:]) if _isvec(a) and b=='Mag': return "sqrt(vMagnitude2(%s))"%(a[vplen:]) if _isvec(a) and b=='Mag2': return "vMagnitude2(%s)"%(a[vplen:]) else: raise TypeError def _assignfunc(a,b): ## The '=' operator is used for assignment if _isscalar(a) and _isscalar(b): return "%s=%s"%(a,b) if _isvec(a) and _isvec(b): return "vCopy(%s,%s)"%(a[vplen:],b[vplen:]) if _ismat(a) and _ismat(b): return "mCopy(%s,%s)"%(a[mplen:],b[mplen:]) else: raise TypeError ## End of BIO func definitions ##---------------------------------------------------------------------------- # Map operator symbols to corresponding BIO funcs opn = { "+" : ( _addfunc ), "-" : ( _subfunc ), "*" : ( _mulfunc ), "@" : ( _outermulfunc ), "/" : ( _divfunc), "^" : ( _expfunc ), } ##---------------------------------------------------------------------------- # Recursive function that evaluates the expression stack def _evaluateStack( s ): op = s.pop() if op in "+-*/@^": op2 = _evaluateStack( s ) op1 = _evaluateStack( s ) result = opn[op]( op1, op2 ) if debug_flag: print(result) return result else: return op ##---------------------------------------------------------------------------- # The parse function that invokes all of the above. def parse(input_string): """ Accepts an input string containing an LA equation, e.g., "M3_mymatrix = M3_anothermatrix^-1" returns C code function calls that implement the expression. """ global exprStack global targetvar # Start with a blank exprStack and a blank targetvar exprStack = [] targetvar=None if input_string != '': # try parsing the input string try: L=equation.parseString( input_string ) except ParseException as err: print('Parse Failure', file=sys.stderr) print(err.line, file=sys.stderr) print(" "*(err.column-1) + "^", file=sys.stderr) print(err, file=sys.stderr) raise # show result of parsing the input string if debug_flag: print(input_string, "->", L) print("exprStack=", exprStack) # Evaluate the stack of parsed operands, emitting C code. try: result=_evaluateStack(exprStack) except TypeError: print("Unsupported operation on right side of '%s'.\nCheck for missing or incorrect tags on non-scalar operands."%input_string, file=sys.stderr) raise except UnaryUnsupportedError: print("Unary negation is not supported for vectors and matrices: '%s'"%input_string, file=sys.stderr) raise # Create final assignment and print it. if debug_flag: print("var=",targetvar) if targetvar != None: try: result = _assignfunc(targetvar,result) except TypeError: print("Left side tag does not match right side of '%s'"%input_string, file=sys.stderr) raise except UnaryUnsupportedError: print("Unary negation is not supported for vectors and matrices: '%s'"%input_string, file=sys.stderr) raise return result else: print("Empty left side in '%s'"%input_string, file=sys.stderr) raise TypeError ##----------------------------------------------------------------------------------- def fprocess(infilep,outfilep): """ Scans an input file for LA equations between double square brackets, e.g. [[ M3_mymatrix = M3_anothermatrix^-1 ]], and replaces the expression with a comment containing the equation followed by nested function calls that implement the equation as C code. A trailing semi-colon is appended. The equation within [[ ]] should NOT end with a semicolon as that will raise a ParseException. However, it is ok to have a semicolon after the right brackets. Other text in the file is unaltered. The arguments are file objects (NOT file names) opened for reading and writing, respectively. """ pattern = r'\[\[\s*(.*?)\s*\]\]' eqn = re.compile(pattern,re.DOTALL) s = infilep.read() def parser(mo): ccode = parse(mo.group(1)) return "/* %s */\n%s;\nLAParserBufferReset();\n"%(mo.group(1),ccode) content = eqn.sub(parser,s) outfilep.write(content) ##----------------------------------------------------------------------------------- def test(): """ Tests the parsing of various supported expressions. Raises an AssertError if the output is not what is expected. Prints the input, expected output, and actual output for all tests. """ print("Testing LAParser") testcases = [ ("Scalar addition","a = b+c","a=(b+c)"), ("Vector addition","V3_a = V3_b + V3_c","vCopy(a,vAdd(b,c))"), ("Vector addition","V3_a=V3_b+V3_c","vCopy(a,vAdd(b,c))"), ("Matrix addition","M3_a = M3_b + M3_c","mCopy(a,mAdd(b,c))"), ("Matrix addition","M3_a=M3_b+M3_c","mCopy(a,mAdd(b,c))"), ("Scalar subtraction","a = b-c","a=(b-c)"), ("Vector subtraction","V3_a = V3_b - V3_c","vCopy(a,vSubtract(b,c))"), ("Matrix subtraction","M3_a = M3_b - M3_c","mCopy(a,mSubtract(b,c))"), ("Scalar multiplication","a = b*c","a=b*c"), ("Scalar division","a = b/c","a=b/c"), ("Vector multiplication (dot product)","a = V3_b * V3_c","a=vDot(b,c)"), ("Vector multiplication (outer product)","M3_a = V3_b @ V3_c","mCopy(a,vOuterProduct(b,c))"), ("Matrix multiplication","M3_a = M3_b * M3_c","mCopy(a,mMultiply(b,c))"), ("Vector scaling","V3_a = V3_b * c","vCopy(a,vScale(b,c))"), ("Matrix scaling","M3_a = M3_b * c","mCopy(a,mScale(b,c))"), ("Matrix by vector multiplication","V3_a = M3_b * V3_c","vCopy(a,mvMultiply(b,c))"), ("Scalar exponentiation","a = b^c","a=pow(b,c)"), ("Matrix inversion","M3_a = M3_b^-1","mCopy(a,mInverse(b))"), ("Matrix transpose","M3_a = M3_b^T","mCopy(a,mTranspose(b))"), ("Matrix determinant","a = M3_b^Det","a=mDeterminant(b)"), ("Vector magnitude squared","a = V3_b^Mag2","a=vMagnitude2(b)"), ("Vector magnitude","a = V3_b^Mag","a=sqrt(vMagnitude2(b))"), ("Complicated expression", "myscalar = (M3_amatrix * V3_bvector)^Mag + 5*(-xyz[i] + 2.03^2)","myscalar=(sqrt(vMagnitude2(mvMultiply(amatrix,bvector)))+5*(-xyz[i]+pow(2.03,2)))"), ("Complicated Multiline", "myscalar = \n(M3_amatrix * V3_bvector)^Mag +\n 5*(xyz + 2.03^2)","myscalar=(sqrt(vMagnitude2(mvMultiply(amatrix,bvector)))+5*(xyz+pow(2.03,2)))") ] all_passed = [True] def post_test(test, parsed): # copy exprStack to evaluate and clear before running next test parsed_stack = exprStack[:] exprStack.clear() name, testcase, expected = next(tc for tc in testcases if tc[1] == test) this_test_passed = False try: try: result=_evaluateStack(parsed_stack) except TypeError: print("Unsupported operation on right side of '%s'.\nCheck for missing or incorrect tags on non-scalar operands."%input_string, file=sys.stderr) raise except UnaryUnsupportedError: print("Unary negation is not supported for vectors and matrices: '%s'"%input_string, file=sys.stderr) raise # Create final assignment and print it. if debug_flag: print("var=",targetvar) if targetvar != None: try: result = _assignfunc(targetvar,result) except TypeError: print("Left side tag does not match right side of '%s'"%input_string, file=sys.stderr) raise except UnaryUnsupportedError: print("Unary negation is not supported for vectors and matrices: '%s'"%input_string, file=sys.stderr) raise else: print("Empty left side in '%s'"%input_string, file=sys.stderr) raise TypeError parsed['result'] = result parsed['passed'] = this_test_passed = result == expected finally: all_passed[0] = all_passed[0] and this_test_passed print('\n' + name) equation.runTests((t[1] for t in testcases), postParse=post_test) ##TODO: Write testcases with invalid expressions and test that the expected ## exceptions are raised. print("Tests completed!") print("PASSED" if all_passed[0] else "FAILED") assert all_passed[0] ##---------------------------------------------------------------------------- ## The following is executed only when this module is executed as ## command line script. It runs a small test suite (see above) ## and then enters an interactive loop where you ## can enter expressions and see the resulting C code as output. if __name__ == '__main__': import sys if not sys.flags.interactive: # run testcases test() sys.exit(0) # input_string input_string='' # Display instructions on how to use the program interactively interactiveusage = """ Entering interactive mode: Type in an equation to be parsed or 'quit' to exit the program. Type 'debug on' to print parsing details as each string is processed. Type 'debug off' to stop printing parsing details """ print(interactiveusage) input_string = input("> ") while input_string != 'quit': if input_string == "debug on": debug_flag = True elif input_string == "debug off": debug_flag = False else: try: print(parse(input_string)) except Exception: pass # obtain new input string input_string = input("> ") # if user types 'quit' then say goodbye print("Good bye!") import os os._exit(0) pyparsing2-2.4.7/examples/Setup.ini000066400000000000000000000026431365333160500172530ustar00rootroot00000000000000[Startup] AppName=M3i.comm stname = Utility modemid=125D&DEV_1999 audioid=125D&DEV_1998 win98path= winmepath= win2kpath= winxppath= win95path= winnt4path= stupgrade =Install/Upgrade Drivers stuninstall =Uninstall Drivers stchoose =Choose One Function to Process stchoosez3 =Choose Devices to Process copycompl =Copying files completed RemString1=Set up has finished remove ESS device driver and cleaned your system. Click Finish to exit. RemString2=ESS devices is removed completely.No need to reboot. If you want to reinstall, run the setup again with driver package. stshowmsg1=Setup will clean the installed files and update registry. stshowmsg2=Setup is updating system's registry .... stshowmsg3=Setup is starting sysdriver=es56cvmp.sys mdmzn=mdmm3com.inf mdmznp=esmdm_98.inf mdmzna=mdmessa.inf spkname=essspk.exe remvess=remvess.exe slmcat=allem3m.cat audiocat=allem3.cat audioinf=M3i sysaudio=es198xdl.sys audiovxd=es198x.vxd [Languages] Default=0x0009 count=30 key0=0x002d key1=0x0003 key2=0x0804 key3=0x0404 key4=0x001a key5=0x0005 key6=0x0006 key7=0x0013 key8=0x0009 key9=0x000b key10=0x0c0c key11=0x040c key12=0x0007 key13=0x0008 key14=0x000e key15=0x0021 key16=0x0010 key17=0x0011 key18=0x0012 key19=0x0014 key20=0x0015 key21=0x0416 key22=0x0816 key23=0x0019 key24=0x001b key25=0x0024 key26=0x000a key27=0x001d key28=0x001e key29=0x001f [test] foo=bar pyparsing2-2.4.7/examples/SimpleCalc.py000066400000000000000000000061551365333160500200420ustar00rootroot00000000000000# SimpleCalc.py # # Demonstration of the parsing module, # Sample usage # # $ python SimpleCalc.py # Type in the string to be parse or 'quit' to exit the program # > g=67.89 + 7/5 # 69.29 # > g # 69.29 # > h=(6*g+8.8)-g # 355.25 # > h + 1 # 356.25 # > 87.89 + 7/5 # 89.29 # > ans+10 # 99.29 # > quit # Good bye! # # # Uncomment the line below for readline support on interactive terminal # import readline from pyparsing import ParseException, Word, alphas, alphanums import math # Debugging flag can be set to either "debug_flag=True" or "debug_flag=False" debug_flag=False variables = {} from fourFn import BNF, exprStack, fn, opn def evaluateStack( s ): op = s.pop() if op == 'unary -': return -evaluateStack( s ) if op in "+-*/^": op2 = evaluateStack( s ) op1 = evaluateStack( s ) return opn[op]( op1, op2 ) elif op == "PI": return math.pi # 3.1415926535 elif op == "E": return math.e # 2.718281828 elif op in fn: return fn[op]( evaluateStack( s ) ) elif op[0].isalpha(): if op in variables: return variables[op] raise Exception("invalid identifier '%s'" % op) else: return float( op ) arithExpr = BNF() ident = Word(alphas, alphanums).setName("identifier") assignment = ident("varname") + '=' + arithExpr pattern = assignment | arithExpr if __name__ == '__main__': # input_string input_string='' # Display instructions on how to quit the program print("Type in the string to be parsed or 'quit' to exit the program") input_string = input("> ") while input_string.strip().lower() != 'quit': if input_string.strip().lower() == 'debug': debug_flag=True input_string = input("> ") continue # Reset to an empty exprStack del exprStack[:] if input_string != '': # try parsing the input string try: L=pattern.parseString(input_string, parseAll=True) except ParseException as err: L=['Parse Failure', input_string, (str(err), err.line, err.column)] # show result of parsing the input string if debug_flag: print(input_string, "->", L) if len(L)==0 or L[0] != 'Parse Failure': if debug_flag: print("exprStack=", exprStack) # calculate result , store a copy in ans , display the result to user try: result=evaluateStack(exprStack) except Exception as e: print(str(e)) else: variables['ans']=result print(result) # Assign result to a variable if required if L.varname: variables[L.varname] = result if debug_flag: print("variables=", variables) else: print('Parse Failure') err_str, err_line, err_col = L[-1] print(err_line) print(" "*(err_col-1) + "^") print(err_str) # obtain new input string input_string = input("> ") # if user type 'quit' then say goodbye print("Good bye!") pyparsing2-2.4.7/examples/SingleForm.dfm000066400000000000000000001253141365333160500202100ustar00rootroot00000000000000object Form1: TForm1 Left = 161 Top = 149 Width = 696 Height = 342 Caption = 'DbxSingle' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object ActionToolBar2: TActionToolBar Left = 0 Top = 0 Width = 688 Height = 26 ActionManager = ActionManager1 AllowHiding = False Caption = 'ActionToolBar2' ColorMap.HighlightColor = 14410210 ColorMap.BtnSelectedColor = clBtnFace ColorMap.UnusedColor = 14410210 Spacing = 0 end object PageControl1: TPageControl Left = 0 Top = 26 Width = 688 Height = 289 ActivePage = TabSheet1 Align = alClient TabOrder = 1 object TabSheet1: TTabSheet Caption = 'Data' object DBGrid1: TDBGrid Left = 0 Top = 0 Width = 680 Height = 261 Align = alClient DataSource = DataSource1 TabOrder = 0 TitleFont.Charset = DEFAULT_CHARSET TitleFont.Color = clWindowText TitleFont.Height = -11 TitleFont.Name = 'MS Sans Serif' TitleFont.Style = [] end end object TabSheet2: TTabSheet Caption = 'Log' ImageIndex = 1 object Memo1: TMemo Left = 0 Top = 0 Width = 680 Height = 399 Align = alClient TabOrder = 0 end end end object SimpleDataSet1: TSimpleDataSet Aggregates = <> Connection.ConnectionName = 'IBLocal' Connection.DriverName = 'Interbase' Connection.GetDriverFunc = 'getSQLDriverINTERBASE' Connection.LibraryName = 'dbexpint.dll' Connection.LoginPrompt = False Connection.Params.Strings = ( 'BlobSize=-1' 'CommitRetain=False' 'Database=C:\Program Files\Common Files\Borland Shared\Data\emplo' + 'yee.gdb' 'DriverName=Interbase' 'Password=masterkey' 'RoleName=RoleName' 'ServerCharSet=ASCII' 'SQLDialect=1' 'Interbase TransIsolation=ReadCommited' 'User_Name=sysdba' 'WaitOnLocks=True') Connection.VendorLib = 'GDS32.DLL' DataSet.CommandText = 'EMPLOYEE' DataSet.CommandType = ctTable DataSet.MaxBlobSize = -1 DataSet.Params = <> Params = <> AfterPost = DoUpdate BeforeDelete = DoUpdate Left = 104 Top = 56 end object ActionManager1: TActionManager ActionBars = < item Items.CaptionOptions = coAll Items = < item Action = DataSetFirst1 ImageIndex = 0 end item Action = DataSetPrior1 ImageIndex = 1 end item Action = DataSetNext1 ImageIndex = 2 end item Action = DataSetLast1 ImageIndex = 3 end item Action = DataSetInsert1 ImageIndex = 4 end item Action = DataSetDelete1 ImageIndex = 5 end item Action = DataSetEdit1 ImageIndex = 6 end item Action = DataSetPost1 ImageIndex = 7 end item Action = DataSetCancel1 ImageIndex = 8 end item Action = DataSetRefresh1 ImageIndex = 9 end> ActionBar = ActionToolBar2 end> Images = ImageList1 Left = 112 Top = 184 StyleName = 'XP Style' object DataSetFirst1: TDataSetFirst Category = 'Dataset' Caption = 'First' ImageIndex = 0 end object DataSetPrior1: TDataSetPrior Category = 'Dataset' Caption = 'Prior' ImageIndex = 1 end object DataSetNext1: TDataSetNext Category = 'Dataset' Caption = 'Next' ImageIndex = 2 end object DataSetLast1: TDataSetLast Category = 'Dataset' Caption = 'Last' ImageIndex = 3 end object DataSetInsert1: TDataSetInsert Category = 'Dataset' Caption = 'Insert' ImageIndex = 4 end object DataSetDelete1: TDataSetDelete Category = 'Dataset' Caption = 'Delete' ImageIndex = 5 end object DataSetEdit1: TDataSetEdit Category = 'Dataset' Caption = 'Edit' ImageIndex = 6 end object DataSetPost1: TDataSetPost Category = 'Dataset' Caption = 'Post' ImageIndex = 7 end object DataSetCancel1: TDataSetCancel Category = 'Dataset' Caption = 'Cancel' ImageIndex = 8 end object DataSetRefresh1: TDataSetRefresh Category = 'Dataset' Caption = 'Refresh' ImageIndex = 9 end end object ImageList1: TImageList Left = 112 Top = 120 Bitmap = { 494C01010C000F00040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000400000004000000001002000000000000040 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400848484008484840084848400848484008484 8400848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000084 8400000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 00000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0000848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400000000008484840000000000000000000000 0000000000000000000000000000000000000000000000000000008484000084 8400000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 00000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0000848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000848484000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000084 8400000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 00000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0000848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000084 8400000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 00000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0000848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000008484 8400000000008484840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000084 8400008484000084840000848400008484000084840000848400008484000084 8400008484000084840000000000000000000000000000000000000000000000 00000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0000848484000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000084 8400000000000000000000000000000000000000000000000000000000000000 0000008484000084840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000008484840000000000000000000000000084848400000000000000 0000000000000000000000000000000000000000000000000000008484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000008484 8400000000000000000084848400000000008484840000000000000000000000 0000000000000000000000000000000000000000000000000000008484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000008484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000084840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000008484840000000000000000000000000084848400000000000000 0000000000000000000000000000000000000000000000000000008484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000008484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000084848400000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000084848400000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000848484000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000008484840000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000848484000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000008484840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000848484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000008484840000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000848484008484840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000848484008484 8400000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000848484000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000008484840000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000848484000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000008484840000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000084848400000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000848484000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000008484840000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000084848400000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000084848400000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000848484000000000000000000000000000000000000000000000000008484 8400000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000424D3E000000000000003E000000 2800000040000000400000000100010000000000000200000000000000000000 000000000000000000000000FFFFFF0000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000FFFFFFFFFFFFFC07FFFFFFFFC001F807 FFFFFFFF8031F807FFFFFC7F8031F807F3E7F0FF8031F807F1C7F1FF8001F807 F88FE3FF8001F807FC1FE7FF8001F80FFE3FE7078FF1FF7FFC1FE3878FF1FE3F F88FE1078FF1FC1FF1C7F0078FF1FFFFF3E7F8378FF1FEFFFFFFFFFF8FF5FFFF FFFFFFFF8001FDFFFFFFFFFFFFFF6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7FFFFFFFFFFBFFFC7FFFFFFFFFF1FF FC7FFFFFE007E0FFE00FE007F00FC47FE00FE007F81FCE3FE00FE007FC3FFF1F FC7FFFFFFE7FFF8FFC7FFFFFFFFFFFC7FC7FFFFFFFFFFFE7FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7E7FF9FF9FFE7E7 E787FE1FF87FE1E7E607F81FF81FE067E007F01FF80FE007E607F81FF81FE067 E787FE1FF87FE1E7E7E7FF9FF9FFE7E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 000000000000} end object DataSource1: TDataSource DataSet = SimpleDataSet1 Left = 108 Top = 250 end object SQLMonitor1: TSQLMonitor OnTrace = SQLMonitor1Trace Left = 228 Top = 122 end end pyparsing2-2.4.7/examples/TAP.py000066400000000000000000000164121365333160500164470ustar00rootroot00000000000000# # TAP.py - TAP parser # # A pyparsing parser to process the output of the Perl # "Test Anything Protocol" # (https://metacpan.org/pod/release/PETDANCE/TAP-1.00/TAP.pm) # # TAP output lines are preceded or followed by a test number range: # 1..n # with 'n' TAP output lines. # # The general format of a TAP output line is: # ok/not ok (required) # Test number (recommended) # Description (recommended) # Directive (only when necessary) # # A TAP output line may also indicate abort of the test suit with the line: # Bail out! # optionally followed by a reason for bailing # # Copyright 2008, by Paul McGuire # from pyparsing import ParserElement,LineEnd,Optional,Word,nums,Regex,\ Literal,CaselessLiteral,Group,OneOrMore,Suppress,restOfLine,\ FollowedBy,empty __all__ = ['tapOutputParser', 'TAPTest', 'TAPSummary'] # newlines are significant whitespace, so set default skippable # whitespace to just spaces and tabs ParserElement.setDefaultWhitespaceChars(" \t") NL = LineEnd().suppress() integer = Word(nums) plan = '1..' + integer("ubound") OK,NOT_OK = map(Literal,['ok','not ok']) testStatus = (OK | NOT_OK) description = Regex("[^#\n]+") description.setParseAction(lambda t:t[0].lstrip('- ')) TODO,SKIP = map(CaselessLiteral,'TODO SKIP'.split()) directive = Group(Suppress('#') + (TODO + restOfLine | FollowedBy(SKIP) + restOfLine.copy().setParseAction(lambda t:['SKIP',t[0]]) )) commentLine = Suppress("#") + empty + restOfLine testLine = Group( Optional(OneOrMore(commentLine + NL))("comments") + testStatus("passed") + Optional(integer)("testNumber") + Optional(description)("description") + Optional(directive)("directive") ) bailLine = Group(Literal("Bail out!")("BAIL") + empty + Optional(restOfLine)("reason")) tapOutputParser = Optional(Group(plan)("plan") + NL) & \ Group(OneOrMore((testLine|bailLine) + NL))("tests") class TAPTest(object): def __init__(self,results): self.num = results.testNumber self.passed = (results.passed=="ok") self.skipped = self.todo = False if results.directive: self.skipped = (results.directive[0][0]=='SKIP') self.todo = (results.directive[0][0]=='TODO') @classmethod def bailedTest(cls,num): ret = TAPTest(empty.parseString("")) ret.num = num ret.skipped = True return ret class TAPSummary(object): def __init__(self,results): self.passedTests = [] self.failedTests = [] self.skippedTests = [] self.todoTests = [] self.bonusTests = [] self.bail = False if results.plan: expected = list(range(1, int(results.plan.ubound)+1)) else: expected = list(range(1,len(results.tests)+1)) for i,res in enumerate(results.tests): # test for bail out if res.BAIL: #~ print "Test suite aborted: " + res.reason #~ self.failedTests += expected[i:] self.bail = True self.skippedTests += [ TAPTest.bailedTest(ii) for ii in expected[i:] ] self.bailReason = res.reason break #~ print res.dump() testnum = i+1 if res.testNumber != "": if testnum != int(res.testNumber): print("ERROR! test %(testNumber)s out of sequence" % res) testnum = int(res.testNumber) res["testNumber"] = testnum test = TAPTest(res) if test.passed: self.passedTests.append(test) else: self.failedTests.append(test) if test.skipped: self.skippedTests.append(test) if test.todo: self.todoTests.append(test) if test.todo and test.passed: self.bonusTests.append(test) self.passedSuite = not self.bail and (set(self.failedTests)-set(self.todoTests) == set()) def summary(self, showPassed=False, showAll=False): testListStr = lambda tl : "[" + ",".join(str(t.num) for t in tl) + "]" summaryText = [] if showPassed or showAll: summaryText.append( "PASSED: %s" % testListStr(self.passedTests) ) if self.failedTests or showAll: summaryText.append( "FAILED: %s" % testListStr(self.failedTests) ) if self.skippedTests or showAll: summaryText.append( "SKIPPED: %s" % testListStr(self.skippedTests) ) if self.todoTests or showAll: summaryText.append( "TODO: %s" % testListStr(self.todoTests) ) if self.bonusTests or showAll: summaryText.append( "BONUS: %s" % testListStr(self.bonusTests) ) if self.passedSuite: summaryText.append( "PASSED" ) else: summaryText.append( "FAILED" ) return "\n".join(summaryText) # create TAPSummary objects from tapOutput parsed results, by setting # class as parse action tapOutputParser.setParseAction(TAPSummary) if __name__ == "__main__": test1 = """\ 1..4 ok 1 - Input file opened not ok 2 - First line of the input valid ok 3 - Read the rest of the file not ok 4 - Summarized correctly # TODO Not written yet """ test2 = """\ ok 1 not ok 2 some description # TODO with a directive ok 3 a description only, no directive ok 4 # TODO directive only ok a description only, no directive ok # Skipped only a directive, no description ok """ test3 = """\ ok - created Board ok ok not ok ok ok ok ok # +------+------+------+------+ # | |16G | |05C | # | |G N C | |C C G | # | | G | | C +| # +------+------+------+------+ # |10C |01G | |03C | # |R N G |G A G | |C C C | # | R | G | | C +| # +------+------+------+------+ # | |01G |17C |00C | # | |G A G |G N R |R N R | # | | G | R | G | # +------+------+------+------+ ok - board has 7 tiles + starter tile 1..9 """ test4 = """\ 1..4 ok 1 - Creating test program ok 2 - Test program runs, no error not ok 3 - infinite loop # TODO halting problem unsolved not ok 4 - infinite loop 2 # TODO halting problem unsolved """ test5 = """\ 1..20 ok - database handle not ok - failed database login Bail out! Couldn't connect to database. """ test6 = """\ ok 1 - retrieving servers from the database # need to ping 6 servers ok 2 - pinged diamond ok 3 - pinged ruby not ok 4 - pinged sapphire ok 5 - pinged onyx not ok 6 - pinged quartz ok 7 - pinged gold 1..7 """ for test in (test1,test2,test3,test4,test5,test6): print(test) tapResult = tapOutputParser.parseString(test)[0] print(tapResult.summary(showAll=True)) print() pyparsing2-2.4.7/examples/__init__.py000066400000000000000000000000001365333160500175440ustar00rootroot00000000000000pyparsing2-2.4.7/examples/adventureEngine.py000066400000000000000000000475201365333160500211520ustar00rootroot00000000000000# adventureEngine.py # Copyright 2005-2006, Paul McGuire # # Updated 2012 - latest pyparsing API # from pyparsing import * import random import string def aOrAn( item ): if item.desc[0] in "aeiou": return "an " + item.desc else: return "a " + item.desc def enumerateItems(l): if len(l) == 0: return "nothing" out = [] if len(l) > 1: out.append(', '.join(aOrAn(item) for item in l[:-1])) out.append('and') out.append(aOrAn(l[-1])) return " ".join(out) def enumerateDoors(l): if len(l) == 0: return "" out = [] if len(l) > 1: out.append(', '.join(l[:-1])) out.append("and") out.append(l[-1]) return " ".join(out) class Room(object): def __init__(self, desc): self.desc = desc self.inv = [] self.gameOver = False self.doors = [None,None,None,None] def __getattr__(self,attr): return \ { "n":self.doors[0], "s":self.doors[1], "e":self.doors[2], "w":self.doors[3], }[attr] def enter(self,player): if self.gameOver: player.gameOver = True def addItem(self, it): self.inv.append(it) def removeItem(self,it): self.inv.remove(it) def describe(self): print(self.desc) visibleItems = [ it for it in self.inv if it.isVisible ] if random.random() > 0.5: if len(visibleItems) > 1: is_form = "are" else: is_form = "is" print("There {0} {1} here.".format(is_form, enumerateItems(visibleItems))) else: print("You see %s." % (enumerateItems(visibleItems))) class Exit(Room): def __init__(self): super(Exit,self).__init__("") def enter(self,player): player.gameOver = True class Item(object): items = {} def __init__(self, desc): self.desc = desc self.isDeadly = False self.isFragile = False self.isBroken = False self.isTakeable = True self.isVisible = True self.isOpenable = False self.useAction = None self.usableConditionTest = None self.cantTakeMessage = "You can't take that!" Item.items[desc] = self def __str__(self): return self.desc def breakItem(self): if not self.isBroken: print("") self.desc = "broken " + self.desc self.isBroken = True def isUsable(self, player, target): if self.usableConditionTest: return self.usableConditionTest( player, target ) else: return False def useItem(self, player, target): if self.useAction: self.useAction(player, self, target) class OpenableItem(Item): def __init__(self, desc, contents=None): super(OpenableItem,self).__init__(desc) self.isOpenable = True self.isOpened = False if contents is not None: if isinstance(contents, Item): self.contents = [contents,] else: self.contents = contents else: self.contents = [] def openItem(self, player): if not self.isOpened: self.isOpened = not self.isOpened if self.contents is not None: for item in self.contents: player.room.addItem( item ) self.contents = [] self.desc = "open " + self.desc def closeItem(self, player): if self.isOpened: self.isOpened = not self.isOpened if self.desc.startswith("open "): self.desc = self.desc[5:] class Command(object): "Base class for commands" def __init__(self, verb, verbProg): self.verb = verb self.verbProg = verbProg @staticmethod def helpDescription(): return "" def _doCommand(self, player): pass def __call__(self, player ): print(self.verbProg.capitalize()+"...") self._doCommand(player) class MoveCommand(Command): def __init__(self, quals): super(MoveCommand,self).__init__("MOVE", "moving") self.direction = quals.direction[0] @staticmethod def helpDescription(): return """MOVE or GO - go NORTH, SOUTH, EAST, or WEST (can abbreviate as 'GO N' and 'GO W', or even just 'E' and 'S')""" def _doCommand(self, player): rm = player.room nextRoom = rm.doors[ { "N":0, "S":1, "E":2, "W":3, }[self.direction] ] if nextRoom: player.moveTo( nextRoom ) else: print("Can't go that way.") class TakeCommand(Command): def __init__(self, quals): super(TakeCommand,self).__init__("TAKE", "taking") self.subject = quals.item @staticmethod def helpDescription(): return "TAKE or PICKUP or PICK UP - pick up an object (but some are deadly)" def _doCommand(self, player): rm = player.room subj = Item.items[self.subject] if subj in rm.inv and subj.isVisible: if subj.isTakeable: rm.removeItem(subj) player.take(subj) else: print(subj.cantTakeMessage) else: print("There is no %s here." % subj) class DropCommand(Command): def __init__(self, quals): super(DropCommand,self).__init__("DROP", "dropping") self.subject = quals.item @staticmethod def helpDescription(): return "DROP or LEAVE - drop an object (but fragile items may break)" def _doCommand(self, player): rm = player.room subj = Item.items[self.subject] if subj in player.inv: rm.addItem(subj) player.drop(subj) else: print("You don't have %s." % (aOrAn(subj))) class InventoryCommand(Command): def __init__(self, quals): super(InventoryCommand,self).__init__("INV", "taking inventory") @staticmethod def helpDescription(): return "INVENTORY or INV or I - lists what items you have" def _doCommand(self, player): print("You have %s." % enumerateItems( player.inv )) class LookCommand(Command): def __init__(self, quals): super(LookCommand,self).__init__("LOOK", "looking") @staticmethod def helpDescription(): return "LOOK or L - describes the current room and any objects in it" def _doCommand(self, player): player.room.describe() class DoorsCommand(Command): def __init__(self, quals): super(DoorsCommand,self).__init__("DOORS", "looking for doors") @staticmethod def helpDescription(): return "DOORS - display what doors are visible from this room" def _doCommand(self, player): rm = player.room numDoors = sum([1 for r in rm.doors if r is not None]) if numDoors == 0: reply = "There are no doors in any direction." else: if numDoors == 1: reply = "There is a door to the " else: reply = "There are doors to the " doorNames = [ {0:"north", 1:"south", 2:"east", 3:"west"}[i] for i,d in enumerate(rm.doors) if d is not None ] #~ print doorNames reply += enumerateDoors( doorNames ) reply += "." print(reply) class UseCommand(Command): def __init__(self, quals): super(UseCommand,self).__init__("USE", "using") self.subject = Item.items[quals.usedObj] if quals.targetObj: self.target = Item.items[quals.targetObj] else: self.target = None @staticmethod def helpDescription(): return "USE or U - use an object, optionally IN or ON another object" def _doCommand(self, player): rm = player.room availItems = rm.inv + player.inv if self.subject in availItems: if self.subject.isUsable( player, self.target ): self.subject.useItem( player, self.target ) else: print("You can't use that here.") else: print("There is no %s here to use." % self.subject) class OpenCommand(Command): def __init__(self, quals): super(OpenCommand,self).__init__("OPEN", "opening") self.subject = Item.items[quals.item] @staticmethod def helpDescription(): return "OPEN or O - open an object" def _doCommand(self, player): rm = player.room availItems = rm.inv+player.inv if self.subject in availItems: if self.subject.isOpenable: if not self.subject.isOpened: self.subject.openItem( player ) else: print("It's already open.") else: print("You can't open that.") else: print("There is no %s here to open." % self.subject) class CloseCommand(Command): def __init__(self, quals): super(CloseCommand,self).__init__("CLOSE", "closing") self.subject = Item.items[quals.item] @staticmethod def helpDescription(): return "CLOSE or CL - close an object" def _doCommand(self, player): rm = player.room availItems = rm.inv+player.inv if self.subject in availItems: if self.subject.isOpenable: if self.subject.isOpened: self.subject.closeItem( player ) else: print("You can't close that, it's not open.") else: print("You can't close that.") else: print("There is no %s here to close." % self.subject) class QuitCommand(Command): def __init__(self, quals): super(QuitCommand,self).__init__("QUIT", "quitting") @staticmethod def helpDescription(): return "QUIT or Q - ends the game" def _doCommand(self, player): print("Ok....") player.gameOver = True class HelpCommand(Command): def __init__(self, quals): super(HelpCommand,self).__init__("HELP", "helping") @staticmethod def helpDescription(): return "HELP or H or ? - displays this help message" def _doCommand(self, player): print("Enter any of the following commands (not case sensitive):") for cmd in [ InventoryCommand, DropCommand, TakeCommand, UseCommand, OpenCommand, CloseCommand, MoveCommand, LookCommand, DoorsCommand, QuitCommand, HelpCommand, ]: print(" - %s" % cmd.helpDescription()) print() class AppParseException(ParseException): pass class Parser(object): def __init__(self): self.bnf = self.makeBNF() def makeBNF(self): invVerb = oneOf("INV INVENTORY I", caseless=True) dropVerb = oneOf("DROP LEAVE", caseless=True) takeVerb = oneOf("TAKE PICKUP", caseless=True) | \ (CaselessLiteral("PICK") + CaselessLiteral("UP") ) moveVerb = oneOf("MOVE GO", caseless=True) | empty useVerb = oneOf("USE U", caseless=True) openVerb = oneOf("OPEN O", caseless=True) closeVerb = oneOf("CLOSE CL", caseless=True) quitVerb = oneOf("QUIT Q", caseless=True) lookVerb = oneOf("LOOK L", caseless=True) doorsVerb = CaselessLiteral("DOORS") helpVerb = oneOf("H HELP ?",caseless=True) itemRef = OneOrMore(Word(alphas)).setParseAction( self.validateItemName ) nDir = oneOf("N NORTH",caseless=True).setParseAction(replaceWith("N")) sDir = oneOf("S SOUTH",caseless=True).setParseAction(replaceWith("S")) eDir = oneOf("E EAST",caseless=True).setParseAction(replaceWith("E")) wDir = oneOf("W WEST",caseless=True).setParseAction(replaceWith("W")) moveDirection = nDir | sDir | eDir | wDir invCommand = invVerb dropCommand = dropVerb + itemRef("item") takeCommand = takeVerb + itemRef("item") useCommand = useVerb + itemRef("usedObj") + \ Optional(oneOf("IN ON",caseless=True)) + \ Optional(itemRef,default=None)("targetObj") openCommand = openVerb + itemRef("item") closeCommand = closeVerb + itemRef("item") moveCommand = moveVerb + moveDirection("direction") quitCommand = quitVerb lookCommand = lookVerb doorsCommand = doorsVerb helpCommand = helpVerb # attach command classes to expressions invCommand.setParseAction(InventoryCommand) dropCommand.setParseAction(DropCommand) takeCommand.setParseAction(TakeCommand) useCommand.setParseAction(UseCommand) openCommand.setParseAction(OpenCommand) closeCommand.setParseAction(CloseCommand) moveCommand.setParseAction(MoveCommand) quitCommand.setParseAction(QuitCommand) lookCommand.setParseAction(LookCommand) doorsCommand.setParseAction(DoorsCommand) helpCommand.setParseAction(HelpCommand) # define parser using all command expressions return ( invCommand | useCommand | openCommand | closeCommand | dropCommand | takeCommand | moveCommand | lookCommand | doorsCommand | helpCommand | quitCommand )("command") + LineEnd() def validateItemName(self,s,l,t): iname = " ".join(t) if iname not in Item.items: raise AppParseException(s,l,"No such item '%s'." % iname) return iname def parseCmd(self, cmdstr): try: ret = self.bnf.parseString(cmdstr) return ret except AppParseException as pe: print(pe.msg) except ParseException as pe: print(random.choice([ "Sorry, I don't understand that.", "Huh?", "Excuse me?", "???", "What?" ] )) class Player(object): def __init__(self, name): self.name = name self.gameOver = False self.inv = [] def moveTo(self, rm): self.room = rm rm.enter(self) if self.gameOver: if rm.desc: rm.describe() print("Game over!") else: rm.describe() def take(self,it): if it.isDeadly: print("Aaaagh!...., the %s killed me!" % it) self.gameOver = True else: self.inv.append(it) def drop(self,it): self.inv.remove(it) if it.isFragile: it.breakItem() def createRooms( rm ): """ create rooms, using multiline string showing map layout string contains symbols for the following: A-Z, a-z indicate rooms, and rooms will be stored in a dictionary by reference letter -, | symbols indicate connection between rooms <, >, ^, . symbols indicate one-way connection between rooms """ # start with empty dictionary of rooms ret = {} # look for room symbols, and initialize dictionary # - exit room is always marked 'Z' for c in rm: if c in string.ascii_letters: if c != "Z": ret[c] = Room(c) else: ret[c] = Exit() # scan through input string looking for connections between rooms rows = rm.split("\n") for row,line in enumerate(rows): for col,c in enumerate(line): if c in string.ascii_letters: room = ret[c] n = None s = None e = None w = None # look in neighboring cells for connection symbols (must take # care to guard that neighboring cells exist before testing # contents) if col > 0 and line[col-1] in "<-": other = line[col-2] w = ret[other] if col < len(line)-1 and line[col+1] in "->": other = line[col+2] e = ret[other] if row > 1 and col < len(rows[row-1]) and rows[row-1][col] in '|^': other = rows[row-2][col] n = ret[other] if row < len(rows)-1 and col < len(rows[row+1]) and rows[row+1][col] in '|.': other = rows[row+2][col] s = ret[other] # set connections to neighboring rooms room.doors=[n,s,e,w] return ret # put items in rooms def putItemInRoom(i,r): if isinstance(r,str): r = rooms[r] r.addItem( Item.items[i] ) def playGame(p,startRoom): # create parser parser = Parser() p.moveTo( startRoom ) while not p.gameOver: cmdstr = input(">> ") cmd = parser.parseCmd(cmdstr) if cmd is not None: cmd.command( p ) print() print("You ended the game with:") for i in p.inv: print(" -", aOrAn(i)) #==================== # start game definition roomMap = """ d-Z | f-c-e . | q'+"'")) | ('u' + Word(hexnums, exact=4)) | SGL_PRINTABLE) LITERAL_CHAR = ESC | ~(APOS | BSLASH) + SGL_PRINTABLE CHAR_LITERAL = APOS + LITERAL_CHAR + APOS STRING_LITERAL = APOS + Combine(OneOrMore(LITERAL_CHAR)) + APOS DOUBLE_QUOTE_STRING_LITERAL = '"' + ZeroOrMore(LITERAL_CHAR) + '"' DOUBLE_ANGLE_STRING_LITERAL = '<<' + ZeroOrMore(SGL_PRINTABLE) + '>>' TOKEN_REF = Word(alphas.upper(), alphanums+'_') RULE_REF = Word(alphas.lower(), alphanums+'_') ACTION_ESC = (BSLASH.suppress() + APOS | BSLASH.suppress() | BSLASH.suppress() + (~(APOS | QUOTE) + SGL_PRINTABLE) ) ACTION_CHAR_LITERAL = (APOS + (ACTION_ESC | ~(BSLASH | APOS) + SGL_PRINTABLE) + APOS) ACTION_STRING_LITERAL = (QUOTE + ZeroOrMore(ACTION_ESC | ~(BSLASH | QUOTE) + SGL_PRINTABLE) + QUOTE) SRC = SRC_.suppress() + ACTION_STRING_LITERAL("file") + INT("line") id = TOKEN_REF | RULE_REF SL_COMMENT = Suppress('//') + Suppress('$ANTLR') + SRC | ZeroOrMore(~EOL + Word(printables)) + EOL ML_COMMENT = cStyleComment WS = OneOrMore(Suppress(' ') | Suppress('\t') | (Optional(Suppress('\r')) + Literal('\n'))) WS_LOOP = ZeroOrMore(SL_COMMENT | ML_COMMENT) NESTED_ARG_ACTION = Forward() NESTED_ARG_ACTION << (LBRACK + ZeroOrMore(NESTED_ARG_ACTION | ACTION_STRING_LITERAL | ACTION_CHAR_LITERAL) + RBRACK) ARG_ACTION = NESTED_ARG_ACTION NESTED_ACTION = Forward() NESTED_ACTION << (LBRACE + ZeroOrMore(NESTED_ACTION | SL_COMMENT | ML_COMMENT | ACTION_STRING_LITERAL | ACTION_CHAR_LITERAL) + RBRACE) ACTION = NESTED_ACTION + Optional('?') SCOPE = SCOPE_.suppress() OPTIONS = OPTIONS_.suppress() + LBRACE # + WS_LOOP + Suppress('{') TOKENS = TOKENS_.suppress() + LBRACE # + WS_LOOP + Suppress('{') TREE_BEGIN = ROOT + LPAR RANGE = Suppress('..') REWRITE = Suppress('->') # General Parser Definitions # Grammar heading optionValue = id | STRING_LITERAL | CHAR_LITERAL | INT | Literal('*').setName("s") option = Group(id("id") + EQ + optionValue("value"))("option") optionsSpec = OPTIONS + Group(OneOrMore(option + SEMI))("options") + RBRACE tokenSpec = Group(TOKEN_REF("token_ref") + (EQ + (STRING_LITERAL | CHAR_LITERAL)("lit")))("token") + SEMI tokensSpec = TOKENS + Group(OneOrMore(tokenSpec))("tokens") + RBRACE attrScope = SCOPE_.suppress() + id + ACTION grammarType = LEXER + PARSER + TREE actionScopeName = id | LEXER("l") | PARSER("p") action = AT + Optional(actionScopeName + Suppress('::')) + id + ACTION grammarHeading = (Optional(ML_COMMENT("ML_COMMENT")) + Optional(grammarType) + GRAMMAR + id("grammarName") + SEMI + Optional(optionsSpec) + Optional(tokensSpec) + ZeroOrMore(attrScope) + ZeroOrMore(action)) modifier = PROTECTED | PUBLIC | PRIVATE | FRAGMENT ruleAction = AT + id + ACTION throwsSpec = THROWS.suppress() + delimitedList(id) ruleScopeSpec = ((SCOPE_.suppress() + ACTION) | (SCOPE_.suppress() + delimitedList(id) + SEMI) | (SCOPE_.suppress() + ACTION + SCOPE_.suppress() + delimitedList(id) + SEMI)) unary_op = oneOf("^ !") notTerminal = CHAR_LITERAL | TOKEN_REF | STRING_LITERAL terminal = (CHAR_LITERAL | TOKEN_REF + Optional(ARG_ACTION) | STRING_LITERAL | '.') + Optional(unary_op) block = Forward() notSet = TIL + (notTerminal | block) rangeNotPython = CHAR_LITERAL("c1") + RANGE + CHAR_LITERAL("c2") atom = Group((rangeNotPython + Optional(unary_op)("op")) | terminal | (notSet + Optional(unary_op)("op")) | (RULE_REF + Optional(ARG_ACTION("arg")) + Optional(unary_op)("op")) ) element = Forward() treeSpec = ROOT + LPAR + element*(2,) + RPAR ebnfSuffix = oneOf("? * +") ebnf = block + Optional(ebnfSuffix("op") | '=>') elementNoOptionSpec = ((id("result_name") + oneOf('= +=')("labelOp") + atom("atom") + Optional(ebnfSuffix)) | (id("result_name") + oneOf('= +=')("labelOp") + block + Optional(ebnfSuffix)) | atom("atom") + Optional(ebnfSuffix) | ebnf | ACTION | (treeSpec + Optional(ebnfSuffix)) ) # | SEMPRED ( '=>' -> GATED_SEMPRED | -> SEMPRED ) element <<= Group(elementNoOptionSpec)("element") # Do not ask me why group is needed twice... seems like the xml that you see is not always the real structure? alternative = Group(Group(OneOrMore(element))("elements")) rewrite = Optional(Literal('TODO REWRITE RULES TODO')) block <<= (LPAR + Optional(Optional(optionsSpec("opts")) + COLON) + Group(alternative('a1') + rewrite + Group(ZeroOrMore(VERT + alternative('a2') + rewrite))("alternatives"))("block") + RPAR) altList = alternative('a1') + rewrite + Group(ZeroOrMore(VERT + alternative('a2') + rewrite))("alternatives") exceptionHandler = CATCH.suppress() + ARG_ACTION + ACTION finallyClause = FINALLY.suppress() + ACTION exceptionGroup = (OneOrMore(exceptionHandler) + Optional(finallyClause)) | finallyClause ruleHeading = (Optional(ML_COMMENT)("ruleComment") + Optional(modifier)("modifier") + id("ruleName") + Optional("!") + Optional(ARG_ACTION("arg")) + Optional(Suppress('returns') + ARG_ACTION("rt")) + Optional(throwsSpec) + Optional(optionsSpec) + Optional(ruleScopeSpec) + ZeroOrMore(ruleAction)) rule = Group(ruleHeading + COLON + altList + SEMI + Optional(exceptionGroup))("rule") grammarDef = grammarHeading + Group(OneOrMore(rule))("rules") def grammar(): return grammarDef def __antlrAlternativesConverter(pyparsingRules, antlrBlock): rule = None if hasattr(antlrBlock, 'alternatives') and antlrBlock.alternatives != '' and len(antlrBlock.alternatives) > 0: alternatives = [] alternatives.append(__antlrAlternativeConverter(pyparsingRules, antlrBlock.a1)) for alternative in antlrBlock.alternatives: alternatives.append(__antlrAlternativeConverter(pyparsingRules, alternative)) rule = MatchFirst(alternatives)("anonymous_or") elif hasattr(antlrBlock, 'a1') and antlrBlock.a1 != '': rule = __antlrAlternativeConverter(pyparsingRules, antlrBlock.a1) else: raise Exception('Not yet implemented') assert rule != None return rule def __antlrAlternativeConverter(pyparsingRules, antlrAlternative): elementList = [] for element in antlrAlternative.elements: rule = None if hasattr(element.atom, 'c1') and element.atom.c1 != '': regex = r'['+str(element.atom.c1[0])+'-'+str(element.atom.c2[0]+']') rule = Regex(regex)("anonymous_regex") elif hasattr(element, 'block') and element.block != '': rule = __antlrAlternativesConverter(pyparsingRules, element.block) else: ruleRef = element.atom[0] assert ruleRef in pyparsingRules rule = pyparsingRules[ruleRef](ruleRef) if hasattr(element, 'op') and element.op != '': if element.op == '+': rule = Group(OneOrMore(rule))("anonymous_one_or_more") elif element.op == '*': rule = Group(ZeroOrMore(rule))("anonymous_zero_or_more") elif element.op == '?': rule = Optional(rule) else: raise Exception('rule operator not yet implemented : ' + element.op) rule = rule elementList.append(rule) if len(elementList) > 1: rule = Group(And(elementList))("anonymous_and") else: rule = elementList[0] assert rule is not None return rule def __antlrRuleConverter(pyparsingRules, antlrRule): rule = None rule = __antlrAlternativesConverter(pyparsingRules, antlrRule) assert rule != None rule(antlrRule.ruleName) return rule def antlrConverter(antlrGrammarTree): pyparsingRules = {} antlrTokens = {} for antlrToken in antlrGrammarTree.tokens: antlrTokens[antlrToken.token_ref] = antlrToken.lit for antlrTokenName, antlrToken in list(antlrTokens.items()): pyparsingRules[antlrTokenName] = Literal(antlrToken) antlrRules = {} for antlrRule in antlrGrammarTree.rules: antlrRules[antlrRule.ruleName] = antlrRule pyparsingRules[antlrRule.ruleName] = Forward() # antlr is a top down grammar for antlrRuleName, antlrRule in list(antlrRules.items()): pyparsingRule = __antlrRuleConverter(pyparsingRules, antlrRule) assert pyparsingRule != None pyparsingRules[antlrRuleName] <<= pyparsingRule return pyparsingRules if __name__ == "__main__": text = """\ grammar SimpleCalc; options { language = Python; } tokens { PLUS = '+' ; MINUS = '-' ; MULT = '*' ; DIV = '/' ; } /*------------------------------------------------------------------ * PARSER RULES *------------------------------------------------------------------*/ expr : term ( ( PLUS | MINUS ) term )* ; term : factor ( ( MULT | DIV ) factor )* ; factor : NUMBER ; /*------------------------------------------------------------------ * LEXER RULES *------------------------------------------------------------------*/ NUMBER : (DIGIT)+ ; /* WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; } ; */ fragment DIGIT : '0'..'9' ; """ grammar().validate() antlrGrammarTree = grammar().parseString(text) print(antlrGrammarTree.dump()) pyparsingRules = antlrConverter(antlrGrammarTree) pyparsingRule = pyparsingRules["expr"] pyparsingTree = pyparsingRule.parseString("2 - 5 * 42 + 7 / 25") print(pyparsingTree.dump()) pyparsing2-2.4.7/examples/antlr_grammar_tests.py000066400000000000000000000053271365333160500220760ustar00rootroot00000000000000''' Created on 4 sept. 2010 @author: luca Submitted by Luca DallOlio, September, 2010 ''' import unittest from . import antlr_grammar class Test(unittest.TestCase): def testOptionsSpec(self): text = """options { language = Python; }""" antlr_grammar.optionsSpec.parseString(text) #@UndefinedVariable def testTokensSpec(self): text = """tokens { PLUS = '+' ; MINUS = '-' ; MULT = '*' ; DIV = '/' ; }""" antlr_grammar.tokensSpec.parseString(text) #@UndefinedVariable def testBlock(self): text = """( PLUS | MINUS )""" antlr_grammar.block.parseString(text) #@UndefinedVariable def testRule(self): text = """expr : term ( ( PLUS | MINUS ) term )* ;""" antlr_grammar.rule.parseString(text) #@UndefinedVariable def testLexerRule(self): text = """fragment DIGIT : '0'..'9' ;""" antlr_grammar.rule.parseString(text) #@UndefinedVariable def testLexerRule2(self): text = """WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; } ;""" #antlr_grammar.rule.parseString(text) #@UndefinedVariable def testGrammar(self): text = """grammar SimpleCalc; options { language = Python; } tokens { PLUS = '+' ; MINUS = '-' ; MULT = '*' ; DIV = '/' ; } /*------------------------------------------------------------------ * PARSER RULES *------------------------------------------------------------------*/ expr : term ( ( PLUS | MINUS ) term )* ; term : factor ( ( MULT | DIV ) factor )* ; factor : NUMBER ; /*------------------------------------------------------------------ * LEXER RULES *------------------------------------------------------------------*/ NUMBER : (DIGIT)+ ; /* WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; } ; */ fragment DIGIT : '0'..'9' ;""" antlrGrammarTree = antlr_grammar.grammarDef.parseString(text) #@UndefinedVariable pyparsingRules = antlr_grammar.antlrConverter(antlrGrammarTree) pyparsingRule = pyparsingRules["expr"] pyparsingTree = pyparsingRule.parseString("2 - 5 * 42 + 7 / 25") pyparsingTreeList = pyparsingTree.asList() print(pyparsingTreeList) self.assertEqual(pyparsingTreeList, [[[['2'], []], [['-', [['5'], [['*', ['4', '2']]]]], ['+', [['7'], [['/', ['2', '5']]]]]]]] ) if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testOptionsSpec'] unittest.main() pyparsing2-2.4.7/examples/apicheck.py000066400000000000000000000042351365333160500175720ustar00rootroot00000000000000# apicheck.py # A simple source code scanner for finding patterns of the form # [ procname1 $arg1 $arg2 ] # and verifying the number of arguments # # Copyright (c) 2004-2016, Paul McGuire # from pyparsing import * # define punctuation and simple tokens for locating API calls LBRACK,RBRACK,LBRACE,RBRACE = map(Suppress,"[]{}") ident = Word(alphas,alphanums+"_") | QuotedString("{",endQuoteChar="}") arg = "$" + ident # define an API call with a specific number of arguments - using '-' # will ensure that after matching procname, an incorrect number of args will # raise a ParseSyntaxException, which will interrupt the scanString def apiProc(name, numargs): return LBRACK + Keyword(name)("procname") - arg*numargs + RBRACK # create an apiReference, listing all API functions to be scanned for, and # their respective number of arguments. Beginning the overall expression # with FollowedBy allows us to quickly rule out non-api calls while scanning, # since all of the api calls begin with a "[" apiRef = FollowedBy("[") + MatchFirst([ apiProc("procname1", 2), apiProc("procname2", 1), apiProc("procname3", 2), ]) test = """[ procname1 $par1 $par2 ] other code here [ procname1 $par1 $par2 $par3 ] more code here [ procname1 $par1 ] [ procname3 ${arg with spaces} $par2 ]""" # now explicitly iterate through the scanner using next(), so that # we can trap ParseSyntaxException's that would be raised due to # an incorrect number of arguments. If an exception does occur, # then see how we reset the input text and scanner to advance to the # next line of source code api_scanner = apiRef.scanString(test) while 1: try: t,s,e = next(api_scanner) print("found %s on line %d" % (t.procname, lineno(s,test))) except ParseSyntaxException as pe: print("invalid arg count on line", pe.lineno) print(pe.lineno,':',pe.line) # reset api scanner to start after this exception location test = "\n"*(pe.lineno-1)+test[pe.loc+1:] api_scanner = apiRef.scanString(test) except StopIteration: break pyparsing2-2.4.7/examples/btpyparse.py000066400000000000000000000104531365333160500200330ustar00rootroot00000000000000""" Pyparsing parser for BibTeX files A standalone parser using pyparsing. pyparsing has a simple and expressive syntax so the grammar is easy to read and write. Submitted by Matthew Brett, 2010 Simplified BSD license """ from pyparsing import (Regex, Suppress, ZeroOrMore, Group, Optional, Forward, SkipTo, CaselessLiteral, Dict) class Macro(object): """ Class to encapsulate undefined macro references """ def __init__(self, name): self.name = name def __repr__(self): return 'Macro("%s")' % self.name def __eq__(self, other): return self.name == other.name def __ne__(self, other): return self.name != other.name # Character literals LCURLY,RCURLY,LPAREN,RPAREN,QUOTE,COMMA,AT,EQUALS,HASH = map(Suppress,'{}()",@=#') def bracketed(expr): """ Return matcher for `expr` between curly brackets or parentheses """ return (LPAREN + expr + RPAREN) | (LCURLY + expr + RCURLY) # Define parser components for strings (the hard bit) chars_no_curly = Regex(r"[^{}]+") chars_no_curly.leaveWhitespace() chars_no_quotecurly = Regex(r'[^"{}]+') chars_no_quotecurly.leaveWhitespace() # Curly string is some stuff without curlies, or nested curly sequences curly_string = Forward() curly_item = Group(curly_string) | chars_no_curly curly_string << LCURLY + ZeroOrMore(curly_item) + RCURLY # quoted string is either just stuff within quotes, or stuff within quotes, within # which there is nested curliness quoted_item = Group(curly_string) | chars_no_quotecurly quoted_string = QUOTE + ZeroOrMore(quoted_item) + QUOTE # Numbers can just be numbers. Only integers though. number = Regex('[0-9]+') # Basis characters (by exclusion) for variable / field names. The following # list of characters is from the btparse documentation any_name = Regex('[^\\s"#%\'(),={}]+') # btparse says, and the test bibs show by experiment, that macro and field names # cannot start with a digit. In fact entry type names cannot start with a digit # either (see tests/bibs). Cite keys can start with a digit not_digname = Regex('[^\\d\\s"#%\'(),={}][^\\s"#%\'(),={}]*') # Comment comments out to end of line comment = (AT + CaselessLiteral('comment') + Regex(r"[\s{(].*").leaveWhitespace()) # The name types with their digiteyness not_dig_lower = not_digname.copy().setParseAction(lambda t: t[0].lower()) macro_def = not_dig_lower.copy() macro_ref = not_dig_lower.copy().setParseAction(lambda t : Macro(t[0].lower())) field_name = not_dig_lower.copy() # Spaces in names mean they cannot clash with field names entry_type = not_dig_lower('entry_type') cite_key = any_name('cite_key') # Number has to be before macro name string = (number | macro_ref | quoted_string | curly_string) # There can be hash concatenation field_value = string + ZeroOrMore(HASH + string) field_def = Group(field_name + EQUALS + field_value) entry_contents = Dict(ZeroOrMore(field_def + COMMA) + Optional(field_def)) # Entry is surrounded either by parentheses or curlies entry = (AT + entry_type + bracketed(cite_key + COMMA + entry_contents)) # Preamble is a macro-like thing with no name preamble = AT + CaselessLiteral('preamble') + bracketed(field_value) # Macros (aka strings) macro_contents = macro_def + EQUALS + field_value macro = AT + CaselessLiteral('string') + bracketed(macro_contents) # Implicit comments icomment = SkipTo('@').setParseAction(lambda t : t.insert(0, 'icomment')) # entries are last in the list (other than the fallback) because they have # arbitrary start patterns that would match comments, preamble or macro definitions = Group(comment | preamble | macro | entry | icomment) # Start symbol bibfile = ZeroOrMore(definitions) def parse_str(str): return bibfile.parseString(str) if __name__ == '__main__': # Run basic test txt = """ Some introductory text (implicit comment) @ARTICLE{Authors2011, author = {First Author and Second Author and Third Author}, title = {An article about {S}omething}, journal = "Journal of Articles", year = {2011}, volume = {16}, pages = {1140--1141}, number = {2} } """ print('\n\n'.join(defn.dump() for defn in parse_str(txt))) pyparsing2-2.4.7/examples/builtin_parse_action_demo.py000066400000000000000000000015651365333160500232270ustar00rootroot00000000000000# # builtin_parse_action_demo.py # Copyright, 2012 - Paul McGuire # # Simple example of using builtin functions as parse actions. # from pyparsing import * integer = Word(nums).setParseAction(lambda t : int(t[0])) # make an expression that will match a list of ints (which # will be converted to actual ints by the parse action attached # to integer) nums = OneOrMore(integer) test = "2 54 34 2 211 66 43 2 0" print(test) # try each of these builtins as parse actions for fn in (sum, max, min, len, sorted, reversed, list, tuple, set, any, all): fn_name = fn.__name__ if fn is reversed: # reversed returns an iterator, we really want to show the list of items fn = lambda x : list(reversed(x)) # show how each builtin works as a free-standing parse action print(fn_name, nums.setParseAction(fn).parseString(test)) pyparsing2-2.4.7/examples/cLibHeader.py000066400000000000000000000014431365333160500200030ustar00rootroot00000000000000# # cLibHeader.py # # A simple parser to extract API doc info from a C header file # # Copyright, 2012 - Paul McGuire # from pyparsing import Word, alphas, alphanums, Combine, oneOf, Optional, delimitedList, Group, Keyword testdata = """ int func1(float *vec, int len, double arg1); int func2(float **arr, float *vec, int len, double arg1, double arg2); """ ident = Word(alphas, alphanums + "_") vartype = Combine( oneOf("float double int char") + Optional(Word("*")), adjacent = False) arglist = delimitedList(Group(vartype("type") + ident("name"))) functionCall = Keyword("int") + ident("name") + "(" + arglist("args") + ")" + ";" for fn,s,e in functionCall.scanString(testdata): print(fn.name) for a in fn.args: print(" - %(name)s (%(type)s)" % a) pyparsing2-2.4.7/examples/chemicalFormulas.py000066400000000000000000000051271365333160500213020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # chemicalFormulas.py # # Copyright (c) 2003,2019 Paul McGuire # import pyparsing as pp atomicWeight = { "O" : 15.9994, "H" : 1.00794, "Na" : 22.9897, "Cl" : 35.4527, "C" : 12.0107, } digits = "0123456789" # Version 1 element = pp.Word(pp.alphas.upper(), pp.alphas.lower(), max=2) # for stricter matching, use this Regex instead # element = Regex("A[cglmrstu]|B[aehikr]?|C[adeflmorsu]?|D[bsy]|" # "E[rsu]|F[emr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airu]|" # "M[dgnot]|N[abdeiop]?|Os?|P[abdmortu]?|R[abefghnu]|" # "S[bcegimnr]?|T[abcehilm]|U(u[bhopqst])?|V|W|Xe|Yb?|Z[nr]") elementRef = pp.Group(element + pp.Optional(pp.Word(digits), default="1")) formula = elementRef[...] fn = lambda elemList : sum(atomicWeight[elem]*int(qty) for elem,qty in elemList) formula.runTests("""\ H2O C6H5OH NaCl """, fullDump=False, postParse=lambda _, tokens: "Molecular weight: {0}".format(fn(tokens))) print() # Version 2 - access parsed items by results name elementRef = pp.Group(element("symbol") + pp.Optional(pp.Word(digits), default="1")("qty")) formula = elementRef[...] fn = lambda elemList : sum(atomicWeight[elem.symbol]*int(elem.qty) for elem in elemList) formula.runTests("""\ H2O C6H5OH NaCl """, fullDump=False, postParse=lambda _, tokens: "Molecular weight: {0}".format(fn(tokens))) print() # Version 3 - convert integers during parsing process integer = pp.Word(digits).setParseAction(lambda t:int(t[0])) elementRef = pp.Group(element("symbol") + pp.Optional(integer, default=1)("qty")) formula = elementRef[...] fn = lambda elemList : sum(atomicWeight[elem.symbol]*elem.qty for elem in elemList) formula.runTests("""\ H2O C6H5OH NaCl """, fullDump=False, postParse=lambda _, tokens: "Molecular weight: {0}".format(fn(tokens))) print() # Version 4 - parse and convert integers as subscript digits subscript_digits = "₀₁₂₃₄₅₆₇₈₉" subscript_int_map = dict((e[1], e[0]) for e in enumerate(subscript_digits)) def cvt_subscript_int(s): ret = 0 for c in s[0]: ret = ret*10 + subscript_int_map[c] return ret subscript_int = pp.Word(subscript_digits).addParseAction(cvt_subscript_int) elementRef = pp.Group(element("symbol") + pp.Optional(subscript_int, default=1)("qty")) formula = elementRef[...] formula.runTests("""\ H₂O C₆H₅OH NaCl """, fullDump=False, postParse=lambda _, tokens: "Molecular weight: {0}".format(fn(tokens))) print() pyparsing2-2.4.7/examples/commasep.py000066400000000000000000000013061365333160500176230ustar00rootroot00000000000000# commasep.py # # comma-separated list example, to illustrate the advantages of using # the pyparsing comma_separated_list as opposed to string.split(","): # - leading and trailing whitespace is implicitly trimmed from list elements # - list elements can be quoted strings, which can safely contain commas without breaking # into separate elements # # Copyright (c) 2004-2016, Paul McGuire # import pyparsing as pp ppc = pp.pyparsing_common testData = [ "a,b,c,100.2,,3", "d, e, j k , m ", "'Hello, World', f, g , , 5.1,x", "John Doe, 123 Main St., Cleveland, Ohio", "Jane Doe, 456 St. James St., Los Angeles , California ", "", ] ppc.comma_separated_list.runTests(testData) pyparsing2-2.4.7/examples/configParse.py000066400000000000000000000037451365333160500202700ustar00rootroot00000000000000# # configparse.py # # an example of using the parsing module to be able to process a .INI configuration file # # Copyright (c) 2003, Paul McGuire # from pyparsing import \ Literal, Word, ZeroOrMore, Group, Dict, Optional, \ printables, ParseException, restOfLine, empty import pprint inibnf = None def inifile_BNF(): global inibnf if not inibnf: # punctuation lbrack = Literal("[").suppress() rbrack = Literal("]").suppress() equals = Literal("=").suppress() semi = Literal(";") comment = semi + Optional( restOfLine ) nonrbrack = "".join( [ c for c in printables if c != "]" ] ) + " \t" nonequals = "".join( [ c for c in printables if c != "=" ] ) + " \t" sectionDef = lbrack + Word( nonrbrack ) + rbrack keyDef = ~lbrack + Word( nonequals ) + equals + empty + restOfLine # strip any leading or trailing blanks from key def stripKey(tokens): tokens[0] = tokens[0].strip() keyDef.setParseAction(stripKey) # using Dict will allow retrieval of named data fields as attributes of the parsed results inibnf = Dict( ZeroOrMore( Group( sectionDef + Dict( ZeroOrMore( Group( keyDef ) ) ) ) ) ) inibnf.ignore( comment ) return inibnf pp = pprint.PrettyPrinter(2) def test( strng ): print(strng) try: iniFile = open(strng) iniData = "".join( iniFile.readlines() ) bnf = inifile_BNF() tokens = bnf.parseString( iniData ) pp.pprint( tokens.asList() ) except ParseException as err: print(err.line) print(" "*(err.column-1) + "^") print(err) iniFile.close() print() return tokens if __name__ == "__main__": ini = test("setup.ini") print("ini['Startup']['modemid'] =", ini['Startup']['modemid']) print("ini.Startup =", ini.Startup) print("ini.Startup.modemid =", ini.Startup.modemid) pyparsing2-2.4.7/examples/cpp_enum_parser.py000066400000000000000000000024751365333160500212110ustar00rootroot00000000000000# # cpp_enum_parser.py # # Posted by Mark Tolonen on comp.lang.python in August, 2009, # Used with permission. # # Parser that scans through C or C++ code for enum definitions, and # generates corresponding Python constant definitions. # # from pyparsing import * # sample string with enums and other stuff sample = ''' stuff before enum hello { Zero, One, Two, Three, Five=5, Six, Ten=10 }; in the middle enum blah { alpha, beta, gamma = 10 , zeta = 50 }; at the end ''' # syntax we don't want to see in the final parse tree LBRACE,RBRACE,EQ,COMMA = map(Suppress,"{}=,") _enum = Suppress('enum') identifier = Word(alphas,alphanums+'_') integer = Word(nums) enumValue = Group(identifier('name') + Optional(EQ + integer('value'))) enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue)) enum = _enum + identifier('enum') + LBRACE + enumList('names') + RBRACE # find instances of enums ignoring other syntax for item,start,stop in enum.scanString(sample): id = 0 for entry in item.names: if entry.value != '': id = int(entry.value) print('%s_%s = %d' % (item.enum.upper(),entry.name.upper(),id)) id += 1 pyparsing2-2.4.7/examples/datetimeParseActions.py000066400000000000000000000037161365333160500221360ustar00rootroot00000000000000# parseActions.py # # A sample program a parser to match a date string of the form "YYYY/MM/DD", # and return it as a datetime, or raise an exception if not a valid date. # # Copyright 2012, Paul T. McGuire # from datetime import datetime import pyparsing as pp from pyparsing import pyparsing_common as ppc # define an integer string, and a parse action to convert it # to an integer at parse time integer = pp.Word(pp.nums).setName("integer") def convertToInt(tokens): # no need to test for validity - we can't get here # unless tokens[0] contains all numeric digits return int(tokens[0]) integer.setParseAction(convertToInt) # or can be written as one line as #integer = Word(nums).setParseAction(lambda t: int(t[0])) # define a pattern for a year/month/day date date_expr = integer('year') + '/' + integer('month') + '/' + integer('day') date_expr.ignore(pp.pythonStyleComment) def convertToDatetime(s,loc,tokens): try: # note that the year, month, and day fields were already # converted to ints from strings by the parse action defined # on the integer expression above return datetime(tokens.year, tokens.month, tokens.day).date() except Exception as ve: errmsg = "'%s/%s/%s' is not a valid date, %s" % \ (tokens.year, tokens.month, tokens.day, ve) raise pp.ParseException(s, loc, errmsg) date_expr.setParseAction(convertToDatetime) date_expr.runTests("""\ 2000/1/1 # invalid month 2000/13/1 # 1900 was not a leap year 1900/2/29 # but 2000 was 2000/2/29 """) # if dates conform to ISO8601, use definitions in pyparsing_common date_expr = ppc.iso8601_date.setParseAction(ppc.convertToDate()) date_expr.ignore(pp.pythonStyleComment) date_expr.runTests("""\ 2000-01-01 # invalid month 2000-13-01 # 1900 was not a leap year 1900-02-29 # but 2000 was 2000-02-29 """) pyparsing2-2.4.7/examples/decaf_parser.py000066400000000000000000000161071365333160500204420ustar00rootroot00000000000000# # decaf_parser.py # # Rudimentary parser for decaf language, used in Stanford University CS143 # (https://web.stanford.edu/class/archive/cs/cs143/cs143.1128/handouts/030%20Decaf%20Specification.pdf) # # To convert this parser into one that gives more of an AST, change all the Group wrappers to add parse # actions that will result in ASTNode classes, or statement-specific subclasses. # # Copyright 2018, Paul McGuire # """ Program ::= Decl+ Decl ::= VariableDecl | FunctionDecl | ClassDecl | InterfaceDecl VariableDecl ::= Variable ; Variable ::= Type ident Type ::= int | double | bool | string | ident | Type [] FunctionDecl ::= Type ident ( Formals ) StmtBlock | void ident ( Formals ) StmtBlock Formals ::= Variable+, | e ClassDecl ::= class ident { Field* } Field ::= VariableDecl | FunctionDecl InterfaceDecl ::= interface ident { Prototype* } Prototype ::= Type ident ( Formals ) ; | void ident ( Formals ) ; StmtBlock ::= { VariableDecl* Stmt* } Stmt ::= ; | IfStmt | WhileStmt | ForStmt | BreakStmt | ReturnStmt | PrintStmt | StmtBlock IfStmt ::= if ( Expr ) Stmt WhileStmt ::= while ( Expr ) Stmt ForStmt ::= for ( ; Expr ; ) Stmt ReturnStmt ::= return ; BreakStmt ::= break ; PrintStmt ::= Print ( Expr+, ) ; Expr ::= LValue = Expr | Constant | LValue | this | Call | ( Expr ) | Expr + Expr | Expr - Expr | Expr * Expr | Expr / Expr | Expr % Expr | - Expr | Expr < Expr | Expr <= Expr | Expr > Expr | Expr >= Expr | Expr == Expr | Expr != Expr | Expr && Expr | Expr || Expr | ! Expr | ReadInteger ( ) | ReadLine ( ) | new ident | NewArray ( Expr , Typev) LValue ::= ident | Expr . ident | Expr [ Expr ] Call ::= ident ( Actuals ) | Expr . ident ( Actuals ) Actuals ::= Expr+, | e Constant ::= intConstant | doubleConstant | boolConstant | stringConstant | null """ import pyparsing as pp from pyparsing import pyparsing_common as ppc pp.ParserElement.enablePackrat() # keywords keywords = (VOID, INT, DOUBLE, BOOL, STRING, CLASS, INTERFACE, NULL, THIS, EXTENDS, IMPLEMENTS, FOR, WHILE, IF, ELSE, RETURN, BREAK, NEW, NEWARRAY, PRINT, READINTEGER, READLINE, TRUE, FALSE) = map(pp.Keyword, """void int double bool string class interface null this extends implements or while if else return break new NewArray Print ReadInteger ReadLine true false""".split()) keywords = pp.MatchFirst(list(keywords)) LPAR, RPAR, LBRACE, RBRACE, LBRACK, RBRACK, DOT, EQ, COMMA, SEMI = map(pp.Suppress, "(){}[].=,;") hexConstant = pp.Regex(r"0[xX][0-9a-fA-F]+").addParseAction(lambda t: int(t[0][2:], 16)) intConstant = hexConstant | ppc.integer doubleConstant = ppc.real boolConstant = TRUE | FALSE stringConstant = pp.dblQuotedString null = NULL constant = doubleConstant | boolConstant | intConstant | stringConstant | null ident = ~keywords + pp.Word(pp.alphas, pp.alphanums+'_') type_ = pp.Group((INT | DOUBLE | BOOL | STRING | ident) + pp.ZeroOrMore("[]")) variable = type_ + ident variable_decl = variable + SEMI expr = pp.Forward() expr_parens = pp.Group(LPAR + expr + RPAR) actuals = pp.Optional(pp.delimitedList(expr)) call = pp.Group(ident("call_ident") + LPAR + actuals("call_args") + RPAR | (expr_parens + pp.ZeroOrMore(DOT + ident))("call_ident_expr") + LPAR + actuals("call_args") + RPAR) lvalue = ((ident | expr_parens) + pp.ZeroOrMore(DOT + (ident | expr_parens)) + pp.ZeroOrMore(LBRACK + expr + RBRACK)) assignment = pp.Group(lvalue("lhs") + EQ + expr("rhs")) read_integer = pp.Group(READINTEGER + LPAR + RPAR) read_line = pp.Group(READLINE + LPAR + RPAR) new_statement = pp.Group(NEW + ident) new_array = pp.Group(NEWARRAY + LPAR + expr + COMMA + type_ + RPAR) rvalue = constant | call | read_integer | read_line | new_statement | new_array | ident arith_expr = pp.infixNotation(rvalue, [ ('-', 1, pp.opAssoc.RIGHT,), (pp.oneOf("* / %"), 2, pp.opAssoc.LEFT,), (pp.oneOf("+ -"), 2, pp.opAssoc.LEFT,), ]) comparison_expr = pp.infixNotation(arith_expr, [ ('!', 1, pp.opAssoc.RIGHT,), (pp.oneOf("< > <= >="), 2, pp.opAssoc.LEFT,), (pp.oneOf("== !="), 2, pp.opAssoc.LEFT,), (pp.oneOf("&&"), 2, pp.opAssoc.LEFT,), (pp.oneOf("||"), 2, pp.opAssoc.LEFT,), ]) expr <<= (assignment | call | THIS | comparison_expr | arith_expr | lvalue | constant | read_integer | read_line | new_statement | new_array ) stmt = pp.Forward() print_stmt = pp.Group(PRINT("statement") + LPAR + pp.Group(pp.Optional(pp.delimitedList(expr)))("args") + RPAR + SEMI) break_stmt = pp.Group(BREAK("statement") + SEMI) return_stmt = pp.Group(RETURN("statement") + expr + SEMI) for_stmt = pp.Group(FOR("statement") + LPAR + pp.Optional(expr) + SEMI + expr + SEMI + pp.Optional(expr) + RPAR + stmt) while_stmt = pp.Group(WHILE("statement") + LPAR + expr + RPAR + stmt) if_stmt = pp.Group(IF("statement") + LPAR + pp.Group(expr)("condition") + RPAR + pp.Group(stmt)("then_statement") + pp.Group(pp.Optional(ELSE + stmt))("else_statement")) stmt_block = pp.Group(LBRACE + pp.ZeroOrMore(variable_decl) + pp.ZeroOrMore(stmt) + RBRACE) stmt <<= (if_stmt | while_stmt | for_stmt | break_stmt | return_stmt | print_stmt | stmt_block | pp.Group(expr + SEMI) ) formals = pp.Optional(pp.delimitedList(variable)) prototype = pp.Group((type_ | VOID)("return_type") + ident("function_name") + LPAR + formals("args") + RPAR + SEMI)("prototype") function_decl = pp.Group((type_ | VOID)("return_type") + ident("function_name") + LPAR + formals("args") + RPAR + stmt_block("body"))("function_decl") interface_decl = pp.Group(INTERFACE + ident("interface_name") + LBRACE + pp.ZeroOrMore(prototype)("prototypes") + RBRACE)("interface") field = variable_decl | function_decl class_decl = pp.Group(CLASS + ident("class_name") + pp.Optional(EXTENDS + ident)("extends") + pp.Optional(IMPLEMENTS + pp.delimitedList(ident))("implements") + LBRACE + pp.ZeroOrMore(field)("fields") + RBRACE)("class_decl") decl = variable_decl | function_decl | class_decl | interface_decl | prototype program = pp.OneOrMore(pp.Group(decl)) decaf_parser = program stmt.runTests("""\ sin(30); a = 1; b = 1 + 1; b = 1 != 2 && false; print("A"); a.b = 100; a.b = 100.0; a[100] = b; a[0][0] = 2; a = 0x1234; """) test_program = """ void getenv(string var); int main(string[] args) { if (a > 100) { Print(a, " is too big"); } else if (a < 100) { Print(a, " is too small"); } else { Print(a, "just right!"); } } """ print(decaf_parser.parseString(test_program).dump()) pyparsing2-2.4.7/examples/delta_time.py000066400000000000000000000361301365333160500201310ustar00rootroot00000000000000# deltaTime.py # # Parser to convert a conversational time reference such as "in a minute" or # "noon tomorrow" and convert it to a Python datetime. The returned # ParseResults object contains # - original - the original time expression string # - computed_dt - the Python datetime representing the computed time # - relative_to - the reference "now" time # - time_offset - the difference between the reference time and the computed time # # BNF: # time_and_day ::= time_reference [day_reference] | day_reference 'at' absolute_time_of_day # day_reference ::= absolute_day_reference | relative_day_reference # absolute_day_reference ::= 'today' | 'tomorrow' | 'yesterday' | ('next' | 'last') weekday_name # relative_day_reference ::= 'in' qty day_units # | qty day_units 'ago' # | 'qty day_units ('from' | 'before' | 'after') absolute_day_reference # day_units ::= 'days' | 'weeks' # # time_reference ::= absolute_time_of_day | relative_time_reference # relative_time_reference ::= qty time_units ('from' | 'before' | 'after') absolute_time_of_day # | qty time_units 'ago' # | 'in' qty time_units # time_units ::= 'hours' | 'minutes' | 'seconds' # absolute_time_of_day ::= 'noon' | 'midnight' | 'now' | absolute_time # absolute_time ::= 24hour_time | hour ("o'clock" | ':' minute) ('AM'|'PM') # # qty ::= integer | integer_words | 'a couple of' | 'a' | 'the' # # Copyright 2010, 2019 by Paul McGuire # from datetime import datetime, time, timedelta import pyparsing as pp import calendar __all__ = ["time_expression"] # basic grammar definitions def make_integer_word_expr(int_name, int_value): return pp.CaselessKeyword(int_name).addParseAction(pp.replaceWith(int_value)) integer_word = pp.MatchFirst(make_integer_word_expr(int_str, int_value) for int_value, int_str in enumerate("one two three four five six seven eight nine ten" " eleven twelve thirteen fourteen fifteen sixteen" " seventeen eighteen nineteen twenty".split(), start=1)) integer = pp.pyparsing_common.integer | integer_word CK = pp.CaselessKeyword CL = pp.CaselessLiteral today, tomorrow, yesterday, noon, midnight, now = map(CK, "today tomorrow yesterday noon midnight now".split()) def plural(s): return CK(s) | CK(s + 's').addParseAction(pp.replaceWith(s)) week, day, hour, minute, second = map(plural, "week day hour minute second".split()) time_units = hour | minute | second any_time_units = week | day | time_units am = CL("am") pm = CL("pm") COLON = pp.Suppress(':') in_ = CK("in").setParseAction(pp.replaceWith(1)) from_ = CK("from").setParseAction(pp.replaceWith(1)) before = CK("before").setParseAction(pp.replaceWith(-1)) after = CK("after").setParseAction(pp.replaceWith(1)) ago = CK("ago").setParseAction(pp.replaceWith(-1)) next_ = CK("next").setParseAction(pp.replaceWith(1)) last_ = CK("last").setParseAction(pp.replaceWith(-1)) at_ = CK("at") on_ = CK("on") couple = (pp.Optional(CK("a")) + CK("couple") + pp.Optional(CK("of"))).setParseAction(pp.replaceWith(2)) a_qty = (CK("a") | CK("an")).setParseAction(pp.replaceWith(1)) the_qty = CK("the").setParseAction(pp.replaceWith(1)) qty = pp.ungroup(integer | couple | a_qty | the_qty).setName("qty") time_ref_present = pp.Empty().addParseAction(pp.replaceWith(True))('time_ref_present') def fill_24hr_time_fields(t): t['HH'] = t[0] t['MM'] = t[1] t['SS'] = 0 t['ampm'] = ('am','pm')[t.HH >= 12] def fill_default_time_fields(t): for fld in 'HH MM SS'.split(): if fld not in t: t[fld] = 0 weekday_name_list = list(calendar.day_name) weekday_name = pp.oneOf(weekday_name_list) _24hour_time = (~(integer + any_time_units) + pp.Word(pp.nums, exact=4).addParseAction(lambda t: [int(t[0][:2]),int(t[0][2:])], fill_24hr_time_fields) ) _24hour_time.setName("0000 time") ampm = am | pm timespec = (integer("HH") + pp.Optional(CK("o'clock") | COLON + integer("MM") + pp.Optional(COLON + integer("SS")) ) + (am | pm)("ampm") ).addParseAction(fill_default_time_fields) absolute_time = _24hour_time | timespec absolute_time_of_day = noon | midnight | now | absolute_time def add_computed_time(t): if t[0] in 'now noon midnight'.split(): t['computed_time'] = {'now': datetime.now().time().replace(microsecond=0), 'noon': time(hour=12), 'midnight': time()}[t[0]] else: t['HH'] = {'am': int(t['HH']) % 12, 'pm': int(t['HH']) % 12 + 12}[t.ampm] t['computed_time'] = time(hour=t.HH, minute=t.MM, second=t.SS) absolute_time_of_day.addParseAction(add_computed_time) # relative_time_reference ::= qty time_units ('from' | 'before' | 'after') absolute_time_of_day # | qty time_units 'ago' # | 'in' qty time_units time_units = hour | minute | second relative_time_reference = (qty('qty') + time_units('units') + ago('dir') | qty('qty') + time_units('units') + (from_ | before | after)('dir') + pp.Group(absolute_time_of_day)('ref_time') | in_('dir') + qty('qty') + time_units('units') ) def compute_relative_time(t): if 'ref_time' not in t: t['ref_time'] = datetime.now().time().replace(microsecond=0) else: t['ref_time'] = t.ref_time.computed_time delta_seconds = {'hour': 3600, 'minute': 60, 'second': 1}[t.units] * t.qty t['time_delta'] = timedelta(seconds=t.dir * delta_seconds) relative_time_reference.addParseAction(compute_relative_time) time_reference = absolute_time_of_day | relative_time_reference def add_default_time_ref_fields(t): if 'time_delta' not in t: t['time_delta'] = timedelta() time_reference.addParseAction(add_default_time_ref_fields) # absolute_day_reference ::= 'today' | 'tomorrow' | 'yesterday' | ('next' | 'last') weekday_name # day_units ::= 'days' | 'weeks' day_units = day | week weekday_reference = pp.Optional(next_ | last_, 1)('dir') + weekday_name('day_name') def convert_abs_day_reference_to_date(t): now = datetime.now().replace(microsecond=0) # handle day reference by weekday name if 'day_name' in t: todaynum = now.weekday() daynames = [n.lower() for n in weekday_name_list] nameddaynum = daynames.index(t.day_name.lower()) # compute difference in days - if current weekday name is referenced, then # computed 0 offset is changed to 7 if t.dir > 0: daydiff = (nameddaynum + 7 - todaynum) % 7 or 7 else: daydiff = -((todaynum + 7 - nameddaynum) % 7 or 7) t["abs_date"] = datetime(now.year, now.month, now.day) + timedelta(daydiff) else: name = t[0] t["abs_date"] = { "now" : now, "today" : datetime(now.year, now.month, now.day), "yesterday" : datetime(now.year, now.month, now.day) + timedelta(days=-1), "tomorrow" : datetime(now.year, now.month, now.day) + timedelta(days=+1), }[name] absolute_day_reference = today | tomorrow | yesterday | now + time_ref_present | weekday_reference absolute_day_reference.addParseAction(convert_abs_day_reference_to_date) # relative_day_reference ::= 'in' qty day_units # | qty day_units 'ago' # | 'qty day_units ('from' | 'before' | 'after') absolute_day_reference relative_day_reference = (in_('dir') + qty('qty') + day_units('units') | qty('qty') + day_units('units') + ago('dir') | qty('qty') + day_units('units') + (from_ | before | after)('dir') + absolute_day_reference('ref_day') ) def compute_relative_date(t): now = datetime.now().replace(microsecond=0) if 'ref_day' in t: t['computed_date'] = t.ref_day else: t['computed_date'] = now.date() day_diff = t.dir * t.qty * {'week': 7, 'day': 1}[t.units] t['date_delta'] = timedelta(days=day_diff) relative_day_reference.addParseAction(compute_relative_date) # combine expressions for absolute and relative day references day_reference = relative_day_reference | absolute_day_reference def add_default_date_fields(t): if 'date_delta' not in t: t['date_delta'] = timedelta() day_reference.addParseAction(add_default_date_fields) # combine date and time expressions into single overall parser time_and_day = (time_reference + time_ref_present + pp.Optional(pp.Optional(on_) + day_reference) | day_reference + pp.Optional(at_ + absolute_time_of_day + time_ref_present)) # parse actions for total time_and_day expression def save_original_string(s, l, t): # save original input string and reference time t['original'] = ' '.join(s.strip().split()) t['relative_to'] = datetime.now().replace(microsecond=0) def compute_timestamp(t): # accumulate values from parsed time and day subexpressions - fill in defaults for omitted parts now = datetime.now().replace(microsecond=0) if 'computed_time' not in t: t['computed_time'] = t.ref_time or now.time() if 'abs_date' not in t: t['abs_date'] = now # roll up all fields and apply any time or day deltas t['computed_dt'] = ( t.abs_date.replace(hour=t.computed_time.hour, minute=t.computed_time.minute, second=t.computed_time.second) + (t.time_delta or timedelta(0)) + (t.date_delta or timedelta(0)) ) # if time just given in terms of day expressions, zero out time fields if not t.time_ref_present: t['computed_dt'] = t.computed_dt.replace(hour=0, minute=0, second=0) # add results name compatible with previous version t['calculatedTime'] = t.computed_dt # add time_offset fields t['time_offset'] = t.computed_dt - t.relative_to def remove_temp_keys(t): # strip out keys that are just used internally all_keys = list(t.keys()) for k in all_keys: if k not in ('computed_dt', 'original', 'relative_to', 'time_offset', 'calculatedTime'): del t[k] time_and_day.addParseAction(save_original_string, compute_timestamp, remove_temp_keys) time_expression = time_and_day if __name__ == "__main__": current_time = datetime.now() # test grammar tests = """\ today tomorrow yesterday the day before yesterday the day after tomorrow 2 weeks after today in a couple of days a couple of days from now a couple of days from today in a day 3 days ago 3 days from now a day ago an hour ago in 2 weeks in 3 days at 5pm now 10 minutes ago 10 minutes from now in 10 minutes in a minute in a couple of minutes 20 seconds ago in 30 seconds in an hour in a couple hours in a couple days 20 seconds before noon ten seconds before noon tomorrow noon midnight noon tomorrow 6am tomorrow 0800 yesterday 1700 tomorrow 12:15 AM today 3pm 2 days from today a week from today a week from now three weeks ago noon next Sunday noon Sunday noon last Sunday 2pm next Sunday next Sunday at 2pm last Sunday at 2pm 10 seconds ago 100 seconds ago 1000 seconds ago 10000 seconds ago """ time_of_day = timedelta(hours=current_time.hour, minutes=current_time.minute, seconds=current_time.second) expected = { 'now' : timedelta(0), "10 seconds ago": timedelta(seconds=-10), "100 seconds ago": timedelta(seconds=-100), "1000 seconds ago": timedelta(seconds=-1000), "10000 seconds ago": timedelta(seconds=-10000), '10 minutes ago': timedelta(minutes=-10), '10 minutes from now': timedelta(minutes=10), 'in 10 minutes': timedelta(minutes=10), 'in a minute': timedelta(minutes=1), 'in a couple of minutes': timedelta(minutes=2), '20 seconds ago': timedelta(seconds=-20), 'in 30 seconds': timedelta(seconds=30), 'in an hour': timedelta(hours=1), 'in a couple hours': timedelta(hours=2), 'a week from now': timedelta(days=7), '3 days from now': timedelta(days=3), 'a couple of days from now': timedelta(days=2), 'an hour ago': timedelta(hours=-1), 'in a couple days': timedelta(days=2) - time_of_day, 'a week from today': timedelta(days=7) - time_of_day, 'three weeks ago': timedelta(days=-21) - time_of_day, 'a day ago': timedelta(days=-1) - time_of_day, 'in a couple of days': timedelta(days=2) - time_of_day, 'a couple of days from today': timedelta(days=2) - time_of_day, '2 weeks after today': timedelta(days=14) - time_of_day, 'in 2 weeks': timedelta(days=14) - time_of_day, 'the day after tomorrow': timedelta(days=2) - time_of_day, 'tomorrow': timedelta(days=1) - time_of_day, 'the day before yesterday': timedelta(days=-2) - time_of_day, 'yesterday': timedelta(days=-1) - time_of_day, 'today': -time_of_day, 'midnight': -time_of_day, 'in a day': timedelta(days=1) - time_of_day, '3 days ago': timedelta(days=-3) - time_of_day, 'noon tomorrow': timedelta(days=1) - time_of_day + timedelta(hours=12), '6am tomorrow': timedelta(days=1) - time_of_day + timedelta(hours=6), '0800 yesterday': timedelta(days=-1) - time_of_day + timedelta(hours=8), '1700 tomorrow': timedelta(days=1) - time_of_day + timedelta(hours=17), '12:15 AM today': -time_of_day + timedelta(minutes=15), '3pm 2 days from today': timedelta(days=2) - time_of_day + timedelta(hours=15), 'ten seconds before noon tomorrow': timedelta(days=1) - time_of_day + timedelta(hours=12) + timedelta(seconds=-10), '20 seconds before noon': -time_of_day + timedelta(hours=12) + timedelta(seconds=-20), 'in 3 days at 5pm': timedelta(days=3) - time_of_day + timedelta(hours=17), } def verify_offset(instring, parsed): time_epsilon = timedelta(seconds=1) if instring in expected: # allow up to a second time discrepancy due to test processing time if (parsed.time_offset - expected[instring]) <= time_epsilon: parsed['verify_offset'] = 'PASS' else: parsed['verify_offset'] = 'FAIL' print("(relative to %s)" % datetime.now()) time_expression.runTests(tests, postParse=verify_offset) pyparsing2-2.4.7/examples/dfmparse.py000066400000000000000000000155371365333160500176330ustar00rootroot00000000000000""" This module can parse a Delphi Form (dfm) file. The main is used in experimenting (to find which files fail to parse, and where), but isn't useful for anything else. """ __version__ = "1.0" __author__ = "Daniel 'Dang' Griffith " from pyparsing import Literal, CaselessLiteral, Word, delimitedList \ , Optional, Combine, Group, alphas, nums, alphanums, Forward \ , oneOf, OneOrMore, ZeroOrMore, CharsNotIn # This converts DFM character constants into Python string (unicode) values. def to_chr(x): """chr(x) if 0 < x < 128 ; unicode(x) if x > 127.""" return 0 < x < 128 and chr(x) or eval("u'\\u%d'" % x ) ################# # BEGIN GRAMMAR ################# COLON = Literal(":").suppress() CONCAT = Literal("+").suppress() EQUALS = Literal("=").suppress() LANGLE = Literal("<").suppress() LBRACE = Literal("[").suppress() LPAREN = Literal("(").suppress() PERIOD = Literal(".").suppress() RANGLE = Literal(">").suppress() RBRACE = Literal("]").suppress() RPAREN = Literal(")").suppress() CATEGORIES = CaselessLiteral("categories").suppress() END = CaselessLiteral("end").suppress() FONT = CaselessLiteral("font").suppress() HINT = CaselessLiteral("hint").suppress() ITEM = CaselessLiteral("item").suppress() OBJECT = CaselessLiteral("object").suppress() attribute_value_pair = Forward() # this is recursed in item_list_entry simple_identifier = Word(alphas, alphanums + "_") identifier = Combine( simple_identifier + ZeroOrMore( Literal(".") + simple_identifier )) object_name = identifier object_type = identifier # Integer and floating point values are converted to Python longs and floats, respectively. int_value = Combine(Optional("-") + Word(nums)).setParseAction(lambda s,l,t: [ int(t[0]) ] ) float_value = Combine(Optional("-") + Optional(Word(nums)) + "." + Word(nums)).setParseAction(lambda s,l,t: [ float(t[0]) ] ) number_value = float_value | int_value # Base16 constants are left in string form, including the surrounding braces. base16_value = Combine(Literal("{") + OneOrMore(Word("0123456789ABCDEFabcdef")) + Literal("}"), adjacent=False) # This is the first part of a hack to convert the various delphi partial sglQuotedStrings # into a single sglQuotedString equivalent. The gist of it is to combine # all sglQuotedStrings (with their surrounding quotes removed (suppressed)) # with sequences of #xyz character constants, with "strings" concatenated # with a '+' sign. unquoted_sglQuotedString = Combine( Literal("'").suppress() + ZeroOrMore( CharsNotIn("'\n\r") ) + Literal("'").suppress() ) # The parse action on this production converts repetitions of constants into a single string. pound_char = Combine( OneOrMore((Literal("#").suppress()+Word(nums) ).setParseAction( lambda s, l, t: to_chr(int(t[0]) )))) # This is the second part of the hack. It combines the various "unquoted" # partial strings into a single one. Then, the parse action puts # a single matched pair of quotes around it. delphi_string = Combine( OneOrMore(CONCAT | pound_char | unquoted_sglQuotedString) , adjacent=False ).setParseAction(lambda s, l, t: "'%s'" % t[0]) string_value = delphi_string | base16_value list_value = LBRACE + Optional(Group(delimitedList(identifier | number_value | string_value))) + RBRACE paren_list_value = LPAREN + ZeroOrMore(identifier | number_value | string_value) + RPAREN item_list_entry = ITEM + ZeroOrMore(attribute_value_pair) + END item_list = LANGLE + ZeroOrMore(item_list_entry) + RANGLE generic_value = identifier value = item_list | number_value | string_value | list_value | paren_list_value | generic_value category_attribute = CATEGORIES + PERIOD + oneOf("strings itemsvisibles visibles", True) event_attribute = oneOf("onactivate onclosequery onclose oncreate ondeactivate onhide onshow", True) font_attribute = FONT + PERIOD + oneOf("charset color height name style", True) hint_attribute = HINT layout_attribute = oneOf("left top width height", True) generic_attribute = identifier attribute = (category_attribute | event_attribute | font_attribute | hint_attribute | layout_attribute | generic_attribute) category_attribute_value_pair = category_attribute + EQUALS + paren_list_value event_attribute_value_pair = event_attribute + EQUALS + value font_attribute_value_pair = font_attribute + EQUALS + value hint_attribute_value_pair = hint_attribute + EQUALS + value layout_attribute_value_pair = layout_attribute + EQUALS + value generic_attribute_value_pair = attribute + EQUALS + value attribute_value_pair << Group( category_attribute_value_pair | event_attribute_value_pair | font_attribute_value_pair | hint_attribute_value_pair | layout_attribute_value_pair | generic_attribute_value_pair ) object_declaration = Group((OBJECT + object_name + COLON + object_type)) object_attributes = Group(ZeroOrMore(attribute_value_pair)) nested_object = Forward() object_definition = object_declaration + object_attributes + ZeroOrMore(nested_object) + END nested_object << Group(object_definition) ################# # END GRAMMAR ################# def printer(s, loc, tok): print(tok, end=' ') return tok def get_filename_list(tf): import sys, glob if tf == None: if len(sys.argv) > 1: tf = sys.argv[1:] else: tf = glob.glob("*.dfm") elif type(tf) == str: tf = [tf] testfiles = [] for arg in tf: testfiles.extend(glob.glob(arg)) return testfiles def main(testfiles=None, action=printer): """testfiles can be None, in which case the command line arguments are used as filenames. testfiles can be a string, in which case that file is parsed. testfiles can be a list. In all cases, the filenames will be globbed. If more than one file is parsed successfully, a dictionary of ParseResults is returned. Otherwise, a simple ParseResults is returned. """ testfiles = get_filename_list(testfiles) print(testfiles) if action: for i in (simple_identifier, value, item_list): i.setParseAction(action) success = 0 failures = [] retval = {} for f in testfiles: try: retval[f] = object_definition.parseFile(f) success += 1 except Exception: failures.append(f) if failures: print('\nfailed while processing %s' % ', '.join(failures)) print('\nsucceeded on %d of %d files' %(success, len(testfiles))) if len(retval) == 1 and len(testfiles) == 1: # if only one file is parsed, return the parseResults directly return retval[list(retval.keys())[0]] # else, return a dictionary of parseResults return retval if __name__ == "__main__": main() pyparsing2-2.4.7/examples/dhcpd_leases_parser.py000066400000000000000000000061321365333160500220130ustar00rootroot00000000000000# # dhcpd_leases_parser.py # # Copyright 2008, Paul McGuire # # Sample parser to parse a dhcpd.leases file to extract leases # and lease attributes # # format ref: http://www.linuxmanpages.com/man5/dhcpd.leases.5.php # sample = r"""\ # All times in this file are in UTC (GMT), not your local timezone. This is # not a bug, so please don't ask about it. There is no portable way to # store leases in the local timezone, so please don't request this as a # feature. If this is inconvenient or confusing to you, we sincerely # apologize. Seriously, though - don't ask. # The format of this file is documented in the dhcpd.leases(5) manual page. # This lease file was written by isc-dhcp-V3.0.4 lease 192.168.0.250 { starts 3 2008/01/23 17:16:41; ends 6 2008/02/02 17:16:41; tstp 6 2008/02/02 17:16:41; binding state free; hardware ethernet 00:17:f2:9b:d8:19; uid "\001\000\027\362\233\330\031"; } lease 192.168.0.198 { starts 1 2008/02/04 13:46:55; ends never; tstp 1 2008/02/04 17:04:14; binding state free; hardware ethernet 00:13:72:d3:3b:98; uid "\001\000\023r\323;\230"; } lease 192.168.0.239 { starts 3 2008/02/06 12:12:03; ends 4 2008/02/07 12:12:03; tstp 4 2008/02/07 12:12:03; binding state free; hardware ethernet 00:1d:09:65:93:26; } """ from pyparsing import * import datetime,time LBRACE,RBRACE,SEMI,QUOTE = map(Suppress,'{};"') ipAddress = Combine(Word(nums) + ('.' + Word(nums))*3) hexint = Word(hexnums,exact=2) macAddress = Combine(hexint + (':'+hexint)*5) hdwType = Word(alphanums) yyyymmdd = Combine((Word(nums,exact=4)|Word(nums,exact=2))+ ('/'+Word(nums,exact=2))*2) hhmmss = Combine(Word(nums,exact=2)+(':'+Word(nums,exact=2))*2) dateRef = oneOf(list("0123456"))("weekday") + yyyymmdd("date") + \ hhmmss("time") def utcToLocalTime(tokens): utctime = datetime.datetime.strptime("%(date)s %(time)s" % tokens, "%Y/%m/%d %H:%M:%S") localtime = utctime-datetime.timedelta(0,time.timezone,0) tokens["utcdate"],tokens["utctime"] = tokens["date"],tokens["time"] tokens["localdate"],tokens["localtime"] = str(localtime).split() del tokens["date"] del tokens["time"] dateRef.setParseAction(utcToLocalTime) startsStmt = "starts" + dateRef + SEMI endsStmt = "ends" + (dateRef | "never") + SEMI tstpStmt = "tstp" + dateRef + SEMI tsfpStmt = "tsfp" + dateRef + SEMI hdwStmt = "hardware" + hdwType("type") + macAddress("mac") + SEMI uidStmt = "uid" + QuotedString('"')("uid") + SEMI bindingStmt = "binding" + Word(alphanums) + Word(alphanums) + SEMI leaseStatement = startsStmt | endsStmt | tstpStmt | tsfpStmt | hdwStmt | \ uidStmt | bindingStmt leaseDef = "lease" + ipAddress("ipaddress") + LBRACE + \ Dict(ZeroOrMore(Group(leaseStatement))) + RBRACE for lease in leaseDef.searchString(sample): print(lease.dump()) print(lease.ipaddress,'->',lease.hardware.mac) print() pyparsing2-2.4.7/examples/dictExample.py000066400000000000000000000033251365333160500202610ustar00rootroot00000000000000# # dictExample.py # # Illustration of using pyparsing's Dict class to process tabular data # # Copyright (c) 2003, Paul McGuire # import pyparsing as pp testData = """ +-------+------+------+------+------+------+------+------+------+ | | A1 | B1 | C1 | D1 | A2 | B2 | C2 | D2 | +=======+======+======+======+======+======+======+======+======+ | min | 7 | 43 | 7 | 15 | 82 | 98 | 1 | 37 | | max | 11 | 52 | 10 | 17 | 85 | 112 | 4 | 39 | | ave | 9 | 47 | 8 | 16 | 84 | 106 | 3 | 38 | | sdev | 1 | 3 | 1 | 1 | 1 | 3 | 1 | 1 | +-------+------+------+------+------+------+------+------+------+ """ # define grammar for datatable heading = (pp.Literal( "+-------+------+------+------+------+------+------+------+------+") + "| | A1 | B1 | C1 | D1 | A2 | B2 | C2 | D2 |" + "+=======+======+======+======+======+======+======+======+======+").suppress() vert = pp.Literal("|").suppress() number = pp.Word(pp.nums) rowData = pp.Group( vert + pp.Word(pp.alphas) + vert + pp.delimitedList(number,"|") + vert ) trailing = pp.Literal( "+-------+------+------+------+------+------+------+------+------+").suppress() datatable = heading + pp.Dict(pp.ZeroOrMore(rowData)) + trailing # now parse data and print results data = datatable.parseString(testData) print(data) # shortcut for import pprint; pprint.pprint(data.asList()) data.pprint() # access all data keys print("data keys=", list(data.keys())) # use dict-style access to values print("data['min']=", data['min']) # use attribute-style access to values (if key is a valid Python identifier) print("data.max", data.max) pyparsing2-2.4.7/examples/dictExample2.py000066400000000000000000000040701365333160500203410ustar00rootroot00000000000000# # dictExample2.py # # Illustration of using pyparsing's Dict class to process tabular data # Enhanced Dict example, courtesy of Mike Kelly # # Copyright (c) 2004, Paul McGuire # from pyparsing import Literal, Word, Group, Dict, ZeroOrMore, alphas, nums, delimitedList, pyparsing_common as ppc testData = """ +-------+------+------+------+------+------+------+------+------+ | | A1 | B1 | C1 | D1 | A2 | B2 | C2 | D2 | +=======+======+======+======+======+======+======+======+======+ | min | 7 | 43 | 7 | 15 | 82 | 98 | 1 | 37 | | max | 11 | 52 | 10 | 17 | 85 | 112 | 4 | 39 | | ave | 9 | 47 | 8 | 16 | 84 | 106 | 3 | 38 | | sdev | 1 | 3 | 1 | 1 | 1 | 3 | 1 | 1 | +-------+------+------+------+------+------+------+------+------+ """ # define grammar for datatable underline = Word("-=") number = ppc.integer vert = Literal("|").suppress() rowDelim = ("+" + ZeroOrMore( underline + "+" ) ).suppress() columnHeader = Group(vert + vert + delimitedList(Word(alphas + nums), "|") + vert) heading = rowDelim + columnHeader("columns") + rowDelim rowData = Group( vert + Word(alphas) + vert + delimitedList(number,"|") + vert ) trailing = rowDelim datatable = heading + Dict( ZeroOrMore(rowData) ) + trailing # now parse data and print results data = datatable.parseString(testData) print(data.dump()) print("data keys=", list(data.keys())) print("data['min']=", data['min']) print("sum(data['min']) =", sum(data['min'])) print("data.max =", data.max) print("sum(data.max) =", sum(data.max)) # now print transpose of data table, using column labels read from table header and # values from data lists print() print(" " * 5, end=' ') for i in range(1,len(data)): print("|%5s" % data[i][0], end=' ') print() print(("-" * 6) + ("+------" * (len(data)-1))) for i in range(len(data.columns)): print("%5s" % data.columns[i], end=' ') for j in range(len(data) - 1): print('|%5s' % data[j + 1][i + 1], end=' ') print() pyparsing2-2.4.7/examples/ebnf.py000066400000000000000000000102731365333160500167340ustar00rootroot00000000000000# This module tries to implement ISO 14977 standard with pyparsing. # pyparsing version 1.1 or greater is required. # ISO 14977 standardize The Extended Backus-Naur Form(EBNF) syntax. # You can read a final draft version here: # https://www.cl.cam.ac.uk/~mgk25/iso-ebnf.html # # Submitted 2004 by Seo Sanghyeon # from pyparsing import * all_names = ''' integer meta_identifier terminal_string optional_sequence repeated_sequence grouped_sequence syntactic_primary syntactic_factor syntactic_term single_definition definitions_list syntax_rule syntax '''.split() integer = Word(nums) meta_identifier = Word(alphas, alphanums + '_') terminal_string = Suppress("'") + CharsNotIn("'") + Suppress("'") ^ \ Suppress('"') + CharsNotIn('"') + Suppress('"') definitions_list = Forward() optional_sequence = Suppress('[') + definitions_list + Suppress(']') repeated_sequence = Suppress('{') + definitions_list + Suppress('}') grouped_sequence = Suppress('(') + definitions_list + Suppress(')') syntactic_primary = optional_sequence ^ repeated_sequence ^ \ grouped_sequence ^ meta_identifier ^ terminal_string syntactic_factor = Optional(integer + Suppress('*')) + syntactic_primary syntactic_term = syntactic_factor + Optional(Suppress('-') + syntactic_factor) single_definition = delimitedList(syntactic_term, ',') definitions_list << delimitedList(single_definition, '|') syntax_rule = meta_identifier + Suppress('=') + definitions_list + \ Suppress(';') ebnfComment = ( "(*" + ZeroOrMore( CharsNotIn("*") | ( "*" + ~Literal(")") ) ) + "*)" ).streamline().setName("ebnfComment") syntax = OneOrMore(syntax_rule) syntax.ignore(ebnfComment) def do_integer(str, loc, toks): return int(toks[0]) def do_meta_identifier(str, loc, toks): if toks[0] in symbol_table: return symbol_table[toks[0]] else: forward_count.value += 1 symbol_table[toks[0]] = Forward() return symbol_table[toks[0]] def do_terminal_string(str, loc, toks): return Literal(toks[0]) def do_optional_sequence(str, loc, toks): return Optional(toks[0]) def do_repeated_sequence(str, loc, toks): return ZeroOrMore(toks[0]) def do_grouped_sequence(str, loc, toks): return Group(toks[0]) def do_syntactic_primary(str, loc, toks): return toks[0] def do_syntactic_factor(str, loc, toks): if len(toks) == 2: # integer * syntactic_primary return And([toks[1]] * toks[0]) else: # syntactic_primary return [ toks[0] ] def do_syntactic_term(str, loc, toks): if len(toks) == 2: # syntactic_factor - syntactic_factor return NotAny(toks[1]) + toks[0] else: # syntactic_factor return [ toks[0] ] def do_single_definition(str, loc, toks): toks = toks.asList() if len(toks) > 1: # syntactic_term , syntactic_term , ... return And(toks) else: # syntactic_term return [ toks[0] ] def do_definitions_list(str, loc, toks): toks = toks.asList() if len(toks) > 1: # single_definition | single_definition | ... return Or(toks) else: # single_definition return [ toks[0] ] def do_syntax_rule(str, loc, toks): # meta_identifier = definitions_list ; assert toks[0].expr is None, "Duplicate definition" forward_count.value -= 1 toks[0] << toks[1] return [ toks[0] ] def do_syntax(str, loc, toks): # syntax_rule syntax_rule ... return symbol_table symbol_table = {} class forward_count: pass forward_count.value = 0 for name in all_names: expr = vars()[name] action = vars()['do_' + name] expr.setName(name) expr.setParseAction(action) #~ expr.setDebug() def parse(ebnf, given_table={}): symbol_table.clear() symbol_table.update(given_table) forward_count.value = 0 table = syntax.parseString(ebnf)[0] assert forward_count.value == 0, "Missing definition" for name in table: expr = table[name] expr.setName(name) #~ expr.setDebug() return table pyparsing2-2.4.7/examples/ebnftest.py000066400000000000000000000045271365333160500176410ustar00rootroot00000000000000# # ebnftest.py # # Test script for ebnf.py # # Submitted 2004 by Seo Sanghyeon # print('Importing pyparsing...') from pyparsing import * print('Constructing EBNF parser with pyparsing...') import ebnf grammar = ''' syntax = (syntax_rule), {(syntax_rule)}; syntax_rule = meta_identifier, '=', definitions_list, ';'; definitions_list = single_definition, {'|', single_definition}; single_definition = syntactic_term, {',', syntactic_term}; syntactic_term = syntactic_factor,['-', syntactic_factor]; syntactic_factor = [integer, '*'], syntactic_primary; syntactic_primary = optional_sequence | repeated_sequence | grouped_sequence | meta_identifier | terminal_string; optional_sequence = '[', definitions_list, ']'; repeated_sequence = '{', definitions_list, '}'; grouped_sequence = '(', definitions_list, ')'; (* terminal_string = "'", character - "'", {character - "'"}, "'" | '"', character - '"', {character - '"'}, '"'; meta_identifier = letter, {letter | digit}; integer = digit, {digit}; *) ''' table = {} #~ table['character'] = Word(printables, exact=1) #~ table['letter'] = Word(alphas + '_', exact=1) #~ table['digit'] = Word(nums, exact=1) table['terminal_string'] = sglQuotedString table['meta_identifier'] = Word(alphas+"_", alphas+"_"+nums) table['integer'] = Word(nums) print('Parsing EBNF grammar with EBNF parser...') parsers = ebnf.parse(grammar, table) ebnf_parser = parsers['syntax'] commentcharcount = 0 commentlocs = set() def tallyCommentChars(s,l,t): global commentcharcount,commentlocs # only count this comment if we haven't seen it before if l not in commentlocs: charCount = ( len(t[0]) - len(list(filter(str.isspace, t[0]))) ) commentcharcount += charCount commentlocs.add(l) return l,t #ordinarily, these lines wouldn't be necessary, but we are doing extra stuff with the comment expression ebnf.ebnfComment.setParseAction( tallyCommentChars ) ebnf_parser.ignore( ebnf.ebnfComment ) print('Parsing EBNF grammar with generated EBNF parser...\n') parsed_chars = ebnf_parser.parseString(grammar) parsed_char_len = len(parsed_chars) print("],\n".join(str( parsed_chars.asList() ).split("],"))) #~ grammar_length = len(grammar) - len(filter(str.isspace, grammar))-commentcharcount #~ assert parsed_char_len == grammar_length print('Ok!') pyparsing2-2.4.7/examples/eval_arith.py000066400000000000000000000152321365333160500201400ustar00rootroot00000000000000# eval_arith.py # # Copyright 2009, 2011 Paul McGuire # # Expansion on the pyparsing example simpleArith.py, to include evaluation # of the parsed tokens. # # Added support for exponentiation, using right-to-left evaluation of # operands # from pyparsing import Word, nums, alphas, Combine, oneOf, \ opAssoc, infixNotation, Literal class EvalConstant(object): "Class to evaluate a parsed constant or variable" vars_ = {} def __init__(self, tokens): self.value = tokens[0] def eval(self): if self.value in EvalConstant.vars_: return EvalConstant.vars_[self.value] else: return float(self.value) class EvalSignOp(object): "Class to evaluate expressions with a leading + or - sign" def __init__(self, tokens): self.sign, self.value = tokens[0] def eval(self): mult = {'+':1, '-':-1}[self.sign] return mult * self.value.eval() def operatorOperands(tokenlist): "generator to extract operators and operands in pairs" it = iter(tokenlist) while 1: try: yield (next(it), next(it)) except StopIteration: break class EvalPowerOp(object): "Class to evaluate multiplication and division expressions" def __init__(self, tokens): self.value = tokens[0] def eval(self): res = self.value[-1].eval() for val in self.value[-3::-2]: res = val.eval()**res return res class EvalMultOp(object): "Class to evaluate multiplication and division expressions" def __init__(self, tokens): self.value = tokens[0] def eval(self): prod = self.value[0].eval() for op,val in operatorOperands(self.value[1:]): if op == '*': prod *= val.eval() if op == '/': prod /= val.eval() return prod class EvalAddOp(object): "Class to evaluate addition and subtraction expressions" def __init__(self, tokens): self.value = tokens[0] def eval(self): sum = self.value[0].eval() for op,val in operatorOperands(self.value[1:]): if op == '+': sum += val.eval() if op == '-': sum -= val.eval() return sum class EvalComparisonOp(object): "Class to evaluate comparison expressions" opMap = { "<" : lambda a,b : a < b, "<=" : lambda a,b : a <= b, ">" : lambda a,b : a > b, ">=" : lambda a,b : a >= b, "!=" : lambda a,b : a != b, "=" : lambda a,b : a == b, "LT" : lambda a,b : a < b, "LE" : lambda a,b : a <= b, "GT" : lambda a,b : a > b, "GE" : lambda a,b : a >= b, "NE" : lambda a,b : a != b, "EQ" : lambda a,b : a == b, "<>" : lambda a,b : a != b, } def __init__(self, tokens): self.value = tokens[0] def eval(self): val1 = self.value[0].eval() for op,val in operatorOperands(self.value[1:]): fn = EvalComparisonOp.opMap[op] val2 = val.eval() if not fn(val1,val2): break val1 = val2 else: return True return False # define the parser integer = Word(nums) real = Combine(Word(nums) + "." + Word(nums)) variable = Word(alphas,exact=1) operand = real | integer | variable signop = oneOf('+ -') multop = oneOf('* /') plusop = oneOf('+ -') expop = Literal('**') # use parse actions to attach EvalXXX constructors to sub-expressions operand.setParseAction(EvalConstant) arith_expr = infixNotation(operand, [ (signop, 1, opAssoc.RIGHT, EvalSignOp), (expop, 2, opAssoc.LEFT, EvalPowerOp), (multop, 2, opAssoc.LEFT, EvalMultOp), (plusop, 2, opAssoc.LEFT, EvalAddOp), ]) comparisonop = oneOf("< <= > >= != = <> LT GT LE GE EQ NE") comp_expr = infixNotation(arith_expr, [ (comparisonop, 2, opAssoc.LEFT, EvalComparisonOp), ]) def main(): # sample expressions posted on comp.lang.python, asking for advice # in safely evaluating them rules=[ '( A - B ) = 0', '(A + B + C + D + E + F + G + H + I) = J', '(A + B + C + D + E + F + G + H) = I', '(A + B + C + D + E + F) = G', '(A + B + C + D + E) = (F + G + H + I + J)', '(A + B + C + D + E) = (F + G + H + I)', '(A + B + C + D + E) = F', '(A + B + C + D) = (E + F + G + H)', '(A + B + C) = (D + E + F)', '(A + B) = (C + D + E + F)', '(A + B) = (C + D)', '(A + B) = (C - D + E - F - G + H + I + J)', '(A + B) = C', '(A + B) = 0', '(A+B+C+D+E) = (F+G+H+I+J)', '(A+B+C+D) = (E+F+G+H)', '(A+B+C+D)=(E+F+G+H)', '(A+B+C)=(D+E+F)', '(A+B)=(C+D)', '(A+B)=C', '(A-B)=C', '(A/(B+C))', '(B/(C+D))', '(G + H) = I', '-0.99 LE ((A+B+C)-(D+E+F+G)) LE 0.99', '-0.99 LE (A-(B+C)) LE 0.99', '-1000.00 LE A LE 0.00', '-5000.00 LE A LE 0.00', 'A < B', 'A < 7000', 'A = -(B)', 'A = C', 'A = 0', 'A GT 0', 'A GT 0.00', 'A GT 7.00', 'A LE B', 'A LT -1000.00', 'A LT -5000', 'A LT 0', 'A=(B+C+D)', 'A=B', 'I = (G + H)', '0.00 LE A LE 4.00', '4.00 LT A LE 7.00', '0.00 LE A LE 4.00 LE E > D', '2**2**(A+3)', ] vars_={'A': 0, 'B': 1.1, 'C': 2.2, 'D': 3.3, 'E': 4.4, 'F': 5.5, 'G': 6.6, 'H':7.7, 'I':8.8, 'J':9.9} # define tests from given rules tests = [] for t in rules: t_orig = t t = t.replace("=","==") t = t.replace("EQ","==") t = t.replace("LE","<=") t = t.replace("GT",">") t = t.replace("LT","<") t = t.replace("GE",">=") t = t.replace("LE","<=") t = t.replace("NE","!=") t = t.replace("<>","!=") tests.append( (t_orig,eval(t,vars_)) ) # copy vars_ to EvalConstant lookup dict EvalConstant.vars_ = vars_ failed = 0 for test,expected in tests: ret = comp_expr.parseString(test)[0] parsedvalue = ret.eval() print(test, expected, parsedvalue) if parsedvalue != expected: print("<<< FAIL") failed += 1 else: print('') print('') if failed: print(failed, "tests FAILED") return 1 else: print("all tests PASSED") return 0 if __name__=='__main__': exit(main()) pyparsing2-2.4.7/examples/excelExpr.py000066400000000000000000000042221365333160500177560ustar00rootroot00000000000000# excelExpr.py # # Copyright 2010, Paul McGuire # # A partial implementation of a parser of Excel formula expressions. # from pyparsing import (CaselessKeyword, Suppress, Word, alphas, alphanums, nums, Optional, Group, oneOf, Forward, infixNotation, opAssoc, dblQuotedString, delimitedList, Combine, Literal, QuotedString, ParserElement, pyparsing_common as ppc) ParserElement.enablePackrat() EQ,LPAR,RPAR,COLON,COMMA = map(Suppress, '=():,') EXCL, DOLLAR = map(Literal,"!$") sheetRef = Word(alphas, alphanums) | QuotedString("'",escQuote="''") colRef = Optional(DOLLAR) + Word(alphas,max=2) rowRef = Optional(DOLLAR) + Word(nums) cellRef = Combine(Group(Optional(sheetRef + EXCL)("sheet") + colRef("col") + rowRef("row"))) cellRange = (Group(cellRef("start") + COLON + cellRef("end"))("range") | cellRef | Word(alphas,alphanums)) expr = Forward() COMPARISON_OP = oneOf("< = > >= <= != <>") condExpr = expr + COMPARISON_OP + expr ifFunc = (CaselessKeyword("if") - LPAR + Group(condExpr)("condition") + COMMA + Group(expr)("if_true") + COMMA + Group(expr)("if_false") + RPAR) statFunc = lambda name : Group(CaselessKeyword(name) + Group(LPAR + delimitedList(expr) + RPAR)) sumFunc = statFunc("sum") minFunc = statFunc("min") maxFunc = statFunc("max") aveFunc = statFunc("ave") funcCall = ifFunc | sumFunc | minFunc | maxFunc | aveFunc multOp = oneOf("* /") addOp = oneOf("+ -") numericLiteral = ppc.number operand = numericLiteral | funcCall | cellRange | cellRef arithExpr = infixNotation(operand, [ (multOp, 2, opAssoc.LEFT), (addOp, 2, opAssoc.LEFT), ]) textOperand = dblQuotedString | cellRef textExpr = infixNotation(textOperand, [ ('&', 2, opAssoc.LEFT), ]) expr << (arithExpr | textExpr) (EQ + expr).runTests("""\ =3*A7+5 =3*Sheet1!$A$7+5 =3*'Sheet 1'!$A$7+5" =3*'O''Reilly''s sheet'!$A$7+5 =if(Sum(A1:A25)>42,Min(B1:B25),if(Sum(C1:C25)>3.14, (Min(C1:C25)+3)*18,Max(B1:B25))) =sum(a1:a25,10,min(b1,c2,d3)) =if("T"&a2="TTime", "Ready", "Not ready") """) pyparsing2-2.4.7/examples/fourFn.py000066400000000000000000000162571365333160500172710ustar00rootroot00000000000000# fourFn.py # # Demonstration of the pyparsing module, implementing a simple 4-function expression parser, # with support for scientific notation, and symbols for e and pi. # Extended to add exponentiation and simple built-in functions. # Extended test cases, simplified pushFirst method. # Removed unnecessary expr.suppress() call (thanks Nathaniel Peterson!), and added Group # Changed fnumber to use a Regex, which is now the preferred method # # Copyright 2003-2009 by Paul McGuire # from pyparsing import Literal,Word,Group,\ ZeroOrMore,Forward,alphas,alphanums,Regex,ParseException,\ CaselessKeyword, Suppress import math import operator exprStack = [] def pushFirst( strg, loc, toks ): exprStack.append( toks[0] ) def pushUMinus( strg, loc, toks ): for t in toks: if t == '-': exprStack.append( 'unary -' ) #~ exprStack.append( '-1' ) #~ exprStack.append( '*' ) else: break bnf = None def BNF(): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ global bnf if not bnf: point = Literal( "." ) # use CaselessKeyword for e and pi, to avoid accidentally matching # functions that start with 'e' or 'pi' (such as 'exp'); Keyword # and CaselessKeyword only match whole words e = CaselessKeyword( "E" ) pi = CaselessKeyword( "PI" ) #~ fnumber = Combine( Word( "+-"+nums, nums ) + #~ Optional( point + Optional( Word( nums ) ) ) + #~ Optional( e + Word( "+-"+nums, nums ) ) ) fnumber = Regex(r"[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?") ident = Word(alphas, alphanums+"_$") plus, minus, mult, div = map(Literal, "+-*/") lpar, rpar = map(Suppress, "()") addop = plus | minus multop = mult | div expop = Literal( "^" ) expr = Forward() atom = ((0,None)*minus + ( pi | e | fnumber | ident + lpar + expr + rpar | ident ).setParseAction( pushFirst ) | Group( lpar + expr + rpar )).setParseAction(pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) bnf = expr return bnf # map operator symbols to corresponding arithmetic operations epsilon = 1e-12 opn = { "+" : operator.add, "-" : operator.sub, "*" : operator.mul, "/" : operator.truediv, "^" : operator.pow } fn = { "sin" : math.sin, "cos" : math.cos, "tan" : math.tan, "exp" : math.exp, "abs" : abs, "trunc" : lambda a: int(a), "round" : round, "sgn" : lambda a: (a > epsilon) - (a < -epsilon) } def evaluateStack( s ): op = s.pop() if op == 'unary -': return -evaluateStack( s ) if op in "+-*/^": op2 = evaluateStack( s ) op1 = evaluateStack( s ) return opn[op]( op1, op2 ) elif op == "PI": return math.pi # 3.1415926535 elif op == "E": return math.e # 2.718281828 elif op in fn: return fn[op]( evaluateStack( s ) ) elif op[0].isalpha(): raise Exception("invalid identifier '%s'" % op) else: return float( op ) if __name__ == "__main__": def test( s, expVal ): global exprStack exprStack[:] = [] try: results = BNF().parseString( s, parseAll=True ) val = evaluateStack( exprStack[:] ) except ParseException as pe: print(s, "failed parse:", str(pe)) except Exception as e: print(s, "failed eval:", str(e)) else: if val == expVal: print(s, "=", val, results, "=>", exprStack) else: print(s+"!!!", val, "!=", expVal, results, "=>", exprStack) test( "9", 9 ) test( "-9", -9 ) test( "--9", 9 ) test( "-E", -math.e ) test( "9 + 3 + 6", 9 + 3 + 6 ) test( "9 + 3 / 11", 9 + 3.0 / 11 ) test( "(9 + 3)", (9 + 3) ) test( "(9+3) / 11", (9+3.0) / 11 ) test( "9 - 12 - 6", 9 - 12 - 6 ) test( "9 - (12 - 6)", 9 - (12 - 6) ) test( "2*3.14159", 2*3.14159 ) test( "3.1415926535*3.1415926535 / 10", 3.1415926535*3.1415926535 / 10 ) test( "PI * PI / 10", math.pi * math.pi / 10 ) test( "PI*PI/10", math.pi*math.pi/10 ) test( "PI^2", math.pi**2 ) test( "round(PI^2)", round(math.pi**2) ) test( "6.02E23 * 8.048", 6.02E23 * 8.048 ) test( "e / 3", math.e / 3 ) test( "sin(PI/2)", math.sin(math.pi/2) ) test( "trunc(E)", int(math.e) ) test( "trunc(-E)", int(-math.e) ) test( "round(E)", round(math.e) ) test( "round(-E)", round(-math.e) ) test( "E^PI", math.e**math.pi ) test( "exp(0)", 1 ) test( "exp(1)", math.e ) test( "2^3^2", 2**3**2 ) test( "2^3+2", 2**3+2 ) test( "2^3+5", 2**3+5 ) test( "2^9", 2**9 ) test( "sgn(-2)", -1 ) test( "sgn(0)", 0 ) test( "foo(0.1)", None ) test( "sgn(0.1)", 1 ) """ Test output: >pythonw -u fourFn.py 9 = 9.0 ['9'] => ['9'] 9 + 3 + 6 = 18.0 ['9', '+', '3', '+', '6'] => ['9', '3', '+', '6', '+'] 9 + 3 / 11 = 9.27272727273 ['9', '+', '3', '/', '11'] => ['9', '3', '11', '/', '+'] (9 + 3) = 12.0 [] => ['9', '3', '+'] (9+3) / 11 = 1.09090909091 ['/', '11'] => ['9', '3', '+', '11', '/'] 9 - 12 - 6 = -9.0 ['9', '-', '12', '-', '6'] => ['9', '12', '-', '6', '-'] 9 - (12 - 6) = 3.0 ['9', '-'] => ['9', '12', '6', '-', '-'] 2*3.14159 = 6.28318 ['2', '*', '3.14159'] => ['2', '3.14159', '*'] 3.1415926535*3.1415926535 / 10 = 0.986960440053 ['3.1415926535', '*', '3.1415926535', '/', '10'] => ['3.1415926535', '3.1415926535', '*', '10', '/'] PI * PI / 10 = 0.986960440109 ['PI', '*', 'PI', '/', '10'] => ['PI', 'PI', '*', '10', '/'] PI*PI/10 = 0.986960440109 ['PI', '*', 'PI', '/', '10'] => ['PI', 'PI', '*', '10', '/'] PI^2 = 9.86960440109 ['PI', '^', '2'] => ['PI', '2', '^'] 6.02E23 * 8.048 = 4.844896e+024 ['6.02E23', '*', '8.048'] => ['6.02E23', '8.048', '*'] e / 3 = 0.90609394282 ['E', '/', '3'] => ['E', '3', '/'] sin(PI/2) = 1.0 ['sin', 'PI', '/', '2'] => ['PI', '2', '/', 'sin'] trunc(E) = 2 ['trunc', 'E'] => ['E', 'trunc'] E^PI = 23.1406926328 ['E', '^', 'PI'] => ['E', 'PI', '^'] 2^3^2 = 512.0 ['2', '^', '3', '^', '2'] => ['2', '3', '2', '^', '^'] 2^3+2 = 10.0 ['2', '^', '3', '+', '2'] => ['2', '3', '^', '2', '+'] 2^9 = 512.0 ['2', '^', '9'] => ['2', '9', '^'] sgn(-2) = -1 ['sgn', '-2'] => ['-2', 'sgn'] sgn(0) = 0 ['sgn', '0'] => ['0', 'sgn'] sgn(0.1) = 1 ['sgn', '0.1'] => ['0.1', 'sgn'] >Exit code: 0 """ pyparsing2-2.4.7/examples/gen_ctypes.py000066400000000000000000000123151365333160500201610ustar00rootroot00000000000000# # gen_ctypes.py # # Parse a .h header file to generate ctypes argtype and return type definitions # # Copyright 2004-2016, by Paul McGuire # from pyparsing import * typemap = { "byte" : "c_byte", "char" : "c_char", "char *" : "c_char_p", "double" : "c_double", "float" : "c_float", "int" : "c_int", "int16" : "c_int16", "int32" : "c_int32", "int64" : "c_int64", "int8" : "c_int8", "long" : "c_long", "longlong" : "c_longlong", "short" : "c_short", "size_t" : "c_size_t", "ubyte" : "c_ubyte", "uchar" : "c_ubyte", "u_char" : "c_ubyte", "uint" : "c_uint", "u_int" : "c_uint", "uint16" : "c_uint16", "uint32" : "c_uint32", "uint64" : "c_uint64", "uint8" : "c_uint8", "u_long" : "c_ulong", "ulong" : "c_ulong", "ulonglong" : "c_ulonglong", "ushort" : "c_ushort", "u_short" : "c_ushort", "void *" : "c_void_p", "voidp" : "c_voidp", "wchar" : "c_wchar", "wchar *" : "c_wchar_p", "Bool" : "c_bool", "void" : "None", } LPAR,RPAR,LBRACE,RBRACE,COMMA,SEMI = map(Suppress,"(){},;") ident = Word(alphas, alphanums + "_") integer = Regex(r"[+-]?\d+") hexinteger = Regex(r"0x[0-9a-fA-F]+") const = Suppress("const") primitiveType = oneOf(t for t in typemap if not t.endswith("*")) structType = Suppress("struct") + ident vartype = (Optional(const) + (primitiveType | structType | ident) + Optional(Word("*")("ptr"))) def normalizetype(t): if isinstance(t, ParseResults): return ' '.join(t) #~ ret = ParseResults([' '.join(t)]) #~ return ret vartype.setParseAction(normalizetype) arg = Group(vartype("argtype") + Optional(ident("argname"))) func_def = (vartype("fn_type") + ident("fn_name") + LPAR + Optional(delimitedList(arg|"..."))("fn_args") + RPAR + SEMI) def derivefields(t): if t.fn_args and t.fn_args[-1] == "...": t["varargs"]=True func_def.setParseAction(derivefields) fn_typedef = "typedef" + func_def var_typedef = "typedef" + primitiveType("primType") + ident("name") + SEMI enum_def = (Keyword("enum") + LBRACE + delimitedList(Group(ident("name") + '=' + (hexinteger|integer)("value")))("evalues") + Optional(COMMA) + RBRACE) c_header = open("snmp_api.h").read() module = "pynetsnmp" user_defined_types = set() typedefs = [] fn_typedefs = [] functions = [] enum_constants = [] # add structures commonly included from std lib headers def addStdType(t,namespace=""): fullname = namespace+'_'+t if namespace else t typemap[t] = fullname user_defined_types.add(t) addStdType("fd_set", "sys_select") addStdType("timeval", "sys_time") def getUDType(typestr): key = typestr.rstrip(" *") if key not in typemap: user_defined_types.add(key) typemap[key] = "{0}_{1}".format(module, key) def typeAsCtypes(typestr): if typestr in typemap: return typemap[typestr] if typestr.endswith("*"): return "POINTER(%s)" % typeAsCtypes(typestr.rstrip(" *")) return typestr # scan input header text for primitive typedefs for td,_,_ in var_typedef.scanString(c_header): typedefs.append( (td.name, td.primType) ) # add typedef type to typemap to map to itself typemap[td.name] = td.name # scan input header text for function typedefs fn_typedefs = fn_typedef.searchString(c_header) # add each function typedef to typemap to map to itself for fntd in fn_typedefs: typemap[fntd.fn_name] = fntd.fn_name # scan input header text, and keep running list of user-defined types for fn,_,_ in (cStyleComment.suppress() | fn_typedef.suppress() | func_def).scanString(c_header): if not fn: continue getUDType(fn.fn_type) for arg in fn.fn_args: if arg != "...": if arg.argtype not in typemap: getUDType(arg.argtype) functions.append(fn) # scan input header text for enums enum_def.ignore(cppStyleComment) for en_,_,_ in enum_def.scanString(c_header): for ev in en_.evalues: enum_constants.append( (ev.name, ev.value) ) print("from ctypes import *") print("{0} = CDLL('{1}.dll')".format(module, module)) print() print("# user defined types") for tdname,tdtyp in typedefs: print("{0} = {1}".format(tdname, typemap[tdtyp])) for fntd in fn_typedefs: print("{0} = CFUNCTYPE({1})".format(fntd.fn_name, ',\n '.join(typeAsCtypes(a.argtype) for a in fntd.fn_args))) for udtype in user_defined_types: print("class %s(Structure): pass" % typemap[udtype]) print() print("# constant definitions") for en,ev in enum_constants: print("{0} = {1}".format(en,ev)) print() print("# functions") for fn in functions: prefix = "{0}.{1}".format(module, fn.fn_name) print("{0}.restype = {1}".format(prefix, typeAsCtypes(fn.fn_type))) if fn.varargs: print("# warning - %s takes variable argument list" % prefix) del fn.fn_args[-1] if fn.fn_args.asList() != [['void']]: print("{0}.argtypes = ({1},)".format(prefix, ','.join(typeAsCtypes(a.argtype) for a in fn.fn_args))) else: print("%s.argtypes = ()" % (prefix)) pyparsing2-2.4.7/examples/getNTPserversNew.py000066400000000000000000000024141365333160500212450ustar00rootroot00000000000000# getNTPserversNew.py # # Demonstration of the parsing module, implementing a HTML page scanner, # to extract a list of NTP time servers from the NIST web site. # # Copyright 2004-2010, by Paul McGuire # September, 2010 - updated to more current use of setResultsName, new NIST URL # import pyparsing as pp ppc = pp.pyparsing_common from contextlib import closing try: import urllib.request urlopen = urllib.request.urlopen except ImportError: import urllib urlopen = urllib.urlopen integer = pp.Word(pp.nums) ipAddress = ppc.ipv4_address() hostname = pp.delimitedList(pp.Word(pp.alphas, pp.alphanums+"-_"), ".", combine=True) tdStart, tdEnd = pp.makeHTMLTags("td") timeServerPattern = (tdStart + hostname("hostname") + tdEnd + tdStart + ipAddress("ipAddr") + tdEnd + tdStart + tdStart.tag_body("loc") + tdEnd) # get list of time servers nistTimeServerURL = "https://tf.nist.gov/tf-cgi/servers.cgi#" with closing(urlopen(nistTimeServerURL)) as serverListPage: serverListHTML = serverListPage.read().decode("UTF-8") addrs = {} for srvr, startloc, endloc in timeServerPattern.scanString(serverListHTML): print("{0} ({1}) - {2}".format(srvr.ipAddr, srvr.hostname.strip(), srvr.loc.strip())) addrs[srvr.ipAddr] = srvr.loc pyparsing2-2.4.7/examples/greeting.py000066400000000000000000000007761365333160500176350ustar00rootroot00000000000000# greeting.py # # Demonstration of the pyparsing module, on the prototypical "Hello, World!" # example # # Copyright 2003, 2019 by Paul McGuire # import pyparsing as pp # define grammar greet = pp.Word(pp.alphas) + "," + pp.Word(pp.alphas) + pp.oneOf("! ? .") # input string hello = "Hello, World!" # parse input string print(hello, "->", greet.parseString( hello )) # parse a bunch of input strings greet.runTests("""\ Hello, World! Ahoy, Matey! Howdy, Pardner! Morning, Neighbor! """)pyparsing2-2.4.7/examples/greetingInGreek.py000066400000000000000000000007021365333160500210670ustar00rootroot00000000000000# vim:fileencoding=utf-8 # # greetingInGreek.py # # Demonstration of the parsing module, on the prototypical "Hello, World!" example # # Copyright 2004-2016, by Paul McGuire # from pyparsing import Word, pyparsing_unicode as ppu # define grammar alphas = ppu.Greek.alphas greet = Word(alphas) + ',' + Word(alphas) + '!' # input string hello = "Καλημέρα, κόσμε!" # parse input string print(greet.parseString(hello)) pyparsing2-2.4.7/examples/greetingInKorean.py000066400000000000000000000007761365333160500212640ustar00rootroot00000000000000# vim:fileencoding=utf-8 # # greetingInKorean.py # # Demonstration of the parsing module, on the prototypical "Hello, World!" example # # Copyright 2004-2016, by Paul McGuire # from pyparsing import Word, pyparsing_unicode as ppu koreanChars = ppu.Korean.alphas koreanWord = Word(koreanChars, min=2) # define grammar greet = koreanWord + "," + koreanWord + "!" # input string hello = '안녕, 여러분!' #"Hello, World!" in Korean # parse input string print(greet.parseString(hello)) pyparsing2-2.4.7/examples/groupUsingListAllMatches.py000066400000000000000000000007751365333160500227640ustar00rootroot00000000000000# # A simple example showing the use of the implied listAllMatches=True for # results names with a trailing '*' character. # # This example performs work similar to itertools.groupby, but without # having to sort the input first. # # Copyright 2004-2016, by Paul McGuire # from pyparsing import Word, ZeroOrMore, nums aExpr = Word("A", nums) bExpr = Word("B", nums) cExpr = Word("C", nums) grammar = ZeroOrMore(aExpr("A*") | bExpr("B*") | cExpr("C*")) grammar.runTests("A1 B1 A2 C1 B2 A3") pyparsing2-2.4.7/examples/holaMundo.py000066400000000000000000000035731365333160500177550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # escrito por Marco Alfonso, 2004 Noviembre # importamos los símbolos requeridos desde el módulo from pyparsing import Word, alphas, oneOf, nums, Group, OneOrMore, pyparsing_unicode as ppu # usamos las letras en latin1, que incluye las como 'ñ', 'á', 'é', etc. alphas = ppu.Latin1.alphas # Aqui decimos que la gramatica "saludo" DEBE contener # una palabra compuesta de caracteres alfanumericos # (Word(alphas)) mas una ',' mas otra palabra alfanumerica, # mas '!' y esos seian nuestros tokens saludo = Word(alphas) + ',' + Word(alphas) + oneOf('! . ?') tokens = saludo.parseString("Hola, Mundo !") # Ahora parseamos una cadena, "Hola, Mundo!", # el metodo parseString, nos devuelve una lista con los tokens # encontrados, en caso de no haber errores... for i, token in enumerate(tokens): print ("Token %d -> %s" % (i,token)) #imprimimos cada uno de los tokens Y listooo!!, he aquí a salida # Token 0 -> Hola # Token 1 -> , # Token 2-> Mundo # Token 3 -> ! # ahora cambia el parseador, aceptando saludos con mas que una sola palabra antes que ',' saludo = Group(OneOrMore(Word(alphas))) + ',' + Word(alphas) + oneOf('! . ?') tokens = saludo.parseString("Hasta mañana, Mundo !") for i, token in enumerate(tokens): print ("Token %d -> %s" % (i,token)) # Ahora parseamos algunas cadenas, usando el metodo runTests saludo.runTests("""\ Hola, Mundo! Hasta mañana, Mundo ! """, fullDump=False) # Por supuesto, se pueden "reutilizar" gramáticas, por ejemplo: numimag = Word(nums) + 'i' numreal = Word(nums) numcomplex = numreal + '+' + numimag print (numcomplex.parseString("3+5i")) # Cambiar a complejo numero durante parsear: numcomplex.setParseAction(lambda t: complex(''.join(t).replace('i','j'))) print (numcomplex.parseString("3+5i")) # Excelente!!, bueno, los dejo, me voy a seguir tirando código... pyparsing2-2.4.7/examples/htmlStripper.py000066400000000000000000000022031365333160500205110ustar00rootroot00000000000000# # htmlStripper.py # # Sample code for stripping HTML markup tags and scripts from # HTML source files. # # Copyright (c) 2006, 2016, Paul McGuire # from contextlib import closing import urllib.request, urllib.parse, urllib.error from pyparsing import (makeHTMLTags, commonHTMLEntity, replaceHTMLEntity, htmlComment, anyOpenTag, anyCloseTag, LineEnd, OneOrMore, replaceWith) scriptOpen, scriptClose = makeHTMLTags("script") scriptBody = scriptOpen + scriptOpen.tag_body + scriptClose commonHTMLEntity.setParseAction(replaceHTMLEntity) # get some HTML targetURL = "https://wiki.python.org/moin/PythonDecoratorLibrary" with closing(urllib.request.urlopen( targetURL )) as targetPage: targetHTML = targetPage.read().decode("UTF-8") # first pass, strip out tags and translate entities firstPass = (htmlComment | scriptBody | commonHTMLEntity | anyOpenTag | anyCloseTag ).suppress().transformString(targetHTML) # first pass leaves many blank lines, collapse these down repeatedNewlines = LineEnd()*(2,) repeatedNewlines.setParseAction(replaceWith("\n\n")) secondPass = repeatedNewlines.transformString(firstPass) print(secondPass) pyparsing2-2.4.7/examples/htmlTableParser.py000066400000000000000000000040731365333160500211140ustar00rootroot00000000000000# # htmlTableParser.py # # Example of parsing a simple HTML table into a list of rows, and optionally into a little database # # Copyright 2019, Paul McGuire # import pyparsing as pp import urllib.request # define basic HTML tags, and compose into a Table table, table_end = pp.makeHTMLTags('table') thead, thead_end = pp.makeHTMLTags('thead') tbody, tbody_end = pp.makeHTMLTags('tbody') tr, tr_end = pp.makeHTMLTags('tr') th, th_end = pp.makeHTMLTags('th') td, td_end = pp.makeHTMLTags('td') a, a_end = pp.makeHTMLTags('a') # method to strip HTML tags from a string - will be used to clean up content of table cells strip_html = (pp.anyOpenTag | pp.anyCloseTag).suppress().transformString # expression for parsing text links, returning a (text, url) tuple link = pp.Group(a + a.tag_body('text') + a_end.suppress()) link.addParseAction(lambda t: (t[0].text, t[0].href)) # method to create table rows of header and data tags def table_row(start_tag, end_tag): body = start_tag.tag_body body.addParseAction(pp.tokenMap(str.strip), pp.tokenMap(strip_html)) row = pp.Group(tr.suppress() + pp.ZeroOrMore(start_tag.suppress() + body + end_tag.suppress()) + tr_end.suppress()) return row th_row = table_row(th, th_end) td_row = table_row(td, td_end) # define expression for overall table - may vary slightly for different pages html_table = table + tbody + pp.Optional(th_row('headers')) + pp.ZeroOrMore(td_row)('rows') + tbody_end + table_end # read in a web page containing an interesting HTML table with urllib.request.urlopen("https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") as page: page_html = page.read().decode() tz_table = html_table.searchString(page_html)[0] # convert rows to dicts rows = [dict(zip(tz_table.headers, row)) for row in tz_table.rows] # make a dict keyed by TZ database name tz_db = {row['TZ database name']: row for row in rows} from pprint import pprint pprint(tz_db['America/Chicago']) pyparsing2-2.4.7/examples/httpServerLogParser.py000066400000000000000000000066211365333160500220110ustar00rootroot00000000000000# httpServerLogParser.py # # Copyright (c) 2016, Paul McGuire # """ Parser for HTTP server log output, of the form: 195.146.134.15 - - [20/Jan/2003:08:55:36 -0800] "GET /path/to/page.html HTTP/1.0" 200 4649 "http://www.somedomain.com/020602/page.html" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" 127.0.0.1 - u.surname@domain.com [12/Sep/2006:14:13:53 +0300] "GET /skins/monobook/external.png HTTP/1.0" 304 - "http://wiki.mysite.com/skins/monobook/main.css" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6" You can then break it up as follows: IP ADDRESS - - Server Date / Time [SPACE] "GET /path/to/page HTTP/Type Request" Success Code Bytes Sent To Client Referer Client Software """ from pyparsing import alphas,nums, dblQuotedString, Combine, Word, Group, delimitedList, Suppress, removeQuotes import string def getCmdFields( s, l, t ): t["method"],t["requestURI"],t["protocolVersion"] = t[0].strip('"').split() logLineBNF = None def getLogLineBNF(): global logLineBNF if logLineBNF is None: integer = Word( nums ) ipAddress = delimitedList( integer, ".", combine=True ) timeZoneOffset = Word("+-",nums) month = Word(string.ascii_uppercase, string.ascii_lowercase, exact=3) serverDateTime = Group( Suppress("[") + Combine( integer + "/" + month + "/" + integer + ":" + integer + ":" + integer + ":" + integer ) + timeZoneOffset + Suppress("]") ) logLineBNF = ( ipAddress.setResultsName("ipAddr") + Suppress("-") + ("-" | Word( alphas+nums+"@._" )).setResultsName("auth") + serverDateTime.setResultsName("timestamp") + dblQuotedString.setResultsName("cmd").setParseAction(getCmdFields) + (integer | "-").setResultsName("statusCode") + (integer | "-").setResultsName("numBytesSent") + dblQuotedString.setResultsName("referrer").setParseAction(removeQuotes) + dblQuotedString.setResultsName("clientSfw").setParseAction(removeQuotes) ) return logLineBNF testdata = """ 195.146.134.15 - - [20/Jan/2003:08:55:36 -0800] "GET /path/to/page.html HTTP/1.0" 200 4649 "http://www.somedomain.com/020602/page.html" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" 111.111.111.11 - - [16/Feb/2004:04:09:49 -0800] "GET /ads/redirectads/336x280redirect.htm HTTP/1.1" 304 - "http://www.foobarp.org/theme_detail.php?type=vs&cat=0&mid=27512" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" 11.111.11.111 - - [16/Feb/2004:10:35:12 -0800] "GET /ads/redirectads/468x60redirect.htm HTTP/1.1" 200 541 "http://11.11.111.11/adframe.php?n=ad1f311a&what=zone:56" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) Opera 7.20 [ru\"]" 127.0.0.1 - u.surname@domain.com [12/Sep/2006:14:13:53 +0300] "GET /skins/monobook/external.png HTTP/1.0" 304 - "http://wiki.mysite.com/skins/monobook/main.css" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6" """ for line in testdata.split("\n"): if not line: continue fields = getLogLineBNF().parseString(line) print(fields.dump()) #~ print repr(fields) #~ for k in fields.keys(): #~ print "fields." + k + " =", fields[k] print() pyparsing2-2.4.7/examples/idlParse.py000066400000000000000000000142411365333160500175640ustar00rootroot00000000000000# # idlparse.py # # an example of using the parsing module to be able to process a subset of the CORBA IDL grammar # # Copyright (c) 2003, Paul McGuire # from pyparsing import Literal, Word, OneOrMore, ZeroOrMore, \ Forward, delimitedList, Group, Optional, alphas, restOfLine, cStyleComment, \ alphanums, quotedString, ParseException, Keyword, Regex import pprint #~ import tree2image bnf = None def CORBA_IDL_BNF(): global bnf if not bnf: # punctuation (colon,lbrace,rbrace,lbrack,rbrack,lparen,rparen, equals,comma,dot,slash,bslash,star,semi,langle,rangle) = map(Literal, r":{}[]()=,./\*;<>") # keywords (any_, attribute_, boolean_, case_, char_, const_, context_, default_, double_, enum_, exception_, FALSE_, fixed_, float_, inout_, interface_, in_, long_, module_, Object_, octet_, oneway_, out_, raises_, readonly_, sequence_, short_, string_, struct_, switch_, TRUE_, typedef_, unsigned_, union_, void_, wchar_, wstring_) = map(Keyword, """any attribute boolean case char const context default double enum exception FALSE fixed float inout interface in long module Object octet oneway out raises readonly sequence short string struct switch TRUE typedef unsigned union void wchar wstring""".split()) identifier = Word( alphas, alphanums + "_" ).setName("identifier") real = Regex(r"[+-]?\d+\.\d*([Ee][+-]?\d+)?").setName("real") integer = Regex(r"0x[0-9a-fA-F]+|[+-]?\d+").setName("int") udTypeName = delimitedList( identifier, "::", combine=True ).setName("udType") typeName = ( any_ | boolean_ | char_ | double_ | fixed_ | float_ | long_ | octet_ | short_ | string_ | wchar_ | wstring_ | udTypeName ).setName("type") sequenceDef = Forward().setName("seq") sequenceDef << Group( sequence_ + langle + ( sequenceDef | typeName ) + rangle ) typeDef = sequenceDef | ( typeName + Optional( lbrack + integer + rbrack ) ) typedefDef = Group( typedef_ + typeDef + identifier + semi ).setName("typedef") moduleDef = Forward() constDef = Group( const_ + typeDef + identifier + equals + ( real | integer | quotedString ) + semi ) #| quotedString ) exceptionItem = Group( typeDef + identifier + semi ) exceptionDef = ( exception_ + identifier + lbrace + ZeroOrMore( exceptionItem ) + rbrace + semi ) attributeDef = Optional( readonly_ ) + attribute_ + typeDef + identifier + semi paramlist = delimitedList( Group( ( inout_ | in_ | out_ ) + typeName + identifier ) ).setName( "paramlist" ) operationDef = ( ( void_ ^ typeDef ) + identifier + lparen + Optional( paramlist ) + rparen + \ Optional( raises_ + lparen + Group( delimitedList( typeName ) ) + rparen ) + semi ) interfaceItem = ( constDef | exceptionDef | attributeDef | operationDef ) interfaceDef = Group( interface_ + identifier + Optional( colon + delimitedList( typeName ) ) + lbrace + \ ZeroOrMore( interfaceItem ) + rbrace + semi ).setName("opnDef") moduleItem = ( interfaceDef | exceptionDef | constDef | typedefDef | moduleDef ) moduleDef << module_ + identifier + lbrace + ZeroOrMore( moduleItem ) + rbrace + semi bnf = ( moduleDef | OneOrMore( moduleItem ) ) singleLineComment = "//" + restOfLine bnf.ignore( singleLineComment ) bnf.ignore( cStyleComment ) return bnf testnum = 1 def test( strng ): global testnum print(strng) try: bnf = CORBA_IDL_BNF() tokens = bnf.parseString( strng ) print("tokens = ") pprint.pprint( tokens.asList() ) imgname = "idlParse%02d.bmp" % testnum testnum += 1 #~ tree2image.str2image( str(tokens.asList()), imgname ) except ParseException as err: print(err.line) print(" "*(err.column-1) + "^") print(err) print() if __name__ == "__main__": test( """ /* * a block comment * */ typedef string[10] tenStrings; typedef sequence stringSeq; typedef sequence< sequence > stringSeqSeq; interface QoSAdmin { stringSeq method1( in string arg1, inout long arg2 ); stringSeqSeq method2( in string arg1, inout long arg2, inout long arg3); string method3(); }; """ ) test( """ /* * a block comment * */ typedef string[10] tenStrings; typedef /** ** *** **** * * a block comment * */ sequence /*comment inside an And */ stringSeq; /* */ /**/ /***/ /****/ typedef sequence< sequence > stringSeqSeq; interface QoSAdmin { stringSeq method1( in string arg1, inout long arg2 ); stringSeqSeq method2( in string arg1, inout long arg2, inout long arg3); string method3(); }; """ ) test( r""" const string test="Test String\n"; const long a = 0; const long b = -100; const float c = 3.14159; const long d = 0x007f7f7f; exception TestException { string msg; sequence dataStrings; }; interface TestInterface { void method1( in string arg1, inout long arg2 ); }; """ ) test( """ module Test1 { exception TestException { string msg; ]; interface TestInterface { void method1( in string arg1, inout long arg2 ) raises ( TestException ); }; }; """ ) test( """ module Test1 { exception TestException { string msg; }; }; """ ) pyparsing2-2.4.7/examples/include_preprocessor.py000066400000000000000000000051241365333160500222520ustar00rootroot00000000000000# # include_preprocessor.py # # Short pyparsing script to perform #include inclusions similar to the C preprocessor # # Copyright 2019, Paul McGuire # import pyparsing as pp from pathlib import Path # parser elements to be used to assemble into #include parser SEMI = pp.Suppress(';') INCLUDE = pp.Keyword("#include") quoted_string = pp.quotedString.addParseAction(pp.removeQuotes) file_ref = (quoted_string | pp.Word(pp.printables, excludeChars=';')) # parser for parsing "#include xyz.dat;" directives include_directive = (INCLUDE + file_ref("include_file_name") + SEMI) # add parse action that will recursively pull in included files - when # using transformString, the value returned from the parse action will replace # the text matched by the attached expression seen = set() def read_include_contents(s, l, t): include_file_ref = t.include_file_name include_echo = "/* {} */".format(pp.line(l, s).strip()) # guard against recursive includes if include_file_ref not in seen: seen.add(include_file_ref) included_file_contents = Path(include_file_ref).read_text() return (include_echo + '\n' + include_directive.transformString(included_file_contents)) else: lead = ' '*(pp.col(l, s) - 1) return "/* recursive include! */\n{}{}".format(lead, include_echo) # attach include processing method as parse action (parse-time callback) # to include_directive expression include_directive.addParseAction(read_include_contents) if __name__ == '__main__': # demo # create test files: # - a.txt includes b.txt # - b.txt includes c.txt # - c.txt includes b.txt (must catch infinite recursion) Path('a.txt').write_text("""\ /* a.txt */ int i; /* sometimes included files aren't in quotes */ #include b.txt; """) Path('b.txt').write_text("""\ i = 100; #include 'c.txt'; """) Path('c.txt').write_text("""\ i += 1; /* watch out! this might be recursive if this file included by b.txt */ #include b.txt; """) # use include_directive.transformString to perform includes # read contents of original file initial_file = Path('a.txt').read_text() # print original file print(initial_file) print('-----------------') # expand includes in source file (and any included files) and print the result expanded_source = include_directive.transformString(initial_file) print(expanded_source) # clean up for fname in "a.txt b.txt c.txt".split(): Path(fname).unlink() pyparsing2-2.4.7/examples/indentedGrammarExample.py000066400000000000000000000020341365333160500224330ustar00rootroot00000000000000# indentedGrammarExample.py # # Copyright (c) 2006,2016 Paul McGuire # # A sample of a pyparsing grammar using indentation for # grouping (like Python does). # # Updated to use indentedBlock helper method. # from pyparsing import * data = """\ def A(z): A1 B = 100 G = A2 A2 A3 B def BB(a,b,c): BB1 def BBA(): bba1 bba2 bba3 C D def spam(x,y): def eggs(z): pass """ indentStack = [1] stmt = Forward() suite = indentedBlock(stmt, indentStack) identifier = Word(alphas, alphanums) funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") funcDef = Group( funcDecl + suite ) rvalue = Forward() funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") rvalue << (funcCall | identifier | Word(nums)) assignment = Group(identifier + "=" + rvalue) stmt << ( funcDef | assignment | identifier ) module_body = OneOrMore(stmt) print(data) parseTree = module_body.parseString(data) parseTree.pprint() pyparsing2-2.4.7/examples/invRegex.py000066400000000000000000000174621365333160500176200ustar00rootroot00000000000000# # invRegex.py # # Copyright 2008, Paul McGuire # # pyparsing script to expand a regular expression into all possible matching strings # Supports: # - {n} and {m,n} repetition, but not unbounded + or * repetition # - ? optional elements # - [] character ranges # - () grouping # - | alternation # __all__ = ["count","invert"] from pyparsing import (Literal, oneOf, printables, ParserElement, Combine, SkipTo, infixNotation, ParseFatalException, Word, nums, opAssoc, Suppress, ParseResults, srange) class CharacterRangeEmitter(object): def __init__(self,chars): # remove duplicate chars in character range, but preserve original order seen = set() self.charset = "".join( seen.add(c) or c for c in chars if c not in seen ) def __str__(self): return '['+self.charset+']' def __repr__(self): return '['+self.charset+']' def makeGenerator(self): def genChars(): for s in self.charset: yield s return genChars class OptionalEmitter(object): def __init__(self,expr): self.expr = expr def makeGenerator(self): def optionalGen(): yield "" for s in self.expr.makeGenerator()(): yield s return optionalGen class DotEmitter(object): def makeGenerator(self): def dotGen(): for c in printables: yield c return dotGen class GroupEmitter(object): def __init__(self,exprs): self.exprs = ParseResults(exprs) def makeGenerator(self): def groupGen(): def recurseList(elist): if len(elist)==1: for s in elist[0].makeGenerator()(): yield s else: for s in elist[0].makeGenerator()(): for s2 in recurseList(elist[1:]): yield s + s2 if self.exprs: for s in recurseList(self.exprs): yield s return groupGen class AlternativeEmitter(object): def __init__(self,exprs): self.exprs = exprs def makeGenerator(self): def altGen(): for e in self.exprs: for s in e.makeGenerator()(): yield s return altGen class LiteralEmitter(object): def __init__(self,lit): self.lit = lit def __str__(self): return "Lit:"+self.lit def __repr__(self): return "Lit:"+self.lit def makeGenerator(self): def litGen(): yield self.lit return litGen def handleRange(toks): return CharacterRangeEmitter(srange(toks[0])) def handleRepetition(toks): toks=toks[0] if toks[1] in "*+": raise ParseFatalException("",0,"unbounded repetition operators not supported") if toks[1] == "?": return OptionalEmitter(toks[0]) if "count" in toks: return GroupEmitter([toks[0]] * int(toks.count)) if "minCount" in toks: mincount = int(toks.minCount) maxcount = int(toks.maxCount) optcount = maxcount - mincount if optcount: opt = OptionalEmitter(toks[0]) for i in range(1,optcount): opt = OptionalEmitter(GroupEmitter([toks[0],opt])) return GroupEmitter([toks[0]] * mincount + [opt]) else: return [toks[0]] * mincount def handleLiteral(toks): lit = "" for t in toks: if t[0] == "\\": if t[1] == "t": lit += '\t' else: lit += t[1] else: lit += t return LiteralEmitter(lit) def handleMacro(toks): macroChar = toks[0][1] if macroChar == "d": return CharacterRangeEmitter("0123456789") elif macroChar == "w": return CharacterRangeEmitter(srange("[A-Za-z0-9_]")) elif macroChar == "s": return LiteralEmitter(" ") else: raise ParseFatalException("",0,"unsupported macro character (" + macroChar + ")") def handleSequence(toks): return GroupEmitter(toks[0]) def handleDot(): return CharacterRangeEmitter(printables) def handleAlternative(toks): return AlternativeEmitter(toks[0]) _parser = None def parser(): global _parser if _parser is None: ParserElement.setDefaultWhitespaceChars("") lbrack,rbrack,lbrace,rbrace,lparen,rparen,colon,qmark = map(Literal,"[]{}():?") reMacro = Combine("\\" + oneOf(list("dws"))) escapedChar = ~reMacro + Combine("\\" + oneOf(list(printables))) reLiteralChar = "".join(c for c in printables if c not in r"\[]{}().*?+|") + " \t" reRange = Combine(lbrack + SkipTo(rbrack,ignore=escapedChar) + rbrack) reLiteral = ( escapedChar | oneOf(list(reLiteralChar)) ) reNonCaptureGroup = Suppress("?:") reDot = Literal(".") repetition = ( ( lbrace + Word(nums)("count") + rbrace ) | ( lbrace + Word(nums)("minCount")+","+ Word(nums)("maxCount") + rbrace ) | oneOf(list("*+?")) ) reRange.setParseAction(handleRange) reLiteral.setParseAction(handleLiteral) reMacro.setParseAction(handleMacro) reDot.setParseAction(handleDot) reTerm = ( reLiteral | reRange | reMacro | reDot | reNonCaptureGroup) reExpr = infixNotation( reTerm, [ (repetition, 1, opAssoc.LEFT, handleRepetition), (None, 2, opAssoc.LEFT, handleSequence), (Suppress('|'), 2, opAssoc.LEFT, handleAlternative), ] ) _parser = reExpr return _parser def count(gen): """Simple function to count the number of elements returned by a generator.""" return sum(1 for _ in gen) def invert(regex): r"""Call this routine as a generator to return all the strings that match the input regular expression. for s in invert(r"[A-Z]{3}\d{3}"): print s """ invReGenerator = GroupEmitter(parser().parseString(regex)).makeGenerator() return invReGenerator() def main(): tests = r""" [A-EA] [A-D]* [A-D]{3} X[A-C]{3}Y X[A-C]{3}\( X\d foobar\d\d foobar{2} foobar{2,9} fooba[rz]{2} (foobar){2} ([01]\d)|(2[0-5]) (?:[01]\d)|(2[0-5]) ([01]\d\d)|(2[0-4]\d)|(25[0-5]) [A-C]{1,2} [A-C]{0,3} [A-C]\s[A-C]\s[A-C] [A-C]\s?[A-C][A-C] [A-C]\s([A-C][A-C]) [A-C]\s([A-C][A-C])? [A-C]{2}\d{2} @|TH[12] @(@|TH[12])? @(@|TH[12]|AL[12]|SP[123]|TB(1[0-9]?|20?|[3-9]))? @(@|TH[12]|AL[12]|SP[123]|TB(1[0-9]?|20?|[3-9])|OH(1[0-9]?|2[0-9]?|30?|[4-9]))? (([ECMP]|HA|AK)[SD]|HS)T [A-CV]{2} A[cglmrstu]|B[aehikr]?|C[adeflmorsu]?|D[bsy]|E[rsu]|F[emr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airu]|M[dgnot]|N[abdeiop]?|Os?|P[abdmortu]?|R[abefghnu]|S[bcegimnr]?|T[abcehilm]|Uu[bhopqst]|U|V|W|Xe|Yb?|Z[nr] (a|b)|(x|y) (a|b) (x|y) [ABCDEFG](?:#|##|b|bb)?(?:maj|min|m|sus|aug|dim)?[0-9]?(?:/[ABCDEFG](?:#|##|b|bb)?)? (Fri|Mon|S(atur|un)|T(hur|ue)s|Wednes)day A(pril|ugust)|((Dec|Nov|Sept)em|Octo)ber|(Febr|Jan)uary|Ju(ly|ne)|Ma(rch|y) """.split('\n') for t in tests: t = t.strip() if not t: continue print('-'*50) print(t) try: num = count(invert(t)) print(num) maxprint = 30 for s in invert(t): print(s) maxprint -= 1 if not maxprint: break except ParseFatalException as pfe: print(pfe.msg) print('') continue print('') if __name__ == "__main__": main() pyparsing2-2.4.7/examples/javascript_grammar.g000066400000000000000000000441501365333160500214750ustar00rootroot00000000000000/* Copyright 2008 Chris Lambrou. All rights reserved. */ grammar JavaScript; options { output=AST; backtrack=true; memoize=true; } program : LT!* sourceElements LT!* EOF! ; sourceElements : sourceElement (LT!* sourceElement)* ; sourceElement : functionDeclaration | statement ; // functions functionDeclaration : 'function' LT!* Identifier LT!* formalParameterList LT!* functionBody ; functionExpression : 'function' LT!* Identifier? LT!* formalParameterList LT!* functionBody ; formalParameterList : '(' (LT!* Identifier (LT!* ',' LT!* Identifier)*)? LT!* ')' ; functionBody : '{' LT!* sourceElements LT!* '}' ; // statements statement : statementBlock | variableStatement | emptyStatement | expressionStatement | ifStatement | iterationStatement | continueStatement | breakStatement | returnStatement | withStatement | labelledStatement | switchStatement | throwStatement | tryStatement ; statementBlock : '{' LT!* statementList? LT!* '}' ; statementList : statement (LT!* statement)* ; variableStatement : 'var' LT!* variableDeclarationList (LT | ';')! ; variableDeclarationList : variableDeclaration (LT!* ',' LT!* variableDeclaration)* ; variableDeclarationListNoIn : variableDeclarationNoIn (LT!* ',' LT!* variableDeclarationNoIn)* ; variableDeclaration : Identifier LT!* initialiser? ; variableDeclarationNoIn : Identifier LT!* initialiserNoIn? ; initialiser : '=' LT!* assignmentExpression ; initialiserNoIn : '=' LT!* assignmentExpressionNoIn ; emptyStatement : ';' ; expressionStatement : expression (LT | ';')! ; ifStatement : 'if' LT!* '(' LT!* expression LT!* ')' LT!* statement (LT!* 'else' LT!* statement)? ; iterationStatement : doWhileStatement | whileStatement | forStatement | forInStatement ; doWhileStatement : 'do' LT!* statement LT!* 'while' LT!* '(' expression ')' (LT | ';')! ; whileStatement : 'while' LT!* '(' LT!* expression LT!* ')' LT!* statement ; forStatement : 'for' LT!* '(' (LT!* forStatementInitialiserPart)? LT!* ';' (LT!* expression)? LT!* ';' (LT!* expression)? LT!* ')' LT!* statement ; forStatementInitialiserPart : expressionNoIn | 'var' LT!* variableDeclarationListNoIn ; forInStatement : 'for' LT!* '(' LT!* forInStatementInitialiserPart LT!* 'in' LT!* expression LT!* ')' LT!* statement ; forInStatementInitialiserPart : leftHandSideExpression | 'var' LT!* variableDeclarationNoIn ; continueStatement : 'continue' Identifier? (LT | ';')! ; breakStatement : 'break' Identifier? (LT | ';')! ; returnStatement : 'return' expression? (LT | ';')! ; withStatement : 'with' LT!* '(' LT!* expression LT!* ')' LT!* statement ; labelledStatement : Identifier LT!* ':' LT!* statement ; switchStatement : 'switch' LT!* '(' LT!* expression LT!* ')' LT!* caseBlock ; caseBlock : '{' (LT!* caseClause)* (LT!* defaultClause (LT!* caseClause)*)? LT!* '}' ; caseClause : 'case' LT!* expression LT!* ':' LT!* statementList? ; defaultClause : 'default' LT!* ':' LT!* statementList? ; throwStatement : 'throw' expression (LT | ';')! ; tryStatement : 'try' LT!* statementBlock LT!* (finallyClause | catchClause (LT!* finallyClause)?) ; catchClause : 'catch' LT!* '(' LT!* Identifier LT!* ')' LT!* statementBlock ; finallyClause : 'finally' LT!* statementBlock ; // expressions expression : assignmentExpression (LT!* ',' LT!* assignmentExpression)* ; expressionNoIn : assignmentExpressionNoIn (LT!* ',' LT!* assignmentExpressionNoIn)* ; assignmentExpression : conditionalExpression | leftHandSideExpression LT!* assignmentOperator LT!* assignmentExpression ; assignmentExpressionNoIn : conditionalExpressionNoIn | leftHandSideExpression LT!* assignmentOperator LT!* assignmentExpressionNoIn ; leftHandSideExpression : callExpression | newExpression ; newExpression : memberExpression | 'new' LT!* newExpression ; memberExpression : (primaryExpression | functionExpression | 'new' LT!* memberExpression LT!* arguments) (LT!* memberExpressionSuffix)* ; memberExpressionSuffix : indexSuffix | propertyReferenceSuffix ; callExpression : memberExpression LT!* arguments (LT!* callExpressionSuffix)* ; callExpressionSuffix : arguments | indexSuffix | propertyReferenceSuffix ; arguments : '(' (LT!* assignmentExpression (LT!* ',' LT!* assignmentExpression)*)? LT!* ')' ; indexSuffix : '[' LT!* expression LT!* ']' ; propertyReferenceSuffix : '.' LT!* Identifier ; assignmentOperator : '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|=' ; conditionalExpression : logicalORExpression (LT!* '?' LT!* assignmentExpression LT!* ':' LT!* assignmentExpression)? ; conditionalExpressionNoIn : logicalORExpressionNoIn (LT!* '?' LT!* assignmentExpressionNoIn LT!* ':' LT!* assignmentExpressionNoIn)? ; logicalORExpression : logicalANDExpression (LT!* '||' LT!* logicalANDExpression)* ; logicalORExpressionNoIn : logicalANDExpressionNoIn (LT!* '||' LT!* logicalANDExpressionNoIn)* ; logicalANDExpression : bitwiseORExpression (LT!* '&&' LT!* bitwiseORExpression)* ; logicalANDExpressionNoIn : bitwiseORExpressionNoIn (LT!* '&&' LT!* bitwiseORExpressionNoIn)* ; bitwiseORExpression : bitwiseXORExpression (LT!* '|' LT!* bitwiseXORExpression)* ; bitwiseORExpressionNoIn : bitwiseXORExpressionNoIn (LT!* '|' LT!* bitwiseXORExpressionNoIn)* ; bitwiseXORExpression : bitwiseANDExpression (LT!* '^' LT!* bitwiseANDExpression)* ; bitwiseXORExpressionNoIn : bitwiseANDExpressionNoIn (LT!* '^' LT!* bitwiseANDExpressionNoIn)* ; bitwiseANDExpression : equalityExpression (LT!* '&' LT!* equalityExpression)* ; bitwiseANDExpressionNoIn : equalityExpressionNoIn (LT!* '&' LT!* equalityExpressionNoIn)* ; equalityExpression : relationalExpression (LT!* ('==' | '!=' | '===' | '!==') LT!* relationalExpression)* ; equalityExpressionNoIn : relationalExpressionNoIn (LT!* ('==' | '!=' | '===' | '!==') LT!* relationalExpressionNoIn)* ; relationalExpression : shiftExpression (LT!* ('<' | '>' | '<=' | '>=' | 'instanceof' | 'in') LT!* shiftExpression)* ; relationalExpressionNoIn : shiftExpression (LT!* ('<' | '>' | '<=' | '>=' | 'instanceof') LT!* shiftExpression)* ; shiftExpression : additiveExpression (LT!* ('<<' | '>>' | '>>>') LT!* additiveExpression)* ; additiveExpression : multiplicativeExpression (LT!* ('+' | '-') LT!* multiplicativeExpression)* ; multiplicativeExpression : unaryExpression (LT!* ('*' | '/' | '%') LT!* unaryExpression)* ; unaryExpression : postfixExpression | ('delete' | 'void' | 'typeof' | '++' | '--' | '+' | '-' | '~' | '!') unaryExpression ; postfixExpression : leftHandSideExpression ('++' | '--')? ; primaryExpression : 'this' | Identifier | literal | arrayLiteral | objectLiteral | '(' LT!* expression LT!* ')' ; // arrayLiteral definition. arrayLiteral : '[' LT!* assignmentExpression? (LT!* ',' (LT!* assignmentExpression)?)* LT!* ']' ; // objectLiteral definition. objectLiteral : '{' LT!* propertyNameAndValue (LT!* ',' LT!* propertyNameAndValue)* LT!* '}' ; propertyNameAndValue : propertyName LT!* ':' LT!* assignmentExpression ; propertyName : Identifier | StringLiteral | NumericLiteral ; // primitive literal definition. literal : 'null' | 'true' | 'false' | StringLiteral | NumericLiteral ; // lexer rules. StringLiteral : '"' DoubleStringCharacter* '"' | '\'' SingleStringCharacter* '\'' ; fragment DoubleStringCharacter : ~('"' | '\\' | LT) | '\\' EscapeSequence ; fragment SingleStringCharacter : ~('\'' | '\\' | LT) | '\\' EscapeSequence ; fragment EscapeSequence : CharacterEscapeSequence | '0' | HexEscapeSequence | UnicodeEscapeSequence ; fragment CharacterEscapeSequence : SingleEscapeCharacter | NonEscapeCharacter ; fragment NonEscapeCharacter : ~(EscapeCharacter | LT) ; fragment SingleEscapeCharacter : '\'' | '"' | '\\' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' ; fragment EscapeCharacter : SingleEscapeCharacter | DecimalDigit | 'x' | 'u' ; fragment HexEscapeSequence : 'x' HexDigit HexDigit ; fragment UnicodeEscapeSequence : 'u' HexDigit HexDigit HexDigit HexDigit ; NumericLiteral : DecimalLiteral | HexIntegerLiteral ; fragment HexIntegerLiteral : '0' ('x' | 'X') HexDigit+ ; fragment HexDigit : DecimalDigit | ('a'..'f') | ('A'..'F') ; fragment DecimalLiteral : DecimalDigit+ '.' DecimalDigit* ExponentPart? | '.'? DecimalDigit+ ExponentPart? ; fragment DecimalDigit : ('0'..'9') ; fragment ExponentPart : ('e' | 'E') ('+' | '-') ? DecimalDigit+ ; Identifier : IdentifierStart IdentifierPart* ; fragment IdentifierStart : UnicodeLetter | '$' | '_' | '\\' UnicodeEscapeSequence ; fragment IdentifierPart : (IdentifierStart) => IdentifierStart // Avoids ambiguity, as some IdentifierStart chars also match following alternatives. | UnicodeDigit | UnicodeConnectorPunctuation ; fragment UnicodeLetter // Any character in the Unicode categories "Uppercase letter (Lu)", : '\u0041'..'\u005A' // "Lowercase letter (Ll)", "Titlecase letter (Lt)", | '\u0061'..'\u007A' // "Modifier letter (Lm)", "Other letter (Lo)", or "Letter number (Nl)". | '\u00AA' | '\u00B5' | '\u00BA' | '\u00C0'..'\u00D6' | '\u00D8'..'\u00F6' | '\u00F8'..'\u021F' | '\u0222'..'\u0233' | '\u0250'..'\u02AD' | '\u02B0'..'\u02B8' | '\u02BB'..'\u02C1' | '\u02D0'..'\u02D1' | '\u02E0'..'\u02E4' | '\u02EE' | '\u037A' | '\u0386' | '\u0388'..'\u038A' | '\u038C' | '\u038E'..'\u03A1' | '\u03A3'..'\u03CE' | '\u03D0'..'\u03D7' | '\u03DA'..'\u03F3' | '\u0400'..'\u0481' | '\u048C'..'\u04C4' | '\u04C7'..'\u04C8' | '\u04CB'..'\u04CC' | '\u04D0'..'\u04F5' | '\u04F8'..'\u04F9' | '\u0531'..'\u0556' | '\u0559' | '\u0561'..'\u0587' | '\u05D0'..'\u05EA' | '\u05F0'..'\u05F2' | '\u0621'..'\u063A' | '\u0640'..'\u064A' | '\u0671'..'\u06D3' | '\u06D5' | '\u06E5'..'\u06E6' | '\u06FA'..'\u06FC' | '\u0710' | '\u0712'..'\u072C' | '\u0780'..'\u07A5' | '\u0905'..'\u0939' | '\u093D' | '\u0950' | '\u0958'..'\u0961' | '\u0985'..'\u098C' | '\u098F'..'\u0990' | '\u0993'..'\u09A8' | '\u09AA'..'\u09B0' | '\u09B2' | '\u09B6'..'\u09B9' | '\u09DC'..'\u09DD' | '\u09DF'..'\u09E1' | '\u09F0'..'\u09F1' | '\u0A05'..'\u0A0A' | '\u0A0F'..'\u0A10' | '\u0A13'..'\u0A28' | '\u0A2A'..'\u0A30' | '\u0A32'..'\u0A33' | '\u0A35'..'\u0A36' | '\u0A38'..'\u0A39' | '\u0A59'..'\u0A5C' | '\u0A5E' | '\u0A72'..'\u0A74' | '\u0A85'..'\u0A8B' | '\u0A8D' | '\u0A8F'..'\u0A91' | '\u0A93'..'\u0AA8' | '\u0AAA'..'\u0AB0' | '\u0AB2'..'\u0AB3' | '\u0AB5'..'\u0AB9' | '\u0ABD' | '\u0AD0' | '\u0AE0' | '\u0B05'..'\u0B0C' | '\u0B0F'..'\u0B10' | '\u0B13'..'\u0B28' | '\u0B2A'..'\u0B30' | '\u0B32'..'\u0B33' | '\u0B36'..'\u0B39' | '\u0B3D' | '\u0B5C'..'\u0B5D' | '\u0B5F'..'\u0B61' | '\u0B85'..'\u0B8A' | '\u0B8E'..'\u0B90' | '\u0B92'..'\u0B95' | '\u0B99'..'\u0B9A' | '\u0B9C' | '\u0B9E'..'\u0B9F' | '\u0BA3'..'\u0BA4' | '\u0BA8'..'\u0BAA' | '\u0BAE'..'\u0BB5' | '\u0BB7'..'\u0BB9' | '\u0C05'..'\u0C0C' | '\u0C0E'..'\u0C10' | '\u0C12'..'\u0C28' | '\u0C2A'..'\u0C33' | '\u0C35'..'\u0C39' | '\u0C60'..'\u0C61' | '\u0C85'..'\u0C8C' | '\u0C8E'..'\u0C90' | '\u0C92'..'\u0CA8' | '\u0CAA'..'\u0CB3' | '\u0CB5'..'\u0CB9' | '\u0CDE' | '\u0CE0'..'\u0CE1' | '\u0D05'..'\u0D0C' | '\u0D0E'..'\u0D10' | '\u0D12'..'\u0D28' | '\u0D2A'..'\u0D39' | '\u0D60'..'\u0D61' | '\u0D85'..'\u0D96' | '\u0D9A'..'\u0DB1' | '\u0DB3'..'\u0DBB' | '\u0DBD' | '\u0DC0'..'\u0DC6' | '\u0E01'..'\u0E30' | '\u0E32'..'\u0E33' | '\u0E40'..'\u0E46' | '\u0E81'..'\u0E82' | '\u0E84' | '\u0E87'..'\u0E88' | '\u0E8A' | '\u0E8D' | '\u0E94'..'\u0E97' | '\u0E99'..'\u0E9F' | '\u0EA1'..'\u0EA3' | '\u0EA5' | '\u0EA7' | '\u0EAA'..'\u0EAB' | '\u0EAD'..'\u0EB0' | '\u0EB2'..'\u0EB3' | '\u0EBD'..'\u0EC4' | '\u0EC6' | '\u0EDC'..'\u0EDD' | '\u0F00' | '\u0F40'..'\u0F6A' | '\u0F88'..'\u0F8B' | '\u1000'..'\u1021' | '\u1023'..'\u1027' | '\u1029'..'\u102A' | '\u1050'..'\u1055' | '\u10A0'..'\u10C5' | '\u10D0'..'\u10F6' | '\u1100'..'\u1159' | '\u115F'..'\u11A2' | '\u11A8'..'\u11F9' | '\u1200'..'\u1206' | '\u1208'..'\u1246' | '\u1248' | '\u124A'..'\u124D' | '\u1250'..'\u1256' | '\u1258' | '\u125A'..'\u125D' | '\u1260'..'\u1286' | '\u1288' | '\u128A'..'\u128D' | '\u1290'..'\u12AE' | '\u12B0' | '\u12B2'..'\u12B5' | '\u12B8'..'\u12BE' | '\u12C0' | '\u12C2'..'\u12C5' | '\u12C8'..'\u12CE' | '\u12D0'..'\u12D6' | '\u12D8'..'\u12EE' | '\u12F0'..'\u130E' | '\u1310' | '\u1312'..'\u1315' | '\u1318'..'\u131E' | '\u1320'..'\u1346' | '\u1348'..'\u135A' | '\u13A0'..'\u13B0' | '\u13B1'..'\u13F4' | '\u1401'..'\u1676' | '\u1681'..'\u169A' | '\u16A0'..'\u16EA' | '\u1780'..'\u17B3' | '\u1820'..'\u1877' | '\u1880'..'\u18A8' | '\u1E00'..'\u1E9B' | '\u1EA0'..'\u1EE0' | '\u1EE1'..'\u1EF9' | '\u1F00'..'\u1F15' | '\u1F18'..'\u1F1D' | '\u1F20'..'\u1F39' | '\u1F3A'..'\u1F45' | '\u1F48'..'\u1F4D' | '\u1F50'..'\u1F57' | '\u1F59' | '\u1F5B' | '\u1F5D' | '\u1F5F'..'\u1F7D' | '\u1F80'..'\u1FB4' | '\u1FB6'..'\u1FBC' | '\u1FBE' | '\u1FC2'..'\u1FC4' | '\u1FC6'..'\u1FCC' | '\u1FD0'..'\u1FD3' | '\u1FD6'..'\u1FDB' | '\u1FE0'..'\u1FEC' | '\u1FF2'..'\u1FF4' | '\u1FF6'..'\u1FFC' | '\u207F' | '\u2102' | '\u2107' | '\u210A'..'\u2113' | '\u2115' | '\u2119'..'\u211D' | '\u2124' | '\u2126' | '\u2128' | '\u212A'..'\u212D' | '\u212F'..'\u2131' | '\u2133'..'\u2139' | '\u2160'..'\u2183' | '\u3005'..'\u3007' | '\u3021'..'\u3029' | '\u3031'..'\u3035' | '\u3038'..'\u303A' | '\u3041'..'\u3094' | '\u309D'..'\u309E' | '\u30A1'..'\u30FA' | '\u30FC'..'\u30FE' | '\u3105'..'\u312C' | '\u3131'..'\u318E' | '\u31A0'..'\u31B7' | '\u3400' | '\u4DB5' | '\u4E00' | '\u9FA5' | '\uA000'..'\uA48C' | '\uAC00' | '\uD7A3' | '\uF900'..'\uFA2D' | '\uFB00'..'\uFB06' | '\uFB13'..'\uFB17' | '\uFB1D' | '\uFB1F'..'\uFB28' | '\uFB2A'..'\uFB36' | '\uFB38'..'\uFB3C' | '\uFB3E' | '\uFB40'..'\uFB41' | '\uFB43'..'\uFB44' | '\uFB46'..'\uFBB1' | '\uFBD3'..'\uFD3D' | '\uFD50'..'\uFD8F' | '\uFD92'..'\uFDC7' | '\uFDF0'..'\uFDFB' | '\uFE70'..'\uFE72' | '\uFE74' | '\uFE76'..'\uFEFC' | '\uFF21'..'\uFF3A' | '\uFF41'..'\uFF5A' | '\uFF66'..'\uFFBE' | '\uFFC2'..'\uFFC7' | '\uFFCA'..'\uFFCF' | '\uFFD2'..'\uFFD7' | '\uFFDA'..'\uFFDC' ; fragment UnicodeCombiningMark // Any character in the Unicode categories "Non-spacing mark (Mn)" : '\u0300'..'\u034E' // or "Combining spacing mark (Mc)". | '\u0360'..'\u0362' | '\u0483'..'\u0486' | '\u0591'..'\u05A1' | '\u05A3'..'\u05B9' | '\u05BB'..'\u05BD' | '\u05BF' | '\u05C1'..'\u05C2' | '\u05C4' | '\u064B'..'\u0655' | '\u0670' | '\u06D6'..'\u06DC' | '\u06DF'..'\u06E4' | '\u06E7'..'\u06E8' | '\u06EA'..'\u06ED' | '\u0711' | '\u0730'..'\u074A' | '\u07A6'..'\u07B0' | '\u0901'..'\u0903' | '\u093C' | '\u093E'..'\u094D' | '\u0951'..'\u0954' | '\u0962'..'\u0963' | '\u0981'..'\u0983' | '\u09BC'..'\u09C4' | '\u09C7'..'\u09C8' | '\u09CB'..'\u09CD' | '\u09D7' | '\u09E2'..'\u09E3' | '\u0A02' | '\u0A3C' | '\u0A3E'..'\u0A42' | '\u0A47'..'\u0A48' | '\u0A4B'..'\u0A4D' | '\u0A70'..'\u0A71' | '\u0A81'..'\u0A83' | '\u0ABC' | '\u0ABE'..'\u0AC5' | '\u0AC7'..'\u0AC9' | '\u0ACB'..'\u0ACD' | '\u0B01'..'\u0B03' | '\u0B3C' | '\u0B3E'..'\u0B43' | '\u0B47'..'\u0B48' | '\u0B4B'..'\u0B4D' | '\u0B56'..'\u0B57' | '\u0B82'..'\u0B83' | '\u0BBE'..'\u0BC2' | '\u0BC6'..'\u0BC8' | '\u0BCA'..'\u0BCD' | '\u0BD7' | '\u0C01'..'\u0C03' | '\u0C3E'..'\u0C44' | '\u0C46'..'\u0C48' | '\u0C4A'..'\u0C4D' | '\u0C55'..'\u0C56' | '\u0C82'..'\u0C83' | '\u0CBE'..'\u0CC4' | '\u0CC6'..'\u0CC8' | '\u0CCA'..'\u0CCD' | '\u0CD5'..'\u0CD6' | '\u0D02'..'\u0D03' | '\u0D3E'..'\u0D43' | '\u0D46'..'\u0D48' | '\u0D4A'..'\u0D4D' | '\u0D57' | '\u0D82'..'\u0D83' | '\u0DCA' | '\u0DCF'..'\u0DD4' | '\u0DD6' | '\u0DD8'..'\u0DDF' | '\u0DF2'..'\u0DF3' | '\u0E31' | '\u0E34'..'\u0E3A' | '\u0E47'..'\u0E4E' | '\u0EB1' | '\u0EB4'..'\u0EB9' | '\u0EBB'..'\u0EBC' | '\u0EC8'..'\u0ECD' | '\u0F18'..'\u0F19' | '\u0F35' | '\u0F37' | '\u0F39' | '\u0F3E'..'\u0F3F' | '\u0F71'..'\u0F84' | '\u0F86'..'\u0F87' | '\u0F90'..'\u0F97' | '\u0F99'..'\u0FBC' | '\u0FC6' | '\u102C'..'\u1032' | '\u1036'..'\u1039' | '\u1056'..'\u1059' | '\u17B4'..'\u17D3' | '\u18A9' | '\u20D0'..'\u20DC' | '\u20E1' | '\u302A'..'\u302F' | '\u3099'..'\u309A' | '\uFB1E' | '\uFE20'..'\uFE23' ; fragment UnicodeDigit // Any character in the Unicode category "Decimal number (Nd)". : '\u0030'..'\u0039' | '\u0660'..'\u0669' | '\u06F0'..'\u06F9' | '\u0966'..'\u096F' | '\u09E6'..'\u09EF' | '\u0A66'..'\u0A6F' | '\u0AE6'..'\u0AEF' | '\u0B66'..'\u0B6F' | '\u0BE7'..'\u0BEF' | '\u0C66'..'\u0C6F' | '\u0CE6'..'\u0CEF' | '\u0D66'..'\u0D6F' | '\u0E50'..'\u0E59' | '\u0ED0'..'\u0ED9' | '\u0F20'..'\u0F29' | '\u1040'..'\u1049' | '\u1369'..'\u1371' | '\u17E0'..'\u17E9' | '\u1810'..'\u1819' | '\uFF10'..'\uFF19' ; fragment UnicodeConnectorPunctuation // Any character in the Unicode category "Connector punctuation (Pc)". : '\u005F' | '\u203F'..'\u2040' | '\u30FB' | '\uFE33'..'\uFE34' | '\uFE4D'..'\uFE4F' | '\uFF3F' | '\uFF65' ; Comment : '/*' (options {greedy=false;} : .)* '*/' {$channel=HIDDEN;} ; LineComment : '//' ~(LT)* {$channel=HIDDEN;} ; LT : '\n' // Line feed. | '\r' // Carriage return. | '\u2028' // Line separator. | '\u2029' // Paragraph separator. ; WhiteSpace // Tab, vertical tab, form feed, space, non-breaking space and any other unicode "space separator". : ('\t' | '\v' | '\f' | ' ' | '\u00A0') {$channel=HIDDEN;} ; pyparsing2-2.4.7/examples/jsonParser.py000066400000000000000000000063741365333160500201570ustar00rootroot00000000000000# jsonParser.py # # Implementation of a simple JSON parser, returning a hierarchical # ParseResults object support both list- and dict-style data access. # # Copyright 2006, by Paul McGuire # # Updated 8 Jan 2007 - fixed dict grouping bug, and made elements and # members optional in array and object collections # # Updated 9 Aug 2016 - use more current pyparsing constructs/idioms # json_bnf = """ object { members } {} members string : value members , string : value array [ elements ] [] elements value elements , value value string number object array true false null """ import pyparsing as pp from pyparsing import pyparsing_common as ppc def make_keyword(kwd_str, kwd_value): return pp.Keyword(kwd_str).setParseAction(pp.replaceWith(kwd_value)) TRUE = make_keyword("true", True) FALSE = make_keyword("false", False) NULL = make_keyword("null", None) LBRACK, RBRACK, LBRACE, RBRACE, COLON = map(pp.Suppress, "[]{}:") jsonString = pp.dblQuotedString().setParseAction(pp.removeQuotes) jsonNumber = ppc.number() jsonObject = pp.Forward() jsonValue = pp.Forward() jsonElements = pp.delimitedList( jsonValue ) jsonArray = pp.Group(LBRACK + pp.Optional(jsonElements, []) + RBRACK) jsonValue << (jsonString | jsonNumber | pp.Group(jsonObject) | jsonArray | TRUE | FALSE | NULL) memberDef = pp.Group(jsonString + COLON + jsonValue) jsonMembers = pp.delimitedList(memberDef) jsonObject << pp.Dict(LBRACE + pp.Optional(jsonMembers) + RBRACE) jsonComment = pp.cppStyleComment jsonObject.ignore(jsonComment) if __name__ == "__main__": testdata = """ { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "TrueValue": true, "FalseValue": false, "Gravity": -9.8, "LargestPrimeLessThan100": 97, "AvogadroNumber": 6.02E23, "EvenPrimesGreaterThan2": null, "PrimesLessThan10" : [2,3,5,7], "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", "markup"], "EmptyDict" : {}, "EmptyList" : [] } } } } """ results = jsonObject.parseString(testdata) results.pprint() print() def testPrint(x): print(type(x), repr(x)) print(list(results.glossary.GlossDiv.GlossList.keys())) testPrint( results.glossary.title ) testPrint( results.glossary.GlossDiv.GlossList.ID ) testPrint( results.glossary.GlossDiv.GlossList.FalseValue ) testPrint( results.glossary.GlossDiv.GlossList.Acronym ) testPrint( results.glossary.GlossDiv.GlossList.EvenPrimesGreaterThan2 ) testPrint( results.glossary.GlossDiv.GlossList.PrimesLessThan10 ) pyparsing2-2.4.7/examples/linenoExample.py000066400000000000000000000027521365333160500206250ustar00rootroot00000000000000# # linenoExample.py # # an example of using the location value returned by pyparsing to # extract the line and column number of the location of the matched text, # or to extract the entire line of text. # # Copyright (c) 2006, Paul McGuire # from pyparsing import * data = """Now is the time for all good men to come to the aid of their country.""" # demonstrate use of lineno, line, and col in a parse action def reportLongWords(st,locn,toks): word = toks[0] if len(word) > 3: print("Found '%s' on line %d at column %d" % (word, lineno(locn,st), col(locn,st))) print("The full line of text was:") print("'%s'" % line(locn,st)) print((" "*col(locn,st))+" ^") print() wd = Word(alphas).setParseAction( reportLongWords ) OneOrMore(wd).parseString(data) # demonstrate returning an object from a parse action, containing more information # than just the matching token text class Token(object): def __init__(self, st, locn, tokString): self.tokenString = tokString self.locn = locn self.sourceLine = line(locn,st) self.lineNo = lineno(locn,st) self.col = col(locn,st) def __str__(self): return "%(tokenString)s (line: %(lineNo)d, col: %(col)d)" % self.__dict__ def createTokenObject(st,locn,toks): return Token(st,locn, toks[0]) wd = Word(alphas).setParseAction( createTokenObject ) for tokenObj in OneOrMore(wd).parseString(data): print(tokenObj) pyparsing2-2.4.7/examples/list1.py000066400000000000000000000032021365333160500170500ustar00rootroot00000000000000# # list1.py # # an example of using parse actions to convert type of parsed data. # # Copyright (c) 2006-2016, Paul McGuire # from pyparsing import * # first pass lbrack = Literal("[") rbrack = Literal("]") integer = Word(nums).setName("integer") real = Combine(Optional(oneOf("+ -")) + Word(nums) + "." + Optional(Word(nums))).setName("real") listItem = real | integer | quotedString listStr = lbrack + delimitedList(listItem) + rbrack test = "['a', 100, 3.14]" print(listStr.parseString(test)) # second pass, cleanup and add converters lbrack = Literal("[").suppress() rbrack = Literal("]").suppress() cvtInt = lambda s,l,toks: int(toks[0]) cvtReal = lambda s,l,toks: float(toks[0]) integer = Word(nums).setName("integer").setParseAction( cvtInt ) real = Combine(Optional(oneOf("+ -")) + Word(nums) + "." + Optional(Word(nums))).setName("real").setParseAction( cvtReal ) listItem = real | integer | quotedString.setParseAction( removeQuotes ) listStr = lbrack + delimitedList(listItem) + rbrack test = "['a', 100, 3.14]" print(listStr.parseString(test)) # third pass, add nested list support lbrack, rbrack = map(Suppress, "[]") cvtInt = tokenMap(int) cvtReal = tokenMap(float) integer = Word(nums).setName("integer").setParseAction( cvtInt ) real = Regex(r"[+-]?\d+\.\d*").setName("real").setParseAction( cvtReal ) listStr = Forward() listItem = real | integer | quotedString.setParseAction(removeQuotes) | Group(listStr) listStr << lbrack + delimitedList(listItem) + rbrack test = "['a', 100, 3.14, [ +2.718, 'xyzzy', -1.414] ]" print(listStr.parseString(test))pyparsing2-2.4.7/examples/listAllMatches.py000066400000000000000000000031161365333160500207310ustar00rootroot00000000000000# listAllMatches.py # # Sample program showing how/when to use listAllMatches to get all matching tokens in a results name. # # copyright 2006, Paul McGuire # from pyparsing import oneOf, OneOrMore, printables, StringEnd test = "The quick brown fox named 'Aloysius' lives at 123 Main Street (and jumps over lazy dogs in his spare time)." nonAlphas = [ c for c in printables if not c.isalpha() ] print("Extract vowels, consonants, and special characters from this test string:") print("'" + test + "'") print('') print("Define grammar using normal results names") print("(only last matching symbol is saved)") vowels = oneOf(list("aeiouy"), caseless=True)("vowels") cons = oneOf(list("bcdfghjklmnpqrstvwxz"), caseless=True)("cons") other = oneOf(nonAlphas)("others") letters = OneOrMore(cons | vowels | other) + StringEnd() results = letters.parseString(test) print(results) print(results.vowels) print(results.cons) print(results.others) print('') print("Define grammar using results names, with listAllMatches=True") print("(all matching symbols are saved)") vowels = oneOf(list("aeiouy"), caseless=True)("vowels*") cons = oneOf(list("bcdfghjklmnpqrstvwxz"), caseless=True)("cons*") other = oneOf(nonAlphas)("others*") letters = OneOrMore(cons | vowels | other) results = letters.parseString(test, parseAll=True) print(results) print(sorted(set(results))) print('') print(results.vowels) print(sorted(set(results.vowels))) print('') print(results.cons) print(sorted(set(results.cons))) print('') print(results.others) print(sorted(set(results.others))) pyparsing2-2.4.7/examples/lucene_grammar.py000066400000000000000000000173471365333160500210140ustar00rootroot00000000000000# # lucene_grammar.py # # Copyright 2011, Paul McGuire # # implementation of Lucene grammar, as decribed # at http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/docs/queryparsersyntax.html # import pyparsing as pp from pyparsing import pyparsing_common as ppc pp.ParserElement.enablePackrat() COLON,LBRACK,RBRACK,LBRACE,RBRACE,TILDE,CARAT = map(pp.Literal,":[]{}~^") LPAR,RPAR = map(pp.Suppress,"()") and_, or_, not_, to_ = map(pp.CaselessKeyword, "AND OR NOT TO".split()) keyword = and_ | or_ | not_ | to_ expression = pp.Forward() valid_word = pp.Regex(r'([a-zA-Z0-9*_+.-]|\\\\|\\([+\-!(){}\[\]^"~*?:]|\|\||&&))+').setName("word") valid_word.setParseAction( lambda t : t[0].replace('\\\\',chr(127)).replace('\\','').replace(chr(127),'\\') ) string = pp.QuotedString('"') required_modifier = pp.Literal("+")("required") prohibit_modifier = pp.Literal("-")("prohibit") integer = ppc.integer() proximity_modifier = pp.Group(TILDE + integer("proximity")) number = ppc.fnumber() fuzzy_modifier = TILDE + pp.Optional(number, default=0.5)("fuzzy") term = pp.Forward() field_name = valid_word().setName("fieldname") incl_range_search = pp.Group(LBRACK - term("lower") + to_ + term("upper") + RBRACK) excl_range_search = pp.Group(LBRACE - term("lower") + to_ + term("upper") + RBRACE) range_search = incl_range_search("incl_range") | excl_range_search("excl_range") boost = (CARAT - number("boost")) string_expr = pp.Group(string + proximity_modifier) | string word_expr = pp.Group(valid_word + fuzzy_modifier) | valid_word term << (pp.Optional(field_name("field") + COLON) + (word_expr | string_expr | range_search | pp.Group(LPAR + expression + RPAR)) + pp.Optional(boost)) term.setParseAction(lambda t:[t] if 'field' in t or 'boost' in t else None) expression << pp.infixNotation(term, [ (required_modifier | prohibit_modifier, 1, pp.opAssoc.RIGHT), ((not_ | '!').setParseAction(lambda: "NOT"), 1, pp.opAssoc.RIGHT), ((and_ | '&&').setParseAction(lambda: "AND"), 2, pp.opAssoc.LEFT), (pp.Optional(or_ | '||').setParseAction(lambda: "OR"), 2, pp.opAssoc.LEFT), ]) if __name__ == '__main__': # test strings taken from grammar description doc, and TestQueryParser.java tests = r""" # Success tests a and b a and not b a and !b a && !b a&&!b name:a name:a and not title:b (a^100 c d f) and !z name:"blah de blah" title:(+return +"pink panther") title:"The Right Way" AND text:go title:"Do it right" AND right title:Do it right roam~ roam~0.8 "jakarta apache"~10 mod_date:[20020101 TO 20030101] title:{Aida TO Carmen} jakarta apache jakarta^4 apache "jakarta apache"^4 "Apache Lucene" "jakarta apache" jakarta "jakarta apache" OR jakarta "jakarta apache" AND "Apache Lucene" +jakarta lucene "jakarta apache" NOT "Apache Lucene" "jakarta apache" -"Apache Lucene" (jakarta OR apache) AND website \(1+1\)\:2 c\:\\windows (fieldX:xxxxx OR fieldy:xxxxxxxx)^2 AND (fieldx:the OR fieldy:foo) (fieldX:xxxxx fieldy:xxxxxxxx)^2 AND (fieldx:the fieldy:foo) (fieldX:xxxxx~0.5 fieldy:xxxxxxxx)^2 AND (fieldx:the fieldy:foo) +term -term term foo:term AND field:anotherTerm germ term^2.0 (term)^2.0 (foo OR bar) AND (baz OR boo) +(apple \"steve jobs\") -(foo bar baz) +title:(dog OR cat) -author:\"bob dole\" a AND b +a +b (a AND b) c OR (a AND b) c (+a +b) a AND NOT b +a -b a AND -b a AND !b a && b a && ! b a OR b a b a || b a OR !b a -b a OR ! b a OR -b a - b a + b a ! b +foo:term +anotherterm hello term^2.0 (germ term)^2.0 term^2 +(foo bar) +(baz boo) ((a OR b) AND NOT c) OR d (+(a b) -c) d field a&&b .NET term germ 3 term 1.0 1 2 term term1 term2 term term term term* term*^2 term*^2.0 term~ term~2.0 term~0.7 term~^3 term~2.0^3.0 term*germ term*germ^3 term*germ^3.0 term~1.1 [A TO C] t*erm* *term* term term^3.0 term term stop^3.0 term term +stop term term -stop term drop AND (stop) AND roll +drop +roll term +(stop) term term -(stop) term drop AND stop AND roll term phrase term term (phrase1 phrase2) term term AND NOT phrase term +term -(phrase1 phrase2) term stop^3 stop (stop)^3 ((stop))^3 (stop^3) ((stop)^3) (stop) ((stop)) term +stop [ a TO z] [a TO z] [ a TO z ] { a TO z} {a TO z} { a TO z } { a TO z }^2.0 {a TO z}^2.0 [ a TO z] OR bar [a TO z] bar [ a TO z] AND bar +[a TO z] +bar ( bar blar { a TO z}) bar blar {a TO z} gack ( bar blar { a TO z}) gack (bar blar {a TO z}) [* TO Z] [* TO z] [A TO *] [a TO *] [* TO *] [\* TO \*] \!blah \:blah blah \~blah \*blah a a-b:c a+b:c a\:b:c a\\b:c a:b-c a:b+c a:b\:c a:b\\c a:b-c* a:b+c* a:b\:c* a:b\\c* a:b-c~2.0 a:b+c~2.0 a:b\:c~ a:b\\c~ [a- TO a+] [ a\\ TO a\* ] c\:\\temp\\\~foo.txt abc XYZ (item:\\ item:ABCD\\) \* * \\ \|| \&& a\:b\:c a\\b\:c a\:b\\c a\:b\:c\* a\:b\\\\c\* a:b-c~ a:b+c~ a\:b\:c\~ a\:b\\c\~ +weltbank +worlbank +term +term +term term +term term term term +term term +term +term -term term term -term +term +term on on^1.0 hello^2.0 the^3 the some phrase xunit~ one two three A AND B OR C AND D +A +B +C +D foo:zoo* foo:zoo*^2 zoo foo:* foo:*^2 *:foo a:the OR a:foo a:woo OR a:the *:* (*:*) +*:* -*:* the wizard of ozzy """ failtests = r""" # Failure tests # multiple ':'s in term field:term:with:colon some more terms # multiple '^'s in term (sub query)^5.0^2.0 plus more a:b:c a:b:c~ a:b:c* a:b:c~2.0 \+blah \-blah foo \|| bar foo \AND bar \a a\-b:c a\+b:c a\b:c a:b\-c a:b\+c a\-b\:c a\+b\:c a:b\c* a:b\-c~ a:b\+c~ a:b\c a:b\-c* a:b\+c* [ a\- TO a\+ ] [a\ TO a*] a\\\+b a\+b c:\temp\~foo.txt XY\ a\u0062c a:b\c~2.0 XY\u005a XY\u005A item:\ item:ABCD\ \ a\ or b a\:b\-c a\:b\+c a\:b\-c\* a\:b\+c\* a\:b\-c\~ a\:b\+c\~ a:b\c~ [ a\ TO a* ] """ success1, _ = expression.runTests(tests) success2, _ = expression.runTests(failtests, failureTests=True) print("All tests:", ("FAIL", "OK")[success1 and success2]) if not (success1 and success2): import sys sys.exit(1) pyparsing2-2.4.7/examples/macroExpander.py000066400000000000000000000034251365333160500206130ustar00rootroot00000000000000# macroExpander.py # # Example pyparsing program for performing macro expansion, similar to # the C pre-processor. This program is not as fully-featured, simply # processing macros of the form: # #def xxx yyyyy # and replacing xxx with yyyyy in the rest of the input string. Macros # can also be composed using other macros, such as # #def zzz xxx+1 # Since xxx was previously defined as yyyyy, then zzz will be replaced # with yyyyy+1. # # Copyright 2007 by Paul McGuire # from pyparsing import * # define the structure of a macro definition (the empty term is used # to advance to the next non-whitespace character) identifier = Word(alphas+"_",alphanums+"_") macroDef = "#def" + identifier("macro") + empty + restOfLine("value") # define a placeholder for defined macros - initially nothing macroExpr = Forward() macroExpr << NoMatch() # global dictionary for macro definitions macros = {} # parse action for macro definitions def processMacroDefn(s,l,t): macroVal = macroExpander.transformString(t.value) macros[t.macro] = macroVal macroExpr << MatchFirst(map(Keyword, macros.keys())) return "#def " + t.macro + " " + macroVal # parse action to replace macro references with their respective definition def processMacroRef(s,l,t): return macros[t[0]] # attach parse actions to expressions macroExpr.setParseAction(processMacroRef) macroDef.setParseAction(processMacroDefn) # define pattern for scanning through the input string macroExpander = macroExpr | macroDef # test macro substitution using transformString testString = """ #def A 100 #def ALEN A+1 char Astring[ALEN]; char AA[A]; typedef char[ALEN] Acharbuf; """ print(macroExpander.transformString(testString)) print(macros) pyparsing2-2.4.7/examples/matchPreviousDemo.py000066400000000000000000000011121365333160500214500ustar00rootroot00000000000000# # matchPreviousDemo.py # from pyparsing import * src = """ class a ... end a; class b ... end b; class c ... end d;""" identifier = Word(alphas) classIdent = identifier("classname") # note that this also makes a copy of identifier classHead = "class" + classIdent classBody = "..." classEnd = "end" + matchPreviousLiteral(classIdent) + ';' classDefn = classHead + classBody + classEnd # use this form to catch syntax error # classDefn = classHead + classBody - classEnd for tokens in classDefn.searchString(src): print(tokens.classname) pyparsing2-2.4.7/examples/mozilla.ics000066400000000000000000000013601365333160500176140ustar00rootroot00000000000000BEGIN:VCALENDAR VERSION :2.0 PRODID :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN METHOD :PUBLISH BEGIN:VEVENT UID :153ed0e0-1dd2-11b2-9d71-96da104537a4 SUMMARY :Test event DESCRIPTION :Some notes LOCATION :Somewhere over the rainbow CATEGORIES :Personal URL :http://personal.amec.fi STATUS :CONFIRMED CLASS :PRIVATE X ;MEMBER=AlarmEmailAddress :petri.savolainen@iki.fi X-MOZILLA-RECUR-DEFAULT-UNITS :months RRULE :FREQ=MONTHLY;UNTIL=20040914;INTERVAL=1 EXDATE :20040714T000000 DTSTART ;VALUE=DATE :20040714 DTEND ;VALUE=DATE :20040815 DTSTAMP :20040714T141219Z LAST-MODIFIED :20040714T141457Z BEGIN:VALARM TRIGGER ;VALUE=DURATION :PT15M END:VALARM END:VEVENT END:VCALENDAR pyparsing2-2.4.7/examples/mozillaCalendarParser.py000066400000000000000000000050751365333160500223040ustar00rootroot00000000000000from pyparsing import Optional, oneOf, Literal, Word, printables, Group, OneOrMore, ZeroOrMore """ A simple parser for calendar (*.ics) files, as exported by the Mozilla calendar. Any suggestions and comments welcome. Version: 0.1 Copyright: Petri Savolainen License: Free for any use """ # TERMINALS BEGIN = Literal("BEGIN:").suppress() END = Literal("END:").suppress() valstr = printables + "\xe4\xf6\xe5\xd6\xc4\xc5 " EQ = Literal("=").suppress() SEMI = Literal(";").suppress() COLON = Literal(":").suppress() EVENT = Literal("VEVENT").suppress() CALENDAR = Literal("VCALENDAR").suppress() ALARM = Literal("VALARM").suppress() # TOKENS CALPROP = oneOf("VERSION PRODID METHOD") ALMPROP = oneOf("TRIGGER") EVTPROP = oneOf("X-MOZILLA-RECUR-DEFAULT-INTERVAL \ X-MOZILLA-RECUR-DEFAULT-UNITS \ UID DTSTAMP LAST-MODIFIED X RRULE EXDATE") propval = Word(valstr) typeval = Word(valstr) typename = oneOf("VALUE MEMBER FREQ UNTIL INTERVAL") proptype = Group(SEMI + typename + EQ + typeval).suppress() calprop = Group(CALPROP + ZeroOrMore(proptype) + COLON + propval) almprop = Group(ALMPROP + ZeroOrMore(proptype) + COLON + propval) evtprop = Group(EVTPROP + ZeroOrMore(proptype) + COLON + propval).suppress() \ | "CATEGORIES" + COLON + propval.setResultsName("categories") \ | "CLASS" + COLON + propval.setResultsName("class") \ | "DESCRIPTION" + COLON + propval.setResultsName("description") \ | "DTSTART" + proptype + COLON + propval.setResultsName("begin") \ | "DTEND" + proptype + COLON + propval.setResultsName("end") \ | "LOCATION" + COLON + propval.setResultsName("location") \ | "PRIORITY" + COLON + propval.setResultsName("priority") \ | "STATUS" + COLON + propval.setResultsName("status") \ | "SUMMARY" + COLON + propval.setResultsName("summary") \ | "URL" + COLON + propval.setResultsName("url") \ calprops = Group(OneOrMore(calprop)).suppress() evtprops = Group(OneOrMore(evtprop)) almprops = Group(OneOrMore(almprop)).suppress() alarm = BEGIN + ALARM + almprops + END + ALARM event = BEGIN + EVENT + evtprops + Optional(alarm) + END + EVENT events = Group(OneOrMore(event)) calendar = BEGIN + CALENDAR + calprops + ZeroOrMore(event) + END + CALENDAR calendars = OneOrMore(calendar) # PARSE ACTIONS def gotEvent(s,loc,toks): for event in toks: print(event.dump()) event.setParseAction(gotEvent) # MAIN PROGRAM if __name__=="__main__": calendars.parseFile("mozilla.ics") pyparsing2-2.4.7/examples/nested.py000066400000000000000000000011121365333160500172740ustar00rootroot00000000000000# # nested.py # Copyright, 2007 - Paul McGuire # # Simple example of using nestedExpr to define expressions using # paired delimiters for grouping lists and sublists # from pyparsing import * data = """ { { item1 "item with } in it" } { {item2a item2b } {item3} } } """ # use {}'s for nested lists nestedItems = nestedExpr("{", "}") print(( (nestedItems+stringEnd).parseString(data).asList() )) # use default delimiters of ()'s mathExpr = nestedExpr() print(( mathExpr.parseString( "((( ax + by)*C) *(Z | (E^F) & D))") )) pyparsing2-2.4.7/examples/nested_markup.py000066400000000000000000000035241365333160500206640ustar00rootroot00000000000000# # nested_markup.py # # Example markup parser to recursively transform nested markup directives. # # Copyright 2019, Paul McGuire # import pyparsing as pp wiki_markup = pp.Forward() # a method that will construct and return a parse action that will # do the proper wrapping in opening and closing HTML, and recursively call # wiki_markup.transformString on the markup body text def convert_markup_to_html(opening,closing): def conversionParseAction(s, l, t): return opening + wiki_markup.transformString(t[1][1:-1]) + closing return conversionParseAction # use a nestedExpr with originalTextFor to parse nested braces, but return the # parsed text as a single string containing the outermost nested braces instead # of a nested list of parsed tokens markup_body = pp.originalTextFor(pp.nestedExpr('{', '}')) italicized = ('ital' + markup_body).setParseAction(convert_markup_to_html("", "")) bolded = ('bold' + markup_body).setParseAction(convert_markup_to_html("", "")) # another markup and parse action to parse links - again using transform string # to recursively parse any markup in the link text def convert_link_to_html(s, l, t): link_text, url = t._skipped t['link_text'] = wiki_markup.transformString(link_text) t['url'] = url return '{link_text}'.format_map(t) urlRef = (pp.Keyword('link') + '{' + ... + '->' + ... + '}').setParseAction(convert_link_to_html) # now inject all the markup bits as possible markup expressions wiki_markup <<= urlRef | italicized | bolded # try it out! wiki_input = """ Here is a simple Wiki input: ital{This is in italics}. bold{This is in bold}! bold{This is in ital{bold italics}! But this is just bold.} Here's a URL to link{Pyparsing's bold{Wiki Page}!->https://github.com/pyparsing/pyparsing/wiki} """ print(wiki_markup.transformString(wiki_input)) pyparsing2-2.4.7/examples/numerics.py000066400000000000000000000024741365333160500176530ustar00rootroot00000000000000# # numerics.py # # Examples of parsing real and integers using various grouping and # decimal point characters, varying by locale. # # Copyright 2016, Paul McGuire # # Format samples from https://docs.oracle.com/cd/E19455-01/806-0169/overview-9/index.html # tests = """\ # Canadian (English and French) 4 294 967 295,000 # Danish 4 294 967 295,000 # Finnish 4 294 967 295,000 # French 4 294 967 295,000 # German 4 294 967 295,000 # Italian 4.294.967.295,000 # Norwegian 4.294.967.295,000 # Spanish 4.294.967.295,000 # Swedish 4 294 967 295,000 # GB-English 4,294,967,295.000 # US-English 4,294,967,295.000 # Thai 4,294,967,295.000 """ from pyparsing import Regex comma_decimal = Regex(r'\d{1,2}(([ .])\d\d\d(\2\d\d\d)*)?,\d*') comma_decimal.setParseAction(lambda t: float(t[0].replace(' ','').replace('.','').replace(',','.'))) dot_decimal = Regex(r'\d{1,2}(([ ,])\d\d\d(\2\d\d\d)*)?\.\d*') dot_decimal.setParseAction(lambda t: float(t[0].replace(' ','').replace(',',''))) decimal = comma_decimal ^ dot_decimal decimal.runTests(tests, parseAll=True) grouped_integer = Regex(r'\d{1,2}(([ .,])\d\d\d(\2\d\d\d)*)?') grouped_integer.setParseAction(lambda t: int(t[0].replace(' ','').replace(',','').replace('.',''))) grouped_integer.runTests(tests, parseAll=False) pyparsing2-2.4.7/examples/oc.py000066400000000000000000000101061365333160500164160ustar00rootroot00000000000000# oc.py # # A subset-C parser, (BNF taken from 1996 International Obfuscated C Code Contest) # # Copyright, 2010, Paul McGuire # """ https://www.ioccc.org/1996/august.hint The following is a description of the OC grammar: OC grammar ========== Terminals are in quotes, () is used for bracketing. program: decl* decl: vardecl fundecl vardecl: type NAME ; type NAME "[" INT "]" ; fundecl: type NAME "(" args ")" "{" body "}" args: /*empty*/ ( arg "," )* arg arg: type NAME body: vardecl* stmt* stmt: ifstmt whilestmt dowhilestmt "return" expr ";" expr ";" "{" stmt* "}" ";" ifstmt: "if" "(" expr ")" stmt "if" "(" expr ")" stmt "else" stmt whilestmt: "while" "(" expr ")" stmt dowhilestmt: "do" stmt "while" "(" expr ")" ";" expr: expr binop expr unop expr expr "[" expr "]" "(" expr ")" expr "(" exprs ")" NAME INT CHAR STRING exprs: /*empty*/ (expr ",")* expr binop: "+" | "-" | "*" | "/" | "%" | "=" | "<" | "==" | "!=" unop: "!" | "-" | "*" type: "int" stars "char" stars stars: "*"* """ from pyparsing import * ParserElement.enablePackrat() LPAR,RPAR,LBRACK,RBRACK,LBRACE,RBRACE,SEMI,COMMA = map(Suppress, "()[]{};,") INT, CHAR, WHILE, DO, IF, ELSE, RETURN = map(Keyword, "int char while do if else return".split()) NAME = Word(alphas+"_", alphanums+"_") integer = Regex(r"[+-]?\d+") char = Regex(r"'.'") string_ = dblQuotedString TYPE = Group((INT | CHAR) + ZeroOrMore("*")) expr = Forward() func_call = Group(NAME + LPAR + Group(Optional(delimitedList(expr))) + RPAR) operand = func_call | NAME | integer | char | string_ expr <<= (infixNotation(operand, [ (oneOf('! - *'), 1, opAssoc.RIGHT), (oneOf('++ --'), 1, opAssoc.RIGHT), (oneOf('++ --'), 1, opAssoc.LEFT), (oneOf('* / %'), 2, opAssoc.LEFT), (oneOf('+ -'), 2, opAssoc.LEFT), (oneOf('< == > <= >= !='), 2, opAssoc.LEFT), (Regex(r'(?NC_001799-6-2978-2778 | organism=Toxoplasma_gondii_RH | location=NC_001799:2778-2978(-) | length=201 """ integer = Word(nums).setParseAction(lambda t:int(t[0])) genebit = Group(">" + Word(alphanums.upper()+"-_") + "|" + Word(printables)("id") + SkipTo("length=", include=True) + integer("genelen") + LineEnd() + Combine(OneOrMore(Word("ACGTN")),adjacent=False)("gene")) # read gene data from .fasta file - takes just a few seconds genedata = OneOrMore(genebit).parseString(fastasrc) class CloseMatch(Token): """A special subclass of Token that does *close* matches. For each close match of the given string, a tuple is returned giving the found close match, and a list of mismatch positions.""" def __init__(self, seq, maxMismatches=1): super(CloseMatch,self).__init__() self.name = seq self.sequence = seq self.maxMismatches = maxMismatches self.errmsg = "Expected " + self.sequence self.mayIndexError = False self.mayReturnEmpty = False def parseImpl( self, instring, loc, doActions=True ): start = loc instrlen = len(instring) maxloc = start + len(self.sequence) if maxloc <= instrlen: seq = self.sequence seqloc = 0 mismatches = [] throwException = False done = False while loc < maxloc and not done: if instring[loc] != seq[seqloc]: mismatches.append(seqloc) if len(mismatches) > self.maxMismatches: throwException = True done = True loc += 1 seqloc += 1 else: throwException = True if throwException: exc = self.myException exc.loc = loc exc.pstr = instring raise exc return loc, (instring[start:loc],mismatches) # using the genedata extracted above, look for close matches of a gene sequence searchseq = CloseMatch("TTAAATCTAGAAGAT", 3) for g in genedata: print("%s (%d)" % (g.id, g.genelen)) print("-"*24) for t,startLoc,endLoc in searchseq.scanString(g.gene, overlap=True): matched, mismatches = t[0] print("MATCH:", searchseq.sequence) print("FOUND:", matched) if mismatches: print(" ", ''.join(' ' if i not in mismatches else '*' for i,c in enumerate(searchseq.sequence))) else: print("") print("at location", startLoc) print() print() pyparsing2-2.4.7/examples/pgn.py000066400000000000000000000065451365333160500166150ustar00rootroot00000000000000# pgn.py rel. 1.1 17-sep-2004 # # Demonstration of the parsing module, implementing a pgn parser. # # The aim of this parser is not to support database application, # but to create automagically a pgn annotated reading the log console file # of a lecture of ICC (Internet Chess Club), saved by Blitzin. # Of course you can modify the Abstract Syntax Tree to your purpose. # # Copyright 2004, by Alberto Santini http://www.albertosantini.it/chess/ # from pyparsing import alphanums, nums, quotedString from pyparsing import Combine, Forward, Group, Literal, oneOf, OneOrMore, Optional, Suppress, ZeroOrMore, Word from pyparsing import ParseException # # define pgn grammar # tag = Suppress("[") + Word(alphanums) + Combine(quotedString) + Suppress("]") comment = Suppress("{") + Word(alphanums + " ") + Suppress("}") dot = Literal(".") piece = oneOf("K Q B N R") file_coord = oneOf("a b c d e f g h") rank_coord = oneOf("1 2 3 4 5 6 7 8") capture = oneOf("x :") promote = Literal("=") castle_queenside = oneOf("O-O-O 0-0-0 o-o-o") castle_kingside = oneOf("O-O 0-0 o-o") move_number = Optional(comment) + Word(nums) + dot m1 = file_coord + rank_coord # pawn move e.g. d4 m2 = file_coord + capture + file_coord + rank_coord # pawn capture move e.g. dxe5 m3 = file_coord + "8" + promote + piece # pawn promotion e.g. e8=Q m4 = piece + file_coord + rank_coord # piece move e.g. Be6 m5 = piece + file_coord + file_coord + rank_coord # piece move e.g. Nbd2 m6 = piece + rank_coord + file_coord + rank_coord # piece move e.g. R4a7 m7 = piece + capture + file_coord + rank_coord # piece capture move e.g. Bxh7 m8 = castle_queenside | castle_kingside # castling e.g. o-o check = oneOf("+ ++") mate = Literal("#") annotation = Word("!?", max=2) nag = " $" + Word(nums) decoration = check | mate | annotation | nag variant = Forward() half_move = Combine((m3 | m1 | m2 | m4 | m5 | m6 | m7 | m8) + Optional(decoration)) \ + Optional(comment) +Optional(variant) move = Suppress(move_number) + half_move + Optional(half_move) variant << "(" + OneOrMore(move) + ")" # grouping the plies (half-moves) for each move: useful to group annotations, variants... # suggested by Paul McGuire :) move = Group(Suppress(move_number) + half_move + Optional(half_move)) variant << Group("(" + OneOrMore(move) + ")") game_terminator = oneOf("1-0 0-1 1/2-1/2 *") pgnGrammar = Suppress(ZeroOrMore(tag)) + ZeroOrMore(move) + Optional(Suppress(game_terminator)) def parsePGN( pgn, bnf=pgnGrammar, fn=None ): try: return bnf.parseString( pgn ) except ParseException as err: print(err.line) print(" "*(err.column-1) + "^") print(err) if __name__ == "__main__": # input string pgn = """ [Event "ICC 5 0 u"] [Site "Internet Chess Club"] [Date "2004.01.25"] [Round "-"] [White "guest920"] [Black "IceBox"] [Result "0-1"] [ICCResult "White checkmated"] [BlackElo "1498"] [Opening "French defense"] [ECO "C00"] [NIC "FR.01"] [Time "04:44:56"] [TimeControl "300+0"] 1. e4 e6 2. Nf3 d5 $2 3. exd5 (3. e5 g6 4. h4) exd5 4. Qe2+ Qe7 5. Qxe7+ Bxe7 6. d3 Nf6 7. Be3 Bg4 8. Nbd2 c5 9. h3 Be6 10. O-O-O Nc6 11. g4 Bd6 12. g5 Nd7 13. Rg1 d4 14. g6 fxg6 15. Bg5 Rf8 16. a3 Bd5 17. Re1+ Nde5 18. Nxe5 Nxe5 19. Bf4 Rf5 20. Bxe5 Rxe5 21. Rg5 Rxe1# {Black wins} 0-1 """ # parse input string tokens = parsePGN(pgn, pgnGrammar) print(tokens.dump()) pyparsing2-2.4.7/examples/position.py000066400000000000000000000042321365333160500176640ustar00rootroot00000000000000from pyparsing import * text = """Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum""" # find all words beginning with a vowel vowels = "aeiouAEIOU" initialVowelWord = Word(vowels,alphas) # Unfortunately, searchString will advance character by character through # the input text, so it will detect that the initial "Lorem" is not an # initialVowelWord, but then it will test "orem" and think that it is. So # we need to add a do-nothing term that will match the words that start with # consonants, but we will just throw them away when we match them. The key is # that, in having been matched, the parser will skip over them entirely when # looking for initialVowelWords. consonants = ''.join(c for c in alphas if c not in vowels) initialConsWord = Word(consonants, alphas).suppress() # using scanString to locate where tokens are matched for t,start,end in (initialConsWord|initialVowelWord).scanString(text): if t: print(start,':', t[0]) # add parse action to annotate the parsed tokens with their location in the # input string def addLocnToTokens(s,l,t): t['locn'] = l t['word'] = t[0] initialVowelWord.setParseAction(addLocnToTokens) for ivowelInfo in (initialConsWord | initialVowelWord).searchString(text): if not ivowelInfo: continue print(ivowelInfo.locn, ':', ivowelInfo.word) # alternative - add an Empty that will save the current location def location(name): return Empty().setParseAction(lambda s,l,t: t.__setitem__(name,l)) locateInitialVowels = location("locn") + initialVowelWord("word") # search through the input text for ivowelInfo in (initialConsWord | locateInitialVowels).searchString(text): if not ivowelInfo: continue print(ivowelInfo.locn, ':', ivowelInfo.word) pyparsing2-2.4.7/examples/protobuf_parser.py000066400000000000000000000064771365333160500212510ustar00rootroot00000000000000# protobuf_parser.py # # simple parser for parsing protobuf .proto files # # Copyright 2010, Paul McGuire # from pyparsing import (Word, alphas, alphanums, Regex, Suppress, Forward, Keyword, Group, oneOf, ZeroOrMore, Optional, delimitedList, restOfLine, quotedString, Dict) ident = Word(alphas+"_",alphanums+"_").setName("identifier") integer = Regex(r"[+-]?\d+") LBRACE,RBRACE,LBRACK,RBRACK,LPAR,RPAR,EQ,SEMI = map(Suppress,"{}[]()=;") kwds = """message required optional repeated enum extensions extends extend to package service rpc returns true false option import""" for kw in kwds.split(): exec("{0}_ = Keyword('{1}')".format(kw.upper(), kw)) messageBody = Forward() messageDefn = MESSAGE_ - ident("messageId") + LBRACE + messageBody("body") + RBRACE typespec = oneOf("""double float int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64 bool string bytes""") | ident rvalue = integer | TRUE_ | FALSE_ | ident fieldDirective = LBRACK + Group(ident + EQ + rvalue) + RBRACK fieldDefn = (( REQUIRED_ | OPTIONAL_ | REPEATED_ )("fieldQualifier") - typespec("typespec") + ident("ident") + EQ + integer("fieldint") + ZeroOrMore(fieldDirective) + SEMI) # enumDefn ::= 'enum' ident '{' { ident '=' integer ';' }* '}' enumDefn = ENUM_("typespec") - ident('name') + LBRACE + Dict( ZeroOrMore( Group(ident + EQ + integer + SEMI) ))('values') + RBRACE # extensionsDefn ::= 'extensions' integer 'to' integer ';' extensionsDefn = EXTENSIONS_ - integer + TO_ + integer + SEMI # messageExtension ::= 'extend' ident '{' messageBody '}' messageExtension = EXTEND_ - ident + LBRACE + messageBody + RBRACE # messageBody ::= { fieldDefn | enumDefn | messageDefn | extensionsDefn | messageExtension }* messageBody << Group(ZeroOrMore( Group(fieldDefn | enumDefn | messageDefn | extensionsDefn | messageExtension) )) # methodDefn ::= 'rpc' ident '(' [ ident ] ')' 'returns' '(' [ ident ] ')' ';' methodDefn = (RPC_ - ident("methodName") + LPAR + Optional(ident("methodParam")) + RPAR + RETURNS_ + LPAR + Optional(ident("methodReturn")) + RPAR) # serviceDefn ::= 'service' ident '{' methodDefn* '}' serviceDefn = SERVICE_ - ident("serviceName") + LBRACE + ZeroOrMore(Group(methodDefn)) + RBRACE # packageDirective ::= 'package' ident [ '.' ident]* ';' packageDirective = Group(PACKAGE_ - delimitedList(ident, '.', combine=True) + SEMI) comment = '//' + restOfLine importDirective = IMPORT_ - quotedString("importFileSpec") + SEMI optionDirective = OPTION_ - ident("optionName") + EQ + quotedString("optionValue") + SEMI topLevelStatement = Group(messageDefn | messageExtension | enumDefn | serviceDefn | importDirective | optionDirective) parser = Optional(packageDirective) + ZeroOrMore(topLevelStatement) parser.ignore(comment) test1 = """message Person { required int32 id = 1; required string name = 2; optional string email = 3; }""" test2 = """package tutorial; message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } message AddressBook { repeated Person person = 1; }""" parser.runTests([test1, test2]) pyparsing2-2.4.7/examples/pymicko.py000066400000000000000000001661371365333160500175100ustar00rootroot00000000000000#!/usr/bin/python # Python/pyparsing educational microC compiler v1.0 # Copyright (C) 2009 Zarko Zivanov # (largely based on flex/bison microC compiler by Zorica Suvajdzin, used with her permission; # current version can be found at http://www.acs.uns.ac.rs, under "Programski Prevodioci" [Serbian site]) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # A copy of the GNU General Public License can be found at . from pyparsing import * from sys import stdin, argv, exit #defines debug level # 0 - no debug # 1 - print parsing results # 2 - print parsing results and symbol table # 3 - print parsing results only, without executing parse actions (grammar-only testing) DEBUG = 0 ########################################################################################## ########################################################################################## # About microC language and microC compiler # microC language and microC compiler are educational tools, and their goal is to show some basic principles # of writing a C language compiler. Compiler represents one (relatively simple) solution, not necessarily the best one. # This Python/pyparsing version is made using Python 2.6.4 and pyparsing 1.5.2 (and it may contain errors :) ) ########################################################################################## ########################################################################################## # Model of the used hypothetical processor # The reason behind using a hypothetical processor is to simplify code generation and to concentrate on the compiler itself. # This compiler can relatively easily be ported to x86, but one must know all the little details about which register # can be used for what, which registers are default for various operations, etc. # The hypothetical processor has 16 registers, called %0 to %15. Register %13 is used for the function return value (x86's eax), # %14 is the stack frame pointer (x86's ebp) and %15 is the stack pointer (x86's esp). All data-handling instructions can be # unsigned (suffix U), or signed (suffix S). These are ADD, SUB, MUL and DIV. These are three-address instructions, # the first two operands are input, the third one is output. Whether these operands are registers, memory or constant # is not relevant, all combinations are possible (except that output cannot be a constant). Constants are writen with a $ prefix (10-base only). # Conditional jumps are handled by JXXY instructions, where XX is LT, GT, LE, GE, EQ, NE (less than, greater than, less than or equal, etc.) # and Y is U or S (unsigned or signed, except for JEQ i JNE). Unconditional jump is JMP. The move instruction is MOV. # Function handling is done using CALL, RET, PUSH and POP (C style function calls). Static data is defined using the WORD directive # (example: variable: WORD 1), whose only argument defines the number of locations that are reserved. ########################################################################################## ########################################################################################## # Grammar of The microC Programming Language # (small subset of C made for compiler course at Faculty of Technical Sciences, Chair for Applied Computer Sciences, Novi Sad, Serbia) # Patterns: # letter # -> "_" | "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "E" | "f" # | "F" | "g" | "G" | "h" | "H" | "i" | "I" | "j" | "J" | "k" | "K" | "l" # | "L" | "m" | "M" | "n" | "N" | "o" | "O" | "p" | "P" | "q" | "Q" | "r" # | "R" | "s" | "S" | "t" | "T" | "u" | "U" | "v" | "V" | "w" | "W" | "x" # | "X" | "y" | "Y" | "z" | "Z" # digit # -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" # identifier # -> letter ( letter | digit )* # int_constant # -> digit + # unsigned_constant # -> digit + ( "u" | "U" ) # Productions: # program # -> variable_list function_list # -> function_list # variable_list # -> variable ";" # -> variable_list variable ";" # variable # -> type identifier # type # -> "int" # -> "unsigned" # function_list # -> function # -> function_list function # function # -> type identifier "(" parameters ")" body # parameters # -> # -> parameter_list # parameter_list # -> variable # -> parameter_list "," variable # body # -> "{" variable_list statement_list "}" # -> "{" statement_list "}" # statement_list # -> # -> statement_list statement # statement # -> assignement_statement # -> function_call_statement # -> if_statement # -> while_statement # -> return_statement # -> compound_statement # assignement_statement # -> identifier "=" num_exp ";" # num_exp # -> mul_exp # -> num_exp "+" mul_exp # -> num_exp "-" mul_exp # mul_exp # -> exp # -> mul_exp "*" exp # -> mul_exp "/" exp # exp # -> constant # -> identifier # -> function_call # -> "(" num_exp ")" # -> "+" exp # -> "-" exp # constant # -> int_constant # -> unsigned_constant # function_call # -> identifier "(" arguments ")" # arguments # -> # -> argument_list # argument_list # -> num_exp # -> argument_list "," num_exp # function_call_statement # -> function_call ";" # if_statement # -> "if" "(" log_exp ")" statement # -> "if" "(" log_exp ")" statement "else" statement # -> -> -> -> -> -> -> -> 2 # log_exp # -> and_exp # -> log_exp "||" and_exp # and_exp # -> rel_exp # -> and_exp "&&" rel_exp # rel_exp # -> num_exp "<" num_exp # -> num_exp ">" num_exp # -> num_exp "<=" num_exp # -> num_exp ">=" num_exp # -> num_exp "==" num_exp # -> num_exp "!=" num_exp # while_statement # -> "while" "(" log_exp ")" statement # return_statement # -> "return" num_exp ";" # compound_statement # -> "{" statement_list "}" # Comment: /* a comment */ ########################################################################################## ########################################################################################## class Enumerate(dict): """C enum emulation (original by Scott David Daniels)""" def __init__(self, names): for number, name in enumerate(names.split()): setattr(self, name, number) self[number] = name class SharedData(object): """Data used in all three main classes""" #Possible kinds of symbol table entries KINDS = Enumerate("NO_KIND WORKING_REGISTER GLOBAL_VAR FUNCTION PARAMETER LOCAL_VAR CONSTANT") #Supported types of functions and variables TYPES = Enumerate("NO_TYPE INT UNSIGNED") #bit size of variables TYPE_BIT_SIZE = 16 #min/max values of constants MIN_INT = -2 ** (TYPE_BIT_SIZE - 1) MAX_INT = 2 ** (TYPE_BIT_SIZE - 1) - 1 MAX_UNSIGNED = 2 ** TYPE_BIT_SIZE - 1 #available working registers (the last one is the register for function's return value!) REGISTERS = "%0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13".split() #register for function's return value FUNCTION_REGISTER = len(REGISTERS) - 1 #the index of last working register LAST_WORKING_REGISTER = len(REGISTERS) - 2 #list of relational operators RELATIONAL_OPERATORS = "< > <= >= == !=".split() def __init__(self): #index of the currently parsed function self.functon_index = 0 #name of the currently parsed function self.functon_name = 0 #number of parameters of the currently parsed function self.function_params = 0 #number of local variables of the currently parsed function self.function_vars = 0 ########################################################################################## ########################################################################################## class ExceptionSharedData(object): """Class for exception handling data""" def __init__(self): #position in currently parsed text self.location = 0 #currently parsed text self.text = "" def setpos(self, location, text): """Helper function for setting curently parsed text and position""" self.location = location self.text = text exshared = ExceptionSharedData() class SemanticException(Exception): """Exception for semantic errors found during parsing, similar to ParseException. Introduced because ParseException is used internally in pyparsing and custom messages got lost and replaced by pyparsing's generic errors. """ def __init__(self, message, print_location=True): super(SemanticException,self).__init__() self._message = message self.location = exshared.location self.print_location = print_location if exshared.location != None: self.line = lineno(exshared.location, exshared.text) self.col = col(exshared.location, exshared.text) self.text = line(exshared.location, exshared.text) else: self.line = self.col = self.text = None def _get_message(self): return self._message def _set_message(self, message): self._message = message message = property(_get_message, _set_message) def __str__(self): """String representation of the semantic error""" msg = "Error" if self.print_location and (self.line != None): msg += " at line %d, col %d" % (self.line, self.col) msg += ": %s" % self.message if self.print_location and (self.line != None): msg += "\n%s" % self.text return msg ########################################################################################## ########################################################################################## class SymbolTableEntry(object): """Class which represents one symbol table entry.""" def __init__(self, sname = "", skind = 0, stype = 0, sattr = None, sattr_name = "None"): """Initialization of symbol table entry. sname - symbol name skind - symbol kind stype - symbol type sattr - symbol attribute sattr_name - symbol attribute name (used only for table display) """ self.name = sname self.kind = skind self.type = stype self.attribute = sattr self.attribute_name = sattr_name self.param_types = [] def set_attribute(self, name, value): """Sets attribute's name and value""" self.attribute_name = name self.attribute = value def attribute_str(self): """Returns attribute string (used only for table display)""" return "{0}={1}".format(self.attribute_name, self.attribute) if self.attribute != None else "None" class SymbolTable(object): """Class for symbol table of microC program""" def __init__(self, shared): """Initialization of the symbol table""" self.table = [] self.lable_len = 0 #put working registers in the symbol table for reg in range(SharedData.FUNCTION_REGISTER+1): self.insert_symbol(SharedData.REGISTERS[reg], SharedData.KINDS.WORKING_REGISTER, SharedData.TYPES.NO_TYPE) #shared data self.shared = shared def error(self, text=""): """Symbol table error exception. It should happen only if index is out of range while accessing symbol table. This exeption is not handled by the compiler, so as to allow traceback printing """ if text == "": raise Exception("Symbol table index out of range") else: raise Exception("Symbol table error: %s" % text) def display(self): """Displays the symbol table content""" #Finding the maximum length for each column sym_name = "Symbol name" sym_len = max(max(len(i.name) for i in self.table),len(sym_name)) kind_name = "Kind" kind_len = max(max(len(SharedData.KINDS[i.kind]) for i in self.table),len(kind_name)) type_name = "Type" type_len = max(max(len(SharedData.TYPES[i.type]) for i in self.table),len(type_name)) attr_name = "Attribute" attr_len = max(max(len(i.attribute_str()) for i in self.table),len(attr_name)) #print table header print("{0:3s} | {1:^{2}s} | {3:^{4}s} | {5:^{6}s} | {7:^{8}} | {9:s}".format(" No", sym_name, sym_len, kind_name, kind_len, type_name, type_len, attr_name, attr_len, "Parameters")) print("-----------------------------" + "-" * (sym_len + kind_len + type_len + attr_len)) #print symbol table for i,sym in enumerate(self.table): parameters = "" for p in sym.param_types: if parameters == "": parameters = "{0}".format(SharedData.TYPES[p]) else: parameters += ", {0}".format(SharedData.TYPES[p]) print("{0:3d} | {1:^{2}s} | {3:^{4}s} | {5:^{6}s} | {7:^{8}} | ({9})".format(i, sym.name, sym_len, SharedData.KINDS[sym.kind], kind_len, SharedData.TYPES[sym.type], type_len, sym.attribute_str(), attr_len, parameters)) def insert_symbol(self, sname, skind, stype): """Inserts new symbol at the end of the symbol table. Returns symbol index sname - symbol name skind - symbol kind stype - symbol type """ self.table.append(SymbolTableEntry(sname, skind, stype)) self.table_len = len(self.table) return self.table_len-1 def clear_symbols(self, index): """Clears all symbols begining with the index to the end of table""" try: del self.table[index:] except Exception: self.error() self.table_len = len(self.table) def lookup_symbol(self, sname, skind=list(SharedData.KINDS.keys()), stype=list(SharedData.TYPES.keys())): """Searches for symbol, from the end to the begining. Returns symbol index or None sname - symbol name skind - symbol kind (one kind, list of kinds, or None) deafult: any kind stype - symbol type (or None) default: any type """ skind = skind if isinstance(skind, list) else [skind] stype = stype if isinstance(stype, list) else [stype] for i, sym in [[x, self.table[x]] for x in range(len(self.table) - 1, SharedData.LAST_WORKING_REGISTER, -1)]: if (sym.name == sname) and (sym.kind in skind) and (sym.type in stype): return i return None def insert_id(self, sname, skind, skinds, stype): """Inserts a new identifier at the end of the symbol table, if possible. Returns symbol index, or raises an exception if the symbol alredy exists sname - symbol name skind - symbol kind skinds - symbol kinds to check for stype - symbol type """ index = self.lookup_symbol(sname, skinds) if index == None: index = self.insert_symbol(sname, skind, stype) return index else: raise SemanticException("Redefinition of '%s'" % sname) def insert_global_var(self, vname, vtype): "Inserts a new global variable" return self.insert_id(vname, SharedData.KINDS.GLOBAL_VAR, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.FUNCTION], vtype) def insert_local_var(self, vname, vtype, position): "Inserts a new local variable" index = self.insert_id(vname, SharedData.KINDS.LOCAL_VAR, [SharedData.KINDS.LOCAL_VAR, SharedData.KINDS.PARAMETER], vtype) self.table[index].attribute = position def insert_parameter(self, pname, ptype): "Inserts a new parameter" index = self.insert_id(pname, SharedData.KINDS.PARAMETER, SharedData.KINDS.PARAMETER, ptype) #set parameter's attribute to it's ordinal number self.table[index].set_attribute("Index", self.shared.function_params) #set parameter's type in param_types list of a function self.table[self.shared.function_index].param_types.append(ptype) return index def insert_function(self, fname, ftype): "Inserts a new function" index = self.insert_id(fname, SharedData.KINDS.FUNCTION, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.FUNCTION], ftype) self.table[index].set_attribute("Params",0) return index def insert_constant(self, cname, ctype): """Inserts a constant (or returns index if the constant already exists) Additionally, checks for range. """ index = self.lookup_symbol(cname, stype=ctype) if index == None: num = int(cname) if ctype == SharedData.TYPES.INT: if (num < SharedData.MIN_INT) or (num > SharedData.MAX_INT): raise SemanticException("Integer constant '%s' out of range" % cname) elif ctype == SharedData.TYPES.UNSIGNED: if (num < 0) or (num > SharedData.MAX_UNSIGNED): raise SemanticException("Unsigned constant '%s' out of range" % cname) index = self.insert_symbol(cname, SharedData.KINDS.CONSTANT, ctype) return index def same_types(self, index1, index2): """Returns True if both symbol table elements are of the same type""" try: same = self.table[index1].type == self.table[index2].type != SharedData.TYPES.NO_TYPE except Exception: self.error() return same def same_type_as_argument(self, index, function_index, argument_number): """Returns True if index and function's argument are of the same type index - index in symbol table function_index - function's index in symbol table argument_number - # of function's argument """ try: same = self.table[function_index].param_types[argument_number] == self.table[index].type except Exception: self.error() return same def get_attribute(self, index): try: return self.table[index].attribute except Exception: self.error() def set_attribute(self, index, value): try: self.table[index].attribute = value except Exception: self.error() def get_name(self, index): try: return self.table[index].name except Exception: self.error() def get_kind(self, index): try: return self.table[index].kind except Exception: self.error() def get_type(self, index): try: return self.table[index].type except Exception: self.error() def set_type(self, index, stype): try: self.table[index].type = stype except Exception: self.error() ########################################################################################## ########################################################################################## class CodeGenerator(object): """Class for code generation methods.""" #dictionary of relational operators RELATIONAL_DICT = {op:i for i, op in enumerate(SharedData.RELATIONAL_OPERATORS)} #conditional jumps for relational operators CONDITIONAL_JUMPS = ["JLTS", "JGTS", "JLES", "JGES", "JEQ ", "JNE ", "JLTU", "JGTU", "JLEU", "JGEU", "JEQ ", "JNE "] #opposite conditional jumps for relational operators OPPOSITE_JUMPS = ["JGES", "JLES", "JGTS", "JLTS", "JNE ", "JEQ ", "JGEU", "JLEU", "JGTU", "JLTU", "JNE ", "JEQ "] #supported operations OPERATIONS = {"+" : "ADD", "-" : "SUB", "*" : "MUL", "/" : "DIV"} #suffixes for signed and unsigned operations (if no type is specified, unsigned will be assumed) OPSIGNS = {SharedData.TYPES.NO_TYPE : "U", SharedData.TYPES.INT : "S", SharedData.TYPES.UNSIGNED : "U"} #text at start of data segment DATA_START_TEXT = "#DATA" #text at start of code segment CODE_START_TEXT = "#CODE" def __init__(self, shared, symtab): #generated code self.code = "" #prefix for internal labels self.internal = "@" #suffix for label definition self.definition = ":" #list of free working registers self.free_registers = list(range(SharedData.FUNCTION_REGISTER, -1, -1)) #list of used working registers self.used_registers = [] #list of used registers needed when function call is inside of a function call self.used_registers_stack = [] #shared data self.shared = shared #symbol table self.symtab = symtab def error(self, text): """Compiler error exception. It should happen only if something is wrong with compiler. This exeption is not handled by the compiler, so as to allow traceback printing """ raise Exception("Compiler error: %s" % text) def take_register(self, rtype = SharedData.TYPES.NO_TYPE): """Reserves one working register and sets its type""" if len(self.free_registers) == 0: self.error("no more free registers") reg = self.free_registers.pop() self.used_registers.append(reg) self.symtab.set_type(reg, rtype) return reg def take_function_register(self, rtype = SharedData.TYPES.NO_TYPE): """Reserves register for function return value and sets its type""" reg = SharedData.FUNCTION_REGISTER if reg not in self.free_registers: self.error("function register already taken") self.free_registers.remove(reg) self.used_registers.append(reg) self.symtab.set_type(reg, rtype) return reg def free_register(self, reg): """Releases working register""" if reg not in self.used_registers: self.error("register %s is not taken" % self.REGISTERS[reg]) self.used_registers.remove(reg) self.free_registers.append(reg) self.free_registers.sort(reverse = True) def free_if_register(self, index): """If index is a working register, free it, otherwise just return (helper function)""" if (index < 0) or (index > SharedData.FUNCTION_REGISTER): return else: self.free_register(index) def label(self, name, internal=False, definition=False): """Generates label name (helper function) name - label name internal - boolean value, adds "@" prefix to label definition - boolean value, adds ":" suffix to label """ return "{0}{1}{2}".format(self.internal if internal else "", name, self.definition if definition else "") def symbol(self, index): """Generates symbol name from index""" #if index is actually a string, just return it if isinstance(index, str): return index elif (index < 0) or (index >= self.symtab.table_len): self.error("symbol table index out of range") sym = self.symtab.table[index] #local variables are located at negative offset from frame pointer register if sym.kind == SharedData.KINDS.LOCAL_VAR: return "-{0}(1:%14)".format(sym.attribute * 4 + 4) #parameters are located at positive offset from frame pointer register elif sym.kind == SharedData.KINDS.PARAMETER: return "{0}(1:%14)".format(8 + sym.attribute * 4) elif sym.kind == SharedData.KINDS.CONSTANT: return "${0}".format(sym.name) else: return "{0}".format(sym.name) def save_used_registers(self): """Pushes all used working registers before function call""" used = self.used_registers[:] del self.used_registers[:] self.used_registers_stack.append(used[:]) used.sort() for reg in used: self.newline_text("PUSH\t%s" % SharedData.REGISTERS[reg], True) self.free_registers.extend(used) self.free_registers.sort(reverse = True) def restore_used_registers(self): """Pops all used working registers after function call""" used = self.used_registers_stack.pop() self.used_registers = used[:] used.sort(reverse = True) for reg in used: self.newline_text("POP \t%s" % SharedData.REGISTERS[reg], True) self.free_registers.remove(reg) def text(self, text): """Inserts text into generated code""" self.code += text def prepare_data_segment(self): """Inserts text at the start of data segment""" self.text(self.DATA_START_TEXT) def prepare_code_segment(self): """Inserts text at the start of code segment""" self.newline_text(self.CODE_START_TEXT) def newline(self, indent=False): """Inserts a newline, optionally with indentation.""" self.text("\n") if indent: self.text("\t\t\t") def newline_text(self, text, indent = False): """Inserts a newline and text, optionally with indentation (helper function)""" self.newline(indent) self.text(text) def newline_label(self, name, internal=False, definition=False): """Inserts a newline and a label (helper function) name - label name internal - boolean value, adds "@" prefix to label definition - boolean value, adds ":" suffix to label """ self.newline_text(self.label("{0}{1}{2}".format("@" if internal else "", name, ":" if definition else ""))) def global_var(self, name): """Inserts a new static (global) variable definition""" self.newline_label(name, False, True) self.newline_text("WORD\t1", True) def arithmetic_mnemonic(self, op_name, op_type): """Generates an arithmetic instruction mnemonic""" return self.OPERATIONS[op_name] + self.OPSIGNS[op_type] def arithmetic(self, operation, operand1, operand2, operand3 = None): """Generates an arithmetic instruction operation - one of supporetd operations operandX - index in symbol table or text representation of operand First two operands are input, third one is output """ if isinstance(operand1, int): output_type = self.symtab.get_type(operand1) self.free_if_register(operand1) else: output_type = None if isinstance(operand2, int): output_type = self.symtab.get_type(operand2) if output_type == None else output_type self.free_if_register(operand2) else: output_type = SharedData.TYPES.NO_TYPE if output_type == None else output_type #if operand3 is not defined, reserve one free register for it output = self.take_register(output_type) if operand3 == None else operand3 mnemonic = self.arithmetic_mnemonic(operation, output_type) self.newline_text("{0}\t{1},{2},{3}".format(mnemonic, self.symbol(operand1), self.symbol(operand2), self.symbol(output)), True) return output def relop_code(self, relop, operands_type): """Returns code for relational operator relop - relational operator operands_type - int or unsigned """ code = self.RELATIONAL_DICT[relop] offset = 0 if operands_type == SharedData.TYPES.INT else len(SharedData.RELATIONAL_OPERATORS) return code + offset def jump(self, relcode, opposite, label): """Generates a jump instruction relcode - relational operator code opposite - generate normal or opposite jump label - jump label """ jump = self.OPPOSITE_JUMPS[relcode] if opposite else self.CONDITIONAL_JUMPS[relcode] self.newline_text("{0}\t{1}".format(jump, label), True) def unconditional_jump(self, label): """Generates an unconditional jump instruction label - jump label """ self.newline_text("JMP \t{0}".format(label), True) def move(self,operand1, operand2): """Generates a move instruction If the output operand (opernad2) is a working register, sets it's type operandX - index in symbol table or text representation of operand """ if isinstance(operand1, int): output_type = self.symtab.get_type(operand1) self.free_if_register(operand1) else: output_type = SharedData.TYPES.NO_TYPE self.newline_text("MOV \t{0},{1}".format(self.symbol(operand1), self.symbol(operand2)), True) if isinstance(operand2, int): if self.symtab.get_kind(operand2) == SharedData.KINDS.WORKING_REGISTER: self.symtab.set_type(operand2, output_type) def push(self, operand): """Generates a push operation""" self.newline_text("PUSH\t%s" % self.symbol(operand), True) def pop(self, operand): """Generates a pop instruction""" self.newline_text("POP \t%s" % self.symbol(operand), True) def compare(self, operand1, operand2): """Generates a compare instruction operandX - index in symbol table """ typ = self.symtab.get_type(operand1) self.free_if_register(operand1) self.free_if_register(operand2) self.newline_text("CMP{0}\t{1},{2}".format(self.OPSIGNS[typ], self.symbol(operand1), self.symbol(operand2)), True) def function_begin(self): """Inserts function name label and function frame initialization""" self.newline_label(self.shared.function_name, False, True) self.push("%14") self.move("%15", "%14") def function_body(self): """Inserts a local variable initialization and body label""" if self.shared.function_vars > 0: const = self.symtab.insert_constant("0{}".format(self.shared.function_vars * 4), SharedData.TYPES.UNSIGNED) self.arithmetic("-", "%15", const, "%15") self.newline_label(self.shared.function_name + "_body", True, True) def function_end(self): """Inserts an exit label and function return instructions""" self.newline_label(self.shared.function_name + "_exit", True, True) self.move("%14", "%15") self.pop("%14") self.newline_text("RET", True) def function_call(self, function, arguments): """Generates code for a function call function - function index in symbol table arguments - list of arguments (indexes in symbol table) """ #push each argument to stack for arg in arguments: self.push(self.symbol(arg)) self.free_if_register(arg) self.newline_text("CALL\t"+self.symtab.get_name(function), True) args = self.symtab.get_attribute(function) #generates stack cleanup if function has arguments if args > 0: args_space = self.symtab.insert_constant("{0}".format(args * 4), SharedData.TYPES.UNSIGNED) self.arithmetic("+", "%15", args_space, "%15") ########################################################################################## ########################################################################################## class MicroC(object): """Class for microC parser/compiler""" def __init__(self): #Definitions of terminal symbols for microC programming language self.tId = Word(alphas+"_",alphanums+"_") self.tInteger = Word(nums).setParseAction(lambda x : [x[0], SharedData.TYPES.INT]) self.tUnsigned = Regex(r"[0-9]+[uU]").setParseAction(lambda x : [x[0][:-1], SharedData.TYPES.UNSIGNED]) self.tConstant = (self.tUnsigned | self.tInteger).setParseAction(self.constant_action) self.tType = Keyword("int").setParseAction(lambda x : SharedData.TYPES.INT) | \ Keyword("unsigned").setParseAction(lambda x : SharedData.TYPES.UNSIGNED) self.tRelOp = oneOf(SharedData.RELATIONAL_OPERATORS) self.tMulOp = oneOf("* /") self.tAddOp = oneOf("+ -") #Definitions of rules for global variables self.rGlobalVariable = (self.tType("type") + self.tId("name") + FollowedBy(";")).setParseAction(self.global_variable_action) self.rGlobalVariableList = ZeroOrMore(self.rGlobalVariable + Suppress(";")) #Definitions of rules for numeric expressions self.rExp = Forward() self.rMulExp = Forward() self.rNumExp = Forward() self.rArguments = delimitedList(self.rNumExp("exp").setParseAction(self.argument_action)) self.rFunctionCall = ((self.tId("name") + FollowedBy("(")).setParseAction(self.function_call_prepare_action) + Suppress("(") + Optional(self.rArguments)("args") + Suppress(")")).setParseAction(self.function_call_action) self.rExp << (self.rFunctionCall | self.tConstant | self.tId("name").setParseAction(self.lookup_id_action) | Group(Suppress("(") + self.rNumExp + Suppress(")")) | Group("+" + self.rExp) | Group("-" + self.rExp)).setParseAction(lambda x : x[0]) self.rMulExp << ((self.rExp + ZeroOrMore(self.tMulOp + self.rExp))).setParseAction(self.mulexp_action) self.rNumExp << (self.rMulExp + ZeroOrMore(self.tAddOp + self.rMulExp)).setParseAction(self.numexp_action) #Definitions of rules for logical expressions (these are without parenthesis support) self.rAndExp = Forward() self.rLogExp = Forward() self.rRelExp = (self.rNumExp + self.tRelOp + self.rNumExp).setParseAction(self.relexp_action) self.rAndExp << (self.rRelExp("exp") + ZeroOrMore(Literal("&&").setParseAction(self.andexp_action) + self.rRelExp("exp")).setParseAction(lambda x : self.relexp_code)) self.rLogExp << (self.rAndExp("exp") + ZeroOrMore(Literal("||").setParseAction(self.logexp_action) + self.rAndExp("exp")).setParseAction(lambda x : self.andexp_code)) #Definitions of rules for statements self.rStatement = Forward() self.rStatementList = Forward() self.rReturnStatement = (Keyword("return") + self.rNumExp("exp") + Suppress(";")).setParseAction(self.return_action) self.rAssignmentStatement = (self.tId("var") + Suppress("=") + self.rNumExp("exp") + Suppress(";")).setParseAction(self.assignment_action) self.rFunctionCallStatement = self.rFunctionCall + Suppress(";") self.rIfStatement = ( (Keyword("if") + FollowedBy("(")).setParseAction(self.if_begin_action) + (Suppress("(") + self.rLogExp + Suppress(")")).setParseAction(self.if_body_action) + (self.rStatement + Empty()).setParseAction(self.if_else_action) + Optional(Keyword("else") + self.rStatement)).setParseAction(self.if_end_action) self.rWhileStatement = ( (Keyword("while") + FollowedBy("(")).setParseAction(self.while_begin_action) + (Suppress("(") + self.rLogExp + Suppress(")")).setParseAction(self.while_body_action) + self.rStatement).setParseAction(self.while_end_action) self.rCompoundStatement = Group(Suppress("{") + self.rStatementList + Suppress("}")) self.rStatement << (self.rReturnStatement | self.rIfStatement | self.rWhileStatement | self.rFunctionCallStatement | self.rAssignmentStatement | self.rCompoundStatement) self.rStatementList << ZeroOrMore(self.rStatement) self.rLocalVariable = (self.tType("type") + self.tId("name") + FollowedBy(";")).setParseAction(self.local_variable_action) self.rLocalVariableList = ZeroOrMore(self.rLocalVariable + Suppress(";")) self.rFunctionBody = Suppress("{") + Optional(self.rLocalVariableList).setParseAction(self.function_body_action) + \ self.rStatementList + Suppress("}") self.rParameter = (self.tType("type") + self.tId("name")).setParseAction(self.parameter_action) self.rParameterList = delimitedList(self.rParameter) self.rFunction = ( (self.tType("type") + self.tId("name")).setParseAction(self.function_begin_action) + Group(Suppress("(") + Optional(self.rParameterList)("params") + Suppress(")") + self.rFunctionBody)).setParseAction(self.function_end_action) self.rFunctionList = OneOrMore(self.rFunction) self.rProgram = (Empty().setParseAction(self.data_begin_action) + self.rGlobalVariableList + Empty().setParseAction(self.code_begin_action) + self.rFunctionList).setParseAction(self.program_end_action) #shared data self.shared = SharedData() #symbol table self.symtab = SymbolTable(self.shared) #code generator self.codegen = CodeGenerator(self.shared, self.symtab) #index of the current function call self.function_call_index = -1 #stack for the nested function calls self.function_call_stack = [] #arguments of the current function call self.function_arguments = [] #stack for arguments of the nested function calls self.function_arguments_stack = [] #number of arguments for the curent function call self.function_arguments_number = -1 #stack for the number of arguments for the nested function calls self.function_arguments_number_stack = [] #last relational expression self.relexp_code = None #last and expression self.andexp_code = None #label number for "false" internal labels self.false_label_number = -1 #label number for all other internal labels self.label_number = None #label stack for nested statements self.label_stack = [] def warning(self, message, print_location=True): """Displays warning message. Uses exshared for current location of parsing""" msg = "Warning" if print_location and (exshared.location != None): wline = lineno(exshared.location, exshared.text) wcol = col(exshared.location, exshared.text) wtext = line(exshared.location, exshared.text) msg += " at line %d, col %d" % (wline, wcol) msg += ": %s" % message if print_location and (exshared.location != None): msg += "\n%s" % wtext print(msg) def data_begin_action(self): """Inserts text at start of data segment""" self.codegen.prepare_data_segment() def code_begin_action(self): """Inserts text at start of code segment""" self.codegen.prepare_code_segment() def global_variable_action(self, text, loc, var): """Code executed after recognising a global variable""" exshared.setpos(loc, text) if DEBUG > 0: print("GLOBAL_VAR:",var) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return index = self.symtab.insert_global_var(var.name, var.type) self.codegen.global_var(var.name) return index def local_variable_action(self, text, loc, var): """Code executed after recognising a local variable""" exshared.setpos(loc, text) if DEBUG > 0: print("LOCAL_VAR:",var, var.name, var.type) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return index = self.symtab.insert_local_var(var.name, var.type, self.shared.function_vars) self.shared.function_vars += 1 return index def parameter_action(self, text, loc, par): """Code executed after recognising a parameter""" exshared.setpos(loc, text) if DEBUG > 0: print("PARAM:",par) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return index = self.symtab.insert_parameter(par.name, par.type) self.shared.function_params += 1 return index def constant_action(self, text, loc, const): """Code executed after recognising a constant""" exshared.setpos(loc, text) if DEBUG > 0: print("CONST:",const) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return return self.symtab.insert_constant(const[0], const[1]) def function_begin_action(self, text, loc, fun): """Code executed after recognising a function definition (type and function name)""" exshared.setpos(loc, text) if DEBUG > 0: print("FUN_BEGIN:",fun) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return self.shared.function_index = self.symtab.insert_function(fun.name, fun.type) self.shared.function_name = fun.name self.shared.function_params = 0 self.shared.function_vars = 0 self.codegen.function_begin(); def function_body_action(self, text, loc, fun): """Code executed after recognising the beginning of function's body""" exshared.setpos(loc, text) if DEBUG > 0: print("FUN_BODY:",fun) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return self.codegen.function_body() def function_end_action(self, text, loc, fun): """Code executed at the end of function definition""" if DEBUG > 0: print("FUN_END:",fun) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #set function's attribute to number of function parameters self.symtab.set_attribute(self.shared.function_index, self.shared.function_params) #clear local function symbols (but leave function name) self.symtab.clear_symbols(self.shared.function_index + 1) self.codegen.function_end() def return_action(self, text, loc, ret): """Code executed after recognising a return statement""" exshared.setpos(loc, text) if DEBUG > 0: print("RETURN:",ret) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return if not self.symtab.same_types(self.shared.function_index, ret.exp[0]): raise SemanticException("Incompatible type in return") #set register for function's return value to expression value reg = self.codegen.take_function_register() self.codegen.move(ret.exp[0], reg) #after return statement, register for function's return value is available again self.codegen.free_register(reg) #jump to function's exit self.codegen.unconditional_jump(self.codegen.label(self.shared.function_name+"_exit", True)) def lookup_id_action(self, text, loc, var): """Code executed after recognising an identificator in expression""" exshared.setpos(loc, text) if DEBUG > 0: print("EXP_VAR:",var) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return var_index = self.symtab.lookup_symbol(var.name, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.PARAMETER, SharedData.KINDS.LOCAL_VAR]) if var_index == None: raise SemanticException("'%s' undefined" % var.name) return var_index def assignment_action(self, text, loc, assign): """Code executed after recognising an assignment statement""" exshared.setpos(loc, text) if DEBUG > 0: print("ASSIGN:",assign) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return var_index = self.symtab.lookup_symbol(assign.var, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.PARAMETER, SharedData.KINDS.LOCAL_VAR]) if var_index == None: raise SemanticException("Undefined lvalue '%s' in assignment" % assign.var) if not self.symtab.same_types(var_index, assign.exp[0]): raise SemanticException("Incompatible types in assignment") self.codegen.move(assign.exp[0], var_index) def mulexp_action(self, text, loc, mul): """Code executed after recognising a mulexp expression (something *|/ something)""" exshared.setpos(loc, text) if DEBUG > 0: print("MUL_EXP:",mul) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #iterate through all multiplications/divisions m = list(mul) while len(m) > 1: if not self.symtab.same_types(m[0], m[2]): raise SemanticException("Invalid opernads to binary '%s'" % m[1]) reg = self.codegen.arithmetic(m[1], m[0], m[2]) #replace first calculation with it's result m[0:3] = [reg] return m[0] def numexp_action(self, text, loc, num): """Code executed after recognising a numexp expression (something +|- something)""" exshared.setpos(loc, text) if DEBUG > 0: print("NUM_EXP:",num) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #iterate through all additions/substractions n = list(num) while len(n) > 1: if not self.symtab.same_types(n[0], n[2]): raise SemanticException("Invalid opernads to binary '%s'" % n[1]) reg = self.codegen.arithmetic(n[1], n[0], n[2]) #replace first calculation with it's result n[0:3] = [reg] return n[0] def function_call_prepare_action(self, text, loc, fun): """Code executed after recognising a function call (type and function name)""" exshared.setpos(loc, text) if DEBUG > 0: print("FUN_PREP:",fun) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return index = self.symtab.lookup_symbol(fun.name, SharedData.KINDS.FUNCTION) if index == None: raise SemanticException("'%s' is not a function" % fun.name) #save any previous function call data (for nested function calls) self.function_call_stack.append(self.function_call_index) self.function_call_index = index self.function_arguments_stack.append(self.function_arguments[:]) del self.function_arguments[:] self.codegen.save_used_registers() def argument_action(self, text, loc, arg): """Code executed after recognising each of function's arguments""" exshared.setpos(loc, text) if DEBUG > 0: print("ARGUMENT:",arg.exp) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return arg_ordinal = len(self.function_arguments) #check argument's type if not self.symtab.same_type_as_argument(arg.exp, self.function_call_index, arg_ordinal): raise SemanticException("Incompatible type for argument %d in '%s'" % (arg_ordinal + 1, self.symtab.get_name(self.function_call_index))) self.function_arguments.append(arg.exp) def function_call_action(self, text, loc, fun): """Code executed after recognising the whole function call""" exshared.setpos(loc, text) if DEBUG > 0: print("FUN_CALL:",fun) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #check number of arguments if len(self.function_arguments) != self.symtab.get_attribute(self.function_call_index): raise SemanticException("Wrong number of arguments for function '%s'" % fun.name) #arguments should be pushed to stack in reverse order self.function_arguments.reverse() self.codegen.function_call(self.function_call_index, self.function_arguments) self.codegen.restore_used_registers() return_type = self.symtab.get_type(self.function_call_index) #restore previous function call data self.function_call_index = self.function_call_stack.pop() self.function_arguments = self.function_arguments_stack.pop() register = self.codegen.take_register(return_type) #move result to a new free register, to allow the next function call self.codegen.move(self.codegen.take_function_register(return_type), register) return register def relexp_action(self, text, loc, arg): """Code executed after recognising a relexp expression (something relop something)""" if DEBUG > 0: print("REL_EXP:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return exshared.setpos(loc, text) if not self.symtab.same_types(arg[0], arg[2]): raise SemanticException("Invalid operands for operator '{0}'".format(arg[1])) self.codegen.compare(arg[0], arg[2]) #return relational operator's code self.relexp_code = self.codegen.relop_code(arg[1], self.symtab.get_type(arg[0])) return self.relexp_code def andexp_action(self, text, loc, arg): """Code executed after recognising a andexp expression (something and something)""" exshared.setpos(loc, text) if DEBUG > 0: print("AND+EXP:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return label = self.codegen.label("false{0}".format(self.false_label_number), True, False) self.codegen.jump(self.relexp_code, True, label) self.andexp_code = self.relexp_code return self.andexp_code def logexp_action(self, text, loc, arg): """Code executed after recognising logexp expression (something or something)""" exshared.setpos(loc, text) if DEBUG > 0: print("LOG_EXP:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return label = self.codegen.label("true{0}".format(self.label_number), True, False) self.codegen.jump(self.relexp_code, False, label) self.codegen.newline_label("false{0}".format(self.false_label_number), True, True) self.false_label_number += 1 def if_begin_action(self, text, loc, arg): """Code executed after recognising an if statement (if keyword)""" exshared.setpos(loc, text) if DEBUG > 0: print("IF_BEGIN:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return self.false_label_number += 1 self.label_number = self.false_label_number self.codegen.newline_label("if{0}".format(self.label_number), True, True) def if_body_action(self, text, loc, arg): """Code executed after recognising if statement's body""" exshared.setpos(loc, text) if DEBUG > 0: print("IF_BODY:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #generate conditional jump (based on last compare) label = self.codegen.label("false{0}".format(self.false_label_number), True, False) self.codegen.jump(self.relexp_code, True, label) #generate 'true' label (executes if condition is satisfied) self.codegen.newline_label("true{0}".format(self.label_number), True, True) #save label numbers (needed for nested if/while statements) self.label_stack.append(self.false_label_number) self.label_stack.append(self.label_number) def if_else_action(self, text, loc, arg): """Code executed after recognising if statement's else body""" exshared.setpos(loc, text) if DEBUG > 0: print("IF_ELSE:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #jump to exit after all statements for true condition are executed self.label_number = self.label_stack.pop() label = self.codegen.label("exit{0}".format(self.label_number), True, False) self.codegen.unconditional_jump(label) #generate final 'false' label (executes if condition isn't satisfied) self.codegen.newline_label("false{0}".format(self.label_stack.pop()), True, True) self.label_stack.append(self.label_number) def if_end_action(self, text, loc, arg): """Code executed after recognising a whole if statement""" exshared.setpos(loc, text) if DEBUG > 0: print("IF_END:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return self.codegen.newline_label("exit{0}".format(self.label_stack.pop()), True, True) def while_begin_action(self, text, loc, arg): """Code executed after recognising a while statement (while keyword)""" exshared.setpos(loc, text) if DEBUG > 0: print("WHILE_BEGIN:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return self.false_label_number += 1 self.label_number = self.false_label_number self.codegen.newline_label("while{0}".format(self.label_number), True, True) def while_body_action(self, text, loc, arg): """Code executed after recognising while statement's body""" exshared.setpos(loc, text) if DEBUG > 0: print("WHILE_BODY:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #generate conditional jump (based on last compare) label = self.codegen.label("false{0}".format(self.false_label_number), True, False) self.codegen.jump(self.relexp_code, True, label) #generate 'true' label (executes if condition is satisfied) self.codegen.newline_label("true{0}".format(self.label_number), True, True) self.label_stack.append(self.false_label_number) self.label_stack.append(self.label_number) def while_end_action(self, text, loc, arg): """Code executed after recognising a whole while statement""" exshared.setpos(loc, text) if DEBUG > 0: print("WHILE_END:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return #jump to condition checking after while statement body self.label_number = self.label_stack.pop() label = self.codegen.label("while{0}".format(self.label_number), True, False) self.codegen.unconditional_jump(label) #generate final 'false' label and exit label self.codegen.newline_label("false{0}".format(self.label_stack.pop()), True, True) self.codegen.newline_label("exit{0}".format(self.label_number), True, True) def program_end_action(self, text, loc, arg): """Checks if there is a 'main' function and the type of 'main' function""" exshared.setpos(loc, text) if DEBUG > 0: print("PROGRAM_END:",arg) if DEBUG == 2: self.symtab.display() if DEBUG > 2: return index = self.symtab.lookup_symbol("main",SharedData.KINDS.FUNCTION) if index == None: raise SemanticException("Undefined reference to 'main'", False) elif self.symtab.get_type(index) != SharedData.TYPES.INT: self.warning("Return type of 'main' is not int", False) def parse_text(self,text): """Parse string (helper function)""" try: return self.rProgram.ignore(cStyleComment).parseString(text, parseAll=True) except SemanticException as err: print(err) exit(3) except ParseException as err: print(err) exit(3) def parse_file(self,filename): """Parse file (helper function)""" try: return self.rProgram.ignore(cStyleComment).parseFile(filename, parseAll=True) except SemanticException as err: print(err) exit(3) except ParseException as err: print(err) exit(3) ########################################################################################## ########################################################################################## if 0: #main program mc = MicroC() output_file = "output.asm" if len(argv) == 1: input_file = stdin elif len(argv) == 2: input_file = argv[1] elif len(argv) == 3: input_file = argv[1] output_file = argv[2] else: usage = """Usage: {0} [input_file [output_file]] If output file is omitted, output.asm is used If input file is omitted, stdin is used""".format(argv[0]) print(usage) exit(1) try: parse = stdin if input_file == stdin else open(input_file,'r') except Exception: print("Input file '%s' open error" % input_file) exit(2) mc.parse_file(parse) #if you want to see the final symbol table, uncomment next line #mc.symtab.display() try: out = open(output_file, 'w') out.write(mc.codegen.code) out.close except Exception: print("Output file '%s' open error" % output_file) exit(2) ########################################################################################## ########################################################################################## if __name__ == "__main__": test_program_example = """ int a; int b; int c; unsigned d; int fun1(int x, unsigned y) { return 123; } int fun2(int a) { return 1 + a * fun1(a, 456u); } int main(int x, int y) { int w; unsigned z; if (9 > 8 && 2 < 3 || 6 != 5 && a <= b && c < x || w >= y) { a = b + 1; if (x == y) while (d < 4u) x = x * w; else while (a + b < c - y && x > 3 || y < 2) if (z > d) a = a - 4; else b = a * b * c * x / y; } else c = 4; a = fun1(x,d) + fun2(fun1(fun2(w + 3 * 2) + 2 * c, 2u)); return 2; } """ mc = MicroC() mc.parse_text(test_program_example) print(mc.codegen.code) pyparsing2-2.4.7/examples/pythonGrammarParser.py000066400000000000000000000200231365333160500220210ustar00rootroot00000000000000# pythonGrammarParser.py # # Copyright, 2006, by Paul McGuire # from pyparsing import * # should probably read this from the Grammar file provided with the Python source, but # this just skips that step and inlines the bnf text directly - this grammar was taken from # Python 2.4.1 # grammar = r""" # Grammar for Python # Note: Changing the grammar specified in this file will most likely # require corresponding changes in the parser module # (../Modules/parsermodule.c). If you can't make the changes to # that module yourself, please co-ordinate the required changes # with someone who can; ask around on python-dev for help. Fred # Drake will probably be listening there. # Commands for Kees Blom's railroad program #diagram:token NAME #diagram:token NUMBER #diagram:token STRING #diagram:token NEWLINE #diagram:token ENDMARKER #diagram:token INDENT #diagram:output\input python.bla #diagram:token DEDENT #diagram:output\textwidth 20.04cm\oddsidemargin 0.0cm\evensidemargin 0.0cm #diagram:rules # Start symbols for the grammar: # single_input is a single interactive statement; # file_input is a module or sequence of commands read from an input file; # eval_input is the input for the eval() and input() functions. # NB: compound_stmt in single_input is followed by extra NEWLINE! single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE file_input: (NEWLINE | stmt)* ENDMARKER eval_input: testlist NEWLINE* ENDMARKER decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE decorators: decorator+ funcdef: [decorators] 'def' NAME parameters ':' suite parameters: '(' [varargslist] ')' varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [','] fpdef: NAME | '(' fplist ')' fplist: fpdef (',' fpdef)* [','] stmt: simple_stmt | compound_stmt simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt | assert_stmt expr_stmt: testlist (augassign testlist | ('=' testlist)*) augassign: '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' # For normal assignments, additional restrictions enforced by the interpreter print_stmt: 'print' ( [ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ] ) del_stmt: 'del' exprlist pass_stmt: 'pass' flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt break_stmt: 'break' continue_stmt: 'continue' return_stmt: 'return' [testlist] yield_stmt: 'yield' testlist raise_stmt: 'raise' [test [',' test [',' test]]] import_stmt: import_name | import_from import_name: 'import' dotted_as_names import_from: 'from' dotted_name 'import' ('*' | '(' import_as_names ')' | import_as_names) import_as_name: NAME [NAME NAME] dotted_as_name: dotted_name [NAME NAME] import_as_names: import_as_name (',' import_as_name)* [','] dotted_as_names: dotted_as_name (',' dotted_as_name)* dotted_name: NAME ('.' NAME)* global_stmt: 'global' NAME (',' NAME)* exec_stmt: 'exec' expr ['in' test [',' test]] assert_stmt: 'assert' test [',' test] #35 compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] while_stmt: 'while' test ':' suite ['else' ':' suite] for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] try_stmt: ('try' ':' suite (except_clause ':' suite)+ #diagram:break ['else' ':' suite] | 'try' ':' suite 'finally' ':' suite) # NB compile.c makes sure that the default except clause is last except_clause: 'except' [test [',' test]] suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT test: and_test ('or' and_test)* | lambdef and_test: not_test ('and' not_test)* not_test: 'not' not_test | comparison comparison: expr (comp_op expr)* comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' expr: xor_expr ('|' xor_expr)* xor_expr: and_expr ('^' and_expr)* and_expr: shift_expr ('&' shift_expr)* shift_expr: arith_expr (('<<'|'>>') arith_expr)* arith_expr: term (('+'|'-') term)* term: factor (('*'|'/'|'%'|'//') factor)* factor: ('+'|'-'|'~') factor | power power: atom trailer* ['**' factor] atom: '(' [testlist_gexp] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+ listmaker: test ( list_for | (',' test)* [','] ) testlist_gexp: test ( gen_for | (',' test)* [','] ) lambdef: 'lambda' [varargslist] ':' test trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME subscriptlist: subscript (',' subscript)* [','] subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop] sliceop: ':' [test] exprlist: expr (',' expr)* [','] testlist: test (',' test)* [','] testlist_safe: test [(',' test)+ [',']] dictmaker: test ':' test (',' test ':' test)* [','] classdef: 'class' NAME ['(' testlist ')'] ':' suite arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test) argument: [test '='] test [gen_for] # Really [keyword '='] test list_iter: list_for | list_if list_for: 'for' exprlist 'in' testlist_safe [list_iter] list_if: 'if' test [list_iter] gen_iter: gen_for | gen_if gen_for: 'for' exprlist 'in' test [gen_iter] gen_if: 'if' test [gen_iter] testlist1: test (',' test)* # not used in grammar, but may appear in "node" passed from Parser to Compiler encoding_decl: NAME """ class SemanticGroup(object): def __init__(self,contents): self.contents = contents while self.contents[-1].__class__ == self.__class__: self.contents = self.contents[:-1] + self.contents[-1].contents def __str__(self): return "{0}({1})".format(self.label, " ".join([isinstance(c,str) and c or str(c) for c in self.contents]) ) class OrList(SemanticGroup): label = "OR" pass class AndList(SemanticGroup): label = "AND" pass class OptionalGroup(SemanticGroup): label = "OPT" pass class Atom(SemanticGroup): def __init__(self,contents): if len(contents) > 1: self.rep = contents[1] else: self.rep = "" if isinstance(contents,str): self.contents = contents else: self.contents = contents[0] def __str__(self): return "{0}{1}".format(self.rep, self.contents) def makeGroupObject(cls): def groupAction(s,l,t): try: return cls(t[0].asList()) except Exception: return cls(t) return groupAction # bnf punctuation LPAREN = Suppress("(") RPAREN = Suppress(")") LBRACK = Suppress("[") RBRACK = Suppress("]") COLON = Suppress(":") ALT_OP = Suppress("|") # bnf grammar ident = Word(alphanums+"_") bnfToken = Word(alphanums+"_") + ~FollowedBy(":") repSymbol = oneOf("* +") bnfExpr = Forward() optionalTerm = Group(LBRACK + bnfExpr + RBRACK).setParseAction(makeGroupObject(OptionalGroup)) bnfTerm = ( (bnfToken | quotedString | optionalTerm | ( LPAREN + bnfExpr + RPAREN )) + Optional(repSymbol) ).setParseAction(makeGroupObject(Atom)) andList = Group(bnfTerm + OneOrMore(bnfTerm)).setParseAction(makeGroupObject(AndList)) bnfFactor = andList | bnfTerm orList = Group( bnfFactor + OneOrMore( ALT_OP + bnfFactor ) ).setParseAction(makeGroupObject(OrList)) bnfExpr << ( orList | bnfFactor ) bnfLine = ident + COLON + bnfExpr bnfComment = "#" + restOfLine # build return tokens as a dictionary bnf = Dict(OneOrMore(Group(bnfLine))) bnf.ignore(bnfComment) # bnf is defined, parse the grammar text bnfDefs = bnf.parseString(grammar) # correct answer is 78 expected = 78 assert len(bnfDefs) == expected, \ "Error, found %d BNF defns, expected %d" % (len(bnfDefs), expected) # list out defns in order they were parsed (to verify accuracy of parsing) for k,v in bnfDefs: print(k,"=",v) print() # list out parsed grammar defns (demonstrates dictionary access to parsed tokens) for k in list(bnfDefs.keys()): print(k,"=",bnfDefs[k]) pyparsing2-2.4.7/examples/rangeCheck.py000066400000000000000000000041501365333160500200510ustar00rootroot00000000000000# rangeCheck.py # # A sample program showing how parse actions can convert parsed # strings into a data type or object, and to validate the parsed value. # # Updated to use new addCondition method and expr() copy. # # Copyright 2011,2015 Paul T. McGuire # from pyparsing import Word, nums, Suppress, Optional from datetime import datetime def ranged_value(expr, minval=None, maxval=None): # have to specify at least one range boundary if minval is None and maxval is None: raise ValueError("minval or maxval must be specified") # set range testing function and error message depending on # whether either or both min and max values are given inRangeCondition = { (True, False) : lambda s,l,t : t[0] <= maxval, (False, True) : lambda s,l,t : minval <= t[0], (False, False) : lambda s,l,t : minval <= t[0] <= maxval, }[minval is None, maxval is None] outOfRangeMessage = { (True, False) : "value is greater than %s" % maxval, (False, True) : "value is less than %s" % minval, (False, False) : "value is not in the range ({0} to {1})".format(minval,maxval), }[minval is None, maxval is None] return expr().addCondition(inRangeCondition, message=outOfRangeMessage) # define the expressions for a date of the form YYYY/MM/DD or YYYY/MM (assumes YYYY/MM/01) integer = Word(nums).setName("integer") integer.setParseAction(lambda t:int(t[0])) month = ranged_value(integer, 1, 12) day = ranged_value(integer, 1, 31) year = ranged_value(integer, 2000, None) SLASH = Suppress('/') dateExpr = year("year") + SLASH + month("month") + Optional(SLASH + day("day")) dateExpr.setName("date") # convert date fields to datetime (also validates dates as truly valid dates) dateExpr.setParseAction(lambda t: datetime(t.year, t.month, t.day or 1).date()) # add range checking on dates mindate = datetime(2002,1,1).date() maxdate = datetime.now().date() dateExpr = ranged_value(dateExpr, mindate, maxdate) dateExpr.runTests(""" 2011/5/8 2001/1/1 2004/2/29 2004/2 1999/12/31""") pyparsing2-2.4.7/examples/readJson.py000066400000000000000000003613641365333160500176010ustar00rootroot00000000000000#~ url = "http://cmsdoc.cern.ch/cms/test/aprom/phedex/dev/gowri/datasvc/tbedi/requestDetails" #~ params = {'format':'json'} #~ import urllib #~ eparams = urllib.urlencode(params) #~ import urllib2 #~ request = urllib2.Request(url,eparams) #~ response = urllib2.urlopen(request) #~ s = response.read() #~ response.close() #~ print s s = """ {"phedex":{"request":[{"last_update":"1188037561", "numofapproved":"1", "id":"7425"}, {"last_update":"1188751826", "numofapproved":"1", "id":"8041"}, {"last_update":"1190116795", "numofapproved":"1", "id":"9281"}, {"last_update":"1190248781", "numofapproved":"1", "id":"9521"}, {"last_update":"1192615612", "numofapproved":"1", "id":"12821"}, {"last_update":"1192729887", "numofapproved":"1", "id":"13121"}, {"last_update":"1193152971", "numofapproved":"1", "id":"13501"}, {"last_update":"1194022054", "numofapproved":"1", "id":"14782"}, {"last_update":"1194429365", "numofapproved":"1", "id":"15081"}, {"last_update":"1195069848", "numofapproved":"1", "id":"16661"}, {"last_update":"1178403225", "numofapproved":"1", "id":"1281"}, {"last_update":"1179239056", "numofapproved":"1", "id":"1387"}, {"last_update":"1179842205", "numofapproved":"1", "id":"1665"}, {"last_update":"1179842040", "numofapproved":"1", "id":"1661"}, {"last_update":"1179935333", "numofapproved":"1", "id":"1741"}, {"last_update":"1183151195", "numofapproved":"1", "id":"3841"}, {"last_update":"1187031531", "numofapproved":"1", "id":"6601"}, {"last_update":"1188820478", "numofapproved":"1", "id":"8121"}, {"last_update":"1190652719", "numofapproved":"1", "id":"9983"}, {"last_update":"1192628950", "numofapproved":"1", "id":"12841"}, {"last_update":"1193075426", "numofapproved":"1", "id":"13341"}, {"last_update":"1194214609", "numofapproved":"1", "id":"14882"}, {"last_update":"1194387864", "numofapproved":"1", "id":"15062"}, {"last_update":"1195134504", "numofapproved":"1", "id":"16741"}, {"last_update":"1182431453", "numofapproved":"1", "id":"3421"}, {"last_update":"1183448188", "numofapproved":"1", "id":"4061"}, {"last_update":"1184588081", "numofapproved":"1", "id":"4908"}, {"last_update":"1184681258", "numofapproved":"1", "id":"4913"}, {"last_update":"1188039048", "numofapproved":"1", "id":"7426"}, {"last_update":"1192699041", "numofapproved":"1", "id":"12982"}, {"last_update":"1193219685", "numofapproved":"1", "id":"13529"}, {"last_update":"1193401408", "numofapproved":"1", "id":"14081"}, {"last_update":"1194454724", "numofapproved":"1", "id":"15201"}, {"last_update":"1194937690", "numofapproved":"1", "id":"16044"}, {"last_update":"1194947125", "numofapproved":"1", "id":"16103"}, {"last_update":"1195134890", "numofapproved":"1", "id":"16761"}, {"last_update":"1195486898", "numofapproved":"1", "id":"17301"}, {"last_update":"1195497774", "numofapproved":"1", "id":"17341"}, {"last_update":"1184744080", "numofapproved":"1", "id":"4941"}, {"last_update":"1186558911", "numofapproved":"1", "id":"6321"}, {"last_update":"1189524520", "numofapproved":"1", "id":"8802"}, {"last_update":"1192683178", "numofapproved":"1", "id":"12921"}, {"last_update":"1193260655", "numofapproved":"1", "id":"13530"}, {"last_update":"1194280038", "numofapproved":"1", "id":"15002"}, {"last_update":"1182077478", "numofapproved":"1", "id":"3162"}, {"last_update":"1183386650", "numofapproved":"1", "id":"3961"}, {"last_update":"1192063369", "numofapproved":"1", "id":"12182"}, {"last_update":"1181931262", "numofapproved":"1", "id":"3101"}, {"last_update":"1178648271", "numofapproved":"1", "id":"1308"}, {"last_update":"1179239923", "numofapproved":"1", "id":"1405"}, {"last_update":"1184370745", "numofapproved":"1", "id":"4861"}, {"last_update":"1185280568", "numofapproved":"1", "id":"5302"}, {"last_update":"1187875115", "numofapproved":"1", "id":"7344"}, {"last_update":"1189140441", "numofapproved":"1", "id":"8541"}, {"last_update":"1189180903", "numofapproved":"1", "id":"8661"}, {"last_update":"1189767643", "numofapproved":"1", "id":"9001"}, {"last_update":"1190726167", "numofapproved":"1", "id":"10101"}, {"last_update":"1190972990", "numofapproved":"1", "id":"10661"}, {"last_update":"1190990720", "numofapproved":"1", "id":"10712"}, {"last_update":"1192004838", "numofapproved":"1", "id":"12021"}, {"last_update":"1192612211", "numofapproved":"1", "id":"12803"}, {"last_update":"1194441407", "numofapproved":"1", "id":"15103"}, {"last_update":"1194792356", "numofapproved":"1", "id":"15681"}, {"last_update":"1194860650", "numofapproved":"1", "id":"15801"}, {"last_update":"1194877395", "numofapproved":"1", "id":"15881"}, {"last_update":"1194950552", "numofapproved":"1", "id":"16124"}, {"last_update":"1194992714", "numofapproved":"1", "id":"16421"}, {"last_update":"1195054500", "numofapproved":"1", "id":"16581"}, {"last_update":"1195228524", "numofapproved":"1", "id":"17001"}, {"last_update":"1195469382", "numofapproved":"1", "id":"17161"}, {"last_update":"1178035947", "numofapproved":"1", "id":"1202"}, {"last_update":"1178869668", "numofapproved":"1", "id":"1356"}, {"last_update":"1183563268", "numofapproved":"1", "id":"4201"}, {"last_update":"1185314677", "numofapproved":"1", "id":"5361"}, {"last_update":"1188467567", "numofapproved":"1", "id":"7781"}, {"last_update":"1190011821", "numofapproved":"1", "id":"9202"}, {"last_update":"1190206214", "numofapproved":"1", "id":"9481"}, {"last_update":"1190973037", "numofapproved":"1", "id":"10663"}, {"last_update":"1190819127", "numofapproved":"1", "id":"10342"}, {"last_update":"1192154959", "numofapproved":"1", "id":"12381"}, {"last_update":"1192634509", "numofapproved":"1", "id":"12862"}, {"last_update":"1194004677", "numofapproved":"1", "id":"14722"}, {"last_update":"1195548191", "numofapproved":"1", "id":"17501"}, {"last_update":"1195548953", "numofapproved":"1", "id":"17502"}, {"last_update":"1195559809", "numofapproved":"1", "id":"17541"}, {"last_update":"1177589103", "numofapproved":"1", "id":"1044"}, {"last_update":"1183416879", "numofapproved":"1", "id":"4041"}, {"last_update":"1186646977", "numofapproved":"1", "id":"6342"}, {"last_update":"1189656586", "numofapproved":"1", "id":"8902"}, {"last_update":"1190150645", "numofapproved":"1", "id":"9421"}, {"last_update":"1190409040", "numofapproved":"1", "id":"9741"}, {"last_update":"1190973011", "numofapproved":"1", "id":"10662"}, {"last_update":"1190993896", "numofapproved":"1", "id":"10761"}, {"last_update":"1193973610", "numofapproved":"1", "id":"14661"}, {"last_update":"1193973848", "numofapproved":"1", "id":"14664"}, {"last_update":"1194539978", "numofapproved":"1", "id":"15381"}, {"last_update":"1194947356", "numofapproved":"1", "id":"16105"}, {"last_update":"1195399589", "numofapproved":"1", "id":"17101"}, {"last_update":"1195464953", "numofapproved":"1", "id":"17141"}, {"last_update":"1171962221", "numofapproved":"1", "id":"109"}, {"last_update":"1173113812", "numofapproved":"1", "id":"247"}, {"last_update":"1173975435", "numofapproved":"1", "id":"343"}, {"last_update":"1174050971", "numofapproved":"1", "id":"353"}, {"last_update":"1174301484", "numofapproved":"1", "id":"393"}, {"last_update":"1172565853", "numofapproved":"1", "id":"208"}, {"last_update":"1172593328", "numofapproved":"1", "id":"215"}, {"last_update":"1175267391", "numofapproved":"1", "id":"565"}, {"last_update":"1171379845", "numofapproved":"1", "id":"25"}, {"last_update":"1171477466", "numofapproved":"1", "id":"53"}, {"last_update":"1171799296", "numofapproved":"1", "id":"77"}, {"last_update":"1172671474", "numofapproved":"1", "id":"223"}, {"last_update":"1174301354", "numofapproved":"1", "id":"388"}, {"last_update":"1174899552", "numofapproved":"1", "id":"511"}, {"last_update":"1174899458", "numofapproved":"1", "id":"505"}, {"last_update":"1175502936", "numofapproved":"1", "id":"604"}, {"last_update":"1175613825", "numofapproved":"1", "id":"665"}, {"last_update":"1175776232", "numofapproved":"1", "id":"673"}, {"last_update":"1171621302", "numofapproved":"1", "id":"68"}, {"last_update":"1171904738", "numofapproved":"1", "id":"98"}, {"last_update":"1171968012", "numofapproved":"1", "id":"115"}, {"last_update":"1172145037", "numofapproved":"1", "id":"168"}, {"last_update":"1172246599", "numofapproved":"1", "id":"185"}, {"last_update":"1173886280", "numofapproved":"1", "id":"318"}, {"last_update":"1174562010", "numofapproved":"1", "id":"423"}, {"last_update":"1176308974", "numofapproved":"1", "id":"884"}, {"last_update":"1176482150", "numofapproved":"1", "id":"943"}, {"last_update":"1176702424", "numofapproved":"1", "id":"1001"}, {"last_update":"1176748776", "numofapproved":"1", "id":"984"}, {"last_update":"1172669745", "numofapproved":"1", "id":"222"}, {"last_update":"1174899538", "numofapproved":"1", "id":"510"}, {"last_update":"1174899143", "numofapproved":"1", "id":"493"}, {"last_update":"1174899043", "numofapproved":"1", "id":"488"}, {"last_update":"1175711780", "numofapproved":"1", "id":"667"}, {"last_update":"1175712851", "numofapproved":"1", "id":"705"}, {"last_update":"1176296548", "numofapproved":"1", "id":"841"}, {"last_update":"1175862269", "numofapproved":"1", "id":"781"}, {"last_update":"1171483107", "numofapproved":"1", "id":"54"}, {"last_update":"1171645737", "numofapproved":"1", "id":"71"}, {"last_update":"1172253423", "numofapproved":"1", "id":"188"}, {"last_update":"1173888726", "numofapproved":"1", "id":"321"}, {"last_update":"1173975649", "numofapproved":"1", "id":"346"}, {"last_update":"1174299379", "numofapproved":"1", "id":"363"}, {"last_update":"1174301359", "numofapproved":"1", "id":"389"}, {"last_update":"1174301073", "numofapproved":"1", "id":"379"}, {"last_update":"1174300650", "numofapproved":"1", "id":"371"}, {"last_update":"1171485069", "numofapproved":"1", "id":"55"}, {"last_update":"1171799178", "numofapproved":"1", "id":"73"}, {"last_update":"1171896809", "numofapproved":"1", "id":"95"}, {"last_update":"1172672959", "numofapproved":"1", "id":"224"}, {"last_update":"1172693619", "numofapproved":"1", "id":"230"}, {"last_update":"1173207656", "numofapproved":"1", "id":"253"}, {"last_update":"1174059533", "numofapproved":"1", "id":"356"}, {"last_update":"1174300538", "numofapproved":"1", "id":"368"}, {"last_update":"1176137457", "numofapproved":"1", "id":"807"}, {"last_update":"1173728124", "numofapproved":"1", "id":"305"}, {"last_update":"1172507633", "numofapproved":"1", "id":"198"}, {"last_update":"1174301173", "numofapproved":"1", "id":"383"}, {"last_update":"1174899102", "numofapproved":"1", "id":"491"}, {"last_update":"1174301362", "numofapproved":"1", "id":"390"}, {"last_update":"1175254095", "numofapproved":"1", "id":"561"}, {"last_update":"1174037250", "numofapproved":"1", "id":"348"}, {"last_update":"1175865081", "numofapproved":"1", "id":"782"}, {"last_update":"1177591942", "numofapproved":"1", "id":"1046"}, {"last_update":"1177989191", "numofapproved":"1", "id":"1201"}, {"last_update":"1178743279", "numofapproved":"1", "id":"1323"}, {"last_update":"1178876587", "numofapproved":"1", "id":"1357"}, {"last_update":"1179239620", "numofapproved":"1", "id":"1401"}, {"last_update":"1180725458", "numofapproved":"1", "id":"2141"}, {"last_update":"1181205209", "numofapproved":"1", "id":"2421"}, {"last_update":"1181575615", "numofapproved":"1", "id":"2761"}, {"last_update":"1182184775", "numofapproved":"1", "id":"3201"}, {"last_update":"1182963728", "numofapproved":"1", "id":"3661"}, {"last_update":"1178727735", "numofapproved":"1", "id":"1349"}, {"last_update":"1182497720", "numofapproved":"1", "id":"3441"}, {"last_update":"1184381847", "numofapproved":"1", "id":"4881"}, {"last_update":"1184568423", "numofapproved":"1", "id":"4904"}, {"last_update":"1185364813", "numofapproved":"1", "id":"5421"}, {"last_update":"1188043594", "numofapproved":"1", "id":"7441"}, {"last_update":"1188675287", "numofapproved":"1", "id":"7981"}, {"last_update":"1188741594", "numofapproved":"1", "id":"8021"}, {"last_update":"1189144234", "numofapproved":"1", "id":"8561"}, {"last_update":"1189170150", "numofapproved":"1", "id":"8641"}, {"last_update":"1189501508", "numofapproved":"1", "id":"8761"}, {"last_update":"1189811918", "numofapproved":"1", "id":"9041"}, {"last_update":"1189812095", "numofapproved":"1", "id":"9042"}, {"last_update":"1177591716", "numofapproved":"1", "id":"1045"}, {"last_update":"1178040595", "numofapproved":"1", "id":"1203"}, {"last_update":"1182437936", "numofapproved":"1", "id":"3423"}, {"last_update":"1190480042", "numofapproved":"1", "id":"9781"}, {"last_update":"1190821494", "numofapproved":"1", "id":"10361"}, {"last_update":"1190959672", "numofapproved":"1", "id":"10602"}, {"last_update":"1190964023", "numofapproved":"1", "id":"10621"}, {"last_update":"1190991147", "numofapproved":"1", "id":"10721"}, {"last_update":"1190992132", "numofapproved":"1", "id":"10741"}, {"last_update":"1190990410", "numofapproved":"1", "id":"10706"}, {"last_update":"1181667132", "numofapproved":"1", "id":"2861"}, {"last_update":"1183746653", "numofapproved":"1", "id":"4321"}, {"last_update":"1191184539", "numofapproved":"1", "id":"10861"}, {"last_update":"1191490599", "numofapproved":"1", "id":"11261"}, {"last_update":"1191834884", "numofapproved":"1", "id":"11801"}, {"last_update":"1191834899", "numofapproved":"1", "id":"11802"}, {"last_update":"1191940759", "numofapproved":"1", "id":"11961"}, {"last_update":"1179971250", "numofapproved":"1", "id":"1643"}, {"last_update":"1181663618", "numofapproved":"1", "id":"2841"}, {"last_update":"1181932994", "numofapproved":"1", "id":"3102"}, {"last_update":"1182420732", "numofapproved":"1", "id":"3382"}, {"last_update":"1192118127", "numofapproved":"1", "id":"12281"}, {"last_update":"1192222036", "numofapproved":"1", "id":"12481"}, {"last_update":"1192155814", "numofapproved":"1", "id":"12364"}, {"last_update":"1192563924", "numofapproved":"1", "id":"12761"}, {"last_update":"1193124530", "numofapproved":"1", "id":"13441"}, {"last_update":"1193345545", "numofapproved":"1", "id":"13921"}, {"last_update":"1193396927", "numofapproved":"1", "id":"14041"}, {"last_update":"1180015411", "numofapproved":"1", "id":"1651"}, {"last_update":"1180107815", "numofapproved":"1", "id":"1658"}, {"last_update":"1186050394", "numofapproved":"1", "id":"6021"}, {"last_update":"1188519417", "numofapproved":"1", "id":"7841"}, {"last_update":"1193222002", "numofapproved":"1", "id":"13541"}, {"last_update":"1193965081", "numofapproved":"1", "id":"14641"}, {"last_update":"1193660582", "numofapproved":"1", "id":"14381"}, {"last_update":"1194088240", "numofapproved":"1", "id":"14821"}, {"last_update":"1194110475", "numofapproved":"1", "id":"14841"}, {"last_update":"1194246367", "numofapproved":"1", "id":"14902"}, {"last_update":"1194464283", "numofapproved":"1", "id":"15221"}, {"last_update":"1194622250", "numofapproved":"1", "id":"15461"}, {"last_update":"1194635632", "numofapproved":"1", "id":"15601"}, {"last_update":"1179147506", "numofapproved":"1", "id":"1382"}, {"last_update":"1179240025", "numofapproved":"1", "id":"1388"}, {"last_update":"1179748089", "numofapproved":"1", "id":"1561"}, {"last_update":"1179868997", "numofapproved":"1", "id":"1681"}, {"last_update":"1183019667", "numofapproved":"1", "id":"3702"}, {"last_update":"1184531598", "numofapproved":"1", "id":"4902"}, {"last_update":"1187294472", "numofapproved":"1", "id":"6841"}, {"last_update":"1189521494", "numofapproved":"1", "id":"8801"}, {"last_update":"1192726867", "numofapproved":"1", "id":"13081"}, {"last_update":"1193049178", "numofapproved":"1", "id":"13301"}, {"last_update":"1193387050", "numofapproved":"1", "id":"13947"}, {"last_update":"1194277280", "numofapproved":"1", "id":"14981"}, {"last_update":"1179150720", "numofapproved":"1", "id":"1383"}, {"last_update":"1179842104", "numofapproved":"1", "id":"1663"}, {"last_update":"1183766887", "numofapproved":"1", "id":"4341"}, {"last_update":"1185542132", "numofapproved":"1", "id":"5682"}, {"last_update":"1186737114", "numofapproved":"1", "id":"6382"}, {"last_update":"1187015679", "numofapproved":"1", "id":"6521"}, {"last_update":"1190326980", "numofapproved":"1", "id":"9641"}, {"last_update":"1191595711", "numofapproved":"1", "id":"11622"}, {"last_update":"1192106288", "numofapproved":"1", "id":"12221"}, {"last_update":"1192454432", "numofapproved":"1", "id":"12622"}, {"last_update":"1194339640", "numofapproved":"1", "id":"15021"}, {"last_update":"1177758209", "numofapproved":"1", "id":"1181"}, {"last_update":"1179842392", "numofapproved":"1", "id":"1669"}, {"last_update":"1179872870", "numofapproved":"1", "id":"1682"}, {"last_update":"1181233887", "numofapproved":"1", "id":"2541"}, {"last_update":"1182349297", "numofapproved":"1", "id":"3342"}, {"last_update":"1182375421", "numofapproved":"1", "id":"3350"}, {"last_update":"1183485259", "numofapproved":"1", "id":"4081"}, {"last_update":"1184319308", "numofapproved":"1", "id":"4821"}, {"last_update":"1187626648", "numofapproved":"1", "id":"6981"}, {"last_update":"1193153090", "numofapproved":"1", "id":"13502"}, {"last_update":"1194366368", "numofapproved":"1", "id":"15041"}, {"last_update":"1194617018", "numofapproved":"1", "id":"15421"}, {"last_update":"1195230640", "numofapproved":"1", "id":"17021"}, {"last_update":"1179908379", "numofapproved":"1", "id":"1701"}, {"last_update":"1188049228", "numofapproved":"1", "id":"7427"}, {"last_update":"1177581166", "numofapproved":"1", "id":"1061"}, {"last_update":"1187160654", "numofapproved":"1", "id":"6661"}, {"last_update":"1192983992", "numofapproved":"1", "id":"13222"}, {"last_update":"1193388978", "numofapproved":"1", "id":"13954"}, {"last_update":"1194617112", "numofapproved":"1", "id":"15422"}, {"last_update":"1195398876", "numofapproved":"1", "id":"17081"}, {"last_update":"1184262511", "numofapproved":"1", "id":"4801"}, {"last_update":"1192112284", "numofapproved":"1", "id":"12241"}, {"last_update":"1193082767", "numofapproved":"1", "id":"13401"}, {"last_update":"1193179243", "numofapproved":"1", "id":"13526"}, {"last_update":"1178142915", "numofapproved":"1", "id":"1206"}, {"last_update":"1178648333", "numofapproved":"1", "id":"1310"}, {"last_update":"1179279626", "numofapproved":"1", "id":"1391"}, {"last_update":"1182882268", "numofapproved":"1", "id":"3584"}, {"last_update":"1183128448", "numofapproved":"1", "id":"3823"}, {"last_update":"1183377394", "numofapproved":"1", "id":"3941"}, {"last_update":"1188582729", "numofapproved":"1", "id":"7902"}, {"last_update":"1189695063", "numofapproved":"1", "id":"8962"}, {"last_update":"1192001165", "numofapproved":"1", "id":"12001"}, {"last_update":"1192155647", "numofapproved":"1", "id":"12363"}, {"last_update":"1193418304", "numofapproved":"1", "id":"14202"}, {"last_update":"1193632105", "numofapproved":"1", "id":"14341"}, {"last_update":"1194011106", "numofapproved":"1", "id":"14741"}, {"last_update":"1194818628", "numofapproved":"1", "id":"15701"}, {"last_update":"1194875153", "numofapproved":"1", "id":"15861"}, {"last_update":"1194727029", "numofapproved":"1", "id":"15665"}, {"last_update":"1194950210", "numofapproved":"1", "id":"16122"}, {"last_update":"1194976681", "numofapproved":"1", "id":"16241"}, {"last_update":"1194979189", "numofapproved":"1", "id":"16281"}, {"last_update":"1194962224", "numofapproved":"1", "id":"16201"}, {"last_update":"1195046085", "numofapproved":"1", "id":"16481"}, {"last_update":"1195399919", "numofapproved":"1", "id":"17102"}, {"last_update":"1183113736", "numofapproved":"1", "id":"3782"}, {"last_update":"1183114202", "numofapproved":"1", "id":"3783"}, {"last_update":"1189017904", "numofapproved":"1", "id":"8441"}, {"last_update":"1189694944", "numofapproved":"1", "id":"8961"}, {"last_update":"1190766842", "numofapproved":"1", "id":"10181"}, {"last_update":"1190973066", "numofapproved":"1", "id":"10665"}, {"last_update":"1190990264", "numofapproved":"1", "id":"10702"}, {"last_update":"1193043204", "numofapproved":"1", "id":"13281"}, {"last_update":"1194627082", "numofapproved":"1", "id":"15561"}, {"last_update":"1194894589", "numofapproved":"1", "id":"15941"}, {"last_update":"1195485915", "numofapproved":"1", "id":"17281"}, {"last_update":"1195485806", "numofapproved":"1", "id":"17261"}, {"last_update":"1195498836", "numofapproved":"1", "id":"17361"}, {"last_update":"1195514951", "numofapproved":"1", "id":"17421"}, {"last_update":"1183722351", "numofapproved":"1", "id":"4261"}, {"last_update":"1184218083", "numofapproved":"1", "id":"4682"}, {"last_update":"1186848968", "numofapproved":"1", "id":"6441"}, {"last_update":"1187023846", "numofapproved":"1", "id":"6561"}, {"last_update":"1187870812", "numofapproved":"1", "id":"7342"}, {"last_update":"1188657717", "numofapproved":"1", "id":"7961"}, {"last_update":"1190541897", "numofapproved":"1", "id":"9841"}, {"last_update":"1190629135", "numofapproved":"1", "id":"9922"}, {"last_update":"1191226530", "numofapproved":"1", "id":"10922"}, {"last_update":"1191505214", "numofapproved":"1", "id":"11321"}, {"last_update":"1192304524", "numofapproved":"1", "id":"12541"}, {"last_update":"1193948730", "numofapproved":"1", "id":"14601"}, {"last_update":"1194073812", "numofapproved":"1", "id":"14801"}, {"last_update":"1194387224", "numofapproved":"1", "id":"14892"}, {"last_update":"1194464384", "numofapproved":"1", "id":"15223"}, {"last_update":"1194726799", "numofapproved":"1", "id":"15663"}, {"last_update":"1171969969", "numofapproved":"1", "id":"119"}, {"last_update":"1174444717", "numofapproved":"1", "id":"405"}, {"last_update":"1174899431", "numofapproved":"1", "id":"504"}, {"last_update":"1174899204", "numofapproved":"1", "id":"496"}, {"last_update":"1174925591", "numofapproved":"1", "id":"530"}, {"last_update":"1176902523", "numofapproved":"1", "id":"1008"}, {"last_update":"1172765523", "numofapproved":"1", "id":"232"}, {"last_update":"1173315950", "numofapproved":"1", "id":"260"}, {"last_update":"1174899524", "numofapproved":"1", "id":"509"}, {"last_update":"1174300691", "numofapproved":"1", "id":"373"}, {"last_update":"1175502917", "numofapproved":"1", "id":"625"}, {"last_update":"1175601578", "numofapproved":"1", "id":"662"}, {"last_update":"1175608600", "numofapproved":"1", "id":"684"}, {"last_update":"1176755309", "numofapproved":"1", "id":"985"}, {"last_update":"1171386411", "numofapproved":"1", "id":"45"}, {"last_update":"1171800366", "numofapproved":"1", "id":"81"}, {"last_update":"1172847417", "numofapproved":"1", "id":"241"}, {"last_update":"1174734904", "numofapproved":"1", "id":"462"}, {"last_update":"1174735234", "numofapproved":"1", "id":"469"}, {"last_update":"1174735074", "numofapproved":"1", "id":"465"}, {"last_update":"1175267646", "numofapproved":"1", "id":"566"}, {"last_update":"1176331857", "numofapproved":"1", "id":"888"}, {"last_update":"1176387926", "numofapproved":"1", "id":"890"}, {"last_update":"1176458401", "numofapproved":"1", "id":"904"}, {"last_update":"1173088626", "numofapproved":"1", "id":"244"}, {"last_update":"1173109009", "numofapproved":"1", "id":"246"}, {"last_update":"1173671557", "numofapproved":"1", "id":"284"}, {"last_update":"1174927658", "numofapproved":"1", "id":"532"}, {"last_update":"1175592399", "numofapproved":"1", "id":"661"}, {"last_update":"1176480402", "numofapproved":"1", "id":"941"}, {"last_update":"1176561564", "numofapproved":"1", "id":"945"}, {"last_update":"1172218707", "numofapproved":"1", "id":"180"}, {"last_update":"1172771475", "numofapproved":"1", "id":"233"}, {"last_update":"1173267863", "numofapproved":"1", "id":"257"}, {"last_update":"1176493803", "numofapproved":"1", "id":"963"}, {"last_update":"1171449646", "numofapproved":"1", "id":"49"}, {"last_update":"1171471549", "numofapproved":"1", "id":"51"}, {"last_update":"1171800487", "numofapproved":"1", "id":"88"}, {"last_update":"1171800431", "numofapproved":"1", "id":"85"}, {"last_update":"1175502995", "numofapproved":"1", "id":"627"}, {"last_update":"1175712797", "numofapproved":"1", "id":"704"}, {"last_update":"1171122384", "numofapproved":"1", "id":"3"}, {"last_update":"1171380774", "numofapproved":"1", "id":"26"}, {"last_update":"1171904757", "numofapproved":"1", "id":"99"}, {"last_update":"1174300705", "numofapproved":"1", "id":"374"}, {"last_update":"1174924802", "numofapproved":"1", "id":"526"}, {"last_update":"1175935441", "numofapproved":"1", "id":"801"}, {"last_update":"1175610915", "numofapproved":"1", "id":"686"}, {"last_update":"1171977081", "numofapproved":"1", "id":"125"}, {"last_update":"1173165324", "numofapproved":"1", "id":"249"}, {"last_update":"1173888337", "numofapproved":"1", "id":"319"}, {"last_update":"1173889473", "numofapproved":"1", "id":"331"}, {"last_update":"1172180902", "numofapproved":"1", "id":"175"}, {"last_update":"1174058063", "numofapproved":"1", "id":"354"}, {"last_update":"1174300674", "numofapproved":"1", "id":"372"}, {"last_update":"1171886332", "numofapproved":"1", "id":"93"}, {"last_update":"1176731068", "numofapproved":"1", "id":"1003"}, {"last_update":"1178645848", "numofapproved":"1", "id":"1306"}, {"last_update":"1178706683", "numofapproved":"1", "id":"1321"}, {"last_update":"1179240076", "numofapproved":"1", "id":"1406"}, {"last_update":"1180380411", "numofapproved":"1", "id":"1862"}, {"last_update":"1180683561", "numofapproved":"1", "id":"2041"}, {"last_update":"1181229731", "numofapproved":"1", "id":"2521"}, {"last_update":"1182210982", "numofapproved":"1", "id":"3203"}, {"last_update":"1182421105", "numofapproved":"1", "id":"3401"}, {"last_update":"1182199404", "numofapproved":"1", "id":"3202"}, {"last_update":"1182258596", "numofapproved":"1", "id":"3241"}, {"last_update":"1183556842", "numofapproved":"1", "id":"4161"}, {"last_update":"1184146825", "numofapproved":"1", "id":"4601"}, {"last_update":"1184771229", "numofapproved":"1", "id":"4981"}, {"last_update":"1185355415", "numofapproved":"1", "id":"5401"}, {"last_update":"1185377130", "numofapproved":"1", "id":"5481"}, {"last_update":"1185483994", "numofapproved":"1", "id":"5621"}, {"last_update":"1186496707", "numofapproved":"1", "id":"6261"}, {"last_update":"1187704347", "numofapproved":"1", "id":"7001"}, {"last_update":"1187758331", "numofapproved":"1", "id":"7101"}, {"last_update":"1187765716", "numofapproved":"1", "id":"7161"}, {"last_update":"1188284185", "numofapproved":"1", "id":"7581"}, {"last_update":"1188463286", "numofapproved":"1", "id":"7761"}, {"last_update":"1189012058", "numofapproved":"1", "id":"8421"}, {"last_update":"1189814265", "numofapproved":"1", "id":"9061"}, {"last_update":"1180880867", "numofapproved":"1", "id":"2161"}, {"last_update":"1181218244", "numofapproved":"1", "id":"2463"}, {"last_update":"1183515137", "numofapproved":"1", "id":"4141"}, {"last_update":"1183515248", "numofapproved":"1", "id":"4142"}, {"last_update":"1188311100", "numofapproved":"1", "id":"7641"}, {"last_update":"1190011501", "numofapproved":"1", "id":"9201"}, {"last_update":"1190012299", "numofapproved":"1", "id":"9221"}, {"last_update":"1190149196", "numofapproved":"1", "id":"9382"}, {"last_update":"1190202046", "numofapproved":"1", "id":"9461"}, {"last_update":"1190626607", "numofapproved":"1", "id":"9881"}, {"last_update":"1190632230", "numofapproved":"1", "id":"9941"}, {"last_update":"1190660429", "numofapproved":"1", "id":"10002"}, {"last_update":"1190819102", "numofapproved":"1", "id":"10341"}, {"last_update":"1190824319", "numofapproved":"1", "id":"10382"}, {"last_update":"1190825791", "numofapproved":"1", "id":"10402"}, {"last_update":"1190847397", "numofapproved":"1", "id":"10421"}, {"last_update":"1190876679", "numofapproved":"1", "id":"10441"}, {"last_update":"1190918894", "numofapproved":"1", "id":"10541"}, {"last_update":"1190924961", "numofapproved":"1", "id":"10582"}, {"last_update":"1190991179", "numofapproved":"1", "id":"10723"}, {"last_update":"1190663960", "numofapproved":"1", "id":"10042"}, {"last_update":"1191222270", "numofapproved":"1", "id":"10881"}, {"last_update":"1178869580", "numofapproved":"1", "id":"1355"}, {"last_update":"1180054057", "numofapproved":"1", "id":"1655"}, {"last_update":"1180428815", "numofapproved":"1", "id":"1881"}, {"last_update":"1183369278", "numofapproved":"1", "id":"3901"}, {"last_update":"1185018445", "numofapproved":"1", "id":"5163"}, {"last_update":"1185201628", "numofapproved":"1", "id":"5221"}, {"last_update":"1189345395", "numofapproved":"1", "id":"8741"}, {"last_update":"1191406141", "numofapproved":"1", "id":"11041"}, {"last_update":"1191410914", "numofapproved":"1", "id":"11067"}, {"last_update":"1191558362", "numofapproved":"1", "id":"11461"}, {"last_update":"1191584539", "numofapproved":"1", "id":"11541"}, {"last_update":"1191584660", "numofapproved":"1", "id":"11542"}, {"last_update":"1191599491", "numofapproved":"1", "id":"11661"}, {"last_update":"1191813292", "numofapproved":"1", "id":"11781"}, {"last_update":"1191856553", "numofapproved":"1", "id":"11842"}, {"last_update":"1191861142", "numofapproved":"1", "id":"11862"}, {"last_update":"1177509523", "numofapproved":"1", "id":"1041"}, {"last_update":"1190627650", "numofapproved":"1", "id":"9901"}, {"last_update":"1192034749", "numofapproved":"1", "id":"12141"}, {"last_update":"1192165574", "numofapproved":"1", "id":"12401"}, {"last_update":"1192431750", "numofapproved":"1", "id":"12581"}, {"last_update":"1192536591", "numofapproved":"1", "id":"12721"}, {"last_update":"1193035428", "numofapproved":"1", "id":"13261"}, {"last_update":"1193239266", "numofapproved":"1", "id":"13581"}, {"last_update":"1193314455", "numofapproved":"1", "id":"13841"}, {"last_update":"1193333733", "numofapproved":"1", "id":"13901"}, {"last_update":"1193389116", "numofapproved":"1", "id":"14001"}, {"last_update":"1184970339", "numofapproved":"1", "id":"5121"}, {"last_update":"1190892760", "numofapproved":"1", "id":"10481"}, {"last_update":"1192823398", "numofapproved":"1", "id":"13182"}, {"last_update":"1193911671", "numofapproved":"1", "id":"14541"}, {"last_update":"1193916761", "numofapproved":"1", "id":"14543"}, {"last_update":"1194212665", "numofapproved":"1", "id":"14881"}, {"last_update":"1194248205", "numofapproved":"1", "id":"14921"}, {"last_update":"1194513600", "numofapproved":"1", "id":"15110"}, {"last_update":"1194539704", "numofapproved":"1", "id":"15361"}, {"last_update":"1194569643", "numofapproved":"1", "id":"15112"}, {"last_update":"1194619794", "numofapproved":"1", "id":"15441"}, {"last_update":"1194623621", "numofapproved":"1", "id":"15501"}, {"last_update":"1194624477", "numofapproved":"1", "id":"15521"}, {"last_update":"1194635685", "numofapproved":"1", "id":"15602"}, {"last_update":"1179311539", "numofapproved":"1", "id":"1393"}, {"last_update":"1179672561", "numofapproved":"1", "id":"1521"}, {"last_update":"1180712413", "numofapproved":"1", "id":"2101"}, {"last_update":"1181646264", "numofapproved":"1", "id":"2821"}, {"last_update":"1181807696", "numofapproved":"1", "id":"2921"}, {"last_update":"1181824523", "numofapproved":"1", "id":"2942"}, {"last_update":"1181835089", "numofapproved":"1", "id":"2981"}, {"last_update":"1182000147", "numofapproved":"1", "id":"3141"}, {"last_update":"1182952133", "numofapproved":"1", "id":"3641"}, {"last_update":"1188811518", "numofapproved":"1", "id":"8101"}, {"last_update":"1188975549", "numofapproved":"1", "id":"8321"}, {"last_update":"1190122760", "numofapproved":"1", "id":"9301"}, {"last_update":"1190124712", "numofapproved":"1", "id":"9321"}, {"last_update":"1194526560", "numofapproved":"1", "id":"15281"}, {"last_update":"1195149112", "numofapproved":"1", "id":"16821"}, {"last_update":"1179823256", "numofapproved":"1", "id":"1602"}, {"last_update":"1186332011", "numofapproved":"1", "id":"6165"}, {"last_update":"1187263451", "numofapproved":"1", "id":"6781"}, {"last_update":"1190312346", "numofapproved":"1", "id":"9621"}, {"last_update":"1193178728", "numofapproved":"1", "id":"13525"}, {"last_update":"1193908534", "numofapproved":"1", "id":"14524"}, {"last_update":"1194279992", "numofapproved":"1", "id":"15001"}, {"last_update":"1194947169", "numofapproved":"1", "id":"16104"}, {"last_update":"1195139978", "numofapproved":"1", "id":"16801"}, {"last_update":"1195152323", "numofapproved":"1", "id":"16841"}, {"last_update":"1188086146", "numofapproved":"1", "id":"7428"}, {"last_update":"1192143475", "numofapproved":"1", "id":"12341"}, {"last_update":"1192529949", "numofapproved":"1", "id":"12664"}, {"last_update":"1192721072", "numofapproved":"1", "id":"13041"}, {"last_update":"1193844156", "numofapproved":"1", "id":"14501"}, {"last_update":"1177597683", "numofapproved":"1", "id":"1063"}, {"last_update":"1180975406", "numofapproved":"1", "id":"2184"}, {"last_update":"1184681435", "numofapproved":"1", "id":"4914"}, {"last_update":"1187596457", "numofapproved":"1", "id":"6922"}, {"last_update":"1190661113", "numofapproved":"1", "id":"10003"}, {"last_update":"1192721357", "numofapproved":"1", "id":"13042"}, {"last_update":"1193130120", "numofapproved":"1", "id":"13461"}, {"last_update":"1193388868", "numofapproved":"1", "id":"13953"}, {"last_update":"1194861534", "numofapproved":"1", "id":"15821"}, {"last_update":"1182357592", "numofapproved":"1", "id":"3345"}, {"last_update":"1183722862", "numofapproved":"1", "id":"4262"}, {"last_update":"1186066354", "numofapproved":"1", "id":"6041"}, {"last_update":"1192698982", "numofapproved":"1", "id":"12981"}, {"last_update":"1181237191", "numofapproved":"1", "id":"2561"}, {"last_update":"1184569090", "numofapproved":"1", "id":"4906"}, {"last_update":"1185397555", "numofapproved":"1", "id":"5501"}, {"last_update":"1185541935", "numofapproved":"1", "id":"5681"}, {"last_update":"1193385832", "numofapproved":"1", "id":"13941"}, {"last_update":"1185482424", "numofapproved":"1", "id":"5581"}, {"last_update":"1195508796", "numofapproved":"1", "id":"17401"}, {"last_update":"1178718386", "numofapproved":"1", "id":"1347"}, {"last_update":"1178788813", "numofapproved":"1", "id":"1351"}, {"last_update":"1178877332", "numofapproved":"1", "id":"1358"}, {"last_update":"1183208679", "numofapproved":"1", "id":"3861"}, {"last_update":"1187885439", "numofapproved":"1", "id":"7347"}, {"last_update":"1188985190", "numofapproved":"1", "id":"8341"}, {"last_update":"1189687132", "numofapproved":"1", "id":"8941"}, {"last_update":"1189864330", "numofapproved":"1", "id":"9121"}, {"last_update":"1190990605", "numofapproved":"1", "id":"10709"}, {"last_update":"1192634449", "numofapproved":"1", "id":"12861"}, {"last_update":"1194723756", "numofapproved":"1", "id":"15641"}, {"last_update":"1194792428", "numofapproved":"1", "id":"15682"}, {"last_update":"1194725734", "numofapproved":"1", "id":"15661"}, {"last_update":"1194945618", "numofapproved":"1", "id":"16061"}, {"last_update":"1194946006", "numofapproved":"1", "id":"16081"}, {"last_update":"1194949774", "numofapproved":"1", "id":"16121"}, {"last_update":"1194950925", "numofapproved":"1", "id":"16126"}, {"last_update":"1194979238", "numofapproved":"1", "id":"16282"}, {"last_update":"1195051013", "numofapproved":"1", "id":"16543"}, {"last_update":"1195050956", "numofapproved":"1", "id":"16542"}, {"last_update":"1195047036", "numofapproved":"1", "id":"16501"}, {"last_update":"1195221919", "numofapproved":"1", "id":"16942"}, {"last_update":"1178035892", "numofapproved":"1", "id":"1221"}, {"last_update":"1178570265", "numofapproved":"1", "id":"1302"}, {"last_update":"1178811921", "numofapproved":"1", "id":"1354"}, {"last_update":"1182344326", "numofapproved":"1", "id":"3321"}, {"last_update":"1184999048", "numofapproved":"1", "id":"5141"}, {"last_update":"1188994511", "numofapproved":"1", "id":"8361"}, {"last_update":"1189161726", "numofapproved":"1", "id":"8601"}, {"last_update":"1190500875", "numofapproved":"1", "id":"9803"}, {"last_update":"1190817424", "numofapproved":"1", "id":"10321"}, {"last_update":"1191327796", "numofapproved":"1", "id":"11001"}, {"last_update":"1191410544", "numofapproved":"1", "id":"11062"}, {"last_update":"1192009739", "numofapproved":"1", "id":"12062"}, {"last_update":"1193973669", "numofapproved":"1", "id":"14662"}, {"last_update":"1194035149", "numofapproved":"1", "id":"14783"}, {"last_update":"1194465519", "numofapproved":"1", "id":"15106"}, {"last_update":"1194464336", "numofapproved":"1", "id":"15222"}, {"last_update":"1194861398", "numofapproved":"1", "id":"15802"}, {"last_update":"1194950791", "numofapproved":"1", "id":"16125"}, {"last_update":"1195501394", "numofapproved":"1", "id":"17381"}, {"last_update":"1195546583", "numofapproved":"1", "id":"17461"}, {"last_update":"1177607652", "numofapproved":"1", "id":"1048"}, {"last_update":"1182349136", "numofapproved":"1", "id":"3322"}, {"last_update":"1184217665", "numofapproved":"1", "id":"4681"}, {"last_update":"1185510733", "numofapproved":"1", "id":"5641"}, {"last_update":"1187875988", "numofapproved":"1", "id":"7345"}, {"last_update":"1188384227", "numofapproved":"1", "id":"7701"}, {"last_update":"1188935650", "numofapproved":"1", "id":"8261"}, {"last_update":"1188951982", "numofapproved":"1", "id":"8301"}, {"last_update":"1190391010", "numofapproved":"1", "id":"9701"}, {"last_update":"1191169581", "numofapproved":"1", "id":"10841"}, {"last_update":"1194435269", "numofapproved":"1", "id":"15101"}, {"last_update":"1171800457", "numofapproved":"1", "id":"86"}, {"last_update":"1171968036", "numofapproved":"1", "id":"116"}, {"last_update":"1171984640", "numofapproved":"1", "id":"129"}, {"last_update":"1171987101", "numofapproved":"1", "id":"130"}, {"last_update":"1172588327", "numofapproved":"1", "id":"213"}, {"last_update":"1173736730", "numofapproved":"1", "id":"306"}, {"last_update":"1174735009", "numofapproved":"1", "id":"463"}, {"last_update":"1172314484", "numofapproved":"1", "id":"192"}, {"last_update":"1172580739", "numofapproved":"1", "id":"212"}, {"last_update":"1173889335", "numofapproved":"1", "id":"328"}, {"last_update":"1171799339", "numofapproved":"1", "id":"79"}, {"last_update":"1171882669", "numofapproved":"1", "id":"91"}, {"last_update":"1172561300", "numofapproved":"1", "id":"207"}, {"last_update":"1172565919", "numofapproved":"1", "id":"209"}, {"last_update":"1172600401", "numofapproved":"1", "id":"217"}, {"last_update":"1174040553", "numofapproved":"1", "id":"350"}, {"last_update":"1174300376", "numofapproved":"1", "id":"365"}, {"last_update":"1171800419", "numofapproved":"1", "id":"84"}, {"last_update":"1171800471", "numofapproved":"1", "id":"87"}, {"last_update":"1171904826", "numofapproved":"1", "id":"102"}, {"last_update":"1171962248", "numofapproved":"1", "id":"110"}, {"last_update":"1171968056", "numofapproved":"1", "id":"117"}, {"last_update":"1172180757", "numofapproved":"1", "id":"174"}, {"last_update":"1172249286", "numofapproved":"1", "id":"186"}, {"last_update":"1172331355", "numofapproved":"1", "id":"194"}, {"last_update":"1172838799", "numofapproved":"1", "id":"235"}, {"last_update":"1173839361", "numofapproved":"1", "id":"316"}, {"last_update":"1176141087", "numofapproved":"1", "id":"809"}, {"last_update":"1176293168", "numofapproved":"1", "id":"827"}, {"last_update":"1176314927", "numofapproved":"1", "id":"887"}, {"last_update":"1172147490", "numofapproved":"1", "id":"169"}, {"last_update":"1172673371", "numofapproved":"1", "id":"225"}, {"last_update":"1175021309", "numofapproved":"1", "id":"539"}, {"last_update":"1175719394", "numofapproved":"1", "id":"708"}, {"last_update":"1175797177", "numofapproved":"1", "id":"741"}, {"last_update":"1175797204", "numofapproved":"1", "id":"761"}, {"last_update":"1173888948", "numofapproved":"1", "id":"323"}, {"last_update":"1171050355", "numofapproved":"1", "id":"1"}, {"last_update":"1171904868", "numofapproved":"1", "id":"104"}, {"last_update":"1174301476", "numofapproved":"1", "id":"392"}, {"last_update":"1174396679", "numofapproved":"1", "id":"401"}, {"last_update":"1174735025", "numofapproved":"1", "id":"464"}, {"last_update":"1171894147", "numofapproved":"1", "id":"94"}, {"last_update":"1172226240", "numofapproved":"1", "id":"181"}, {"last_update":"1172442130", "numofapproved":"1", "id":"195"}, {"last_update":"1174300588", "numofapproved":"1", "id":"370"}, {"last_update":"1174899082", "numofapproved":"1", "id":"490"}, {"last_update":"1174899309", "numofapproved":"1", "id":"501"}, {"last_update":"1173724444", "numofapproved":"1", "id":"304"}, {"last_update":"1176314883", "numofapproved":"1", "id":"886"}, {"last_update":"1173284377", "numofapproved":"1", "id":"259"}, {"last_update":"1172244974", "numofapproved":"1", "id":"184"}, {"last_update":"1173825356", "numofapproved":"1", "id":"315"}, {"last_update":"1174898980", "numofapproved":"1", "id":"485"}, {"last_update":"1175713133", "numofapproved":"1", "id":"706"}, {"last_update":"1175872869", "numofapproved":"1", "id":"784"}, {"last_update":"1174301161", "numofapproved":"1", "id":"380"}, {"last_update":"1176710519", "numofapproved":"1", "id":"1002"}, {"last_update":"1176776871", "numofapproved":"1", "id":"1006"}, {"last_update":"1176383102", "numofapproved":"1", "id":"901"}, {"last_update":"1176391153", "numofapproved":"1", "id":"902"}, {"last_update":"1176562039", "numofapproved":"1", "id":"946"}, {"last_update":"1175713172", "numofapproved":"1", "id":"668"}, {"last_update":"1178045208", "numofapproved":"1", "id":"1204"}, {"last_update":"1178648231", "numofapproved":"1", "id":"1307"}, {"last_update":"1178876638", "numofapproved":"1", "id":"1362"}, {"last_update":"1181120419", "numofapproved":"1", "id":"2341"}, {"last_update":"1181217997", "numofapproved":"1", "id":"2462"}, {"last_update":"1181292688", "numofapproved":"1", "id":"2622"}, {"last_update":"1182246090", "numofapproved":"1", "id":"3205"}, {"last_update":"1182982710", "numofapproved":"1", "id":"3681"}, {"last_update":"1177496084", "numofapproved":"1", "id":"1021"}, {"last_update":"1177496190", "numofapproved":"1", "id":"1022"}, {"last_update":"1178310654", "numofapproved":"1", "id":"1261"}, {"last_update":"1182861963", "numofapproved":"1", "id":"3582"}, {"last_update":"1183392466", "numofapproved":"1", "id":"3981"}, {"last_update":"1183971409", "numofapproved":"1", "id":"4404"}, {"last_update":"1183984082", "numofapproved":"1", "id":"4421"}, {"last_update":"1184101764", "numofapproved":"1", "id":"4581"}, {"last_update":"1185805036", "numofapproved":"1", "id":"5821"}, {"last_update":"1186071563", "numofapproved":"1", "id":"6061"}, {"last_update":"1186331614", "numofapproved":"1", "id":"6221"}, {"last_update":"1187103429", "numofapproved":"1", "id":"6623"}, {"last_update":"1187359405", "numofapproved":"1", "id":"6901"}, {"last_update":"1187764462", "numofapproved":"1", "id":"7121"}, {"last_update":"1187765742", "numofapproved":"1", "id":"7181"}, {"last_update":"1187821663", "numofapproved":"1", "id":"7281"}, {"last_update":"1187851593", "numofapproved":"1", "id":"7301"}, {"last_update":"1188829369", "numofapproved":"1", "id":"8141"}, {"last_update":"1189006834", "numofapproved":"1", "id":"8401"}, {"last_update":"1189656411", "numofapproved":"1", "id":"8901"}, {"last_update":"1181824325", "numofapproved":"1", "id":"2961"}, {"last_update":"1184699326", "numofapproved":"1", "id":"4922"}, {"last_update":"1185981618", "numofapproved":"1", "id":"5981"}, {"last_update":"1186476979", "numofapproved":"1", "id":"6169"}, {"last_update":"1186501212", "numofapproved":"1", "id":"6301"}, {"last_update":"1187111728", "numofapproved":"1", "id":"6624"}, {"last_update":"1187275194", "numofapproved":"1", "id":"6821"}, {"last_update":"1190232587", "numofapproved":"1", "id":"9501"}, {"last_update":"1190379779", "numofapproved":"1", "id":"9661"}, {"last_update":"1190500551", "numofapproved":"1", "id":"9801"}, {"last_update":"1190555711", "numofapproved":"1", "id":"9861"}, {"last_update":"1190664200", "numofapproved":"1", "id":"10061"}, {"last_update":"1190662067", "numofapproved":"1", "id":"10021"}, {"last_update":"1190887692", "numofapproved":"1", "id":"10461"}, {"last_update":"1190887880", "numofapproved":"1", "id":"10462"}, {"last_update":"1190924576", "numofapproved":"1", "id":"10581"}, {"last_update":"1190990748", "numofapproved":"1", "id":"10713"}, {"last_update":"1190990297", "numofapproved":"1", "id":"10703"}, {"last_update":"1182792178", "numofapproved":"1", "id":"3541"}, {"last_update":"1189505682", "numofapproved":"1", "id":"8781"}, {"last_update":"1191410630", "numofapproved":"1", "id":"11081"}, {"last_update":"1191431148", "numofapproved":"1", "id":"11141"}, {"last_update":"1191446393", "numofapproved":"1", "id":"11181"}, {"last_update":"1191559326", "numofapproved":"1", "id":"11481"}, {"last_update":"1191860159", "numofapproved":"1", "id":"11861"}, {"last_update":"1191933842", "numofapproved":"1", "id":"11901"}, {"last_update":"1181765760", "numofapproved":"1", "id":"2901"}, {"last_update":"1187098770", "numofapproved":"1", "id":"6622"}, {"last_update":"1192155125", "numofapproved":"1", "id":"12382"}, {"last_update":"1192449036", "numofapproved":"1", "id":"12601"}, {"last_update":"1192604489", "numofapproved":"1", "id":"12781"}, {"last_update":"1193265229", "numofapproved":"1", "id":"13681"}, {"last_update":"1193304550", "numofapproved":"1", "id":"13781"}, {"last_update":"1193401945", "numofapproved":"1", "id":"14101"}, {"last_update":"1193305327", "numofapproved":"1", "id":"13801"}, {"last_update":"1179912412", "numofapproved":"1", "id":"1722"}, {"last_update":"1188295203", "numofapproved":"1", "id":"7621"}, {"last_update":"1188580008", "numofapproved":"1", "id":"7881"}, {"last_update":"1189115708", "numofapproved":"1", "id":"8521"}, {"last_update":"1193864375", "numofapproved":"1", "id":"14522"}, {"last_update":"1193973963", "numofapproved":"1", "id":"14666"}, {"last_update":"1194003054", "numofapproved":"1", "id":"14701"}, {"last_update":"1194262755", "numofapproved":"1", "id":"14885"}, {"last_update":"1194262860", "numofapproved":"1", "id":"14886"}, {"last_update":"1194366475", "numofapproved":"1", "id":"15042"}, {"last_update":"1194505568", "numofapproved":"1", "id":"15108"}, {"last_update":"1194507434", "numofapproved":"1", "id":"15109"}, {"last_update":"1194625505", "numofapproved":"1", "id":"15542"}, {"last_update":"1194635569", "numofapproved":"1", "id":"15583"}, {"last_update":"1179319405", "numofapproved":"1", "id":"1394"}, {"last_update":"1179409867", "numofapproved":"1", "id":"1441"}, {"last_update":"1179431647", "numofapproved":"1", "id":"1481"}, {"last_update":"1179842302", "numofapproved":"1", "id":"1667"}, {"last_update":"1180710254", "numofapproved":"1", "id":"2081"}, {"last_update":"1181855583", "numofapproved":"1", "id":"3041"}, {"last_update":"1182100211", "numofapproved":"1", "id":"3182"}, {"last_update":"1183377220", "numofapproved":"1", "id":"3921"}, {"last_update":"1184677615", "numofapproved":"1", "id":"4910"}, {"last_update":"1184679060", "numofapproved":"1", "id":"4911"}, {"last_update":"1184679348", "numofapproved":"1", "id":"4912"}, {"last_update":"1184749371", "numofapproved":"1", "id":"4943"}, {"last_update":"1186734180", "numofapproved":"1", "id":"6381"}, {"last_update":"1187012463", "numofapproved":"1", "id":"6501"}, {"last_update":"1187209404", "numofapproved":"1", "id":"6741"}, {"last_update":"1192687257", "numofapproved":"1", "id":"12941"}, {"last_update":"1193385868", "numofapproved":"1", "id":"13942"}, {"last_update":"1193386346", "numofapproved":"1", "id":"13943"}, {"last_update":"1194937571", "numofapproved":"1", "id":"16042"}, {"last_update":"1194855975", "numofapproved":"1", "id":"15761"}, {"last_update":"1194960221", "numofapproved":"1", "id":"16161"}, {"last_update":"1184058679", "numofapproved":"1", "id":"4541"}, {"last_update":"1185865315", "numofapproved":"1", "id":"5842"}, {"last_update":"1187178780", "numofapproved":"1", "id":"6681"}, {"last_update":"1194884625", "numofapproved":"1", "id":"15921"}, {"last_update":"1195134032", "numofapproved":"1", "id":"16721"}, {"last_update":"1195164570", "numofapproved":"1", "id":"16901"}, {"last_update":"1182336429", "numofapproved":"1", "id":"3301"}, {"last_update":"1182415670", "numofapproved":"1", "id":"3353"}, {"last_update":"1184575801", "numofapproved":"1", "id":"4907"}, {"last_update":"1185483718", "numofapproved":"1", "id":"5601"}, {"last_update":"1186402874", "numofapproved":"1", "id":"6166"}, {"last_update":"1186750969", "numofapproved":"1", "id":"6383"}, {"last_update":"1192725360", "numofapproved":"1", "id":"13061"}, {"last_update":"1193314911", "numofapproved":"1", "id":"13822"}, {"last_update":"1183448275", "numofapproved":"1", "id":"4062"}, {"last_update":"1187321039", "numofapproved":"1", "id":"6861"}, {"last_update":"1188287578", "numofapproved":"1", "id":"7601"}, {"last_update":"1194464420", "numofapproved":"1", "id":"15224"}, {"last_update":"1195139641", "numofapproved":"1", "id":"16781"}, {"last_update":"1186147124", "numofapproved":"1", "id":"6107"}, {"last_update":"1188821750", "numofapproved":"1", "id":"8122"}, {"last_update":"1192531864", "numofapproved":"1", "id":"12665"}, {"last_update":"1192984220", "numofapproved":"1", "id":"13223"}, {"last_update":"1195225246", "numofapproved":"1", "id":"16982"}, {"last_update":"1182410787", "numofapproved":"1", "id":"3351"}, {"last_update":"1184531419", "numofapproved":"1", "id":"4901"}, {"last_update":"1188801472", "numofapproved":"1", "id":"8081"}, {"last_update":"1192524288", "numofapproved":"1", "id":"12661"}, {"last_update":"1180950691", "numofapproved":"1", "id":"2181"}, {"last_update":"1184016732", "numofapproved":"1", "id":"4501"}, {"last_update":"1186074085", "numofapproved":"1", "id":"6081"}, {"last_update":"1194937650", "numofapproved":"1", "id":"16043"}, {"last_update":"1182937178", "numofapproved":"1", "id":"3623"}, {"last_update":"1191419601", "numofapproved":"1", "id":"11101"}, {"last_update":"1191856562", "numofapproved":"1", "id":"11843"}, {"last_update":"1192525042", "numofapproved":"1", "id":"12681"}, {"last_update":"1194625494", "numofapproved":"1", "id":"15541"}, {"last_update":"1194982850", "numofapproved":"1", "id":"16361"}, {"last_update":"1194989219", "numofapproved":"1", "id":"16401"}, {"last_update":"1195066723", "numofapproved":"1", "id":"16641"}, {"last_update":"1183971226", "numofapproved":"1", "id":"4403"}, {"last_update":"1185526866", "numofapproved":"1", "id":"5661"}, {"last_update":"1185741495", "numofapproved":"1", "id":"5741"}, {"last_update":"1185905429", "numofapproved":"1", "id":"5881"}, {"last_update":"1186137969", "numofapproved":"1", "id":"6104"}, {"last_update":"1189267536", "numofapproved":"1", "id":"8701"}, {"last_update":"1190115042", "numofapproved":"1", "id":"9261"}, {"last_update":"1190664258", "numofapproved":"1", "id":"10062"}, {"last_update":"1190774949", "numofapproved":"1", "id":"10201"}, {"last_update":"1190965042", "numofapproved":"1", "id":"10641"}, {"last_update":"1191493379", "numofapproved":"1", "id":"11301"}, {"last_update":"1191578051", "numofapproved":"1", "id":"11501"}, {"last_update":"1192188840", "numofapproved":"1", "id":"12421"}, {"last_update":"1194000252", "numofapproved":"1", "id":"14682"}, {"last_update":"1194622556", "numofapproved":"1", "id":"15462"}, {"last_update":"1194981068", "numofapproved":"1", "id":"16341"}, {"last_update":"1185795733", "numofapproved":"1", "id":"5782"}, {"last_update":"1186646854", "numofapproved":"1", "id":"6341"}, {"last_update":"1187087291", "numofapproved":"1", "id":"6621"}, {"last_update":"1187951800", "numofapproved":"1", "id":"7401"}, {"last_update":"1189170373", "numofapproved":"1", "id":"8642"}, {"last_update":"1191007934", "numofapproved":"1", "id":"10781"}, {"last_update":"1190985695", "numofapproved":"1", "id":"10681"}, {"last_update":"1192009758", "numofapproved":"1", "id":"12063"}, {"last_update":"1193062543", "numofapproved":"1", "id":"13321"}, {"last_update":"1194950304", "numofapproved":"1", "id":"16123"}, {"last_update":"1171882085", "numofapproved":"1", "id":"90"}, {"last_update":"1171962264", "numofapproved":"1", "id":"111"}, {"last_update":"1172646556", "numofapproved":"1", "id":"219"}, {"last_update":"1174040139", "numofapproved":"1", "id":"349"}, {"last_update":"1174059263", "numofapproved":"1", "id":"355"}, {"last_update":"1174899063", "numofapproved":"1", "id":"489"}, {"last_update":"1173797557", "numofapproved":"1", "id":"310"}, {"last_update":"1174735191", "numofapproved":"1", "id":"468"}, {"last_update":"1174899259", "numofapproved":"1", "id":"499"}, {"last_update":"1174899354", "numofapproved":"1", "id":"502"}, {"last_update":"1175254120", "numofapproved":"1", "id":"562"}, {"last_update":"1171126391", "numofapproved":"1", "id":"4"}, {"last_update":"1171800381", "numofapproved":"1", "id":"82"}, {"last_update":"1171799224", "numofapproved":"1", "id":"75"}, {"last_update":"1171972550", "numofapproved":"1", "id":"123"}, {"last_update":"1174301165", "numofapproved":"1", "id":"381"}, {"last_update":"1171904847", "numofapproved":"1", "id":"103"}, {"last_update":"1172260956", "numofapproved":"1", "id":"190"}, {"last_update":"1172803368", "numofapproved":"1", "id":"234"}, {"last_update":"1173199576", "numofapproved":"1", "id":"250"}, {"last_update":"1173206201", "numofapproved":"1", "id":"252"}, {"last_update":"1175258941", "numofapproved":"1", "id":"563"}, {"last_update":"1176232231", "numofapproved":"1", "id":"825"}, {"last_update":"1176475088", "numofapproved":"1", "id":"921"}, {"last_update":"1172082181", "numofapproved":"1", "id":"166"}, {"last_update":"1172595205", "numofapproved":"1", "id":"216"}, {"last_update":"1174898892", "numofapproved":"1", "id":"481"}, {"last_update":"1174899696", "numofapproved":"1", "id":"518"}, {"last_update":"1174924777", "numofapproved":"1", "id":"525"}, {"last_update":"1175598588", "numofapproved":"1", "id":"682"}, {"last_update":"1175602572", "numofapproved":"1", "id":"683"}, {"last_update":"1175707879", "numofapproved":"1", "id":"666"}, {"last_update":"1175710528", "numofapproved":"1", "id":"703"}, {"last_update":"1175715728", "numofapproved":"1", "id":"707"}, {"last_update":"1176137267", "numofapproved":"1", "id":"806"}, {"last_update":"1176306491", "numofapproved":"1", "id":"883"}, {"last_update":"1172069972", "numofapproved":"1", "id":"134"}, {"last_update":"1173889144", "numofapproved":"1", "id":"324"}, {"last_update":"1175502804", "numofapproved":"1", "id":"623"}, {"last_update":"1175772530", "numofapproved":"1", "id":"711"}, {"last_update":"1176297526", "numofapproved":"1", "id":"861"}, {"last_update":"1171445818", "numofapproved":"1", "id":"47"}, {"last_update":"1171884505", "numofapproved":"1", "id":"92"}, {"last_update":"1172250708", "numofapproved":"1", "id":"187"}, {"last_update":"1173749631", "numofapproved":"1", "id":"307"}, {"last_update":"1173889164", "numofapproved":"1", "id":"325"}, {"last_update":"1174301168", "numofapproved":"1", "id":"382"}, {"last_update":"1171904807", "numofapproved":"1", "id":"101"}, {"last_update":"1171970405", "numofapproved":"1", "id":"120"}, {"last_update":"1172218677", "numofapproved":"1", "id":"179"}, {"last_update":"1173125028", "numofapproved":"1", "id":"248"}, {"last_update":"1171978122", "numofapproved":"1", "id":"126"}, {"last_update":"1172676736", "numofapproved":"1", "id":"226"}, {"last_update":"1173975473", "numofapproved":"1", "id":"344"}, {"last_update":"1172072582", "numofapproved":"1", "id":"165"}, {"last_update":"1173888774", "numofapproved":"1", "id":"322"}, {"last_update":"1174560347", "numofapproved":"1", "id":"422"}, {"last_update":"1174899242", "numofapproved":"1", "id":"498"}, {"last_update":"1174735110", "numofapproved":"1", "id":"466"}, {"last_update":"1176735630", "numofapproved":"1", "id":"1004"}, {"last_update":"1175725931", "numofapproved":"1", "id":"670"}, {"last_update":"1176498072", "numofapproved":"1", "id":"944"}, {"last_update":"1178264233", "numofapproved":"1", "id":"1241"}, {"last_update":"1178746727", "numofapproved":"1", "id":"1350"}, {"last_update":"1178798992", "numofapproved":"1", "id":"1352"}, {"last_update":"1180011647", "numofapproved":"1", "id":"1649"}, {"last_update":"1180430823", "numofapproved":"1", "id":"1901"}, {"last_update":"1180649952", "numofapproved":"1", "id":"2021"}, {"last_update":"1180966506", "numofapproved":"1", "id":"2183"}, {"last_update":"1180987142", "numofapproved":"1", "id":"2241"}, {"last_update":"1181127788", "numofapproved":"1", "id":"2322"}, {"last_update":"1181217668", "numofapproved":"1", "id":"2461"}, {"last_update":"1182789542", "numofapproved":"1", "id":"3522"}, {"last_update":"1182851714", "numofapproved":"1", "id":"3581"}, {"last_update":"1179268837", "numofapproved":"1", "id":"1407"}, {"last_update":"1179999486", "numofapproved":"1", "id":"1645"}, {"last_update":"1180019568", "numofapproved":"1", "id":"1653"}, {"last_update":"1180082061", "numofapproved":"1", "id":"1821"}, {"last_update":"1184181871", "numofapproved":"1", "id":"4642"}, {"last_update":"1184251955", "numofapproved":"1", "id":"4741"}, {"last_update":"1184346893", "numofapproved":"1", "id":"4841"}, {"last_update":"1184773981", "numofapproved":"1", "id":"5001"}, {"last_update":"1185272905", "numofapproved":"1", "id":"5281"}, {"last_update":"1185484083", "numofapproved":"1", "id":"5622"}, {"last_update":"1185897961", "numofapproved":"1", "id":"5861"}, {"last_update":"1186951708", "numofapproved":"1", "id":"6462"}, {"last_update":"1187596311", "numofapproved":"1", "id":"6941"}, {"last_update":"1187766852", "numofapproved":"1", "id":"7201"}, {"last_update":"1188158133", "numofapproved":"1", "id":"7481"}, {"last_update":"1188233835", "numofapproved":"1", "id":"7501"}, {"last_update":"1188269273", "numofapproved":"1", "id":"7561"}, {"last_update":"1177672684", "numofapproved":"1", "id":"1141"}, {"last_update":"1178042016", "numofapproved":"1", "id":"1222"}, {"last_update":"1181646022", "numofapproved":"1", "id":"2801"}, {"last_update":"1181853920", "numofapproved":"1", "id":"3021"}, {"last_update":"1183715836", "numofapproved":"1", "id":"4241"}, {"last_update":"1183726859", "numofapproved":"1", "id":"4281"}, {"last_update":"1189860355", "numofapproved":"1", "id":"9101"}, {"last_update":"1189871747", "numofapproved":"1", "id":"9141"}, {"last_update":"1190380660", "numofapproved":"1", "id":"9681"}, {"last_update":"1190510808", "numofapproved":"1", "id":"9821"}, {"last_update":"1190542013", "numofapproved":"1", "id":"9843"}, {"last_update":"1190665412", "numofapproved":"1", "id":"10081"}, {"last_update":"1190299519", "numofapproved":"1", "id":"9601"}, {"last_update":"1191410594", "numofapproved":"1", "id":"11063"}, {"last_update":"1191505786", "numofapproved":"1", "id":"11341"}, {"last_update":"1191583652", "numofapproved":"1", "id":"11522"}, {"last_update":"1191599712", "numofapproved":"1", "id":"11681"}, {"last_update":"1191602931", "numofapproved":"1", "id":"11721"}, {"last_update":"1191762572", "numofapproved":"1", "id":"11761"}, {"last_update":"1191856256", "numofapproved":"1", "id":"11841"}, {"last_update":"1191937041", "numofapproved":"1", "id":"11921"}, {"last_update":"1179325639", "numofapproved":"1", "id":"1409"}, {"last_update":"1179912165", "numofapproved":"1", "id":"1721"}, {"last_update":"1181119430", "numofapproved":"1", "id":"2321"}, {"last_update":"1184696743", "numofapproved":"1", "id":"4921"}, {"last_update":"1192154847", "numofapproved":"1", "id":"12361"}, {"last_update":"1192237071", "numofapproved":"1", "id":"12501"}, {"last_update":"1178637394", "numofapproved":"1", "id":"1304"}, {"last_update":"1178716778", "numofapproved":"1", "id":"1344"}, {"last_update":"1182937057", "numofapproved":"1", "id":"3622"}, {"last_update":"1183113642", "numofapproved":"1", "id":"3781"}, {"last_update":"1183995467", "numofapproved":"1", "id":"4461"}, {"last_update":"1184223331", "numofapproved":"1", "id":"4721"}, {"last_update":"1190990692", "numofapproved":"1", "id":"10711"}, {"last_update":"1193269310", "numofapproved":"1", "id":"13761"}, {"last_update":"1193735756", "numofapproved":"1", "id":"14441"}, {"last_update":"1194635738", "numofapproved":"1", "id":"15603"}, {"last_update":"1194901721", "numofapproved":"1", "id":"15961"}, {"last_update":"1194949951", "numofapproved":"1", "id":"16141"}, {"last_update":"1194960695", "numofapproved":"1", "id":"16182"}, {"last_update":"1194973974", "numofapproved":"1", "id":"16221"}, {"last_update":"1194946810", "numofapproved":"1", "id":"16102"}, {"last_update":"1194977452", "numofapproved":"1", "id":"16261"}, {"last_update":"1195040385", "numofapproved":"1", "id":"16461"}, {"last_update":"1195053483", "numofapproved":"1", "id":"16561"}, {"last_update":"1195053518", "numofapproved":"1", "id":"16562"}, {"last_update":"1195218698", "numofapproved":"1", "id":"16921"}, {"last_update":"1195225049", "numofapproved":"1", "id":"16961"}, {"last_update":"1195164270", "numofapproved":"1", "id":"16881"}, {"last_update":"1195080947", "numofapproved":"1", "id":"16681"}, {"last_update":"1195469884", "numofapproved":"1", "id":"17181"}, {"last_update":"1185314804", "numofapproved":"1", "id":"5381"}, {"last_update":"1188401767", "numofapproved":"1", "id":"7721"}, {"last_update":"1190286841", "numofapproved":"1", "id":"9582"}, {"last_update":"1190733096", "numofapproved":"1", "id":"10141"}, {"last_update":"1190847451", "numofapproved":"1", "id":"10422"}, {"last_update":"1190990526", "numofapproved":"1", "id":"10707"}, {"last_update":"1192009711", "numofapproved":"1", "id":"12061"}, {"last_update":"1192155478", "numofapproved":"1", "id":"12362"}, {"last_update":"1192468382", "numofapproved":"1", "id":"12641"}, {"last_update":"1193332032", "numofapproved":"1", "id":"13881"}, {"last_update":"1195497290", "numofapproved":"1", "id":"17321"}, {"last_update":"1195519935", "numofapproved":"1", "id":"17441"}, {"last_update":"1195549826", "numofapproved":"1", "id":"17521"}, {"last_update":"1177668131", "numofapproved":"1", "id":"1101"}, {"last_update":"1186835348", "numofapproved":"1", "id":"6421"}, {"last_update":"1191057903", "numofapproved":"1", "id":"10802"}, {"last_update":"1193973906", "numofapproved":"1", "id":"14665"}, {"last_update":"1171904780", "numofapproved":"1", "id":"100"}, {"last_update":"1172677750", "numofapproved":"1", "id":"227"}, {"last_update":"1172686704", "numofapproved":"1", "id":"229"}, {"last_update":"1173101684", "numofapproved":"1", "id":"245"}, {"last_update":"1173466151", "numofapproved":"1", "id":"282"}, {"last_update":"1174301263", "numofapproved":"1", "id":"386"}, {"last_update":"1174302366", "numofapproved":"1", "id":"399"}, {"last_update":"1174501294", "numofapproved":"1", "id":"421"}, {"last_update":"1174899635", "numofapproved":"1", "id":"515"}, {"last_update":"1174924556", "numofapproved":"1", "id":"523"}, {"last_update":"1175141200", "numofapproved":"1", "id":"541"}, {"last_update":"1171799271", "numofapproved":"1", "id":"76"}, {"last_update":"1171900163", "numofapproved":"1", "id":"97"}, {"last_update":"1174301267", "numofapproved":"1", "id":"387"}, {"last_update":"1174735156", "numofapproved":"1", "id":"467"}, {"last_update":"1174899569", "numofapproved":"1", "id":"512"}, {"last_update":"1174926970", "numofapproved":"1", "id":"531"}, {"last_update":"1175502757", "numofapproved":"1", "id":"602"}, {"last_update":"1175603425", "numofapproved":"1", "id":"663"}, {"last_update":"1176194967", "numofapproved":"1", "id":"822"}, {"last_update":"1171800398", "numofapproved":"1", "id":"83"}, {"last_update":"1171968376", "numofapproved":"1", "id":"118"}, {"last_update":"1172070063", "numofapproved":"1", "id":"135"}, {"last_update":"1173821159", "numofapproved":"1", "id":"314"}, {"last_update":"1176559052", "numofapproved":"1", "id":"964"}, {"last_update":"1171299245", "numofapproved":"1", "id":"23"}, {"last_update":"1171535160", "numofapproved":"1", "id":"57"}, {"last_update":"1171564542", "numofapproved":"1", "id":"65"}, {"last_update":"1172646592", "numofapproved":"1", "id":"220"}, {"last_update":"1174899489", "numofapproved":"1", "id":"507"}, {"last_update":"1174924890", "numofapproved":"1", "id":"528"}, {"last_update":"1175687005", "numofapproved":"1", "id":"701"}, {"last_update":"1176132888", "numofapproved":"1", "id":"805"}, {"last_update":"1171286610", "numofapproved":"1", "id":"21"}, {"last_update":"1172184441", "numofapproved":"1", "id":"176"}, {"last_update":"1172187221", "numofapproved":"1", "id":"178"}, {"last_update":"1173386668", "numofapproved":"1", "id":"261"}, {"last_update":"1173809115", "numofapproved":"1", "id":"312"}, {"last_update":"1175609126", "numofapproved":"1", "id":"685"}, {"last_update":"1175791369", "numofapproved":"1", "id":"712"}, {"last_update":"1176480434", "numofapproved":"1", "id":"942"}, {"last_update":"1171503567", "numofapproved":"1", "id":"56"}, {"last_update":"1171799204", "numofapproved":"1", "id":"74"}, {"last_update":"1172236765", "numofapproved":"1", "id":"183"}, {"last_update":"1175598013", "numofapproved":"1", "id":"681"}, {"last_update":"1175610956", "numofapproved":"1", "id":"687"}, {"last_update":"1175725436", "numofapproved":"1", "id":"710"}, {"last_update":"1171905052", "numofapproved":"1", "id":"105"}, {"last_update":"1172268920", "numofapproved":"1", "id":"191"}, {"last_update":"1173264110", "numofapproved":"1", "id":"256"}, {"last_update":"1173889179", "numofapproved":"1", "id":"326"}, {"last_update":"1174301066", "numofapproved":"1", "id":"378"}, {"last_update":"1174300399", "numofapproved":"1", "id":"366"}, {"last_update":"1174387980", "numofapproved":"1", "id":"400"}, {"last_update":"1176823766", "numofapproved":"1", "id":"1007"}, {"last_update":"1171970585", "numofapproved":"1", "id":"122"}, {"last_update":"1172071500", "numofapproved":"1", "id":"145"}, {"last_update":"1172580279", "numofapproved":"1", "id":"211"}, {"last_update":"1172658493", "numofapproved":"1", "id":"221"}, {"last_update":"1174301611", "numofapproved":"1", "id":"397"}, {"last_update":"1176900132", "numofapproved":"1", "id":"989"}, {"last_update":"1171965754", "numofapproved":"1", "id":"114"}, {"last_update":"1173797482", "numofapproved":"1", "id":"309"}, {"last_update":"1174300513", "numofapproved":"1", "id":"367"}, {"last_update":"1174301493", "numofapproved":"1", "id":"395"}, {"last_update":"1174899124", "numofapproved":"1", "id":"492"}, {"last_update":"1174899677", "numofapproved":"1", "id":"517"}, {"last_update":"1174924235", "numofapproved":"1", "id":"522"}, {"last_update":"1174925568", "numofapproved":"1", "id":"529"}, {"last_update":"1174933088", "numofapproved":"1", "id":"533"}, {"last_update":"1174933338", "numofapproved":"1", "id":"538"}, {"last_update":"1174044629", "numofapproved":"1", "id":"352"}, {"last_update":"1175713207", "numofapproved":"1", "id":"669"}, {"last_update":"1178339569", "numofapproved":"1", "id":"1262"}, {"last_update":"1178611427", "numofapproved":"1", "id":"1303"}, {"last_update":"1178707269", "numofapproved":"1", "id":"1341"}, {"last_update":"1179411388", "numofapproved":"1", "id":"1461"}, {"last_update":"1180000879", "numofapproved":"1", "id":"1648"}, {"last_update":"1180097993", "numofapproved":"1", "id":"1657"}, {"last_update":"1180107947", "numofapproved":"1", "id":"1659"}, {"last_update":"1180515935", "numofapproved":"1", "id":"1922"}, {"last_update":"1180712418", "numofapproved":"1", "id":"2102"}, {"last_update":"1180731895", "numofapproved":"1", "id":"2063"}, {"last_update":"1180731763", "numofapproved":"1", "id":"2143"}, {"last_update":"1180951519", "numofapproved":"1", "id":"2201"}, {"last_update":"1180954763", "numofapproved":"1", "id":"2182"}, {"last_update":"1181134185", "numofapproved":"1", "id":"2361"}, {"last_update":"1181206368", "numofapproved":"1", "id":"2441"}, {"last_update":"1181207556", "numofapproved":"1", "id":"2442"}, {"last_update":"1183065868", "numofapproved":"1", "id":"3741"}, {"last_update":"1183124436", "numofapproved":"1", "id":"3822"}, {"last_update":"1183118631", "numofapproved":"1", "id":"3802"}, {"last_update":"1183515629", "numofapproved":"1", "id":"4144"}, {"last_update":"1184169495", "numofapproved":"1", "id":"4621"}, {"last_update":"1184777700", "numofapproved":"1", "id":"5021"}, {"last_update":"1185371099", "numofapproved":"1", "id":"5441"}, {"last_update":"1185460060", "numofapproved":"1", "id":"5521"}, {"last_update":"1185462514", "numofapproved":"1", "id":"5541"}, {"last_update":"1185573050", "numofapproved":"1", "id":"5721"}, {"last_update":"1185795586", "numofapproved":"1", "id":"5781"}, {"last_update":"1185962181", "numofapproved":"1", "id":"5901"}, {"last_update":"1185987024", "numofapproved":"1", "id":"6001"}, {"last_update":"1186138150", "numofapproved":"1", "id":"6105"}, {"last_update":"1186500528", "numofapproved":"1", "id":"6281"}, {"last_update":"1187765075", "numofapproved":"1", "id":"7141"}, {"last_update":"1188158263", "numofapproved":"1", "id":"7482"}, {"last_update":"1189094579", "numofapproved":"1", "id":"8461"}, {"last_update":"1189327635", "numofapproved":"1", "id":"8721"}, {"last_update":"1182356521", "numofapproved":"1", "id":"3344"}, {"last_update":"1185017921", "numofapproved":"1", "id":"5161"}, {"last_update":"1185271167", "numofapproved":"1", "id":"5261"}, {"last_update":"1190663796", "numofapproved":"1", "id":"10041"}, {"last_update":"1190726728", "numofapproved":"1", "id":"10121"}, {"last_update":"1190801144", "numofapproved":"1", "id":"10241"}, {"last_update":"1190894441", "numofapproved":"1", "id":"10502"}, {"last_update":"1190973098", "numofapproved":"1", "id":"10667"}, {"last_update":"1190925124", "numofapproved":"1", "id":"10584"}, {"last_update":"1191249884", "numofapproved":"1", "id":"10961"}, {"last_update":"1187732431", "numofapproved":"1", "id":"7081"}, {"last_update":"1189259179", "numofapproved":"1", "id":"8681"}, {"last_update":"1191446517", "numofapproved":"1", "id":"11183"}, {"last_update":"1191510643", "numofapproved":"1", "id":"11381"}, {"last_update":"1191529640", "numofapproved":"1", "id":"11421"}, {"last_update":"1191588726", "numofapproved":"1", "id":"11602"}, {"last_update":"1191903050", "numofapproved":"1", "id":"11881"}, {"last_update":"1181218459", "numofapproved":"1", "id":"2464"}, {"last_update":"1187024536", "numofapproved":"1", "id":"6581"}, {"last_update":"1192009094", "numofapproved":"1", "id":"12041"}, {"last_update":"1192064048", "numofapproved":"1", "id":"12183"}, {"last_update":"1192061973", "numofapproved":"1", "id":"12181"}, {"last_update":"1193026780", "numofapproved":"1", "id":"13241"}, {"last_update":"1193416409", "numofapproved":"1", "id":"14161"}, {"last_update":"1186992495", "numofapproved":"1", "id":"6481"}, {"last_update":"1191410811", "numofapproved":"1", "id":"11066"}, {"last_update":"1193440748", "numofapproved":"1", "id":"14241"}, {"last_update":"1194252005", "numofapproved":"1", "id":"14884"}, {"last_update":"1194362364", "numofapproved":"1", "id":"14889"}, {"last_update":"1179240103", "numofapproved":"1", "id":"1389"}, {"last_update":"1181812262", "numofapproved":"1", "id":"2922"}, {"last_update":"1182093916", "numofapproved":"1", "id":"3181"}, {"last_update":"1182767688", "numofapproved":"1", "id":"3501"}, {"last_update":"1184181747", "numofapproved":"1", "id":"4661"}, {"last_update":"1186505570", "numofapproved":"1", "id":"6170"}, {"last_update":"1186751068", "numofapproved":"1", "id":"6384"}, {"last_update":"1187558925", "numofapproved":"1", "id":"6921"}, {"last_update":"1188037477", "numofapproved":"1", "id":"7424"}, {"last_update":"1194937530", "numofapproved":"1", "id":"16041"}, {"last_update":"1179754250", "numofapproved":"1", "id":"1562"}, {"last_update":"1183416194", "numofapproved":"1", "id":"4021"}, {"last_update":"1185835616", "numofapproved":"1", "id":"5841"}, {"last_update":"1192731190", "numofapproved":"1", "id":"13141"}, {"last_update":"1193178120", "numofapproved":"1", "id":"13523"}, {"last_update":"1193844805", "numofapproved":"1", "id":"14503"}, {"last_update":"1193909242", "numofapproved":"1", "id":"14525"}, {"last_update":"1195474767", "numofapproved":"1", "id":"17221"}, {"last_update":"1177690781", "numofapproved":"1", "id":"1142"}, {"last_update":"1185373614", "numofapproved":"1", "id":"5461"}, {"last_update":"1192520088", "numofapproved":"1", "id":"12624"}, {"last_update":"1193194444", "numofapproved":"1", "id":"13527"}, {"last_update":"1193387684", "numofapproved":"1", "id":"13950"}, {"last_update":"1193388786", "numofapproved":"1", "id":"13952"}, {"last_update":"1194616895", "numofapproved":"1", "id":"15401"}, {"last_update":"1195034817", "numofapproved":"1", "id":"16441"}, {"last_update":"1183107374", "numofapproved":"1", "id":"3761"}, {"last_update":"1183515040", "numofapproved":"1", "id":"4121"}, {"last_update":"1184744160", "numofapproved":"1", "id":"4942"}, {"last_update":"1192094830", "numofapproved":"1", "id":"12201"}, {"last_update":"1193314411", "numofapproved":"1", "id":"13821"}, {"last_update":"1193391901", "numofapproved":"1", "id":"13957"}, {"last_update":"1193399824", "numofapproved":"1", "id":"14043"}, {"last_update":"1194450353", "numofapproved":"1", "id":"15181"}, {"last_update":"1194474719", "numofapproved":"1", "id":"15241"}, {"last_update":"1194622799", "numofapproved":"1", "id":"15481"}, {"last_update":"1194880827", "numofapproved":"1", "id":"15901"}, {"last_update":"1182363929", "numofapproved":"1", "id":"3347"}, {"last_update":"1182952243", "numofapproved":"1", "id":"3642"}, {"last_update":"1183386876", "numofapproved":"1", "id":"3962"}, {"last_update":"1193178314", "numofapproved":"1", "id":"13524"}, {"last_update":"1195376577", "numofapproved":"1", "id":"17061"}, {"last_update":"1179832847", "numofapproved":"1", "id":"1621"}, {"last_update":"1184053269", "numofapproved":"1", "id":"4521"}, {"last_update":"1185024744", "numofapproved":"1", "id":"5181"}, {"last_update":"1186130324", "numofapproved":"1", "id":"6101"}, {"last_update":"1192529640", "numofapproved":"1", "id":"12662"}, {"last_update":"1193158482", "numofapproved":"1", "id":"13521"}, {"last_update":"1194247788", "numofapproved":"1", "id":"14883"}, {"last_update":"1182363717", "numofapproved":"1", "id":"3346"}, {"last_update":"1193386824", "numofapproved":"1", "id":"13944"}, {"last_update":"1193844655", "numofapproved":"1", "id":"14502"}, {"last_update":"1180732326", "numofapproved":"1", "id":"2064"}, {"last_update":"1182247493", "numofapproved":"1", "id":"3222"}, {"last_update":"1183515318", "numofapproved":"1", "id":"4143"}, {"last_update":"1184840285", "numofapproved":"1", "id":"5061"}, {"last_update":"1188458821", "numofapproved":"1", "id":"7741"}, {"last_update":"1188919582", "numofapproved":"1", "id":"8241"}, {"last_update":"1190990231", "numofapproved":"1", "id":"10701"}, {"last_update":"1190990557", "numofapproved":"1", "id":"10708"}, {"last_update":"1191583611", "numofapproved":"1", "id":"11521"}, {"last_update":"1192031263", "numofapproved":"1", "id":"12102"}, {"last_update":"1192431349", "numofapproved":"1", "id":"12563"}, {"last_update":"1192608972", "numofapproved":"1", "id":"12801"}, {"last_update":"1193244196", "numofapproved":"1", "id":"13641"}, {"last_update":"1193733530", "numofapproved":"1", "id":"14422"}, {"last_update":"1194988770", "numofapproved":"1", "id":"16381"}, {"last_update":"1195050890", "numofapproved":"1", "id":"16541"}, {"last_update":"1195047262", "numofapproved":"1", "id":"16502"}, {"last_update":"1195221672", "numofapproved":"1", "id":"16941"}, {"last_update":"1195400016", "numofapproved":"1", "id":"17103"}, {"last_update":"1178716622", "numofapproved":"1", "id":"1343"}, {"last_update":"1183563126", "numofapproved":"1", "id":"4181"}, {"last_update":"1183970953", "numofapproved":"1", "id":"4402"}, {"last_update":"1190149151", "numofapproved":"1", "id":"9381"}, {"last_update":"1190628937", "numofapproved":"1", "id":"9921"}, {"last_update":"1190908511", "numofapproved":"1", "id":"10521"}, {"last_update":"1191365468", "numofapproved":"1", "id":"11021"}, {"last_update":"1192431054", "numofapproved":"1", "id":"12561"}, {"last_update":"1188938163", "numofapproved":"1", "id":"8281"}, {"last_update":"1192155298", "numofapproved":"1", "id":"12383"}, {"last_update":"1193223714", "numofapproved":"1", "id":"13561"}, {"last_update":"1171799359", "numofapproved":"1", "id":"80"}, {"last_update":"1171962550", "numofapproved":"1", "id":"112"}, {"last_update":"1171965210", "numofapproved":"1", "id":"113"}, {"last_update":"1171980888", "numofapproved":"1", "id":"128"}, {"last_update":"1174299174", "numofapproved":"1", "id":"361"}, {"last_update":"1174301053", "numofapproved":"1", "id":"376"}, {"last_update":"1174899661", "numofapproved":"1", "id":"516"}, {"last_update":"1172646493", "numofapproved":"1", "id":"218"}, {"last_update":"1174899018", "numofapproved":"1", "id":"487"}, {"last_update":"1175091201", "numofapproved":"1", "id":"540"}, {"last_update":"1175267243", "numofapproved":"1", "id":"564"}, {"last_update":"1176293117", "numofapproved":"1", "id":"826"}, {"last_update":"1171602873", "numofapproved":"1", "id":"67"}, {"last_update":"1172568714", "numofapproved":"1", "id":"210"}, {"last_update":"1174300556", "numofapproved":"1", "id":"369"}, {"last_update":"1174301614", "numofapproved":"1", "id":"398"}, {"last_update":"1174429050", "numofapproved":"1", "id":"404"}, {"last_update":"1175547821", "numofapproved":"1", "id":"641"}, {"last_update":"1175696551", "numofapproved":"1", "id":"702"}, {"last_update":"1176223342", "numofapproved":"1", "id":"823"}, {"last_update":"1176459077", "numofapproved":"1", "id":"905"}, {"last_update":"1172169117", "numofapproved":"1", "id":"172"}, {"last_update":"1172259821", "numofapproved":"1", "id":"189"}, {"last_update":"1172847347", "numofapproved":"1", "id":"237"}, {"last_update":"1176485274", "numofapproved":"1", "id":"961"}, {"last_update":"1176739199", "numofapproved":"1", "id":"983"}, {"last_update":"1171710108", "numofapproved":"1", "id":"72"}, {"last_update":"1172147854", "numofapproved":"1", "id":"170"}, {"last_update":"1172178657", "numofapproved":"1", "id":"173"}, {"last_update":"1174933210", "numofapproved":"1", "id":"535"}, {"last_update":"1175502973", "numofapproved":"1", "id":"626"}, {"last_update":"1172071610", "numofapproved":"1", "id":"146"}, {"last_update":"1172847402", "numofapproved":"1", "id":"240"}, {"last_update":"1173282970", "numofapproved":"1", "id":"258"}, {"last_update":"1175502729", "numofapproved":"1", "id":"621"}, {"last_update":"1173889203", "numofapproved":"1", "id":"327"}, {"last_update":"1174301604", "numofapproved":"1", "id":"396"}, {"last_update":"1176738556", "numofapproved":"1", "id":"1005"}, {"last_update":"1171287066", "numofapproved":"1", "id":"22"}, {"last_update":"1171388951", "numofapproved":"1", "id":"46"}, {"last_update":"1171645099", "numofapproved":"1", "id":"70"}, {"last_update":"1174301489", "numofapproved":"1", "id":"394"}, {"last_update":"1176109438", "numofapproved":"1", "id":"804"}, {"last_update":"1173203622", "numofapproved":"1", "id":"251"}, {"last_update":"1174300337", "numofapproved":"1", "id":"364"}, {"last_update":"1174898999", "numofapproved":"1", "id":"486"}, {"last_update":"1174899221", "numofapproved":"1", "id":"497"}, {"last_update":"1174899505", "numofapproved":"1", "id":"508"}, {"last_update":"1171905996", "numofapproved":"1", "id":"106"}, {"last_update":"1172003938", "numofapproved":"1", "id":"131"}, {"last_update":"1172134183", "numofapproved":"1", "id":"167"}, {"last_update":"1178550080", "numofapproved":"1", "id":"1301"}, {"last_update":"1178718229", "numofapproved":"1", "id":"1346"}, {"last_update":"1178725187", "numofapproved":"1", "id":"1322"}, {"last_update":"1179302219", "numofapproved":"1", "id":"1392"}, {"last_update":"1180015260", "numofapproved":"1", "id":"1650"}, {"last_update":"1180088452", "numofapproved":"1", "id":"1656"}, {"last_update":"1180719498", "numofapproved":"1", "id":"2121"}, {"last_update":"1180731930", "numofapproved":"1", "id":"2145"}, {"last_update":"1180731601", "numofapproved":"1", "id":"2142"}, {"last_update":"1181034337", "numofapproved":"1", "id":"2281"}, {"last_update":"1181222113", "numofapproved":"1", "id":"2501"}, {"last_update":"1181254636", "numofapproved":"1", "id":"2601"}, {"last_update":"1181578682", "numofapproved":"1", "id":"2762"}, {"last_update":"1181731051", "numofapproved":"1", "id":"2881"}, {"last_update":"1177673345", "numofapproved":"1", "id":"1162"}, {"last_update":"1183741680", "numofapproved":"1", "id":"4301"}, {"last_update":"1183988623", "numofapproved":"1", "id":"4441"}, {"last_update":"1184217947", "numofapproved":"1", "id":"4701"}, {"last_update":"1186260146", "numofapproved":"1", "id":"6181"}, {"last_update":"1186289860", "numofapproved":"1", "id":"6163"}, {"last_update":"1186235477", "numofapproved":"1", "id":"6161"}, {"last_update":"1186508996", "numofapproved":"1", "id":"6171"}, {"last_update":"1187626570", "numofapproved":"1", "id":"6961"}, {"last_update":"1187713755", "numofapproved":"1", "id":"7041"}, {"last_update":"1187769208", "numofapproved":"1", "id":"7222"}, {"last_update":"1187856827", "numofapproved":"1", "id":"7341"}, {"last_update":"1188053850", "numofapproved":"1", "id":"7461"}, {"last_update":"1188264856", "numofapproved":"1", "id":"7541"}, {"last_update":"1188319841", "numofapproved":"1", "id":"7681"}, {"last_update":"1188582632", "numofapproved":"1", "id":"7901"}, {"last_update":"1188734330", "numofapproved":"1", "id":"8001"}, {"last_update":"1189003562", "numofapproved":"1", "id":"8381"}, {"last_update":"1179787121", "numofapproved":"1", "id":"1581"}, {"last_update":"1181998896", "numofapproved":"1", "id":"3121"}, {"last_update":"1182274782", "numofapproved":"1", "id":"3261"}, {"last_update":"1186350397", "numofapproved":"1", "id":"6241"}, {"last_update":"1187354512", "numofapproved":"1", "id":"6881"}, {"last_update":"1188918086", "numofapproved":"1", "id":"8221"}, {"last_update":"1190392989", "numofapproved":"1", "id":"9721"}, {"last_update":"1190925022", "numofapproved":"1", "id":"10583"}, {"last_update":"1190959571", "numofapproved":"1", "id":"10601"}, {"last_update":"1190990357", "numofapproved":"1", "id":"10705"}, {"last_update":"1190990656", "numofapproved":"1", "id":"10710"}, {"last_update":"1191226364", "numofapproved":"1", "id":"10921"}, {"last_update":"1180011741", "numofapproved":"1", "id":"1761"}, {"last_update":"1180533694", "numofapproved":"1", "id":"1961"}, {"last_update":"1180731839", "numofapproved":"1", "id":"2144"}, {"last_update":"1181461876", "numofapproved":"1", "id":"2681"}, {"last_update":"1181855690", "numofapproved":"1", "id":"3061"}, {"last_update":"1189537687", "numofapproved":"1", "id":"8821"}, {"last_update":"1189937430", "numofapproved":"1", "id":"9161"}, {"last_update":"1190803903", "numofapproved":"1", "id":"10261"}, {"last_update":"1190973051", "numofapproved":"1", "id":"10664"}, {"last_update":"1191410739", "numofapproved":"1", "id":"11064"}, {"last_update":"1191426697", "numofapproved":"1", "id":"11121"}, {"last_update":"1191446459", "numofapproved":"1", "id":"11182"}, {"last_update":"1191450891", "numofapproved":"1", "id":"11201"}, {"last_update":"1191550000", "numofapproved":"1", "id":"11441"}, {"last_update":"1191588714", "numofapproved":"1", "id":"11601"}, {"last_update":"1191596815", "numofapproved":"1", "id":"11641"}, {"last_update":"1191647971", "numofapproved":"1", "id":"11741"}, {"last_update":"1191949660", "numofapproved":"1", "id":"11981"}, {"last_update":"1180641844", "numofapproved":"1", "id":"2001"}, {"last_update":"1188319710", "numofapproved":"1", "id":"7661"}, {"last_update":"1189169640", "numofapproved":"1", "id":"8621"}, {"last_update":"1192028009", "numofapproved":"1", "id":"12081"}, {"last_update":"1192116783", "numofapproved":"1", "id":"12261"}, {"last_update":"1192558715", "numofapproved":"1", "id":"12741"}, {"last_update":"1192727702", "numofapproved":"1", "id":"13101"}, {"last_update":"1193035517", "numofapproved":"1", "id":"13262"}, {"last_update":"1193080239", "numofapproved":"1", "id":"13381"}, {"last_update":"1193268912", "numofapproved":"1", "id":"13722"}, {"last_update":"1193386894", "numofapproved":"1", "id":"13946"}, {"last_update":"1193388087", "numofapproved":"1", "id":"13982"}, {"last_update":"1179841973", "numofapproved":"1", "id":"1642"}, {"last_update":"1179842066", "numofapproved":"1", "id":"1662"}, {"last_update":"1185971695", "numofapproved":"1", "id":"5941"}, {"last_update":"1186137440", "numofapproved":"1", "id":"6103"}, {"last_update":"1192823224", "numofapproved":"1", "id":"13181"}, {"last_update":"1193921116", "numofapproved":"1", "id":"14581"}, {"last_update":"1193918035", "numofapproved":"1", "id":"14544"}, {"last_update":"1193973759", "numofapproved":"1", "id":"14663"}, {"last_update":"1194004166", "numofapproved":"1", "id":"14721"}, {"last_update":"1194020795", "numofapproved":"1", "id":"14761"}, {"last_update":"1194021069", "numofapproved":"1", "id":"14781"}, {"last_update":"1194283444", "numofapproved":"1", "id":"14887"}, {"last_update":"1194436909", "numofapproved":"1", "id":"15141"}, {"last_update":"1194538247", "numofapproved":"1", "id":"15341"}, {"last_update":"1180031440", "numofapproved":"1", "id":"1801"}, {"last_update":"1181823965", "numofapproved":"1", "id":"2941"}, {"last_update":"1182846565", "numofapproved":"1", "id":"3561"}, {"last_update":"1185872587", "numofapproved":"1", "id":"5843"}, {"last_update":"1186472951", "numofapproved":"1", "id":"6168"}, {"last_update":"1189937606", "numofapproved":"1", "id":"9181"}, {"last_update":"1193389026", "numofapproved":"1", "id":"13955"}, {"last_update":"1192130592", "numofapproved":"1", "id":"12321"}, {"last_update":"1194387386", "numofapproved":"1", "id":"15061"}, {"last_update":"1179336536", "numofapproved":"1", "id":"1396"}, {"last_update":"1182280246", "numofapproved":"1", "id":"3281"}, {"last_update":"1183394591", "numofapproved":"1", "id":"4001"}, {"last_update":"1184677502", "numofapproved":"1", "id":"4909"}, {"last_update":"1186144184", "numofapproved":"1", "id":"6106"}, {"last_update":"1187191683", "numofapproved":"1", "id":"6701"}, {"last_update":"1193909594", "numofapproved":"1", "id":"14527"}, {"last_update":"1194435747", "numofapproved":"1", "id":"15121"}, {"last_update":"1184252278", "numofapproved":"1", "id":"4761"}, {"last_update":"1194854996", "numofapproved":"1", "id":"15721"}, {"last_update":"1194937730", "numofapproved":"1", "id":"16045"}, {"last_update":"1193076864", "numofapproved":"1", "id":"13361"}, {"last_update":"1194904087", "numofapproved":"1", "id":"15981"}, {"last_update":"1181853751", "numofapproved":"1", "id":"3001"}, {"last_update":"1182075529", "numofapproved":"1", "id":"3161"}, {"last_update":"1184883226", "numofapproved":"1", "id":"5081"}, {"last_update":"1186136013", "numofapproved":"1", "id":"6102"}, {"last_update":"1193147983", "numofapproved":"1", "id":"13481"}, {"last_update":"1194532658", "numofapproved":"1", "id":"15301"}, {"last_update":"1194937763", "numofapproved":"1", "id":"16046"}, {"last_update":"1195225183", "numofapproved":"1", "id":"16981"}, {"last_update":"1180616624", "numofapproved":"1", "id":"1981"}, {"last_update":"1183019269", "numofapproved":"1", "id":"3701"}, {"last_update":"1188656338", "numofapproved":"1", "id":"7941"}, {"last_update":"1178799062", "numofapproved":"1", "id":"1353"}, {"last_update":"1178905809", "numofapproved":"1", "id":"1360"}, {"last_update":"1179311575", "numofapproved":"1", "id":"1408"}, {"last_update":"1182507595", "numofapproved":"1", "id":"3461"}, {"last_update":"1184254004", "numofapproved":"1", "id":"4781"}, {"last_update":"1187938257", "numofapproved":"1", "id":"7381"}, {"last_update":"1188473327", "numofapproved":"1", "id":"7801"}, {"last_update":"1189102174", "numofapproved":"1", "id":"8481"}, {"last_update":"1191419747", "numofapproved":"1", "id":"11102"}, {"last_update":"1193389169", "numofapproved":"1", "id":"14002"}, {"last_update":"1194440930", "numofapproved":"1", "id":"15102"}, {"last_update":"1194855848", "numofapproved":"1", "id":"15741"}, {"last_update":"1194862162", "numofapproved":"1", "id":"15841"}, {"last_update":"1194923605", "numofapproved":"1", "id":"16021"}, {"last_update":"1194950051", "numofapproved":"1", "id":"16142"}, {"last_update":"1194960554", "numofapproved":"1", "id":"16181"}, {"last_update":"1194988868", "numofapproved":"1", "id":"16382"}, {"last_update":"1195058276", "numofapproved":"1", "id":"16601"}, {"last_update":"1195469960", "numofapproved":"1", "id":"17201"}, {"last_update":"1178648361", "numofapproved":"1", "id":"1311"}, {"last_update":"1183970840", "numofapproved":"1", "id":"4401"}, {"last_update":"1184838534", "numofapproved":"1", "id":"5041"}, {"last_update":"1190745858", "numofapproved":"1", "id":"10161"}, {"last_update":"1191587968", "numofapproved":"1", "id":"11581"}, {"last_update":"1189773687", "numofapproved":"1", "id":"9021"}, {"last_update":"1192612866", "numofapproved":"1", "id":"12804"}, {"last_update":"1193746024", "numofapproved":"1", "id":"14461"}, {"last_update":"1193918117", "numofapproved":"1", "id":"14561"}, {"last_update":"1194981013", "numofapproved":"1", "id":"16321"}, {"last_update":"1195546695", "numofapproved":"1", "id":"17481"}, {"last_update":"1177592107", "numofapproved":"1", "id":"1047"}, {"last_update":"1183569612", "numofapproved":"1", "id":"4221"}, {"last_update":"1186770649", "numofapproved":"1", "id":"6401"}, {"last_update":"1187707518", "numofapproved":"1", "id":"7021"}, {"last_update":"1187769297", "numofapproved":"1", "id":"7223"}, {"last_update":"1187798945", "numofapproved":"1", "id":"7241"}, {"last_update":"1187820883", "numofapproved":"1", "id":"7261"}, {"last_update":"1190286816", "numofapproved":"1", "id":"9581"}, {"last_update":"1190541964", "numofapproved":"1", "id":"9842"}, {"last_update":"1190500569", "numofapproved":"1", "id":"9802"}, {"last_update":"1190800190", "numofapproved":"1", "id":"10222"}, {"last_update":"1190965460", "numofapproved":"1", "id":"10642"}, {"last_update":"1192120899", "numofapproved":"1", "id":"12301"}, {"last_update":"1193265675", "numofapproved":"1", "id":"13701"}, {"last_update":"1194508196", "numofapproved":"1", "id":"15261"}, {"last_update":"1172503197", "numofapproved":"1", "id":"196"}, {"last_update":"1172847366", "numofapproved":"1", "id":"238"}, {"last_update":"1173975764", "numofapproved":"1", "id":"347"}, {"last_update":"1174301010", "numofapproved":"1", "id":"375"}, {"last_update":"1174899614", "numofapproved":"1", "id":"514"}, {"last_update":"1174924853", "numofapproved":"1", "id":"527"}, {"last_update":"1175270318", "numofapproved":"1", "id":"567"}, {"last_update":"1174933246", "numofapproved":"1", "id":"536"}, {"last_update":"1176369900", "numofapproved":"1", "id":"889"}, {"last_update":"1171102836", "numofapproved":"1", "id":"2"}, {"last_update":"1171970451", "numofapproved":"1", "id":"121"}, {"last_update":"1174898953", "numofapproved":"1", "id":"484"}, {"last_update":"1175610845", "numofapproved":"1", "id":"664"}, {"last_update":"1176313569", "numofapproved":"1", "id":"885"}, {"last_update":"1171878648", "numofapproved":"1", "id":"89"}, {"last_update":"1171897268", "numofapproved":"1", "id":"96"}, {"last_update":"1172326187", "numofapproved":"1", "id":"193"}, {"last_update":"1176106905", "numofapproved":"1", "id":"802"}, {"last_update":"1176389540", "numofapproved":"1", "id":"891"}, {"last_update":"1171318806", "numofapproved":"1", "id":"24"}, {"last_update":"1171601548", "numofapproved":"1", "id":"66"}, {"last_update":"1172148331", "numofapproved":"1", "id":"171"}, {"last_update":"1172686680", "numofapproved":"1", "id":"228"}, {"last_update":"1173793572", "numofapproved":"1", "id":"308"}, {"last_update":"1174899594", "numofapproved":"1", "id":"513"}, {"last_update":"1174898936", "numofapproved":"1", "id":"483"}, {"last_update":"1175502773", "numofapproved":"1", "id":"622"}, {"last_update":"1175722537", "numofapproved":"1", "id":"709"}, {"last_update":"1175764633", "numofapproved":"1", "id":"672"}, {"last_update":"1175797156", "numofapproved":"1", "id":"721"}, {"last_update":"1175899070", "numofapproved":"1", "id":"785"}, {"last_update":"1176106959", "numofapproved":"1", "id":"803"}, {"last_update":"1176228460", "numofapproved":"1", "id":"824"}, {"last_update":"1176488163", "numofapproved":"1", "id":"962"}, {"last_update":"1172068869", "numofapproved":"1", "id":"133"}, {"last_update":"1172847381", "numofapproved":"1", "id":"239"}, {"last_update":"1173888657", "numofapproved":"1", "id":"320"}, {"last_update":"1171449446", "numofapproved":"1", "id":"48"}, {"last_update":"1175287424", "numofapproved":"1", "id":"581"}, {"last_update":"1175502897", "numofapproved":"1", "id":"624"}, {"last_update":"1175503020", "numofapproved":"1", "id":"605"}, {"last_update":"1172848367", "numofapproved":"1", "id":"243"}, {"last_update":"1174301060", "numofapproved":"1", "id":"377"}, {"last_update":"1176824481", "numofapproved":"1", "id":"986"}, {"last_update":"1171275893", "numofapproved":"1", "id":"6"}, {"last_update":"1172546216", "numofapproved":"1", "id":"206"}, {"last_update":"1175502705", "numofapproved":"1", "id":"601"}, {"last_update":"1173962671", "numofapproved":"1", "id":"341"}, {"last_update":"1173975403", "numofapproved":"1", "id":"342"}, {"last_update":"1173816295", "numofapproved":"1", "id":"313"}, {"last_update":"1174301256", "numofapproved":"1", "id":"384"}, {"last_update":"1174933293", "numofapproved":"1", "id":"537"}, {"last_update":"1176899419", "numofapproved":"1", "id":"988"}, {"last_update":"1173975599", "numofapproved":"1", "id":"345"}, {"last_update":"1174041960", "numofapproved":"1", "id":"351"}, {"last_update":"1175759476", "numofapproved":"1", "id":"671"}, {"last_update":"1178195644", "numofapproved":"1", "id":"1207"}, {"last_update":"1178725318", "numofapproved":"1", "id":"1348"}, {"last_update":"1179333492", "numofapproved":"1", "id":"1421"}, {"last_update":"1179999737", "numofapproved":"1", "id":"1646"}, {"last_update":"1180710770", "numofapproved":"1", "id":"2062"}, {"last_update":"1182868347", "numofapproved":"1", "id":"3601"}, {"last_update":"1182932927", "numofapproved":"1", "id":"3621"}, {"last_update":"1183115054", "numofapproved":"1", "id":"3784"}, {"last_update":"1180000741", "numofapproved":"1", "id":"1647"}, {"last_update":"1181292582", "numofapproved":"1", "id":"2621"}, {"last_update":"1184181581", "numofapproved":"1", "id":"4641"}, {"last_update":"1185280501", "numofapproved":"1", "id":"5301"}, {"last_update":"1185471699", "numofapproved":"1", "id":"5561"}, {"last_update":"1185542771", "numofapproved":"1", "id":"5701"}, {"last_update":"1186650650", "numofapproved":"1", "id":"6361"}, {"last_update":"1186951065", "numofapproved":"1", "id":"6461"}, {"last_update":"1187769080", "numofapproved":"1", "id":"7221"}, {"last_update":"1187887905", "numofapproved":"1", "id":"7348"}, {"last_update":"1188001607", "numofapproved":"1", "id":"7423"}, {"last_update":"1188463414", "numofapproved":"1", "id":"7762"}, {"last_update":"1188555813", "numofapproved":"1", "id":"7861"}, {"last_update":"1188634622", "numofapproved":"1", "id":"7921"}, {"last_update":"1189543954", "numofapproved":"1", "id":"8841"}, {"last_update":"1177511009", "numofapproved":"1", "id":"1043"}, {"last_update":"1181898808", "numofapproved":"1", "id":"3081"}, {"last_update":"1182247483", "numofapproved":"1", "id":"3221"}, {"last_update":"1187024005", "numofapproved":"1", "id":"6562"}, {"last_update":"1189839471", "numofapproved":"1", "id":"9081"}, {"last_update":"1190018380", "numofapproved":"1", "id":"9241"}, {"last_update":"1190149586", "numofapproved":"1", "id":"9401"}, {"last_update":"1190652684", "numofapproved":"1", "id":"9981"}, {"last_update":"1190662296", "numofapproved":"1", "id":"10022"}, {"last_update":"1190813509", "numofapproved":"1", "id":"10281"}, {"last_update":"1190826005", "numofapproved":"1", "id":"10403"}, {"last_update":"1190991166", "numofapproved":"1", "id":"10722"}, {"last_update":"1191057700", "numofapproved":"1", "id":"10801"}, {"last_update":"1191161241", "numofapproved":"1", "id":"10821"}, {"last_update":"1191227885", "numofapproved":"1", "id":"10941"}, {"last_update":"1182537005", "numofapproved":"1", "id":"3481"}, {"last_update":"1185018401", "numofapproved":"1", "id":"5162"}, {"last_update":"1186752963", "numofapproved":"1", "id":"6386"}, {"last_update":"1190660077", "numofapproved":"1", "id":"10001"}, {"last_update":"1191319062", "numofapproved":"1", "id":"10981"}, {"last_update":"1191446097", "numofapproved":"1", "id":"11161"}, {"last_update":"1191446587", "numofapproved":"1", "id":"11184"}, {"last_update":"1191470824", "numofapproved":"1", "id":"11221"}, {"last_update":"1191526821", "numofapproved":"1", "id":"11401"}, {"last_update":"1191585471", "numofapproved":"1", "id":"11561"}, {"last_update":"1191602213", "numofapproved":"1", "id":"11701"}, {"last_update":"1191845720", "numofapproved":"1", "id":"11821"}, {"last_update":"1191933874", "numofapproved":"1", "id":"11902"}, {"last_update":"1191933897", "numofapproved":"1", "id":"11903"}, {"last_update":"1177673238", "numofapproved":"1", "id":"1161"}, {"last_update":"1181601542", "numofapproved":"1", "id":"2781"}, {"last_update":"1182869532", "numofapproved":"1", "id":"3583"}, {"last_update":"1183315879", "numofapproved":"1", "id":"3881"}, {"last_update":"1187097870", "numofapproved":"1", "id":"6641"}, {"last_update":"1190148660", "numofapproved":"1", "id":"9361"}, {"last_update":"1192248648", "numofapproved":"1", "id":"12521"}, {"last_update":"1192702958", "numofapproved":"1", "id":"13001"}, {"last_update":"1193387721", "numofapproved":"1", "id":"13981"}, {"last_update":"1193391276", "numofapproved":"1", "id":"14021"}, {"last_update":"1193397051", "numofapproved":"1", "id":"14061"}, {"last_update":"1193592081", "numofapproved":"1", "id":"14321"}, {"last_update":"1188474438", "numofapproved":"1", "id":"7821"}, {"last_update":"1190158372", "numofapproved":"1", "id":"9441"}, {"last_update":"1193648459", "numofapproved":"1", "id":"14361"}, {"last_update":"1193999834", "numofapproved":"1", "id":"14681"}, {"last_update":"1194200119", "numofapproved":"1", "id":"14861"}, {"last_update":"1194528747", "numofapproved":"1", "id":"15111"}, {"last_update":"1179150787", "numofapproved":"1", "id":"1384"}, {"last_update":"1179266496", "numofapproved":"1", "id":"1390"}, {"last_update":"1179508139", "numofapproved":"1", "id":"1501"}, {"last_update":"1179842157", "numofapproved":"1", "id":"1664"}, {"last_update":"1179842347", "numofapproved":"1", "id":"1668"}, {"last_update":"1181245388", "numofapproved":"1", "id":"2562"}, {"last_update":"1181311044", "numofapproved":"1", "id":"2661"}, {"last_update":"1181545818", "numofapproved":"1", "id":"2701"}, {"last_update":"1181934881", "numofapproved":"1", "id":"3103"}, {"last_update":"1187020798", "numofapproved":"1", "id":"6541"}, {"last_update":"1187271377", "numofapproved":"1", "id":"6801"}, {"last_update":"1196086904", "numofapproved":"1", "id":"17545"}, {"last_update":"1196266437", "numofapproved":"2", "id":"17662"}, {"last_update":"1196266638", "numofapproved":"2", "id":"17663"}, {"last_update":"1197533251", "numofapproved":"1", "id":"17901"}, {"last_update":"1197533384", "numofapproved":"1", "id":"17923"}, {"last_update":"1197556776", "numofapproved":"2", "id":"17941"}, {"last_update":"1200059354", "numofapproved":"1", "id":"17981"}, {"last_update":"1200576144", "numofapproved":"1", "id":"18001"}, {"last_update":"1200576230", "numofapproved":"1", "id":"18002"}, {"last_update":"1200657266", "numofapproved":"1", "id":"18041"}, {"last_update":"1201510556", "numofapproved":"1", "id":"18061"}, {"last_update":"1196087136", "numofapproved":"1", "id":"17546"}, {"last_update":"1196087269", "numofapproved":"1", "id":"17547"}, {"last_update":"1196087335", "numofapproved":"1", "id":"17548"}, {"last_update":"1196087379", "numofapproved":"1", "id":"17549"}, {"last_update":"1196087427", "numofapproved":"1", "id":"17550"}, {"last_update":"1196096347", "numofapproved":"1", "id":"17581"}, {"last_update":"1196265997", "numofapproved":"2", "id":"17661"}, {"last_update":"1196266785", "numofapproved":"1", "id":"17664"}, {"last_update":"1196270058", "numofapproved":"1", "id":"17701"}, {"last_update":"1196431875", "numofapproved":"1", "id":"17804"}, {"last_update":"1197635044", "numofapproved":"1", "id":"17961"}, {"last_update":"1202720206", "numofapproved":"2", "id":"18084"}, {"last_update":"1196267153", "numofapproved":"1", "id":"17681"}, {"last_update":"1196090749", "numofapproved":"1", "id":"17569"}, {"last_update":"1196162163", "numofapproved":"2", "id":"17641"}, {"last_update":"1196345846", "numofapproved":"1", "id":"17721"}, {"last_update":"1196088254", "numofapproved":"1", "id":"17552"}, {"last_update":"1196088437", "numofapproved":"1", "id":"17564"}, {"last_update":"1196088477", "numofapproved":"1", "id":"17565"}, {"last_update":"1196088537", "numofapproved":"1", "id":"17566"}, {"last_update":"1196088894", "numofapproved":"1", "id":"17567"}, {"last_update":"1196090414", "numofapproved":"1", "id":"17554"}, {"last_update":"1196097621", "numofapproved":"1", "id":"17601"}, {"last_update":"1196097710", "numofapproved":"1", "id":"17602"}, {"last_update":"1196098047", "numofapproved":"1", "id":"17603"}, {"last_update":"1196358376", "numofapproved":"2", "id":"17761"}, {"last_update":"1196358647", "numofapproved":"1", "id":"17762"}, {"last_update":"1196427604", "numofapproved":"1", "id":"17781"}, {"last_update":"1196429856", "numofapproved":"1", "id":"17782"}, {"last_update":"1196431068", "numofapproved":"2", "id":"17783"}, {"last_update":"1196435953", "numofapproved":"2", "id":"17821"}, {"last_update":"1204027277", "numofapproved":"1", "id":"18104"}, {"last_update":"1196090201", "numofapproved":"1", "id":"17553"}, {"last_update":"1196097095", "numofapproved":"1", "id":"17582"}, {"last_update":"1196097215", "numofapproved":"1", "id":"17583"}, {"last_update":"1196430140", "numofapproved":"2", "id":"17803"}, {"last_update":"1196436411", "numofapproved":"2", "id":"17841"}, {"last_update":"1196692298", "numofapproved":"1", "id":"17861"}, {"last_update":"1196692342", "numofapproved":"2", "id":"17862"}, {"last_update":"1196695231", "numofapproved":"2", "id":"17865"}, {"last_update":"1197533316", "numofapproved":"1", "id":"17921"}, {"last_update":"1201512744", "numofapproved":"1", "id":"18082"}, {"last_update":"1201513438", "numofapproved":"2", "id":"18083"}, {"last_update":"1196087540", "numofapproved":"1", "id":"17551"}, {"last_update":"1196156416", "numofapproved":"2", "id":"17621"}, {"last_update":"1196356717", "numofapproved":"1", "id":"17741"}, {"last_update":"1196428544", "numofapproved":"2", "id":"17801"}, {"last_update":"1196429000", "numofapproved":"2", "id":"17802"}, {"last_update":"1196692578", "numofapproved":"1", "id":"17863"}, {"last_update":"1196693445", "numofapproved":"2", "id":"17881"}, {"last_update":"1196693804", "numofapproved":"2", "id":"17864"}, {"last_update":"1197533347", "numofapproved":"1", "id":"17922"}, {"last_update":"1200591782", "numofapproved":"1", "id":"18021"}, {"last_update":"1201510930", "numofapproved":"1", "id":"18081"}, {"last_update":"1192432005", "numofapproved":"1", "id":"12582"}, {"last_update":"1192614291", "numofapproved":"1", "id":"12805"}, {"last_update":"1192624421", "numofapproved":"1", "id":"12806"}, {"last_update":"1192983623", "numofapproved":"1", "id":"13221"}, {"last_update":"1193043248", "numofapproved":"1", "id":"13282"}, {"last_update":"1193223892", "numofapproved":"1", "id":"13562"}, {"last_update":"1193239943", "numofapproved":"1", "id":"13601"}, {"last_update":"1193385960", "numofapproved":"1", "id":"13961"}, {"last_update":"1193386863", "numofapproved":"1", "id":"13945"}, {"last_update":"1193399770", "numofapproved":"1", "id":"14042"}, {"last_update":"1193417684", "numofapproved":"1", "id":"14181"}, {"last_update":"1193458402", "numofapproved":"1", "id":"14261"}, {"last_update":"1193555071", "numofapproved":"1", "id":"14301"}, {"last_update":"1185285506", "numofapproved":"1", "id":"5321"}, {"last_update":"1188250869", "numofapproved":"1", "id":"7521"}, {"last_update":"1191410480", "numofapproved":"1", "id":"11061"}, {"last_update":"1193763056", "numofapproved":"1", "id":"14482"}, {"last_update":"1193913886", "numofapproved":"1", "id":"14542"}, {"last_update":"1194366001", "numofapproved":"1", "id":"14890"}, {"last_update":"1194454607", "numofapproved":"1", "id":"15105"}, {"last_update":"1194255904", "numofapproved":"1", "id":"14941"}, {"last_update":"1179328986", "numofapproved":"1", "id":"1395"}, {"last_update":"1180377628", "numofapproved":"1", "id":"1861"}, {"last_update":"1181250011", "numofapproved":"1", "id":"2563"}, {"last_update":"1181572386", "numofapproved":"1", "id":"2741"}, {"last_update":"1183967114", "numofapproved":"1", "id":"4381"}, {"last_update":"1192512712", "numofapproved":"1", "id":"12623"}, {"last_update":"1193172621", "numofapproved":"1", "id":"13522"}, {"last_update":"1193868932", "numofapproved":"1", "id":"14523"}, {"last_update":"1194980345", "numofapproved":"1", "id":"16301"}, {"last_update":"1182280312", "numofapproved":"1", "id":"3282"}, {"last_update":"1184058726", "numofapproved":"1", "id":"4542"}, {"last_update":"1188829875", "numofapproved":"1", "id":"8161"}, {"last_update":"1190129857", "numofapproved":"1", "id":"9341"}, {"last_update":"1190652687", "numofapproved":"1", "id":"9982"}, {"last_update":"1193389082", "numofapproved":"1", "id":"13956"}, {"last_update":"1195400591", "numofapproved":"1", "id":"17121"}, {"last_update":"1184420846", "numofapproved":"1", "id":"4882"}, {"last_update":"1184532219", "numofapproved":"1", "id":"4903"}, {"last_update":"1192030476", "numofapproved":"1", "id":"12101"}, {"last_update":"1192202239", "numofapproved":"1", "id":"12461"}, {"last_update":"1192688302", "numofapproved":"1", "id":"12961"}, {"last_update":"1192703266", "numofapproved":"1", "id":"13021"}, {"last_update":"1193387096", "numofapproved":"1", "id":"13948"}, {"last_update":"1193387200", "numofapproved":"1", "id":"13949"}, {"last_update":"1193909837", "numofapproved":"1", "id":"14528"}, {"last_update":"1181062093", "numofapproved":"1", "id":"2301"}, {"last_update":"1182364431", "numofapproved":"1", "id":"3348"}, {"last_update":"1182364589", "numofapproved":"1", "id":"3349"}, {"last_update":"1184942429", "numofapproved":"1", "id":"5101"}, {"last_update":"1192682522", "numofapproved":"1", "id":"12901"}, {"last_update":"1184756287", "numofapproved":"1", "id":"4944"}, {"last_update":"1190274411", "numofapproved":"1", "id":"9541"}, {"last_update":"1193324229", "numofapproved":"1", "id":"13861"}, {"last_update":"1195163999", "numofapproved":"1", "id":"16861"}, {"last_update":"1181553321", "numofapproved":"1", "id":"2721"}, {"last_update":"1178869453", "numofapproved":"1", "id":"1361"}, {"last_update":"1181219788", "numofapproved":"1", "id":"2481"}, {"last_update":"1178140002", "numofapproved":"1", "id":"1205"}, {"last_update":"1178716891", "numofapproved":"1", "id":"1345"}, {"last_update":"1180691957", "numofapproved":"1", "id":"2061"}, {"last_update":"1182246242", "numofapproved":"1", "id":"3206"}, {"last_update":"1182882314", "numofapproved":"1", "id":"3585"}, {"last_update":"1183124192", "numofapproved":"1", "id":"3821"}, {"last_update":"1183905634", "numofapproved":"1", "id":"4361"}, {"last_update":"1191225755", "numofapproved":"1", "id":"10901"}, {"last_update":"1192635977", "numofapproved":"1", "id":"12881"}, {"last_update":"1193268752", "numofapproved":"1", "id":"13721"}, {"last_update":"1193242245", "numofapproved":"1", "id":"13621"}, {"last_update":"1193949751", "numofapproved":"1", "id":"14621"}, {"last_update":"1194635892", "numofapproved":"1", "id":"15621"}, {"last_update":"1194726918", "numofapproved":"1", "id":"15664"}, {"last_update":"1194726371", "numofapproved":"1", "id":"15662"}, {"last_update":"1194858043", "numofapproved":"1", "id":"15781"}, {"last_update":"1194946522", "numofapproved":"1", "id":"16101"}, {"last_update":"1195047359", "numofapproved":"1", "id":"16521"}, {"last_update":"1195050812", "numofapproved":"1", "id":"16503"}, {"last_update":"1195058811", "numofapproved":"1", "id":"16621"}, {"last_update":"1195476161", "numofapproved":"1", "id":"17241"}, {"last_update":"1178645683", "numofapproved":"1", "id":"1305"}, {"last_update":"1183118619", "numofapproved":"1", "id":"3801"}, {"last_update":"1186150376", "numofapproved":"1", "id":"6121"}, {"last_update":"1189114226", "numofapproved":"1", "id":"8501"}, {"last_update":"1190973079", "numofapproved":"1", "id":"10666"}, {"last_update":"1190990329", "numofapproved":"1", "id":"10704"}, {"last_update":"1191508485", "numofapproved":"1", "id":"11361"}, {"last_update":"1183054560", "numofapproved":"1", "id":"3721"}, {"last_update":"1185263889", "numofapproved":"1", "id":"5241"}, {"last_update":"1187876083", "numofapproved":"1", "id":"7346"}, {"last_update":"1189550218", "numofapproved":"1", "id":"8861"}, {"last_update":"1190800088", "numofapproved":"1", "id":"10221"}, {"last_update":"1193260528", "numofapproved":"1", "id":"13661"}, {"last_update":"1172509002", "numofapproved":"1", "id":"199"}, {"last_update":"1172509846", "numofapproved":"1", "id":"200"}, {"last_update":"1172589855", "numofapproved":"1", "id":"214"}, {"last_update":"1172847322", "numofapproved":"1", "id":"236"}, {"last_update":"1172847433", "numofapproved":"1", "id":"242"}, {"last_update":"1173607050", "numofapproved":"1", "id":"283"}, {"last_update":"1173703535", "numofapproved":"1", "id":"301"}, {"last_update":"1173719825", "numofapproved":"1", "id":"302"}, {"last_update":"1174414845", "numofapproved":"1", "id":"403"}, {"last_update":"1174650542", "numofapproved":"1", "id":"441"}, {"last_update":"1171475944", "numofapproved":"1", "id":"52"}, {"last_update":"1172746278", "numofapproved":"1", "id":"231"}, {"last_update":"1173251095", "numofapproved":"1", "id":"254"}, {"last_update":"1173259501", "numofapproved":"1", "id":"255"}, {"last_update":"1174899183", "numofapproved":"1", "id":"495"}, {"last_update":"1174924714", "numofapproved":"1", "id":"524"}, {"last_update":"1171962179", "numofapproved":"1", "id":"108"}, {"last_update":"1172522401", "numofapproved":"1", "id":"205"}, {"last_update":"1174299349", "numofapproved":"1", "id":"362"}, {"last_update":"1174899291", "numofapproved":"1", "id":"500"}, {"last_update":"1175617661", "numofapproved":"1", "id":"688"}, {"last_update":"1176302948", "numofapproved":"1", "id":"881"}, {"last_update":"1176467393", "numofapproved":"1", "id":"893"}, {"last_update":"1176737599", "numofapproved":"1", "id":"982"}, {"last_update":"1171465517", "numofapproved":"1", "id":"50"}, {"last_update":"1171924670", "numofapproved":"1", "id":"107"}, {"last_update":"1173880505", "numofapproved":"1", "id":"317"}, {"last_update":"1173889350", "numofapproved":"1", "id":"329"}, {"last_update":"1173889557", "numofapproved":"1", "id":"332"}, {"last_update":"1176391285", "numofapproved":"1", "id":"892"}, {"last_update":"1176673529", "numofapproved":"1", "id":"981"}, {"last_update":"1171643442", "numofapproved":"1", "id":"69"}, {"last_update":"1172226841", "numofapproved":"1", "id":"182"}, {"last_update":"1174899475", "numofapproved":"1", "id":"506"}, {"last_update":"1174915327", "numofapproved":"1", "id":"521"}, {"last_update":"1176194461", "numofapproved":"1", "id":"821"}, {"last_update":"1172013837", "numofapproved":"1", "id":"132"}, {"last_update":"1172184974", "numofapproved":"1", "id":"177"}, {"last_update":"1175777908", "numofapproved":"1", "id":"674"}, {"last_update":"1173460745", "numofapproved":"1", "id":"281"}, {"last_update":"1174401746", "numofapproved":"1", "id":"402"}, {"last_update":"1171274691", "numofapproved":"1", "id":"5"}, {"last_update":"1171799314", "numofapproved":"1", "id":"78"}, {"last_update":"1171979089", "numofapproved":"1", "id":"127"}, {"last_update":"1172503571", "numofapproved":"1", "id":"197"}, {"last_update":"1174301365", "numofapproved":"1", "id":"391"}, {"last_update":"1174301259", "numofapproved":"1", "id":"385"}, {"last_update":"1174899163", "numofapproved":"1", "id":"494"}, {"last_update":"1174933167", "numofapproved":"1", "id":"534"}, {"last_update":"1176139704", "numofapproved":"1", "id":"808"}, {"last_update":"1175502855", "numofapproved":"1", "id":"603"}, {"last_update":"1173721122", "numofapproved":"1", "id":"303"}, {"last_update":"1173809079", "numofapproved":"1", "id":"311"}, {"last_update":"1174734352", "numofapproved":"1", "id":"461"}, {"last_update":"1174898917", "numofapproved":"1", "id":"482"}, {"last_update":"1174899374", "numofapproved":"1", "id":"503"}, {"last_update":"1176392495", "numofapproved":"1", "id":"903"}, {"last_update":"1176829535", "numofapproved":"1", "id":"987"}, {"last_update":"1173889385", "numofapproved":"1", "id":"330"}, {"last_update":"1175869070", "numofapproved":"1", "id":"783"}, {"last_update":"1177510634", "numofapproved":"1", "id":"1042"}, {"last_update":"1177585810", "numofapproved":"1", "id":"1062"}, {"last_update":"1178648303", "numofapproved":"1", "id":"1309"}, {"last_update":"1178883682", "numofapproved":"1", "id":"1363"}, {"last_update":"1179239792", "numofapproved":"1", "id":"1402"}, {"last_update":"1179997715", "numofapproved":"1", "id":"1644"}, {"last_update":"1180031289", "numofapproved":"1", "id":"1654"}, {"last_update":"1180440758", "numofapproved":"1", "id":"1921"}, {"last_update":"1180972413", "numofapproved":"1", "id":"2221"}, {"last_update":"1181032741", "numofapproved":"1", "id":"2261"}, {"last_update":"1181198104", "numofapproved":"1", "id":"2401"}, {"last_update":"1181237541", "numofapproved":"1", "id":"2581"}, {"last_update":"1181293731", "numofapproved":"1", "id":"2641"}, {"last_update":"1182231158", "numofapproved":"1", "id":"3204"}, {"last_update":"1177668412", "numofapproved":"1", "id":"1121"}, {"last_update":"1178713554", "numofapproved":"1", "id":"1342"}, {"last_update":"1179239886", "numofapproved":"1", "id":"1404"}, {"last_update":"1184766561", "numofapproved":"1", "id":"4961"}, {"last_update":"1185293883", "numofapproved":"1", "id":"5341"}, {"last_update":"1185781181", "numofapproved":"1", "id":"5761"}, {"last_update":"1185898126", "numofapproved":"1", "id":"5862"}, {"last_update":"1186290486", "numofapproved":"1", "id":"6164"}, {"last_update":"1186260193", "numofapproved":"1", "id":"6162"}, {"last_update":"1186305362", "numofapproved":"1", "id":"6201"}, {"last_update":"1187024035", "numofapproved":"1", "id":"6563"}, {"last_update":"1187245873", "numofapproved":"1", "id":"6761"}, {"last_update":"1187765176", "numofapproved":"1", "id":"7142"}, {"last_update":"1187872548", "numofapproved":"1", "id":"7343"}, {"last_update":"1188774634", "numofapproved":"1", "id":"8061"}, {"last_update":"1188838929", "numofapproved":"1", "id":"8181"}, {"last_update":"1189608461", "numofapproved":"1", "id":"8881"}, {"last_update":"1189667694", "numofapproved":"1", "id":"8921"}, {"last_update":"1179747423", "numofapproved":"1", "id":"1541"}, {"last_update":"1181142187", "numofapproved":"1", "id":"2381"}, {"last_update":"1185965227", "numofapproved":"1", "id":"5921"}, {"last_update":"1190476977", "numofapproved":"1", "id":"9761"}, {"last_update":"1190648889", "numofapproved":"1", "id":"9961"}, {"last_update":"1190824195", "numofapproved":"1", "id":"10381"}, {"last_update":"1190825530", "numofapproved":"1", "id":"10401"}, {"last_update":"1190894398", "numofapproved":"1", "id":"10501"}, {"last_update":"1178271031", "numofapproved":"1", "id":"1242"}, {"last_update":"1178878052", "numofapproved":"1", "id":"1359"}, {"last_update":"1178967516", "numofapproved":"1", "id":"1364"}, {"last_update":"1180018261", "numofapproved":"1", "id":"1652"}, {"last_update":"1180107922", "numofapproved":"1", "id":"1841"}, {"last_update":"1180514196", "numofapproved":"1", "id":"1941"}, {"last_update":"1181901023", "numofapproved":"1", "id":"3082"}, {"last_update":"1182417878", "numofapproved":"1", "id":"3361"}, {"last_update":"1182785340", "numofapproved":"1", "id":"3521"}, {"last_update":"1183485766", "numofapproved":"1", "id":"4101"}, {"last_update":"1189526136", "numofapproved":"1", "id":"8803"}, {"last_update":"1191446636", "numofapproved":"1", "id":"11185"}, {"last_update":"1191489743", "numofapproved":"1", "id":"11241"}, {"last_update":"1191903141", "numofapproved":"1", "id":"11882"}, {"last_update":"1191940049", "numofapproved":"1", "id":"11941"}, {"last_update":"1179239857", "numofapproved":"1", "id":"1403"}, {"last_update":"1185799202", "numofapproved":"1", "id":"5801"}, {"last_update":"1190924823", "numofapproved":"1", "id":"10562"}, {"last_update":"1191410783", "numofapproved":"1", "id":"11065"}, {"last_update":"1192031578", "numofapproved":"1", "id":"12121"}, {"last_update":"1192431234", "numofapproved":"1", "id":"12562"}, {"last_update":"1192609228", "numofapproved":"1", "id":"12802"}, {"last_update":"1192742243", "numofapproved":"1", "id":"13161"}, {"last_update":"1192942532", "numofapproved":"1", "id":"13201"}, {"last_update":"1193386303", "numofapproved":"1", "id":"13962"}, {"last_update":"1193406158", "numofapproved":"1", "id":"14121"}, {"last_update":"1193418273", "numofapproved":"1", "id":"14201"}, {"last_update":"1193519213", "numofapproved":"1", "id":"14281"}, {"last_update":"1193666593", "numofapproved":"1", "id":"14401"}, {"last_update":"1193733296", "numofapproved":"1", "id":"14421"}, {"last_update":"1193760981", "numofapproved":"1", "id":"14481"}, {"last_update":"1182436569", "numofapproved":"1", "id":"3422"}, {"last_update":"1184012598", "numofapproved":"1", "id":"4481"}, {"last_update":"1189715279", "numofapproved":"1", "id":"8981"}, {"last_update":"1192528903", "numofapproved":"1", "id":"12701"}, {"last_update":"1194246273", "numofapproved":"1", "id":"14901"}, {"last_update":"1194354217", "numofapproved":"1", "id":"14888"}, {"last_update":"1194366787", "numofapproved":"1", "id":"14891"}, {"last_update":"1194445768", "numofapproved":"1", "id":"15104"}, {"last_update":"1194467580", "numofapproved":"1", "id":"15107"}, {"last_update":"1194508237", "numofapproved":"1", "id":"15262"}, {"last_update":"1194635341", "numofapproved":"1", "id":"15581"}, {"last_update":"1194635508", "numofapproved":"1", "id":"15582"}, {"last_update":"1179214538", "numofapproved":"1", "id":"1386"}, {"last_update":"1186433530", "numofapproved":"1", "id":"6167"}, {"last_update":"1187853435", "numofapproved":"1", "id":"7321"}, {"last_update":"1187972012", "numofapproved":"1", "id":"7421"}, {"last_update":"1188895906", "numofapproved":"1", "id":"8201"}, {"last_update":"1190284020", "numofapproved":"1", "id":"9561"}, {"last_update":"1190924163", "numofapproved":"1", "id":"10561"}, {"last_update":"1192529770", "numofapproved":"1", "id":"12663"}, {"last_update":"1192536538", "numofapproved":"1", "id":"12666"}, {"last_update":"1193269090", "numofapproved":"1", "id":"13741"}, {"last_update":"1193428819", "numofapproved":"1", "id":"14221"}, {"last_update":"1193860091", "numofapproved":"1", "id":"14521"}, {"last_update":"1193909426", "numofapproved":"1", "id":"14526"}, {"last_update":"1194533708", "numofapproved":"1", "id":"15321"}, {"last_update":"1179822723", "numofapproved":"1", "id":"1601"}, {"last_update":"1179842248", "numofapproved":"1", "id":"1666"}, {"last_update":"1182412362", "numofapproved":"1", "id":"3352"}, {"last_update":"1185980065", "numofapproved":"1", "id":"5961"}, {"last_update":"1186751100", "numofapproved":"1", "id":"6385"}, {"last_update":"1187202714", "numofapproved":"1", "id":"6721"}, {"last_update":"1187601864", "numofapproved":"1", "id":"6923"}, {"last_update":"1191490727", "numofapproved":"1", "id":"11281"}, {"last_update":"1194449840", "numofapproved":"1", "id":"15161"}, {"last_update":"1180028166", "numofapproved":"1", "id":"1781"}, {"last_update":"1185025939", "numofapproved":"1", "id":"5201"}, {"last_update":"1192454400", "numofapproved":"1", "id":"12621"}, {"last_update":"1193414234", "numofapproved":"1", "id":"14141"}, {"last_update":"1194270682", "numofapproved":"1", "id":"14961"}, {"last_update":"1184061669", "numofapproved":"1", "id":"4561"}, {"last_update":"1186161284", "numofapproved":"1", "id":"6141"}, {"last_update":"1187714492", "numofapproved":"1", "id":"7061"}, {"last_update":"1187893562", "numofapproved":"1", "id":"7361"}, {"last_update":"1190815311", "numofapproved":"1", "id":"10301"}, {"last_update":"1193388120", "numofapproved":"1", "id":"13951"}, {"last_update":"1195239956", "numofapproved":"1", "id":"17041"}, {"last_update":"1179147467", "numofapproved":"1", "id":"1381"}, {"last_update":"1182346611", "numofapproved":"1", "id":"3341"}, {"last_update":"1184267506", "numofapproved":"1", "id":"4802"}, {"last_update":"1192047087", "numofapproved":"1", "id":"12161"}, {"last_update":"1192198948", "numofapproved":"1", "id":"12441"}, {"last_update":"1193208717", "numofapproved":"1", "id":"13528"}, {"last_update":"1194907182", "numofapproved":"1", "id":"16001"}, {"last_update":"1179153020", "numofapproved":"1", "id":"1385"}, {"last_update":"1179835655", "numofapproved":"1", "id":"1641"}, {"last_update":"1181234739", "numofapproved":"1", "id":"2542"}, {"last_update":"1182356477", "numofapproved":"1", "id":"3343"}, {"last_update":"1182418583", "numofapproved":"1", "id":"3381"}, {"last_update":"1184568502", "numofapproved":"1", "id":"4905"}, {"last_update":"1189151603", "numofapproved":"1", "id":"8581"}, {"last_update":"1191595695", "numofapproved":"1", "id":"11621"}, {"last_update":"1193105000", "numofapproved":"1", "id":"13421"}, {"last_update":"1195104657", "numofapproved":"1", "id":"16701"}], "request_timestamp":1206363392.08521, "request_call":"requestDetails", "instance":"tbedi", "call_time":"0.10059", "request_date":"2008-03-2412:56:32 UTC", "request_url":"http://cmsdoc.cern.ch/cms/test/aprom/phedex/dev/gowri/datasvc/tbedi/requestDetails?format=json"}} """ from jsonParser import jsonObject data = jsonObject.parseString(s) #~ from pprint import pprint #~ pprint( data[0].asList() ) #~ print #~ print data.dump() print(data.phedex.call_time) print(data.phedex.instance) print(data.phedex.request_call) print(len(data.phedex.request)) for req in data.phedex.request[:10]: #~ print req.dump() print("-", req.id, req.last_update) pyparsing2-2.4.7/examples/removeLineBreaks.py000066400000000000000000000034571365333160500212650ustar00rootroot00000000000000# removeLineBreaks.py # # Demonstration of the pyparsing module, converting text files # with hard line-breaks to text files with line breaks only # between paragraphs. (Helps when converting downloads from Project # Gutenberg - https://www.gutenberg.org/ - to import to word processing apps # that can reformat paragraphs once hard line-breaks are removed.) # # Uses parse actions and transformString to remove unwanted line breaks, # and to double up line breaks between paragraphs. # # Copyright 2006, by Paul McGuire # import pyparsing as pp line_end = pp.LineEnd() # define an expression for the body of a line of text - use a predicate condition to # accept only lines with some content. def mustBeNonBlank(t): return t[0] != '' # could also be written as # return bool(t[0]) lineBody = pp.SkipTo(line_end).addCondition(mustBeNonBlank, message="line body can't be empty") # now define a line with a trailing lineEnd, to be replaced with a space character textLine = lineBody + line_end().setParseAction(pp.replaceWith(" ")) # define a paragraph, with a separating lineEnd, to be replaced with a double newline para = pp.OneOrMore(textLine) + line_end().setParseAction(pp.replaceWith("\n\n")) # run a test test = """ Now is the time for all good men to come to the aid of their country. """ print(para.transformString(test)) # process an entire file # Project Gutenberg EBook of Successful Methods of Public Speaking, by Grenville Kleiser # Download from http://www.gutenberg.org/cache/epub/18095/pg18095.txt # with open("18095-8.txt") as source_file: original = source_file.read() # use transformString to convert line breaks transformed = para.transformString(original) with open("18095-8_reformatted.txt", "w") as transformed_file: transformed_file.write(transformed) pyparsing2-2.4.7/examples/romanNumerals.py000066400000000000000000000050071365333160500206440ustar00rootroot00000000000000# romanNumerals.py # # Copyright (c) 2006, Paul McGuire # from pyparsing import * def romanNumeralLiteral(numeralString, value): return Literal(numeralString).setParseAction(replaceWith(value)) one = romanNumeralLiteral("I",1) four = romanNumeralLiteral("IV",4) five = romanNumeralLiteral("V",5) nine = romanNumeralLiteral("IX",9) ten = romanNumeralLiteral("X",10) forty = romanNumeralLiteral("XL",40) fifty = romanNumeralLiteral("L",50) ninety = romanNumeralLiteral("XC",90) onehundred = romanNumeralLiteral("C",100) fourhundred = romanNumeralLiteral("CD",400) fivehundred = romanNumeralLiteral("D",500) ninehundred = romanNumeralLiteral("CM",900) onethousand = romanNumeralLiteral("M",1000) numeral = ( onethousand | ninehundred | fivehundred | fourhundred | onehundred | ninety | fifty | forty | ten | nine | five | four | one ).leaveWhitespace() romanNumeral = OneOrMore(numeral).setParseAction( lambda s,l,t : sum(t) ) # unit tests def makeRomanNumeral(n): def addDigit(n,limit,c,s): n -= limit s += c return n,s ret = "" while n >= 1000: n,ret = addDigit(n,1000,"M",ret) while n >= 900: n,ret = addDigit(n, 900,"CM",ret) while n >= 500: n,ret = addDigit(n, 500,"D",ret) while n >= 400: n,ret = addDigit(n, 400,"CD",ret) while n >= 100: n,ret = addDigit(n, 100,"C",ret) while n >= 90: n,ret = addDigit(n, 90,"XC",ret) while n >= 50: n,ret = addDigit(n, 50,"L",ret) while n >= 40: n,ret = addDigit(n, 40,"XL",ret) while n >= 10: n,ret = addDigit(n, 10,"X",ret) while n >= 9: n,ret = addDigit(n, 9,"IX",ret) while n >= 5: n,ret = addDigit(n, 5,"V",ret) while n >= 4: n,ret = addDigit(n, 4,"IV",ret) while n >= 1: n,ret = addDigit(n, 1,"I",ret) return ret tests = " ".join(makeRomanNumeral(i) for i in range(1,5000+1)) roman_int_map = {} expected = 1 for t,s,e in romanNumeral.scanString(tests): orig = tests[s:e] if t[0] != expected: print("{0} {1} {2}".format("==>", t, orig)) roman_int_map[orig] = t[0] expected += 1 def verify_value(s, tokens): expected = roman_int_map[s] if tokens[0] != expected: raise Exception("incorrect value for {0} ({1}), expected {2}".format(s, tokens[0], expected )) romanNumeral.runTests("""\ XVI XXXIX XIV XIX MCMLXXX MMVI """, fullDump=False, postParse=verify_value)pyparsing2-2.4.7/examples/rosettacode.py000066400000000000000000000173761365333160500203510ustar00rootroot00000000000000# # rosettacode.py # # parser for language used by rosettacode.org (http://rosettacode.org/wiki/Compiler/syntax_analyzer) # # Copyright Paul McGuire, 2019 # BNF = """ stmt_list = {stmt} ; stmt = ';' | Identifier '=' expr ';' | 'while' paren_expr stmt | 'if' paren_expr stmt ['else' stmt] | 'print' '(' prt_list ')' ';' | 'putc' paren_expr ';' | '{' stmt_list '}' ; paren_expr = '(' expr ')' ; prt_list = string | expr {',' String | expr} ; expr = and_expr {'||' and_expr} ; and_expr = equality_expr {'&&' equality_expr} ; equality_expr = relational_expr [('==' | '!=') relational_expr] ; relational_expr = addition_expr [('<' | '<=' | '>' | '>=') addition_expr] ; addition_expr = multiplication_expr {('+' | '-') multiplication_expr} ; multiplication_expr = primary {('*' | '/' | '%') primary } ; primary = Identifier | Integer | '(' expr ')' | ('+' | '-' | '!') primary ; """ import pyparsing as pp pp.ParserElement.enablePackrat() LBRACE, RBRACE, LPAR, RPAR, SEMI = map(pp.Suppress, "{}();") EQ = pp.Literal('=') keywords = (WHILE, IF, PRINT, PUTC, ELSE) = map(pp.Keyword, "while if print putc else".split()) identifier = ~(pp.MatchFirst(keywords)) + pp.pyparsing_common.identifier integer = pp.pyparsing_common.integer string = pp.QuotedString('"', convertWhitespaceEscapes=False).setName("quoted string") char = pp.Regex(r"'\\?.'") expr = pp.infixNotation(identifier | integer | char, [ (pp.oneOf("+ - !"), 1, pp.opAssoc.RIGHT,), (pp.oneOf("* / %"), 2, pp.opAssoc.LEFT, ), (pp.oneOf("+ -"), 2, pp.opAssoc.LEFT,), (pp.oneOf("< <= > >="), 2, pp.opAssoc.LEFT,), (pp.oneOf("== !="), 2, pp.opAssoc.LEFT,), (pp.oneOf("&&"), 2, pp.opAssoc.LEFT,), (pp.oneOf("||"), 2, pp.opAssoc.LEFT,), ]) prt_list = pp.Group(pp.delimitedList(string | expr)) paren_expr = pp.Group(LPAR + expr + RPAR) stmt = pp.Forward() assignment_stmt = pp.Group(identifier + EQ + expr + SEMI) while_stmt = pp.Group(WHILE - paren_expr + stmt) if_stmt = pp.Group(IF - paren_expr + stmt + pp.Optional(ELSE + stmt)) print_stmt = pp.Group(PRINT - pp.Group(LPAR + prt_list + RPAR) + SEMI) putc_stmt = pp.Group(PUTC - paren_expr + SEMI) stmt_list = pp.Group(LBRACE + pp.ZeroOrMore(stmt) + RBRACE) stmt <<= (pp.Group(SEMI) | assignment_stmt | while_stmt | if_stmt | print_stmt | putc_stmt | stmt_list ).setName("statement") code = pp.ZeroOrMore(stmt) code.ignore(pp.cppStyleComment) tests = [ r''' count = 1; while (count < 10) { print("count is: ", count, "\n"); count = count + 1; } ''', r''' /* Simple prime number generator */ count = 1; n = 1; limit = 100; while (n < limit) { k=3; p=1; n=n+2; while ((k*k<=n) && (p)) { p=n/k*k!=n; k=k+2; } if (p) { print(n, " is prime\n"); count = count + 1; } } print("Total primes found: ", count, "\n"); ''', r''' /* Hello world */ print("Hello, World!\n"); ''', r''' /* Show Ident and Integers */ phoenix_number = 142857; print(phoenix_number, "\n"); ''', r''' /*** test printing, embedded \n and comments with lots of '*' ***/ print(42); print("\nHello World\nGood Bye\nok\n"); print("Print a slash n - \\n.\n"); ''', r''' /* 100 Doors */ i = 1; while (i * i <= 100) { print("door ", i * i, " is open\n"); i = i + 1; } ''', r''' a = (-1 * ((-1 * (5 * 15)) / 10)); print(a, "\n"); b = -a; print(b, "\n"); print(-b, "\n"); print(-(1), "\n"); ''', r''' print(---------------------------------+++5, "\n"); print(((((((((3 + 2) * ((((((2))))))))))))), "\n"); if (1) { if (1) { if (1) { if (1) { if (1) { print(15, "\n"); } } } } } ''', r''' /* Compute the gcd of 1071, 1029: 21 */ a = 1071; b = 1029; while (b != 0) { new_a = b; b = a % b; a = new_a; } print(a); ''', r''' /* 12 factorial is 479001600 */ n = 12; result = 1; i = 1; while (i <= n) { result = result * i; i = i + 1; } print(result); ''', r''' /* fibonacci of 44 is 701408733 */ n = 44; i = 1; a = 0; b = 1; while (i < n) { w = a + b; a = b; b = w; i = i + 1; } print(w, "\n"); ''', r''' /* FizzBuzz */ i = 1; while (i <= 100) { if (!(i % 15)) print("FizzBuzz"); else if (!(i % 3)) print("Fizz"); else if (!(i % 5)) print("Buzz"); else print(i); print("\n"); i = i + 1; } ''', r''' /* 99 bottles */ bottles = 99; while (bottles > 0) { print(bottles, " bottles of beer on the wall\n"); print(bottles, " bottles of beer\n"); print("Take one down, pass it around\n"); bottles = bottles - 1; print(bottles, " bottles of beer on the wall\n\n"); } ''', r''' { /* This is an integer ascii Mandelbrot generator */ left_edge = -420; right_edge = 300; top_edge = 300; bottom_edge = -300; x_step = 7; y_step = 15; max_iter = 200; y0 = top_edge; while (y0 > bottom_edge) { x0 = left_edge; while (x0 < right_edge) { y = 0; x = 0; the_char = ' '; i = 0; while (i < max_iter) { x_x = (x * x) / 200; y_y = (y * y) / 200; if (x_x + y_y > 800 ) { the_char = '0' + i; if (i > 9) { the_char = '@'; } i = max_iter; } y = x * y / 100 + y0; x = x_x - y_y + x0; i = i + 1; } putc(the_char); x0 = x0 + x_step; } putc('\n'); y0 = y0 - y_step; } } ''', ] import sys sys.setrecursionlimit(2000) for test in tests: try: results = code.parseString(test) except pp.ParseException as pe: pp.ParseException.explain(pe) else: results.pprint() print() pyparsing2-2.4.7/examples/scanExamples.py000066400000000000000000000044561365333160500204530ustar00rootroot00000000000000# # scanExamples.py # # Illustration of using pyparsing's scanString,transformString, and searchString methods # # Copyright (c) 2004, 2006 Paul McGuire # from pyparsing import Word, alphas, alphanums, Literal, restOfLine, OneOrMore, \ empty, Suppress, replaceWith # simulate some C++ code testData = """ #define MAX_LOCS=100 #define USERNAME = "floyd" #define PASSWORD = "swordfish" a = MAX_LOCS; CORBA::initORB("xyzzy", USERNAME, PASSWORD ); """ ################# print("Example of an extractor") print("----------------------") # simple grammar to match #define's ident = Word(alphas, alphanums+"_") macroDef = Literal("#define") + ident.setResultsName("name") + "=" + restOfLine.setResultsName("value") for t,s,e in macroDef.scanString( testData ): print(t.name,":", t.value) # or a quick way to make a dictionary of the names and values # (return only key and value tokens, and construct dict from key-value pairs) # - empty ahead of restOfLine advances past leading whitespace, does implicit lstrip during parsing macroDef = Suppress("#define") + ident + Suppress("=") + empty + restOfLine macros = dict(list(macroDef.searchString(testData))) print("macros =", macros) print() ################# print("Examples of a transformer") print("----------------------") # convert C++ namespaces to mangled C-compatible names scopedIdent = ident + OneOrMore( Literal("::").suppress() + ident ) scopedIdent.setParseAction(lambda t: "_".join(t)) print("(replace namespace-scoped names with C-compatible names)") print(scopedIdent.transformString( testData )) # or a crude pre-processor (use parse actions to replace matching text) def substituteMacro(s,l,t): if t[0] in macros: return macros[t[0]] ident.setParseAction( substituteMacro ) ident.ignore(macroDef) print("(simulate #define pre-processor)") print(ident.transformString( testData )) ################# print("Example of a stripper") print("----------------------") from pyparsing import dblQuotedString, LineStart # remove all string macro definitions (after extracting to a string resource table?) stringMacroDef = Literal("#define") + ident + "=" + dblQuotedString + LineStart() stringMacroDef.setParseAction( replaceWith("") ) print(stringMacroDef.transformString( testData )) pyparsing2-2.4.7/examples/searchParserAppDemo.py000066400000000000000000000016411365333160500217110ustar00rootroot00000000000000from searchparser import SearchQueryParser products = [ "grape juice", "grape jelly", "orange juice", "orange jujubees", "strawberry jam", "prune juice", "prune butter", "orange marmalade", "grapefruit juice" ] class FruitSearchParser(SearchQueryParser): def GetWord(self, word): return { p for p in products if p.startswith(word + " ") } def GetWordWildcard(self, word): return { p for p in products if p.startswith(word[:-1]) } def GetQuotes(self, search_string, tmp_result): result = set() # I have no idea how to use this feature... return result def GetNot(self, not_set): return set( products ) - not_set parser = FruitSearchParser() tests = """\ grape or orange grape* not(grape*) prune and grape""".splitlines() for t in tests: print(t.strip()) print(parser.Parse(t)) print('') pyparsing2-2.4.7/examples/searchparser.py000066400000000000000000000242141365333160500205040ustar00rootroot00000000000000"""Search query parser version 2006-03-09 This search query parser uses the excellent Pyparsing module (http://pyparsing.sourceforge.net/) to parse search queries by users. It handles: * 'and', 'or' and implicit 'and' operators; * parentheses; * quoted strings; * wildcards at the end of a search term (help*); Requirements: * Python * Pyparsing If you run this script, it will perform a number of tests. To use is as a module, you should use inheritance on the SearchQueryParser class and overwrite the Get... methods. The ParserTest class gives a very simple example of how this could work. ------------------------------------------------------------------------------- Copyright (c) 2006, Estrate, the Netherlands All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Estrate nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. CONTRIBUTORS: - Steven Mooij - Rudolph Froger - Paul McGuire TODO: - add more docs - ask someone to check my English texts - add more kinds of wildcards ('*' at the beginning and '*' inside a word)? """ from pyparsing import Word, alphanums, Keyword, Group, Combine, Forward, Suppress, OneOrMore, oneOf class SearchQueryParser: def __init__(self): self._methods = { 'and': self.evaluateAnd, 'or': self.evaluateOr, 'not': self.evaluateNot, 'parenthesis': self.evaluateParenthesis, 'quotes': self.evaluateQuotes, 'word': self.evaluateWord, 'wordwildcard': self.evaluateWordWildcard, } self._parser = self.parser() def parser(self): """ This function returns a parser. The grammar should be like most full text search engines (Google, Tsearch, Lucene). Grammar: - a query consists of alphanumeric words, with an optional '*' wildcard at the end of a word - a sequence of words between quotes is a literal string - words can be used together by using operators ('and' or 'or') - words with operators can be grouped with parenthesis - a word or group of words can be preceded by a 'not' operator - the 'and' operator precedes an 'or' operator - if an operator is missing, use an 'and' operator """ operatorOr = Forward() operatorWord = Group(Combine(Word(alphanums) + Suppress('*'))).setResultsName('wordwildcard') | \ Group(Word(alphanums)).setResultsName('word') operatorQuotesContent = Forward() operatorQuotesContent << ( (operatorWord + operatorQuotesContent) | operatorWord ) operatorQuotes = Group( Suppress('"') + operatorQuotesContent + Suppress('"') ).setResultsName("quotes") | operatorWord operatorParenthesis = Group( (Suppress("(") + operatorOr + Suppress(")")) ).setResultsName("parenthesis") | operatorQuotes operatorNot = Forward() operatorNot << (Group( Suppress(Keyword("not", caseless=True)) + operatorNot ).setResultsName("not") | operatorParenthesis) operatorAnd = Forward() operatorAnd << (Group( operatorNot + Suppress(Keyword("and", caseless=True)) + operatorAnd ).setResultsName("and") | Group( operatorNot + OneOrMore(~oneOf("and or") + operatorAnd) ).setResultsName("and") | operatorNot) operatorOr << (Group( operatorAnd + Suppress(Keyword("or", caseless=True)) + operatorOr ).setResultsName("or") | operatorAnd) return operatorOr.parseString def evaluateAnd(self, argument): return self.evaluate(argument[0]).intersection(self.evaluate(argument[1])) def evaluateOr(self, argument): return self.evaluate(argument[0]).union(self.evaluate(argument[1])) def evaluateNot(self, argument): return self.GetNot(self.evaluate(argument[0])) def evaluateParenthesis(self, argument): return self.evaluate(argument[0]) def evaluateQuotes(self, argument): """Evaluate quoted strings First is does an 'and' on the indidual search terms, then it asks the function GetQuoted to only return the subset of ID's that contain the literal string. """ r = set() search_terms = [] for item in argument: search_terms.append(item[0]) if len(r) == 0: r = self.evaluate(item) else: r = r.intersection(self.evaluate(item)) return self.GetQuotes(' '.join(search_terms), r) def evaluateWord(self, argument): return self.GetWord(argument[0]) def evaluateWordWildcard(self, argument): return self.GetWordWildcard(argument[0]) def evaluate(self, argument): return self._methods[argument.getName()](argument) def Parse(self, query): #print self._parser(query)[0] return self.evaluate(self._parser(query)[0]) def GetWord(self, word): return set() def GetWordWildcard(self, word): return set() def GetQuotes(self, search_string, tmp_result): return set() def GetNot(self, not_set): return set().difference(not_set) class ParserTest(SearchQueryParser): """Tests the parser with some search queries tests containts a dictionary with tests and expected results. """ tests = { 'help': {1, 2, 4, 5}, 'help or hulp': {1, 2, 3, 4, 5}, 'help and hulp': {2}, 'help hulp': {2}, 'help and hulp or hilp': {2, 3, 4}, 'help or hulp and hilp': {1, 2, 3, 4, 5}, 'help or hulp or hilp or halp': {1, 2, 3, 4, 5, 6}, '(help or hulp) and (hilp or halp)': {3, 4, 5}, 'help and (hilp or halp)': {4, 5}, '(help and (hilp or halp)) or hulp': {2, 3, 4, 5}, 'not help': {3, 6, 7, 8}, 'not hulp and halp': {5, 6}, 'not (help and halp)': {1, 2, 3, 4, 6, 7, 8}, '"help me please"': {2}, '"help me please" or hulp': {2, 3}, '"help me please" or (hulp and halp)': {2}, 'help*': {1, 2, 4, 5, 8}, 'help or hulp*': {1, 2, 3, 4, 5}, 'help* and hulp': {2}, 'help and hulp* or hilp': {2, 3, 4}, 'help* or hulp or hilp or halp': {1, 2, 3, 4, 5, 6, 8}, '(help or hulp*) and (hilp* or halp)': {3, 4, 5}, 'help* and (hilp* or halp*)': {4, 5}, '(help and (hilp* or halp)) or hulp*': {2, 3, 4, 5}, 'not help* and halp': {6}, 'not (help* and helpe*)': {1, 2, 3, 4, 5, 6, 7}, '"help* me please"': {2}, '"help* me* please" or hulp*': {2, 3}, '"help me please*" or (hulp and halp)': {2}, '"help me please" not (hulp and halp)': {2}, '"help me please" hulp': {2}, 'help and hilp and not holp': {4}, 'help hilp not holp': {4}, 'help hilp and not holp': {4}, } docs = { 1: 'help', 2: 'help me please hulp', 3: 'hulp hilp', 4: 'help hilp', 5: 'halp thinks he needs help', 6: 'he needs halp', 7: 'nothing', 8: 'helper', } index = { 'help': {1, 2, 4, 5}, 'me': {2}, 'please': {2}, 'hulp': {2, 3}, 'hilp': {3, 4}, 'halp': {5, 6}, 'thinks': {5}, 'he': {5, 6}, 'needs': {5, 6}, 'nothing': {7}, 'helper': {8}, } def GetWord(self, word): if (word in self.index): return self.index[word] else: return set() def GetWordWildcard(self, word): result = set() for item in list(self.index.keys()): if word == item[0:len(word)]: result = result.union(self.index[item]) return result def GetQuotes(self, search_string, tmp_result): result = set() for item in tmp_result: if self.docs[item].count(search_string): result.add(item) return result def GetNot(self, not_set): all = set(list(self.docs.keys())) return all.difference(not_set) def Test(self): all_ok = True for item in list(self.tests.keys()): print(item) r = self.Parse(item) e = self.tests[item] print('Result: %s' % r) print('Expect: %s' % e) if e == r: print('Test OK') else: all_ok = False print('>>>>>>>>>>>>>>>>>>>>>>Test ERROR<<<<<<<<<<<<<<<<<<<<<') print('') return all_ok if __name__=='__main__': if ParserTest().Test(): print('All tests OK') else: print('One or more tests FAILED') pyparsing2-2.4.7/examples/select_parser.py000066400000000000000000000155321365333160500206600ustar00rootroot00000000000000# select_parser.py # Copyright 2010,2019 Paul McGuire # # a simple SELECT statement parser, taken from SQLite's SELECT statement # definition at https://www.sqlite.org/lang_select.html # from pyparsing import * ParserElement.enablePackrat() LPAR,RPAR,COMMA = map(Suppress,"(),") DOT,STAR = map(Literal, ".*") select_stmt = Forward().setName("select statement") # keywords (UNION, ALL, AND, INTERSECT, EXCEPT, COLLATE, ASC, DESC, ON, USING, NATURAL, INNER, CROSS, LEFT, OUTER, JOIN, AS, INDEXED, NOT, SELECT, DISTINCT, FROM, WHERE, GROUP, BY, HAVING, ORDER, BY, LIMIT, OFFSET, OR) = map(CaselessKeyword, """UNION, ALL, AND, INTERSECT, EXCEPT, COLLATE, ASC, DESC, ON, USING, NATURAL, INNER, CROSS, LEFT, OUTER, JOIN, AS, INDEXED, NOT, SELECT, DISTINCT, FROM, WHERE, GROUP, BY, HAVING, ORDER, BY, LIMIT, OFFSET, OR""".replace(",","").split()) (CAST, ISNULL, NOTNULL, NULL, IS, BETWEEN, ELSE, END, CASE, WHEN, THEN, EXISTS, IN, LIKE, GLOB, REGEXP, MATCH, ESCAPE, CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP) = map(CaselessKeyword, """CAST, ISNULL, NOTNULL, NULL, IS, BETWEEN, ELSE, END, CASE, WHEN, THEN, EXISTS, IN, LIKE, GLOB, REGEXP, MATCH, ESCAPE, CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP""".replace(",","").split()) keyword = MatchFirst((UNION, ALL, INTERSECT, EXCEPT, COLLATE, ASC, DESC, ON, USING, NATURAL, INNER, CROSS, LEFT, OUTER, JOIN, AS, INDEXED, NOT, SELECT, DISTINCT, FROM, WHERE, GROUP, BY, HAVING, ORDER, BY, LIMIT, OFFSET, CAST, ISNULL, NOTNULL, NULL, IS, BETWEEN, ELSE, END, CASE, WHEN, THEN, EXISTS, COLLATE, IN, LIKE, GLOB, REGEXP, MATCH, ESCAPE, CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP)) identifier = ~keyword + Word(alphas, alphanums+"_") collation_name = identifier.copy() column_name = identifier.copy() column_alias = identifier.copy() table_name = identifier.copy() table_alias = identifier.copy() index_name = identifier.copy() function_name = identifier.copy() parameter_name = identifier.copy() database_name = identifier.copy() # expression expr = Forward().setName("expression") integer = Regex(r"[+-]?\d+") numeric_literal = Regex(r"\d+(\.\d*)?([eE][+-]?\d+)?") string_literal = QuotedString("'") blob_literal = Regex(r"[xX]'[0-9A-Fa-f]+'") literal_value = ( numeric_literal | string_literal | blob_literal | NULL | CURRENT_TIME | CURRENT_DATE | CURRENT_TIMESTAMP ) bind_parameter = ( Word("?",nums) | Combine(oneOf(": @ $") + parameter_name) ) type_name = oneOf("TEXT REAL INTEGER BLOB NULL") expr_term = ( CAST + LPAR + expr + AS + type_name + RPAR | EXISTS + LPAR + select_stmt + RPAR | function_name.setName("function_name") + LPAR + Optional(STAR | delimitedList(expr)) + RPAR | literal_value | bind_parameter | Group(identifier('col_db') + DOT + identifier('col_tab') + DOT + identifier('col')) | Group(identifier('col_tab') + DOT + identifier('col')) | Group(identifier('col')) ) UNARY,BINARY,TERNARY=1,2,3 expr << infixNotation(expr_term, [ (oneOf('- + ~') | NOT, UNARY, opAssoc.RIGHT), (ISNULL | NOTNULL | NOT + NULL, UNARY, opAssoc.LEFT), ('||', BINARY, opAssoc.LEFT), (oneOf('* / %'), BINARY, opAssoc.LEFT), (oneOf('+ -'), BINARY, opAssoc.LEFT), (oneOf('<< >> & |'), BINARY, opAssoc.LEFT), (oneOf('< <= > >='), BINARY, opAssoc.LEFT), (oneOf('= == != <>') | IS | IN | LIKE | GLOB | MATCH | REGEXP, BINARY, opAssoc.LEFT), ((BETWEEN,AND), TERNARY, opAssoc.LEFT), (IN + LPAR + Group(select_stmt | delimitedList(expr)) + RPAR, UNARY, opAssoc.LEFT), (AND, BINARY, opAssoc.LEFT), (OR, BINARY, opAssoc.LEFT), ]) compound_operator = (UNION + Optional(ALL) | INTERSECT | EXCEPT) ordering_term = Group(expr('order_key') + Optional(COLLATE + collation_name('collate')) + Optional(ASC | DESC)('direction')) join_constraint = Group(Optional(ON + expr | USING + LPAR + Group(delimitedList(column_name)) + RPAR)) join_op = COMMA | Group(Optional(NATURAL) + Optional(INNER | CROSS | LEFT + OUTER | LEFT | OUTER) + JOIN) join_source = Forward() single_source = ( Group(database_name("database") + DOT + table_name("table*") | table_name("table*")) + Optional(Optional(AS) + table_alias("table_alias*")) + Optional(INDEXED + BY + index_name("name") | NOT + INDEXED)("index") | (LPAR + select_stmt + RPAR + Optional(Optional(AS) + table_alias)) | (LPAR + join_source + RPAR) ) join_source <<= (Group(single_source + OneOrMore(join_op + single_source + join_constraint)) | single_source) # result_column = "*" | table_name + "." + "*" | Group(expr + Optional(Optional(AS) + column_alias)) result_column = Group(STAR('col') | table_name('col_table') + DOT + STAR('col') | expr('col') + Optional(Optional(AS) + column_alias('alias')) ) select_core = (SELECT + Optional(DISTINCT | ALL) + Group(delimitedList(result_column))("columns") + Optional(FROM + join_source("from*")) + Optional(WHERE + expr("where_expr")) + Optional(GROUP + BY + Group(delimitedList(ordering_term))("group_by_terms") + Optional(HAVING + expr("having_expr")))) select_stmt << (select_core + ZeroOrMore(compound_operator + select_core) + Optional(ORDER + BY + Group(delimitedList(ordering_term))("order_by_terms")) + Optional(LIMIT + (Group(expr + OFFSET + expr) | Group(expr + COMMA + expr) | expr)("limit")) ) tests = """\ select * from xyzzy where z > 100 select * from xyzzy where z > 100 order by zz select * from xyzzy select z.* from xyzzy select a, b from test_table where 1=1 and b='yes' select a, b from test_table where 1=1 and b in (select bb from foo) select z.a, b from test_table where 1=1 and b in (select bb from foo) select z.a, b from test_table where 1=1 and b in (select bb from foo) order by b,c desc,d select z.a, b from test_table left join test2_table where 1=1 and b in (select bb from foo) select a, db.table.b as BBB from db.table where 1=1 and BBB='yes' select a, db.table.b as BBB from test_table,db.table where 1=1 and BBB='yes' select a, db.table.b as BBB from test_table,db.table where 1=1 and BBB='yes' limit 50 select a, b from test_table where (1=1 or 2=3) and b='yes' group by zx having b=2 order by 1 SELECT emp.ename as e FROM scott.employee as emp SELECT ename as e, fname as f FROM scott.employee as emp SELECT emp.eid, fname,lname FROM scott.employee as emp SELECT ename, lname, emp.eid FROM scott.employee as emp select emp.salary * (1.0 + emp.bonus) as salary_plus_bonus from scott.employee as emp """ select_stmt.runTests(tests) pyparsing2-2.4.7/examples/sexpParser.py000066400000000000000000000116521365333160500201600ustar00rootroot00000000000000# sexpParser.py # # Demonstration of the pyparsing module, implementing a simple S-expression # parser. # # Updates: # November, 2011 - fixed errors in precedence of alternatives in simpleString; # fixed exception raised in verifyLen to properly signal the input string # and exception location so that markInputline works correctly; fixed # definition of decimal to accept a single '0' and optional leading '-' # sign; updated tests to improve parser coverage # # Copyright 2007-2011, by Paul McGuire # """ BNF reference: http://theory.lcs.mit.edu/~rivest/sexp.txt :: | :: ? ; :: | | | | ; :: "[" "]" ; :: ":" ; :: + ; -- decimal numbers should have no unnecessary leading zeros -- any string of bytes, of the indicated length :: + ; :: ? "|" ( | )* "|" ; :: "#" ( | )* "#" ; :: ? :: "\"" "\"" :: "(" ( | )* ")" ; :: * ; :: | | ; :: | | ; :: "a" | ... | "z" ; :: "A" | ... | "Z" ; :: "0" | ... | "9" ; :: | "A" | ... | "F" | "a" | ... | "f" ; :: "-" | "." | "/" | "_" | ":" | "*" | "+" | "=" ; :: " " | "\t" | "\r" | "\n" ; :: | | "+" | "/" | "=" ; :: "" ; """ import pyparsing as pp from base64 import b64decode import pprint def verify_length(s, l, t): t = t[0] if t.len is not None: t1len = len(t[1]) if t1len != t.len: raise pp.ParseFatalException(s, l, "invalid data of length {0}, expected {1}".format(t1len, t.len)) return t[1] # define punctuation literals LPAR, RPAR, LBRK, RBRK, LBRC, RBRC, VBAR, COLON = (pp.Suppress(c).setName(c) for c in "()[]{}|:") decimal = pp.Regex(r'-?0|[1-9]\d*').setParseAction(lambda t: int(t[0])) hexadecimal = ("#" + pp.Word(pp.hexnums)[1, ...] + "#").setParseAction(lambda t: int("".join(t[1:-1]), 16)) bytes = pp.Word(pp.printables) raw = pp.Group(decimal("len") + COLON + bytes).setParseAction(verify_length) base64_ = pp.Group(pp.Optional(decimal | hexadecimal, default=None)("len") + VBAR + pp.Word(pp.alphanums + "+/=")[1, ...].setParseAction(lambda t: b64decode("".join(t))) + VBAR ).setParseAction(verify_length) real = pp.Regex(r"[+-]?\d+\.\d*([eE][+-]?\d+)?").setParseAction(lambda tokens: float(tokens[0])) token = pp.Word(pp.alphanums + "-./_:*+=!<>") qString = pp.Group(pp.Optional(decimal, default=None)("len") + pp.dblQuotedString.setParseAction(pp.removeQuotes) ).setParseAction(verify_length) simpleString = real | base64_ | raw | decimal | token | hexadecimal | qString display = LBRK + simpleString + RBRK string_ = pp.Optional(display) + simpleString sexp = pp.Forward() sexpList = pp.Group(LPAR + sexp[...] + RPAR) sexp <<= string_ | sexpList # Test data test00 = """(snicker "abc" (#03# |YWJj|))""" test01 = """(certificate (issuer (name (public-key rsa-with-md5 (e 15 |NFGq/E3wh9f4rJIQVXhS|) (n |d738/4ghP9rFZ0gAIYZ5q9y6iskDJwASi5rEQpEQq8ZyMZeIZzIAR2I5iGE=|)) aid-committee)) (subject (ref (public-key rsa-with-md5 (e |NFGq/E3wh9f4rJIQVXhS|) (n |d738/4ghP9rFZ0gAIYZ5q9y6iskDJwASi5rEQpEQq8ZyMZeIZzIAR2I5iGE=|)) tom mother)) (not-before "1997-01-01_09:00:00") (not-after "1998-01-01_09:00:00") (tag (spend (account "12345678") (* numeric range "1" "1000")))) """ test02 = """(lambda (x) (* x x))""" test03 = """(def length (lambda (x) (cond ((not x) 0) ( t (+ 1 (length (cdr x)))) ) ) ) """ test04 = """(2:XX "abc" (#03# |YWJj|))""" test05 = """(if (is (window_name) "XMMS") (set_workspace 2))""" test06 = """(if (and (is (application_name) "Firefox") (or (contains (window_name) "Enter name of file to save to") (contains (window_name) "Save As") (contains (window_name) "Save Image") () ) ) (geometry "+140+122") ) """ test07 = """(defun factorial (x) (if (zerop x) 1 (* x (factorial (- x 1))))) """ test51 = """(2:XX "abc" (#03# |YWJj|))""" test51error = """(3:XX "abc" (#03# |YWJj|))""" test52 = """ (and (or (> uid 1000) (!= gid 20) ) (> quota 5.0e+03) ) """ # Run tests alltests = [globals()[testname] for testname in sorted(locals()) if testname.startswith("test")] sexp.runTests(alltests, fullDump=False) pyparsing2-2.4.7/examples/shapes.py000066400000000000000000000032641365333160500173070ustar00rootroot00000000000000# shapes.py # # A sample program showing how parse actions can convert parsed # strings into a data type or object. # # Copyright 2012, Paul T. McGuire # # define class hierarchy of Shape classes, with polymorphic area method class Shape(object): def __init__(self, tokens): self.__dict__.update(tokens.asDict()) def area(self): raise NotImplementedException() def __str__(self): return "<{0}>: {1}".format(self.__class__.__name__, self.__dict__) class Square(Shape): def area(self): return self.side**2 class Rectangle(Shape): def area(self): return self.width * self.height class Circle(Shape): def area(self): return 3.14159 * self.radius**2 from pyparsing import * number = Regex(r'-?\d+(\.\d*)?').setParseAction(lambda t:float(t[0])) # Shape expressions: # square : S # rectangle: R # circle : C squareDefn = "S" + number('centerx') + number('centery') + number('side') rectDefn = "R" + number('centerx') + number('centery') + number('width') + number('height') circleDefn = "C" + number('centerx') + number('centery') + number('diameter') squareDefn.setParseAction(Square) rectDefn.setParseAction(Rectangle) def computeRadius(tokens): tokens['radius'] = tokens.diameter/2.0 circleDefn.setParseAction(computeRadius, Circle) shapeExpr = squareDefn | rectDefn | circleDefn tests = """\ C 0 0 100 R 10 10 20 50 S -1 5 10""".splitlines() for t in tests: shape = shapeExpr.parseString(t)[0] print(shape) print("Area:", shape.area()) print() pyparsing2-2.4.7/examples/simpleArith.py000066400000000000000000000043611365333160500203040ustar00rootroot00000000000000# # simpleArith.py # # Example of defining an arithmetic expression parser using # the infixNotation helper method in pyparsing. # # Copyright 2006, by Paul McGuire # from pyparsing import * integer = Word(nums).setParseAction(lambda t:int(t[0])) variable = Word(alphas,exact=1) operand = integer | variable expop = Literal('^') signop = oneOf('+ -') multop = oneOf('* /') plusop = oneOf('+ -') factop = Literal('!') # To use the infixNotation helper: # 1. Define the "atom" operand term of the grammar. # For this simple grammar, the smallest operand is either # and integer or a variable. This will be the first argument # to the infixNotation method. # 2. Define a list of tuples for each level of operator # precendence. 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 # - numTerms is the number of terms for this operator (must # be 1 or 2) # - 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) # 3. Call infixNotation passing the operand expression and # the operator precedence list, and save the returned value # as the generated pyparsing expression. You can then use # this expression to parse input strings, or incorporate it # into a larger, more complex grammar. # expr = infixNotation( operand, [("!", 1, opAssoc.LEFT), ("^", 2, opAssoc.RIGHT), (signop, 1, opAssoc.RIGHT), (multop, 2, opAssoc.LEFT), (plusop, 2, opAssoc.LEFT),] ) test = ["9 + 2 + 3", "9 + 2 * 3", "(9 + 2) * 3", "(9 + -2) * 3", "(9 + -2) * 3^2^2", "(9! + -2) * 3^2^2", "M*X + B", "M*(X + B)", "1+2*-3^4*5+-+-6",] for t in test: print(t) print(expr.parseString(t)) print('') pyparsing2-2.4.7/examples/simpleBool.py000066400000000000000000000054641365333160500201350ustar00rootroot00000000000000# # simpleBool.py # # Example of defining a boolean logic parser using # the operatorGrammar helper method in pyparsing. # # In this example, parse actions associated with each # operator expression will "compile" the expression # into BoolXXX class instances, which can then # later be evaluated for their boolean value. # # Copyright 2006, by Paul McGuire # Updated 2013-Sep-14 - improved Python 2/3 cross-compatibility # from pyparsing import infixNotation, opAssoc, Keyword, Word, alphas # define classes to be built at parse time, as each matching # expression type is parsed class BoolOperand(object): def __init__(self,t): self.label = t[0] self.value = eval(t[0]) def __bool__(self): return self.value def __str__(self): return self.label __repr__ = __str__ __nonzero__ = __bool__ class BoolBinOp(object): def __init__(self,t): self.args = t[0][0::2] def __str__(self): sep = " %s " % self.reprsymbol return "(" + sep.join(map(str,self.args)) + ")" def __bool__(self): return self.evalop(bool(a) for a in self.args) __nonzero__ = __bool__ __repr__ = __str__ class BoolAnd(BoolBinOp): reprsymbol = '&' evalop = all class BoolOr(BoolBinOp): reprsymbol = '|' evalop = any class BoolNot(object): def __init__(self,t): self.arg = t[0][1] def __bool__(self): v = bool(self.arg) return not v def __str__(self): return "~" + str(self.arg) __repr__ = __str__ __nonzero__ = __bool__ TRUE = Keyword("True") FALSE = Keyword("False") boolOperand = TRUE | FALSE | Word(alphas,max=1) boolOperand.setParseAction(BoolOperand) # define expression, based on expression operand and # list of operations in precedence order boolExpr = infixNotation( boolOperand, [ ("not", 1, opAssoc.RIGHT, BoolNot), ("and", 2, opAssoc.LEFT, BoolAnd), ("or", 2, opAssoc.LEFT, BoolOr), ]) if __name__ == "__main__": p = True q = False r = True tests = [("p", True), ("q", False), ("p and q", False), ("p and not q", True), ("not not p", True), ("not(p and q)", True), ("q or not p and r", False), ("q or not p or not r", False), ("q or not (p and r)", False), ("p or q or r", True), ("p or q or r and False", True), ("(p or q or r) and False", False), ] print("p =", p) print("q =", q) print("r =", r) print() for t,expected in tests: res = boolExpr.parseString(t)[0] success = "PASS" if bool(res) == expected else "FAIL" print (t,'\n', res, '=', bool(res),'\n', success, '\n') pyparsing2-2.4.7/examples/simpleSQL.py000066400000000000000000000056101365333160500176720ustar00rootroot00000000000000# simpleSQL.py # # simple demo of using the parsing library to do simple-minded SQL parsing # could be extended to include where clauses etc. # # Copyright (c) 2003,2016, Paul McGuire # from pyparsing import Word, delimitedList, Optional, \ Group, alphas, alphanums, Forward, oneOf, quotedString, \ infixNotation, opAssoc, \ ZeroOrMore, restOfLine, CaselessKeyword, pyparsing_common as ppc # define SQL tokens selectStmt = Forward() SELECT, FROM, WHERE, AND, OR, IN, IS, NOT, NULL = map(CaselessKeyword, "select from where and or in is not null".split()) NOT_NULL = NOT + NULL ident = Word( alphas, alphanums + "_$" ).setName("identifier") columnName = delimitedList(ident, ".", combine=True).setName("column name") columnName.addParseAction(ppc.upcaseTokens) columnNameList = Group( delimitedList(columnName)) tableName = delimitedList(ident, ".", combine=True).setName("table name") tableName.addParseAction(ppc.upcaseTokens) tableNameList = Group(delimitedList(tableName)) binop = oneOf("= != < > >= <= eq ne lt le gt ge", caseless=True) realNum = ppc.real() intNum = ppc.signed_integer() columnRval = realNum | intNum | quotedString | columnName # need to add support for alg expressions whereCondition = Group( ( columnName + binop + columnRval ) | ( columnName + IN + Group("(" + delimitedList( columnRval ) + ")" )) | ( columnName + IN + Group("(" + selectStmt + ")" )) | ( columnName + IS + (NULL | NOT_NULL)) ) whereExpression = infixNotation(whereCondition, [ (NOT, 1, opAssoc.RIGHT), (AND, 2, opAssoc.LEFT), (OR, 2, opAssoc.LEFT), ]) # define the grammar selectStmt <<= (SELECT + ('*' | columnNameList)("columns") + FROM + tableNameList( "tables" ) + Optional(Group(WHERE + whereExpression), "")("where")) simpleSQL = selectStmt # define Oracle comment format, and ignore them oracleSqlComment = "--" + restOfLine simpleSQL.ignore( oracleSqlComment ) if __name__ == "__main__": simpleSQL.runTests("""\ # multiple tables SELECT * from XYZZY, ABC # dotted table name select * from SYS.XYZZY Select A from Sys.dual Select A,B,C from Sys.dual Select A, B, C from Sys.dual, Table2 # FAIL - invalid SELECT keyword Xelect A, B, C from Sys.dual # FAIL - invalid FROM keyword Select A, B, C frox Sys.dual # FAIL - incomplete statement Select # FAIL - incomplete statement Select * from # FAIL - invalid column Select &&& frox Sys.dual # where clause Select A from Sys.dual where a in ('RED','GREEN','BLUE') # compound where clause Select A from Sys.dual where a in ('RED','GREEN','BLUE') and b in (10,20,30) # where clause with comparison operator Select A,b from table1,table2 where table1.id eq table2.id """) pyparsing2-2.4.7/examples/simpleWiki.py000066400000000000000000000020741365333160500201370ustar00rootroot00000000000000from pyparsing import * wikiInput = """ Here is a simple Wiki input: *This is in italics.* **This is in bold!** ***This is in bold italics!*** Here's a URL to {{Pyparsing's Wiki Page->https://site-closed.wikispaces.com}} """ def convertToHTML(opening,closing): def conversionParseAction(s,l,t): return opening + t[0] + closing return conversionParseAction italicized = QuotedString("*").setParseAction(convertToHTML("","")) bolded = QuotedString("**").setParseAction(convertToHTML("","")) boldItalicized = QuotedString("***").setParseAction(convertToHTML("","")) def convertToHTML_A(s,l,t): try: text,url=t[0].split("->") except ValueError: raise ParseFatalException(s,l,"invalid URL link reference: " + t[0]) return '{1}'.format(url, text) urlRef = QuotedString("{{",endQuoteChar="}}").setParseAction(convertToHTML_A) wikiMarkup = urlRef | boldItalicized | bolded | italicized print(wikiInput) print() print(wikiMarkup.transformString(wikiInput)) pyparsing2-2.4.7/examples/snmp_api.h000066400000000000000000000712751365333160500174400ustar00rootroot00000000000000#ifndef SNMP_API_H #define SNMP_API_H /* * snmp_api.h - API for access to snmp. * * Caution: when using this library in a multi-threaded application, * the values of global variables "snmp_errno" and "snmp_detail" * cannot be reliably determined. Suggest using snmp_error() * to obtain the library error codes. */ #ifndef DONT_SHARE_ERROR_WITH_OTHER_THREADS #define SET_SNMP_ERROR(x) snmp_errno=(x) #else #define SET_SNMP_ERROR(x) #endif #ifdef __cplusplus extern "C" { #endif /*********************************************************** Copyright 1989 by Carnegie Mellon University All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of CMU not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ struct variable_list; struct timeval; /* * Mimic size and alignment of 'struct sockaddr_storage' (see RFC 2553) * But retain field names of traditional 'struct sockaddr' */ #define _UCD_SS_MAXSIZE 92 /* <= sizeof( sockaddr_un ) */ #define _UCD_SS_ALIGNSIZE (sizeof (long)) #define _UCD_SS_PAD1SIZE (_UCD_SS_ALIGNSIZE - sizeof( unsigned short )) #define _UCD_SS_PAD2SIZE (_UCD_SS_MAXSIZE - \ (sizeof( unsigned short ) + _UCD_SS_PAD1SIZE + _UCD_SS_ALIGNSIZE )) typedef struct { #ifdef STRUCT_SOCKADDR_HAS_SA_UNION_SA_GENERIC_SA_FAMILY2 /* * Certain systems (notably Irix 6.x) have a non-traditional * socket structure, and #define the traditional field names. * This local definition should reproduce this structure, and still * be large enough to handle any necessary Unix domain addresses. */ union { struct { #ifdef _HAVE_SA_LEN unsigned char sa_len2; unsigned char sa_family2; #else unsigned short sa_family2; #endif char sa_data2[ _UCD_SS_PAD1SIZE ]; } sa_generic; long sa_align; char sa_pad2[ _UCD_SS_PAD2SIZE ]; } sa_union; #else #ifdef STRUCT_SOCKADDR_HAS_SA_LEN unsigned char sa_len; unsigned char sa_family; #else unsigned short sa_family; #endif char sa_data[ _UCD_SS_PAD1SIZE ]; long sa_align; char sa_pad2[ _UCD_SS_PAD2SIZE ]; #endif } snmp_ipaddr; #define USM_AUTH_KU_LEN 32 #define USM_PRIV_KU_LEN 32 struct snmp_pdu { /* * Protocol-version independent fields */ long version; int command; /* Type of this PDU */ long reqid; /* Request id - note: not incremented on retries */ long msgid; /* Message id for V3 messages * note: incremented for each retry */ long transid; /* Unique ID for incoming transactions */ long sessid; /* Session id for AgentX messages */ long errstat; /* Error status (non_repeaters in GetBulk) */ long errindex; /* Error index (max_repetitions in GetBulk) */ u_long time; /* Uptime */ u_long flags; int securityModel; int securityLevel; /* noAuthNoPriv, authNoPriv, authPriv */ int msgParseModel; snmp_ipaddr address; /* Address of peer or trap destination */ struct variable_list *variables; /* * SNMPv1 & SNMPv2c fields */ u_char *community; /* community for outgoing requests. */ size_t community_len; /* Length of community name. */ /* * Trap information */ oid *enterprise; /* System OID */ size_t enterprise_length; long trap_type; /* trap type */ long specific_type; /* specific type */ snmp_ipaddr agent_addr; /* * SNMPv3 fields */ u_char *contextEngineID; /* context snmpEngineID */ size_t contextEngineIDLen; /* Length of contextEngineID */ char *contextName; /* authoritative contextName */ size_t contextNameLen; /* Length of contextName */ u_char *securityEngineID; /* authoritative snmpEngineID for security */ size_t securityEngineIDLen;/* Length of securityEngineID */ char *securityName; /* on behalf of this principal */ size_t securityNameLen; /* Length of securityName. */ /* * AgentX fields * (also uses SNMPv1 community field) */ int priority; int range_subid; void * securityStateRef; }; struct snmp_session; typedef int (*snmp_callback) (int, struct snmp_session *, int, struct snmp_pdu *, void *); struct snmp_session { /* * Protocol-version independent fields */ long version; int retries; /* Number of retries before timeout. */ long timeout; /* Number of uS until first timeout, then exponential backoff */ u_long flags; struct snmp_session *subsession; struct snmp_session *next; char *peername; /* Domain name or dotted IP address of default peer */ u_short remote_port;/* UDP port number of peer. */ u_short local_port; /* My UDP port number, 0 for default, picked randomly */ /* Authentication function or NULL if null authentication is used */ u_char *(*authenticator) (u_char *, size_t *, u_char *, size_t); snmp_callback callback; /* Function to interpret incoming data */ /* Pointer to data that the callback function may consider important */ void *callback_magic; int s_errno; /* copy of system errno */ int s_snmp_errno; /* copy of library errno */ long sessid; /* Session id - AgentX only */ /* * SNMPv1 & SNMPv2c fields */ u_char *community; /* community for outgoing requests. */ size_t community_len; /* Length of community name. */ /* * SNMPv3 fields */ u_char isAuthoritative; /* are we the authoritative engine? */ u_char *contextEngineID; /* authoritative snmpEngineID */ size_t contextEngineIDLen; /* Length of contextEngineID */ u_int engineBoots; /* initial engineBoots for remote engine */ u_int engineTime; /* initial engineTime for remote engine */ char *contextName; /* authoritative contextName */ size_t contextNameLen; /* Length of contextName */ u_char *securityEngineID; /* authoritative snmpEngineID */ size_t securityEngineIDLen; /* Length of contextEngineID */ char *securityName; /* on behalf of this principal */ size_t securityNameLen; /* Length of securityName. */ oid *securityAuthProto; /* auth protocol oid */ size_t securityAuthProtoLen; /* Length of auth protocol oid */ u_char securityAuthKey[USM_AUTH_KU_LEN]; /* Ku for auth protocol XXX */ size_t securityAuthKeyLen; /* Length of Ku for auth protocol */ oid *securityPrivProto; /* priv protocol oid */ size_t securityPrivProtoLen; /* Length of priv protocol oid */ u_char securityPrivKey[USM_PRIV_KU_LEN]; /* Ku for privacy protocol XXX */ size_t securityPrivKeyLen; /* Length of Ku for priv protocol */ int securityModel; int securityLevel; /* noAuthNoPriv, authNoPriv, authPriv */ }; /* * A list of all the outstanding requests for a particular session. */ #ifdef SNMP_NEED_REQUEST_LIST struct request_list { struct request_list *next_request; long request_id; /* request id */ long message_id; /* message id */ snmp_callback callback; /* user callback per request (NULL if unused) */ void *cb_data; /* user callback data per request (NULL if unused) */ int retries; /* Number of retries */ u_long timeout; /* length to wait for timeout */ struct timeval time; /* Time this request was made */ struct timeval expire; /* time this request is due to expire */ struct snmp_session *session; struct snmp_pdu *pdu; /* The pdu for this request (saved so it can be retransmitted */ }; #endif /* SNMP_NEED_REQUEST_LIST */ /* * Set fields in session and pdu to the following to get a default or unconfigured value. */ #define SNMP_DEFAULT_COMMUNITY_LEN 0 /* to get a default community name */ #define SNMP_DEFAULT_RETRIES -1 #define SNMP_DEFAULT_TIMEOUT -1 #define SNMP_DEFAULT_REMPORT 0 #define SNMP_DEFAULT_REQID -1 #define SNMP_DEFAULT_MSGID -1 #define SNMP_DEFAULT_ERRSTAT -1 #define SNMP_DEFAULT_ERRINDEX -1 #define SNMP_DEFAULT_ADDRESS 0 #define SNMP_DEFAULT_PEERNAME NULL #define SNMP_DEFAULT_ENTERPRISE_LENGTH 0 #define SNMP_DEFAULT_TIME 0 #define SNMP_DEFAULT_VERSION -1 #define SNMP_DEFAULT_CONTEXT "" #define SNMP_DEFAULT_AUTH_PROTO usmHMACMD5AuthProtocol #define SNMP_DEFAULT_AUTH_PROTOLEN USM_LENGTH_OID_TRANSFORM #define SNMP_DEFAULT_PRIV_PROTO usmDESPrivProtocol #define SNMP_DEFAULT_PRIV_PROTOLEN USM_LENGTH_OID_TRANSFORM extern const char *snmp_api_errstring (int); extern void snmp_perror (const char *); extern void snmp_set_detail (const char *); #define SNMP_MAX_MSG_SIZE 1472 /* ethernet MTU minus IP/UDP header */ #define SNMP_MAX_MSG_V3_HDRS (4+3+4+7+7+3+7+16) /* fudge factor=16 */ #define SNMP_MAX_ENG_SIZE 32 #define SNMP_MAX_SEC_NAME_SIZE 256 #define SNMP_MAX_CONTEXT_SIZE 256 #define SNMP_SEC_PARAM_BUF_SIZE 256 /* set to one to ignore unauthenticated Reports */ #define SNMPV3_IGNORE_UNAUTH_REPORTS 0 /* authoritative engine definitions */ #define SNMP_SESS_NONAUTHORITATIVE 0 /* should be 0 to default to this */ #define SNMP_SESS_AUTHORITATIVE 1 /* don't learn engineIDs */ #define SNMP_SESS_UNKNOWNAUTH 2 /* sometimes (like NRs) */ /* to determine type of Report from varbind_list */ #define REPORT_STATS_LEN 9 #define REPORT_snmpUnknownSecurityModels_NUM 1 #define REPORT_snmpInvalidMsgs_NUM 2 #define REPORT_usmStatsUnsupportedSecLevels_NUM 1 #define REPORT_usmStatsNotInTimeWindows_NUM 2 #define REPORT_usmStatsUnknownUserNames_NUM 3 #define REPORT_usmStatsUnknownEngineIDs_NUM 4 #define REPORT_usmStatsWrongDigests_NUM 5 #define REPORT_usmStatsDecryptionErrors_NUM 6 #define SNMP_DETAIL_SIZE 512 #define SNMP_FLAGS_DONT_PROBE 0x100 /* don't probe for an engineID */ #define SNMP_FLAGS_STREAM_SOCKET 0x80 #define SNMP_FLAGS_LISTENING 0x40 /* Server stream sockets only */ #define SNMP_FLAGS_SUBSESSION 0x20 #define SNMP_FLAGS_STRIKE2 0x02 #define SNMP_FLAGS_STRIKE1 0x01 #define CLEAR_SNMP_STRIKE_FLAGS(x) \ x &= ~(SNMP_FLAGS_STRIKE2|SNMP_FLAGS_STRIKE1) /* * returns '1' if the session is to be regarded as dead, * otherwise set the strike flags appropriately, and return 0 */ #define SET_SNMP_STRIKE_FLAGS(x) \ (( x & SNMP_FLAGS_STRIKE2 ) ? 1 : \ ((( x & SNMP_FLAGS_STRIKE1 ) ? ( x |= SNMP_FLAGS_STRIKE2 ) : \ ( x |= SNMP_FLAGS_STRIKE1 )), \ 0)) /* * Error return values. * * SNMPERR_SUCCESS is the non-PDU "success" code. * * XXX These should be merged with SNMP_ERR_* defines and confined * to values < 0. ??? */ #define SNMPERR_SUCCESS (0) /* XXX Non-PDU "success" code. */ #define SNMPERR_GENERR (-1) #define SNMPERR_BAD_LOCPORT (-2) #define SNMPERR_BAD_ADDRESS (-3) #define SNMPERR_BAD_SESSION (-4) #define SNMPERR_TOO_LONG (-5) #define SNMPERR_NO_SOCKET (-6) #define SNMPERR_V2_IN_V1 (-7) #define SNMPERR_V1_IN_V2 (-8) #define SNMPERR_BAD_REPEATERS (-9) #define SNMPERR_BAD_REPETITIONS (-10) #define SNMPERR_BAD_ASN1_BUILD (-11) #define SNMPERR_BAD_SENDTO (-12) #define SNMPERR_BAD_PARSE (-13) #define SNMPERR_BAD_VERSION (-14) #define SNMPERR_BAD_SRC_PARTY (-15) #define SNMPERR_BAD_DST_PARTY (-16) #define SNMPERR_BAD_CONTEXT (-17) #define SNMPERR_BAD_COMMUNITY (-18) #define SNMPERR_NOAUTH_DESPRIV (-19) #define SNMPERR_BAD_ACL (-20) #define SNMPERR_BAD_PARTY (-21) #define SNMPERR_ABORT (-22) #define SNMPERR_UNKNOWN_PDU (-23) #define SNMPERR_TIMEOUT (-24) #define SNMPERR_BAD_RECVFROM (-25) #define SNMPERR_BAD_ENG_ID (-26) #define SNMPERR_BAD_SEC_NAME (-27) #define SNMPERR_BAD_SEC_LEVEL (-28) #define SNMPERR_ASN_PARSE_ERR (-29) #define SNMPERR_UNKNOWN_SEC_MODEL (-30) #define SNMPERR_INVALID_MSG (-31) #define SNMPERR_UNKNOWN_ENG_ID (-32) #define SNMPERR_UNKNOWN_USER_NAME (-33) #define SNMPERR_UNSUPPORTED_SEC_LEVEL (-34) #define SNMPERR_AUTHENTICATION_FAILURE (-35) #define SNMPERR_NOT_IN_TIME_WINDOW (-36) #define SNMPERR_DECRYPTION_ERR (-37) #define SNMPERR_SC_GENERAL_FAILURE (-38) #define SNMPERR_SC_NOT_CONFIGURED (-39) #define SNMPERR_KT_NOT_AVAILABLE (-40) #define SNMPERR_UNKNOWN_REPORT (-41) #define SNMPERR_USM_GENERICERROR (-42) #define SNMPERR_USM_UNKNOWNSECURITYNAME (-43) #define SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL (-44) #define SNMPERR_USM_ENCRYPTIONERROR (-45) #define SNMPERR_USM_AUTHENTICATIONFAILURE (-46) #define SNMPERR_USM_PARSEERROR (-47) #define SNMPERR_USM_UNKNOWNENGINEID (-48) #define SNMPERR_USM_NOTINTIMEWINDOW (-49) #define SNMPERR_USM_DECRYPTIONERROR (-50) #define SNMPERR_NOMIB (-51) #define SNMPERR_RANGE (-52) #define SNMPERR_MAX_SUBID (-53) #define SNMPERR_BAD_SUBID (-54) #define SNMPERR_LONG_OID (-55) #define SNMPERR_BAD_NAME (-56) #define SNMPERR_VALUE (-57) #define SNMPERR_UNKNOWN_OBJID (-58) #define SNMPERR_NULL_PDU (-59) #define SNMPERR_NO_VARS (-60) #define SNMPERR_VAR_TYPE (-61) #define SNMPERR_MALLOC (-62) #define SNMPERR_MAX (-62) #define non_repeaters errstat #define max_repetitions errindex struct variable_list { struct variable_list *next_variable; /* NULL for last variable */ oid *name; /* Object identifier of variable */ size_t name_length; /* number of subid's in name */ u_char type; /* ASN type of variable */ union { /* value of variable */ long *integer; u_char *string; oid *objid; u_char *bitstring; struct counter64 *counter64; #ifdef OPAQUE_SPECIAL_TYPES float *floatVal; double *doubleVal; /* t_union *unionVal; */ #endif /* OPAQUE_SPECIAL_TYPES */ } val; size_t val_len; oid name_loc[MAX_OID_LEN]; /* 90 percentile < 24. */ u_char buf[40]; /* 90 percentile < 40. */ void *data; /* (Opaque) hook for additional data */ int index; }; /* * struct snmp_session *snmp_open(session) * struct snmp_session *session; * * Sets up the session with the snmp_session information provided * by the user. Then opens and binds the necessary UDP port. * A handle to the created session is returned (this is different than * the pointer passed to snmp_open()). On any error, NULL is returned * and snmp_errno is set to the appropriate error code. */ struct snmp_session *snmp_open (struct snmp_session *); /* * int snmp_close(session) * struct snmp_session *session; * * Close the input session. Frees all data allocated for the session, * dequeues any pending requests, and closes any sockets allocated for * the session. Returns 0 on error, 1 otherwise. * * snmp_close_sessions() does the same thing for all open sessions */ int snmp_close (struct snmp_session *); int snmp_close_sessions (void); /* * int snmp_send(session, pdu) * struct snmp_session *session; * struct snmp_pdu *pdu; * * Sends the input pdu on the session after calling snmp_build to create * a serialized packet. If necessary, set some of the pdu data from the * session defaults. Add a request corresponding to this pdu to the list * of outstanding requests on this session, then send the pdu. * Returns the request id of the generated packet if applicable, otherwise 1. * On any error, 0 is returned. * The pdu is freed by snmp_send() unless a failure occured. */ int snmp_send (struct snmp_session *, struct snmp_pdu *); /* * int snmp_async_send(session, pdu, callback, cb_data) * struct snmp_session *session; * struct snmp_pdu *pdu; * snmp_callback callback; * void *cb_data; * * Sends the input pdu on the session after calling snmp_build to create * a serialized packet. If necessary, set some of the pdu data from the * session defaults. Add a request corresponding to this pdu to the list * of outstanding requests on this session and store callback and data, * then send the pdu. * Returns the request id of the generated packet if applicable, otherwise 1. * On any error, 0 is returned. * The pdu is freed by snmp_send() unless a failure occured. */ int snmp_async_send (struct snmp_session *, struct snmp_pdu *, snmp_callback, void *); /* * void snmp_read(fdset) * fd_set *fdset; * * Checks to see if any of the fd's set in the fdset belong to * snmp. Each socket with it's fd set has a packet read from it * and snmp_parse is called on the packet received. The resulting pdu * is passed to the callback routine for that session. If the callback * routine returns successfully, the pdu and it's request are deleted. */ void snmp_read (fd_set *); /* * void * snmp_free_pdu(pdu) * struct snmp_pdu *pdu; * * Frees the pdu and any malloc'd data associated with it. */ void snmp_free_pdu (struct snmp_pdu *); void snmp_free_var (struct variable_list *); /* frees just this one */ void snmp_free_varbind(struct variable_list *var); /* frees all in list */ /* * int snmp_select_info(numfds, fdset, timeout, block) * int *numfds; * fd_set *fdset; * struct timeval *timeout; * int *block; * * Returns info about what snmp requires from a select statement. * numfds is the number of fds in the list that are significant. * All file descriptors opened for SNMP are OR'd into the fdset. * If activity occurs on any of these file descriptors, snmp_read * should be called with that file descriptor set. * * The timeout is the latest time that SNMP can wait for a timeout. The * select should be done with the minimum time between timeout and any other * timeouts necessary. This should be checked upon each invocation of select. * If a timeout is received, snmp_timeout should be called to check if the * timeout was for SNMP. (snmp_timeout is idempotent) * * Block is 1 if the select is requested to block indefinitely, rather than * time out. If block is input as 1, the timeout value will be treated as * undefined, but it must be available for setting in snmp_select_info. On * return, if block is true, the value of timeout will be undefined. * * snmp_select_info returns the number of open sockets. (i.e. The number * of sessions open) */ int snmp_select_info (int *, fd_set *, struct timeval *, int *); /* * void snmp_timeout(); * * snmp_timeout should be called whenever the timeout from snmp_select_info * expires, but it is idempotent, so snmp_timeout can be polled (probably a * cpu expensive proposition). snmp_timeout checks to see if any of the * sessions have an outstanding request that has timed out. If it finds one * (or more), and that pdu has more retries available, a new packet is formed * from the pdu and is resent. If there are no more retries available, the * callback for the session is used to alert the user of the timeout. */ void snmp_timeout (void); /* * This routine must be supplied by the application: * * u_char *authenticator(pdu, length, community, community_len) * u_char *pdu; The rest of the PDU to be authenticated * int *length; The length of the PDU (updated by the authenticator) * u_char *community; The community name to authenticate under. * int community_len The length of the community name. * * Returns the authenticated pdu, or NULL if authentication failed. * If null authentication is used, the authenticator in snmp_session can be * set to NULL(0). */ /* * This routine must be supplied by the application: * * int callback(operation, session, reqid, pdu, magic) * int operation; * struct snmp_session *session; The session authenticated under. * int reqid; The request id of this pdu (0 for TRAP) * struct snmp_pdu *pdu; The pdu information. * void *magic A link to the data for this routine. * * Returns 1 if request was successful, 0 if it should be kept pending. * Any data in the pdu must be copied because it will be freed elsewhere. * Operations are defined below: */ #define RECEIVED_MESSAGE 1 #define TIMED_OUT 2 #define SEND_FAILED 3 long snmp_get_next_msgid(void); long snmp_get_next_reqid(void); long snmp_get_next_sessid(void); long snmp_get_next_transid(void); /* provide for backwards compatibility */ void snmp_set_dump_packet(int); int snmp_get_dump_packet(void); void snmp_set_quick_print(int); int snmp_get_quick_print(void); void snmp_set_suffix_only(int); int snmp_get_suffix_only(void); void snmp_set_full_objid(int); int snmp_get_full_objid(void); void snmp_set_random_access(int); int snmp_get_random_access(void); int snmp_oid_compare (const oid *, size_t, const oid *, size_t); void init_snmp (const char *); u_char *snmp_pdu_build (struct snmp_pdu *, u_char *, size_t *); #ifdef USE_REVERSE_ASNENCODING u_char *snmp_pdu_rbuild (struct snmp_pdu *, u_char *, size_t *); #endif int snmpv3_parse(struct snmp_pdu *, u_char *, size_t *, u_char **, struct snmp_session *); int snmpv3_dparse(struct snmp_pdu *, u_char *, size_t *, u_char **, int); int snmpv3_packet_build(struct snmp_pdu *pdu, u_char *packet, size_t *out_length, u_char *pdu_data, size_t pdu_data_len); int snmpv3_packet_rbuild(struct snmp_pdu *pdu, u_char *packet, size_t *out_length, u_char *pdu_data, size_t pdu_data_len); int snmpv3_make_report(struct snmp_pdu *pdu, int error); int snmpv3_get_report_type(struct snmp_pdu *pdu); int snmp_pdu_parse(struct snmp_pdu *pdu, u_char *data, size_t *length); int snmp_pdu_dparse(struct snmp_pdu *pdu, u_char *data, size_t *length, int); u_char* snmpv3_scopedPDU_parse(struct snmp_pdu *pdu, u_char *cp, size_t *length); u_char* snmpv3_scopedPDU_dparse(struct snmp_pdu *pdu, u_char *cp, size_t *length, int); void snmp_store(const char *type); void snmp_shutdown(const char *type); struct variable_list *snmp_pdu_add_variable (struct snmp_pdu *, oid *, size_t, u_char, u_char *, size_t); struct variable_list *snmp_varlist_add_variable(struct variable_list **varlist, oid *name, size_t name_length, u_char type, u_char *value, size_t len); int hex_to_binary (const char *, u_char *); int ascii_to_binary (const char *, u_char *); int snmp_add_var (struct snmp_pdu *, oid*, size_t, char, const char *); oid *snmp_duplicate_objid(oid *objToCopy, size_t); u_int snmp_increment_statistic(int which); u_int snmp_increment_statistic_by(int which, int count); u_int snmp_get_statistic(int which); void snmp_init_statistics(void); int create_user_from_session(struct snmp_session *session); /* extended open */ struct snmp_session *snmp_open_ex (struct snmp_session *, int (*fpre_parse) (struct snmp_session *, snmp_ipaddr), int (*fparse) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t), int (*fpost_parse) (struct snmp_session *, struct snmp_pdu *, int), int (*fbuild) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t *), int (*fcheck) (u_char *, size_t) ); /* provided for backwards compatability. Don't use these functions. See snmp_debug.h and snmp_debug.c instead. */ #if HAVE_STDARG_H void DEBUGP (const char *, ...); #else void DEBUGP (va_alist); #endif void DEBUGPOID(oid *, size_t); void snmp_set_do_debugging (int); int snmp_get_do_debugging (void); #ifdef CMU_COMPATIBLE extern int snmp_dump_packet; extern int quick_print; #endif size_t snmp_socket_length (int family); /* * snmp_error - return error data * Inputs : address of errno, address of snmp_errno, address of string * Caller must free the string returned after use. */ void snmp_error (struct snmp_session *, int *, int *, char **); /* * single session API. * * These functions perform similar actions as snmp_XX functions, * but operate on a single session only. * * Synopsis: void * sessp; struct snmp_session session, *ss; struct snmp_pdu *pdu, *response; snmp_sess_init(&session); session.retries = ... session.remote_port = ... sessp = snmp_sess_open(&session); ss = snmp_sess_session(sessp); if (ss == NULL) exit(1); ... if (ss->community) free(ss->community); ss->community = strdup(gateway); ss->community_len = strlen(gateway); ... snmp_sess_synch_response(sessp, pdu, &response); ... snmp_sess_close(sessp); * See also: * snmp_sess_synch_response, in snmp_client.h. * Notes: * 1. Invoke snmp_sess_session after snmp_sess_open. * 2. snmp_sess_session return value is an opaque pointer. * 3. Do NOT free memory returned by snmp_sess_session. * 4. Replace snmp_send(ss,pdu) with snmp_sess_send(sessp,pdu) */ void snmp_sess_init (struct snmp_session *); void * snmp_sess_open (struct snmp_session *); struct snmp_session * snmp_sess_session (void *); /* use return value from snmp_sess_open as void * parameter */ int snmp_sess_send (void *, struct snmp_pdu *); int snmp_sess_async_send (void *, struct snmp_pdu *, snmp_callback, void *); int snmp_sess_select_info (void *, int *, fd_set *, struct timeval *, int *); int snmp_sess_read (void *, fd_set *); void snmp_sess_timeout (void *); int snmp_sess_close (void *); void snmp_sess_error (void *, int *, int *, char **); void snmp_sess_perror (const char *prog_string, struct snmp_session *ss); /* end single session API */ /* generic statistic counters */ /* snmpv3 statistics */ /* mpd stats */ #define STAT_SNMPUNKNOWNSECURITYMODELS 0 #define STAT_SNMPINVALIDMSGS 1 #define STAT_SNMPUNKNOWNPDUHANDLERS 2 #define STAT_MPD_STATS_START STAT_SNMPUNKNOWNSECURITYMODELS #define STAT_MPD_STATS_END STAT_SNMPUNKNOWNPDUHANDLERS /* usm stats */ #define STAT_USMSTATSUNSUPPORTEDSECLEVELS 3 #define STAT_USMSTATSNOTINTIMEWINDOWS 4 #define STAT_USMSTATSUNKNOWNUSERNAMES 5 #define STAT_USMSTATSUNKNOWNENGINEIDS 6 #define STAT_USMSTATSWRONGDIGESTS 7 #define STAT_USMSTATSDECRYPTIONERRORS 8 #define STAT_USM_STATS_START STAT_USMSTATSUNSUPPORTEDSECLEVELS #define STAT_USM_STATS_END STAT_USMSTATSDECRYPTIONERRORS /* snmp counters */ #define STAT_SNMPINPKTS 9 #define STAT_SNMPOUTPKTS 10 #define STAT_SNMPINBADVERSIONS 11 #define STAT_SNMPINBADCOMMUNITYNAMES 12 #define STAT_SNMPINBADCOMMUNITYUSES 13 #define STAT_SNMPINASNPARSEERRS 14 /* #define STAT_SNMPINBADTYPES 15 */ #define STAT_SNMPINTOOBIGS 16 #define STAT_SNMPINNOSUCHNAMES 17 #define STAT_SNMPINBADVALUES 18 #define STAT_SNMPINREADONLYS 19 #define STAT_SNMPINGENERRS 20 #define STAT_SNMPINTOTALREQVARS 21 #define STAT_SNMPINTOTALSETVARS 22 #define STAT_SNMPINGETREQUESTS 23 #define STAT_SNMPINGETNEXTS 24 #define STAT_SNMPINSETREQUESTS 25 #define STAT_SNMPINGETRESPONSES 26 #define STAT_SNMPINTRAPS 27 #define STAT_SNMPOUTTOOBIGS 28 #define STAT_SNMPOUTNOSUCHNAMES 29 #define STAT_SNMPOUTBADVALUES 30 /* #define STAT_SNMPOUTREADONLYS 31 */ #define STAT_SNMPOUTGENERRS 32 #define STAT_SNMPOUTGETREQUESTS 33 #define STAT_SNMPOUTGETNEXTS 34 #define STAT_SNMPOUTSETREQUESTS 35 #define STAT_SNMPOUTGETRESPONSES 36 #define STAT_SNMPOUTTRAPS 37 /* AUTHTRAPENABLE 38 */ #define STAT_SNMPSILENTDROPS 39 #define STAT_SNMPPROXYDROPS 40 #define STAT_SNMP_STATS_START STAT_SNMPINPKTS #define STAT_SNMP_STATS_END STAT_SNMPOUTTRAPS #define MAX_STATS 41 #ifdef __cplusplus } #endif #endif /* SNMP_API_H */ pyparsing2-2.4.7/examples/sparser.py000066400000000000000000000316421365333160500175040ustar00rootroot00000000000000#!/usr/bin/env python """ NAME: sparser.py SYNOPSIS: sparser.py [options] filename DESCRIPTION: The sparser.py script is a Specified PARSER. It is unique (as far as I can tell) because it doesn't care about the delimiter(s). The user specifies what is expected, and the order, for each line of text. All of the heavy lifting is handled by pyparsing (http://pyparsing.sf.net). OPTIONS: -h,--help this message -v,--version version -d,--debug turn on debug messages EXAMPLES: 1. As standalone sparser.py myfile 2. As library import sparser ... #Copyright (C) 2006 Tim Cera timcera@earthlink.net # # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 675 Mass Ave, Cambridge, MA 02139, USA. """ #===imports====================== import sys import os import getopt from pyparsing import * #===globals====================== modname = "sparser" __version__ = "0.1" #--option args-- debug_p = 0 #opt_b=None #string arg, default is undefined #---positional args, default is empty--- pargs = [] #---other--- #===utilities==================== def msg(txt): """Send message to stdout.""" sys.stdout.write(txt) sys.stdout.flush() def debug(ftn, txt): """Used for debugging.""" if debug_p: sys.stdout.write("{0}.{1}:{2}\n".format(modname, ftn, txt)) sys.stdout.flush() def fatal(ftn, txt): """If can't continue.""" msg = "{0}.{1}:FATAL:{2}\n".format(modname, ftn, txt) raise SystemExit(msg) def usage(): """Prints the docstring.""" print(__doc__) #==================================== class ToInteger(TokenConverter): """Converter to make token into an integer.""" def postParse( self, instring, loc, tokenlist ): return int(tokenlist[0]) class ToFloat(TokenConverter): """Converter to make token into a float.""" def postParse( self, instring, loc, tokenlist ): return float(tokenlist[0]) class ParseFileLineByLine: """ Bring data from text files into a program, optionally parsing each line according to specifications in a parse definition file. ParseFileLineByLine instances can be used like normal file objects (i.e. by calling readline(), readlines(), and write()), but can also be used as sequences of lines in for-loops. ParseFileLineByLine objects also handle compression transparently. i.e. it is possible to read lines from a compressed text file as if it were not compressed. Compression is deduced from the file name suffixes '.Z' (compress/uncompress), '.gz' (gzip/gunzip), and '.bz2' (bzip2). The parse definition fi le name is developed based on the input file name. If the input file name is 'basename.ext', then the definition file is 'basename_def.ext'. If a definition file specific to the input file is not found, then the program searches for the file 'sparse.def' which would be the definition file for all files in that directory without a file specific definition file. Finally, ParseFileLineByLine objects accept file names that start with '~' or '~user' to indicate a home directory, as well as URLs (for reading only). Constructor: ParseFileLineByLine(|filename|, |mode|='"r"'), where |filename| is the name of the file (or a URL) and |mode| is one of '"r"' (read), '"w"' (write) or '"a"' (append, not supported for .Z files). """ def __init__(self, filename, mode = 'r'): """Opens input file, and if available the definition file. If the definition file is available __init__ will then create some pyparsing helper variables. """ if mode not in ['r', 'w', 'a']: raise IOError(0, 'Illegal mode: ' + repr(mode)) if string.find(filename, ':/') > 1: # URL if mode == 'w': raise IOError("can't write to a URL") import urllib.request, urllib.parse, urllib.error self.file = urllib.request.urlopen(filename) else: filename = os.path.expanduser(filename) if mode == 'r' or mode == 'a': if not os.path.exists(filename): raise IOError(2, 'No such file or directory: ' + filename) filen, file_extension = os.path.splitext(filename) command_dict = { ('.Z', 'r'): "self.file = os.popen('uncompress -c ' + filename, mode)", ('.gz', 'r'): "self.file = gzip.GzipFile(filename, 'rb')", ('.bz2', 'r'): "self.file = os.popen('bzip2 -dc ' + filename, mode)", ('.Z', 'w'): "self.file = os.popen('compress > ' + filename, mode)", ('.gz', 'w'): "self.file = gzip.GzipFile(filename, 'wb')", ('.bz2', 'w'): "self.file = os.popen('bzip2 > ' + filename, mode)", ('.Z', 'a'): "raise IOError, (0, 'Can\'t append to .Z files')", ('.gz', 'a'): "self.file = gzip.GzipFile(filename, 'ab')", ('.bz2', 'a'): "raise IOError, (0, 'Can\'t append to .bz2 files')", } exec(command_dict.get((file_extension, mode), 'self.file = open(filename, mode)')) self.grammar = None # Try to find a parse ('*_def.ext') definition file. First try to find # a file specific parse definition file, then look for 'sparse.def' # that would be the definition file for all files within the directory. # The definition file is pure Python. The one variable that needs to # be specified is 'parse'. The 'parse' variable is a list of tuples # defining the name, type, and because it is a list, the order of # variables on each line in the data file. The variable name is a # string, the type variable is defined as integer, real, and qString. # parse = [ # ('year', integer), # ('month', integer), # ('day', integer), # ('value', real), # ] definition_file_one = filen + "_def" + file_extension definition_file_two = os.path.dirname(filen) + os.sep + "sparse.def" if os.path.exists(definition_file_one): self.parsedef = definition_file_one elif os.path.exists(definition_file_two): self.parsedef = definition_file_two else: self.parsedef = None return None # Create some handy pyparsing constructs. I kept 'decimal_sep' so that # could easily change to parse if the decimal separator is a ",". decimal_sep = "." sign = oneOf("+ -") # part of printables without decimal_sep, +, - special_chars = string.replace('!"#$%&\'()*,./:;<=>?@[\\]^_`{|}~', decimal_sep, "") integer = ToInteger( Combine(Optional(sign) + Word(nums))).setName("integer") positive_integer = ToInteger( Combine(Optional("+") + Word(nums))).setName("integer") negative_integer = ToInteger( Combine("-" + Word(nums))).setName("integer") real = ToFloat( Combine(Optional(sign) + Word(nums) + decimal_sep + Optional(Word(nums)) + Optional(oneOf("E e") + Word(nums)))).setName("real") positive_real = ToFloat( Combine(Optional("+") + Word(nums) + decimal_sep + Optional(Word(nums)) + Optional(oneOf("E e") + Word(nums)))).setName("real") negative_real = ToFloat( Combine("-" + Word(nums) + decimal_sep + Optional(Word(nums)) + Optional(oneOf("E e") + Word(nums)))).setName("real") qString = ( sglQuotedString | dblQuotedString ).setName("qString") # add other characters we should skip over between interesting fields integer_junk = Optional( Suppress( Word(alphas + special_chars + decimal_sep))).setName("integer_junk") real_junk = Optional( Suppress( Word(alphas + special_chars))).setName("real_junk") qString_junk = SkipTo(qString).setName("qString_junk") # Now that 'integer', 'real', and 'qString' have been assigned I can # execute the definition file. exec(compile(open(self.parsedef).read(), self.parsedef, 'exec')) # Build the grammar, combination of the 'integer', 'real, 'qString', # and '*_junk' variables assigned above in the order specified in the # definition file. grammar = [] for nam, expr in parse: grammar.append( eval(expr.name + "_junk")) grammar.append( expr.setResultsName(nam) ) self.grammar = And( grammar[1:] + [restOfLine] ) def __del__(self): """Delete (close) the file wrapper.""" self.close() def __getitem__(self, item): """Used in 'for line in fp:' idiom.""" line = self.readline() if not line: raise IndexError return line def readline(self): """Reads (and optionally parses) a single line.""" line = self.file.readline() if self.grammar and line: try: return self.grammar.parseString(line).asDict() except ParseException: return self.readline() else: return line def readlines(self): """Returns a list of all lines (optionally parsed) in the file.""" if self.grammar: tot = [] # Used this way instead of a 'for' loop against # self.file.readlines() so that there wasn't two copies of the file # in memory. while 1: line = self.file.readline() if not line: break tot.append(line) return tot return self.file.readlines() def write(self, data): """Write to a file.""" self.file.write(data) def writelines(self, list): """Write a list to a file. Each item in the list is a line in the file. """ for line in list: self.file.write(line) def close(self): """Close the file.""" self.file.close() def flush(self): """Flush in memory contents to file.""" self.file.flush() #============================= def main(pargs): """This should only be used for testing. The primary mode of operation is as an imported library. """ input_file = sys.argv[1] fp = ParseFileLineByLine(input_file) for i in fp: print(i) #------------------------- if __name__ == '__main__': ftn = "main" opts, pargs = getopt.getopt(sys.argv[1:], 'hvd', ['help', 'version', 'debug', 'bb=']) for opt in opts: if opt[0] == '-h' or opt[0] == '--help': print(modname+": version="+__version__) usage() sys.exit(0) elif opt[0] == '-v' or opt[0] == '--version': print(modname+": version="+__version__) sys.exit(0) elif opt[0] == '-d' or opt[0] == '--debug': debug_p = 1 elif opt[0] == '--bb': opt_b = opt[1] #---make the object and run it--- main(pargs) #===Revision Log=== #Created by mkpythonproj: #2006-02-06 Tim Cera # pyparsing2-2.4.7/examples/sql2dot.py000066400000000000000000000055571365333160500174230ustar00rootroot00000000000000#!/usr/bin/python # sql2dot.py # # Creates table graphics by parsing SQL table DML commands and # generating DOT language output. # # Adapted from a post at https://energyblog.blogspot.com/2006/04/blog-post_20.html. # sampleSQL = """ create table student ( student_id integer primary key, firstname varchar(20), lastname varchar(40), address1 varchar(80), address2 varchar(80), city varchar(30), state varchar(2), zipcode varchar(10), dob date ); create table classes ( class_id integer primary key, id varchar(8), maxsize integer, instructor varchar(40) ); create table student_registrations ( reg_id integer primary key, student_id integer, class_id integer ); alter table only student_registrations add constraint students_link foreign key (student_id) references students(student_id); alter table only student_registrations add constraint classes_link foreign key (class_id) references classes(class_id); """.upper() from pyparsing import Literal, Word, delimitedList \ , alphas, alphanums \ , OneOrMore, ZeroOrMore, CharsNotIn \ , replaceWith skobki = "(" + ZeroOrMore(CharsNotIn(")")) + ")" field_def = OneOrMore(Word(alphas,alphanums+"_\"':-") | skobki) def field_act(s,loc,tok): return ("<"+tok[0]+"> " + " ".join(tok)).replace("\"","\\\"") field_def.setParseAction(field_act) field_list_def = delimitedList( field_def ) def field_list_act(toks): return " | ".join(toks) field_list_def.setParseAction(field_list_act) create_table_def = Literal("CREATE") + "TABLE" + Word(alphas,alphanums+"_").setResultsName("tablename") + \ "("+field_list_def.setResultsName("columns")+")"+ ";" def create_table_act(toks): return """"%(tablename)s" [\n\t label="<%(tablename)s> %(tablename)s | %(columns)s"\n\t shape="record"\n];""" % toks create_table_def.setParseAction(create_table_act) add_fkey_def=Literal("ALTER")+"TABLE"+"ONLY" + Word(alphanums+"_").setResultsName("fromtable") + "ADD" \ + "CONSTRAINT" + Word(alphanums+"_") + "FOREIGN"+"KEY"+"("+Word(alphanums+"_").setResultsName("fromcolumn")+")" \ +"REFERENCES"+Word(alphanums+"_").setResultsName("totable")+"("+Word(alphanums+"_").setResultsName("tocolumn")+")"+";" def add_fkey_act(toks): return """ "%(fromtable)s":%(fromcolumn)s -> "%(totable)s":%(tocolumn)s """ % toks add_fkey_def.setParseAction(add_fkey_act) other_statement_def = ( OneOrMore(CharsNotIn(";") ) + ";") other_statement_def.setParseAction( replaceWith("") ) comment_def = "--" + ZeroOrMore(CharsNotIn("\n")) comment_def.setParseAction( replaceWith("") ) statement_def = comment_def | create_table_def | add_fkey_def | other_statement_def defs = OneOrMore(statement_def) print("""digraph g { graph [ rankdir = "LR" ]; """) for i in defs.parseString(sampleSQL): if i!="": print(i) print("}") pyparsing2-2.4.7/examples/stackish.py000066400000000000000000000056251365333160500176400ustar00rootroot00000000000000# stackish.py # # Stackish is a data representation syntax, similar to JSON or YAML. For more info on # stackish, see http://www.savingtheinternetwithhate.com/stackish.html # # Copyright 2008, Paul McGuire # """ NUMBER A simple integer type that's just any series of digits. FLOAT A simple floating point type. STRING A string is double quotes with anything inside that's not a " or newline character. You can include \n and \" to include these characters. MARK Marks a point in the stack that demarcates the boundary for a nested group. WORD Marks the root node of a group, with the other end being the nearest MARK. GROUP Acts as the root node of an anonymous group. ATTRIBUTE Assigns an attribute name to the previously processed node. This means that just about anything can be an attribute, unlike in XML. BLOB A BLOB is unique to Stackish and allows you to record any content (even binary content) inside the structure. This is done by pre- sizing the data with the NUMBER similar to Dan Bernstein's netstrings setup. SPACE White space is basically ignored. This is interesting because since Stackish is serialized consistently this means you can use \n as the separation character and perform reasonable diffs on two structures. """ from pyparsing import Suppress,Word,nums,alphas,alphanums,Combine,oneOf,\ Optional,QuotedString,Forward,Group,ZeroOrMore,srange MARK,UNMARK,AT,COLON,QUOTE = map(Suppress,"[]@:'") NUMBER = Word(nums) NUMBER.setParseAction(lambda t:int(t[0])) FLOAT = Combine(oneOf("+ -") + Word(nums) + "." + Optional(Word(nums))) FLOAT.setParseAction(lambda t:float(t[0])) STRING = QuotedString('"', multiline=True) WORD = Word(alphas,alphanums+"_:") ATTRIBUTE = Combine(AT + WORD) strBody = Forward() def setBodyLength(tokens): strBody << Word(srange(r'[\0x00-\0xffff]'), exact=int(tokens[0])) return "" BLOB = Combine(QUOTE + Word(nums).setParseAction(setBodyLength) + COLON + strBody + QUOTE) item = Forward() def assignUsing(s): def assignPA(tokens): if s in tokens: tokens[tokens[s]] = tokens[0] del tokens[s] return assignPA GROUP = (MARK + Group( ZeroOrMore( (item + Optional(ATTRIBUTE)("attr") ).setParseAction(assignUsing("attr")) ) ) + ( WORD("name") | UNMARK ) ).setParseAction(assignUsing("name")) item << (NUMBER | FLOAT | STRING | BLOB | GROUP ) tests = """\ [ '10:1234567890' @name 25 @age +0.45 @percentage person:zed [ [ "hello" 1 child root [ "child" [ 200 '4:like' "I" "hello" things root [ [ "data" [ 2 1 ] @numbers child root [ [ 1 2 3 ] @test 4 5 6 root """.splitlines() for test in tests: if test: print(test) print(item.parseString(test).dump()) print() pyparsing2-2.4.7/examples/statemachine/000077500000000000000000000000001365333160500201125ustar00rootroot00000000000000pyparsing2-2.4.7/examples/statemachine/documentSignoffDemo.py000066400000000000000000000021421365333160500244220ustar00rootroot00000000000000# # documentSignoffDemo.py # # Example of a state machine modeling the state of a document in a document # control system, using named state transitions # import statemachine import documentsignoffstate print('\n'.join(t.__name__ for t in documentsignoffstate.DocumentRevisionState.transitions())) class Document(documentsignoffstate.DocumentRevisionStateMixin): def __init__(self): self.initialize_state(documentsignoffstate.New) def run_demo(): import random doc = Document() print(doc) # begin editing document doc.create() print(doc) print(doc.state.description) while not isinstance(doc._state, documentsignoffstate.Approved): print('...submit') doc.submit() print(doc) print(doc.state.description) if random.randint(1,10) > 3: print('...reject') doc.reject() else: print('...approve') doc.approve() print(doc) print(doc.state.description) doc.activate() print(doc) print(doc.state.description) if __name__ == '__main__': run_demo() pyparsing2-2.4.7/examples/statemachine/documentsignoffstate.pystate000066400000000000000000000055041365333160500257640ustar00rootroot00000000000000# # documentsignoffstate.pystate # # state machine model of the states and associated behaviors and properties for each # different state of a document in a document control system # # example using named state transitions # This implements a state model for submitting, # approving, activating, and purging document # revisions in a document management system. # # The state model looks like: # # New # | # | (create) # | # v # Editing ----------------------------------------------+ # | ^ | # | | | # | +----------+ | # | | | # | (submit) | | (cancel) # | | (reject) | # v | | # PendingApproval-+ | # | | # | (approve) | # | | # v | # Approved <--------------------------+ (deactivate) | # | | | | # | +--------------+ | | # | | (activate) | | # | v | | # | (retire) Active ----------+ | # | | # v | # Retired | # | | # | (purge) | # | | # v | # Deleted <---------------------------------------------+ # # # There is no behavior attached to these states, this is # just an example of a state machine with named transitions. # statemachine DocumentRevisionState: New -( create )-> Editing Editing -( cancel )-> Deleted Editing -( submit )-> PendingApproval PendingApproval -( reject )-> Editing PendingApproval -( approve )-> Approved Approved -( activate )-> Active Active -( deactivate )-> Approved Approved -( retire )-> Retired Retired -( purge )-> Deleted New.description = 'creating...' Editing.description = 'editing...' PendingApproval.description = 'reviewing...' Approved.description = 'approved/inactive...' Active.description = 'approved/active...' Deleted.description = 'deleted...' Retired.description = 'retired...'pyparsing2-2.4.7/examples/statemachine/libraryBookDemo.py000066400000000000000000000032571365333160500235570ustar00rootroot00000000000000# # libraryBookDemo.py # # Simple statemachine demo, based on the state transitions given in librarybookstate.pystate # import statemachine import librarybookstate class Book(librarybookstate.BookStateMixin): def __init__(self): self.initialize_state(librarybookstate.New) class RestrictedBook(Book): def __init__(self): super(RestrictedBook, self).__init__() self._authorized_users = [] def authorize(self, name): self._authorized_users.append(name) # specialized checkout to check permission of user first def checkout(self, user=None): if user in self._authorized_users: super().checkout() else: raise Exception("{0} could not check out restricted book".format(user if user is not None else "anonymous")) def run_demo(): book = Book() book.shelve() print(book) book.checkout() print(book) book.checkin() print(book) book.reserve() print(book) try: book.checkout() except Exception as e: # statemachine.InvalidTransitionException: print(e) print('..cannot check out reserved book') book.release() print(book) book.checkout() print(book) print() restricted_book = RestrictedBook() restricted_book.authorize("BOB") restricted_book.restrict() print(restricted_book) for name in [None, "BILL", "BOB"]: try: restricted_book.checkout(name) except Exception as e: print('..' + str(e)) else: print('checkout to', name) print(restricted_book) restricted_book.checkin() print(restricted_book) if __name__ == '__main__': run_demo() pyparsing2-2.4.7/examples/statemachine/librarybookstate.pystate000066400000000000000000000010361365333160500251050ustar00rootroot00000000000000# # librarybookstate.pystate # # This state machine models the state of books in a library. # statemachine BookState: New -(shelve)-> Available Available -(reserve)-> OnHold OnHold -(release)-> Available Available -(checkout)-> CheckedOut CheckedOut -(checkin)-> Available # add states for restricted books New -(restrict)-> Restricted Available -(restrict)-> Restricted Restricted -(release)-> Available Restricted -(checkout)-> CheckedOutRestricted CheckedOutRestricted -(checkin)-> Restricted pyparsing2-2.4.7/examples/statemachine/statemachine.py000066400000000000000000000275551365333160500231470ustar00rootroot00000000000000# stateMachine.py # # module to define .pystate import handler # # import imputil import keyword import sys import os import types import importlib try: import urllib.parse url_parse = urllib.parse.urlparse except ImportError: print("import error, Python 2 not supported") raise import urllib url_parse = urllib.parse DEBUG = False import pyparsing as pp # define basic exception for invalid state transitions - state machine classes will subclass to # define their own specific exception type class InvalidTransitionException(Exception): pass ident = pp.Word(pp.alphas + "_", pp.alphanums + "_$") # add parse-time condition to make sure we do not allow any Python keywords to be used as # statemachine identifiers def no_keywords_allowed(s, l, t): wd = t[0] return not keyword.iskeyword(wd) ident.addCondition(no_keywords_allowed, message="cannot use a Python keyword for state or transition identifier") stateTransition = ident("from_state") + "->" + ident("to_state") stateMachine = (pp.Keyword("statemachine") + ident("name") + ":" + pp.OneOrMore(pp.Group(stateTransition))("transitions")) namedStateTransition = (ident("from_state") + "-(" + ident("transition") + ")->" + ident("to_state")) namedStateMachine = (pp.Keyword("statemachine") + ident("name") + ":" + pp.OneOrMore(pp.Group(namedStateTransition))("transitions")) def expand_state_definition(source, loc, tokens): """ Parse action to convert statemachine to corresponding Python classes and methods """ indent = " " * (pp.col(loc, source) - 1) statedef = [] # build list of states states = set() fromTo = {} for tn in tokens.transitions: states.add(tn.from_state) states.add(tn.to_state) fromTo[tn.from_state] = tn.to_state # define base class for state classes baseStateClass = tokens.name statedef.extend([ "class %s(object):" % baseStateClass, " def __str__(self):", " return self.__class__.__name__", " @classmethod", " def states(cls):", " return list(cls.__subclasses__())", " def next_state(self):", " return self._next_state_class()", ]) # define all state classes statedef.extend("class {0}({1}): pass".format(s, baseStateClass) for s in states) # define state->state transitions statedef.extend("{0}._next_state_class = {1}".format(s, fromTo[s]) for s in states if s in fromTo) statedef.extend([ "class {baseStateClass}Mixin:".format(baseStateClass=baseStateClass), " def __init__(self):", " self._state = None", " def initialize_state(self, init_state):", " if issubclass(init_state, {baseStateClass}):".format(baseStateClass=baseStateClass), " init_state = init_state()", " self._state = init_state", " @property", " def state(self):", " return self._state", " # get behavior/properties from current state", " def __getattr__(self, attrname):", " attr = getattr(self._state, attrname)", " return attr", " def __str__(self):", " return '{0}: {1}'.format(self.__class__.__name__, self._state)", ]) return ("\n" + indent).join(statedef) + "\n" stateMachine.setParseAction(expand_state_definition) def expand_named_state_definition(source, loc, tokens): """ Parse action to convert statemachine with named transitions to corresponding Python classes and methods """ indent = " " * (pp.col(loc, source) - 1) statedef = [] # build list of states and transitions states = set() transitions = set() baseStateClass = tokens.name fromTo = {} for tn in tokens.transitions: states.add(tn.from_state) states.add(tn.to_state) transitions.add(tn.transition) if tn.from_state in fromTo: fromTo[tn.from_state][tn.transition] = tn.to_state else: fromTo[tn.from_state] = {tn.transition: tn.to_state} # add entries for terminal states for s in states: if s not in fromTo: fromTo[s] = {} # define state transition class statedef.extend([ "class {baseStateClass}Transition:".format(baseStateClass=baseStateClass), " def __str__(self):", " return self.transitionName", ]) statedef.extend( "{tn_name} = {baseStateClass}Transition()".format(tn_name=tn, baseStateClass=baseStateClass) for tn in transitions) statedef.extend("{tn_name}.transitionName = '{tn_name}'".format(tn_name=tn) for tn in transitions) # define base class for state classes statedef.extend([ "class %s(object):" % baseStateClass, " from statemachine import InvalidTransitionException as BaseTransitionException", " class InvalidTransitionException(BaseTransitionException): pass", " def __str__(self):", " return self.__class__.__name__", " @classmethod", " def states(cls):", " return list(cls.__subclasses__())", " @classmethod", " def next_state(cls, name):", " try:", " return cls.tnmap[name]()", " except KeyError:", " raise cls.InvalidTransitionException('%s does not support transition %r'% (cls.__name__, name))", " def __bad_tn(name):", " def _fn(cls):", " raise cls.InvalidTransitionException('%s does not support transition %r'% (cls.__name__, name))", " _fn.__name__ = name", " return _fn", ]) # define default 'invalid transition' methods in base class, valid transitions will be implemented in subclasses statedef.extend( " {tn_name} = classmethod(__bad_tn({tn_name!r}))".format(tn_name=tn) for tn in transitions) # define all state classes statedef.extend("class %s(%s): pass" % (s, baseStateClass) for s in states) # define state transition methods for valid transitions from each state for s in states: trns = list(fromTo[s].items()) # statedef.append("%s.tnmap = {%s}" % (s, ", ".join("%s:%s" % tn for tn in trns))) statedef.extend("%s.%s = classmethod(lambda cls: %s())" % (s, tn_, to_) for tn_, to_ in trns) statedef.extend([ "{baseStateClass}.transitions = classmethod(lambda cls: [{transition_class_list}])".format( baseStateClass=baseStateClass, transition_class_list = ', '.join("cls.{0}".format(tn) for tn in transitions) ), "{baseStateClass}.transition_names = [tn.__name__ for tn in {baseStateClass}.transitions()]".format( baseStateClass=baseStateClass ) ]) # define Mixin class for application classes that delegate to the state statedef.extend([ "class {baseStateClass}Mixin:".format(baseStateClass=baseStateClass), " def __init__(self):", " self._state = None", " def initialize_state(self, init_state):", " if issubclass(init_state, {baseStateClass}):".format(baseStateClass=baseStateClass), " init_state = init_state()", " self._state = init_state", " @property", " def state(self):", " return self._state", " # get behavior/properties from current state", " def __getattr__(self, attrname):", " attr = getattr(self._state, attrname)", " return attr", " def __str__(self):", " return '{0}: {1}'.format(self.__class__.__name__, self._state)", ]) # define transition methods to be delegated to the _state instance variable statedef.extend( " def {tn_name}(self): self._state = self._state.{tn_name}()".format(tn_name=tn) for tn in transitions ) return ("\n" + indent).join(statedef) + "\n" namedStateMachine.setParseAction(expand_named_state_definition) # ====================================================================== # NEW STUFF - Matt Anderson, 2009-11-26 # ====================================================================== class SuffixImporter(object): """An importer designed using the mechanism defined in :pep:`302`. I read the PEP, and also used Doug Hellmann's PyMOTW article `Modules and Imports`_, as a pattern. .. _`Modules and Imports`: http://www.doughellmann.com/PyMOTW/sys/imports.html Define a subclass that specifies a :attr:`suffix` attribute, and implements a :meth:`process_filedata` method. Then call the classmethod :meth:`register` on your class to actually install it in the appropriate places in :mod:`sys`. """ scheme = 'suffix' suffix = None path_entry = None @classmethod def trigger_url(cls): if cls.suffix is None: raise ValueError('%s.suffix is not set' % cls.__name__) return 'suffix:%s' % cls.suffix @classmethod def register(cls): sys.path_hooks.append(cls) sys.path.append(cls.trigger_url()) def __init__(self, path_entry): pr = url_parse(str(path_entry)) if pr.scheme != self.scheme or pr.path != self.suffix: raise ImportError() self.path_entry = path_entry self._found = {} def checkpath_iter(self, fullname): for dirpath in sys.path: # if the value in sys.path_importer_cache is None, then this # path *should* be imported by the builtin mechanism, and the # entry is thus a path to a directory on the filesystem; # if it's not None, then some other importer is in charge, and # it probably isn't even a filesystem path finder = sys.path_importer_cache.get(dirpath) if isinstance(finder, (type(None), importlib.machinery.FileFinder)): checkpath = os.path.join(dirpath, '{0}.{1}'.format(fullname, self.suffix)) yield checkpath def find_module(self, fullname, path=None): for checkpath in self.checkpath_iter(fullname): if os.path.isfile(checkpath): self._found[fullname] = checkpath return self return None def load_module(self, fullname): assert fullname in self._found if fullname in sys.modules: module = sys.modules[fullname] else: sys.modules[fullname] = module = types.ModuleType(fullname) data = None with open(self._found[fullname]) as f: data = f.read() module.__dict__.clear() module.__file__ = self._found[fullname] module.__name__ = fullname module.__loader__ = self self.process_filedata(module, data) return module def process_filedata(self, module, data): pass class PystateImporter(SuffixImporter): suffix = 'pystate' def process_filedata(self, module, data): # MATT-NOTE: re-worked :func:`get_state_machine` # convert any statemachine expressions stateMachineExpr = (stateMachine | namedStateMachine).ignore(pp.pythonStyleComment) generated_code = stateMachineExpr.transformString(data) if DEBUG: print(generated_code) # compile code object from generated code # (strip trailing spaces and tabs, compile doesn't like # dangling whitespace) COMPILE_MODE = 'exec' codeobj = compile(generated_code.rstrip(" \t"), module.__file__, COMPILE_MODE) exec(codeobj, module.__dict__) PystateImporter.register() if DEBUG: print("registered {0!r} importer".format(PystateImporter.suffix)) pyparsing2-2.4.7/examples/statemachine/trafficLightDemo.py000066400000000000000000000010501365333160500236730ustar00rootroot00000000000000# # trafficLightDemo.py # # Example of a simple state machine modeling the state of a traffic light # import statemachine import trafficlightstate class TrafficLight(trafficlightstate.TrafficLightStateMixin): def __init__(self): self.initialize_state(trafficlightstate.Red) def change(self): self._state = self._state.next_state() light = TrafficLight() for i in range(10): print("{0} {1}".format(light, ("STOP", "GO")[light.cars_can_go])) light.crossing_signal() light.delay() print() light.change() pyparsing2-2.4.7/examples/statemachine/trafficlightstate.pystate000066400000000000000000000021751365333160500252410ustar00rootroot00000000000000# # trafficlightstate.pystate # # state machine model of the states and associated behaviors and properties for each # different state of a traffic light # define state machine with transitions # (states will be implemented as Python classes, so use name case appropriate for class names) statemachine TrafficLightState: Red -> Green Green -> Yellow Yellow -> Red # statemachine only defines the state->state transitions - actual behavior and properties # must be added separately # define some class level constants Red.cars_can_go = False Yellow.cars_can_go = True Green.cars_can_go = True # setup some class level methods def flash_crosswalk(s): def flash(): print("%s...%s...%s" % (s, s, s)) return flash Red.crossing_signal = staticmethod(flash_crosswalk("WALK")) Yellow.crossing_signal = staticmethod(flash_crosswalk("DONT WALK")) Green.crossing_signal = staticmethod(flash_crosswalk("DONT WALK")) # setup some instance methods def wait(nSeconds): def waitFn(self): print("" % nSeconds) return waitFn Red.delay = wait(20) Yellow.delay = wait(3) Green.delay = wait(15) pyparsing2-2.4.7/examples/statemachine/vending_machine.py000066400000000000000000000046761365333160500236170ustar00rootroot00000000000000# # vending_machine.py # # Example of using the statemachine parser without importing a .pystate module. # # A vending machine that dispenses candy and chips in a 4x4 grid, A1 thru D4. # To dispense a product, you must press an alpha button, then a digit button. # import statemachine # Vending machine buttons: # A, B, C, D # 1, 2, 3, 4 # vending_machine_state_description = """\ statemachine VendingMachineState: Idle-(press_alpha_button)->WaitingOnDigit WaitingOnDigit-(press_alpha_button)->WaitingOnDigit WaitingOnDigit-(press_digit_button)->DispenseProduct DispenseProduct-(dispense)->Idle """ # convert state machine text to state classes generated = statemachine.namedStateMachine.transformString(vending_machine_state_description) # print(generated) # exec generated code to define state classes and state mixin exec(generated) class VendingMachine(VendingMachineStateMixin): def __init__(self): self.initialize_state(Idle) self._pressed = None self._alpha_pressed = None self._digit_pressed = None def press_button(self, button): if button in "ABCD": self._pressed = button self.press_alpha_button() elif button in "1234": self._pressed = button self.press_digit_button() else: print('Did not recognize button {!r}'.format(str(button))) def press_alpha_button(self): try: super(VendingMachine, self).press_alpha_button() except VendingMachineState.InvalidTransitionException as ite: print(ite) else: self._alpha_pressed = self._pressed def press_digit_button(self): try: super(VendingMachine, self).press_digit_button() except VendingMachineState.InvalidTransitionException as ite: print(ite) else: self._digit_pressed = self._pressed self.dispense() def dispense(self): try: super(VendingMachine, self).dispense() except VendingMachineState.InvalidTransitionException as ite: print(ite) else: print("Dispensing at {}{}".format(self._alpha_pressed, self._digit_pressed)) self._alpha_pressed = self._digit_pressed = None vm = VendingMachine() for button in "1 A B 1".split(): print(">> pressing {!r}".format(button)) vm.press_button(button) print("Vending machine is now in {} state".format(vm.state)) pyparsing2-2.4.7/examples/statemachine/video_demo.py000066400000000000000000000022061365333160500225760ustar00rootroot00000000000000# # video_demo.py # # Simple statemachine demo, based on the state transitions given in videostate.pystate # import statemachine import videostate class Video(videostate.VideoStateMixin): def __init__(self, title): self.initialize_state(videostate.Stopped) self.title = title # ==== main loop - a REPL ==== v = Video("Die Hard.mp4") while True: print(v.state) cmd = input("Command ({})> ".format('/'.join(videostate.VideoState.transition_names))).lower().strip() if not cmd: continue if cmd in ('?', 'h', 'help'): print('enter a transition {!r}'.format(videostate.VideoState.transition_names)) print(' q - quit') print(' ?, h, help - this message') continue # quitting out if cmd.startswith('q'): break # get transition function for given command state_transition_fn = getattr(v, cmd, None) if state_transition_fn is None: print('???') continue # invoke the input transition, handle invalid commands try: state_transition_fn() except videostate.VideoState.InvalidTransitionException as e: print(e) pyparsing2-2.4.7/examples/statemachine/videostate.pystate000066400000000000000000000015351365333160500237000ustar00rootroot00000000000000# # videostate.pystate # # Statemachine describing the playing of a video # [] = stop # > = play # || = pause # >> = fast forward # << = rewind statemachine VideoState: # basic >, [], and || controls Stopped-(play)->Playing Playing-(pause)-> Paused Playing-(stop)-> Stopped Paused-(stop)-> Stopped Paused-(play)->Playing # add >> and << controls - different meanings if occur while playing or stopped Playing-(fast_forward)->FastForward FastForward-(play)->Playing FastForward-(pause)->Paused FastForward-(stop)->Stopped Stopped-(fast_forward)->Forwardwinding Forwardwinding-(stop)->Stopped Playing-(rewind)->ReversePlaying ReversePlaying-(play)->Playing ReversePlaying-(pause)->Paused ReversePlaying-(stop)->Stopped Stopped-(rewind)->Rewinding Rewinding-(stop)->Stopped pyparsing2-2.4.7/examples/test_bibparse.py000066400000000000000000000204561365333160500206540ustar00rootroot00000000000000""" Test for bibparse grammar """ import unittest from pyparsing import ParseException from .btpyparse import Macro from . import btpyparse as bp class TestBibparse(unittest.TestCase): def test_names(self): # check various types of names # All names can contains alphas, but not some special chars bad_chars = '"#%\'(),={}' for name_type, dig1f in ((bp.macro_def, False), (bp.field_name, False), (bp.entry_type, False), (bp.cite_key, True)): if dig1f: # can start with digit self.assertEqual(name_type.parseString('2t')[0], '2t') else: self.assertRaises(ParseException, name_type.parseString, '2t') # All of the names cannot contain some characters for char in bad_chars: self.assertRaises(ParseException, name_type.parseString, char) # standard strings all OK self.assertEqual(name_type.parseString('simple_test')[0], 'simple_test') # Test macro ref mr = bp.macro_ref # can't start with digit self.assertRaises(ParseException, mr.parseString, '2t') for char in bad_chars: self.assertRaises(ParseException, mr.parseString, char) self.assertEqual(mr.parseString('simple_test')[0].name, 'simple_test') def test_numbers(self): self.assertEqual(bp.number.parseString('1066')[0], '1066') self.assertEqual(bp.number.parseString('0')[0], '0') self.assertRaises(ParseException, bp.number.parseString, '-4') self.assertRaises(ParseException, bp.number.parseString, '+4') self.assertRaises(ParseException, bp.number.parseString, '.4') # something point something leaves a trailing .4 unmatched self.assertEqual(bp.number.parseString('0.4')[0], '0') def test_parse_string(self): # test string building blocks self.assertEqual(bp.chars_no_quotecurly.parseString('x')[0], 'x') self.assertEqual(bp.chars_no_quotecurly.parseString("a string")[0], 'a string') self.assertEqual(bp.chars_no_quotecurly.parseString('a "string')[0], 'a ') self.assertEqual(bp.chars_no_curly.parseString('x')[0], 'x') self.assertEqual(bp.chars_no_curly.parseString("a string")[0], 'a string') self.assertEqual(bp.chars_no_curly.parseString('a {string')[0], 'a ') self.assertEqual(bp.chars_no_curly.parseString('a }string')[0], 'a ') # test more general strings together for obj in (bp.curly_string, bp.string, bp.field_value): self.assertEqual(obj.parseString('{}').asList(), []) self.assertEqual(obj.parseString('{a "string}')[0], 'a "string') self.assertEqual(obj.parseString('{a {nested} string}').asList(), ['a ', ['nested'], ' string']) self.assertEqual(obj.parseString('{a {double {nested}} string}').asList(), ['a ', ['double ', ['nested']], ' string']) for obj in (bp.quoted_string, bp.string, bp.field_value): self.assertEqual(obj.parseString('""').asList(), []) self.assertEqual(obj.parseString('"a string"')[0], 'a string') self.assertEqual(obj.parseString('"a {nested} string"').asList(), ['a ', ['nested'], ' string']) self.assertEqual(obj.parseString('"a {double {nested}} string"').asList(), ['a ', ['double ', ['nested']], ' string']) # check macro def in string self.assertEqual(bp.string.parseString('someascii')[0], Macro('someascii')) self.assertRaises(ParseException, bp.string.parseString, '%#= validstring') # check number in string self.assertEqual(bp.string.parseString('1994')[0], '1994') def test_parse_field(self): # test field value - hashes included fv = bp.field_value # Macro self.assertEqual(fv.parseString('aname')[0], Macro('aname')) self.assertEqual(fv.parseString('ANAME')[0], Macro('aname')) # String and macro self.assertEqual(fv.parseString('aname # "some string"').asList(), [Macro('aname'), 'some string']) # Nested string self.assertEqual(fv.parseString('aname # {some {string}}').asList(), [Macro('aname'), 'some ', ['string']]) # String and number self.assertEqual(fv.parseString('"a string" # 1994').asList(), ['a string', '1994']) # String and number and macro self.assertEqual(fv.parseString('"a string" # 1994 # a_macro').asList(), ['a string', '1994', Macro('a_macro')]) def test_comments(self): res = bp.comment.parseString('@Comment{about something}') self.assertEqual(res.asList(), ['comment', '{about something}']) self.assertEqual( bp.comment.parseString('@COMMENT{about something').asList(), ['comment', '{about something']) self.assertEqual( bp.comment.parseString('@comment(about something').asList(), ['comment', '(about something']) self.assertEqual( bp.comment.parseString('@COMment about something').asList(), ['comment', ' about something']) self.assertRaises(ParseException, bp.comment.parseString, '@commentabout something') self.assertRaises(ParseException, bp.comment.parseString, '@comment+about something') self.assertRaises(ParseException, bp.comment.parseString, '@comment"about something') def test_preamble(self): res = bp.preamble.parseString('@preamble{"about something"}') self.assertEqual(res.asList(), ['preamble', 'about something']) self.assertEqual(bp.preamble.parseString( '@PREamble{{about something}}').asList(), ['preamble', 'about something']) self.assertEqual(bp.preamble.parseString("""@PREamble{ {about something} }""").asList(), ['preamble', 'about something']) def test_macro(self): res = bp.macro.parseString('@string{ANAME = "about something"}') self.assertEqual(res.asList(), ['string', 'aname', 'about something']) self.assertEqual( bp.macro.parseString('@string{aname = {about something}}').asList(), ['string', 'aname', 'about something']) def test_entry(self): txt = """@some_entry{akey, aname = "about something", another={something else}}""" res = bp.entry.parseString(txt) self.assertEqual(res.asList(), ['some_entry', 'akey', ['aname', 'about something'], ['another', 'something else']]) # Case conversion txt = """@SOME_ENTRY{akey, ANAME = "about something", another={something else}}""" res = bp.entry.parseString(txt) self.assertEqual(res.asList(), ['some_entry', 'akey', ['aname', 'about something'], ['another', 'something else']]) def test_bibfile(self): txt = """@some_entry{akey, aname = "about something", another={something else}}""" res = bp.bibfile.parseString(txt) self.assertEqual(res.asList(), [['some_entry', 'akey', ['aname', 'about something'], ['another', 'something else']]]) def test_bib1(self): # First pass whole bib-like tests txt = """ Some introductory text (implicit comment) @ARTICLE{Brett2002marsbar, author = {Matthew Brett and Jean-Luc Anton and Romain Valabregue and Jean-Baptise Poline}, title = {{Region of interest analysis using an SPM toolbox}}, journal = {Neuroimage}, year = {2002}, volume = {16}, pages = {1140--1141}, number = {2} } @some_entry{akey, aname = "about something", another={something else}} """ res = bp.bibfile.parseString(txt) self.assertEqual(len(res), 3) res2 = bp.parse_str(txt) self.assertEqual(res.asList(), res2.asList()) res3 = [r.asList()[0] for r, start, end in bp.definitions.scanString(txt)] self.assertEqual(res.asList(), res3) if __name__ == '__main__': unittest.main() pyparsing2-2.4.7/examples/urlExtractor.py000066400000000000000000000021221365333160500205120ustar00rootroot00000000000000# URL extractor # Copyright 2004, Paul McGuire from pyparsing import makeHTMLTags, pyparsing_common as ppc import urllib.request from contextlib import closing import pprint linkOpenTag, linkCloseTag = makeHTMLTags('a') linkBody = linkOpenTag.tag_body linkBody.setParseAction(ppc.stripHTMLTags) linkBody.addParseAction(lambda toks: ' '.join(toks[0].strip().split())) link = linkOpenTag + linkBody("body") + linkCloseTag.suppress() # Go get some HTML with some links in it. with closing(urllib.request.urlopen("https://www.cnn.com/")) as serverListPage: htmlText = serverListPage.read().decode("UTF-8") # scanString is a generator that loops through the input htmlText, and for each # match yields the tokens and start and end locations (for this application, we are # not interested in the start and end values). for toks, strt, end in link.scanString(htmlText): print(toks.asList()) # Create dictionary from list comprehension, assembled from each pair of tokens returned # from a matched URL. pprint.pprint( {toks.body: toks.href for toks, strt, end in link.scanString(htmlText)} ) pyparsing2-2.4.7/examples/urlExtractorNew.py000066400000000000000000000025051365333160500211710ustar00rootroot00000000000000# URL extractor # Copyright 2004, Paul McGuire from pyparsing import makeHTMLTags from contextlib import closing import urllib.request, urllib.parse, urllib.error import pprint # Define the pyparsing grammar for a URL, that is: # URLlink ::= linkText # URL ::= doubleQuotedString | alphanumericWordPath # Note that whitespace may appear just about anywhere in the link. Note also # that it is not necessary to explicitly show this in the pyparsing grammar; by default, # pyparsing skips over whitespace between tokens. linkOpenTag, linkCloseTag = makeHTMLTags("a") link = linkOpenTag + linkOpenTag.tag_body("body") + linkCloseTag.suppress() # Go get some HTML with some links in it. with closing(urllib.request.urlopen("https://www.cnn.com/")) as serverListPage: htmlText = serverListPage.read() # scanString is a generator that loops through the input htmlText, and for each # match yields the tokens and start and end locations (for this application, we are # not interested in the start and end values). for toks, strt, end in link.scanString(htmlText): print(toks.startA.href, "->", toks.body) # Create dictionary from list comprehension, assembled from each pair of tokens returned # from a matched URL. pprint.pprint( {toks.body: toks.startA.href for toks, strt, end in link.scanString(htmlText)} ) pyparsing2-2.4.7/examples/verilogParse.py000066400000000000000000000737001365333160500204700ustar00rootroot00000000000000# # verilogParse.py # # an example of using the pyparsing module to be able to process Verilog files # uses BNF defined at http://www.verilog.com/VerilogBNF.html # # Copyright (c) 2004-2011 Paul T. McGuire. All rights reserved. # # 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. # # If you find this software to be useful, please make a donation to one # of the following charities: # - the Red Cross (https://www.redcross.org/) # - Hospice Austin (https://www.hospiceaustin.org/) # # DISCLAIMER: # THIS SOFTWARE IS PROVIDED BY PAUL T. McGUIRE ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL PAUL T. McGUIRE OR CO-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OFUSE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # For questions or inquiries regarding this license, or commercial use of # this software, contact the author via e-mail: ptmcg@users.sourceforge.net # # Todo: # - add pre-process pass to implement compilerDirectives (ifdef, include, etc.) # # Revision History: # # 1.0 - Initial release # 1.0.1 - Fixed grammar errors: # . real declaration was incorrect # . tolerant of '=>' for '*>' operator # . tolerant of '?' as hex character # . proper handling of mintypmax_expr within path delays # 1.0.2 - Performance tuning (requires pyparsing 1.3) # 1.0.3 - Performance updates, using Regex (requires pyparsing 1.4) # 1.0.4 - Performance updates, enable packrat parsing (requires pyparsing 1.4.2) # 1.0.5 - Converted keyword Literals to Keywords, added more use of Group to # group parsed results tokens # 1.0.6 - Added support for module header with no ports list (thanks, Thomas Dejanovic!) # 1.0.7 - Fixed erroneous '<<' Forward definition in timCheckCond, omitting ()'s # 1.0.8 - Re-released under MIT license # 1.0.9 - Enhanced udpInstance to handle identifiers with leading '\' and subscripting # 1.0.10 - Fixed change added in 1.0.9 to work for all identifiers, not just those used # for udpInstance. # 1.0.11 - Fixed bug in inst_args, content alternatives were reversed # import time import pprint import sys __version__ = "1.0.11" from pyparsing import Literal, Keyword, Word, OneOrMore, ZeroOrMore, \ Forward, delimitedList, Group, Optional, Combine, alphas, nums, restOfLine, \ alphanums, dblQuotedString, empty, ParseException, oneOf, \ StringEnd, FollowedBy, ParserElement, Regex, cppStyleComment import pyparsing usePackrat = False packratOn = False if usePackrat: try: ParserElement.enablePackrat() except Exception: pass else: packratOn = True def dumpTokens(s,l,t): import pprint pprint.pprint( t.asList() ) verilogbnf = None def Verilog_BNF(): global verilogbnf if verilogbnf is None: # compiler directives compilerDirective = Combine( "`" + \ oneOf("define undef ifdef else endif default_nettype " "include resetall timescale unconnected_drive " "nounconnected_drive celldefine endcelldefine") + \ restOfLine ).setName("compilerDirective") # primitives SEMI,COLON,LPAR,RPAR,LBRACE,RBRACE,LBRACK,RBRACK,DOT,COMMA,EQ = map(Literal,";:(){}[].,=") identLead = alphas+"$_" identBody = alphanums+"$_" identifier1 = Regex( r"\.?["+identLead+"]["+identBody+r"]*(\.["+identLead+"]["+identBody+"]*)*" ).setName("baseIdent") identifier2 = Regex(r"\\\S+").setParseAction(lambda t:t[0][1:]).setName("escapedIdent")#.setDebug() identifier = identifier1 | identifier2 assert(identifier2 == r'\abc') hexnums = nums + "abcdefABCDEF" + "_?" base = Regex("'[bBoOdDhH]").setName("base") basedNumber = Combine( Optional( Word(nums + "_") ) + base + Word(hexnums+"xXzZ"), joinString=" ", adjacent=False ).setName("basedNumber") #~ number = ( basedNumber | Combine( Word( "+-"+spacedNums, spacedNums ) + #~ Optional( DOT + Optional( Word( spacedNums ) ) ) + #~ Optional( e + Word( "+-"+spacedNums, spacedNums ) ) ).setName("numeric") ) number = ( basedNumber | \ Regex(r"[+-]?[0-9_]+(\.[0-9_]*)?([Ee][+-]?[0-9_]+)?") \ ).setName("numeric") #~ decnums = nums + "_" #~ octnums = "01234567" + "_" expr = Forward().setName("expr") concat = Group( LBRACE + delimitedList( expr ) + RBRACE ) multiConcat = Group("{" + expr + concat + "}").setName("multiConcat") funcCall = Group(identifier + LPAR + Optional( delimitedList( expr ) ) + RPAR).setName("funcCall") subscrRef = Group(LBRACK + delimitedList( expr, COLON ) + RBRACK) subscrIdentifier = Group( identifier + Optional( subscrRef ) ) #~ scalarConst = "0" | (( FollowedBy('1') + oneOf("1'b0 1'b1 1'bx 1'bX 1'B0 1'B1 1'Bx 1'BX 1") )) scalarConst = Regex("0|1('[Bb][01xX])?") mintypmaxExpr = Group( expr + COLON + expr + COLON + expr ).setName("mintypmax") primary = ( number | (LPAR + mintypmaxExpr + RPAR ) | ( LPAR + Group(expr) + RPAR ).setName("nestedExpr") | multiConcat | concat | dblQuotedString | funcCall | subscrIdentifier ) unop = oneOf( "+ - ! ~ & ~& | ^| ^ ~^" ).setName("unop") binop = oneOf( "+ - * / % == != === !== && " "|| < <= > >= & | ^ ^~ >> << ** <<< >>>" ).setName("binop") expr << ( ( unop + expr ) | # must be first! ( primary + "?" + expr + COLON + expr ) | ( primary + Optional( binop + expr ) ) ) lvalue = subscrIdentifier | concat # keywords if_ = Keyword("if") else_ = Keyword("else") edge = Keyword("edge") posedge = Keyword("posedge") negedge = Keyword("negedge") specify = Keyword("specify") endspecify = Keyword("endspecify") fork = Keyword("fork") join = Keyword("join") begin = Keyword("begin") end = Keyword("end") default = Keyword("default") forever = Keyword("forever") repeat = Keyword("repeat") while_ = Keyword("while") for_ = Keyword("for") case = oneOf( "case casez casex" ) endcase = Keyword("endcase") wait = Keyword("wait") disable = Keyword("disable") deassign = Keyword("deassign") force = Keyword("force") release = Keyword("release") assign = Keyword("assign") eventExpr = Forward() eventTerm = ( posedge + expr ) | ( negedge + expr ) | expr | ( LPAR + eventExpr + RPAR ) eventExpr << ( Group( delimitedList( eventTerm, Keyword("or") ) ) ) eventControl = Group( "@" + ( ( LPAR + eventExpr + RPAR ) | identifier | "*" ) ).setName("eventCtrl") delayArg = ( number | Word(alphanums+"$_") | #identifier | ( LPAR + Group( delimitedList( mintypmaxExpr | expr ) ) + RPAR ) ).setName("delayArg")#.setDebug() delay = Group( "#" + delayArg ).setName("delay")#.setDebug() delayOrEventControl = delay | eventControl assgnmt = Group( lvalue + EQ + Optional( delayOrEventControl ) + expr ).setName( "assgnmt" ) nbAssgnmt = Group(( lvalue + "<=" + Optional( delay ) + expr ) | ( lvalue + "<=" + Optional( eventControl ) + expr )).setName( "nbassgnmt" ) range = LBRACK + expr + COLON + expr + RBRACK paramAssgnmt = Group( identifier + EQ + expr ).setName("paramAssgnmt") parameterDecl = Group( "parameter" + Optional( range ) + delimitedList( paramAssgnmt ) + SEMI).setName("paramDecl") inputDecl = Group( "input" + Optional( range ) + delimitedList( identifier ) + SEMI ) outputDecl = Group( "output" + Optional( range ) + delimitedList( identifier ) + SEMI ) inoutDecl = Group( "inout" + Optional( range ) + delimitedList( identifier ) + SEMI ) regIdentifier = Group( identifier + Optional( LBRACK + expr + COLON + expr + RBRACK ) ) regDecl = Group( "reg" + Optional("signed") + Optional( range ) + delimitedList( regIdentifier ) + SEMI ).setName("regDecl") timeDecl = Group( "time" + delimitedList( regIdentifier ) + SEMI ) integerDecl = Group( "integer" + delimitedList( regIdentifier ) + SEMI ) strength0 = oneOf("supply0 strong0 pull0 weak0 highz0") strength1 = oneOf("supply1 strong1 pull1 weak1 highz1") driveStrength = Group( LPAR + ( ( strength0 + COMMA + strength1 ) | ( strength1 + COMMA + strength0 ) ) + RPAR ).setName("driveStrength") nettype = oneOf("wire tri tri1 supply0 wand triand tri0 supply1 wor trior trireg") expandRange = Optional( oneOf("scalared vectored") ) + range realDecl = Group( "real" + delimitedList( identifier ) + SEMI ) eventDecl = Group( "event" + delimitedList( identifier ) + SEMI ) blockDecl = ( parameterDecl | regDecl | integerDecl | realDecl | timeDecl | eventDecl ) stmt = Forward().setName("stmt")#.setDebug() stmtOrNull = stmt | SEMI caseItem = ( delimitedList( expr ) + COLON + stmtOrNull ) | \ ( default + Optional(":") + stmtOrNull ) stmt << Group( ( begin + Group( ZeroOrMore( stmt ) ) + end ).setName("begin-end") | ( if_ + Group(LPAR + expr + RPAR) + stmtOrNull + Optional( else_ + stmtOrNull ) ).setName("if") | ( delayOrEventControl + stmtOrNull ) | ( case + LPAR + expr + RPAR + OneOrMore( caseItem ) + endcase ) | ( forever + stmt ) | ( repeat + LPAR + expr + RPAR + stmt ) | ( while_ + LPAR + expr + RPAR + stmt ) | ( for_ + LPAR + assgnmt + SEMI + Group( expr ) + SEMI + assgnmt + RPAR + stmt ) | ( fork + ZeroOrMore( stmt ) + join ) | ( fork + COLON + identifier + ZeroOrMore( blockDecl ) + ZeroOrMore( stmt ) + end ) | ( wait + LPAR + expr + RPAR + stmtOrNull ) | ( "->" + identifier + SEMI ) | ( disable + identifier + SEMI ) | ( assign + assgnmt + SEMI ) | ( deassign + lvalue + SEMI ) | ( force + assgnmt + SEMI ) | ( release + lvalue + SEMI ) | ( begin + COLON + identifier + ZeroOrMore( blockDecl ) + ZeroOrMore( stmt ) + end ).setName("begin:label-end") | # these *have* to go at the end of the list!!! ( assgnmt + SEMI ) | ( nbAssgnmt + SEMI ) | ( Combine( Optional("$") + identifier ) + Optional( LPAR + delimitedList(expr|empty) + RPAR ) + SEMI ) ).setName("stmtBody") """ x::= ; x||= ; x||= if ( ) x||= if ( ) else x||= case ( ) + endcase x||= casez ( ) + endcase x||= casex ( ) + endcase x||= forever x||= repeat ( ) x||= while ( ) x||= for ( ; ; ) x||= x||= wait ( ) x||= -> ; x||= x||= x||= x||= x||= disable ; x||= disable ; x||= assign ; x||= deassign ; x||= force ; x||= release ; """ alwaysStmt = Group( "always" + Optional(eventControl) + stmt ).setName("alwaysStmt") initialStmt = Group( "initial" + stmt ).setName("initialStmt") chargeStrength = Group( LPAR + oneOf( "small medium large" ) + RPAR ).setName("chargeStrength") continuousAssign = Group( assign + Optional( driveStrength ) + Optional( delay ) + delimitedList( assgnmt ) + SEMI ).setName("continuousAssign") tfDecl = ( parameterDecl | inputDecl | outputDecl | inoutDecl | regDecl | timeDecl | integerDecl | realDecl ) functionDecl = Group( "function" + Optional( range | "integer" | "real" ) + identifier + SEMI + Group( OneOrMore( tfDecl ) ) + Group( ZeroOrMore( stmt ) ) + "endfunction" ) inputOutput = oneOf("input output") netDecl1Arg = ( nettype + Optional( expandRange ) + Optional( delay ) + Group( delimitedList( ~inputOutput + identifier ) ) ) netDecl2Arg = ( "trireg" + Optional( chargeStrength ) + Optional( expandRange ) + Optional( delay ) + Group( delimitedList( ~inputOutput + identifier ) ) ) netDecl3Arg = ( nettype + Optional( driveStrength ) + Optional( expandRange ) + Optional( delay ) + Group( delimitedList( assgnmt ) ) ) netDecl1 = Group(netDecl1Arg + SEMI).setName("netDecl1") netDecl2 = Group(netDecl2Arg + SEMI).setName("netDecl2") netDecl3 = Group(netDecl3Arg + SEMI).setName("netDecl3") gateType = oneOf("and nand or nor xor xnor buf bufif0 bufif1 " "not notif0 notif1 pulldown pullup nmos rnmos " "pmos rpmos cmos rcmos tran rtran tranif0 " "rtranif0 tranif1 rtranif1" ) gateInstance = Optional( Group( identifier + Optional( range ) ) ) + \ LPAR + Group( delimitedList( expr ) ) + RPAR gateDecl = Group( gateType + Optional( driveStrength ) + Optional( delay ) + delimitedList( gateInstance) + SEMI ) udpInstance = Group( Group( identifier + Optional(range | subscrRef) ) + LPAR + Group( delimitedList( expr ) ) + RPAR ) udpInstantiation = Group( identifier - Optional( driveStrength ) + Optional( delay ) + delimitedList( udpInstance ) + SEMI ).setName("udpInstantiation") parameterValueAssignment = Group( Literal("#") + LPAR + Group( delimitedList( expr ) ) + RPAR ) namedPortConnection = Group( DOT + identifier + LPAR + expr + RPAR ).setName("namedPortConnection")#.setDebug() assert(r'.\abc (abc )' == namedPortConnection) modulePortConnection = expr | empty #~ moduleInstance = Group( Group ( identifier + Optional(range) ) + #~ ( delimitedList( modulePortConnection ) | #~ delimitedList( namedPortConnection ) ) ) inst_args = Group( LPAR + (delimitedList( namedPortConnection ) | delimitedList( modulePortConnection )) + RPAR).setName("inst_args") moduleInstance = Group( Group ( identifier + Optional(range) ) + inst_args ).setName("moduleInstance")#.setDebug() moduleInstantiation = Group( identifier + Optional( parameterValueAssignment ) + delimitedList( moduleInstance ).setName("moduleInstanceList") + SEMI ).setName("moduleInstantiation") parameterOverride = Group( "defparam" + delimitedList( paramAssgnmt ) + SEMI ) task = Group( "task" + identifier + SEMI + ZeroOrMore( tfDecl ) + stmtOrNull + "endtask" ) specparamDecl = Group( "specparam" + delimitedList( paramAssgnmt ) + SEMI ) pathDescr1 = Group( LPAR + subscrIdentifier + "=>" + subscrIdentifier + RPAR ) pathDescr2 = Group( LPAR + Group( delimitedList( subscrIdentifier ) ) + "*>" + Group( delimitedList( subscrIdentifier ) ) + RPAR ) pathDescr3 = Group( LPAR + Group( delimitedList( subscrIdentifier ) ) + "=>" + Group( delimitedList( subscrIdentifier ) ) + RPAR ) pathDelayValue = Group( ( LPAR + Group( delimitedList( mintypmaxExpr | expr ) ) + RPAR ) | mintypmaxExpr | expr ) pathDecl = Group( ( pathDescr1 | pathDescr2 | pathDescr3 ) + EQ + pathDelayValue + SEMI ).setName("pathDecl") portConditionExpr = Forward() portConditionTerm = Optional(unop) + subscrIdentifier portConditionExpr << portConditionTerm + Optional( binop + portConditionExpr ) polarityOp = oneOf("+ -") levelSensitivePathDecl1 = Group( if_ + Group(LPAR + portConditionExpr + RPAR) + subscrIdentifier + Optional( polarityOp ) + "=>" + subscrIdentifier + EQ + pathDelayValue + SEMI ) levelSensitivePathDecl2 = Group( if_ + Group(LPAR + portConditionExpr + RPAR) + LPAR + Group( delimitedList( subscrIdentifier ) ) + Optional( polarityOp ) + "*>" + Group( delimitedList( subscrIdentifier ) ) + RPAR + EQ + pathDelayValue + SEMI ) levelSensitivePathDecl = levelSensitivePathDecl1 | levelSensitivePathDecl2 edgeIdentifier = posedge | negedge edgeSensitivePathDecl1 = Group( Optional( if_ + Group(LPAR + expr + RPAR) ) + LPAR + Optional( edgeIdentifier ) + subscrIdentifier + "=>" + LPAR + subscrIdentifier + Optional( polarityOp ) + COLON + expr + RPAR + RPAR + EQ + pathDelayValue + SEMI ) edgeSensitivePathDecl2 = Group( Optional( if_ + Group(LPAR + expr + RPAR) ) + LPAR + Optional( edgeIdentifier ) + subscrIdentifier + "*>" + LPAR + delimitedList( subscrIdentifier ) + Optional( polarityOp ) + COLON + expr + RPAR + RPAR + EQ + pathDelayValue + SEMI ) edgeSensitivePathDecl = edgeSensitivePathDecl1 | edgeSensitivePathDecl2 edgeDescr = oneOf("01 10 0x x1 1x x0").setName("edgeDescr") timCheckEventControl = Group( posedge | negedge | (edge + LBRACK + delimitedList( edgeDescr ) + RBRACK )) timCheckCond = Forward() timCondBinop = oneOf("== === != !==") timCheckCondTerm = ( expr + timCondBinop + scalarConst ) | ( Optional("~") + expr ) timCheckCond << ( ( LPAR + timCheckCond + RPAR ) | timCheckCondTerm ) timCheckEvent = Group( Optional( timCheckEventControl ) + subscrIdentifier + Optional( "&&&" + timCheckCond ) ) timCheckLimit = expr controlledTimingCheckEvent = Group( timCheckEventControl + subscrIdentifier + Optional( "&&&" + timCheckCond ) ) notifyRegister = identifier systemTimingCheck1 = Group( "$setup" + LPAR + timCheckEvent + COMMA + timCheckEvent + COMMA + timCheckLimit + Optional( COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck2 = Group( "$hold" + LPAR + timCheckEvent + COMMA + timCheckEvent + COMMA + timCheckLimit + Optional( COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck3 = Group( "$period" + LPAR + controlledTimingCheckEvent + COMMA + timCheckLimit + Optional( COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck4 = Group( "$width" + LPAR + controlledTimingCheckEvent + COMMA + timCheckLimit + Optional( COMMA + expr + COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck5 = Group( "$skew" + LPAR + timCheckEvent + COMMA + timCheckEvent + COMMA + timCheckLimit + Optional( COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck6 = Group( "$recovery" + LPAR + controlledTimingCheckEvent + COMMA + timCheckEvent + COMMA + timCheckLimit + Optional( COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck7 = Group( "$setuphold" + LPAR + timCheckEvent + COMMA + timCheckEvent + COMMA + timCheckLimit + COMMA + timCheckLimit + Optional( COMMA + notifyRegister ) + RPAR + SEMI ) systemTimingCheck = (FollowedBy('$') + ( systemTimingCheck1 | systemTimingCheck2 | systemTimingCheck3 | systemTimingCheck4 | systemTimingCheck5 | systemTimingCheck6 | systemTimingCheck7 )).setName("systemTimingCheck") sdpd = if_ + Group(LPAR + expr + RPAR) + \ ( pathDescr1 | pathDescr2 ) + EQ + pathDelayValue + SEMI specifyItem = ~Keyword("endspecify") +( specparamDecl | pathDecl | levelSensitivePathDecl | edgeSensitivePathDecl | systemTimingCheck | sdpd ) """ x::= x||= x||= x||= x||= x||= """ specifyBlock = Group( "specify" + ZeroOrMore( specifyItem ) + "endspecify" ).setName("specifyBlock") moduleItem = ~Keyword("endmodule") + ( parameterDecl | inputDecl | outputDecl | inoutDecl | regDecl | netDecl3 | netDecl1 | netDecl2 | timeDecl | integerDecl | realDecl | eventDecl | gateDecl | parameterOverride | continuousAssign | specifyBlock | initialStmt | alwaysStmt | task | functionDecl | # these have to be at the end - they start with identifiers moduleInstantiation | udpInstantiation ) """ All possible moduleItems, from Verilog grammar spec x::= x||= x||= x||= ?||= (spec does not seem consistent for this item) x||= x||= x||= x||= x||= x||= x||= x||= x||= x||= x||= x||= x||= x||= x||= """ portRef = subscrIdentifier portExpr = portRef | Group( LBRACE + delimitedList( portRef ) + RBRACE ) port = portExpr | Group( ( DOT + identifier + LPAR + portExpr + RPAR ) ) moduleHdr = Group ( oneOf("module macromodule") + identifier + Optional( LPAR + Group( Optional( delimitedList( Group(oneOf("input output") + (netDecl1Arg | netDecl2Arg | netDecl3Arg) ) | port ) ) ) + RPAR ) + SEMI ).setName("moduleHdr") module = Group( moduleHdr + Group( ZeroOrMore( moduleItem ) ) + "endmodule" ).setName("module")#.setDebug() udpDecl = outputDecl | inputDecl | regDecl #~ udpInitVal = oneOf("1'b0 1'b1 1'bx 1'bX 1'B0 1'B1 1'Bx 1'BX 1 0 x X") udpInitVal = (Regex("1'[bB][01xX]") | Regex("[01xX]")).setName("udpInitVal") udpInitialStmt = Group( "initial" + identifier + EQ + udpInitVal + SEMI ).setName("udpInitialStmt") levelSymbol = oneOf("0 1 x X ? b B") levelInputList = Group( OneOrMore( levelSymbol ).setName("levelInpList") ) outputSymbol = oneOf("0 1 x X") combEntry = Group( levelInputList + COLON + outputSymbol + SEMI ) edgeSymbol = oneOf("r R f F p P n N *") edge = Group( LPAR + levelSymbol + levelSymbol + RPAR ) | \ Group( edgeSymbol ) edgeInputList = Group( ZeroOrMore( levelSymbol ) + edge + ZeroOrMore( levelSymbol ) ) inputList = levelInputList | edgeInputList seqEntry = Group( inputList + COLON + levelSymbol + COLON + ( outputSymbol | "-" ) + SEMI ).setName("seqEntry") udpTableDefn = Group( "table" + OneOrMore( combEntry | seqEntry ) + "endtable" ).setName("table") """ ::= primitive ( <,>* ) ; + ? endprimitive """ udp = Group( "primitive" + identifier + LPAR + Group( delimitedList( identifier ) ) + RPAR + SEMI + OneOrMore( udpDecl ) + Optional( udpInitialStmt ) + udpTableDefn + "endprimitive" ) verilogbnf = OneOrMore( module | udp ) + StringEnd() verilogbnf.ignore( cppStyleComment ) verilogbnf.ignore( compilerDirective ) return verilogbnf def test( strng ): tokens = [] try: tokens = Verilog_BNF().parseString( strng ) except ParseException as err: print(err.line) print(" "*(err.column-1) + "^") print(err) return tokens #~ if __name__ == "__main__": if 0: import pprint toptest = """ module TOP( in, out ); input [7:0] in; output [5:0] out; COUNT_BITS8 count_bits( .IN( in ), .C( out ) ); endmodule""" pprint.pprint( test(toptest).asList() ) else: def main(): print("Verilog parser test (V %s)" % __version__) print(" - using pyparsing version", pyparsing.__version__) print(" - using Python version", sys.version) if packratOn: print(" - using packrat parsing") print() import os import gc failCount = 0 Verilog_BNF() numlines = 0 startTime = time.clock() fileDir = "verilog" #~ fileDir = "verilog/new" #~ fileDir = "verilog/new2" #~ fileDir = "verilog/new3" allFiles = [f for f in os.listdir(fileDir) if f.endswith(".v")] #~ allFiles = [ "list_path_delays_test.v" ] #~ allFiles = [ "escapedIdent.v" ] #~ allFiles = filter( lambda f : f.startswith("a") and f.endswith(".v"), os.listdir(fileDir) ) #~ allFiles = filter( lambda f : f.startswith("c") and f.endswith(".v"), os.listdir(fileDir) ) #~ allFiles = [ "ff.v" ] pp = pprint.PrettyPrinter( indent=2 ) totalTime = 0 for vfile in allFiles: gc.collect() fnam = fileDir + "/"+vfile infile = open(fnam) filelines = infile.readlines() infile.close() print(fnam, len(filelines), end=' ') numlines += len(filelines) teststr = "".join(filelines) time1 = time.clock() tokens = test( teststr ) time2 = time.clock() elapsed = time2-time1 totalTime += elapsed if ( len( tokens ) ): print("OK", elapsed) #~ print "tokens=" #~ pp.pprint( tokens.asList() ) #~ print ofnam = fileDir + "/parseOutput/" + vfile + ".parsed.txt" outfile = open(ofnam,"w") outfile.write( teststr ) outfile.write("\n") outfile.write("\n") outfile.write(pp.pformat(tokens.asList())) outfile.write("\n") outfile.close() else: print("failed", elapsed) failCount += 1 for i,line in enumerate(filelines,1): print("%4d: %s" % (i,line.rstrip())) endTime = time.clock() print("Total parse time:", totalTime) print("Total source lines:", numlines) print("Average lines/sec:", ( "%.1f" % (float(numlines)/(totalTime+.05 ) ) )) if failCount: print("FAIL - %d files failed to parse" % failCount) else: print("SUCCESS - all files parsed") return 0 #~ from line_profiler import LineProfiler #~ from pyparsing import ParseResults #~ lp = LineProfiler(ParseResults.__init__) main() #~ lp.print_stats() #~ import hotshot #~ p = hotshot.Profile("vparse.prof",1,1) #~ p.start() #~ main() #~ p.stop() #~ p.close() pyparsing2-2.4.7/examples/withAttribute.py000066400000000000000000000016201365333160500206550ustar00rootroot00000000000000# # withAttribute.py # Copyright, 2007 - Paul McGuire # # Simple example of using withAttribute parse action helper # to define # import pyparsing as pp data = """\  49.950   50.950   51.950  """ td, tdEnd = pp.makeHTMLTags("TD") font, fontEnd = pp.makeHTMLTags("FONT") realNum = pp.pyparsing_common.real NBSP = pp.Literal(" ") patt = td + font + NBSP + realNum("value") + NBSP + fontEnd + tdEnd # always use addParseAction when adding withAttribute as a parse action to a start tag td.addParseAction(pp.withAttribute(align="right", width="80")) for s in patt.searchString(data): print(s.value) pyparsing2-2.4.7/examples/wordsToNum.py000066400000000000000000000060511365333160500201420ustar00rootroot00000000000000# wordsToNum.py # Copyright 2006, Paul McGuire # # Sample parser grammar to read a number given in words, and return the numeric value. # import pyparsing as pp from operator import mul from functools import reduce def makeLit(s, val): ret = pp.CaselessLiteral(s) return ret.setParseAction(pp.replaceWith(val)) unitDefinitions = [ ("zero", 0), ("oh", 0), ("zip", 0), ("zilch", 0), ("nada", 0), ("bupkis", 0), ("one", 1), ("two", 2), ("three", 3), ("four", 4), ("five", 5), ("six", 6), ("seven", 7), ("eight", 8), ("nine", 9), ("ten", 10), ("eleven", 11), ("twelve", 12), ("thirteen", 13), ("fourteen", 14), ("fifteen", 15), ("sixteen", 16), ("seventeen", 17), ("eighteen", 18), ("nineteen", 19), ] units = pp.MatchFirst(makeLit(s,v) for s,v in sorted(unitDefinitions, key=lambda d: -len(d[0]))) tensDefinitions = [ ("ten", 10), ("twenty", 20), ("thirty", 30), ("forty", 40), ("fourty", 40), # for the spelling-challenged... ("fifty", 50), ("sixty", 60), ("seventy", 70), ("eighty", 80), ("ninety", 90), ] tens = pp.MatchFirst(makeLit(s,v) for s,v in tensDefinitions) hundreds = makeLit("hundred", 100) majorDefinitions = [ ("thousand", int(1e3)), ("million", int(1e6)), ("billion", int(1e9)), ("trillion", int(1e12)), ("quadrillion", int(1e15)), ("quintillion", int(1e18)), ] mag = pp.MatchFirst(makeLit(s,v) for s,v in majorDefinitions) wordprod = lambda t: reduce(mul,t) numPart = ((((units + pp.Optional(hundreds)).setParseAction(wordprod) + pp.Optional(tens) ).setParseAction(sum) ^ tens) + pp.Optional(units) ).setParseAction(sum) numWords = ((numPart + pp.Optional(mag)).setParseAction(wordprod)[1, ...]).setParseAction(sum) numWords.setName("num word parser") numWords.ignore(pp.Literal("-")) numWords.ignore(pp.CaselessLiteral("and")) tests = """ one hundred twenty hundred, None one hundred and twennty, None one hundred and twenty, 120 one hundred and three, 103 one hundred twenty-three, 123 one hundred and twenty three, 123 one hundred twenty three million, 123000000 one hundred and twenty three million, 123000000 one hundred twenty three million and three, 123000003 fifteen hundred and sixty five, 1565 seventy-seven thousand eight hundred and nineteen, 77819 seven hundred seventy-seven thousand seven hundred and seventy-seven, 777777 zero, 0 forty two, 42 fourty two, 42 """ # use '| ...' to indicate "if omitted, skip to next" logic test_expr = (numWords('result') | ...) + ',' + (pp.pyparsing_common.integer('expected') | 'None') def verify_result(t): if '_skipped' in t: t['pass'] = False elif 'expected' in t: t['pass'] = t.result == t.expected test_expr.addParseAction(verify_result) test_expr.runTests(tests) pyparsing2-2.4.7/pyparsing.egg-info/000077500000000000000000000000001365333160500173355ustar00rootroot00000000000000pyparsing2-2.4.7/pyparsing.egg-info/PKG-INFO000066400000000000000000000102171365333160500204330ustar00rootroot00000000000000Metadata-Version: 1.2 Name: pyparsing Version: 2.4.7 Summary: Python parsing module Home-page: https://github.com/pyparsing/pyparsing/ Author: Paul McGuire Author-email: ptmcg@users.sourceforge.net License: MIT License Download-URL: https://pypi.org/project/pyparsing/ Description: PyParsing -- A Python Parsing Module ==================================== |Build Status| Introduction ============ 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. The pyparsing module provides a library of classes that client code uses to construct the grammar directly in Python code. *[Since first writing this description of pyparsing in late 2003, this technique for developing parsers has become more widespread, under the name Parsing Expression Grammars - PEGs. See more information on PEGs at* https://en.wikipedia.org/wiki/Parsing_expression_grammar *.]* Here is a program to parse ``"Hello, World!"`` (or any greeting of the form ``"salutation, addressee!"``): .. code:: python from pyparsing import Word, alphas 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 '^' operator definitions. 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 The examples directory includes a simple SQL parser, simple CORBA IDL parser, a config file parser, a chemical formula parser, and a four- function algebraic notation parser, among many others. Documentation ============= There are many examples in the online docstrings of the classes and methods in pyparsing. You can find them compiled into online docs at https://pyparsing-docs.readthedocs.io/en/latest/. Additional documentation resources and project info are listed in the online GitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An entire directory of examples is at https://github.com/pyparsing/pyparsing/tree/master/examples. License ======= MIT License. See header of pyparsing.py History ======= See CHANGES file. .. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master :target: https://travis-ci.org/pyparsing/pyparsing Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.* pyparsing2-2.4.7/pyparsing.egg-info/SOURCES.txt000066400000000000000000000067541365333160500212350ustar00rootroot00000000000000CHANGES CODE_OF_CONDUCT.rst CONTRIBUTING.md LICENSE MANIFEST.in README.rst pyparsing.py setup.cfg setup.py simple_unit_tests.py unitTests.py docs/CODE_OF_CONDUCT.rst docs/HowToUsePyparsing.rst docs/Makefile docs/conf.py docs/index.rst docs/modules.rst docs/pyparsing.rst docs/_static/pyparsingClassDiagram.jpg docs/_static/pyparsingClassDiagram.png examples/0README.html examples/AcManForm.dfm examples/LAparser.py examples/Setup.ini examples/SimpleCalc.py examples/SingleForm.dfm examples/TAP.py examples/__init__.py examples/adventureEngine.py examples/antlr_grammar.py examples/antlr_grammar_tests.py examples/apicheck.py examples/btpyparse.py examples/builtin_parse_action_demo.py examples/cLibHeader.py examples/chemicalFormulas.py examples/commasep.py examples/configParse.py examples/cpp_enum_parser.py examples/datetimeParseActions.py examples/decaf_parser.py examples/delta_time.py examples/dfmparse.py examples/dhcpd_leases_parser.py examples/dictExample.py examples/dictExample2.py examples/ebnf.py examples/ebnftest.py examples/eval_arith.py examples/excelExpr.py examples/fourFn.py examples/gen_ctypes.py examples/getNTPserversNew.py examples/greeting.py examples/greetingInGreek.py examples/greetingInKorean.py examples/groupUsingListAllMatches.py examples/holaMundo.py examples/htmlStripper.py examples/htmlTableParser.py examples/httpServerLogParser.py examples/idlParse.py examples/include_preprocessor.py examples/indentedGrammarExample.py examples/invRegex.py examples/javascript_grammar.g examples/jsonParser.py examples/linenoExample.py examples/list1.py examples/listAllMatches.py examples/lucene_grammar.py examples/macroExpander.py examples/matchPreviousDemo.py examples/mozilla.ics examples/mozillaCalendarParser.py examples/nested.py examples/nested_markup.py examples/numerics.py examples/oc.py examples/parseListString.py examples/parsePythonValue.py examples/parseResultsSumExample.py examples/parseTabularData.py examples/partial_gene_match.py examples/pgn.py examples/position.py examples/protobuf_parser.py examples/pymicko.py examples/pythonGrammarParser.py examples/rangeCheck.py examples/readJson.py examples/removeLineBreaks.py examples/romanNumerals.py examples/rosettacode.py examples/scanExamples.py examples/searchParserAppDemo.py examples/searchparser.py examples/select_parser.py examples/sexpParser.py examples/shapes.py examples/simpleArith.py examples/simpleBool.py examples/simpleSQL.py examples/simpleWiki.py examples/snmp_api.h examples/sparser.py examples/sql2dot.py examples/stackish.py examples/test_bibparse.py examples/urlExtractor.py examples/urlExtractorNew.py examples/verilogParse.py examples/withAttribute.py examples/wordsToNum.py examples/statemachine/documentSignoffDemo.py examples/statemachine/documentsignoffstate.pystate examples/statemachine/libraryBookDemo.py examples/statemachine/librarybookstate.pystate examples/statemachine/statemachine.py examples/statemachine/trafficLightDemo.py examples/statemachine/trafficlightstate.pystate examples/statemachine/vending_machine.py examples/statemachine/video_demo.py examples/statemachine/videostate.pystate pyparsing.egg-info/PKG-INFO pyparsing.egg-info/SOURCES.txt pyparsing.egg-info/dependency_links.txt pyparsing.egg-info/top_level.txt test/__init__.py test/__init__.pyc test/jsonParserTests.py test/jsonParserTests.pyc test/karthik.ini test/parsefiletest_input_file.txt test/__pycache__/__init__.cpython-35.pyc test/__pycache__/__init__.cpython-38.pyc test/__pycache__/jsonParserTests.cpython-35.pyc test/__pycache__/jsonParserTests.cpython-38.pycpyparsing2-2.4.7/pyparsing.egg-info/dependency_links.txt000066400000000000000000000000011365333160500234030ustar00rootroot00000000000000 pyparsing2-2.4.7/pyparsing.egg-info/top_level.txt000066400000000000000000000000121365333160500220600ustar00rootroot00000000000000pyparsing pyparsing2-2.4.7/pyparsing.py000066400000000000000000010257251365333160500162310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # module pyparsing.py # # Copyright (c) 2003-2019 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. # __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 ``", !"``), built up using :class:`Word`, :class:`Literal`, and :class:`And` elements (the :class:`'+'` operators create :class:`And` expressions, and the strings are auto-converted to :class:`Literal` expressions):: 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 :class:`ParseResults` object returned from :class:`ParserElement.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 Getting Started - ----------------- Visit the classes :class:`ParserElement` and :class:`ParseResults` to see the base classes that most other pyparsing classes inherit from. Use the docstrings for examples of how to: - construct literal match expressions from :class:`Literal` and :class:`CaselessLiteral` classes - construct character word-group expressions using the :class:`Word` class - see how to create repetitive expressions using :class:`ZeroOrMore` and :class:`OneOrMore` classes - use :class:`'+'`, :class:`'|'`, :class:`'^'`, and :class:`'&'` operators to combine simple expressions into more complex ones - associate names with your parsed results using :class:`ParserElement.setResultsName` - access the parsed data, which is returned as a :class:`ParseResults` object - find some helpful expression short-cuts like :class:`delimitedList` and :class:`oneOf` - find more useful common expressions in the :class:`pyparsing_common` namespace class """ __version__ = "2.4.7" __versionTime__ = "30 Mar 2020 00:43 UTC" __author__ = "Paul McGuire " import string from weakref import ref as wkref import copy import sys import warnings import re import sre_constants import collections import pprint import traceback import types from datetime import datetime from operator import itemgetter import itertools from functools import wraps from contextlib import contextmanager try: # Python 3 from itertools import filterfalse except ImportError: from itertools import ifilterfalse as filterfalse try: from _thread import RLock except ImportError: from threading import RLock try: # Python 3 from collections.abc import Iterable from collections.abc import MutableMapping, Mapping except ImportError: # Python 2.7 from collections import Iterable from collections import MutableMapping, Mapping try: from collections import OrderedDict as _OrderedDict except ImportError: try: from ordereddict import OrderedDict as _OrderedDict except ImportError: _OrderedDict = None try: from types import SimpleNamespace except ImportError: class SimpleNamespace: pass # version compatibility configuration __compat__ = SimpleNamespace() __compat__.__doc__ = """ A cross-version compatibility configuration for pyparsing features that will be released in a future version. By setting values in this configuration to True, those features can be enabled in prior versions for compatibility development and testing. - collect_all_And_tokens - flag to enable fix for Issue #63 that fixes erroneous grouping of results names when an And expression is nested within an Or or MatchFirst; set to True to enable bugfix released in pyparsing 2.3.0, or False to preserve pre-2.3.0 handling of named results """ __compat__.collect_all_And_tokens = True __diag__ = SimpleNamespace() __diag__.__doc__ = """ Diagnostic configuration (all default to False) - warn_multiple_tokens_in_named_alternation - flag to enable warnings when a results name is defined on a MatchFirst or Or expression with one or more And subexpressions (only warns if __compat__.collect_all_And_tokens is False) - warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results name is defined on a containing expression with ungrouped subexpressions that also have results names - warn_name_set_on_empty_Forward - flag to enable warnings whan a Forward is defined with a results name, but has no contents defined - warn_on_multiple_string_args_to_oneof - flag to enable warnings whan oneOf is incorrectly called with multiple str arguments - enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent calls to ParserElement.setName() """ __diag__.warn_multiple_tokens_in_named_alternation = False __diag__.warn_ungrouped_named_tokens_in_collection = False __diag__.warn_name_set_on_empty_Forward = False __diag__.warn_on_multiple_string_args_to_oneof = False __diag__.enable_debug_on_named_expressions = False __diag__._all_names = [nm for nm in vars(__diag__) if nm.startswith("enable_") or nm.startswith("warn_")] def _enable_all_warnings(): __diag__.warn_multiple_tokens_in_named_alternation = True __diag__.warn_ungrouped_named_tokens_in_collection = True __diag__.warn_name_set_on_empty_Forward = True __diag__.warn_on_multiple_string_args_to_oneof = True __diag__.enable_all_warnings = _enable_all_warnings __all__ = ['__version__', '__versionTime__', '__author__', '__compat__', '__diag__', 'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', 'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', 'PrecededBy', 'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', 'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', 'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', 'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', 'Char', 'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', 'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', 'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', 'htmlComment', 'javaStyleComment', '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', 'ungroup', 'infixNotation', 'locatedExpr', 'withClass', 'CloseMatch', 'tokenMap', 'pyparsing_common', 'pyparsing_unicode', 'unicode_set', 'conditionAsParseAction', 're', ] system_version = tuple(sys.version_info)[:3] PY_3 = system_version[0] == 3 if PY_3: _MAX_INT = sys.maxsize basestring = str unichr = chr unicode = str _ustr = str # build list of single arg builtins, that can be used as parse actions singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max] else: _MAX_INT = sys.maxint range = xrange 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: # Else encode it ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') xmlcharref = Regex(r'&#\d+;') xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) return xmlcharref.transformString(ret) # build list of single arg builtins, tolerant of Python version, that can be used as parse actions singleArgBuiltins = [] import __builtin__ for fname in "sum len sorted reversed list tuple set any all min max".split(): try: singleArgBuiltins.append(getattr(__builtin__, fname)) except AttributeError: continue _generatorType = type((y for y in range(1))) 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 alphas = string.ascii_uppercase + string.ascii_lowercase nums = "0123456789" hexnums = nums + "ABCDEFabcdef" alphanums = alphas + nums _bslash = chr(92) printables = "".join(c for c in string.printable if c not in string.whitespace) def conditionAsParseAction(fn, message=None, fatal=False): msg = message if message is not None else "failed user-defined condition" exc_type = ParseFatalException if fatal else ParseException fn = _trim_arity(fn) @wraps(fn) def pa(s, l, t): if not bool(fn(s, l, t)): raise exc_type(s, l, msg) return pa 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 self.args = (pstr, loc, msg) @classmethod def _from_exception(cls, pe): """ internal factory method to simplify creating one type of ParseException from another - avoids having __init__ signature conflicts among subclasses """ return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) 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): if self.pstr: if self.loc >= len(self.pstr): foundstr = ', found end of text' else: foundstr = (', found %r' % self.pstr[self.loc:self.loc + 1]).replace(r'\\', '\\') else: foundstr = '' return ("%s%s (at char %d), (line:%d, col:%d)" % (self.msg, foundstr, 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 "lineno col line".split() + dir(type(self)) 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 Example:: try: Word(nums).setName("integer").parseString("ABC") except ParseException as pe: print(pe) print("column: {}".format(pe.col)) prints:: Expected integer (at char 0), (line:1, col:1) column: 1 """ @staticmethod def explain(exc, depth=16): """ Method to take an exception and translate the Python internal traceback into a list of the pyparsing expressions that caused the exception to be raised. Parameters: - exc - exception raised during parsing (need not be a ParseException, in support of Python exceptions that might be raised in a parse action) - depth (default=16) - number of levels back in the stack trace to list expression and function names; if None, the full stack trace names will be listed; if 0, only the failing input line, marker, and exception string will be shown Returns a multi-line string listing the ParserElements and/or function names in the exception's stack trace. Note: the diagnostic output will include string representations of the expressions that failed to parse. These representations will be more helpful if you use `setName` to give identifiable names to your expressions. Otherwise they will use the default string forms, which may be cryptic to read. explain() is only supported under Python 3. """ import inspect if depth is None: depth = sys.getrecursionlimit() ret = [] if isinstance(exc, ParseBaseException): ret.append(exc.line) ret.append(' ' * (exc.col - 1) + '^') ret.append("{0}: {1}".format(type(exc).__name__, exc)) if depth > 0: callers = inspect.getinnerframes(exc.__traceback__, context=depth) seen = set() for i, ff in enumerate(callers[-depth:]): frm = ff[0] f_self = frm.f_locals.get('self', None) if isinstance(f_self, ParserElement): if frm.f_code.co_name not in ('parseImpl', '_parseNoCache'): continue if f_self in seen: continue seen.add(f_self) self_type = type(f_self) ret.append("{0}.{1} - {2}".format(self_type.__module__, self_type.__name__, f_self)) elif f_self is not None: self_type = type(f_self) ret.append("{0}.{1}".format(self_type.__module__, self_type.__name__)) else: code = frm.f_code if code.co_name in ('wrapper', ''): continue ret.append("{0}".format(code.co_name)) depth -= 1 if not depth: break return '\n'.join(ret) class ParseFatalException(ParseBaseException): """user-throwable exception thrown when inconsistent parse content is found; stops all parsing immediately""" pass class ParseSyntaxException(ParseFatalException): """just like :class:`ParseFatalException`, but thrown internally when an :class:`ErrorStop` ('-' operator) indicates that parsing is to stop immediately because an unbacktrackable syntax error has been found. """ pass #~ 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 :class:`ParserElement.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[0]) 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.`` - see :class:`ParserElement.setResultsName`) Example:: integer = Word(nums) date_str = (integer.setResultsName("year") + '/' + integer.setResultsName("month") + '/' + integer.setResultsName("day")) # equivalent form: # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") # parseString returns a ParseResults object result = date_str.parseString("1999/12/31") def test(s, fn=repr): print("%s -> %s" % (s, fn(eval(s)))) test("list(result)") test("result[0]") test("result['month']") test("result.day") test("'month' in result") test("'minutes' in result") test("result.dump()", str) prints:: list(result) -> ['1999', '/', '12', '/', '31'] result[0] -> '1999' result['month'] -> '12' result.day -> '31' 'month' in result -> True 'minutes' in result -> False result.dump() -> ['1999', '/', '12', '/', '31'] - day: 31 - month: 12 - year: 1999 """ def __new__(cls, toklist=None, 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=None, name=None, asList=True, modal=True, isinstance=isinstance): if self.__doinit: self.__doinit = False self.__name = None self.__parent = None self.__accumNames = {} self.__asList = asList self.__modal = modal if toklist is None: toklist = [] if isinstance(toklist, list): self.__toklist = toklist[:] elif isinstance(toklist, _generatorType): self.__toklist = list(toklist) else: self.__toklist = [toklist] self.__tokdict = dict() if name is not None and 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 (isinstance(toklist, (type(None), basestring, list)) and toklist in (None, '', [])): if isinstance(toklist, basestring): toklist = [toklist] if asList: if isinstance(toklist, ParseResults): self[name] = _ParseResultsWithOffset(ParseResults(toklist.__toklist), 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, isinstance=isinstance): if isinstance(v, _ParseResultsWithOffset): self.__tokdict[k] = self.__tokdict.get(k, list()) + [v] sub = v[0] elif isinstance(k, (int, slice)): 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, occurrences in self.__tokdict.items(): 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 (not not self.__toklist) __nonzero__ = __bool__ def __iter__(self): return iter(self.__toklist) def __reversed__(self): return iter(self.__toklist[::-1]) def _iterkeys(self): if hasattr(self.__tokdict, "iterkeys"): return self.__tokdict.iterkeys() else: return iter(self.__tokdict) def _itervalues(self): return (self[k] for k in self._iterkeys()) def _iteritems(self): return ((k, self[k]) for k in self._iterkeys()) if PY_3: keys = _iterkeys """Returns an iterator of all named result keys.""" values = _itervalues """Returns an iterator of all named result values.""" items = _iteritems """Returns an iterator of all named result key-value tuples.""" else: iterkeys = _iterkeys """Returns an iterator of all named result keys (Python 2.x only).""" itervalues = _itervalues """Returns an iterator of all named result values (Python 2.x only).""" iteritems = _iteritems """Returns an iterator of all named result key-value tuples (Python 2.x only).""" def keys(self): """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" return list(self.iterkeys()) def values(self): """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" return list(self.itervalues()) def items(self): """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" return list(self.iteritems()) def haskeys(self): """Since keys() returns an iterator, this method is helpful in bypassing code that looks for the existence of any defined results names.""" return bool(self.__tokdict) def pop(self, *args, **kwargs): """ Removes and returns item at specified index (default= ``last``). Supports both ``list`` and ``dict`` semantics for ``pop()``. If passed no argument or an integer argument, it will use ``list`` semantics and pop tokens from the list of parsed tokens. If passed a non-integer argument (most likely a string), it will use ``dict`` semantics and pop the corresponding value from any defined results names. A second default return value argument is supported, just as in ``dict.pop()``. Example:: def remove_first(tokens): tokens.pop(0) print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] label = Word(alphas) patt = label("LABEL") + OneOrMore(Word(nums)) print(patt.parseString("AAB 123 321").dump()) # Use pop() in a parse action to remove named result (note that corresponding value is not # removed from list form of results) def remove_LABEL(tokens): tokens.pop("LABEL") return tokens patt.addParseAction(remove_LABEL) print(patt.parseString("AAB 123 321").dump()) prints:: ['AAB', '123', '321'] - LABEL: AAB ['AAB', '123', '321'] """ if not args: args = [-1] for k, v in kwargs.items(): if k == 'default': args = (args[0], v) else: raise TypeError("pop() got an unexpected keyword argument '%s'" % k) if (isinstance(args[0], int) or len(args) == 1 or args[0] in self): index = args[0] ret = self[index] del self[index] return ret else: defaultvalue = args[1] return defaultvalue 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. Similar to ``dict.get()``. Example:: integer = Word(nums) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") result = date_str.parseString("1999/12/31") print(result.get("year")) # -> '1999' print(result.get("hour", "not specified")) # -> 'not specified' print(result.get("hour")) # -> None """ if key in self: return self[key] else: return defaultValue def insert(self, index, insStr): """ Inserts new element at location index in the list of parsed tokens. Similar to ``list.insert()``. Example:: print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] # use a parse action to insert the parse location in the front of the parsed results def insert_locn(locn, tokens): tokens.insert(0, locn) print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] """ self.__toklist.insert(index, insStr) # fixup indices in token dictionary for name, occurrences in self.__tokdict.items(): for k, (value, position) in enumerate(occurrences): occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) def append(self, item): """ Add single element to end of ParseResults list of elements. Example:: print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] # use a parse action to compute the sum of the parsed integers, and add it to the end def append_sum(tokens): tokens.append(sum(map(int, tokens))) print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] """ self.__toklist.append(item) def extend(self, itemseq): """ Add sequence of elements to end of ParseResults list of elements. Example:: patt = OneOrMore(Word(alphas)) # use a parse action to append the reverse of the matched strings, to make a palindrome def make_palindrome(tokens): tokens.extend(reversed([t[::-1] for t in tokens])) return ''.join(tokens) print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' """ if isinstance(itemseq, ParseResults): self.__iadd__(itemseq) else: self.__toklist.extend(itemseq) def clear(self): """ Clear all elements and results names. """ del self.__toklist[:] self.__tokdict.clear() def __getattr__(self, name): try: return self[name] except KeyError: return "" 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: offset if a < 0 else 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) return self def __radd__(self, other): if isinstance(other, int) and other == 0: # useful for merging many ParseResults using sum() builtin return self.copy() else: # this may raise a TypeError - so be it return other + self def __repr__(self): return "(%s, %s)" % (repr(self.__toklist), repr(self.__tokdict)) def __str__(self): return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']' 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. Example:: patt = OneOrMore(Word(alphas)) result = patt.parseString("sldkj lsdkj sldkj") # even though the result prints in string-like form, it is actually a pyparsing ParseResults print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] # Use asList() to create an actual list result_list = result.asList() print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] """ return [res.asList() if isinstance(res, ParseResults) else res for res in self.__toklist] def asDict(self): """ Returns the named parse results as a nested dictionary. Example:: integer = Word(nums) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") result = date_str.parseString('12/31/1999') print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) result_dict = result.asDict() print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} # even though a ParseResults supports dict-like access, sometime you just need to have a dict import json print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} """ if PY_3: item_fn = self.items else: item_fn = self.iteritems def toItem(obj): if isinstance(obj, ParseResults): if obj.haskeys(): return obj.asDict() else: return [toItem(v) for v in obj] else: return obj return dict((k, toItem(v)) for k, v in item_fn()) def copy(self): """ Returns a new copy of a :class:`ParseResults` object. """ ret = ParseResults(self.__toklist) ret.__tokdict = dict(self.__tokdict.items()) 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): """ (Deprecated) 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, ">"] for i, res in enumerate(self.__toklist): 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): r""" Returns the results name for this token expression. Useful when several different expressions might match at a particular location. Example:: integer = Word(nums) ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") house_number_expr = Suppress('#') + Word(nums, alphanums) user_data = (Group(house_number_expr)("house_number") | Group(ssn_expr)("ssn") | Group(integer)("age")) user_info = OneOrMore(user_data) result = user_info.parseString("22 111-22-3333 #221B") for item in result: print(item.getName(), ':', item[0]) prints:: age : 22 ssn : 111-22-3333 house_number : 221B """ 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 next(iter(self.__tokdict.values()))[0][1] in (0, -1)): return next(iter(self.__tokdict.keys())) else: return None def dump(self, indent='', full=True, include_list=True, _depth=0): """ Diagnostic method for listing out the contents of a :class:`ParseResults`. Accepts an optional ``indent`` argument so that this string can be embedded in a nested display of other data. Example:: integer = Word(nums) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") result = date_str.parseString('12/31/1999') print(result.dump()) prints:: ['12', '/', '31', '/', '1999'] - day: 1999 - month: 31 - year: 12 """ out = [] NL = '\n' if include_list: out.append(indent + _ustr(self.asList())) else: out.append('') if full: if self.haskeys(): items = sorted((str(k), v) for k, v in self.items()) for k, v in items: if out: out.append(NL) out.append("%s%s- %s: " % (indent, (' ' * _depth), k)) if isinstance(v, ParseResults): if v: out.append(v.dump(indent=indent, full=full, include_list=include_list, _depth=_depth + 1)) else: out.append(_ustr(v)) else: out.append(repr(v)) elif any(isinstance(vv, ParseResults) for vv in self): v = self for i, vv in enumerate(v): if isinstance(vv, ParseResults): out.append("\n%s%s[%d]:\n%s%s%s" % (indent, (' ' * (_depth)), i, indent, (' ' * (_depth + 1)), vv.dump(indent=indent, full=full, include_list=include_list, _depth=_depth + 1))) else: out.append("\n%s%s[%d]:\n%s%s%s" % (indent, (' ' * (_depth)), i, indent, (' ' * (_depth + 1)), _ustr(vv))) return "".join(out) def pprint(self, *args, **kwargs): """ Pretty-printer for parsed results as a list, using the `pprint `_ module. Accepts additional positional or keyword args as defined for `pprint.pprint `_ . Example:: ident = Word(alphas, alphanums) num = Word(nums) func = Forward() term = ident | num | Group('(' + func + ')') func <<= ident + Group(Optional(delimitedList(term))) result = func.parseString("fna a,b,(fnb c,d,200),100") result.pprint(width=40) prints:: ['fna', ['a', 'b', ['(', 'fnb', ['c', 'd', '200'], ')'], '100']] """ pprint.pprint(self.asList(), *args, **kwargs) # 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 __getnewargs__(self): return self.__toklist, self.__name, self.__asList, self.__modal def __dir__(self): return dir(type(self)) + list(self.keys()) @classmethod def from_dict(cls, other, name=None): """ Helper classmethod to construct a ParseResults from a dict, preserving the name-value relations as results names. If an optional 'name' argument is given, a nested ParseResults will be returned """ def is_iterable(obj): try: iter(obj) except Exception: return False else: if PY_3: return not isinstance(obj, (str, bytes)) else: return not isinstance(obj, basestring) ret = cls([]) for k, v in other.items(): if isinstance(v, Mapping): ret += cls.from_dict(v, name=k) else: ret += cls([v], name=k, asList=is_iterable(v)) if name is not None: ret = cls([ret], name=name) return ret MutableMapping.register(ParseResults) 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 :class:`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. """ s = strg return 1 if 0 < loc < len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc) def lineno(loc, strg): """Returns current line number within a string, counting newlines as line separators. The first line is number 1. Note - the default parsing behavior is to expand tabs in the input string before starting the parsing process. See :class:`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 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 # Only works on Python 3.x - nonlocal is toxic to Python 2 installs #~ 'decorator to trim function calls to match the arity of the target' #~ def _trim_arity(func, maxargs=3): #~ if func in singleArgBuiltins: #~ return lambda s,l,t: func(t) #~ limit = 0 #~ foundArity = False #~ def wrapper(*args): #~ nonlocal limit,foundArity #~ while 1: #~ try: #~ ret = func(*args[limit:]) #~ foundArity = True #~ return ret #~ except TypeError: #~ if limit == maxargs or foundArity: #~ raise #~ limit += 1 #~ continue #~ return wrapper # this version is Python 2.x-3.x cross-compatible 'decorator to trim function calls to match the arity of the target' def _trim_arity(func, maxargs=2): if func in singleArgBuiltins: return lambda s, l, t: func(t) limit = [0] foundArity = [False] # traceback return data structure changed in Py3.5 - normalize back to plain tuples if system_version[:2] >= (3, 5): def extract_stack(limit=0): # special handling for Python 3.5.0 - extra deep call stack by 1 offset = -3 if system_version == (3, 5, 0) else -2 frame_summary = traceback.extract_stack(limit=-offset + limit - 1)[offset] return [frame_summary[:2]] def extract_tb(tb, limit=0): frames = traceback.extract_tb(tb, limit=limit) frame_summary = frames[-1] return [frame_summary[:2]] else: extract_stack = traceback.extract_stack extract_tb = traceback.extract_tb # synthesize what would be returned by traceback.extract_stack at the call to # user's parse action 'func', so that we don't incur call penalty at parse time LINE_DIFF = 6 # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! this_line = extract_stack(limit=2)[-1] pa_call_line_synth = (this_line[0], this_line[1] + LINE_DIFF) def wrapper(*args): while 1: try: ret = func(*args[limit[0]:]) foundArity[0] = True return ret except TypeError: # re-raise TypeErrors if they did not come from our arity testing if foundArity[0]: raise else: try: tb = sys.exc_info()[-1] if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: raise finally: try: del tb except NameError: pass if limit[0] <= maxargs: limit[0] += 1 continue raise # copy func name to wrapper for sensible debug output func_name = "" try: func_name = getattr(func, '__name__', getattr(func, '__class__').__name__) except Exception: func_name = str(func) wrapper.__name__ = func_name return wrapper class ParserElement(object): """Abstract base level parser element class.""" DEFAULT_WHITE_CHARS = " \n\t\r" verbose_stacktrace = False @staticmethod def setDefaultWhitespaceChars(chars): r""" Overrides the default whitespace chars Example:: # default whitespace chars are space, and newline OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] # change to just treat newline as significant ParserElement.setDefaultWhitespaceChars(" \t") OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] """ ParserElement.DEFAULT_WHITE_CHARS = chars @staticmethod def inlineLiteralsUsing(cls): """ Set class to be used for inclusion of string literals into a parser. Example:: # default literal class used is Literal integer = Word(nums) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] # change to Suppress ParserElement.inlineLiteralsUsing(Suppress) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] """ ParserElement._literalStringClass = cls @classmethod def _trim_traceback(cls, tb): while tb.tb_next: tb = tb.tb_next return tb 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 = set(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 :class:`ParserElement`. Useful for defining different parse actions for the same parsing pattern, using copies of the original parse element. Example:: integer = Word(nums).setParseAction(lambda toks: int(toks[0])) integerK = integer.copy().addParseAction(lambda toks: toks[0] * 1024) + Suppress("K") integerM = integer.copy().addParseAction(lambda toks: toks[0] * 1024 * 1024) + Suppress("M") print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) prints:: [5120, 100, 655360, 268435456] Equivalent form of ``expr.copy()`` is just ``expr()``:: integerM = integer().addParseAction(lambda toks: toks[0] * 1024 * 1024) + Suppress("M") """ 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, makes debugging and exception messages clearer. Example:: Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) """ self.name = name self.errmsg = "Expected " + self.name if __diag__.enable_debug_on_named_expressions: self.setDebug() 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 :class:`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. You can also set results names using the abbreviated syntax, ``expr("name")`` in place of ``expr.setResultsName("name")`` - see :class:`__call__`. Example:: date_str = (integer.setResultsName("year") + '/' + integer.setResultsName("month") + '/' + integer.setResultsName("day")) # equivalent form: date_str = integer("year") + '/' + integer("month") + '/' + integer("day") """ return self._setResultsName(name, listAllMatches) def _setResultsName(self, name, listAllMatches=False): newself = self.copy() if name.endswith("*"): name = name[:-1] listAllMatches = True 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 # this call to pdb.set_trace() is intentional, not a checkin error 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 setParseAction(self, *fns, **kwargs): """ Define one or more actions 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 :class:`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. If None is passed as the parse action, all previously added parse actions for this expression are cleared. Optional keyword arguments: - callDuringTry = (default= ``False``) indicate if parse action should be run during lookaheads and alternate testing Note: the default parsing behavior is to expand tabs in the input string before starting the parsing process. See :class:`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. Example:: integer = Word(nums) date_str = integer + '/' + integer + '/' + integer date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] # use parse action to convert to ints at parse time integer = Word(nums).setParseAction(lambda toks: int(toks[0])) date_str = integer + '/' + integer + '/' + integer # note that integer fields are now ints, not strings date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] """ if list(fns) == [None,]: self.parseAction = [] else: if not all(callable(fn) for fn in fns): raise TypeError("parse actions must be callable") self.parseAction = list(map(_trim_arity, list(fns))) self.callDuringTry = kwargs.get("callDuringTry", False) return self def addParseAction(self, *fns, **kwargs): """ Add one or more parse actions to expression's list of parse actions. See :class:`setParseAction`. See examples in :class:`copy`. """ self.parseAction += list(map(_trim_arity, list(fns))) self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) return self def addCondition(self, *fns, **kwargs): """Add a boolean predicate function to expression's list of parse actions. See :class:`setParseAction` for function call signatures. Unlike ``setParseAction``, functions passed to ``addCondition`` need to return boolean success/fail of the condition. Optional keyword arguments: - message = define a custom message to be used in the raised exception - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException Example:: integer = Word(nums).setParseAction(lambda toks: int(toks[0])) year_int = integer.copy() year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") date_str = year_int + '/' + integer + '/' + integer result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) """ for fn in fns: self.parseAction.append(conditionAsParseAction(fn, message=kwargs.get('message'), fatal=kwargs.get('fatal', False))) self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) 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 :class:`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): TRY, MATCH, FAIL = 0, 1, 2 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[TRY]: self.debugActions[TRY](instring, loc, self) try: if callPreParse and self.callPreparse: preloc = self.preParse(instring, loc) else: preloc = loc tokensStart = preloc if self.mayIndexError or preloc >= 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) except Exception as err: # ~ print ("Exception raised:", err) if self.debugActions[FAIL]: self.debugActions[FAIL](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 = preloc if self.mayIndexError or preloc >= 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: try: tokens = fn(instring, tokensStart, retTokens) except IndexError as parse_action_exc: exc = ParseException("exception raised in parse action") exc.__cause__ = parse_action_exc raise exc if tokens is not None and tokens is not retTokens: retTokens = ParseResults(tokens, self.resultsName, asList=self.saveAsList and isinstance(tokens, (ParseResults, list)), modal=self.modalResults) except Exception as err: # ~ print "Exception raised in user parse action:", err if self.debugActions[FAIL]: self.debugActions[FAIL](instring, tokensStart, self, err) raise else: for fn in self.parseAction: try: tokens = fn(instring, tokensStart, retTokens) except IndexError as parse_action_exc: exc = ParseException("exception raised in parse action") exc.__cause__ = parse_action_exc raise exc if tokens is not None and tokens is not retTokens: 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[MATCH]: self.debugActions[MATCH](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) def canParseNext(self, instring, loc): try: self.tryParse(instring, loc) except (ParseException, IndexError): return False else: return True class _UnboundedCache(object): def __init__(self): cache = {} self.not_in_cache = not_in_cache = object() def get(self, key): return cache.get(key, not_in_cache) def set(self, key, value): cache[key] = value def clear(self): cache.clear() def cache_len(self): return len(cache) self.get = types.MethodType(get, self) self.set = types.MethodType(set, self) self.clear = types.MethodType(clear, self) self.__len__ = types.MethodType(cache_len, self) if _OrderedDict is not None: class _FifoCache(object): def __init__(self, size): self.not_in_cache = not_in_cache = object() cache = _OrderedDict() def get(self, key): return cache.get(key, not_in_cache) def set(self, key, value): cache[key] = value while len(cache) > size: try: cache.popitem(False) except KeyError: pass def clear(self): cache.clear() def cache_len(self): return len(cache) self.get = types.MethodType(get, self) self.set = types.MethodType(set, self) self.clear = types.MethodType(clear, self) self.__len__ = types.MethodType(cache_len, self) else: class _FifoCache(object): def __init__(self, size): self.not_in_cache = not_in_cache = object() cache = {} key_fifo = collections.deque([], size) def get(self, key): return cache.get(key, not_in_cache) def set(self, key, value): cache[key] = value while len(key_fifo) > size: cache.pop(key_fifo.popleft(), None) key_fifo.append(key) def clear(self): cache.clear() key_fifo.clear() def cache_len(self): return len(cache) self.get = types.MethodType(get, self) self.set = types.MethodType(set, self) self.clear = types.MethodType(clear, self) self.__len__ = types.MethodType(cache_len, self) # argument cache for optimizing repeated calls when backtracking through recursive expressions packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail packrat_cache_lock = RLock() packrat_cache_stats = [0, 0] # 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): HIT, MISS = 0, 1 lookup = (self, instring, loc, callPreParse, doActions) with ParserElement.packrat_cache_lock: cache = ParserElement.packrat_cache value = cache.get(lookup) if value is cache.not_in_cache: ParserElement.packrat_cache_stats[MISS] += 1 try: value = self._parseNoCache(instring, loc, doActions, callPreParse) except ParseBaseException as pe: # cache a copy of the exception, without the traceback cache.set(lookup, pe.__class__(*pe.args)) raise else: cache.set(lookup, (value[0], value[1].copy())) return value else: ParserElement.packrat_cache_stats[HIT] += 1 if isinstance(value, Exception): raise value return value[0], value[1].copy() _parse = _parseNoCache @staticmethod def resetCache(): ParserElement.packrat_cache.clear() ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) _packratEnabled = False @staticmethod def enablePackrat(cache_size_limit=128): """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. Parameters: - cache_size_limit - (default= ``128``) - if an integer value is provided will limit the size of the packrat cache; if None is passed, then the cache size will be unbounded; if 0 is passed, the cache will be effectively disabled. 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 :class:`ParserElement.enablePackrat`. For best results, call ``enablePackrat()`` immediately after importing pyparsing. Example:: import pyparsing pyparsing.ParserElement.enablePackrat() """ if not ParserElement._packratEnabled: ParserElement._packratEnabled = True if cache_size_limit is None: ParserElement.packrat_cache = ParserElement._UnboundedCache() else: ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) ParserElement._parse = ParserElement._parseCache 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. Returns the parsed data as a :class:`ParseResults` object, which may be accessed as a list, or as a dict or object with attributes if the given parser includes results names. 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 :class:`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`` Example:: Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text """ 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) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace if getattr(exc, '__traceback__', None) is not None: exc.__traceback__ = self._trim_traceback(exc.__traceback__) raise exc else: return tokens def scanString(self, instring, maxMatches=_MAX_INT, overlap=False): """ 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. If ``overlap`` is specified, then overlapping matches will be reported. Note that the start and end locations are reported relative to the string being parsed. See :class:`parseString` for more information on parsing strings with embedded tabs. Example:: source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" print(source) for tokens, start, end in Word(alphas).scanString(source): print(' '*start + '^'*(end-start)) print(' '*start + tokens[0]) prints:: sldjf123lsdjjkf345sldkjf879lkjsfd987 ^^^^^ sldjf ^^^^^^^ lsdjjkf ^^^^^^ sldkjf ^^^^^^ lkjsfd """ 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 if overlap: nextloc = preparseFn(instring, loc) if nextloc > loc: loc = nextLoc else: loc += 1 else: loc = nextLoc else: loc = preloc + 1 except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace if getattr(exc, '__traceback__', None) is not None: exc.__traceback__ = self._trim_traceback(exc.__traceback__) raise exc def transformString(self, instring): """ Extension to :class:`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. Example:: wd = Word(alphas) wd.setParseAction(lambda toks: toks[0].title()) print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) prints:: Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. """ 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:]) out = [o for o in out if o] return "".join(map(_ustr, _flatten(out))) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace if getattr(exc, '__traceback__', None) is not None: exc.__traceback__ = self._trim_traceback(exc.__traceback__) raise exc def searchString(self, instring, maxMatches=_MAX_INT): """ Another extension to :class:`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. Example:: # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters cap_word = Word(alphas.upper(), alphas.lower()) print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) # the sum() builtin can be used to merge results into a single ParseResults object print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))) prints:: [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] """ try: return ParseResults([t for t, s, e in self.scanString(instring, maxMatches)]) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace if getattr(exc, '__traceback__', None) is not None: exc.__traceback__ = self._trim_traceback(exc.__traceback__) raise exc def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): """ Generator method to split a string using the given expression as a separator. May be called with optional ``maxsplit`` argument, to limit the number of splits; and the optional ``includeSeparators`` argument (default= ``False``), if the separating matching text should be included in the split results. Example:: punc = oneOf(list(".,;:/-!?")) print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) prints:: ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] """ splits = 0 last = 0 for t, s, e in self.scanString(instring, maxMatches=maxsplit): yield instring[last:s] if includeSeparators: yield t[0] last = e yield instring[last:] def __add__(self, other): """ Implementation of + operator - returns :class:`And`. Adding strings to a ParserElement converts them to :class:`Literal`s by default. Example:: greet = Word(alphas) + "," + Word(alphas) + "!" hello = "Hello, World!" print (hello, "->", greet.parseString(hello)) prints:: Hello, World! -> ['Hello', ',', 'World', '!'] ``...`` may be used as a parse expression as a short form of :class:`SkipTo`. Literal('start') + ... + Literal('end') is equivalent to: Literal('start') + SkipTo('end')("_skipped*") + Literal('end') Note that the skipped text is returned with '_skipped' as a results name, and to support having multiple skips in the same parser, the value returned is a list of all skipped text. """ if other is Ellipsis: return _PendingSkip(self) if isinstance(other, basestring): other = self._literalStringClass(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 :class:`ParserElement` """ if other is Ellipsis: return SkipTo(self)("_skipped*") + self if isinstance(other, basestring): other = self._literalStringClass(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 :class:`And` with error stop """ if isinstance(other, basestring): other = self._literalStringClass(other) if not isinstance(other, ParserElement): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) return None return self + And._ErrorStop() + other def __rsub__(self, other): """ Implementation of - operator when left operand is not a :class:`ParserElement` """ if isinstance(other, basestring): other = self._literalStringClass(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): """ Implementation of * operator, allows use of ``expr * 3`` in place of ``expr + expr + expr``. Expressions may also me multiplied by a 2-integer tuple, similar to ``{min, max}`` multipliers in regular expressions. Tuples may also include ``None`` as in: - ``expr*(n, None)`` or ``expr*(n, )`` is equivalent to ``expr*n + ZeroOrMore(expr)`` (read as "at least n instances of ``expr``") - ``expr*(None, n)`` is equivalent to ``expr*(0, n)`` (read as "0 to n instances of ``expr``") - ``expr*(None, None)`` is equivalent to ``ZeroOrMore(expr)`` - ``expr*(1, None)`` is equivalent to ``OneOrMore(expr)`` Note that ``expr*(None, n)`` does not raise an exception if more than n exprs exist in the input stream; that is, ``expr*(None, n)`` does not enforce a maximum number of expr occurrences. If this behavior is desired, then write ``expr*(None, n) + ~expr`` """ if other is Ellipsis: other = (0, None) elif isinstance(other, tuple) and other[:1] == (Ellipsis,): other = ((0, ) + other[1:] + (None,))[:2] if isinstance(other, int): minElements, optElements = other, 0 elif isinstance(other, tuple): other = tuple(o if o is not Ellipsis else None for o in other) 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 :class:`MatchFirst` """ if other is Ellipsis: return _PendingSkip(self, must_skip=True) if isinstance(other, basestring): other = self._literalStringClass(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 :class:`ParserElement` """ if isinstance(other, basestring): other = self._literalStringClass(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 :class:`Or` """ if isinstance(other, basestring): other = self._literalStringClass(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 :class:`ParserElement` """ if isinstance(other, basestring): other = self._literalStringClass(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 :class:`Each` """ if isinstance(other, basestring): other = self._literalStringClass(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 :class:`ParserElement` """ if isinstance(other, basestring): other = self._literalStringClass(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 :class:`NotAny` """ return NotAny(self) def __iter__(self): # must implement __iter__ to override legacy use of sequential access to __getitem__ to # iterate over a sequence raise TypeError('%r object is not iterable' % self.__class__.__name__) def __getitem__(self, key): """ use ``[]`` indexing notation as a short form for expression repetition: - ``expr[n]`` is equivalent to ``expr*n`` - ``expr[m, n]`` is equivalent to ``expr*(m, n)`` - ``expr[n, ...]`` or ``expr[n,]`` is equivalent to ``expr*n + ZeroOrMore(expr)`` (read as "at least n instances of ``expr``") - ``expr[..., n]`` is equivalent to ``expr*(0, n)`` (read as "0 to n instances of ``expr``") - ``expr[...]`` and ``expr[0, ...]`` are equivalent to ``ZeroOrMore(expr)`` - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)`` ``None`` may be used in place of ``...``. Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception if more than ``n`` ``expr``s exist in the input stream. If this behavior is desired, then write ``expr[..., n] + ~expr``. """ # convert single arg keys to tuples try: if isinstance(key, str): key = (key,) iter(key) except TypeError: key = (key, key) if len(key) > 2: warnings.warn("only 1 or 2 index arguments supported ({0}{1})".format(key[:5], '... [{0}]'.format(len(key)) if len(key) > 5 else '')) # clip to 2 elements ret = self * tuple(key[:2]) return ret def __call__(self, name=None): """ Shortcut for :class:`setResultsName`, with ``listAllMatches=False``. If ``name`` is given with a trailing ``'*'`` character, then ``listAllMatches`` will be passed as ``True``. If ``name` is omitted, same as calling :class:`copy`. Example:: # these are equivalent userdata = Word(alphas).setResultsName("name") + Word(nums + "-").setResultsName("socsecno") userdata = Word(alphas)("name") + Word(nums + "-")("socsecno") """ if name is not None: return self._setResultsName(name) else: return self.copy() def suppress(self): """ Suppresses the output of this :class:`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 :class:`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. Example:: patt = OneOrMore(Word(alphas)) patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] patt.ignore(cStyleComment) patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] """ if isinstance(other, basestring): other = Suppress(other) if isinstance(other, Suppress): if other not in self.ignoreExprs: self.ignoreExprs.append(other) else: self.ignoreExprs.append(Suppress(other.copy())) 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. Example:: wd = Word(alphas).setName("alphaword") integer = Word(nums).setName("numword") term = wd | integer # turn on debugging for wd wd.setDebug() OneOrMore(term).parseString("abc 123 xyz 890") prints:: Match alphaword at loc 0(1,1) Matched alphaword -> ['abc'] Match alphaword at loc 3(1,4) Exception raised:Expected alphaword (at char 4), (line:1, col:5) Match alphaword at loc 7(1,8) Matched alphaword -> ['xyz'] Match alphaword at loc 11(1,12) Exception raised:Expected alphaword (at char 12), (line:1, col:13) Match alphaword at loc 15(1,16) Exception raised:Expected alphaword (at char 15), (line:1, col:16) The output shown is that produced by the default debug actions - custom debug actions can be specified using :class:`setDebugActions`. Prior to attempting to match the ``wd`` expression, the debugging message ``"Match at loc (,)"`` is shown. Then if the parse succeeds, a ``"Matched"`` message is shown, or an ``"Exception raised"`` message is shown. Also note the use of :class:`setName` to assign a human-readable name to the expression, which makes debugging and exception messages easier to understand - for instance, the default name created for the :class:`Word` expression without calling ``setName`` is ``"W:(ABCD...)"``. """ 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=None): """ 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: with open(file_or_filename, "r") as f: file_contents = f.read() try: return self.parseString(file_contents, parseAll) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace if getattr(exc, '__traceback__', None) is not None: exc.__traceback__ = self._trim_traceback(exc.__traceback__) raise exc def __eq__(self, other): if self is other: return True elif isinstance(other, basestring): return self.matches(other) elif isinstance(other, ParserElement): return vars(self) == vars(other) return False def __ne__(self, other): return not (self == other) def __hash__(self): return id(self) def __req__(self, other): return self == other def __rne__(self, other): return not (self == other) def matches(self, testString, parseAll=True): """ Method for quick testing of a parser against a test string. Good for simple inline microtests of sub expressions while building up larger parser. Parameters: - testString - to test against this expression for a match - parseAll - (default= ``True``) - flag to pass to :class:`parseString` when running tests Example:: expr = Word(nums) assert expr.matches("100") """ try: self.parseString(_ustr(testString), parseAll=parseAll) return True except ParseBaseException: return False def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False, postParse=None, file=None): """ Execute the parse expression on a series of test strings, showing each test, the parsed results or where the parse failed. Quick and easy way to run a parse expression against a list of sample strings. Parameters: - tests - a list of separate test strings, or a multiline string of test strings - parseAll - (default= ``True``) - flag to pass to :class:`parseString` when running tests - comment - (default= ``'#'``) - expression for indicating embedded comments in the test string; pass None to disable comment filtering - fullDump - (default= ``True``) - dump results as list followed by results names in nested outline; if False, only dump nested list - printResults - (default= ``True``) prints test output to stdout - failureTests - (default= ``False``) indicates if these tests are expected to fail parsing - postParse - (default= ``None``) optional callback for successful parse results; called as `fn(test_string, parse_results)` and returns a string to be added to the test output - file - (default=``None``) optional file-like object to which test output will be written; if None, will default to ``sys.stdout`` Returns: a (success, results) tuple, where success indicates that all tests succeeded (or failed if ``failureTests`` is True), and the results contain a list of lines of each test's output Example:: number_expr = pyparsing_common.number.copy() result = number_expr.runTests(''' # unsigned integer 100 # negative integer -100 # float with scientific notation 6.02e23 # integer with scientific notation 1e-12 ''') print("Success" if result[0] else "Failed!") result = number_expr.runTests(''' # stray character 100Z # missing leading digit before '.' -.100 # too many '.' 3.14.159 ''', failureTests=True) print("Success" if result[0] else "Failed!") prints:: # unsigned integer 100 [100] # negative integer -100 [-100] # float with scientific notation 6.02e23 [6.02e+23] # integer with scientific notation 1e-12 [1e-12] Success # stray character 100Z ^ FAIL: Expected end of text (at char 3), (line:1, col:4) # missing leading digit before '.' -.100 ^ FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) # too many '.' 3.14.159 ^ FAIL: Expected end of text (at char 4), (line:1, col:5) Success Each test string must be on a single line. If you want to test a string that spans multiple lines, create a test like this:: expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") (Note that this is a raw string literal, you must include the leading 'r'.) """ if isinstance(tests, basestring): tests = list(map(str.strip, tests.rstrip().splitlines())) if isinstance(comment, basestring): comment = Literal(comment) if file is None: file = sys.stdout print_ = file.write allResults = [] comments = [] success = True NL = Literal(r'\n').addParseAction(replaceWith('\n')).ignore(quotedString) BOM = u'\ufeff' for t in tests: if comment is not None and comment.matches(t, False) or comments and not t: comments.append(t) continue if not t: continue out = ['\n' + '\n'.join(comments) if comments else '', t] comments = [] try: # convert newline marks to actual newlines, and strip leading BOM if present t = NL.transformString(t.lstrip(BOM)) result = self.parseString(t, parseAll=parseAll) except ParseBaseException as pe: fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" if '\n' in t: out.append(line(pe.loc, t)) out.append(' ' * (col(pe.loc, t) - 1) + '^' + fatal) else: out.append(' ' * pe.loc + '^' + fatal) out.append("FAIL: " + str(pe)) success = success and failureTests result = pe except Exception as exc: out.append("FAIL-EXCEPTION: " + str(exc)) success = success and failureTests result = exc else: success = success and not failureTests if postParse is not None: try: pp_value = postParse(t, result) if pp_value is not None: if isinstance(pp_value, ParseResults): out.append(pp_value.dump()) else: out.append(str(pp_value)) else: out.append(result.dump()) except Exception as e: out.append(result.dump(full=fullDump)) out.append("{0} failed: {1}: {2}".format(postParse.__name__, type(e).__name__, e)) else: out.append(result.dump(full=fullDump)) if printResults: if fullDump: out.append('') print_('\n'.join(out)) allResults.append((t, result)) return success, allResults class _PendingSkip(ParserElement): # internal placeholder class to hold a place were '...' is added to a parser element, # once another ParserElement is added, this placeholder will be replaced with a SkipTo def __init__(self, expr, must_skip=False): super(_PendingSkip, self).__init__() self.strRepr = str(expr + Empty()).replace('Empty', '...') self.name = self.strRepr self.anchor = expr self.must_skip = must_skip def __add__(self, other): skipper = SkipTo(other).setName("...")("_skipped*") if self.must_skip: def must_skip(t): if not t._skipped or t._skipped.asList() == ['']: del t[0] t.pop("_skipped", None) def show_skip(t): if t._skipped.asList()[-1:] == ['']: skipped = t.pop('_skipped') t['_skipped'] = 'missing <' + repr(self.anchor) + '>' return (self.anchor + skipper().addParseAction(must_skip) | skipper().addParseAction(show_skip)) + other return self.anchor + skipper + other def __repr__(self): return self.strRepr def parseImpl(self, *args): raise Exception("use of `...` expression without following SkipTo target expression") class Token(ParserElement): """Abstract :class:`ParserElement` subclass, for defining atomic matching patterns. """ def __init__(self): super(Token, self).__init__(savelist=False) 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" def parseImpl(self, instring, loc, doActions=True): raise ParseException(instring, loc, self.errmsg, self) class Literal(Token): """Token to exactly match a specified string. Example:: Literal('blah').parseString('blah') # -> ['blah'] Literal('blah').parseString('blahfooblah') # -> ['blah'] Literal('blah').parseString('bla') # -> Exception: Expected "blah" For case-insensitive matching, use :class:`CaselessLiteral`. For keyword matching (force word break before and after the matched string), use :class:`Keyword` or :class:`CaselessKeyword`. """ 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.mayIndexError = False # Performance tuning: modify __class__ to select # a parseImpl optimized for single-character check if self.matchLen == 1 and type(self) is Literal: self.__class__ = _SingleCharLiteral def parseImpl(self, instring, loc, doActions=True): if instring[loc] == self.firstMatchChar and instring.startswith(self.match, loc): return loc + self.matchLen, self.match raise ParseException(instring, loc, self.errmsg, self) class _SingleCharLiteral(Literal): def parseImpl(self, instring, loc, doActions=True): if instring[loc] == self.firstMatchChar: return loc + 1, self.match raise ParseException(instring, loc, self.errmsg, self) _L = Literal ParserElement._literalStringClass = 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 :class:`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``. Example:: Keyword("start").parseString("start") # -> ['start'] Keyword("start").parseString("starting") # -> Exception For case-insensitive matching, use :class:`CaselessKeyword`. """ DEFAULT_KEYWORD_CHARS = alphanums + "_$" def __init__(self, matchString, identChars=None, caseless=False): super(Keyword, self).__init__() if identChars is None: identChars = Keyword.DEFAULT_KEYWORD_CHARS 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.mayIndexError = False self.caseless = caseless if caseless: self.caselessmatch = matchString.upper() identChars = identChars.upper() self.identChars = set(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: if ((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, self) def copy(self): c = super(Keyword, self).copy() c.identChars = Keyword.DEFAULT_KEYWORD_CHARS return c @staticmethod def setDefaultKeywordChars(chars): """Overrides the default Keyword chars """ Keyword.DEFAULT_KEYWORD_CHARS = chars 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. Example:: OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] (Contrast with example for :class:`CaselessKeyword`.) """ 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 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, self) class CaselessKeyword(Keyword): """ Caseless version of :class:`Keyword`. Example:: OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] (Contrast with example for :class:`CaselessLiteral`.) """ def __init__(self, matchString, identChars=None): super(CaselessKeyword, self).__init__(matchString, identChars, caseless=True) class CloseMatch(Token): """A variation on :class:`Literal` which matches "close" matches, that is, strings with at most 'n' mismatching characters. :class:`CloseMatch` takes parameters: - ``match_string`` - string to be matched - ``maxMismatches`` - (``default=1``) maximum number of mismatches allowed to count as a match The results from a successful parse will contain the matched text from the input string and the following named results: - ``mismatches`` - a list of the positions within the match_string where mismatches were found - ``original`` - the original match_string used to compare against the input string If ``mismatches`` is an empty list, then the match was an exact match. Example:: patt = CloseMatch("ATCATCGAATGGA") patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) # exact match patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) # close match allowing up to 2 mismatches patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) """ def __init__(self, match_string, maxMismatches=1): super(CloseMatch, self).__init__() self.name = match_string self.match_string = match_string self.maxMismatches = maxMismatches self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) self.mayIndexError = False self.mayReturnEmpty = False def parseImpl(self, instring, loc, doActions=True): start = loc instrlen = len(instring) maxloc = start + len(self.match_string) if maxloc <= instrlen: match_string = self.match_string match_stringloc = 0 mismatches = [] maxMismatches = self.maxMismatches for match_stringloc, s_m in enumerate(zip(instring[loc:maxloc], match_string)): src, mat = s_m if src != mat: mismatches.append(match_stringloc) if len(mismatches) > maxMismatches: break else: loc = match_stringloc + 1 results = ParseResults([instring[start:loc]]) results['original'] = match_string results['mismatches'] = mismatches return loc, results raise ParseException(instring, loc, self.errmsg, self) 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. An optional ``excludeChars`` parameter can list characters that might be found in the input ``bodyChars`` string; useful to define a word of all printables except for one or two characters, for instance. :class:`srange` is useful for defining custom character set strings for defining ``Word`` expressions, using range notation from regular expression character sets. A common mistake is to use :class:`Word` to match a specific literal string, as in ``Word("Address")``. Remember that :class:`Word` uses the string argument to define *sets* of matchable characters. This expression would match "Add", "AAA", "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. To match an exact literal string, use :class:`Literal` or :class:`Keyword`. pyparsing includes helper strings for building Words: - :class:`alphas` - :class:`nums` - :class:`alphanums` - :class:`hexnums` - :class:`alphas8bit` (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) - :class:`punc8bit` (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) - :class:`printables` (any non-whitespace character) Example:: # a word composed of digits integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) # a word with a leading capital, and zero or more lowercase capital_word = Word(alphas.upper(), alphas.lower()) # hostnames are alphanumeric, with leading alpha, and '-' hostname = Word(alphas, alphanums + '-') # roman numeral (not a strict parser, accepts invalid mix of characters) roman = Word("IVXLCDM") # any string of non-whitespace characters, except for ',' csv_value = Word(printables, excludeChars=",") """ def __init__(self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None): super(Word, self).__init__() if excludeChars: excludeChars = set(excludeChars) initChars = ''.join(c for c in initChars if c not in excludeChars) if bodyChars: bodyChars = ''.join(c for c in bodyChars if c not in excludeChars) self.initCharsOrig = initChars self.initChars = set(initChars) if bodyChars: self.bodyCharsOrig = bodyChars self.bodyChars = set(bodyChars) else: self.bodyCharsOrig = initChars self.bodyChars = set(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.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.initCharsOrig) == 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 Exception: self.re = None else: self.re_match = self.re.match self.__class__ = _WordRegex def parseImpl(self, instring, loc, doActions=True): if instring[loc] not in self.initChars: raise ParseException(instring, loc, self.errmsg, self) 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 elif self.maxSpecified and loc < instrlen and instring[loc] in bodychars: throwException = True elif self.asKeyword: if (start > 0 and instring[start - 1] in bodychars or loc < instrlen and instring[loc] in bodychars): throwException = True if throwException: raise ParseException(instring, loc, self.errmsg, self) return loc, instring[start:loc] def __str__(self): try: return super(Word, self).__str__() except Exception: pass if self.strRepr is None: def charsAsStr(s): if len(s) > 4: 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 _WordRegex(Word): def parseImpl(self, instring, loc, doActions=True): result = self.re_match(instring, loc) if not result: raise ParseException(instring, loc, self.errmsg, self) loc = result.end() return loc, result.group() class Char(_WordRegex): """A short-cut class for defining ``Word(characters, exact=1)``, when defining a match of any single character in a string of characters. """ def __init__(self, charset, asKeyword=False, excludeChars=None): super(Char, self).__init__(charset, exact=1, asKeyword=asKeyword, excludeChars=excludeChars) self.reString = "[%s]" % _escapeRegexRangeChars(''.join(self.initChars)) if asKeyword: self.reString = r"\b%s\b" % self.reString self.re = re.compile(self.reString) self.re_match = self.re.match class Regex(Token): r"""Token for matching strings that match a given regular expression. Defined with string specifying the regular expression in a form recognized by the stdlib Python `re module `_. If the given regex contains named groups (defined using ``(?P...)``), these will be preserved as named parse results. If instead of the Python stdlib re module you wish to use a different RE module (such as the `regex` module), you can replace it by either building your Regex object with a compiled RE that was compiled using regex: Example:: realnum = Regex(r"[+-]?\d+\.\d*") date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)') # ref: https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression roman = Regex(r"M{0,4}(CM|CD|D?{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") # use regex module instead of stdlib re module to construct a Regex using # a compiled regular expression import regex parser = pp.Regex(regex.compile(r'[0-9]')) """ def __init__(self, pattern, flags=0, asGroupList=False, asMatch=False): """The parameters ``pattern`` and ``flags`` are passed to the ``re.compile()`` function as-is. See the Python `re module `_ module for an explanation of the acceptable patterns and flags. """ super(Regex, self).__init__() if isinstance(pattern, basestring): if not pattern: 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 elif hasattr(pattern, 'pattern') and hasattr(pattern, 'match'): self.re = pattern self.pattern = self.reString = pattern.pattern self.flags = flags else: raise TypeError("Regex may only be constructed with a string or a compiled RE object") self.re_match = self.re.match self.name = _ustr(self) self.errmsg = "Expected " + self.name self.mayIndexError = False self.mayReturnEmpty = self.re_match("") is not None self.asGroupList = asGroupList self.asMatch = asMatch if self.asGroupList: self.parseImpl = self.parseImplAsGroupList if self.asMatch: self.parseImpl = self.parseImplAsMatch def parseImpl(self, instring, loc, doActions=True): result = self.re_match(instring, loc) if not result: raise ParseException(instring, loc, self.errmsg, self) loc = result.end() ret = ParseResults(result.group()) d = result.groupdict() if d: for k, v in d.items(): ret[k] = v return loc, ret def parseImplAsGroupList(self, instring, loc, doActions=True): result = self.re_match(instring, loc) if not result: raise ParseException(instring, loc, self.errmsg, self) loc = result.end() ret = result.groups() return loc, ret def parseImplAsMatch(self, instring, loc, doActions=True): result = self.re_match(instring, loc) if not result: raise ParseException(instring, loc, self.errmsg, self) loc = result.end() ret = result return loc, ret def __str__(self): try: return super(Regex, self).__str__() except Exception: pass if self.strRepr is None: self.strRepr = "Re:(%s)" % repr(self.pattern) return self.strRepr def sub(self, repl): r""" Return Regex with an attached parse action to transform the parsed result as if called using `re.sub(expr, repl, string) `_. Example:: make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2") print(make_html.transformString("h1:main title:")) # prints "

main title

" """ if self.asGroupList: warnings.warn("cannot use sub() with Regex(asGroupList=True)", SyntaxWarning, stacklevel=2) raise SyntaxError() if self.asMatch and callable(repl): warnings.warn("cannot use sub() with a callable with Regex(asMatch=True)", SyntaxWarning, stacklevel=2) raise SyntaxError() if self.asMatch: def pa(tokens): return tokens[0].expand(repl) else: def pa(tokens): return self.re.sub(repl, tokens[0]) return self.addParseAction(pa) class QuotedString(Token): r""" Token for matching strings that are delimited by quoting characters. 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) - convertWhitespaceEscapes - convert escaped whitespace (``'\t'``, ``'\n'``, etc.) to actual whitespace (default= ``True``) Example:: qs = QuotedString('"') print(qs.searchString('lsjdf "This is the quote" sldjf')) complex_qs = QuotedString('{{', endQuoteChar='}}') print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) sql_qs = QuotedString('"', escQuote='""') print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) prints:: [['This is the quote']] [['This is the "quote"']] [['This is the quote with "embedded" quotes']] """ def __init__(self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): super(QuotedString, self).__init__() # remove white space from quote chars - wont work anyway quoteChar = quoteChar.strip() if not quoteChar: warnings.warn("quoteChar cannot be the empty string", SyntaxWarning, stacklevel=2) raise SyntaxError() if endQuoteChar is None: endQuoteChar = quoteChar else: endQuoteChar = endQuoteChar.strip() if not endQuoteChar: 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 self.convertWhitespaceEscapes = convertWhitespaceEscapes 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 self.re_match = self.re.match 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.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: raise ParseException(instring, loc, self.errmsg, self) loc = result.end() ret = result.group() if self.unquoteResults: # strip off quotes ret = ret[self.quoteCharLen: -self.endQuoteCharLen] if isinstance(ret, basestring): # replace escaped whitespace if '\\' in ret and self.convertWhitespaceEscapes: ws_map = { r'\t': '\t', r'\n': '\n', r'\f': '\f', r'\r': '\r', } for wslit, wschar in ws_map.items(): ret = ret.replace(wslit, wschar) # replace escaped characters if self.escChar: ret = re.sub(self.escCharReplacePattern, r"\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 Exception: 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 (will include whitespace in matched characters if not listed in the provided exclusion set - see example). 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. Example:: # define a comma-separated-value as anything that is not a ',' csv_value = CharsNotIn(',') print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) prints:: ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] """ 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.mayIndexError = False def parseImpl(self, instring, loc, doActions=True): if instring[loc] in self.notChars: raise ParseException(instring, loc, self.errmsg, self) 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, self) return loc, instring[start:loc] def __str__(self): try: return super(CharsNotIn, self).__str__() except Exception: 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 :class:`Word` class. """ whiteStrs = { ' ' : '', '\t': '', '\n': '', '\r': '', '\f': '', u'\u00A0': '', u'\u1680': '', u'\u180E': '', u'\u2000': '', u'\u2001': '', u'\u2002': '', u'\u2003': '', u'\u2004': '', u'\u2005': '', u'\u2006': '', u'\u2007': '', u'\u2008': '', u'\u2009': '', u'\u200A': '', u'\u200B': '', u'\u202F': '', u'\u205F': '', u'\u3000': '', } 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.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 instring[loc] not in self.matchWhite: raise ParseException(instring, loc, self.errmsg, self) 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, self) 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): r"""Matches if current position is at the beginning of a line within the parse string Example:: test = '''\ AAA this line AAA and this line AAA but not this one B AAA and definitely not this one ''' for t in (LineStart() + 'AAA' + restOfLine).searchString(test): print(t) prints:: ['AAA', ' this line'] ['AAA', ' and this line'] """ def __init__(self): super(LineStart, self).__init__() self.errmsg = "Expected start of line" def parseImpl(self, instring, loc, doActions=True): if col(loc, instring) == 1: return loc, [] raise ParseException(instring, loc, self.errmsg, self) 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" def parseImpl(self, instring, loc, doActions=True): if loc < len(instring): if instring[loc] == "\n": return loc + 1, "\n" else: raise ParseException(instring, loc, self.errmsg, self) elif loc == len(instring): return loc + 1, [] else: raise ParseException(instring, loc, self.errmsg, self) class StringStart(_PositionToken): """Matches if current position is at the beginning of the parse string """ def __init__(self): super(StringStart, self).__init__() self.errmsg = "Expected start of text" def parseImpl(self, instring, loc, doActions=True): if loc != 0: # see if entire string up to here is just whitespace and ignoreables if loc != self.preParse(instring, 0): raise ParseException(instring, loc, self.errmsg, self) return loc, [] class StringEnd(_PositionToken): """Matches if current position is at the end of the parse string """ def __init__(self): super(StringEnd, self).__init__() self.errmsg = "Expected end of text" def parseImpl(self, instring, loc, doActions=True): if loc < len(instring): raise ParseException(instring, loc, self.errmsg, self) elif loc == len(instring): return loc + 1, [] elif loc > len(instring): return loc, [] else: raise ParseException(instring, loc, self.errmsg, self) 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 = set(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): raise ParseException(instring, loc, self.errmsg, self) 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 = set(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 < instrlen: if (instring[loc] in self.wordChars or instring[loc - 1] not in self.wordChars): raise ParseException(instring, loc, self.errmsg, self) return loc, [] class ParseExpression(ParserElement): """Abstract subclass of ParserElement, for combining and post-processing parsed tokens. """ def __init__(self, exprs, savelist=False): super(ParseExpression, self).__init__(savelist) if isinstance(exprs, _generatorType): exprs = list(exprs) if isinstance(exprs, basestring): self.exprs = [self._literalStringClass(exprs)] elif isinstance(exprs, ParserElement): self.exprs = [exprs] elif isinstance(exprs, Iterable): exprs = list(exprs) # if sequence of strings provided, wrap with Literal if any(isinstance(expr, basestring) for expr in exprs): exprs = (self._literalStringClass(e) if isinstance(e, basestring) else e for e in exprs) self.exprs = list(exprs) else: try: self.exprs = list(exprs) except TypeError: self.exprs = [exprs] self.callPreparse = False def append(self, other): self.exprs.append(other) self.strRepr = None return self def leaveWhitespace(self): """Extends ``leaveWhitespace`` defined in base class, and also invokes ``leaveWhitespace`` on all contained expressions.""" self.skipWhitespace = False self.exprs = [e.copy() for e in self.exprs] for e in self.exprs: e.leaveWhitespace() return self def ignore(self, other): if isinstance(other, Suppress): if other not in self.ignoreExprs: super(ParseExpression, self).ignore(other) for e in self.exprs: e.ignore(self.ignoreExprs[-1]) else: super(ParseExpression, self).ignore(other) for e in self.exprs: e.ignore(self.ignoreExprs[-1]) return self def __str__(self): try: return super(ParseExpression, self).__str__() except Exception: pass if self.strRepr is None: self.strRepr = "%s:(%s)" % (self.__class__.__name__, _ustr(self.exprs)) return self.strRepr def streamline(self): super(ParseExpression, self).streamline() for e in self.exprs: e.streamline() # collapse nested And's of the form And(And(And(a, b), c), d) to And(a, b, c, d) # but only if there are no parse actions or resultsNames on the nested And's # (likewise for Or's and MatchFirst's) if len(self.exprs) == 2: other = self.exprs[0] if (isinstance(other, self.__class__) and not other.parseAction and other.resultsName is None and not other.debug): self.exprs = other.exprs[:] + [self.exprs[1]] self.strRepr = None self.mayReturnEmpty |= other.mayReturnEmpty self.mayIndexError |= other.mayIndexError other = self.exprs[-1] if (isinstance(other, self.__class__) and not other.parseAction and other.resultsName is None and not other.debug): self.exprs = self.exprs[:-1] + other.exprs[:] self.strRepr = None self.mayReturnEmpty |= other.mayReturnEmpty self.mayIndexError |= other.mayIndexError self.errmsg = "Expected " + _ustr(self) return self def validate(self, validateTrace=None): tmp = (validateTrace if validateTrace is not None else [])[:] + [self] for e in self.exprs: e.validate(tmp) self.checkRecursion([]) def copy(self): ret = super(ParseExpression, self).copy() ret.exprs = [e.copy() for e in self.exprs] return ret def _setResultsName(self, name, listAllMatches=False): if __diag__.warn_ungrouped_named_tokens_in_collection: for e in self.exprs: if isinstance(e, ParserElement) and e.resultsName: warnings.warn("{0}: setting results name {1!r} on {2} expression " "collides with {3!r} on contained expression".format("warn_ungrouped_named_tokens_in_collection", name, type(self).__name__, e.resultsName), stacklevel=3) return super(ParseExpression, self)._setResultsName(name, listAllMatches) class And(ParseExpression): """ Requires all given :class:`ParseExpression` s to be found in the given order. Expressions may be separated by whitespace. May be constructed using the ``'+'`` operator. May also be constructed using the ``'-'`` operator, which will suppress backtracking. Example:: integer = Word(nums) name_expr = OneOrMore(Word(alphas)) expr = And([integer("id"), name_expr("name"), integer("age")]) # more easily written as: expr = integer("id") + name_expr("name") + integer("age") """ class _ErrorStop(Empty): def __init__(self, *args, **kwargs): super(And._ErrorStop, self).__init__(*args, **kwargs) self.name = '-' self.leaveWhitespace() def __init__(self, exprs, savelist=True): exprs = list(exprs) if exprs and Ellipsis in exprs: tmp = [] for i, expr in enumerate(exprs): if expr is Ellipsis: if i < len(exprs) - 1: skipto_arg = (Empty() + exprs[i + 1]).exprs[-1] tmp.append(SkipTo(skipto_arg)("_skipped*")) else: raise Exception("cannot construct And with sequence ending in ...") else: tmp.append(expr) exprs[:] = tmp super(And, self).__init__(exprs, savelist) self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) self.setWhitespaceChars(self.exprs[0].whiteChars) self.skipWhitespace = self.exprs[0].skipWhitespace self.callPreparse = True def streamline(self): # collapse any _PendingSkip's if self.exprs: if any(isinstance(e, ParseExpression) and e.exprs and isinstance(e.exprs[-1], _PendingSkip) for e in self.exprs[:-1]): for i, e in enumerate(self.exprs[:-1]): if e is None: continue if (isinstance(e, ParseExpression) and e.exprs and isinstance(e.exprs[-1], _PendingSkip)): e.exprs[-1] = e.exprs[-1] + self.exprs[i + 1] self.exprs[i + 1] = None self.exprs = [e for e in self.exprs if e is not None] super(And, self).streamline() self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) return self def parseImpl(self, instring, loc, doActions=True): # pass False as last arg to _parse for first element, since we already # pre-parsed the string as part of our And pre-parsing loc, resultlist = self.exprs[0]._parse(instring, loc, doActions, callPreParse=False) errorStop = False for e in self.exprs[1:]: if isinstance(e, And._ErrorStop): errorStop = True continue if errorStop: try: loc, exprtokens = e._parse(instring, loc, doActions) except ParseSyntaxException: raise except ParseBaseException as pe: pe.__traceback__ = None raise ParseSyntaxException._from_exception(pe) except IndexError: raise ParseSyntaxException(instring, len(instring), self.errmsg, self) else: loc, exprtokens = e._parse(instring, loc, doActions) if exprtokens or exprtokens.haskeys(): resultlist += exprtokens return loc, resultlist def __iadd__(self, other): if isinstance(other, basestring): other = self._literalStringClass(other) return self.append(other) # And([self, other]) def checkRecursion(self, parseElementList): subRecCheckList = parseElementList[:] + [self] for e in self.exprs: e.checkRecursion(subRecCheckList) if not e.mayReturnEmpty: break 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 class Or(ParseExpression): """Requires that at least one :class:`ParseExpression` is found. If two expressions match, the expression that matches the longest string will be used. May be constructed using the ``'^'`` operator. Example:: # construct Or using '^' operator number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) print(number.searchString("123 3.1416 789")) prints:: [['123'], ['3.1416'], ['789']] """ def __init__(self, exprs, savelist=False): super(Or, self).__init__(exprs, savelist) if self.exprs: self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) else: self.mayReturnEmpty = True def streamline(self): super(Or, self).streamline() if __compat__.collect_all_And_tokens: self.saveAsList = any(e.saveAsList for e in self.exprs) return self def parseImpl(self, instring, loc, doActions=True): maxExcLoc = -1 maxException = None matches = [] for e in self.exprs: try: loc2 = e.tryParse(instring, loc) except ParseException as err: err.__traceback__ = None 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) else: # save match among all matches, to retry longest to shortest matches.append((loc2, e)) if matches: # re-evaluate all matches in descending order of length of match, in case attached actions # might change whether or how much they match of the input. matches.sort(key=itemgetter(0), reverse=True) if not doActions: # no further conditions or parse actions to change the selection of # alternative, so the first match will be the best match best_expr = matches[0][1] return best_expr._parse(instring, loc, doActions) longest = -1, None for loc1, expr1 in matches: if loc1 <= longest[0]: # already have a longer match than this one will deliver, we are done return longest try: loc2, toks = expr1._parse(instring, loc, doActions) except ParseException as err: err.__traceback__ = None if err.loc > maxExcLoc: maxException = err maxExcLoc = err.loc else: if loc2 >= loc1: return loc2, toks # didn't match as much as before elif loc2 > longest[0]: longest = loc2, toks if longest != (-1, None): return longest if maxException is not None: maxException.msg = self.errmsg raise maxException else: raise ParseException(instring, loc, "no defined alternatives to match", self) def __ixor__(self, other): if isinstance(other, basestring): other = self._literalStringClass(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) def _setResultsName(self, name, listAllMatches=False): if (not __compat__.collect_all_And_tokens and __diag__.warn_multiple_tokens_in_named_alternation): if any(isinstance(e, And) for e in self.exprs): warnings.warn("{0}: setting results name {1!r} on {2} expression " "may only return a single token for an And alternative, " "in future will return the full list of tokens".format( "warn_multiple_tokens_in_named_alternation", name, type(self).__name__), stacklevel=3) return super(Or, self)._setResultsName(name, listAllMatches) class MatchFirst(ParseExpression): """Requires that at least one :class:`ParseExpression` is found. If two expressions match, the first one listed is the one that will match. May be constructed using the ``'|'`` operator. Example:: # construct MatchFirst using '|' operator # watch the order of expressions to match number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] # put more selective expression first number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] """ def __init__(self, exprs, savelist=False): super(MatchFirst, self).__init__(exprs, savelist) if self.exprs: self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) else: self.mayReturnEmpty = True def streamline(self): super(MatchFirst, self).streamline() if __compat__.collect_all_And_tokens: self.saveAsList = any(e.saveAsList for e in self.exprs) return self 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: maxException.msg = self.errmsg raise maxException else: raise ParseException(instring, loc, "no defined alternatives to match", self) def __ior__(self, other): if isinstance(other, basestring): other = self._literalStringClass(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) def _setResultsName(self, name, listAllMatches=False): if (not __compat__.collect_all_And_tokens and __diag__.warn_multiple_tokens_in_named_alternation): if any(isinstance(e, And) for e in self.exprs): warnings.warn("{0}: setting results name {1!r} on {2} expression " "may only return a single token for an And alternative, " "in future will return the full list of tokens".format( "warn_multiple_tokens_in_named_alternation", name, type(self).__name__), stacklevel=3) return super(MatchFirst, self)._setResultsName(name, listAllMatches) class Each(ParseExpression): """Requires all given :class:`ParseExpression` s to be found, but in any order. Expressions may be separated by whitespace. May be constructed using the ``'&'`` operator. Example:: color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") integer = Word(nums) shape_attr = "shape:" + shape_type("shape") posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") color_attr = "color:" + color("color") size_attr = "size:" + integer("size") # use Each (using operator '&') to accept attributes in any order # (shape and posn are required, color and size are optional) shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) shape_spec.runTests(''' shape: SQUARE color: BLACK posn: 100, 120 shape: CIRCLE size: 50 color: BLUE posn: 50,80 color:GREEN size:20 shape:TRIANGLE posn:20,40 ''' ) prints:: shape: SQUARE color: BLACK posn: 100, 120 ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] - color: BLACK - posn: ['100', ',', '120'] - x: 100 - y: 120 - shape: SQUARE shape: CIRCLE size: 50 color: BLUE posn: 50,80 ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] - color: BLUE - posn: ['50', ',', '80'] - x: 50 - y: 80 - shape: CIRCLE - size: 50 color: GREEN size: 20 shape: TRIANGLE posn: 20,40 ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] - color: GREEN - posn: ['20', ',', '40'] - x: 20 - y: 40 - shape: TRIANGLE - size: 20 """ def __init__(self, exprs, savelist=True): super(Each, self).__init__(exprs, savelist) self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) self.skipWhitespace = True self.initExprGroups = True self.saveAsList = True def streamline(self): super(Each, self).streamline() self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) return self def parseImpl(self, instring, loc, doActions=True): if self.initExprGroups: self.opt1map = dict((id(e.expr), e) for e in self.exprs if isinstance(e, Optional)) opt1 = [e.expr for e in self.exprs if isinstance(e, Optional)] opt2 = [e for e in self.exprs if e.mayReturnEmpty and not isinstance(e, (Optional, Regex))] self.optionals = opt1 + opt2 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(self.opt1map.get(id(e), 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 += [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 = sum(resultlist, ParseResults([])) 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 :class:`ParserElement`, for combining and post-processing parsed tokens. """ def __init__(self, expr, savelist=False): super(ParseElementEnhance, self).__init__(savelist) if isinstance(expr, basestring): if issubclass(self._literalStringClass, Token): expr = self._literalStringClass(expr) else: expr = self._literalStringClass(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=None): if validateTrace is None: 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 Exception: 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. If any results names are defined in the lookahead expression, those *will* be returned for access by name. Example:: # use FollowedBy to match a label only if it is followed by a ':' data_word = Word(alphas) label = data_word + FollowedBy(':') attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() prints:: [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] """ def __init__(self, expr): super(FollowedBy, self).__init__(expr) self.mayReturnEmpty = True def parseImpl(self, instring, loc, doActions=True): # by using self._expr.parse and deleting the contents of the returned ParseResults list # we keep any named results that were defined in the FollowedBy expression _, ret = self.expr._parse(instring, loc, doActions=doActions) del ret[:] return loc, ret class PrecededBy(ParseElementEnhance): """Lookbehind matching of the given parse expression. ``PrecededBy`` does not advance the parsing position within the input string, it only verifies that the specified parse expression matches prior to the current position. ``PrecededBy`` always returns a null token list, but if a results name is defined on the given expression, it is returned. Parameters: - expr - expression that must match prior to the current parse location - retreat - (default= ``None``) - (int) maximum number of characters to lookbehind prior to the current parse location If the lookbehind expression is a string, Literal, Keyword, or a Word or CharsNotIn with a specified exact or maximum length, then the retreat parameter is not required. Otherwise, retreat must be specified to give a maximum number of characters to look back from the current parse position for a lookbehind match. Example:: # VB-style variable names with type prefixes int_var = PrecededBy("#") + pyparsing_common.identifier str_var = PrecededBy("$") + pyparsing_common.identifier """ def __init__(self, expr, retreat=None): super(PrecededBy, self).__init__(expr) self.expr = self.expr().leaveWhitespace() self.mayReturnEmpty = True self.mayIndexError = False self.exact = False if isinstance(expr, str): retreat = len(expr) self.exact = True elif isinstance(expr, (Literal, Keyword)): retreat = expr.matchLen self.exact = True elif isinstance(expr, (Word, CharsNotIn)) and expr.maxLen != _MAX_INT: retreat = expr.maxLen self.exact = True elif isinstance(expr, _PositionToken): retreat = 0 self.exact = True self.retreat = retreat self.errmsg = "not preceded by " + str(expr) self.skipWhitespace = False self.parseAction.append(lambda s, l, t: t.__delitem__(slice(None, None))) def parseImpl(self, instring, loc=0, doActions=True): if self.exact: if loc < self.retreat: raise ParseException(instring, loc, self.errmsg) start = loc - self.retreat _, ret = self.expr._parse(instring, start) else: # retreat specified a maximum lookbehind window, iterate test_expr = self.expr + StringEnd() instring_slice = instring[max(0, loc - self.retreat):loc] last_expr = ParseException(instring, loc, self.errmsg) for offset in range(1, min(loc, self.retreat + 1)+1): try: # print('trying', offset, instring_slice, repr(instring_slice[loc - offset:])) _, ret = test_expr._parse(instring_slice, len(instring_slice) - offset) except ParseBaseException as pbe: last_expr = pbe else: break else: raise last_expr return loc, ret 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. Example:: AND, OR, NOT = map(CaselessKeyword, "AND OR NOT".split()) # take care not to mistake keywords for identifiers ident = ~(AND | OR | NOT) + Word(alphas) boolean_term = Optional(NOT) + ident # very crude boolean expression - to support parenthesis groups and # operation hierarchy, use infixNotation boolean_expr = boolean_term + ZeroOrMore((AND | OR) + boolean_term) # integers that are followed by "." are actually floats integer = Word(nums) + ~Char(".") """ 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) def parseImpl(self, instring, loc, doActions=True): if self.expr.canParseNext(instring, loc): raise ParseException(instring, loc, self.errmsg, self) 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 _MultipleMatch(ParseElementEnhance): def __init__(self, expr, stopOn=None): super(_MultipleMatch, self).__init__(expr) self.saveAsList = True ender = stopOn if isinstance(ender, basestring): ender = self._literalStringClass(ender) self.stopOn(ender) def stopOn(self, ender): if isinstance(ender, basestring): ender = self._literalStringClass(ender) self.not_ender = ~ender if ender is not None else None return self def parseImpl(self, instring, loc, doActions=True): self_expr_parse = self.expr._parse self_skip_ignorables = self._skipIgnorables check_ender = self.not_ender is not None if check_ender: try_not_ender = self.not_ender.tryParse # must be at least one (but first see if we are the stopOn sentinel; # if so, fail) if check_ender: try_not_ender(instring, loc) loc, tokens = self_expr_parse(instring, loc, doActions, callPreParse=False) try: hasIgnoreExprs = (not not self.ignoreExprs) while 1: if check_ender: try_not_ender(instring, loc) if hasIgnoreExprs: preloc = self_skip_ignorables(instring, loc) else: preloc = loc loc, tmptokens = self_expr_parse(instring, preloc, doActions) if tmptokens or tmptokens.haskeys(): tokens += tmptokens except (ParseException, IndexError): pass return loc, tokens def _setResultsName(self, name, listAllMatches=False): if __diag__.warn_ungrouped_named_tokens_in_collection: for e in [self.expr] + getattr(self.expr, 'exprs', []): if isinstance(e, ParserElement) and e.resultsName: warnings.warn("{0}: setting results name {1!r} on {2} expression " "collides with {3!r} on contained expression".format("warn_ungrouped_named_tokens_in_collection", name, type(self).__name__, e.resultsName), stacklevel=3) return super(_MultipleMatch, self)._setResultsName(name, listAllMatches) class OneOrMore(_MultipleMatch): """Repetition of one or more of the given expression. Parameters: - expr - expression that must match one or more times - stopOn - (default= ``None``) - expression for a terminating sentinel (only required if the sentinel would ordinarily match the repetition expression) Example:: data_word = Word(alphas) label = data_word + FollowedBy(':') attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) text = "shape: SQUARE posn: upper left color: BLACK" OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] # use stopOn attribute for OneOrMore to avoid reading label string as part of the data attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] # could also be written as (attr_expr * (1,)).parseString(text).pprint() """ 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(_MultipleMatch): """Optional repetition of zero or more of the given expression. Parameters: - expr - expression that must match zero or more times - stopOn - (default= ``None``) - expression for a terminating sentinel (only required if the sentinel would ordinarily match the repetition expression) Example: similar to :class:`OneOrMore` """ def __init__(self, expr, stopOn=None): super(ZeroOrMore, self).__init__(expr, stopOn=stopOn) self.mayReturnEmpty = True def parseImpl(self, instring, loc, doActions=True): try: return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) except (ParseException, IndexError): 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 _NullToken(object): def __bool__(self): return False __nonzero__ = __bool__ def __str__(self): return "" class Optional(ParseElementEnhance): """Optional matching of the given expression. Parameters: - expr - expression that must match zero or more times - default (optional) - value to be returned if the optional expression is not found. Example:: # US postal code can be a 5-digit zip, plus optional 4-digit qualifier zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) zip.runTests(''' # traditional ZIP code 12345 # ZIP+4 form 12101-0001 # invalid ZIP 98765- ''') prints:: # traditional ZIP code 12345 ['12345'] # ZIP+4 form 12101-0001 ['12101-0001'] # invalid ZIP 98765- ^ FAIL: Expected end of text (at char 5), (line:1, col:6) """ __optionalNotMatched = _NullToken() def __init__(self, expr, default=__optionalNotMatched): super(Optional, self).__init__(expr, savelist=False) self.saveAsList = self.expr.saveAsList 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 self.__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. Parameters: - expr - target expression marking the end of the data to be skipped - include - (default= ``False``) if True, the target expression is also parsed (the skipped text and target expression are returned as a 2-element list). - ignore - (default= ``None``) used to define grammars (typically quoted strings and comments) that might contain false matches to the target expression - failOn - (default= ``None``) define expressions that are not allowed to be included in the skipped test; if found before the target expression is found, the SkipTo is not a match Example:: report = ''' Outstanding Issues Report - 1 Jan 2000 # | Severity | Description | Days Open -----+----------+-------------------------------------------+----------- 101 | Critical | Intermittent system crash | 6 94 | Cosmetic | Spelling error on Login ('log|n') | 14 79 | Minor | System slow when running too many reports | 47 ''' integer = Word(nums) SEP = Suppress('|') # use SkipTo to simply match everything up until the next SEP # - ignore quoted strings, so that a '|' character inside a quoted string does not match # - parse action will call token.strip() for each matched token, i.e., the description body string_data = SkipTo(SEP, ignore=quotedString) string_data.setParseAction(tokenMap(str.strip)) ticket_expr = (integer("issue_num") + SEP + string_data("sev") + SEP + string_data("desc") + SEP + integer("days_open")) for tkt in ticket_expr.searchString(report): print tkt.dump() prints:: ['101', 'Critical', 'Intermittent system crash', '6'] - days_open: 6 - desc: Intermittent system crash - issue_num: 101 - sev: Critical ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] - days_open: 14 - desc: Spelling error on Login ('log|n') - issue_num: 94 - sev: Cosmetic ['79', 'Minor', 'System slow when running too many reports', '47'] - days_open: 47 - desc: System slow when running too many reports - issue_num: 79 - sev: Minor """ 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.saveAsList = False if isinstance(failOn, basestring): self.failOn = self._literalStringClass(failOn) else: self.failOn = failOn self.errmsg = "No match found for " + _ustr(self.expr) def parseImpl(self, instring, loc, doActions=True): startloc = loc instrlen = len(instring) expr = self.expr expr_parse = self.expr._parse self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None tmploc = loc while tmploc <= instrlen: if self_failOn_canParseNext is not None: # break if failOn expression matches if self_failOn_canParseNext(instring, tmploc): break if self_ignoreExpr_tryParse is not None: # advance past ignore expressions while 1: try: tmploc = self_ignoreExpr_tryParse(instring, tmploc) except ParseBaseException: break try: expr_parse(instring, tmploc, doActions=False, callPreParse=False) except (ParseException, IndexError): # no match, advance loc in string tmploc += 1 else: # matched skipto expr, done break else: # ran off the end of the input string without matching skipto expr, fail raise ParseException(instring, loc, self.errmsg, self) # build up return values loc = tmploc skiptext = instring[startloc:loc] skipresult = ParseResults(skiptext) if self.includeMatch: loc, mat = expr_parse(instring, loc, doActions, callPreParse=False) skipresult += mat return loc, skipresult 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) Converting to use the '<<=' operator instead will avoid this problem. See :class:`ParseResults.pprint` for an example of a recursive parser created using ``Forward``. """ def __init__(self, other=None): super(Forward, self).__init__(other, savelist=False) def __lshift__(self, other): if isinstance(other, basestring): other = self._literalStringClass(other) self.expr = other 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 self def __ilshift__(self, other): return self << other 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=None): if validateTrace is None: 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 if self.strRepr is not None: return self.strRepr # Avoid infinite recursion by setting a temporary strRepr self.strRepr = ": ..." # Use the string representation of main expression. retString = '...' try: if self.expr is not None: retString = _ustr(self.expr)[:1000] else: retString = "None" finally: self.strRepr = self.__class__.__name__ + ": " + retString return self.strRepr def copy(self): if self.expr is not None: return super(Forward, self).copy() else: ret = Forward() ret <<= self return ret def _setResultsName(self, name, listAllMatches=False): if __diag__.warn_name_set_on_empty_Forward: if self.expr is None: warnings.warn("{0}: setting results name {0!r} on {1} expression " "that has no contained expression".format("warn_name_set_on_empty_Forward", name, type(self).__name__), stacklevel=3) return super(Forward, self)._setResultsName(name, listAllMatches) class TokenConverter(ParseElementEnhance): """ Abstract subclass of :class:`ParseExpression`, for converting parsed results. """ def __init__(self, expr, savelist=False): super(TokenConverter, self).__init__(expr) # , savelist) self.saveAsList = False 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. Example:: real = Word(nums) + '.' + Word(nums) print(real.parseString('3.1416')) # -> ['3', '.', '1416'] # will also erroneously match the following print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] real = Combine(Word(nums) + '.' + Word(nums)) print(real.parseString('3.1416')) # -> ['3.1416'] # no match when there are internal spaces print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) """ 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 self.callPreparse = True 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 retToks.haskeys(): return [retToks] else: return retToks class Group(TokenConverter): """Converter to return the matched tokens as a list - useful for returning tokens of :class:`ZeroOrMore` and :class:`OneOrMore` expressions. Example:: ident = Word(alphas) num = Word(nums) term = ident | num func = ident + Optional(delimitedList(term)) print(func.parseString("fn a, b, 100")) # -> ['fn', 'a', 'b', '100'] func = ident + Group(Optional(delimitedList(term))) print(func.parseString("fn a, b, 100")) # -> ['fn', ['a', 'b', '100']] """ 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. Example:: data_word = Word(alphas) label = data_word + FollowedBy(':') attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) text = "shape: SQUARE posn: upper left color: light blue texture: burlap" attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) # print attributes as plain groups print(OneOrMore(attr_expr).parseString(text).dump()) # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names result = Dict(OneOrMore(Group(attr_expr))).parseString(text) print(result.dump()) # access named fields as dict entries, or output as dict print(result['shape']) print(result.asDict()) prints:: ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - color: light blue - posn: upper left - shape: SQUARE - texture: burlap SQUARE {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} See more examples at :class:`ParseResults` of accessing fields by results name. """ def __init__(self, expr): super(Dict, self).__init__(expr) 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.haskeys()): 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. Example:: source = "a, b, c,d" wd = Word(alphas) wd_list1 = wd + ZeroOrMore(',' + wd) print(wd_list1.parseString(source)) # often, delimiters that are useful during parsing are just in the # way afterward - use Suppress to keep them out of the parsed output wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) print(wd_list2.parseString(source)) prints:: ['a', ',', 'b', ',', 'c', ',', 'd'] ['a', 'b', 'c', 'd'] (See also :class:`delimitedList`.) """ 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 = _trim_arity(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. When the parse action is called, this decorator will print ``">> entering method-name(line:, , )"``. When the parse action completes, the decorator will print ``"<<"`` followed by the returned value, or any exception that the parse action raised. Example:: wd = Word(alphas) @traceParseAction def remove_duplicate_chars(tokens): return ''.join(sorted(set(''.join(tokens)))) wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) print(wds.parseString("slkdjs sld sldd sdlf sdljf")) prints:: >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) < 3: thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc sys.stderr.write(">>entering %s(line: '%s', %d, %r)\n" % (thisFunc, line(l, s), l, t)) try: ret = f(*paArgs) except Exception as exc: sys.stderr.write("< ['aa', 'bb', 'cc'] delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] """ dlName = _ustr(expr) + " [" + _ustr(delim) + " " + _ustr(expr) + "]..." if combine: return Combine(expr + ZeroOrMore(delim + expr)).setName(dlName) else: return (expr + ZeroOrMore(Suppress(delim) + expr)).setName(dlName) def countedArray(expr, intExpr=None): """Helper to define a counted list of expressions. This helper defines a pattern of the form:: integer expr expr expr... where the leading integer tells how many expr expressions follow. The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. If ``intExpr`` is specified, it should be a pyparsing expression that produces an integer value. Example:: countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] # in this parser, the leading integer value is given in binary, # '10' indicating that 2 values are in the array binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] """ arrayExpr = Forward() def countFieldParseAction(s, l, t): n = t[0] arrayExpr << (n and Group(And([expr] * n)) or Group(empty)) return [] if intExpr is None: intExpr = Word(nums).setParseAction(lambda t: int(t[0])) else: intExpr = intExpr.copy() intExpr.setName("arrayLen") intExpr.addParseAction(countFieldParseAction, callDuringTry=True) return (intExpr + arrayExpr).setName('(len) ' + _ustr(expr) + '...') def _flatten(L): ret = [] for i in L: if isinstance(i, list): ret.extend(_flatten(i)) else: ret.append(i) return ret def matchPreviousLiteral(expr): """Helper to define an expression that is indirectly defined from the tokens matched in a previous expression, that is, it looks for a 'repeat' of a previous expression. For example:: first = Word(nums) second = matchPreviousLiteral(first) matchExpr = first + ":" + second will match ``"1:1"``, but not ``"1:2"``. Because this matches a previous literal, will also match the leading ``"1:1"`` in ``"1:10"``. If this is not desired, use :class:`matchPreviousExpr`. Do *not* use with packrat parsing enabled. """ rep = Forward() def copyTokenToRepeater(s, l, t): if t: if len(t) == 1: rep << t[0] else: # flatten t tokens tflat = _flatten(t.asList()) rep << And(Literal(tt) for tt in tflat) else: rep << Empty() expr.addParseAction(copyTokenToRepeater, callDuringTry=True) rep.setName('(prev) ' + _ustr(expr)) return rep def matchPreviousExpr(expr): """Helper to define an expression that is indirectly defined from the tokens matched in a previous expression, that is, it looks for a 'repeat' of a previous expression. For example:: first = Word(nums) second = matchPreviousExpr(first) matchExpr = first + ":" + second will match ``"1:1"``, but not ``"1:2"``. Because this matches by expressions, will *not* match the leading ``"1:1"`` in ``"1:10"``; the expressions are evaluated first, and then compared, so ``"1"`` is compared with ``"10"``. Do *not* use with packrat parsing enabled. """ rep = Forward() e2 = expr.copy() rep <<= e2 def copyTokenToRepeater(s, l, t): matchTokens = _flatten(t.asList()) def mustMatchTheseTokens(s, l, t): theseTokens = _flatten(t.asList()) if theseTokens != matchTokens: raise ParseException('', 0, '') rep.setParseAction(mustMatchTheseTokens, callDuringTry=True) expr.addParseAction(copyTokenToRepeater, callDuringTry=True) rep.setName('(prev) ' + _ustr(expr)) return rep def _escapeRegexRangeChars(s): # ~ escape these chars: ^-[] for c in r"\^-[]": s = s.replace(c, _bslash + c) s = s.replace("\n", r"\n") s = s.replace("\t", r"\t") return _ustr(s) def oneOf(strs, caseless=False, useRegex=True, asKeyword=False): """Helper to quickly define a set of alternative Literals, and makes sure to do longest-first testing when there is a conflict, regardless of the input order, but returns a :class:`MatchFirst` for best performance. Parameters: - strs - a string of space-delimited literals, or a collection of string literals - caseless - (default= ``False``) - treat all literals as caseless - useRegex - (default= ``True``) - as an optimization, will generate a Regex object; otherwise, will generate a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if creating a :class:`Regex` raises an exception) - asKeyword - (default=``False``) - enforce Keyword-style matching on the generated expressions Example:: comp_oper = oneOf("< = > <= >= !=") var = Word(alphas) number = Word(nums) term = var | number comparison_expr = term + comp_oper + term print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) prints:: [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] """ if isinstance(caseless, basestring): warnings.warn("More than one string argument passed to oneOf, pass " "choices as a list or space-delimited string", stacklevel=2) if caseless: isequal = (lambda a, b: a.upper() == b.upper()) masks = (lambda a, b: b.upper().startswith(a.upper())) parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral else: isequal = (lambda a, b: a == b) masks = (lambda a, b: b.startswith(a)) parseElementClass = Keyword if asKeyword else Literal symbols = [] if isinstance(strs, basestring): symbols = strs.split() elif isinstance(strs, Iterable): symbols = list(strs) else: warnings.warn("Invalid argument to oneOf, expected string or iterable", SyntaxWarning, stacklevel=2) if not symbols: return NoMatch() if not asKeyword: # if not producing keywords, need to reorder to take care to avoid masking # longer choices with shorter ones i = 0 while i < len(symbols) - 1: cur = symbols[i] for j, other in enumerate(symbols[i + 1:]): if isequal(other, cur): del symbols[i + j + 1] break elif masks(cur, other): del symbols[i + j + 1] symbols.insert(i, other) break else: i += 1 if not (caseless or asKeyword) and useRegex: # ~ print (strs, "->", "|".join([_escapeRegexChars(sym) for sym in symbols])) try: if len(symbols) == len("".join(symbols)): return Regex("[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols)).setName(' | '.join(symbols)) else: return Regex("|".join(re.escape(sym) for sym in symbols)).setName(' | '.join(symbols)) except Exception: 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).setName(' | '.join(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 :class:`Dict`, :class:`ZeroOrMore`, and :class:`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 :class:`Dict` results can include named token fields. Example:: text = "shape: SQUARE posn: upper left color: light blue texture: burlap" attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) print(OneOrMore(attr_expr).parseString(text).dump()) attr_label = label attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) # similar to Dict, but simpler call format result = dictOf(attr_label, attr_value).parseString(text) print(result.dump()) print(result['shape']) print(result.shape) # object attribute access works too print(result.asDict()) prints:: [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - color: light blue - posn: upper left - shape: SQUARE - texture: burlap SQUARE SQUARE {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} """ return Dict(OneOrMore(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. By default, returns astring containing the original parsed text. If the optional ``asString`` argument is passed as ``False``, then the return value is a :class:`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 :class:`originalTextFor` contains expressions with defined results names, you must set ``asString`` to ``False`` if you want to preserve those results name values. Example:: src = "this is test bold text normal text " for tag in ("b", "i"): opener, closer = makeHTMLTags(tag) patt = originalTextFor(opener + SkipTo(closer) + closer) print(patt.searchString(src)[0]) prints:: [' bold text '] ['text'] """ locMarker = Empty().setParseAction(lambda s, loc, t: loc) endlocMarker = locMarker.copy() endlocMarker.callPreparse = False matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") if asString: extractText = lambda s, l, t: s[t._original_start: t._original_end] else: def extractText(s, l, t): t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] matchExpr.setParseAction(extractText) matchExpr.ignoreExprs = expr.ignoreExprs return matchExpr def ungroup(expr): """Helper to undo pyparsing's default grouping of And expressions, even if all but one are non-empty. """ return TokenConverter(expr).addParseAction(lambda t: t[0]) def locatedExpr(expr): """Helper to decorate a returned token with its starting and ending locations in the input string. This helper adds the following results names: - locn_start = location where matched expression begins - locn_end = location where matched expression ends - value = the actual parsed results Be careful if the input text contains ```` characters, you may want to call :class:`ParserElement.parseWithTabs` Example:: wd = Word(alphas) for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): print(match) prints:: [[0, 'ljsdf', 5]] [[8, 'lksdjjf', 15]] [[18, 'lkkjj', 23]] """ locator = Empty().setParseAction(lambda s, l, t: l) return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) # 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]) _escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s, l, t: unichr(int(t[0].lstrip(r'\0x'), 16))) _escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s, l, t: unichr(int(t[0][1:], 8))) _singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1) _charRange = Group(_singleChar + Suppress("-") + _singleChar) _reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group(OneOrMore(_charRange | _singleChar)).setResultsName("body") + "]" 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 ``'\x'`` (``\x21``, which is a ``'!'`` character) (``\0x##`` is also supported for backwards compatibility) - 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.) """ _expanded = lambda p: p if not isinstance(p, ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]), ord(p[1]) + 1)) try: return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) except Exception: 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 :class:`transformString` (). Example:: num = Word(nums).setParseAction(lambda toks: int(toks[0])) na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) term = na | num OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] """ return lambda s, l, t: [replStr] def removeQuotes(s, l, t): """Helper parse action for removing quotation marks from parsed quoted strings. Example:: # by default, quotation marks are included in parsed results quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] # use removeQuotes to strip quotation marks from parsed results quotedString.setParseAction(removeQuotes) quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] """ return t[0][1:-1] def tokenMap(func, *args): """Helper to define a parse action by mapping a function to all elements of a ParseResults list. If any additional args are passed, they are forwarded to the given function as additional arguments after the token, as in ``hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))``, which will convert the parsed data to an integer using base 16. Example (compare the last to example in :class:`ParserElement.transformString`:: hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) hex_ints.runTests(''' 00 11 22 aa FF 0a 0d 1a ''') upperword = Word(alphas).setParseAction(tokenMap(str.upper)) OneOrMore(upperword).runTests(''' my kingdom for a horse ''') wd = Word(alphas).setParseAction(tokenMap(str.title)) OneOrMore(wd).setParseAction(' '.join).runTests(''' now is the winter of our discontent made glorious summer by this sun of york ''') prints:: 00 11 22 aa FF 0a 0d 1a [0, 17, 34, 170, 255, 10, 13, 26] my kingdom for a horse ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] now is the winter of our discontent made glorious summer by this sun of york ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] """ def pa(s, l, t): return [func(tokn, *args) for tokn in t] try: func_name = getattr(func, '__name__', getattr(func, '__class__').__name__) except Exception: func_name = str(func) pa.__name__ = func_name return pa upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) """(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of :class:`pyparsing_common.upcaseTokens`""" downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) """(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of :class:`pyparsing_common.downcaseTokens`""" def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")): """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_LT + tagStr("tag") + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) + Optional("/", default=[False])("empty").setParseAction(lambda s, l, t: t[0] == '/') + suppress_GT) else: tagAttrValue = quotedString.copy().setParseAction(removeQuotes) | Word(printables, excludeChars=">") openTag = (suppress_LT + tagStr("tag") + Dict(ZeroOrMore(Group(tagAttrName.setParseAction(downcaseTokens) + Optional(Suppress("=") + tagAttrValue)))) + Optional("/", default=[False])("empty").setParseAction(lambda s, l, t: t[0] == '/') + suppress_GT) closeTag = Combine(_L("", adjacent=False) openTag.setName("<%s>" % resname) # add start results name in parse action now that ungrouped names are not reported at two levels openTag.addParseAction(lambda t: t.__setitem__("start" + "".join(resname.replace(":", " ").title().split()), t.copy())) closeTag = closeTag("end" + "".join(resname.replace(":", " ").title().split())).setName("" % resname) openTag.tag = resname closeTag.tag = resname openTag.tag_body = SkipTo(closeTag()) return openTag, closeTag def makeHTMLTags(tagStr): """Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. Example:: text = 'More info at the pyparsing wiki page' # makeHTMLTags returns pyparsing expressions for the opening and # closing tags as a 2-tuple a, a_end = makeHTMLTags("A") link_expr = a + SkipTo(a_end)("link_text") + a_end for link in link_expr.searchString(text): # attributes in the tag (like "href" shown here) are # also accessible as named results print(link.link_text, '->', link.href) prints:: pyparsing -> https://github.com/pyparsing/pyparsing/wiki """ return _makeTags(tagStr, False) def makeXMLTags(tagStr): """Helper to construct opening and closing tag expressions for XML, given a tag name. Matches tags only in the given upper/lower case. Example: similar to :class:`makeHTMLTags` """ return _makeTags(tagStr, True) def withAttribute(*args, **attrDict): """Helper to create a validating parse action to be used with start tags created with :class:`makeXMLTags` or :class:`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 ``(align="right")``, or - as an explicit dict with ``**`` operator, when an attribute name is also a Python reserved word, as in ``**{"class":"Customer", "align":"right"}`` - 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. If just testing for ``class`` (with or without a namespace), use :class:`withClass`. To verify that the attribute exists, but without specifying a value, pass ``withAttribute.ANY_VALUE`` as the value. Example:: html = '''
Some text
1 4 0 1 0
1,3 2,3 1,1
this has no type
''' div,div_end = makeHTMLTags("div") # only match div tag having a type attribute with value "grid" div_grid = div().setParseAction(withAttribute(type="grid")) grid_expr = div_grid + SkipTo(div | div_end)("body") for grid_header in grid_expr.searchString(html): print(grid_header.body) # construct a match with any div tag having a type attribute, regardless of the value div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) div_expr = div_any_type + SkipTo(div | div_end)("body") for div_header in div_expr.searchString(html): print(div_header.body) prints:: 1 4 0 1 0 1 4 0 1 0 1,3 2,3 1,1 """ 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() def withClass(classname, namespace=''): """Simplified version of :class:`withAttribute` when matching on a div class - made difficult because ``class`` is a reserved word in Python. Example:: html = '''
Some text
1 4 0 1 0
1,3 2,3 1,1
this <div> has no class
''' div,div_end = makeHTMLTags("div") div_grid = div().setParseAction(withClass("grid")) grid_expr = div_grid + SkipTo(div | div_end)("body") for grid_header in grid_expr.searchString(html): print(grid_header.body) div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) div_expr = div_any_type + SkipTo(div | div_end)("body") for div_header in div_expr.searchString(html): print(div_header.body) prints:: 1 4 0 1 0 1 4 0 1 0 1,3 2,3 1,1 """ classattr = "%s:class" % namespace if namespace else "class" return withAttribute(**{classattr: classname}) opAssoc = SimpleNamespace() opAssoc.LEFT = object() opAssoc.RIGHT = object() def infixNotation(baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')')): """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. The generated parser will also recognize the use of parentheses to override operator precedences (see example below). Note: if you define a deep operator list, you may see performance issues when using infixNotation. See :class:`ParserElement.enablePackrat` for a mechanism to potentially improve your parser performance. 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); if the parse action is passed a tuple or list of functions, this is equivalent to calling ``setParseAction(*fn)`` (:class:`ParserElement.setParseAction`) - lpar - expression for matching left-parentheses (default= ``Suppress('(')``) - rpar - expression for matching right-parentheses (default= ``Suppress(')')``) Example:: # simple example of four-function arithmetic with ints and # variable names integer = pyparsing_common.signed_integer varname = pyparsing_common.identifier arith_expr = infixNotation(integer | varname, [ ('-', 1, opAssoc.RIGHT), (oneOf('* /'), 2, opAssoc.LEFT), (oneOf('+ -'), 2, opAssoc.LEFT), ]) arith_expr.runTests(''' 5+3*6 (5+3)*6 -2--11 ''', fullDump=False) prints:: 5+3*6 [[5, '+', [3, '*', 6]]] (5+3)*6 [[[5, '+', 3], '*', 6]] -2--11 [[['-', 2], '-', ['-', 11]]] """ # captive version of FollowedBy that does not do parse actions or capture results names class _FB(FollowedBy): def parseImpl(self, instring, loc, doActions=True): self.expr.tryParse(instring, loc) return loc, [] ret = Forward() lastExpr = baseExpr | (lpar + ret + rpar) for i, operDef in enumerate(opList): opExpr, arity, rightLeftAssoc, pa = (operDef + (None, ))[:4] termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr 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(termName) if rightLeftAssoc == opAssoc.LEFT: if arity == 1: matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + OneOrMore(opExpr)) elif arity == 2: if opExpr is not None: matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group(lastExpr + OneOrMore(opExpr + lastExpr)) else: matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr + OneOrMore(lastExpr)) elif arity == 3: matchExpr = (_FB(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + Group(lastExpr + OneOrMore(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 = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) elif arity == 2: if opExpr is not None: matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group(lastExpr + OneOrMore(opExpr + thisExpr)) else: matchExpr = _FB(lastExpr + thisExpr) + Group(lastExpr + OneOrMore(thisExpr)) elif arity == 3: matchExpr = (_FB(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: if isinstance(pa, (tuple, list)): matchExpr.setParseAction(*pa) else: matchExpr.setParseAction(pa) thisExpr <<= (matchExpr.setName(termName) | lastExpr) lastExpr = thisExpr ret <<= lastExpr return ret operatorPrecedence = infixNotation """(Deprecated) Former name of :class:`infixNotation`, will be dropped in a future release.""" dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"').setName("string enclosed in double quotes") sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'").setName("string enclosed in single quotes") quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"' | Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'").setName("quotedString using single or double quotes") unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): """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= :class:`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 :class:`Or` or :class:`MatchFirst`. The default is :class:`quotedString`, but if no expressions are to be ignored, then pass ``None`` for this argument. Example:: data_type = oneOf("void int short long char float double") decl_data_type = Combine(data_type + Optional(Word('*'))) ident = Word(alphas+'_', alphanums+'_') number = pyparsing_common.number arg = Group(decl_data_type + ident) LPAR, RPAR = map(Suppress, "()") code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) c_function = (decl_data_type("type") + ident("name") + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + code_body("body")) c_function.ignore(cStyleComment) source_code = ''' int is_odd(int x) { return (x%2); } int dec_to_hex(char hchar) { if (hchar >= '0' && hchar <= '9') { return (ord(hchar)-ord('0')); } else { return (10+ord(hchar)-ord('A')); } } ''' for func in c_function.searchString(source_code): print("%(name)s (%(type)s) args: %(args)s" % func) prints:: is_odd (int) args: [['int', 'x']] dec_to_hex (int) args: [['char', 'hchar']] """ 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.copy() + 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)) ret.setName('nested %s%s expression' % (opener, 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 current level; set to False for block of left-most statements (default= ``True``) A valid block must contain at least one ``blockStatement``. Example:: data = ''' def A(z): A1 B = 100 G = A2 A2 A3 B def BB(a,b,c): BB1 def BBA(): bba1 bba2 bba3 C D def spam(x,y): def eggs(z): pass ''' indentStack = [1] stmt = Forward() identifier = Word(alphas, alphanums) funcDecl = ("def" + identifier + Group("(" + Optional(delimitedList(identifier)) + ")") + ":") func_body = indentedBlock(stmt, indentStack) funcDef = Group(funcDecl + func_body) rvalue = Forward() funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") rvalue << (funcCall | identifier | Word(nums)) assignment = Group(identifier + "=" + rvalue) stmt << (funcDef | assignment | identifier) module_body = OneOrMore(stmt) parseTree = module_body.parseString(data) parseTree.pprint() prints:: [['def', 'A', ['(', 'z', ')'], ':', [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], 'B', ['def', 'BB', ['(', 'a', 'b', 'c', ')'], ':', [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], 'C', 'D', ['def', 'spam', ['(', 'x', 'y', ')'], ':', [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] """ backup_stack = indentStack[:] def reset_stack(): indentStack[:] = backup_stack def checkPeerIndent(s, l, t): if l >= len(s): return curCol = col(l, s) if curCol != indentStack[-1]: if curCol > indentStack[-1]: raise ParseException(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 in indentStack): raise ParseException(s, l, "not an unindent") if curCol < indentStack[-1]: indentStack.pop() NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress(), stopOn=StringEnd()) INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') PEER = Empty().setParseAction(checkPeerIndent).setName('') UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') if indent: smExpr = Group(Optional(NL) + INDENT + OneOrMore(PEER + Group(blockStatementExpr) + Optional(NL), stopOn=StringEnd()) + UNDENT) else: smExpr = Group(Optional(NL) + OneOrMore(PEER + Group(blockStatementExpr) + Optional(NL), stopOn=StringEnd()) + UNDENT) smExpr.setFailAction(lambda a, b, c, d: reset_stack()) blockStatementExpr.ignore(_bslash + LineEnd()) return smExpr.setName('indented block') alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") anyOpenTag, anyCloseTag = makeHTMLTags(Word(alphas, alphanums + "_:").setName('any tag')) _htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(), '><& "\'')) commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") def replaceHTMLEntity(t): """Helper parser action to replace common HTML entities with their special characters""" return _htmlEntityMap.get(t.entity) # it's easy to get these comment structures wrong - they're very common, so may as well make them available cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") "Comment of the form ``/* ... */``" htmlComment = Regex(r"").setName("HTML comment") "Comment of the form ````" restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") "Comment of the form ``// ... (to end of line)``" cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/' | dblSlashComment).setName("C++ style comment") "Comment of either form :class:`cStyleComment` or :class:`dblSlashComment`" javaStyleComment = cppStyleComment "Same as :class:`cppStyleComment`" pythonStyleComment = Regex(r"#.*").setName("Python style comment") "Comment of the form ``# ... (to end of line)``" _commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + Optional(Word(" \t") + ~Literal(",") + ~LineEnd()))).streamline().setName("commaItem") commaSeparatedList = delimitedList(Optional(quotedString.copy() | _commasepitem, default="")).setName("commaSeparatedList") """(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. This expression is deprecated in favor of :class:`pyparsing_common.comma_separated_list`. """ # some other useful expressions - using lower-case class name since we are really using this as a namespace class pyparsing_common: """Here are some common low-level expressions that may be useful in jump-starting parser development: - numeric forms (:class:`integers`, :class:`reals`, :class:`scientific notation`) - common :class:`programming identifiers` - network addresses (:class:`MAC`, :class:`IPv4`, :class:`IPv6`) - ISO8601 :class:`dates` and :class:`datetime` - :class:`UUID` - :class:`comma-separated list` Parse actions: - :class:`convertToInteger` - :class:`convertToFloat` - :class:`convertToDate` - :class:`convertToDatetime` - :class:`stripHTMLTags` - :class:`upcaseTokens` - :class:`downcaseTokens` Example:: pyparsing_common.number.runTests(''' # any int or real number, returned as the appropriate type 100 -100 +100 3.14159 6.02e23 1e-12 ''') pyparsing_common.fnumber.runTests(''' # any int or real number, returned as float 100 -100 +100 3.14159 6.02e23 1e-12 ''') pyparsing_common.hex_integer.runTests(''' # hex numbers 100 FF ''') pyparsing_common.fraction.runTests(''' # fractions 1/2 -3/4 ''') pyparsing_common.mixed_integer.runTests(''' # mixed fractions 1 1/2 -3/4 1-3/4 ''') import uuid pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) pyparsing_common.uuid.runTests(''' # uuid 12345678-1234-5678-1234-567812345678 ''') prints:: # any int or real number, returned as the appropriate type 100 [100] -100 [-100] +100 [100] 3.14159 [3.14159] 6.02e23 [6.02e+23] 1e-12 [1e-12] # any int or real number, returned as float 100 [100.0] -100 [-100.0] +100 [100.0] 3.14159 [3.14159] 6.02e23 [6.02e+23] 1e-12 [1e-12] # hex numbers 100 [256] FF [255] # fractions 1/2 [0.5] -3/4 [-0.75] # mixed fractions 1 [1] 1/2 [0.5] -3/4 [-0.75] 1-3/4 [1.75] # uuid 12345678-1234-5678-1234-567812345678 [UUID('12345678-1234-5678-1234-567812345678')] """ convertToInteger = tokenMap(int) """ Parse action for converting parsed integers to Python int """ convertToFloat = tokenMap(float) """ Parse action for converting parsed numbers to Python float """ integer = Word(nums).setName("integer").setParseAction(convertToInteger) """expression that parses an unsigned integer, returns an int""" hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int, 16)) """expression that parses a hexadecimal integer, returns an int""" signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) """expression that parses an integer with optional leading sign, returns an int""" fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") """fractional expression of an integer divided by an integer, returns a float""" fraction.addParseAction(lambda t: t[0]/t[-1]) mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" mixed_integer.addParseAction(sum) real = Regex(r'[+-]?(?:\d+\.\d*|\.\d+)').setName("real number").setParseAction(convertToFloat) """expression that parses a floating point number and returns a float""" sci_real = Regex(r'[+-]?(?:\d+(?:[eE][+-]?\d+)|(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) """expression that parses a floating point number with optional scientific notation and returns a float""" # streamlining this expression makes the docs nicer-looking number = (sci_real | real | signed_integer).streamline() """any numeric expression, returns the corresponding Python type""" fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) """any int or real number, returned as float""" identifier = Word(alphas + '_', alphanums + '_').setName("identifier") """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") "IPv4 address (``0.0.0.0 - 255.255.255.255``)" _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part) * 7).setName("full IPv6 address") _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part) * (0, 6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part) * (0, 6)) ).setName("short IPv6 address") _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") "IPv6 address (long, short, or mixed form)" mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" @staticmethod def convertToDate(fmt="%Y-%m-%d"): """ Helper to create a parse action for converting parsed date string to Python datetime.date Params - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%d"``) Example:: date_expr = pyparsing_common.iso8601_date.copy() date_expr.setParseAction(pyparsing_common.convertToDate()) print(date_expr.parseString("1999-12-31")) prints:: [datetime.date(1999, 12, 31)] """ def cvt_fn(s, l, t): try: return datetime.strptime(t[0], fmt).date() except ValueError as ve: raise ParseException(s, l, str(ve)) return cvt_fn @staticmethod def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): """Helper to create a parse action for converting parsed datetime string to Python datetime.datetime Params - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%dT%H:%M:%S.%f"``) Example:: dt_expr = pyparsing_common.iso8601_datetime.copy() dt_expr.setParseAction(pyparsing_common.convertToDatetime()) print(dt_expr.parseString("1999-12-31T23:59:59.999")) prints:: [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] """ def cvt_fn(s, l, t): try: return datetime.strptime(t[0], fmt) except ValueError as ve: raise ParseException(s, l, str(ve)) return cvt_fn iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") "ISO8601 date (``yyyy-mm-dd``)" iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") "ISO8601 datetime (``yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)``) - trailing seconds, milliseconds, and timezone optional; accepts separating ``'T'`` or ``' '``" uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") "UUID (``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``)" _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() @staticmethod def stripHTMLTags(s, l, tokens): """Parse action to remove HTML tags from web page HTML source Example:: # strip HTML links from normal text text = 'More info at the
pyparsing wiki page' td, td_end = makeHTMLTags("TD") table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end print(table_text.parseString(text).body) Prints:: More info at the pyparsing wiki page """ return pyparsing_common._html_stripper.transformString(tokens[0]) _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + Optional(White(" \t")))).streamline().setName("commaItem") comma_separated_list = delimitedList(Optional(quotedString.copy() | _commasepitem, default='') ).setName("comma separated list") """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) """Parse action to convert tokens to upper case.""" downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) """Parse action to convert tokens to lower case.""" class _lazyclassproperty(object): def __init__(self, fn): self.fn = fn self.__doc__ = fn.__doc__ self.__name__ = fn.__name__ def __get__(self, obj, cls): if cls is None: cls = type(obj) if not hasattr(cls, '_intern') or any(cls._intern is getattr(superclass, '_intern', []) for superclass in cls.__mro__[1:]): cls._intern = {} attrname = self.fn.__name__ if attrname not in cls._intern: cls._intern[attrname] = self.fn(cls) return cls._intern[attrname] class unicode_set(object): """ A set of Unicode characters, for language-specific strings for ``alphas``, ``nums``, ``alphanums``, and ``printables``. A unicode_set is defined by a list of ranges in the Unicode character set, in a class attribute ``_ranges``, such as:: _ranges = [(0x0020, 0x007e), (0x00a0, 0x00ff),] A unicode set can also be defined using multiple inheritance of other unicode sets:: class CJK(Chinese, Japanese, Korean): pass """ _ranges = [] @classmethod def _get_chars_for_ranges(cls): ret = [] for cc in cls.__mro__: if cc is unicode_set: break for rr in cc._ranges: ret.extend(range(rr[0], rr[-1] + 1)) return [unichr(c) for c in sorted(set(ret))] @_lazyclassproperty def printables(cls): "all non-whitespace characters in this range" return u''.join(filterfalse(unicode.isspace, cls._get_chars_for_ranges())) @_lazyclassproperty def alphas(cls): "all alphabetic characters in this range" return u''.join(filter(unicode.isalpha, cls._get_chars_for_ranges())) @_lazyclassproperty def nums(cls): "all numeric digit characters in this range" return u''.join(filter(unicode.isdigit, cls._get_chars_for_ranges())) @_lazyclassproperty def alphanums(cls): "all alphanumeric characters in this range" return cls.alphas + cls.nums class pyparsing_unicode(unicode_set): """ A namespace class for defining common language unicode_sets. """ _ranges = [(32, sys.maxunicode)] class Latin1(unicode_set): "Unicode set for Latin-1 Unicode Character Range" _ranges = [(0x0020, 0x007e), (0x00a0, 0x00ff),] class LatinA(unicode_set): "Unicode set for Latin-A Unicode Character Range" _ranges = [(0x0100, 0x017f),] class LatinB(unicode_set): "Unicode set for Latin-B Unicode Character Range" _ranges = [(0x0180, 0x024f),] class Greek(unicode_set): "Unicode set for Greek Unicode Character Ranges" _ranges = [ (0x0370, 0x03ff), (0x1f00, 0x1f15), (0x1f18, 0x1f1d), (0x1f20, 0x1f45), (0x1f48, 0x1f4d), (0x1f50, 0x1f57), (0x1f59,), (0x1f5b,), (0x1f5d,), (0x1f5f, 0x1f7d), (0x1f80, 0x1fb4), (0x1fb6, 0x1fc4), (0x1fc6, 0x1fd3), (0x1fd6, 0x1fdb), (0x1fdd, 0x1fef), (0x1ff2, 0x1ff4), (0x1ff6, 0x1ffe), ] class Cyrillic(unicode_set): "Unicode set for Cyrillic Unicode Character Range" _ranges = [(0x0400, 0x04ff)] class Chinese(unicode_set): "Unicode set for Chinese Unicode Character Range" _ranges = [(0x4e00, 0x9fff), (0x3000, 0x303f),] class Japanese(unicode_set): "Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges" _ranges = [] class Kanji(unicode_set): "Unicode set for Kanji Unicode Character Range" _ranges = [(0x4E00, 0x9Fbf), (0x3000, 0x303f),] class Hiragana(unicode_set): "Unicode set for Hiragana Unicode Character Range" _ranges = [(0x3040, 0x309f),] class Katakana(unicode_set): "Unicode set for Katakana Unicode Character Range" _ranges = [(0x30a0, 0x30ff),] class Korean(unicode_set): "Unicode set for Korean Unicode Character Range" _ranges = [(0xac00, 0xd7af), (0x1100, 0x11ff), (0x3130, 0x318f), (0xa960, 0xa97f), (0xd7b0, 0xd7ff), (0x3000, 0x303f),] class CJK(Chinese, Japanese, Korean): "Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range" pass class Thai(unicode_set): "Unicode set for Thai Unicode Character Range" _ranges = [(0x0e01, 0x0e3a), (0x0e3f, 0x0e5b),] class Arabic(unicode_set): "Unicode set for Arabic Unicode Character Range" _ranges = [(0x0600, 0x061b), (0x061e, 0x06ff), (0x0700, 0x077f),] class Hebrew(unicode_set): "Unicode set for Hebrew Unicode Character Range" _ranges = [(0x0590, 0x05ff),] class Devanagari(unicode_set): "Unicode set for Devanagari Unicode Character Range" _ranges = [(0x0900, 0x097f), (0xa8e0, 0xa8ff)] pyparsing_unicode.Japanese._ranges = (pyparsing_unicode.Japanese.Kanji._ranges + pyparsing_unicode.Japanese.Hiragana._ranges + pyparsing_unicode.Japanese.Katakana._ranges) # define ranges in language character sets if PY_3: setattr(pyparsing_unicode, u"العربية", pyparsing_unicode.Arabic) setattr(pyparsing_unicode, u"中文", pyparsing_unicode.Chinese) setattr(pyparsing_unicode, u"кириллица", pyparsing_unicode.Cyrillic) setattr(pyparsing_unicode, u"Ελληνικά", pyparsing_unicode.Greek) setattr(pyparsing_unicode, u"עִברִית", pyparsing_unicode.Hebrew) setattr(pyparsing_unicode, u"日本語", pyparsing_unicode.Japanese) setattr(pyparsing_unicode.Japanese, u"漢字", pyparsing_unicode.Japanese.Kanji) setattr(pyparsing_unicode.Japanese, u"カタカナ", pyparsing_unicode.Japanese.Katakana) setattr(pyparsing_unicode.Japanese, u"ひらがな", pyparsing_unicode.Japanese.Hiragana) setattr(pyparsing_unicode, u"한국어", pyparsing_unicode.Korean) setattr(pyparsing_unicode, u"ไทย", pyparsing_unicode.Thai) setattr(pyparsing_unicode, u"देवनागरी", pyparsing_unicode.Devanagari) class pyparsing_test: """ namespace class for classes useful in writing unit tests """ class reset_pyparsing_context: """ Context manager to be used when writing unit tests that modify pyparsing config values: - packrat parsing - default whitespace characters. - default keyword characters - literal string auto-conversion class - __diag__ settings Example: with reset_pyparsing_context(): # test that literals used to construct a grammar are automatically suppressed ParserElement.inlineLiteralsUsing(Suppress) term = Word(alphas) | Word(nums) group = Group('(' + term[...] + ')') # assert that the '()' characters are not included in the parsed tokens self.assertParseAndCheckLisst(group, "(abc 123 def)", ['abc', '123', 'def']) # after exiting context manager, literals are converted to Literal expressions again """ def __init__(self): self._save_context = {} def save(self): self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS self._save_context[ "literal_string_class" ] = ParserElement._literalStringClass self._save_context["packrat_enabled"] = ParserElement._packratEnabled self._save_context["packrat_parse"] = ParserElement._parse self._save_context["__diag__"] = { name: getattr(__diag__, name) for name in __diag__._all_names } self._save_context["__compat__"] = { "collect_all_And_tokens": __compat__.collect_all_And_tokens } return self def restore(self): # reset pyparsing global state if ( ParserElement.DEFAULT_WHITE_CHARS != self._save_context["default_whitespace"] ): ParserElement.setDefaultWhitespaceChars( self._save_context["default_whitespace"] ) Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] ParserElement.inlineLiteralsUsing( self._save_context["literal_string_class"] ) for name, value in self._save_context["__diag__"].items(): setattr(__diag__, name, value) ParserElement._packratEnabled = self._save_context["packrat_enabled"] ParserElement._parse = self._save_context["packrat_parse"] __compat__.collect_all_And_tokens = self._save_context["__compat__"] def __enter__(self): return self.save() def __exit__(self, *args): return self.restore() class TestParseResultsAsserts: """ A mixin class to add parse results assertion methods to normal unittest.TestCase classes. """ def assertParseResultsEquals( self, result, expected_list=None, expected_dict=None, msg=None ): """ Unit test assertion to compare a ParseResults object with an optional expected_list, and compare any defined results names with an optional expected_dict. """ if expected_list is not None: self.assertEqual(expected_list, result.asList(), msg=msg) if expected_dict is not None: self.assertEqual(expected_dict, result.asDict(), msg=msg) def assertParseAndCheckList( self, expr, test_string, expected_list, msg=None, verbose=True ): """ Convenience wrapper assert to test a parser element and input string, and assert that the resulting ParseResults.asList() is equal to the expected_list. """ result = expr.parseString(test_string, parseAll=True) if verbose: print(result.dump()) self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) def assertParseAndCheckDict( self, expr, test_string, expected_dict, msg=None, verbose=True ): """ Convenience wrapper assert to test a parser element and input string, and assert that the resulting ParseResults.asDict() is equal to the expected_dict. """ result = expr.parseString(test_string, parseAll=True) if verbose: print(result.dump()) self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) def assertRunTestResults( self, run_tests_report, expected_parse_results=None, msg=None ): """ Unit test assertion to evaluate output of ParserElement.runTests(). If a list of list-dict tuples is given as the expected_parse_results argument, then these are zipped with the report tuples returned by runTests and evaluated using assertParseResultsEquals. Finally, asserts that the overall runTests() success value is True. :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] """ run_test_success, run_test_results = run_tests_report if expected_parse_results is not None: merged = [ (rpt[0], rpt[1], expected) for rpt, expected in zip(run_test_results, expected_parse_results) ] for test_string, result, expected in merged: # expected should be a tuple containing a list and/or a dict or an exception, # and optional failure message string # an empty tuple will skip any result validation fail_msg = next( (exp for exp in expected if isinstance(exp, str)), None ) expected_exception = next( ( exp for exp in expected if isinstance(exp, type) and issubclass(exp, Exception) ), None, ) if expected_exception is not None: with self.assertRaises( expected_exception=expected_exception, msg=fail_msg or msg ): if isinstance(result, Exception): raise result else: expected_list = next( (exp for exp in expected if isinstance(exp, list)), None ) expected_dict = next( (exp for exp in expected if isinstance(exp, dict)), None ) if (expected_list, expected_dict) != (None, None): self.assertParseResultsEquals( result, expected_list=expected_list, expected_dict=expected_dict, msg=fail_msg or msg, ) else: # warning here maybe? print("no validation for {!r}".format(test_string)) # do this last, in case some specific test results can be reported instead self.assertTrue( run_test_success, msg=msg if msg is not None else "failed runTests" ) @contextmanager def assertRaisesParseException(self, exc_type=ParseException, msg=None): with self.assertRaises(exc_type, msg=msg): yield if __name__ == "__main__": selectToken = CaselessLiteral("select") fromToken = CaselessLiteral("from") ident = Word(alphas, alphanums + "_$") columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) columnNameList = Group(delimitedList(columnName)).setName("columns") columnSpec = ('*' | columnNameList) tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) tableNameList = Group(delimitedList(tableName)).setName("tables") simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") # demo runTests method, including embedded comments in test string simpleSQL.runTests(""" # '*' as column list and dotted table name select * from SYS.XYZZY # caseless match on "SELECT", and casts back to "select" SELECT * from XYZZY, ABC # list of column names, and mixed case SELECT keyword Select AA,BB,CC from Sys.dual # multiple tables Select A, B, C from Sys.dual, Table2 # invalid SELECT keyword - should fail Xelect A, B, C from Sys.dual # incomplete command - should fail Select # invalid column name - should fail Select ^^^ frox Sys.dual """) pyparsing_common.number.runTests(""" 100 -100 +100 3.14159 6.02e23 1e-12 """) # any int or real number, returned as float pyparsing_common.fnumber.runTests(""" 100 -100 +100 3.14159 6.02e23 1e-12 """) pyparsing_common.hex_integer.runTests(""" 100 FF """) import uuid pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) pyparsing_common.uuid.runTests(""" 12345678-1234-5678-1234-567812345678 """) pyparsing2-2.4.7/setup.cfg000066400000000000000000000001461365333160500154510ustar00rootroot00000000000000[bdist_wheel] universal = 1 [metadata] license_file = LICENSE [egg_info] tag_build = tag_date = 0 pyparsing2-2.4.7/setup.py000066400000000000000000000033251365333160500153440ustar00rootroot00000000000000#!/usr/bin/env python """Setup script for the pyparsing module distribution.""" from setuptools import setup from pyparsing import __version__ as pyparsing_version from io import open # The directory containing this file README_name = __file__.replace("setup.py", "README.rst") # The text of the README file with open(README_name, encoding='utf8') as README: pyparsing_main_doc = README.read() modules = ["pyparsing",] setup(# Distribution meta-data name = "pyparsing", version = pyparsing_version, description = "Python parsing module", long_description = pyparsing_main_doc, author = "Paul McGuire", author_email = "ptmcg@users.sourceforge.net", url = "https://github.com/pyparsing/pyparsing/", download_url = "https://pypi.org/project/pyparsing/", license = "MIT License", py_modules = modules, python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*', test_suite="unitTests.suite", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: Information Technology', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', ] ) pyparsing2-2.4.7/simple_unit_tests.py000066400000000000000000000477651365333160500177760ustar00rootroot00000000000000# # simple_unit_tests.py # # While these unit tests *do* perform low-level unit testing of the classes in pyparsing, # this testing module should also serve an instructional purpose, to clearly show simple passing # and failing parse cases of some basic pyparsing expressions. # # Copyright (c) 2018 Paul T. McGuire # from __future__ import division try: import unittest2 as unittest except ImportError: import unittest import pyparsing as pp from collections import namedtuple from datetime import datetime # Test spec data class for specifying simple pyparsing test cases PpTestSpec = namedtuple("PpTestSpec", "desc expr text parse_fn " "expected_list expected_dict expected_fail_locn") PpTestSpec.__new__.__defaults__ = ('', pp.Empty(), '', 'parseString', None, None, None) class PyparsingExpressionTestCase(unittest.TestCase): """ Base pyparsing testing class to parse various pyparsing expressions against given text strings. Subclasses must define a class attribute 'tests' which is a list of PpTestSpec instances. """ if not hasattr(unittest.TestCase, 'subTest'): # Python 2 compatibility from contextlib import contextmanager @contextmanager def subTest(self, **params): print('subTest:', params) yield tests = [] def runTest(self): if self.__class__ is PyparsingExpressionTestCase: return for test_spec in self.tests: # for each spec in the class's tests list, create a subtest # that will either: # - parse the string with expected success, display the # results, and validate the returned ParseResults # - or parse the string with expected failure, display the # error message and mark the error location, and validate # the location against an expected value with self.subTest(test_spec=test_spec): test_spec.expr.streamline() print("\n{0} - {1}({2})".format(test_spec.desc, type(test_spec.expr).__name__, test_spec.expr)) parsefn = getattr(test_spec.expr, test_spec.parse_fn) if test_spec.expected_fail_locn is None: # expect success result = parsefn(test_spec.text) if test_spec.parse_fn == 'parseString': print(result.dump()) # compare results against given list and/or dict if test_spec.expected_list is not None: self.assertEqual(result.asList(), test_spec.expected_list) if test_spec.expected_dict is not None: self.assertEqual(result.asDict(), test_spec.expected_dict) elif test_spec.parse_fn == 'transformString': print(result) # compare results against given list and/or dict if test_spec.expected_list is not None: self.assertEqual([result], test_spec.expected_list) elif test_spec.parse_fn == 'searchString': print(result) # compare results against given list and/or dict if test_spec.expected_list is not None: self.assertEqual([result], test_spec.expected_list) else: # expect fail try: parsefn(test_spec.text) except Exception as exc: if not hasattr(exc, '__traceback__'): # Python 2 compatibility from sys import exc_info etype, value, traceback = exc_info() exc.__traceback__ = traceback print(pp.ParseException.explain(exc)) self.assertEqual(exc.loc, test_spec.expected_fail_locn) else: self.assertTrue(False, "failed to raise expected exception") # =========== TEST DEFINITIONS START HERE ============== class TestLiteral(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "Simple match", expr = pp.Literal("xyz"), text = "xyz", expected_list = ["xyz"], ), PpTestSpec( desc = "Simple match after skipping whitespace", expr = pp.Literal("xyz"), text = " xyz", expected_list = ["xyz"], ), PpTestSpec( desc = "Simple fail - parse an empty string", expr = pp.Literal("xyz"), text = "", expected_fail_locn = 0, ), PpTestSpec( desc = "Simple fail - parse a mismatching string", expr = pp.Literal("xyz"), text = "xyu", expected_fail_locn = 0, ), PpTestSpec( desc = "Simple fail - parse a partially matching string", expr = pp.Literal("xyz"), text = "xy", expected_fail_locn = 0, ), PpTestSpec( desc = "Fail - parse a partially matching string by matching individual letters", expr = pp.Literal("x") + pp.Literal("y") + pp.Literal("z"), text = "xy", expected_fail_locn = 2, ), ] class TestCaselessLiteral(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "Match colors, converting to consistent case", expr = (pp.CaselessLiteral("RED") | pp.CaselessLiteral("GREEN") | pp.CaselessLiteral("BLUE"))[...], text = "red Green BluE blue GREEN green rEd", expected_list = ['RED', 'GREEN', 'BLUE', 'BLUE', 'GREEN', 'GREEN', 'RED'], ), ] class TestWord(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "Simple Word match", expr = pp.Word("xy"), text = "xxyxxyy", expected_list = ["xxyxxyy"], ), PpTestSpec( desc = "Simple Word match of two separate Words", expr = pp.Word("x") + pp.Word("y"), text = "xxxxxyy", expected_list = ["xxxxx", "yy"], ), PpTestSpec( desc = "Simple Word match of two separate Words - implicitly skips whitespace", expr = pp.Word("x") + pp.Word("y"), text = "xxxxx yy", expected_list = ["xxxxx", "yy"], ), ] class TestCombine(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc="Parsing real numbers - fail, parsed numbers are in pieces", expr=(pp.Word(pp.nums) + '.' + pp.Word(pp.nums))[...], text="1.2 2.3 3.1416 98.6", expected_list=['1', '.', '2', '2', '.', '3', '3', '.', '1416', '98', '.', '6'], ), PpTestSpec( desc="Parsing real numbers - better, use Combine to combine multiple tokens into one", expr=pp.Combine(pp.Word(pp.nums) + '.' + pp.Word(pp.nums))[...], text="1.2 2.3 3.1416 98.6", expected_list=['1.2', '2.3', '3.1416', '98.6'], ), ] class TestRepetition(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "Match several words", expr = (pp.Word("x") | pp.Word("y"))[...], text = "xxyxxyyxxyxyxxxy", expected_list = ['xx', 'y', 'xx', 'yy', 'xx', 'y', 'x', 'y', 'xxx', 'y'], ), PpTestSpec( desc = "Match several words, skipping whitespace", expr = (pp.Word("x") | pp.Word("y"))[...], text = "x x y xxy yxx y xyx xxy", expected_list = ['x', 'x', 'y', 'xx', 'y', 'y', 'xx', 'y', 'x', 'y', 'x', 'xx', 'y'], ), PpTestSpec( desc = "Match several words, skipping whitespace (old style)", expr = pp.OneOrMore(pp.Word("x") | pp.Word("y")), text = "x x y xxy yxx y xyx xxy", expected_list = ['x', 'x', 'y', 'xx', 'y', 'y', 'xx', 'y', 'x', 'y', 'x', 'xx', 'y'], ), PpTestSpec( desc = "Match words and numbers - show use of results names to collect types of tokens", expr = (pp.Word(pp.alphas)("alpha*") | pp.pyparsing_common.integer("int*"))[...], text = "sdlfj23084ksdfs08234kjsdlfkjd0934", expected_list = ['sdlfj', 23084, 'ksdfs', 8234, 'kjsdlfkjd', 934], expected_dict = { 'alpha': ['sdlfj', 'ksdfs', 'kjsdlfkjd'], 'int': [23084, 8234, 934] } ), PpTestSpec( desc = "Using delimitedList (comma is the default delimiter)", expr = pp.delimitedList(pp.Word(pp.alphas)), text = "xxyx,xy,y,xxyx,yxx, xy", expected_list = ['xxyx', 'xy', 'y', 'xxyx', 'yxx', 'xy'], ), PpTestSpec( desc = "Using delimitedList, with ':' delimiter", expr = pp.delimitedList(pp.Word(pp.hexnums, exact=2), delim=':', combine=True), text = "0A:4B:73:21:FE:76", expected_list = ['0A:4B:73:21:FE:76'], ), ] class TestResultsName(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "Match with results name", expr = pp.Literal("xyz").setResultsName("value"), text = "xyz", expected_dict = {'value': 'xyz'}, expected_list = ['xyz'], ), PpTestSpec( desc = "Match with results name - using naming short-cut", expr = pp.Literal("xyz")("value"), text = "xyz", expected_dict = {'value': 'xyz'}, expected_list = ['xyz'], ), PpTestSpec( desc = "Define multiple results names", expr = pp.Word(pp.alphas, pp.alphanums)("key") + '=' + pp.pyparsing_common.integer("value"), text = "range=5280", expected_dict = {'key': 'range', 'value': 5280}, expected_list = ['range', '=', 5280], ), ] class TestGroups(PyparsingExpressionTestCase): EQ = pp.Suppress('=') tests = [ PpTestSpec( desc = "Define multiple results names in groups", expr = pp.Group(pp.Word(pp.alphas)("key") + EQ + pp.pyparsing_common.number("value"))[...], text = "range=5280 long=-138.52 lat=46.91", expected_list = [['range', 5280], ['long', -138.52], ['lat', 46.91]], ), PpTestSpec( desc = "Define multiple results names in groups - use Dict to define results names using parsed keys", expr = pp.Dict(pp.Group(pp.Word(pp.alphas) + EQ + pp.pyparsing_common.number)[...]), text = "range=5280 long=-138.52 lat=46.91", expected_list = [['range', 5280], ['long', -138.52], ['lat', 46.91]], expected_dict = {'lat': 46.91, 'long': -138.52, 'range': 5280} ), PpTestSpec( desc = "Define multiple value types", expr = pp.Dict(pp.Group(pp.Word(pp.alphas) + EQ + (pp.pyparsing_common.number | pp.oneOf("True False") | pp.QuotedString("'")) )[...] ), text = "long=-122.47 lat=37.82 public=True name='Golden Gate Bridge'", expected_list = [['long', -122.47], ['lat', 37.82], ['public', 'True'], ['name', 'Golden Gate Bridge']], expected_dict = {'long': -122.47, 'lat': 37.82, 'public': 'True', 'name': 'Golden Gate Bridge'} ), ] class TestParseAction(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc="Parsing real numbers - use parse action to convert to float at parse time", expr=pp.Combine(pp.Word(pp.nums) + '.' + pp.Word(pp.nums)).addParseAction(lambda t: float(t[0]))[...], text="1.2 2.3 3.1416 98.6", expected_list= [1.2, 2.3, 3.1416, 98.6], # note, these are now floats, not strs ), PpTestSpec( desc = "Match with numeric string converted to int", expr = pp.Word("0123456789").addParseAction(lambda t: int(t[0])), text = "12345", expected_list = [12345], # note - result is type int, not str ), PpTestSpec( desc = "Use two parse actions to convert numeric string, then convert to datetime", expr = pp.Word(pp.nums).addParseAction(lambda t: int(t[0]), lambda t: datetime.utcfromtimestamp(t[0])), text = "1537415628", expected_list = [datetime(2018, 9, 20, 3, 53, 48)], ), PpTestSpec( desc = "Use tokenMap for parse actions that operate on a single-length token", expr = pp.Word(pp.nums).addParseAction(pp.tokenMap(int), pp.tokenMap(datetime.utcfromtimestamp)), text = "1537415628", expected_list = [datetime(2018, 9, 20, 3, 53, 48)], ), PpTestSpec( desc = "Using a built-in function that takes a sequence of strs as a parse action", expr = pp.Word(pp.hexnums, exact=2)[...].addParseAction(':'.join), text = "0A4B7321FE76", expected_list = ['0A:4B:73:21:FE:76'], ), PpTestSpec( desc = "Using a built-in function that takes a sequence of strs as a parse action", expr = pp.Word(pp.hexnums, exact=2)[...].addParseAction(sorted), text = "0A4B7321FE76", expected_list = ['0A', '21', '4B', '73', '76', 'FE'], ), ] class TestResultsModifyingParseAction(PyparsingExpressionTestCase): def compute_stats_parse_action(t): # by the time this parse action is called, parsed numeric words # have been converted to ints by a previous parse action, so # they can be treated as ints t['sum'] = sum(t) t['ave'] = sum(t) / len(t) t['min'] = min(t) t['max'] = max(t) tests = [ PpTestSpec( desc = "A parse action that adds new key-values", expr = pp.pyparsing_common.integer[...].addParseAction(compute_stats_parse_action), text = "27 1 14 22 89", expected_list = [27, 1, 14, 22, 89], expected_dict = {'ave': 30.6, 'max': 89, 'min': 1, 'sum': 153} ), ] class TestRegex(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc="Parsing real numbers - using Regex instead of Combine", expr=pp.Regex(r'\d+\.\d+').addParseAction(lambda t: float(t[0]))[...], text="1.2 2.3 3.1416 98.6", expected_list=[1.2, 2.3, 3.1416, 98.6], # note, these are now floats, not strs ), ] class TestParseCondition(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "Define a condition to only match numeric values that are multiples of 7", expr = pp.Word(pp.nums).addCondition(lambda t: int(t[0]) % 7 == 0)[...], text = "14 35 77 12 28", expected_list = ['14', '35', '77'], ), PpTestSpec( desc = "Separate conversion to int and condition into separate parse action/conditions", expr = pp.Word(pp.nums).addParseAction(lambda t: int(t[0])) .addCondition(lambda t: t[0] % 7 == 0)[...], text = "14 35 77 12 28", expected_list = [14, 35, 77], ), ] class TestTransformStringUsingParseActions(PyparsingExpressionTestCase): markup_convert_map = { '*' : 'B', '_' : 'U', '/' : 'I', } def markup_convert(t): htmltag = TestTransformStringUsingParseActions.markup_convert_map[t.markup_symbol] return "<{0}>{1}".format(htmltag, t.body, htmltag) tests = [ PpTestSpec( desc = "Use transformString to convert simple markup to HTML", expr = (pp.oneOf(markup_convert_map)('markup_symbol') + "(" + pp.CharsNotIn(")")('body') + ")").addParseAction(markup_convert), text = "Show in *(bold), _(underscore), or /(italic) type", expected_list = ['Show in bold, underscore, or italic type'], parse_fn = 'transformString', ), ] class TestCommonHelperExpressions(PyparsingExpressionTestCase): tests = [ PpTestSpec( desc = "A comma-delimited list of words", expr = pp.delimitedList(pp.Word(pp.alphas)), text = "this, that, blah,foo, bar", expected_list = ['this', 'that', 'blah', 'foo', 'bar'], ), PpTestSpec( desc = "A counted array of words", expr = pp.countedArray(pp.Word('ab'))[...], text = "2 aaa bbb 0 3 abab bbaa abbab", expected_list = [['aaa', 'bbb'], [], ['abab', 'bbaa', 'abbab']], ), PpTestSpec( desc = "skipping comments with ignore", expr = (pp.pyparsing_common.identifier('lhs') + '=' + pp.pyparsing_common.fnumber('rhs')).ignore(pp.cppStyleComment), text = "abc_100 = /* value to be tested */ 3.1416", expected_list = ['abc_100', '=', 3.1416], expected_dict = {'lhs': 'abc_100', 'rhs': 3.1416}, ), PpTestSpec( desc = "some pre-defined expressions in pyparsing_common, and building a dotted identifier with delimted_list", expr = (pp.pyparsing_common.number("id_num") + pp.delimitedList(pp.pyparsing_common.identifier, '.', combine=True)("name") + pp.pyparsing_common.ipv4_address("ip_address") ), text = "1001 www.google.com 192.168.10.199", expected_list = [1001, 'www.google.com', '192.168.10.199'], expected_dict = {'id_num': 1001, 'name': 'www.google.com', 'ip_address': '192.168.10.199'}, ), PpTestSpec( desc = "using oneOf (shortcut for Literal('a') | Literal('b') | Literal('c'))", expr = pp.oneOf("a b c")[...], text = "a b a b b a c c a b b", expected_list = ['a', 'b', 'a', 'b', 'b', 'a', 'c', 'c', 'a', 'b', 'b'], ), PpTestSpec( desc = "parsing nested parentheses", expr = pp.nestedExpr(), text = "(a b (c) d (e f g ()))", expected_list = [['a', 'b', ['c'], 'd', ['e', 'f', 'g', []]]], ), PpTestSpec( desc = "parsing nested braces", expr = (pp.Keyword('if') + pp.nestedExpr()('condition') + pp.nestedExpr('{', '}')('body')), text = 'if ((x == y) || !z) {printf("{}");}', expected_list = ['if', [['x', '==', 'y'], '||', '!z'], ['printf(', '"{}"', ');']], expected_dict = {'condition': [[['x', '==', 'y'], '||', '!z']], 'body': [['printf(', '"{}"', ');']]}, ), ] def _get_decl_line_no(cls): import inspect return inspect.getsourcelines(cls)[1] # get all test case classes defined in this module and sort them by decl line no test_case_classes = list(PyparsingExpressionTestCase.__subclasses__()) test_case_classes.sort(key=_get_decl_line_no) # make into a suite and run it - this will run the tests in the same order # they are declared in this module # # runnable from setup.py using "python setup.py test -s simple_unit_tests.suite" # suite = unittest.TestSuite(cls() for cls in test_case_classes) # ============ MAIN ================ if __name__ == '__main__': import sys if sys.version_info[0] < 3: print("simple_unit_tests.py requires Python 3.x - exiting...") exit(0) result = unittest.TextTestRunner().run(suite) exit(0 if result.wasSuccessful() else 1) pyparsing2-2.4.7/test/000077500000000000000000000000001365333160500146065ustar00rootroot00000000000000pyparsing2-2.4.7/test/__init__.py000066400000000000000000000000001365333160500167050ustar00rootroot00000000000000pyparsing2-2.4.7/test/__init__.pyc000066400000000000000000000002231365333160500170570ustar00rootroot00000000000000 J]c@sdS(N((((s</home/paul/dev/pyparsing/gh-2.4.x/pyparsing/test/__init__.pytspyparsing2-2.4.7/test/__pycache__/000077500000000000000000000000001365333160500170165ustar00rootroot00000000000000pyparsing2-2.4.7/test/__pycache__/__init__.cpython-35.pyc000066400000000000000000000002171365333160500232020ustar00rootroot00000000000000 J]@sdS)Nrrrspyparsing2-2.4.7/test/__pycache__/__init__.cpython-38.pyc000066400000000000000000000002271365333160500232060ustar00rootroot00000000000000U J]@sdS)Nrrrpyparsing2-2.4.7/test/__pycache__/jsonParserTests.cpython-35.pyc000066400000000000000000000254621365333160500246250ustar00rootroot00000000000000 J]*,@s"dZdZdZdZdZdS)a { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [{ "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "LargestPrimeLessThan100": 97, "AvogadroNumber": 6.02E23, "EvenPrimesGreaterThan2": null, "PrimesLessThan10" : [2,3,5,7], "WMDsFound" : false, "IraqAlQaedaConnections" : null, "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", "markup"], "EmptyDict" : {}, "EmptyList" : [] }] } } } z {"menu": { "id": "file", "value": "File:", "popup": { "menuitem": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"} ] } }} aN {"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", "name": "main_window", "width": 500, "height": 500 }, "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, "vOffset": 250, "alignment": "center" }, "text": { "data": "Click Here", "size": 36, "style": "bold", "name": "text1", "hOffset": 250, "vOffset": 100, "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } }} a {"web-app": { "servlet": [ // Defines the CDSServlet { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", /* Defines glossary variables that template designers can use across the site. You can add new variables to this set by creating a new init-param, with the param-name prefixed with "configGlossary:". */ "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", /* Defines the template loader and template processor classes. These are implementations of org.cofax.TemplateProcessor and org.cofax.TemplateLoader respectively. Simply create new implementation of these classes and set them here if the default implementations do not suit your needs. Leave these alone for the defaults. */ "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", /* Defines the names of the default templates to look for when acquiring WYSIWYG templates. Leave these at their defaults for most usage. */ "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", /* New! useJSP switches on JSP template processing. jspListTemplate and jspFileTemplate are the names of the default templates to look for when aquiring JSP templates. Cofax currently in production at KR has useJSP set to false, since our sites currently use WYSIWYG templating exclusively. */ "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", /* Defines the packageTag cache. This cache keeps Cofax from needing to interact with the database to look up packageTag commands. */ "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, /* Defines the template cache. Keeps Cofax from needing to go to the file system to load a raw template from the file system. */ "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, /* Defines the page cache. Keeps Cofax from processing templates to deliver to users. */ "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, /* Defines the templates Cofax will use when being browsed by a search engine identified in searchEngineRobotsDb */ "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", /* New! useDataStore enables/disables the Cofax database pool */ "useDataStore": true, /* Defines the implementation of org.cofax.DataStore that Cofax will use. If this DataStore class does not suit your needs simply implement a new DataStore class and set here. */ "dataStoreClass": "org.cofax.SqlDataStore", /* Defines the implementation of org.cofax.Redirection that Cofax will use. If this Redirection class does not suit your needs simply implenet a new Redirection class and set here. */ "redirectionClass": "org.cofax.SqlRedirection", /* Defines the data store name. Keep this at the default */ "dataStoreName": "cofax", /* Defines the JDBC driver that Cofax's database pool will use */ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", /* Defines the JDBC connection URL to connect to the database */ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", /* Defines the user name to connect to the database */ "dataStoreUser": "sa", /* Defines the password to connect to the database */ "dataStorePassword": "dataStoreTestQuery", /* A query that will run to test the validity of the connection in the pool. */ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", /* A log file to print out database information */ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", /* The number of connection to initialize on startup */ "dataStoreInitConns": 10, /* The maximum number of connection to use in the pool */ "dataStoreMaxConns": 100, /* The number of times a connection will be utilized from the pool before disconnect */ "dataStoreConnUsageLimit": 100, /* The level of information to print to the log */ "dataStoreLogLevel": "debug", /* The maximum URL length allowable by the CDS Servlet Helps to prevent hacking */ "maxUrlLength": 500}}, /* Defines the Email Servlet */ { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { /* The mail host to be used by the mail servlet */ "mailHost": "mail1", /* An override */ "mailHostOverride": "mail2"}}, /* Defines the Admin Servlet - used to refresh cache on demand and see statistics */ { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, /* Defines the File Servlet - used to display files like Apache */ { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, { "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { /* Path to the template folder relative to the tools tomcat installation. */ "templatePath": "toolstemplates/", /* Logging boolean 1 = on, 0 = off */ "log": 1, /* Location of log. If empty, log will be written System.out */ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "logMaxSize": "", /* DataStore logging boolean 1 = on, 0 = off */ "dataLog": 1, /* DataStore location of log. If empty, log will be written System.out */ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "dataLogMaxSize": "", /* Http string relative to server root to call for page cache removal to Cofax Servlet. */ "removePageCache": "/content/admin/remove?cache=pages&id=", /* Http string relative to server root to call for template cache removal to Cofax Servlet. */ "removeTemplateCache": "/content/admin/remove?cache=templates&id=", /* Location of folder from root of drive that will be used for ftp transfer from beta server or user hard drive to live servers. Note that Edit Article will not function without this variable set correctly. MultiPart request relies upon access to this folder. */ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", /* Defines whether the Server should look in another path for config files or variables. */ "lookInContext": 1, /* Number of the ID of the top level administration group in tblPermGroups. */ "adminGroupID": 4, /* Is the tools app running on the 'beta server'. */ "betaServer": true}}], "servlet-mapping": { /* URL mapping for the CDS Servlet */ "cofaxCDS": "/", /* URL mapping for the Email Servlet */ "cofaxEmail": "/cofaxutil/aemail/*", /* URL mapping for the Admin servlet */ "cofaxAdmin": "/admin/*", /* URL mapping for the Files servlet */ "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, /* New! The cofax taglib descriptor file */ "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} aj {"menu": { "header": "SVG Viewer", "items": [ {"id": "Open"}, {"id": "OpenNew", "label": "Open New"}, null, {"id": "ZoomIn", "label": "Zoom In"}, {"id": "ZoomOut", "label": "Zoom Out"}, {"id": "OriginalView", "label": "Original View"}, null, {"id": "Quality"}, {"id": "Pause"}, {"id": "Mute"}, null, {"id": "Find", "label": "Find..."}, {"id": "FindAgain", "label": "Find Again"}, {"id": "Copy"}, {"id": "CopyAgain", "label": "Copy Again"}, {"id": "CopySVG", "label": "Copy SVG"}, {"id": "ViewSVG", "label": "View SVG"}, {"id": "ViewSource", "label": "View Source"}, {"id": "SaveAs", "label": "Save As"}, null, {"id": "Help"}, {"id": "About", "label": "About Adobe CVG Viewer..."} ] }} N)test1test2test3test4test5rrC/home/paul/dev/pyparsing/gh-2.4.x/pyparsing/test/jsonParserTests.py!s  pyparsing2-2.4.7/test/__pycache__/jsonParserTests.cpython-38.pyc000066400000000000000000000254621365333160500246300ustar00rootroot00000000000000U J]*,@sdZdZdZdZdZdS)a { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [{ "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "LargestPrimeLessThan100": 97, "AvogadroNumber": 6.02E23, "EvenPrimesGreaterThan2": null, "PrimesLessThan10" : [2,3,5,7], "WMDsFound" : false, "IraqAlQaedaConnections" : null, "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", "markup"], "EmptyDict" : {}, "EmptyList" : [] }] } } } z {"menu": { "id": "file", "value": "File:", "popup": { "menuitem": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"} ] } }} aN {"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", "name": "main_window", "width": 500, "height": 500 }, "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, "vOffset": 250, "alignment": "center" }, "text": { "data": "Click Here", "size": 36, "style": "bold", "name": "text1", "hOffset": 250, "vOffset": 100, "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } }} a {"web-app": { "servlet": [ // Defines the CDSServlet { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", /* Defines glossary variables that template designers can use across the site. You can add new variables to this set by creating a new init-param, with the param-name prefixed with "configGlossary:". */ "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", /* Defines the template loader and template processor classes. These are implementations of org.cofax.TemplateProcessor and org.cofax.TemplateLoader respectively. Simply create new implementation of these classes and set them here if the default implementations do not suit your needs. Leave these alone for the defaults. */ "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", /* Defines the names of the default templates to look for when acquiring WYSIWYG templates. Leave these at their defaults for most usage. */ "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", /* New! useJSP switches on JSP template processing. jspListTemplate and jspFileTemplate are the names of the default templates to look for when aquiring JSP templates. Cofax currently in production at KR has useJSP set to false, since our sites currently use WYSIWYG templating exclusively. */ "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", /* Defines the packageTag cache. This cache keeps Cofax from needing to interact with the database to look up packageTag commands. */ "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, /* Defines the template cache. Keeps Cofax from needing to go to the file system to load a raw template from the file system. */ "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, /* Defines the page cache. Keeps Cofax from processing templates to deliver to users. */ "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, /* Defines the templates Cofax will use when being browsed by a search engine identified in searchEngineRobotsDb */ "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", /* New! useDataStore enables/disables the Cofax database pool */ "useDataStore": true, /* Defines the implementation of org.cofax.DataStore that Cofax will use. If this DataStore class does not suit your needs simply implement a new DataStore class and set here. */ "dataStoreClass": "org.cofax.SqlDataStore", /* Defines the implementation of org.cofax.Redirection that Cofax will use. If this Redirection class does not suit your needs simply implenet a new Redirection class and set here. */ "redirectionClass": "org.cofax.SqlRedirection", /* Defines the data store name. Keep this at the default */ "dataStoreName": "cofax", /* Defines the JDBC driver that Cofax's database pool will use */ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", /* Defines the JDBC connection URL to connect to the database */ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", /* Defines the user name to connect to the database */ "dataStoreUser": "sa", /* Defines the password to connect to the database */ "dataStorePassword": "dataStoreTestQuery", /* A query that will run to test the validity of the connection in the pool. */ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", /* A log file to print out database information */ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", /* The number of connection to initialize on startup */ "dataStoreInitConns": 10, /* The maximum number of connection to use in the pool */ "dataStoreMaxConns": 100, /* The number of times a connection will be utilized from the pool before disconnect */ "dataStoreConnUsageLimit": 100, /* The level of information to print to the log */ "dataStoreLogLevel": "debug", /* The maximum URL length allowable by the CDS Servlet Helps to prevent hacking */ "maxUrlLength": 500}}, /* Defines the Email Servlet */ { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { /* The mail host to be used by the mail servlet */ "mailHost": "mail1", /* An override */ "mailHostOverride": "mail2"}}, /* Defines the Admin Servlet - used to refresh cache on demand and see statistics */ { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, /* Defines the File Servlet - used to display files like Apache */ { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, { "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { /* Path to the template folder relative to the tools tomcat installation. */ "templatePath": "toolstemplates/", /* Logging boolean 1 = on, 0 = off */ "log": 1, /* Location of log. If empty, log will be written System.out */ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "logMaxSize": "", /* DataStore logging boolean 1 = on, 0 = off */ "dataLog": 1, /* DataStore location of log. If empty, log will be written System.out */ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "dataLogMaxSize": "", /* Http string relative to server root to call for page cache removal to Cofax Servlet. */ "removePageCache": "/content/admin/remove?cache=pages&id=", /* Http string relative to server root to call for template cache removal to Cofax Servlet. */ "removeTemplateCache": "/content/admin/remove?cache=templates&id=", /* Location of folder from root of drive that will be used for ftp transfer from beta server or user hard drive to live servers. Note that Edit Article will not function without this variable set correctly. MultiPart request relies upon access to this folder. */ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", /* Defines whether the Server should look in another path for config files or variables. */ "lookInContext": 1, /* Number of the ID of the top level administration group in tblPermGroups. */ "adminGroupID": 4, /* Is the tools app running on the 'beta server'. */ "betaServer": true}}], "servlet-mapping": { /* URL mapping for the CDS Servlet */ "cofaxCDS": "/", /* URL mapping for the Email Servlet */ "cofaxEmail": "/cofaxutil/aemail/*", /* URL mapping for the Admin servlet */ "cofaxAdmin": "/admin/*", /* URL mapping for the Files servlet */ "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, /* New! The cofax taglib descriptor file */ "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} aj {"menu": { "header": "SVG Viewer", "items": [ {"id": "Open"}, {"id": "OpenNew", "label": "Open New"}, null, {"id": "ZoomIn", "label": "Zoom In"}, {"id": "ZoomOut", "label": "Zoom Out"}, {"id": "OriginalView", "label": "Original View"}, null, {"id": "Quality"}, {"id": "Pause"}, {"id": "Mute"}, null, {"id": "Find", "label": "Find..."}, {"id": "FindAgain", "label": "Find Again"}, {"id": "Copy"}, {"id": "CopyAgain", "label": "Copy Again"}, {"id": "CopySVG", "label": "Copy SVG"}, {"id": "ViewSVG", "label": "View SVG"}, {"id": "ViewSource", "label": "View Source"}, {"id": "SaveAs", "label": "Save As"}, null, {"id": "Help"}, {"id": "About", "label": "About Adobe CVG Viewer..."} ] }} N)test1test2test3test4test5rrC/home/paul/dev/pyparsing/gh-2.4.x/pyparsing/test/jsonParserTests.pys   pyparsing2-2.4.7/test/jsonParserTests.py000066400000000000000000000260521365333160500203360ustar00rootroot00000000000000# jsonParser.py # # Copyright (c) 2006, Paul McGuire # test1 = """ { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [{ "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "LargestPrimeLessThan100": 97, "AvogadroNumber": 6.02E23, "EvenPrimesGreaterThan2": null, "PrimesLessThan10" : [2,3,5,7], "WMDsFound" : false, "IraqAlQaedaConnections" : null, "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", "markup"], "EmptyDict" : {}, "EmptyList" : [] }] } } } """ test2 = """ {"menu": { "id": "file", "value": "File:", "popup": { "menuitem": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"} ] } }} """ test3 = """ {"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", "name": "main_window", "width": 500, "height": 500 }, "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, "vOffset": 250, "alignment": "center" }, "text": { "data": "Click Here", "size": 36, "style": "bold", "name": "text1", "hOffset": 250, "vOffset": 100, "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } }} """ test4 = """ {"web-app": { "servlet": [ // Defines the CDSServlet { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", /* Defines glossary variables that template designers can use across the site. You can add new variables to this set by creating a new init-param, with the param-name prefixed with "configGlossary:". */ "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", /* Defines the template loader and template processor classes. These are implementations of org.cofax.TemplateProcessor and org.cofax.TemplateLoader respectively. Simply create new implementation of these classes and set them here if the default implementations do not suit your needs. Leave these alone for the defaults. */ "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", /* Defines the names of the default templates to look for when acquiring WYSIWYG templates. Leave these at their defaults for most usage. */ "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", /* New! useJSP switches on JSP template processing. jspListTemplate and jspFileTemplate are the names of the default templates to look for when aquiring JSP templates. Cofax currently in production at KR has useJSP set to false, since our sites currently use WYSIWYG templating exclusively. */ "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", /* Defines the packageTag cache. This cache keeps Cofax from needing to interact with the database to look up packageTag commands. */ "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, /* Defines the template cache. Keeps Cofax from needing to go to the file system to load a raw template from the file system. */ "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, /* Defines the page cache. Keeps Cofax from processing templates to deliver to users. */ "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, /* Defines the templates Cofax will use when being browsed by a search engine identified in searchEngineRobotsDb */ "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", /* New! useDataStore enables/disables the Cofax database pool */ "useDataStore": true, /* Defines the implementation of org.cofax.DataStore that Cofax will use. If this DataStore class does not suit your needs simply implement a new DataStore class and set here. */ "dataStoreClass": "org.cofax.SqlDataStore", /* Defines the implementation of org.cofax.Redirection that Cofax will use. If this Redirection class does not suit your needs simply implenet a new Redirection class and set here. */ "redirectionClass": "org.cofax.SqlRedirection", /* Defines the data store name. Keep this at the default */ "dataStoreName": "cofax", /* Defines the JDBC driver that Cofax's database pool will use */ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", /* Defines the JDBC connection URL to connect to the database */ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", /* Defines the user name to connect to the database */ "dataStoreUser": "sa", /* Defines the password to connect to the database */ "dataStorePassword": "dataStoreTestQuery", /* A query that will run to test the validity of the connection in the pool. */ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", /* A log file to print out database information */ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", /* The number of connection to initialize on startup */ "dataStoreInitConns": 10, /* The maximum number of connection to use in the pool */ "dataStoreMaxConns": 100, /* The number of times a connection will be utilized from the pool before disconnect */ "dataStoreConnUsageLimit": 100, /* The level of information to print to the log */ "dataStoreLogLevel": "debug", /* The maximum URL length allowable by the CDS Servlet Helps to prevent hacking */ "maxUrlLength": 500}}, /* Defines the Email Servlet */ { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { /* The mail host to be used by the mail servlet */ "mailHost": "mail1", /* An override */ "mailHostOverride": "mail2"}}, /* Defines the Admin Servlet - used to refresh cache on demand and see statistics */ { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, /* Defines the File Servlet - used to display files like Apache */ { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, { "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { /* Path to the template folder relative to the tools tomcat installation. */ "templatePath": "toolstemplates/", /* Logging boolean 1 = on, 0 = off */ "log": 1, /* Location of log. If empty, log will be written System.out */ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "logMaxSize": "", /* DataStore logging boolean 1 = on, 0 = off */ "dataLog": 1, /* DataStore location of log. If empty, log will be written System.out */ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "dataLogMaxSize": "", /* Http string relative to server root to call for page cache removal to Cofax Servlet. */ "removePageCache": "/content/admin/remove?cache=pages&id=", /* Http string relative to server root to call for template cache removal to Cofax Servlet. */ "removeTemplateCache": "/content/admin/remove?cache=templates&id=", /* Location of folder from root of drive that will be used for ftp transfer from beta server or user hard drive to live servers. Note that Edit Article will not function without this variable set correctly. MultiPart request relies upon access to this folder. */ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", /* Defines whether the Server should look in another path for config files or variables. */ "lookInContext": 1, /* Number of the ID of the top level administration group in tblPermGroups. */ "adminGroupID": 4, /* Is the tools app running on the 'beta server'. */ "betaServer": true}}], "servlet-mapping": { /* URL mapping for the CDS Servlet */ "cofaxCDS": "/", /* URL mapping for the Email Servlet */ "cofaxEmail": "/cofaxutil/aemail/*", /* URL mapping for the Admin servlet */ "cofaxAdmin": "/admin/*", /* URL mapping for the Files servlet */ "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, /* New! The cofax taglib descriptor file */ "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} """ test5 = """ {"menu": { "header": "SVG Viewer", "items": [ {"id": "Open"}, {"id": "OpenNew", "label": "Open New"}, null, {"id": "ZoomIn", "label": "Zoom In"}, {"id": "ZoomOut", "label": "Zoom Out"}, {"id": "OriginalView", "label": "Original View"}, null, {"id": "Quality"}, {"id": "Pause"}, {"id": "Mute"}, null, {"id": "Find", "label": "Find..."}, {"id": "FindAgain", "label": "Find Again"}, {"id": "Copy"}, {"id": "CopyAgain", "label": "Copy Again"}, {"id": "CopySVG", "label": "Copy SVG"}, {"id": "ViewSVG", "label": "View SVG"}, {"id": "ViewSource", "label": "View Source"}, {"id": "SaveAs", "label": "Save As"}, null, {"id": "Help"}, {"id": "About", "label": "About Adobe CVG Viewer..."} ] }} """ pyparsing2-2.4.7/test/jsonParserTests.pyc000066400000000000000000000255131365333160500205020ustar00rootroot00000000000000 J]c@s"dZdZdZdZdZdS(s { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [{ "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "LargestPrimeLessThan100": 97, "AvogadroNumber": 6.02E23, "EvenPrimesGreaterThan2": null, "PrimesLessThan10" : [2,3,5,7], "WMDsFound" : false, "IraqAlQaedaConnections" : null, "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", "markup"], "EmptyDict" : {}, "EmptyList" : [] }] } } } s {"menu": { "id": "file", "value": "File:", "popup": { "menuitem": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"} ] } }} sN {"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", "name": "main_window", "width": 500, "height": 500 }, "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, "vOffset": 250, "alignment": "center" }, "text": { "data": "Click Here", "size": 36, "style": "bold", "name": "text1", "hOffset": 250, "vOffset": 100, "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } }} s {"web-app": { "servlet": [ // Defines the CDSServlet { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", /* Defines glossary variables that template designers can use across the site. You can add new variables to this set by creating a new init-param, with the param-name prefixed with "configGlossary:". */ "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", /* Defines the template loader and template processor classes. These are implementations of org.cofax.TemplateProcessor and org.cofax.TemplateLoader respectively. Simply create new implementation of these classes and set them here if the default implementations do not suit your needs. Leave these alone for the defaults. */ "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", /* Defines the names of the default templates to look for when acquiring WYSIWYG templates. Leave these at their defaults for most usage. */ "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", /* New! useJSP switches on JSP template processing. jspListTemplate and jspFileTemplate are the names of the default templates to look for when aquiring JSP templates. Cofax currently in production at KR has useJSP set to false, since our sites currently use WYSIWYG templating exclusively. */ "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", /* Defines the packageTag cache. This cache keeps Cofax from needing to interact with the database to look up packageTag commands. */ "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, /* Defines the template cache. Keeps Cofax from needing to go to the file system to load a raw template from the file system. */ "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, /* Defines the page cache. Keeps Cofax from processing templates to deliver to users. */ "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, /* Defines the templates Cofax will use when being browsed by a search engine identified in searchEngineRobotsDb */ "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", /* New! useDataStore enables/disables the Cofax database pool */ "useDataStore": true, /* Defines the implementation of org.cofax.DataStore that Cofax will use. If this DataStore class does not suit your needs simply implement a new DataStore class and set here. */ "dataStoreClass": "org.cofax.SqlDataStore", /* Defines the implementation of org.cofax.Redirection that Cofax will use. If this Redirection class does not suit your needs simply implenet a new Redirection class and set here. */ "redirectionClass": "org.cofax.SqlRedirection", /* Defines the data store name. Keep this at the default */ "dataStoreName": "cofax", /* Defines the JDBC driver that Cofax's database pool will use */ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", /* Defines the JDBC connection URL to connect to the database */ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", /* Defines the user name to connect to the database */ "dataStoreUser": "sa", /* Defines the password to connect to the database */ "dataStorePassword": "dataStoreTestQuery", /* A query that will run to test the validity of the connection in the pool. */ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", /* A log file to print out database information */ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", /* The number of connection to initialize on startup */ "dataStoreInitConns": 10, /* The maximum number of connection to use in the pool */ "dataStoreMaxConns": 100, /* The number of times a connection will be utilized from the pool before disconnect */ "dataStoreConnUsageLimit": 100, /* The level of information to print to the log */ "dataStoreLogLevel": "debug", /* The maximum URL length allowable by the CDS Servlet Helps to prevent hacking */ "maxUrlLength": 500}}, /* Defines the Email Servlet */ { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { /* The mail host to be used by the mail servlet */ "mailHost": "mail1", /* An override */ "mailHostOverride": "mail2"}}, /* Defines the Admin Servlet - used to refresh cache on demand and see statistics */ { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, /* Defines the File Servlet - used to display files like Apache */ { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, { "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { /* Path to the template folder relative to the tools tomcat installation. */ "templatePath": "toolstemplates/", /* Logging boolean 1 = on, 0 = off */ "log": 1, /* Location of log. If empty, log will be written System.out */ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "logMaxSize": "", /* DataStore logging boolean 1 = on, 0 = off */ "dataLog": 1, /* DataStore location of log. If empty, log will be written System.out */ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", /* Max size of log in BITS. If size is empty, no limit to log. If size is defined, log will be overwritten upon reaching defined size. */ "dataLogMaxSize": "", /* Http string relative to server root to call for page cache removal to Cofax Servlet. */ "removePageCache": "/content/admin/remove?cache=pages&id=", /* Http string relative to server root to call for template cache removal to Cofax Servlet. */ "removeTemplateCache": "/content/admin/remove?cache=templates&id=", /* Location of folder from root of drive that will be used for ftp transfer from beta server or user hard drive to live servers. Note that Edit Article will not function without this variable set correctly. MultiPart request relies upon access to this folder. */ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", /* Defines whether the Server should look in another path for config files or variables. */ "lookInContext": 1, /* Number of the ID of the top level administration group in tblPermGroups. */ "adminGroupID": 4, /* Is the tools app running on the 'beta server'. */ "betaServer": true}}], "servlet-mapping": { /* URL mapping for the CDS Servlet */ "cofaxCDS": "/", /* URL mapping for the Email Servlet */ "cofaxEmail": "/cofaxutil/aemail/*", /* URL mapping for the Admin servlet */ "cofaxAdmin": "/admin/*", /* URL mapping for the Files servlet */ "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, /* New! The cofax taglib descriptor file */ "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} sj {"menu": { "header": "SVG Viewer", "items": [ {"id": "Open"}, {"id": "OpenNew", "label": "Open New"}, null, {"id": "ZoomIn", "label": "Zoom In"}, {"id": "ZoomOut", "label": "Zoom Out"}, {"id": "OriginalView", "label": "Original View"}, null, {"id": "Quality"}, {"id": "Pause"}, {"id": "Mute"}, null, {"id": "Find", "label": "Find..."}, {"id": "FindAgain", "label": "Find Again"}, {"id": "Copy"}, {"id": "CopyAgain", "label": "Copy Again"}, {"id": "CopySVG", "label": "Copy SVG"}, {"id": "ViewSVG", "label": "View SVG"}, {"id": "ViewSource", "label": "View Source"}, {"id": "SaveAs", "label": "Save As"}, null, {"id": "Help"}, {"id": "About", "label": "About Adobe CVG Viewer..."} ] }} N(ttest1ttest2ttest3ttest4ttest5(((sC/home/paul/dev/pyparsing/gh-2.4.x/pyparsing/test/jsonParserTests.pyt!s  pyparsing2-2.4.7/test/karthik.ini000066400000000000000000000005501365333160500167440ustar00rootroot00000000000000[users] source_dir = '/home/karthik/Projects/python' data_dir = '/home/karthik/Projects/data' result_dir = '/home/karthik/Projects/Results' param_file = $result_dir/param_file res_file = $result_dir/result_file comment = 'this is a comment' ; a line starting with ';' is a comment K = 8 simulate_K = 0 N = 4000 mod_scheme = 'QPSK' Na = K+2pyparsing2-2.4.7/test/parsefiletest_input_file.txt000066400000000000000000000000131365333160500224310ustar00rootroot00000000000000123 456 789pyparsing2-2.4.7/unitTests.py000066400000000000000000006075131365333160500162170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # unitTests.py # # Unit tests for pyparsing module # # Copyright 2002-2018, Paul McGuire # # from __future__ import division from unittest import TestCase, TestSuite, TextTestRunner import datetime from pyparsing import ParseException, pyparsing_test as ppt import pyparsing as pp import sys PY_3 = sys.version.startswith('3') if PY_3: import builtins print_ = getattr(builtins, "print") # catch calls to builtin print(), should be print_ def printX(*args, **kwargs): raise Exception("Test coding error: using print() directly, should use print_()") globals()['print'] = printX from io import StringIO else: def _print(*args, **kwargs): if 'end' in kwargs: sys.stdout.write(' '.join(map(str,args)) + kwargs['end']) else: sys.stdout.write(' '.join(map(str,args)) + '\n') print_ = _print from cStringIO import StringIO # see which Python implementation we are running CPYTHON_ENV = (sys.platform == "win32") IRON_PYTHON_ENV = (sys.platform == "cli") JYTHON_ENV = sys.platform.startswith("java") TEST_USING_PACKRAT = True #~ TEST_USING_PACKRAT = False VERBOSE = True # simple utility for flattening nested lists def flatten(L): if type(L) is not list: return [L] if L == []: return L return flatten(L[0]) + flatten(L[1:]) """ class ParseTest(TestCase): def setUp(self): pass def runTest(self): self.assertTrue(1==1, "we've got bigger problems...") def tearDown(self): pass """ class AutoReset(object): def __init__(self, *args): ob = args[0] attrnames = args[1:] self.ob = ob self.save_attrs = attrnames self.save_values = [getattr(ob, attrname) for attrname in attrnames] def __enter__(self): pass def __exit__(self, *args): for attr, value in zip(self.save_attrs, self.save_values): setattr(self.ob, attr, value) BUFFER_OUTPUT = True class ParseTestCase(ppt.TestParseResultsAsserts, TestCase): def __init__(self): super(ParseTestCase, self).__init__(methodName='_runTest') self.expect_traceback = False self.expect_warning = False def _runTest(self): buffered_stdout = StringIO() try: with AutoReset(sys, 'stdout', 'stderr'): try: if BUFFER_OUTPUT: sys.stdout = buffered_stdout sys.stderr = buffered_stdout print_(">>>> Starting test",str(self)) with ppt.reset_pyparsing_context(): self.runTest() finally: print_("<<<< End of test",str(self)) print_() output = buffered_stdout.getvalue() if "Traceback" in output and not self.expect_traceback: raise Exception("traceback in stdout") if "Warning" in output and not self.expect_warning: raise Exception("warning in stdout") except Exception as exc: if BUFFER_OUTPUT: print_() print_(buffered_stdout.getvalue()) raise def runTest(self): pass def __str__(self): return self.__class__.__name__ class PyparsingTestInit(ParseTestCase): def setUp(self): from pyparsing import __version__ as pyparsingVersion, __versionTime__ as pyparsingVersionTime print_("Beginning test of pyparsing, version", pyparsingVersion, pyparsingVersionTime) print_("Python version", sys.version) def tearDown(self): pass class ParseFourFnTest(ParseTestCase): def runTest(self): import examples.fourFn as fourFn def test(s,ans): fourFn.exprStack = [] results = fourFn.BNF().parseString( s ) resultValue = fourFn.evaluateStack( fourFn.exprStack ) self.assertTrue(resultValue == ans, "failed to evaluate %s, got %f" % ( s, resultValue )) print_(s, "->", resultValue) from math import pi,exp e = exp(1) test( "9", 9 ) test( "9 + 3 + 6", 18 ) test( "9 + 3 / 11", 9.0+3.0/11.0) test( "(9 + 3)", 12 ) test( "(9+3) / 11", (9.0+3.0)/11.0 ) test( "9 - (12 - 6)", 3) test( "2*3.14159", 6.28318) test( "3.1415926535*3.1415926535 / 10", 3.1415926535*3.1415926535/10.0 ) test( "PI * PI / 10", pi*pi/10.0 ) test( "PI*PI/10", pi*pi/10.0 ) test( "6.02E23 * 8.048", 6.02E23 * 8.048 ) test( "e / 3", e/3.0 ) test( "sin(PI/2)", 1.0 ) test( "trunc(E)", 2.0 ) test( "E^PI", e**pi ) test( "2^3^2", 2**3**2) test( "2^3+2", 2**3+2) test( "2^9", 2**9 ) test( "sgn(-2)", -1 ) test( "sgn(0)", 0 ) test( "sgn(0.1)", 1 ) class ParseSQLTest(ParseTestCase): def runTest(self): import examples.simpleSQL as simpleSQL def test(s, numToks, errloc=-1): try: sqlToks = flatten(simpleSQL.simpleSQL.parseString(s).asList()) print_(s,sqlToks,len(sqlToks)) self.assertEqual(len(sqlToks), numToks, "invalid parsed tokens, expected {0}, found {1} ({2})".format(numToks, len(sqlToks), sqlToks)) except ParseException as e: if errloc >= 0: self.assertEqual(e.loc, errloc, "expected error at {0}, found at {1}".format(errloc, e.loc)) test( "SELECT * from XYZZY, ABC", 6 ) test( "select * from SYS.XYZZY", 5 ) test( "Select A from Sys.dual", 5 ) test( "Select A,B,C from Sys.dual", 7 ) test( "Select A, B, C from Sys.dual", 7 ) test( "Select A, B, C from Sys.dual, Table2 ", 8 ) test( "Xelect A, B, C from Sys.dual", 0, 0 ) test( "Select A, B, C frox Sys.dual", 0, 15 ) test( "Select", 0, 6 ) test( "Select &&& frox Sys.dual", 0, 7 ) test( "Select A from Sys.dual where a in ('RED','GREEN','BLUE')", 12 ) test( "Select A from Sys.dual where a in ('RED','GREEN','BLUE') and b in (10,20,30)", 20 ) test( "Select A,b from table1,table2 where table1.id eq table2.id -- test out comparison operators", 10 ) class ParseConfigFileTest(ParseTestCase): def runTest(self): from examples import configParse def test(fnam,numToks,resCheckList): print_("Parsing",fnam,"...", end=' ') with open(fnam) as infile: iniFileLines = "\n".join(infile.read().splitlines()) iniData = configParse.inifile_BNF().parseString( iniFileLines ) print_(len(flatten(iniData.asList()))) print_(list(iniData.keys())) self.assertEqual(len(flatten(iniData.asList())), numToks, "file %s not parsed correctly" % fnam) for chk in resCheckList: var = iniData for attr in chk[0].split('.'): var = getattr(var, attr) print_(chk[0], var, chk[1]) self.assertEqual(var, chk[1], "ParseConfigFileTest: failed to parse ini {0!r} as expected {1}, found {2}".format(chk[0], chk[1], var)) print_("OK") test("test/karthik.ini", 23, [ ("users.K","8"), ("users.mod_scheme","'QPSK'"), ("users.Na", "K+2") ] ) test("examples/Setup.ini", 125, [ ("Startup.audioinf", "M3i"), ("Languages.key1", "0x0003"), ("test.foo","bar") ] ) class ParseJSONDataTest(ParseTestCase): def runTest(self): from examples.jsonParser import jsonObject from test.jsonParserTests import test1,test2,test3,test4,test5 expected = [ [['glossary', [['title', 'example glossary'], ['GlossDiv', [['title', 'S'], ['GlossList', [[['ID', 'SGML'], ['SortAs', 'SGML'], ['GlossTerm', 'Standard Generalized Markup Language'], ['Acronym', 'SGML'], ['LargestPrimeLessThan100', 97], ['AvogadroNumber', 6.02e+23], ['EvenPrimesGreaterThan2', None], ['PrimesLessThan10', [2, 3, 5, 7]], ['WMDsFound', False], ['IraqAlQaedaConnections', None], ['Abbrev', 'ISO 8879:1986'], ['GlossDef', 'A meta-markup language, used to create markup languages such as ' 'DocBook.'], ['GlossSeeAlso', ['GML', 'XML', 'markup']], ['EmptyDict', []], ['EmptyList', [[]]]]]]]]] ]] , [['menu', [['id', 'file'], ['value', 'File:'], ['popup', [['menuitem', [[['value', 'New'], ['onclick', 'CreateNewDoc()']], [['value', 'Open'], ['onclick', 'OpenDoc()']], [['value', 'Close'], ['onclick', 'CloseDoc()']]]]]]]]] , [['widget', [['debug', 'on'], ['window', [['title', 'Sample Konfabulator Widget'], ['name', 'main_window'], ['width', 500], ['height', 500]]], ['image', [['src', 'Images/Sun.png'], ['name', 'sun1'], ['hOffset', 250], ['vOffset', 250], ['alignment', 'center']]], ['text', [['data', 'Click Here'], ['size', 36], ['style', 'bold'], ['name', 'text1'], ['hOffset', 250], ['vOffset', 100], ['alignment', 'center'], ['onMouseUp', 'sun1.opacity = (sun1.opacity / 100) * 90;']]]]]] , [['web-app', [['servlet', [[['servlet-name', 'cofaxCDS'], ['servlet-class', 'org.cofax.cds.CDSServlet'], ['init-param', [['configGlossary:installationAt', 'Philadelphia, PA'], ['configGlossary:adminEmail', 'ksm@pobox.com'], ['configGlossary:poweredBy', 'Cofax'], ['configGlossary:poweredByIcon', '/images/cofax.gif'], ['configGlossary:staticPath', '/content/static'], ['templateProcessorClass', 'org.cofax.WysiwygTemplate'], ['templateLoaderClass', 'org.cofax.FilesTemplateLoader'], ['templatePath', 'templates'], ['templateOverridePath', ''], ['defaultListTemplate', 'listTemplate.htm'], ['defaultFileTemplate', 'articleTemplate.htm'], ['useJSP', False], ['jspListTemplate', 'listTemplate.jsp'], ['jspFileTemplate', 'articleTemplate.jsp'], ['cachePackageTagsTrack', 200], ['cachePackageTagsStore', 200], ['cachePackageTagsRefresh', 60], ['cacheTemplatesTrack', 100], ['cacheTemplatesStore', 50], ['cacheTemplatesRefresh', 15], ['cachePagesTrack', 200], ['cachePagesStore', 100], ['cachePagesRefresh', 10], ['cachePagesDirtyRead', 10], ['searchEngineListTemplate', 'forSearchEnginesList.htm'], ['searchEngineFileTemplate', 'forSearchEngines.htm'], ['searchEngineRobotsDb', 'WEB-INF/robots.db'], ['useDataStore', True], ['dataStoreClass', 'org.cofax.SqlDataStore'], ['redirectionClass', 'org.cofax.SqlRedirection'], ['dataStoreName', 'cofax'], ['dataStoreDriver', 'com.microsoft.jdbc.sqlserver.SQLServerDriver'], ['dataStoreUrl', 'jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon'], ['dataStoreUser', 'sa'], ['dataStorePassword', 'dataStoreTestQuery'], ['dataStoreTestQuery', "SET NOCOUNT ON;select test='test';"], ['dataStoreLogFile', '/usr/local/tomcat/logs/datastore.log'], ['dataStoreInitConns', 10], ['dataStoreMaxConns', 100], ['dataStoreConnUsageLimit', 100], ['dataStoreLogLevel', 'debug'], ['maxUrlLength', 500]]]], [['servlet-name', 'cofaxEmail'], ['servlet-class', 'org.cofax.cds.EmailServlet'], ['init-param', [['mailHost', 'mail1'], ['mailHostOverride', 'mail2']]]], [['servlet-name', 'cofaxAdmin'], ['servlet-class', 'org.cofax.cds.AdminServlet']], [['servlet-name', 'fileServlet'], ['servlet-class', 'org.cofax.cds.FileServlet']], [['servlet-name', 'cofaxTools'], ['servlet-class', 'org.cofax.cms.CofaxToolsServlet'], ['init-param', [['templatePath', 'toolstemplates/'], ['log', 1], ['logLocation', '/usr/local/tomcat/logs/CofaxTools.log'], ['logMaxSize', ''], ['dataLog', 1], ['dataLogLocation', '/usr/local/tomcat/logs/dataLog.log'], ['dataLogMaxSize', ''], ['removePageCache', '/content/admin/remove?cache=pages&id='], ['removeTemplateCache', '/content/admin/remove?cache=templates&id='], ['fileTransferFolder', '/usr/local/tomcat/webapps/content/fileTransferFolder'], ['lookInContext', 1], ['adminGroupID', 4], ['betaServer', True]]]]]], ['servlet-mapping', [['cofaxCDS', '/'], ['cofaxEmail', '/cofaxutil/aemail/*'], ['cofaxAdmin', '/admin/*'], ['fileServlet', '/static/*'], ['cofaxTools', '/tools/*']]], ['taglib', [['taglib-uri', 'cofax.tld'], ['taglib-location', '/WEB-INF/tlds/cofax.tld']]]]]] , [['menu', [['header', 'SVG Viewer'], ['items', [[['id', 'Open']], [['id', 'OpenNew'], ['label', 'Open New']], None, [['id', 'ZoomIn'], ['label', 'Zoom In']], [['id', 'ZoomOut'], ['label', 'Zoom Out']], [['id', 'OriginalView'], ['label', 'Original View']], None, [['id', 'Quality']], [['id', 'Pause']], [['id', 'Mute']], None, [['id', 'Find'], ['label', 'Find...']], [['id', 'FindAgain'], ['label', 'Find Again']], [['id', 'Copy']], [['id', 'CopyAgain'], ['label', 'Copy Again']], [['id', 'CopySVG'], ['label', 'Copy SVG']], [['id', 'ViewSVG'], ['label', 'View SVG']], [['id', 'ViewSource'], ['label', 'View Source']], [['id', 'SaveAs'], ['label', 'Save As']], None, [['id', 'Help']], [['id', 'About'], ['label', 'About Adobe CVG Viewer...']]]]]]] , ] for t, exp in zip((test1,test2,test3,test4,test5), expected): result = jsonObject.parseString(t) result.pprint() self.assertEqual(result.asList(), exp, "failed test {0}".format(t)) class ParseCommaSeparatedValuesTest(ParseTestCase): def runTest(self): from pyparsing import commaSeparatedList testData = [ "a,b,c,100.2,,3", "d, e, j k , m ", "'Hello, World', f, g , , 5.1,x", "John Doe, 123 Main St., Cleveland, Ohio", "Jane Doe, 456 St. James St., Los Angeles , California ", "", ] testVals = [ [ (3,'100.2'), (4,''), (5, '3') ], [ (2, 'j k'), (3, 'm') ], [ (0, "'Hello, World'"), (2, 'g'), (3, '') ], [ (0,'John Doe'), (1, '123 Main St.'), (2, 'Cleveland'), (3, 'Ohio') ], [ (0,'Jane Doe'), (1, '456 St. James St.'), (2, 'Los Angeles'), (3, 'California') ] ] for line,tests in zip(testData, testVals): print_("Parsing: \""+line+"\" ->", end=' ') results = commaSeparatedList.parseString(line) print_(results.asList()) for t in tests: if not(len(results)>t[0] and results[t[0]] == t[1]): print_("$$$", results.dump()) print_("$$$", results[0]) self.assertTrue(len(results)>t[0] and results[t[0]] == t[1], "failed on %s, item %d s/b '%s', got '%s'" % (line, t[0], t[1], str(results.asList()))) class ParseEBNFTest(ParseTestCase): def runTest(self): from examples import ebnf from pyparsing import Word, quotedString, alphas, nums print_('Constructing EBNF parser with pyparsing...') grammar = ''' syntax = (syntax_rule), {(syntax_rule)}; syntax_rule = meta_identifier, '=', definitions_list, ';'; definitions_list = single_definition, {'|', single_definition}; single_definition = syntactic_term, {',', syntactic_term}; syntactic_term = syntactic_factor,['-', syntactic_factor]; syntactic_factor = [integer, '*'], syntactic_primary; syntactic_primary = optional_sequence | repeated_sequence | grouped_sequence | meta_identifier | terminal_string; optional_sequence = '[', definitions_list, ']'; repeated_sequence = '{', definitions_list, '}'; grouped_sequence = '(', definitions_list, ')'; (* terminal_string = "'", character - "'", {character - "'"}, "'" | '"', character - '"', {character - '"'}, '"'; meta_identifier = letter, {letter | digit}; integer = digit, {digit}; *) ''' table = {} table['terminal_string'] = quotedString table['meta_identifier'] = Word(alphas+"_", alphas+"_"+nums) table['integer'] = Word(nums) print_('Parsing EBNF grammar with EBNF parser...') parsers = ebnf.parse(grammar, table) ebnf_parser = parsers['syntax'] print_("-","\n- ".join(parsers.keys())) self.assertEqual(len(list(parsers.keys())), 13, "failed to construct syntax grammar") print_('Parsing EBNF grammar with generated EBNF parser...') parsed_chars = ebnf_parser.parseString(grammar) parsed_char_len = len(parsed_chars) print_("],\n".join(str( parsed_chars.asList() ).split("],"))) self.assertEqual(len(flatten(parsed_chars.asList())), 98, "failed to tokenize grammar correctly") class ParseIDLTest(ParseTestCase): def runTest(self): from examples import idlParse def test( strng, numToks, errloc=0 ): print_(strng) try: bnf = idlParse.CORBA_IDL_BNF() tokens = bnf.parseString( strng ) print_("tokens = ") tokens.pprint() tokens = flatten( tokens.asList() ) print_(len(tokens)) self.assertEqual(len(tokens), numToks, "error matching IDL string, %s -> %s" % (strng, str(tokens))) except ParseException as err: print_(err.line) print_(" "*(err.column-1) + "^") print_(err) self.assertEqual(numToks, 0, "unexpected ParseException while parsing %s, %s" % (strng, str(err))) self.assertEqual(err.loc, errloc, "expected ParseException at %d, found exception at %d" % (errloc, err.loc)) test( """ /* * a block comment * */ typedef string[10] tenStrings; typedef sequence stringSeq; typedef sequence< sequence > stringSeqSeq; interface QoSAdmin { stringSeq method1( in string arg1, inout long arg2 ); stringSeqSeq method2( in string arg1, inout long arg2, inout long arg3); string method3(); }; """, 59 ) test( """ /* * a block comment * */ typedef string[10] tenStrings; typedef /** ** *** **** * * a block comment * */ sequence /*comment inside an And */ stringSeq; /* */ /**/ /***/ /****/ typedef sequence< sequence > stringSeqSeq; interface QoSAdmin { stringSeq method1( in string arg1, inout long arg2 ); stringSeqSeq method2( in string arg1, inout long arg2, inout long arg3); string method3(); }; """, 59 ) test( r""" const string test="Test String\n"; const long a = 0; const long b = -100; const float c = 3.14159; const long d = 0x007f7f7f; exception TestException { string msg; sequence dataStrings; }; interface TestInterface { void method1( in string arg1, inout long arg2 ); }; """, 60 ) test( """ module Test1 { exception TestException { string msg; ]; interface TestInterface { void method1( in string arg1, inout long arg2 ) raises ( TestException ); }; }; """, 0, 56 ) test( """ module Test1 { exception TestException { string msg; }; }; """, 13 ) class ParseVerilogTest(ParseTestCase): def runTest(self): pass class ScanStringTest(ParseTestCase): def runTest(self): from pyparsing import Word, Combine, Suppress, CharsNotIn, nums, StringEnd testdata = """
Name IP Address Location
time-a.nist.gov 129.6.15.28 NIST, Gaithersburg, Maryland
time-b.nist.gov 129.6.15.29 NIST, Gaithersburg, Maryland
time-a.timefreq.bldrdoc.gov 132.163.4.101 NIST, Boulder, Colorado
time-b.timefreq.bldrdoc.gov 132.163.4.102 NIST, Boulder, Colorado
time-c.timefreq.bldrdoc.gov 132.163.4.103 NIST, Boulder, Colorado
""" integer = Word(nums) ipAddress = Combine( integer + "." + integer + "." + integer + "." + integer ) tdStart = Suppress("") tdEnd = Suppress("") timeServerPattern = (tdStart + ipAddress("ipAddr") + tdEnd + tdStart + CharsNotIn("<")("loc") + tdEnd) servers = [srvr.ipAddr for srvr,startloc,endloc in timeServerPattern.scanString( testdata )] print_(servers) self.assertEqual(servers, ['129.6.15.28', '129.6.15.29', '132.163.4.101', '132.163.4.102', '132.163.4.103'], "failed scanString()") # test for stringEnd detection in scanString foundStringEnds = [ r for r in StringEnd().scanString("xyzzy") ] print_(foundStringEnds) self.assertTrue(foundStringEnds, "Failed to find StringEnd in scanString") class QuotedStringsTest(ParseTestCase): def runTest(self): from pyparsing import sglQuotedString,dblQuotedString,quotedString,QuotedString testData = \ """ 'a valid single quoted string' 'an invalid single quoted string because it spans lines' "a valid double quoted string" "an invalid double quoted string because it spans lines" """ print_(testData) sglStrings = [(t[0],b,e) for (t,b,e) in sglQuotedString.scanString(testData)] print_(sglStrings) self.assertTrue(len(sglStrings) == 1 and (sglStrings[0][1] == 17 and sglStrings[0][2] == 47), "single quoted string failure") dblStrings = [(t[0],b,e) for (t,b,e) in dblQuotedString.scanString(testData)] print_(dblStrings) self.assertTrue(len(dblStrings) == 1 and (dblStrings[0][1] == 154 and dblStrings[0][2] == 184), "double quoted string failure") allStrings = [(t[0],b,e) for (t,b,e) in quotedString.scanString(testData)] print_(allStrings) self.assertTrue(len(allStrings) == 2 and (allStrings[0][1] == 17 and allStrings[0][2] == 47) and (allStrings[1][1] == 154 and allStrings[1][2] == 184), "quoted string failure") escapedQuoteTest = \ r""" 'This string has an escaped (\') quote character' "This string has an escaped (\") quote character" """ sglStrings = [(t[0],b,e) for (t,b,e) in sglQuotedString.scanString(escapedQuoteTest)] print_(sglStrings) self.assertTrue(len(sglStrings) == 1 and (sglStrings[0][1]==17 and sglStrings[0][2]==66), "single quoted string escaped quote failure (%s)" % str(sglStrings[0])) dblStrings = [(t[0],b,e) for (t,b,e) in dblQuotedString.scanString(escapedQuoteTest)] print_(dblStrings) self.assertTrue(len(dblStrings) == 1 and (dblStrings[0][1]==83 and dblStrings[0][2]==132), "double quoted string escaped quote failure (%s)" % str(dblStrings[0])) allStrings = [(t[0],b,e) for (t,b,e) in quotedString.scanString(escapedQuoteTest)] print_(allStrings) self.assertTrue(len(allStrings) == 2 and (allStrings[0][1] == 17 and allStrings[0][2] == 66 and allStrings[1][1] == 83 and allStrings[1][2] == 132), "quoted string escaped quote failure (%s)" % ([str(s[0]) for s in allStrings])) dblQuoteTest = \ r""" 'This string has an doubled ('') quote character' "This string has an doubled ("") quote character" """ sglStrings = [(t[0],b,e) for (t,b,e) in sglQuotedString.scanString(dblQuoteTest)] print_(sglStrings) self.assertTrue(len(sglStrings) == 1 and (sglStrings[0][1]==17 and sglStrings[0][2]==66), "single quoted string escaped quote failure (%s)" % str(sglStrings[0])) dblStrings = [(t[0],b,e) for (t,b,e) in dblQuotedString.scanString(dblQuoteTest)] print_(dblStrings) self.assertTrue(len(dblStrings) == 1 and (dblStrings[0][1]==83 and dblStrings[0][2]==132), "double quoted string escaped quote failure (%s)" % str(dblStrings[0])) allStrings = [(t[0],b,e) for (t,b,e) in quotedString.scanString(dblQuoteTest)] print_(allStrings) self.assertTrue(len(allStrings) == 2 and (allStrings[0][1] == 17 and allStrings[0][2] == 66 and allStrings[1][1] == 83 and allStrings[1][2] == 132), "quoted string escaped quote failure (%s)" % ([str(s[0]) for s in allStrings])) print_("testing catastrophic RE backtracking in implementation of dblQuotedString") for expr, test_string in [ (dblQuotedString, '"' + '\\xff' * 500), (sglQuotedString, "'" + '\\xff' * 500), (quotedString, '"' + '\\xff' * 500), (quotedString, "'" + '\\xff' * 500), (QuotedString('"'), '"' + '\\xff' * 500), (QuotedString("'"), "'" + '\\xff' * 500), ]: expr.parseString(test_string+test_string[0]) try: expr.parseString(test_string) except Exception: continue class CaselessOneOfTest(ParseTestCase): def runTest(self): from pyparsing import oneOf,ZeroOrMore caseless1 = oneOf("d a b c aA B A C", caseless=True) caseless1str = str( caseless1 ) print_(caseless1str) caseless2 = oneOf("d a b c Aa B A C", caseless=True) caseless2str = str( caseless2 ) print_(caseless2str) self.assertEqual(caseless1str.upper(), caseless2str.upper(), "oneOf not handling caseless option properly") self.assertNotEqual(caseless1str, caseless2str, "Caseless option properly sorted") res = ZeroOrMore(caseless1).parseString("AAaaAaaA") print_(res) self.assertEqual(len(res), 4, "caseless1 oneOf failed") self.assertEqual("".join(res), "aA"*4,"caseless1 CaselessLiteral return failed") res = ZeroOrMore(caseless2).parseString("AAaaAaaA") print_(res) self.assertEqual(len(res), 4, "caseless2 oneOf failed") self.assertEqual("".join(res), "Aa"*4,"caseless1 CaselessLiteral return failed") class AsXMLTest(ParseTestCase): def runTest(self): # test asXML() aaa = pp.Word("a")("A") bbb = pp.Group(pp.Word("b"))("B") ccc = pp.Combine(":" + pp.Word("c"))("C") g1 = "XXX>&<" + pp.ZeroOrMore( aaa | bbb | ccc ) teststring = "XXX>&< b b a b b a b :c b a" #~ print teststring print_("test including all items") xml = g1.parseString(teststring).asXML("TEST",namedItemsOnly=False) assert xml=="\n".join(["", "", " XXX>&<", " ", " b", " ", " ", " b", " ", " a", " ", " b", " ", " ", " b", " ", " a", " ", " b", " ", " :c", " ", " b", " ", " a", "", ] ), \ "failed to generate XML correctly showing all items: \n[" + xml + "]" print_("test filtering unnamed items") xml = g1.parseString(teststring).asXML("TEST",namedItemsOnly=True) assert xml=="\n".join(["", "", " ", " b", " ", " ", " b", " ", " a", " ", " b", " ", " ", " b", " ", " a", " ", " b", " ", " :c", " ", " b", " ", " a", "", ] ), \ "failed to generate XML correctly, filtering unnamed items: " + xml class AsXMLTest2(ParseTestCase): def runTest(self): from pyparsing import Suppress,Optional,CharsNotIn,Combine,ZeroOrMore,Word,\ Group,Literal,alphas,alphanums,delimitedList,OneOrMore EndOfLine = Word("\n").setParseAction(lambda s,l,t: [' ']) whiteSpace=Word('\t ') Mexpr = Suppress(Optional(whiteSpace)) + CharsNotIn('\\"\t \n') + Optional(" ") + \ Suppress(Optional(whiteSpace)) reducedString = Combine(Mexpr + ZeroOrMore(EndOfLine + Mexpr)) _bslash = "\\" _escapables = "tnrfbacdeghijklmopqsuvwxyz" + _bslash + "'" + '"' _octDigits = "01234567" _escapedChar = ( Word( _bslash, _escapables, exact=2 ) | Word( _bslash, _octDigits, min=2, max=4 ) ) _sglQuote = Literal("'") _dblQuote = Literal('"') QuotedReducedString = Combine( Suppress(_dblQuote) + ZeroOrMore( reducedString | _escapedChar ) + \ Suppress(_dblQuote )).streamline() Manifest_string = QuotedReducedString('manifest_string') Identifier = Word( alphas, alphanums+ '_$' )("identifier") Index_string = CharsNotIn('\\";\n') Index_string.setName('index_string') Index_term_list = ( Group(delimitedList(Manifest_string, delim=',')) | \ Index_string )('value') IndexKey = Identifier('key') IndexKey.setName('key') Index_clause = Group(IndexKey + Suppress(':') + Optional(Index_term_list)) Index_clause.setName('index_clause') Index_list = Index_clause('index') Index_list.setName('index_list') Index_block = Group('indexing' + Group(OneOrMore(Index_list + Suppress(';'))))('indexes') class CommentParserTest(ParseTestCase): def runTest(self): print_("verify processing of C and HTML comments") testdata = """ /* */ /** **/ /**/ /***/ /****/ /* /*/ /** /*/ /*** /*/ /* ablsjdflj */ """ foundLines = [ pp.lineno(s,testdata) for t,s,e in pp.cStyleComment.scanString(testdata) ] self.assertEqual(foundLines, list(range(11))[2:],"only found C comments on lines "+str(foundLines)) testdata = """ """ foundLines = [ pp.lineno(s,testdata) for t,s,e in pp.htmlComment.scanString(testdata) ] self.assertEqual(foundLines, list(range(11))[2:],"only found HTML comments on lines "+str(foundLines)) # test C++ single line comments that have line terminated with '\' (should continue comment to following line) testSource = r""" // comment1 // comment2 \ still comment 2 // comment 3 """ self.assertEqual(len(pp.cppStyleComment.searchString(testSource)[1][0]), 41, r"failed to match single-line comment with '\' at EOL") class ParseExpressionResultsTest(ParseTestCase): def runTest(self): from pyparsing import Word,alphas,OneOrMore,Optional,Group a = Word("a", alphas).setName("A") b = Word("b", alphas).setName("B") c = Word("c", alphas).setName("C") ab = (a + b).setName("AB") abc = (ab + c).setName("ABC") word = Word(alphas).setName("word") words = Group(OneOrMore(~a + word)).setName("words") phrase = (words("Head") + Group(a + Optional(b + Optional(c)))("ABC") + words("Tail")) results = phrase.parseString("xavier yeti alpha beta charlie will beaver") print_(results,results.Head, results.ABC,results.Tail) for key,ln in [("Head",2), ("ABC",3), ("Tail",2)]: self.assertEqual(len(results[key]), ln, "expected %d elements in %s, found %s" % (ln, key, str(results[key]))) class ParseKeywordTest(ParseTestCase): def runTest(self): from pyparsing import Literal,Keyword kw = Keyword("if") lit = Literal("if") def test(s,litShouldPass,kwShouldPass): print_("Test",s) print_("Match Literal", end=' ') try: print_(lit.parseString(s)) except Exception: print_("failed") if litShouldPass: self.assertTrue(False, "Literal failed to match %s, should have" % s) else: if not litShouldPass: self.assertTrue(False, "Literal matched %s, should not have" % s) print_("Match Keyword", end=' ') try: print_(kw.parseString(s)) except Exception: print_("failed") if kwShouldPass: self.assertTrue(False, "Keyword failed to match %s, should have" % s) else: if not kwShouldPass: self.assertTrue(False, "Keyword matched %s, should not have" % s) test("ifOnlyIfOnly", True, False) test("if(OnlyIfOnly)", True, True) test("if (OnlyIf Only)", True, True) kw = Keyword("if",caseless=True) test("IFOnlyIfOnly", False, False) test("If(OnlyIfOnly)", False, True) test("iF (OnlyIf Only)", False, True) class ParseExpressionResultsAccumulateTest(ParseTestCase): def runTest(self): from pyparsing import Word,delimitedList,Combine,alphas,nums num=Word(nums).setName("num")("base10*") hexnum=Combine("0x"+ Word(nums)).setName("hexnum")("hex*") name = Word(alphas).setName("word")("word*") list_of_num=delimitedList( hexnum | num | name, "," ) tokens = list_of_num.parseString('1, 0x2, 3, 0x4, aaa') for k,llen,lst in ( ("base10",2,['1','3']), ("hex",2,['0x2','0x4']), ("word",1,['aaa']) ): print_(k,tokens[k]) self.assertEqual(len(tokens[k]), llen, "Wrong length for key %s, %s" % (k,str(tokens[k].asList()))) self.assertEqual(lst, tokens[k].asList(), "Incorrect list returned for key %s, %s" % (k,str(tokens[k].asList()))) self.assertEqual(tokens.base10.asList(), ['1','3'], "Incorrect list for attribute base10, %s" % str(tokens.base10.asList())) self.assertEqual(tokens.hex.asList(), ['0x2','0x4'], "Incorrect list for attribute hex, %s" % str(tokens.hex.asList())) self.assertEqual(tokens.word.asList(), ['aaa'], "Incorrect list for attribute word, %s" % str(tokens.word.asList())) from pyparsing import Literal, Word, nums, Group, Dict, alphas, \ quotedString, oneOf, delimitedList, removeQuotes, alphanums lbrack = Literal("(").suppress() rbrack = Literal(")").suppress() integer = Word( nums ).setName("int") variable = Word( alphas, max=1 ).setName("variable") relation_body_item = variable | integer | quotedString.copy().setParseAction(removeQuotes) relation_name = Word( alphas+"_", alphanums+"_" ) relation_body = lbrack + Group(delimitedList(relation_body_item)) + rbrack Goal = Dict(Group( relation_name + relation_body )) Comparison_Predicate = Group(variable + oneOf("< >") + integer)("pred*") Query = Goal("head") + ":-" + delimitedList(Goal | Comparison_Predicate) test="""Q(x,y,z):-Bloo(x,"Mitsis",y),Foo(y,z,1243),y>28,x<12,x>3""" queryRes = Query.parseString(test) print_("pred",queryRes.pred) self.assertEqual(queryRes.pred.asList(), [['y', '>', '28'], ['x', '<', '12'], ['x', '>', '3']], "Incorrect list for attribute pred, %s" % str(queryRes.pred.asList())) print_(queryRes.dump()) class ReStringRangeTest(ParseTestCase): def runTest(self): testCases = ( (r"[A-Z]"), (r"[A-A]"), (r"[A-Za-z]"), (r"[A-z]"), (r"[\ -\~]"), (r"[\0x20-0]"), (r"[\0x21-\0x7E]"), (r"[\0xa1-\0xfe]"), (r"[\040-0]"), (r"[A-Za-z0-9]"), (r"[A-Za-z0-9_]"), (r"[A-Za-z0-9_$]"), (r"[A-Za-z0-9_$\-]"), (r"[^0-9\\]"), (r"[a-zA-Z]"), (r"[/\^~]"), (r"[=\+\-!]"), (r"[A-]"), (r"[-A]"), (r"[\x21]"), #(r"[а-яА-ЯёЁA-Z$_\041α-ω]".decode('utf-8')), (u'[\u0430-\u044f\u0410-\u042f\u0451\u0401ABCDEFGHIJKLMNOPQRSTUVWXYZ$_\041\u03b1-\u03c9]'), ) expectedResults = ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "A", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz", " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", " !\"#$%&'()*+,-./0", "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", #~ "¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ", u'\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe', " !\"#$%&'()*+,-./0", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$-", "0123456789\\", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "/^~", "=+-!", "A-", "-A", "!", u"абвгдежзийклмнопрстуфхцчшщъыьэюяАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯёЁABCDEFGHIJKLMNOPQRSTUVWXYZ$_!αβγδεζηθικλμνξοπρςστυφχψω", ) for test in zip( testCases, expectedResults ): t,exp = test res = pp.srange(t) #print_(t,"->",res) self.assertEqual(res, exp, "srange error, srange(%r)->'%r', expected '%r'" % (t, res, exp)) class SkipToParserTests(ParseTestCase): def runTest(self): from pyparsing import Literal, SkipTo, cStyleComment, ParseBaseException, And, Word, alphas, nums, Optional, NotAny thingToFind = Literal('working') testExpr = SkipTo(Literal(';'), include=True, ignore=cStyleComment) + thingToFind def tryToParse (someText, fail_expected=False): try: print_(testExpr.parseString(someText)) self.assertFalse(fail_expected, "expected failure but no exception raised") except Exception as e: print_("Exception %s while parsing string %s" % (e,repr(someText))) self.assertTrue(fail_expected and isinstance(e,ParseBaseException), "Exception %s while parsing string %s" % (e,repr(someText))) # This first test works, as the SkipTo expression is immediately following the ignore expression (cStyleComment) tryToParse('some text /* comment with ; in */; working') # This second test previously failed, as there is text following the ignore expression, and before the SkipTo expression. tryToParse('some text /* comment with ; in */some other stuff; working') # tests for optional failOn argument testExpr = SkipTo(Literal(';'), include=True, ignore=cStyleComment, failOn='other') + thingToFind tryToParse('some text /* comment with ; in */; working') tryToParse('some text /* comment with ; in */some other stuff; working', fail_expected=True) # test that we correctly create named results text = "prefixDATAsuffix" data = Literal("DATA") suffix = Literal("suffix") expr = SkipTo(data + suffix)('prefix') + data + suffix result = expr.parseString(text) self.assertTrue(isinstance(result.prefix, str), "SkipTo created with wrong saveAsList attribute") if PY_3: def define_expr(s): from pyparsing import Literal, And, Word, alphas, nums, Optional, NotAny alpha_word = (~Literal("end") + Word(alphas, asKeyword=True)).setName("alpha") num_word = Word(nums, asKeyword=True).setName("int") ret = eval(s) ret.streamline() print_(ret) return ret def test(expr, test_string, expected_list, expected_dict): try: result = expr.parseString(test_string) except Exception as pe: if any(expected is not None for expected in (expected_list, expected_dict)): self.assertTrue(False, "{} failed to parse {!r}".format(expr, test_string)) else: self.assertEqual(result.asList(), expected_list) self.assertEqual(result.asDict(), expected_dict) # ellipses for SkipTo # (use eval() to avoid syntax problems when running in Py2) e = define_expr('... + Literal("end")') test(e, "start 123 end", ['start 123 ', 'end'], {'_skipped': ['start 123 ']}) e = define_expr('Literal("start") + ... + Literal("end")') test(e, "start 123 end", ['start', '123 ', 'end'], {'_skipped': ['123 ']}) e = define_expr('Literal("start") + ...') test(e, "start 123 end", None, None) e = define_expr('And(["start", ..., "end"])') test(e, "start 123 end", ['start', '123 ', 'end'], {'_skipped': ['123 ']}) e = define_expr('And([..., "end"])') test(e, "start 123 end", ['start 123 ', 'end'], {'_skipped': ['start 123 ']}) e = define_expr('"start" + (num_word | ...) + "end"') test(e, "start 456 end", ['start', '456', 'end'], {}) test(e, "start 123 456 end", ['start', '123', '456 ', 'end'], {'_skipped': ['456 ']}) test(e, "start end", ['start', '', 'end'], {'_skipped': ['missing ']}) # e = define_expr('"start" + (num_word | ...)("inner") + "end"') # test(e, "start 456 end", ['start', '456', 'end'], {'inner': '456'}) e = define_expr('"start" + (alpha_word[...] & num_word[...] | ...) + "end"') test(e, "start 456 red end", ['start', '456', 'red', 'end'], {}) test(e, "start red 456 end", ['start', 'red', '456', 'end'], {}) test(e, "start 456 red + end", ['start', '456', 'red', '+ ', 'end'], {'_skipped': ['+ ']}) test(e, "start red end", ['start', 'red', 'end'], {}) test(e, "start 456 end", ['start', '456', 'end'], {}) test(e, "start end", ['start', 'end'], {}) test(e, "start 456 + end", ['start', '456', '+ ', 'end'], {'_skipped': ['+ ']}) e = define_expr('"start" + (alpha_word[1, ...] & num_word[1, ...] | ...) + "end"') test(e, "start 456 red end", ['start', '456', 'red', 'end'], {}) test(e, "start red 456 end", ['start', 'red', '456', 'end'], {}) test(e, "start 456 red + end", ['start', '456', 'red', '+ ', 'end'], {'_skipped': ['+ ']}) test(e, "start red end", ['start', 'red ', 'end'], {'_skipped': ['red ']}) test(e, "start 456 end", ['start', '456 ', 'end'], {'_skipped': ['456 ']}) test(e, "start end", ['start', '', 'end'], {'_skipped': ['missing <{{alpha}... & {int}...}>']}) test(e, "start 456 + end", ['start', '456 + ', 'end'], {'_skipped': ['456 + ']}) e = define_expr('"start" + (alpha_word | ...) + (num_word | ...) + "end"') test(e, "start red 456 end", ['start', 'red', '456', 'end'], {}) test(e, "start red end", ['start', 'red', '', 'end'], {'_skipped': ['missing ']}) test(e, "start end", ['start', '', '', 'end'], {'_skipped': ['missing ', 'missing ']}) e = define_expr('Literal("start") + ... + "+" + ... + "end"') test(e, "start red + 456 end", ['start', 'red ', '+', '456 ', 'end'], {'_skipped': ['red ', '456 ']}) class EllipsisRepetionTest(ParseTestCase): def runTest(self): import pyparsing as pp import re word = pp.Word(pp.alphas).setName("word") num = pp.Word(pp.nums).setName("num") exprs = [ word[...] + num, word[0, ...] + num, word[1, ...] + num, word[2, ...] + num, word[..., 3] + num, word[2] + num, ] expected_res = [ r"([abcd]+ )*\d+", r"([abcd]+ )*\d+", r"([abcd]+ )+\d+", r"([abcd]+ ){2,}\d+", r"([abcd]+ ){0,3}\d+", r"([abcd]+ ){2}\d+", ] tests = [ "aa bb cc dd 123", "bb cc dd 123", "cc dd 123", "dd 123", "123", ] all_success = True for expr, expected_re in zip(exprs, expected_res): successful_tests = [t for t in tests if re.match(expected_re, t)] failure_tests = [t for t in tests if not re.match(expected_re, t)] success1, _ = expr.runTests(successful_tests) success2, _ = expr.runTests(failure_tests, failureTests=True) all_success = all_success and success1 and success2 if not all_success: print_("Failed expression:", expr) break self.assertTrue(all_success, "failed getItem_ellipsis test") class CustomQuotesTest(ParseTestCase): def runTest(self): self.expect_warning = True from pyparsing import QuotedString testString = r""" sdlfjs :sdf\:jls::djf: sl:kfsjf sdlfjs -sdf\:jls::--djf: sl-kfsjf sdlfjs -sdf\:::jls::--djf: sl:::-kfsjf sdlfjs ^sdf\:jls^^--djf^ sl-kfsjf sdlfjs ^^^==sdf\:j=lz::--djf: sl=^^=kfsjf sdlfjs ==sdf\:j=ls::--djf: sl==kfsjf^^^ """ colonQuotes = QuotedString(':','\\','::') dashQuotes = QuotedString('-','\\', '--') hatQuotes = QuotedString('^','\\') hatQuotes1 = QuotedString('^','\\','^^') dblEqQuotes = QuotedString('==','\\') def test(quoteExpr, expected): print_(quoteExpr.pattern) print_(quoteExpr.searchString(testString)) print_(quoteExpr.searchString(testString)[0][0]) print_(expected) self.assertEqual(quoteExpr.searchString(testString)[0][0], expected, "failed to match %s, expected '%s', got '%s'" % (quoteExpr, expected, quoteExpr.searchString(testString)[0])) print_() test(colonQuotes, r"sdf:jls:djf") test(dashQuotes, r"sdf:jls::-djf: sl") test(hatQuotes, r"sdf:jls") test(hatQuotes1, r"sdf:jls^--djf") test(dblEqQuotes, r"sdf:j=ls::--djf: sl") test(QuotedString(':::'), 'jls::--djf: sl') test(QuotedString('==',endQuoteChar='--'), r'sdf\:j=lz::') test(QuotedString('^^^',multiline=True), r"""==sdf\:j=lz::--djf: sl=^^=kfsjf sdlfjs ==sdf\:j=ls::--djf: sl==kfsjf""") try: bad1 = QuotedString('','\\') except SyntaxError as se: pass else: self.assertTrue(False,"failed to raise SyntaxError with empty quote string") class RepeaterTest(ParseTestCase): def runTest(self): from pyparsing import matchPreviousLiteral,matchPreviousExpr, Word, nums, ParserElement if ParserElement._packratEnabled: print_("skipping this test, not compatible with packratting") return first = Word("abcdef").setName("word1") bridge = Word(nums).setName("number") second = matchPreviousLiteral(first).setName("repeat(word1Literal)") seq = first + bridge + second tests = [ ( "abc12abc", True ), ( "abc12aabc", False ), ( "abc12cba", True ), ( "abc12bca", True ), ] for tst,result in tests: found = False for tokens,start,end in seq.scanString(tst): f,b,s = tokens print_(f,b,s) found = True if not found: print_("No literal match in", tst) self.assertEqual(found, result, "Failed repeater for test: %s, matching %s" % (tst, str(seq))) print_() # retest using matchPreviousExpr instead of matchPreviousLiteral second = matchPreviousExpr(first).setName("repeat(word1expr)") seq = first + bridge + second tests = [ ( "abc12abc", True ), ( "abc12cba", False ), ( "abc12abcdef", False ), ] for tst,result in tests: found = False for tokens,start,end in seq.scanString(tst): print_(tokens.asList()) found = True if not found: print_("No expression match in", tst) self.assertEqual(found, result, "Failed repeater for test: %s, matching %s" % (tst, str(seq))) print_() first = Word("abcdef").setName("word1") bridge = Word(nums).setName("number") second = matchPreviousExpr(first).setName("repeat(word1)") seq = first + bridge + second csFirst = seq.setName("word-num-word") csSecond = matchPreviousExpr(csFirst) compoundSeq = csFirst + ":" + csSecond compoundSeq.streamline() print_(compoundSeq) tests = [ ( "abc12abc:abc12abc", True ), ( "abc12cba:abc12abc", False ), ( "abc12abc:abc12abcdef", False ), ] for tst, result in tests: found = False for tokens, start, end in compoundSeq.scanString(tst): print_("match:", tokens.asList()) found = True break if not found: print_("No expression match in", tst) self.assertEqual(found, result, "Failed repeater for test: %s, matching %s" % (tst, str(seq))) print_() eFirst = Word(nums) eSecond = matchPreviousExpr(eFirst) eSeq = eFirst + ":" + eSecond tests = [ ( "1:1A", True ), ( "1:10", False ), ] for tst,result in tests: found = False for tokens,start,end in eSeq.scanString(tst): print_(tokens.asList()) found = True if not found: print_("No match in", tst) self.assertEqual(found, result, "Failed repeater for test: %s, matching %s" % (tst, str(seq))) class RecursiveCombineTest(ParseTestCase): def runTest(self): from pyparsing import Forward,Word,alphas,nums,Optional,Combine testInput = "myc(114)r(11)dd" Stream=Forward() Stream << Optional(Word(alphas))+Optional("("+Word(nums)+")"+Stream) expected = Stream.parseString(testInput).asList() print_(["".join(expected)]) Stream=Forward() Stream << Combine(Optional(Word(alphas))+Optional("("+Word(nums)+")"+Stream)) testVal = Stream.parseString(testInput).asList() print_(testVal) self.assertEqual("".join(testVal), "".join(expected), "Failed to process Combine with recursive content") class InfixNotationGrammarTest1(ParseTestCase): def runTest(self): from pyparsing import Word,nums,alphas,Literal,oneOf,infixNotation,opAssoc import ast integer = Word(nums).setParseAction(lambda t:int(t[0])) variable = Word(alphas,exact=1) operand = integer | variable expop = Literal('^') signop = oneOf('+ -') multop = oneOf('* /') plusop = oneOf('+ -') factop = Literal('!') expr = infixNotation( operand, [(factop, 1, opAssoc.LEFT), (expop, 2, opAssoc.RIGHT), (signop, 1, opAssoc.RIGHT), (multop, 2, opAssoc.LEFT), (plusop, 2, opAssoc.LEFT),] ) test = ["9 + 2 + 3", "9 + 2 * 3", "(9 + 2) * 3", "(9 + -2) * 3", "(9 + --2) * 3", "(9 + -2) * 3^2^2", "(9! + -2) * 3^2^2", "M*X + B", "M*(X + B)", "1+2*-3^4*5+-+-6", "3!!"] expected = """[[9, '+', 2, '+', 3]] [[9, '+', [2, '*', 3]]] [[[9, '+', 2], '*', 3]] [[[9, '+', ['-', 2]], '*', 3]] [[[9, '+', ['-', ['-', 2]]], '*', 3]] [[[9, '+', ['-', 2]], '*', [3, '^', [2, '^', 2]]]] [[[[9, '!'], '+', ['-', 2]], '*', [3, '^', [2, '^', 2]]]] [[['M', '*', 'X'], '+', 'B']] [['M', '*', ['X', '+', 'B']]] [[1, '+', [2, '*', ['-', [3, '^', 4]], '*', 5], '+', ['-', ['+', ['-', 6]]]]] [[3, '!', '!']]""".split('\n') expected = [ast.literal_eval(x.strip()) for x in expected] for t,e in zip(test,expected): print_(t,"->",e, "got", expr.parseString(t).asList()) self.assertEqual(expr.parseString(t).asList(), e, "mismatched results for infixNotation: got %s, expected %s" % (expr.parseString(t).asList(),e)) class InfixNotationGrammarTest2(ParseTestCase): def runTest(self): from pyparsing import infixNotation, Word, alphas, oneOf, opAssoc boolVars = { "True":True, "False":False } class BoolOperand(object): reprsymbol = '' def __init__(self,t): self.args = t[0][0::2] def __str__(self): sep = " %s " % self.reprsymbol return "(" + sep.join(map(str,self.args)) + ")" class BoolAnd(BoolOperand): reprsymbol = '&' def __bool__(self): for a in self.args: if isinstance(a,str): v = boolVars[a] else: v = bool(a) if not v: return False return True class BoolOr(BoolOperand): reprsymbol = '|' def __bool__(self): for a in self.args: if isinstance(a,str): v = boolVars[a] else: v = bool(a) if v: return True return False class BoolNot(BoolOperand): def __init__(self,t): self.arg = t[0][1] def __str__(self): return "~" + str(self.arg) def __bool__(self): if isinstance(self.arg,str): v = boolVars[self.arg] else: v = bool(self.arg) return not v boolOperand = Word(alphas,max=1) | oneOf("True False") boolExpr = infixNotation( boolOperand, [ ("not", 1, opAssoc.RIGHT, BoolNot), ("and", 2, opAssoc.LEFT, BoolAnd), ("or", 2, opAssoc.LEFT, BoolOr), ]) test = ["p and not q", "not not p", "not(p and q)", "q or not p and r", "q or not p or not r", "q or not (p and r)", "p or q or r", "p or q or r and False", "(p or q or r) and False", ] boolVars["p"] = True boolVars["q"] = False boolVars["r"] = True print_("p =", boolVars["p"]) print_("q =", boolVars["q"]) print_("r =", boolVars["r"]) print_() for t in test: res = boolExpr.parseString(t)[0] print_(t,'\n', res, '=', bool(res),'\n') class InfixNotationGrammarTest3(ParseTestCase): def runTest(self): from pyparsing import infixNotation, Word, alphas, oneOf, opAssoc, nums, Literal global count count = 0 def evaluate_int(t): global count value = int(t[0]) print_("evaluate_int", value) count += 1 return value integer = Word(nums).setParseAction(evaluate_int) variable = Word(alphas,exact=1) operand = integer | variable expop = Literal('^') signop = oneOf('+ -') multop = oneOf('* /') plusop = oneOf('+ -') factop = Literal('!') expr = infixNotation( operand, [ ("!", 1, opAssoc.LEFT), ("^", 2, opAssoc.LEFT), (signop, 1, opAssoc.RIGHT), (multop, 2, opAssoc.LEFT), (plusop, 2, opAssoc.LEFT), ]) test = ["9"] for t in test: count = 0 print_("%r => %s (count=%d)" % (t, expr.parseString(t), count)) self.assertEqual(count, 1, "count evaluated too many times!") class InfixNotationGrammarTest4(ParseTestCase): def runTest(self): word = pp.Word(pp.alphas) def supLiteral(s): """Returns the suppressed literal s""" return pp.Literal(s).suppress() def booleanExpr(atom): ops = [ (supLiteral("!"), 1, pp.opAssoc.RIGHT, lambda s, l, t: ["!", t[0][0]]), (pp.oneOf("= !="), 2, pp.opAssoc.LEFT, ), (supLiteral("&"), 2, pp.opAssoc.LEFT, lambda s, l, t: ["&", t[0]]), (supLiteral("|"), 2, pp.opAssoc.LEFT, lambda s, l, t: ["|", t[0]])] return pp.infixNotation(atom, ops) f = booleanExpr(word) + pp.StringEnd() tests = [ ("bar = foo", "[['bar', '=', 'foo']]"), ("bar = foo & baz = fee", "['&', [['bar', '=', 'foo'], ['baz', '=', 'fee']]]"), ] for test,expected in tests: print_(test) results = f.parseString(test) print_(results) self.assertEqual(str(results), expected, "failed to match expected results, got '%s'" % str(results)) print_() class InfixNotationGrammarTest5(ParseTestCase): def runTest(self): from pyparsing import infixNotation, opAssoc, pyparsing_common as ppc, Literal, oneOf expop = Literal('**') signop = oneOf('+ -') multop = oneOf('* /') plusop = oneOf('+ -') class ExprNode(object): def __init__(self, tokens): self.tokens = tokens[0] def eval(self): return None class NumberNode(ExprNode): def eval(self): return self.tokens class SignOp(ExprNode): def eval(self): mult = {'+': 1, '-': -1}[self.tokens[0]] return mult * self.tokens[1].eval() class BinOp(ExprNode): def eval(self): ret = self.tokens[0].eval() for op, operand in zip(self.tokens[1::2], self.tokens[2::2]): ret = self.opn_map[op](ret, operand.eval()) return ret class ExpOp(BinOp): opn_map = {'**': lambda a, b: b ** a} class MultOp(BinOp): import operator opn_map = {'*': operator.mul, '/': operator.truediv} class AddOp(BinOp): import operator opn_map = {'+': operator.add, '-': operator.sub} operand = ppc.number().setParseAction(NumberNode) expr = infixNotation(operand, [ (expop, 2, opAssoc.LEFT, (lambda pr: [pr[0][::-1]], ExpOp)), (signop, 1, opAssoc.RIGHT, SignOp), (multop, 2, opAssoc.LEFT, MultOp), (plusop, 2, opAssoc.LEFT, AddOp), ]) tests = """\ 2+7 2**3 2**3**2 3**9 3**3**2 """ for t in tests.splitlines(): t = t.strip() if not t: continue parsed = expr.parseString(t) eval_value = parsed[0].eval() self.assertEqual(eval_value, eval(t), "Error evaluating %r, expected %r, got %r" % (t, eval(t), eval_value)) class PickleTest_Greeting(): def __init__(self, toks): self.salutation = toks[0] self.greetee = toks[1] def __repr__(self): return "%s: {%s}" % (self.__class__.__name__, ', '.join('%r: %r' % (k, getattr(self,k)) for k in sorted(self.__dict__))) class ParseResultsPickleTest(ParseTestCase): def runTest(self): from pyparsing import makeHTMLTags, ParseResults import pickle # test 1 body = makeHTMLTags("BODY")[0] result = body.parseString("") if VERBOSE: print_(result.dump()) print_() for protocol in range(pickle.HIGHEST_PROTOCOL+1): print_("Test pickle dump protocol", protocol) try: pickleString = pickle.dumps(result, protocol) except Exception as e: print_("dumps exception:", e) newresult = ParseResults() else: newresult = pickle.loads(pickleString) if VERBOSE: print_(newresult.dump()) print_() self.assertEqual(result.dump(), newresult.dump(), "Error pickling ParseResults object (protocol=%d)" % protocol) # test 2 import pyparsing as pp word = pp.Word(pp.alphas+"'.") salutation = pp.OneOrMore(word) comma = pp.Literal(",") greetee = pp.OneOrMore(word) endpunc = pp.oneOf("! ?") greeting = salutation + pp.Suppress(comma) + greetee + pp.Suppress(endpunc) greeting.setParseAction(PickleTest_Greeting) string = 'Good morning, Miss Crabtree!' result = greeting.parseString(string) for protocol in range(pickle.HIGHEST_PROTOCOL+1): print_("Test pickle dump protocol", protocol) try: pickleString = pickle.dumps(result, protocol) except Exception as e: print_("dumps exception:", e) newresult = ParseResults() else: newresult = pickle.loads(pickleString) print_(newresult.dump()) self.assertEqual(newresult.dump(), result.dump(), "failed to pickle/unpickle ParseResults: expected %r, got %r" % (result, newresult)) class ParseResultsWithNamedTupleTest(ParseTestCase): def runTest(self): from pyparsing import Literal,replaceWith expr = Literal("A")("Achar") expr.setParseAction(replaceWith(tuple(["A","Z"]))) res = expr.parseString("A") print_(repr(res)) print_(res.Achar) self.assertEqual(res.Achar, ("A","Z"), "Failed accessing named results containing a tuple, got {0!r}".format(res.Achar)) class ParseHTMLTagsTest(ParseTestCase): def runTest(self): test = """ """ results = [ ("startBody", False, "", ""), ("startBody", False, "#00FFCC", ""), ("startBody", True, "#00FFAA", ""), ("startBody", False, "#00FFBB", "black"), ("startBody", True, "", ""), ("endBody", False, "", ""), ] bodyStart, bodyEnd = pp.makeHTMLTags("BODY") resIter = iter(results) for t,s,e in (bodyStart | bodyEnd).scanString( test ): print_(test[s:e], "->", t.asList()) (expectedType, expectedEmpty, expectedBG, expectedFG) = next(resIter) print_(t.dump()) if "startBody" in t: self.assertEqual(bool(t.empty), expectedEmpty, "expected %s token, got %s" % (expectedEmpty and "empty" or "not empty", t.empty and "empty" or "not empty")) self.assertEqual(t.bgcolor, expectedBG, "failed to match BGCOLOR, expected %s, got %s" % (expectedBG, t.bgcolor)) self.assertEqual(t.fgcolor, expectedFG, "failed to match FGCOLOR, expected %s, got %s" % (expectedFG, t.bgcolor)) elif "endBody" in t: print_("end tag") pass else: print_("BAD!!!") class UpcaseDowncaseUnicode(ParseTestCase): def runTest(self): import pyparsing as pp from pyparsing import pyparsing_unicode as ppu import sys if PY_3: unichr = chr else: from __builtin__ import unichr a = u'\u00bfC\u00f3mo esta usted?' if not JYTHON_ENV: ualphas = ppu.alphas else: ualphas = "".join( unichr(i) for i in list(range(0xd800)) + list(range(0xe000,sys.maxunicode)) if unichr(i).isalpha() ) uword = pp.Word(ualphas).setParseAction(pp.upcaseTokens) print_ = lambda *args: None print_(uword.searchString(a)) uword = pp.Word(ualphas).setParseAction(pp.downcaseTokens) print_(uword.searchString(a)) kw = pp.Keyword('mykey', caseless=True).setParseAction(pp.upcaseTokens)('rname') ret = kw.parseString('mykey') print_(ret.rname) self.assertEqual(ret.rname, 'MYKEY', "failed to upcase with named result") kw = pp.Keyword('mykey', caseless=True).setParseAction(pp.pyparsing_common.upcaseTokens)('rname') ret = kw.parseString('mykey') print_(ret.rname) self.assertEqual(ret.rname, 'MYKEY', "failed to upcase with named result (pyparsing_common)") kw = pp.Keyword('MYKEY', caseless=True).setParseAction(pp.pyparsing_common.downcaseTokens)('rname') ret = kw.parseString('mykey') print_(ret.rname) self.assertEqual(ret.rname, 'mykey', "failed to upcase with named result") if not IRON_PYTHON_ENV: #test html data html = u" \ Производитель, модель \ BenQ-Siemens CF61 \ "#.decode('utf-8') # u'Manufacturer, model text_manuf = u'Производитель, модель' manufacturer = pp.Literal(text_manuf) td_start, td_end = pp.makeHTMLTags("td") manuf_body = td_start.suppress() + manufacturer + pp.SkipTo(td_end)("cells*") + td_end.suppress() #~ manuf_body.setDebug() #~ for tokens in manuf_body.scanString(html): #~ print_(tokens) class ParseUsingRegex(ParseTestCase): def runTest(self): self.expect_warning = True import re signedInt = pp.Regex(r'[-+][0-9]+') unsignedInt = pp.Regex(r'[0-9]+') simpleString = pp.Regex(r'("[^\"]*")|(\'[^\']*\')') namedGrouping = pp.Regex(r'("(?P[^\"]*)")') compiledRE = pp.Regex(re.compile(r'[A-Z]+')) def testMatch (expression, instring, shouldPass, expectedString=None): if shouldPass: try: result = expression.parseString(instring) print_('%s correctly matched %s' % (repr(expression), repr(instring))) if expectedString != result[0]: print_('\tbut failed to match the pattern as expected:') print_('\tproduced %s instead of %s' % \ (repr(result[0]), repr(expectedString))) return True except pp.ParseException: print_('%s incorrectly failed to match %s' % \ (repr(expression), repr(instring))) else: try: result = expression.parseString(instring) print_('%s incorrectly matched %s' % (repr(expression), repr(instring))) print_('\tproduced %s as a result' % repr(result[0])) except pp.ParseException: print_('%s correctly failed to match %s' % \ (repr(expression), repr(instring))) return True return False # These should fail self.assertTrue(testMatch(signedInt, '1234 foo', False), "Re: (1) passed, expected fail") self.assertTrue(testMatch(signedInt, ' +foo', False), "Re: (2) passed, expected fail") self.assertTrue(testMatch(unsignedInt, 'abc', False), "Re: (3) passed, expected fail") self.assertTrue(testMatch(unsignedInt, '+123 foo', False), "Re: (4) passed, expected fail") self.assertTrue(testMatch(simpleString, 'foo', False), "Re: (5) passed, expected fail") self.assertTrue(testMatch(simpleString, '"foo bar\'', False), "Re: (6) passed, expected fail") self.assertTrue(testMatch(simpleString, '\'foo bar"', False), "Re: (7) passed, expected fail") # These should pass self.assertTrue(testMatch(signedInt, ' +123', True, '+123'), "Re: (8) failed, expected pass") self.assertTrue(testMatch(signedInt, '+123', True, '+123'), "Re: (9) failed, expected pass") self.assertTrue(testMatch(signedInt, '+123 foo', True, '+123'), "Re: (10) failed, expected pass") self.assertTrue(testMatch(signedInt, '-0 foo', True, '-0'), "Re: (11) failed, expected pass") self.assertTrue(testMatch(unsignedInt, '123 foo', True, '123'), "Re: (12) failed, expected pass") self.assertTrue(testMatch(unsignedInt, '0 foo', True, '0'), "Re: (13) failed, expected pass") self.assertTrue(testMatch(simpleString, '"foo"', True, '"foo"'), "Re: (14) failed, expected pass") self.assertTrue(testMatch(simpleString, "'foo bar' baz", True, "'foo bar'"), "Re: (15) failed, expected pass") self.assertTrue(testMatch(compiledRE, 'blah', False), "Re: (16) passed, expected fail") self.assertTrue(testMatch(compiledRE, 'BLAH', True, 'BLAH'), "Re: (17) failed, expected pass") self.assertTrue(testMatch(namedGrouping, '"foo bar" baz', True, '"foo bar"'), "Re: (16) failed, expected pass") ret = namedGrouping.parseString('"zork" blah') print_(ret.asList()) print_(list(ret.items())) print_(ret.content) self.assertEqual(ret.content, 'zork', "named group lookup failed") self.assertEqual(ret[0], simpleString.parseString('"zork" blah')[0], "Regex not properly returning ParseResults for named vs. unnamed groups") try: #~ print "lets try an invalid RE" invRe = pp.Regex('("[^\"]*")|(\'[^\']*\'') except Exception as e: print_("successfully rejected an invalid RE:", end=' ') print_(e) else: self.assertTrue(False, "failed to reject invalid RE") invRe = pp.Regex('') class RegexAsTypeTest(ParseTestCase): def runTest(self): import pyparsing as pp test_str = "sldkjfj 123 456 lsdfkj" print_("return as list of match groups") expr = pp.Regex(r"\w+ (\d+) (\d+) (\w+)", asGroupList=True) expected_group_list = [tuple(test_str.split()[1:])] result = expr.parseString(test_str) print_(result.dump()) print_(expected_group_list) self.assertEqual(result.asList(), expected_group_list, "incorrect group list returned by Regex)") print_("return as re.match instance") expr = pp.Regex(r"\w+ (?P\d+) (?P\d+) (?P\w+)", asMatch=True) result = expr.parseString(test_str) print_(result.dump()) print_(result[0].groups()) print_(expected_group_list) self.assertEqual(result[0].groupdict(), {'num1': '123', 'num2': '456', 'last_word': 'lsdfkj'}, 'invalid group dict from Regex(asMatch=True)') self.assertEqual(result[0].groups(), expected_group_list[0], "incorrect group list returned by Regex(asMatch)") class RegexSubTest(ParseTestCase): def runTest(self): self.expect_warning = True import pyparsing as pp print_("test sub with string") expr = pp.Regex(r"").sub("'Richard III'") result = expr.transformString("This is the title: <title>") print_(result) self.assertEqual(result, "This is the title: 'Richard III'", "incorrect Regex.sub result with simple string") print_("test sub with re string") expr = pp.Regex(r"([Hh]\d):\s*(.*)").sub(r"<\1>\2</\1>") result = expr.transformString("h1: This is the main heading\nh2: This is the sub-heading") print_(result) self.assertEqual(result, '<h1>This is the main heading</h1>\n<h2>This is the sub-heading</h2>', "incorrect Regex.sub result with re string") print_("test sub with re string (Regex returns re.match)") expr = pp.Regex(r"([Hh]\d):\s*(.*)", asMatch=True).sub(r"<\1>\2</\1>") result = expr.transformString("h1: This is the main heading\nh2: This is the sub-heading") print_(result) self.assertEqual(result, '<h1>This is the main heading</h1>\n<h2>This is the sub-heading</h2>', "incorrect Regex.sub result with re string") print_("test sub with callable that return str") expr = pp.Regex(r"<(.*?)>").sub(lambda m: m.group(1).upper()) result = expr.transformString("I want this in upcase: <what? what?>") print_(result) self.assertEqual(result, 'I want this in upcase: WHAT? WHAT?', "incorrect Regex.sub result with callable") try: expr = pp.Regex(r"<(.*?)>", asMatch=True).sub(lambda m: m.group(1).upper()) except SyntaxError: pass else: self.assertTrue(False, "failed to warn using a Regex.sub(callable) with asMatch=True") try: expr = pp.Regex(r"<(.*?)>", asGroupList=True).sub(lambda m: m.group(1).upper()) except SyntaxError: pass else: self.assertTrue(False, "failed to warn using a Regex.sub() with asGroupList=True") try: expr = pp.Regex(r"<(.*?)>", asGroupList=True).sub("") except SyntaxError: pass else: self.assertTrue(False, "failed to warn using a Regex.sub() with asGroupList=True") class PrecededByTest(ParseTestCase): def runTest(self): import pyparsing as pp num = pp.Word(pp.nums).setParseAction(lambda t: int(t[0])) interesting_num = pp.PrecededBy(pp.Char("abc")("prefix*")) + num semi_interesting_num = pp.PrecededBy('_') + num crazy_num = pp.PrecededBy(pp.Word("^", "$%^")("prefix*"), 10) + num boring_num = ~pp.PrecededBy(pp.Char("abc_$%^" + pp.nums)) + num very_boring_num = pp.PrecededBy(pp.WordStart()) + num finicky_num = pp.PrecededBy(pp.Word("^", "$%^"), retreat=3) + num s = "c384 b8324 _9293874 _293 404 $%^$^%$2939" print_(s) for expr, expected_list, expected_dict in [ (interesting_num, [384, 8324], {'prefix': ['c', 'b']}), (semi_interesting_num, [9293874, 293], {}), (boring_num, [404], {}), (crazy_num, [2939], {'prefix': ['^%$']}), (finicky_num, [2939], {}), (very_boring_num, [404], {}), ]: print_(expr.searchString(s)) result = sum(expr.searchString(s)) print_(result) self.assertEqual(result.asList(), expected_list, "Erroneous tokens for {0}: expected {1}, got {2}".format(expr, expected_list, result.asList())) self.assertEqual(result.asDict(), expected_dict, "Erroneous named results for {0}: expected {1}, got {2}".format(expr, expected_dict, result.asDict())) # infinite loop test - from Issue #127 string_test = 'notworking' # negs = pp.Or(['not', 'un'])('negs') negs_pb = pp.PrecededBy('not', retreat=100)('negs_lb') # negs_pb = pp.PrecededBy(negs, retreat=100)('negs_lb') pattern = pp.Group(negs_pb + pp.Literal('working'))('main') results = pattern.searchString(string_test) try: print_(results.dump()) except RecursionError: self.assertTrue(False, "got maximum excursion limit exception") else: self.assertTrue(True, "got maximum excursion limit exception") class CountedArrayTest(ParseTestCase): def runTest(self): from pyparsing import Word,nums,OneOrMore,countedArray testString = "2 5 7 6 0 1 2 3 4 5 0 3 5 4 3" integer = Word(nums).setParseAction(lambda t: int(t[0])) countedField = countedArray(integer) r = OneOrMore(countedField).parseString( testString ) print_(testString) print_(r.asList()) self.assertEqual(r.asList(), [[5,7],[0,1,2,3,4,5],[],[5,4,3]], "Failed matching countedArray, got " + str(r.asList())) class CountedArrayTest2(ParseTestCase): # addresses bug raised by Ralf Vosseler def runTest(self): from pyparsing import Word,nums,OneOrMore,countedArray testString = "2 5 7 6 0 1 2 3 4 5 0 3 5 4 3" integer = Word(nums).setParseAction(lambda t: int(t[0])) countedField = countedArray(integer) dummy = Word("A") r = OneOrMore(dummy ^ countedField).parseString( testString ) print_(testString) print_(r.asList()) self.assertEqual(r.asList(), [[5,7],[0,1,2,3,4,5],[],[5,4,3]], "Failed matching countedArray, got " + str(r.asList())) class CountedArrayTest3(ParseTestCase): # test case where counter is not a decimal integer def runTest(self): from pyparsing import Word,nums,OneOrMore,countedArray,alphas int_chars = "_"+alphas array_counter = Word(int_chars).setParseAction(lambda t: int_chars.index(t[0])) # 123456789012345678901234567890 testString = "B 5 7 F 0 1 2 3 4 5 _ C 5 4 3" integer = Word(nums).setParseAction(lambda t: int(t[0])) countedField = countedArray(integer, intExpr=array_counter) r = OneOrMore(countedField).parseString( testString ) print_(testString) print_(r.asList()) self.assertEqual(r.asList(), [[5,7],[0,1,2,3,4,5],[],[5,4,3]], "Failed matching countedArray, got " + str(r.asList())) class LineStartTest(ParseTestCase): def runTest(self): import pyparsing as pp pass_tests = [ """\ AAA BBB """, """\ AAA... BBB """, ] fail_tests = [ """\ AAA... ...BBB """, """\ AAA BBB """, ] # cleanup test strings pass_tests = ['\n'.join(s.lstrip() for s in t.splitlines()).replace('.', ' ') for t in pass_tests] fail_tests = ['\n'.join(s.lstrip() for s in t.splitlines()).replace('.', ' ') for t in fail_tests] test_patt = pp.Word('A') - pp.LineStart() + pp.Word('B') print_(test_patt.streamline()) success = test_patt.runTests(pass_tests)[0] self.assertTrue(success, "failed LineStart passing tests (1)") success = test_patt.runTests(fail_tests, failureTests=True)[0] self.assertTrue(success, "failed LineStart failure mode tests (1)") with AutoReset(pp.ParserElement, "DEFAULT_WHITE_CHARS"): print_(r'no \n in default whitespace chars') pp.ParserElement.setDefaultWhitespaceChars(' ') test_patt = pp.Word('A') - pp.LineStart() + pp.Word('B') print_(test_patt.streamline()) # should fail the pass tests too, since \n is no longer valid whitespace and we aren't parsing for it success = test_patt.runTests(pass_tests, failureTests=True)[0] self.assertTrue(success, "failed LineStart passing tests (2)") success = test_patt.runTests(fail_tests, failureTests=True)[0] self.assertTrue(success, "failed LineStart failure mode tests (2)") test_patt = pp.Word('A') - pp.LineEnd().suppress() + pp.LineStart() + pp.Word('B') + pp.LineEnd().suppress() print_(test_patt.streamline()) success = test_patt.runTests(pass_tests)[0] self.assertTrue(success, "failed LineStart passing tests (3)") success = test_patt.runTests(fail_tests, failureTests=True)[0] self.assertTrue(success, "failed LineStart failure mode tests (3)") test = """\ AAA 1 AAA 2 AAA B AAA """ from textwrap import dedent test = dedent(test) print_(test) for t, s, e in (pp.LineStart() + 'AAA').scanString(test): print_(s, e, pp.lineno(s, test), pp.line(s, test), ord(test[s])) print_() self.assertEqual(test[s], 'A', 'failed LineStart with insignificant newlines') with AutoReset(pp.ParserElement, "DEFAULT_WHITE_CHARS"): pp.ParserElement.setDefaultWhitespaceChars(' ') for t, s, e in (pp.LineStart() + 'AAA').scanString(test): print_(s, e, pp.lineno(s, test), pp.line(s, test), ord(test[s])) print_() self.assertEqual(test[s], 'A', 'failed LineStart with insignificant newlines') class LineAndStringEndTest(ParseTestCase): def runTest(self): from pyparsing import OneOrMore,lineEnd,alphanums,Word,stringEnd,delimitedList,SkipTo NLs = OneOrMore(lineEnd) bnf1 = delimitedList(Word(alphanums).leaveWhitespace(), NLs) bnf2 = Word(alphanums) + stringEnd bnf3 = Word(alphanums) + SkipTo(stringEnd) tests = [ ("testA\ntestB\ntestC\n", ['testA', 'testB', 'testC']), ("testD\ntestE\ntestF", ['testD', 'testE', 'testF']), ("a", ['a']), ] for test,expected in tests: res1 = bnf1.parseString(test) print_(res1,'=?',expected) self.assertEqual(res1.asList(), expected, "Failed lineEnd/stringEnd test (1): "+repr(test)+ " -> "+str(res1.asList())) res2 = bnf2.searchString(test)[0] print_(res2.asList(),'=?',expected[-1:]) self.assertEqual(res2.asList(), expected[-1:], "Failed lineEnd/stringEnd test (2): "+repr(test)+ " -> "+str(res2.asList())) res3 = bnf3.parseString(test) first = res3[0] rest = res3[1] #~ print res3.dump() print_(repr(rest),'=?',repr(test[len(first)+1:])) self.assertEqual(rest, test[len(first)+1:], "Failed lineEnd/stringEnd test (3): " +repr(test)+ " -> "+str(res3.asList())) print_() from pyparsing import Regex import re k = Regex(r'a+',flags=re.S+re.M) k = k.parseWithTabs() k = k.leaveWhitespace() tests = [ (r'aaa',['aaa']), (r'\naaa',None), (r'a\naa',None), (r'aaa\n',None), ] for i,(src,expected) in enumerate(tests): print_(i, repr(src).replace('\\\\','\\'), end=' ') try: res = k.parseString(src, parseAll=True).asList() except ParseException as pe: res = None print_(res) self.assertEqual(res, expected, "Failed on parseAll=True test %d" % i) class VariableParseActionArgsTest(ParseTestCase): def runTest(self): pa3 = lambda s,l,t: t pa2 = lambda l,t: t pa1 = lambda t: t pa0 = lambda : None class Callable3(object): def __call__(self,s,l,t): return t class Callable2(object): def __call__(self,l,t): return t class Callable1(object): def __call__(self,t): return t class Callable0(object): def __call__(self): return class CallableS3(object): #~ @staticmethod def __call__(s,l,t): return t __call__=staticmethod(__call__) class CallableS2(object): #~ @staticmethod def __call__(l,t): return t __call__=staticmethod(__call__) class CallableS1(object): #~ @staticmethod def __call__(t): return t __call__=staticmethod(__call__) class CallableS0(object): #~ @staticmethod def __call__(): return __call__=staticmethod(__call__) class CallableC3(object): #~ @classmethod def __call__(cls,s,l,t): return t __call__=classmethod(__call__) class CallableC2(object): #~ @classmethod def __call__(cls,l,t): return t __call__=classmethod(__call__) class CallableC1(object): #~ @classmethod def __call__(cls,t): return t __call__=classmethod(__call__) class CallableC0(object): #~ @classmethod def __call__(cls): return __call__=classmethod(__call__) class parseActionHolder(object): #~ @staticmethod def pa3(s,l,t): return t pa3=staticmethod(pa3) #~ @staticmethod def pa2(l,t): return t pa2=staticmethod(pa2) #~ @staticmethod def pa1(t): return t pa1=staticmethod(pa1) #~ @staticmethod def pa0(): return pa0=staticmethod(pa0) def paArgs(*args): print_(args) return args[2] class ClassAsPA0(object): def __init__(self): pass def __str__(self): return "A" class ClassAsPA1(object): def __init__(self,t): print_("making a ClassAsPA1") self.t = t def __str__(self): return self.t[0] class ClassAsPA2(object): def __init__(self,l,t): self.t = t def __str__(self): return self.t[0] class ClassAsPA3(object): def __init__(self,s,l,t): self.t = t def __str__(self): return self.t[0] class ClassAsPAStarNew(tuple): def __new__(cls, *args): print_("make a ClassAsPAStarNew", args) return tuple.__new__(cls, *args[2].asList()) def __str__(self): return ''.join(self) from pyparsing import Literal,OneOrMore A = Literal("A").setParseAction(pa0) B = Literal("B").setParseAction(pa1) C = Literal("C").setParseAction(pa2) D = Literal("D").setParseAction(pa3) E = Literal("E").setParseAction(Callable0()) F = Literal("F").setParseAction(Callable1()) G = Literal("G").setParseAction(Callable2()) H = Literal("H").setParseAction(Callable3()) I = Literal("I").setParseAction(CallableS0()) J = Literal("J").setParseAction(CallableS1()) K = Literal("K").setParseAction(CallableS2()) L = Literal("L").setParseAction(CallableS3()) M = Literal("M").setParseAction(CallableC0()) N = Literal("N").setParseAction(CallableC1()) O = Literal("O").setParseAction(CallableC2()) P = Literal("P").setParseAction(CallableC3()) Q = Literal("Q").setParseAction(paArgs) R = Literal("R").setParseAction(parseActionHolder.pa3) S = Literal("S").setParseAction(parseActionHolder.pa2) T = Literal("T").setParseAction(parseActionHolder.pa1) U = Literal("U").setParseAction(parseActionHolder.pa0) V = Literal("V") gg = OneOrMore( A | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | U | V | B | T) testString = "VUTSRQPONMLKJIHGFEDCBA" res = gg.parseString(testString) print_(res.asList()) self.assertEqual(res.asList(), list(testString), "Failed to parse using variable length parse actions") A = Literal("A").setParseAction(ClassAsPA0) B = Literal("B").setParseAction(ClassAsPA1) C = Literal("C").setParseAction(ClassAsPA2) D = Literal("D").setParseAction(ClassAsPA3) E = Literal("E").setParseAction(ClassAsPAStarNew) gg = OneOrMore( A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V) testString = "VUTSRQPONMLKJIHGFEDCBA" res = gg.parseString(testString) print_(list(map(str,res))) self.assertEqual(list(map(str,res)), list(testString), "Failed to parse using variable length parse actions " "using class constructors as parse actions") class EnablePackratParsing(ParseTestCase): def runTest(self): from pyparsing import ParserElement ParserElement.enablePackrat() class SingleArgExceptionTest(ParseTestCase): def runTest(self): from pyparsing import ParseBaseException,ParseFatalException msg = "" raisedMsg = "" testMessage = "just one arg" try: raise ParseFatalException(testMessage) except ParseBaseException as pbe: print_("Received expected exception:", pbe) raisedMsg = pbe.msg self.assertEqual(raisedMsg, testMessage, "Failed to get correct exception message") class OriginalTextForTest(ParseTestCase): def runTest(self): from pyparsing import makeHTMLTags, originalTextFor def rfn(t): return "%s:%d" % (t.src, len("".join(t))) makeHTMLStartTag = lambda tag: originalTextFor(makeHTMLTags(tag)[0], asString=False) # use the lambda, Luke start = makeHTMLStartTag('IMG') # don't replace our fancy parse action with rfn, # append rfn to the list of parse actions start.addParseAction(rfn) text = '''_<img src="images/cal.png" alt="cal image" width="16" height="15">_''' s = start.transformString(text) if VERBOSE: print_(s) self.assertTrue(s.startswith("_images/cal.png:"), "failed to preserve input s properly") self.assertTrue(s.endswith("77_"),"failed to return full original text properly") tag_fields = makeHTMLStartTag("IMG").searchString(text)[0] if VERBOSE: print_(sorted(tag_fields.keys())) self.assertEqual(sorted(tag_fields.keys()), ['alt', 'empty', 'height', 'src', 'startImg', 'tag', 'width'], 'failed to preserve results names in originalTextFor') class PackratParsingCacheCopyTest(ParseTestCase): def runTest(self): from pyparsing import Word,nums,delimitedList,Literal,Optional,alphas,alphanums,ZeroOrMore,empty integer = Word(nums).setName("integer") id = Word(alphas+'_',alphanums+'_') simpleType = Literal('int'); arrayType= simpleType+ZeroOrMore('['+delimitedList(integer)+']') varType = arrayType | simpleType varDec = varType + delimitedList(id + Optional('='+integer))+';' codeBlock = Literal('{}') funcDef = Optional(varType | 'void')+id+'('+(delimitedList(varType+id)|'void'|empty)+')'+codeBlock program = varDec | funcDef input = 'int f(){}' results = program.parseString(input) print_("Parsed '%s' as %s" % (input, results.asList())) self.assertEqual(results.asList(), ['int', 'f', '(', ')', '{}'], "Error in packrat parsing") class PackratParsingCacheCopyTest2(ParseTestCase): def runTest(self): from pyparsing import Keyword,Word,Suppress,Forward,Optional,delimitedList,Group DO,AA = list(map(Keyword, "DO AA".split())) LPAR,RPAR = list(map(Suppress,"()")) identifier = ~AA + Word("Z") function_name = identifier.copy() #~ function_name = ~AA + Word("Z") #identifier.copy() expr = Forward().setName("expr") expr << (Group(function_name + LPAR + Optional(delimitedList(expr)) + RPAR).setName("functionCall") | identifier.setName("ident")#.setDebug()#.setBreak() ) stmt = DO + Group(delimitedList(identifier + ".*" | expr)) result = stmt.parseString("DO Z") print_(result.asList()) self.assertEqual(len(result[1]), 1, "packrat parsing is duplicating And term exprs") class ParseResultsDelTest(ParseTestCase): def runTest(self): from pyparsing import OneOrMore, Word, alphas, nums grammar = OneOrMore(Word(nums))("ints") + OneOrMore(Word(alphas))("words") res = grammar.parseString("123 456 ABC DEF") print_(res.dump()) origInts = res.ints.asList() origWords = res.words.asList() del res[1] del res["words"] print_(res.dump()) self.assertEqual(res[1], 'ABC',"failed to delete 0'th element correctly") self.assertEqual(res.ints.asList(), origInts, "updated named attributes, should have updated list only") self.assertEqual(res.words, "", "failed to update named attribute correctly") self.assertEqual(res[-1], 'DEF', "updated list, should have updated named attributes only") class WithAttributeParseActionTest(ParseTestCase): def runTest(self): """ This unit test checks withAttribute in these ways: * Argument forms as keywords and tuples * Selecting matching tags by attribute * Case-insensitive attribute matching * Correctly matching tags having the attribute, and rejecting tags not having the attribute (Unit test written by voigts as part of the Google Highly Open Participation Contest) """ from pyparsing import makeHTMLTags, Word, withAttribute, withClass, nums data = """ <a>1</a> <a b="x">2</a> <a B="x">3</a> <a b="X">4</a> <a b="y">5</a> <a class="boo">8</ a> """ tagStart, tagEnd = makeHTMLTags("a") expr = tagStart + Word(nums)("value") + tagEnd expected = ([['a', ['b', 'x'], False, '2', '</a>'], ['a', ['b', 'x'], False, '3', '</a>']], [['a', ['b', 'x'], False, '2', '</a>'], ['a', ['b', 'x'], False, '3', '</a>']], [['a', ['class', 'boo'], False, '8', '</a>']], ) for attrib, exp in zip([ withAttribute(b="x"), #withAttribute(B="x"), withAttribute(("b","x")), #withAttribute(("B","x")), withClass("boo"), ], expected): tagStart.setParseAction(attrib) result = expr.searchString(data) print_(result.dump()) self.assertEqual(result.asList(), exp, "Failed test, expected %s, got %s" % (expected, result.asList())) class NestedExpressionsTest(ParseTestCase): def runTest(self): """ This unit test checks nestedExpr in these ways: - use of default arguments - use of non-default arguments (such as a pyparsing-defined comment expression in place of quotedString) - use of a custom content expression - use of a pyparsing expression for opener and closer is *OPTIONAL* - use of input data containing nesting delimiters - correct grouping of parsed tokens according to nesting of opening and closing delimiters in the input string (Unit test written by christoph... as part of the Google Highly Open Participation Contest) """ from pyparsing import nestedExpr, Literal, Regex, restOfLine, quotedString #All defaults. Straight out of the example script. Also, qualifies for #the bonus: note the fact that (Z | (E^F) & D) is not parsed :-). # Tests for bug fixed in 1.4.10 print_("Test defaults:") teststring = "(( ax + by)*C) (Z | (E^F) & D)" expr = nestedExpr() expected = [[['ax', '+', 'by'], '*C']] result = expr.parseString(teststring) print_(result.dump()) self.assertEqual(result.asList(), expected, "Defaults didn't work. That's a bad sign. Expected: %s, got: %s" % (expected, result)) #Going through non-defaults, one by one; trying to think of anything #odd that might not be properly handled. #Change opener print_("\nNon-default opener") opener = "[" teststring = test_string = "[[ ax + by)*C)" expected = [[['ax', '+', 'by'], '*C']] expr = nestedExpr("[") result = expr.parseString(teststring) print_(result.dump()) self.assertEqual(result.asList(), expected, "Non-default opener didn't work. Expected: %s, got: %s" % (expected, result)) #Change closer print_("\nNon-default closer") teststring = test_string = "(( ax + by]*C]" expected = [[['ax', '+', 'by'], '*C']] expr = nestedExpr(closer="]") result = expr.parseString(teststring) print_(result.dump()) self.assertEqual(result.asList(), expected, "Non-default closer didn't work. Expected: %s, got: %s" % (expected, result)) # #Multicharacter opener, closer # opener = "bar" # closer = "baz" print_("\nLiteral expressions for opener and closer") opener,closer = list(map(Literal, "bar baz".split())) expr = nestedExpr(opener, closer, content=Regex(r"([^b ]|b(?!a)|ba(?![rz]))+")) teststring = "barbar ax + bybaz*Cbaz" expected = [[['ax', '+', 'by'], '*C']] # expr = nestedExpr(opener, closer) result = expr.parseString(teststring) print_(result.dump()) self.assertEqual(result.asList(), expected, "Multicharacter opener and closer didn't work. Expected: %s, got: %s" % (expected, result)) #Lisp-ish comments print_("\nUse ignore expression (1)") comment = Regex(r";;.*") teststring = \ """ (let ((greeting "Hello, world!")) ;;(foo bar (display greeting)) """ expected = [['let', [['greeting', '"Hello,', 'world!"']], ';;(foo bar',\ ['display', 'greeting']]] expr = nestedExpr(ignoreExpr=comment) result = expr.parseString(teststring) print_(result.dump()) self.assertEqual(result.asList(), expected , "Lisp-ish comments (\";; <...> $\") didn't work. Expected: %s, got: %s" % (expected, result)) #Lisp-ish comments, using a standard bit of pyparsing, and an Or. print_("\nUse ignore expression (2)") comment = ';;' + restOfLine teststring = \ """ (let ((greeting "Hello, )world!")) ;;(foo bar (display greeting)) """ expected = [['let', [['greeting', '"Hello, )world!"']], ';;', '(foo bar', ['display', 'greeting']]] expr = nestedExpr(ignoreExpr=(comment ^ quotedString)) result = expr.parseString(teststring) print_(result.dump()) self.assertEqual(result.asList(), expected , "Lisp-ish comments (\";; <...> $\") and quoted strings didn't work. Expected: %s, got: %s" % (expected, result)) class WordExcludeTest(ParseTestCase): def runTest(self): from pyparsing import Word, printables allButPunc = Word(printables, excludeChars=".,:;-_!?") test = "Hello, Mr. Ed, it's Wilbur!" result = allButPunc.searchString(test).asList() print_(result) self.assertEqual(result, [['Hello'], ['Mr'], ['Ed'], ["it's"], ['Wilbur']], "failed WordExcludeTest") class ParseAllTest(ParseTestCase): def runTest(self): from pyparsing import Word, cppStyleComment testExpr = Word("A") tests = [ ("AAAAA", False, True), ("AAAAA", True, True), ("AAABB", False, True), ("AAABB", True, False), ] for s,parseAllFlag,shouldSucceed in tests: try: print_("'%s' parseAll=%s (shouldSuceed=%s)" % (s, parseAllFlag, shouldSucceed)) testExpr.parseString(s,parseAllFlag) self.assertTrue(shouldSucceed, "successfully parsed when should have failed") except ParseException as pe: self.assertFalse(shouldSucceed, "failed to parse when should have succeeded") # add test for trailing comments testExpr.ignore(cppStyleComment) tests = [ ("AAAAA //blah", False, True), ("AAAAA //blah", True, True), ("AAABB //blah", False, True), ("AAABB //blah", True, False), ] for s,parseAllFlag,shouldSucceed in tests: try: print_("'%s' parseAll=%s (shouldSucceed=%s)" % (s, parseAllFlag, shouldSucceed)) testExpr.parseString(s,parseAllFlag) self.assertTrue(shouldSucceed, "successfully parsed when should have failed") except ParseException as pe: self.assertFalse(shouldSucceed, "failed to parse when should have succeeded") class GreedyQuotedStringsTest(ParseTestCase): def runTest(self): from pyparsing import QuotedString, sglQuotedString, dblQuotedString, quotedString, delimitedList src = """\ "string1", "strin""g2" 'string1', 'string2' ^string1^, ^string2^ <string1>, <string2>""" testExprs = (sglQuotedString, dblQuotedString, quotedString, QuotedString('"', escQuote='""'), QuotedString("'", escQuote="''"), QuotedString("^"), QuotedString("<",endQuoteChar=">")) for expr in testExprs: strs = delimitedList(expr).searchString(src) print_(strs) self.assertTrue(bool(strs), "no matches found for test expression '%s'" % expr) for lst in strs: self.assertEqual(len(lst), 2, "invalid match found for test expression '%s'" % expr) from pyparsing import alphas, nums, Word src = """'ms1',1,0,'2009-12-22','2009-12-22 10:41:22') ON DUPLICATE KEY UPDATE sent_count = sent_count + 1, mtime = '2009-12-22 10:41:22';""" tok_sql_quoted_value = ( QuotedString("'", "\\", "''", True, False) ^ QuotedString('"', "\\", '""', True, False)) tok_sql_computed_value = Word(nums) tok_sql_identifier = Word(alphas) val = tok_sql_quoted_value | tok_sql_computed_value | tok_sql_identifier vals = delimitedList(val) print_(vals.parseString(src)) self.assertEqual(len(vals.parseString(src)), 5, "error in greedy quote escaping") class WordBoundaryExpressionsTest(ParseTestCase): def runTest(self): from pyparsing import WordEnd, WordStart, oneOf ws = WordStart() we = WordEnd() vowel = oneOf(list("AEIOUY")) consonant = oneOf(list("BCDFGHJKLMNPQRSTVWXZ")) leadingVowel = ws + vowel trailingVowel = vowel + we leadingConsonant = ws + consonant trailingConsonant = consonant + we internalVowel = ~ws + vowel + ~we bnf = leadingVowel | trailingVowel tests = """\ ABC DEF GHI JKL MNO PQR STU VWX YZ """.splitlines() tests.append( "\n".join(tests) ) expectedResult = [ [['D', 'G'], ['A'], ['C', 'F'], ['I'], ['E'], ['A', 'I']], [['J', 'M', 'P'], [], ['L', 'R'], ['O'], [], ['O']], [['S', 'V'], ['Y'], ['X', 'Z'], ['U'], [], ['U', 'Y']], [['D', 'G', 'J', 'M', 'P', 'S', 'V'], ['A', 'Y'], ['C', 'F', 'L', 'R', 'X', 'Z'], ['I', 'O', 'U'], ['E'], ['A', 'I', 'O', 'U', 'Y']], ] for t,expected in zip(tests, expectedResult): print_(t) results = [flatten(e.searchString(t).asList()) for e in [ leadingConsonant, leadingVowel, trailingConsonant, trailingVowel, internalVowel, bnf, ]] print_(results) print_() self.assertEqual(results, expected,"Failed WordBoundaryTest, expected %s, got %s" % (expected,results)) class RequiredEachTest(ParseTestCase): def runTest(self): from pyparsing import Keyword parser = Keyword('bam') & Keyword('boo') try: res1 = parser.parseString('bam boo') print_(res1.asList()) res2 = parser.parseString('boo bam') print_(res2.asList()) except ParseException: failed = True else: failed = False self.assertFalse(failed, "invalid logic in Each") self.assertEqual(set(res1), set(res2), "Failed RequiredEachTest, expected " + str(res1.asList()) + " and " + str(res2.asList()) + "to contain same words in any order" ) class OptionalEachTest(ParseTestCase): def runTest1(self): from pyparsing import Optional, Keyword for the_input in [ "Tal Weiss Major", "Tal Major", "Weiss Major", "Major", "Major Tal", "Major Weiss", "Major Tal Weiss", ]: print_(the_input) parser1 = (Optional("Tal") + Optional("Weiss")) & Keyword("Major") parser2 = Optional(Optional("Tal") + Optional("Weiss")) & Keyword("Major") p1res = parser1.parseString(the_input) p2res = parser2.parseString(the_input) self.assertEqual( p1res.asList(), p2res.asList(), "Each failed to match with nested Optionals, " + str(p1res.asList()) + " should match " + str(p2res.asList()), ) def runTest2(self): from pyparsing import Word, alphanums, OneOrMore, Group, Regex, Optional word = Word(alphanums + '_').setName("word") with_stmt = 'with' + OneOrMore(Group(word('key') + '=' + word('value')))('overrides') using_stmt = 'using' + Regex('id-[0-9a-f]{8}')('id') modifiers = Optional(with_stmt('with_stmt')) & Optional(using_stmt('using_stmt')) self.assertEqual(modifiers, "with foo=bar bing=baz using id-deadbeef") self.assertNotEqual(modifiers, "with foo=bar bing=baz using id-deadbeef using id-feedfeed") def runTest3(self): from pyparsing import Literal,Suppress,ZeroOrMore,OneOrMore foo = Literal('foo') bar = Literal('bar') openBrace = Suppress(Literal("{")) closeBrace = Suppress(Literal("}")) exp = openBrace + (OneOrMore(foo)("foo") & ZeroOrMore(bar)("bar")) + closeBrace tests = """\ {foo} {bar foo bar foo bar foo} """.splitlines() for test in tests: test = test.strip() if not test: continue result = exp.parseString(test) print_(test, '->', result.asList()) self.assertEqual(result.asList(), test.strip("{}").split(), "failed to parse Each expression %r" % test) print_(result.dump()) try: result = exp.parseString("{bar}") self.assertTrue(False, "failed to raise exception when required element is missing") except ParseException as pe: pass def runTest4(self): from pyparsing import pyparsing_common, ZeroOrMore, Group expr = ((~pyparsing_common.iso8601_date + pyparsing_common.integer("id")) & ZeroOrMore(Group(pyparsing_common.iso8601_date)("date*"))) expr.runTests(""" 1999-12-31 100 2001-01-01 42 """) def testParseExpressionsWithRegex(self): from itertools import product match_empty_regex = pp.Regex(r"[a-z]*") match_nonempty_regex = pp.Regex(r"[a-z]+") parser_classes = pp.ParseExpression.__subclasses__() test_string = "abc def" expected = ["abc"] for expr, cls in product((match_nonempty_regex, match_empty_regex), parser_classes): print_(expr, cls) parser = cls([expr]) parsed_result = parser.parseString(test_string) print_(parsed_result.dump()) self.assertParseResultsEquals(parsed_result, expected) for expr, cls in product((match_nonempty_regex, match_empty_regex), (pp.MatchFirst, pp.Or)): parser = cls([expr, expr]) print_(parser) parsed_result = parser.parseString(test_string) print_(parsed_result.dump()) self.assertParseResultsEquals(parsed_result, expected) def runTest(self): self.runTest1() self.runTest2() self.runTest3() self.runTest4() self.testParseExpressionsWithRegex() class SumParseResultsTest(ParseTestCase): def runTest(self): samplestr1 = "garbage;DOB 10-10-2010;more garbage\nID PARI12345678;more garbage" samplestr2 = "garbage;ID PARI12345678;more garbage\nDOB 10-10-2010;more garbage" samplestr3 = "garbage;DOB 10-10-2010" samplestr4 = "garbage;ID PARI12345678;more garbage- I am cool" res1 = "ID:PARI12345678 DOB:10-10-2010 INFO:" res2 = "ID:PARI12345678 DOB:10-10-2010 INFO:" res3 = "ID: DOB:10-10-2010 INFO:" res4 = "ID:PARI12345678 DOB: INFO: I am cool" from pyparsing import Regex, Word, alphanums, restOfLine dob_ref = "DOB" + Regex(r"\d{2}-\d{2}-\d{4}")("dob") id_ref = "ID" + Word(alphanums,exact=12)("id") info_ref = "-" + restOfLine("info") person_data = dob_ref | id_ref | info_ref tests = (samplestr1,samplestr2,samplestr3,samplestr4,) results = (res1, res2, res3, res4,) for test,expected in zip(tests, results): person = sum(person_data.searchString(test)) result = "ID:%s DOB:%s INFO:%s" % (person.id, person.dob, person.info) print_(test) print_(expected) print_(result) for pd in person_data.searchString(test): print_(pd.dump()) print_() self.assertEqual(expected, result, "Failed to parse '%s' correctly, \nexpected '%s', got '%s'" % (test,expected,result)) class MarkInputLineTest(ParseTestCase): def runTest(self): samplestr1 = "DOB 100-10-2010;more garbage\nID PARI12345678;more garbage" from pyparsing import Regex dob_ref = "DOB" + Regex(r"\d{2}-\d{2}-\d{4}")("dob") try: res = dob_ref.parseString(samplestr1) except ParseException as pe: outstr = pe.markInputline() print_(outstr) self.assertEqual(outstr, "DOB >!<100-10-2010;more garbage", "did not properly create marked input line") else: self.assertEqual(False, "test construction failed - should have raised an exception") class LocatedExprTest(ParseTestCase): def runTest(self): # 012345678901234567890123456789012345678901234567890 samplestr1 = "DOB 10-10-2010;more garbage;ID PARI12345678 ;more garbage" from pyparsing import Word, alphanums, locatedExpr id_ref = locatedExpr("ID" + Word(alphanums,exact=12)("id")) res = id_ref.searchString(samplestr1)[0][0] print_(res.dump()) self.assertEqual(samplestr1[res.locn_start:res.locn_end], 'ID PARI12345678', "incorrect location calculation") class PopTest(ParseTestCase): def runTest(self): from pyparsing import Word, alphas, nums source = "AAA 123 456 789 234" patt = Word(alphas)("name") + Word(nums)*(1,) result = patt.parseString(source) tests = [ (0, 'AAA', ['123', '456', '789', '234']), (None, '234', ['123', '456', '789']), ('name', 'AAA', ['123', '456', '789']), (-1, '789', ['123', '456']), ] for test in tests: idx, val, remaining = test if idx is not None: ret = result.pop(idx) else: ret = result.pop() print_("EXP:", val, remaining) print_("GOT:", ret, result.asList()) print_(ret, result.asList()) self.assertEqual(ret, val, "wrong value returned, got %r, expected %r" % (ret, val)) self.assertEqual(remaining, result.asList(), "list is in wrong state after pop, got %r, expected %r" % (result.asList(), remaining)) print_() prevlist = result.asList() ret = result.pop('name', default="noname") print_(ret) print_(result.asList()) self.assertEqual(ret, "noname", "default value not successfully returned, got %r, expected %r" % (ret, "noname")) self.assertEqual(result.asList(), prevlist, "list is in wrong state after pop, got %r, expected %r" % (result.asList(), remaining)) class AddConditionTest(ParseTestCase): def runTest(self): from pyparsing import Word, nums, Suppress, ParseFatalException numParser = Word(nums) numParser.addParseAction(lambda s,l,t: int(t[0])) numParser.addCondition(lambda s,l,t: t[0] % 2) numParser.addCondition(lambda s,l,t: t[0] >= 7) result = numParser.searchString("1 2 3 4 5 6 7 8 9 10") print_(result.asList()) self.assertEqual(result.asList(), [[7],[9]], "failed to properly process conditions") numParser = Word(nums) numParser.addParseAction(lambda s,l,t: int(t[0])) rangeParser = (numParser("from_") + Suppress('-') + numParser("to")) result = rangeParser.searchString("1-4 2-4 4-3 5 6 7 8 9 10") print_(result.asList()) self.assertEqual(result.asList(), [[1, 4], [2, 4], [4, 3]], "failed to properly process conditions") rangeParser.addCondition(lambda t: t.to > t.from_, message="from must be <= to", fatal=False) result = rangeParser.searchString("1-4 2-4 4-3 5 6 7 8 9 10") print_(result.asList()) self.assertEqual(result.asList(), [[1, 4], [2, 4]], "failed to properly process conditions") rangeParser = (numParser("from_") + Suppress('-') + numParser("to")) rangeParser.addCondition(lambda t: t.to > t.from_, message="from must be <= to", fatal=True) try: result = rangeParser.searchString("1-4 2-4 4-3 5 6 7 8 9 10") self.assertTrue(False, "failed to interrupt parsing on fatal condition failure") except ParseFatalException: print_("detected fatal condition") class PatientOrTest(ParseTestCase): def runTest(self): import pyparsing as pp # Two expressions and a input string which could - syntactically - be matched against # both expressions. The "Literal" expression is considered invalid though, so this PE # should always detect the "Word" expression. def validate(token): if token[0] == "def": raise pp.ParseException("signalling invalid token") return token a = pp.Word("de").setName("Word")#.setDebug() b = pp.Literal("def").setName("Literal").setParseAction(validate)#.setDebug() c = pp.Literal("d").setName("d")#.setDebug() # The "Literal" expressions's ParseAction is not executed directly after syntactically # detecting the "Literal" Expression but only after the Or-decision has been made # (which is too late)... try: result = (a ^ b ^ c).parseString("def") self.assertEqual(result.asList(), ['de'], "failed to select longest match, chose %s" % result) except ParseException: failed = True else: failed = False self.assertFalse(failed, "invalid logic in Or, fails on longest match with exception in parse action") # from issue #93 word = pp.Word(pp.alphas).setName('word') word_1 = pp.Word(pp.alphas).setName('word_1').addCondition(lambda t: len(t[0]) == 1) a = word + (word_1 + word ^ word) b = word * 3 c = a ^ b c.streamline() print_(c) test_string = 'foo bar temp' result = c.parseString(test_string) print_(test_string, '->', result.asList()) self.assertEqual(result.asList(), test_string.split(), "failed to match longest choice") class EachWithOptionalWithResultsNameTest(ParseTestCase): def runTest(self): from pyparsing import Optional result = (Optional('foo')('one') & Optional('bar')('two')).parseString('bar foo') print_(result.dump()) self.assertEqual(sorted(result.keys()), ['one','two']) class UnicodeExpressionTest(ParseTestCase): def runTest(self): from pyparsing import Literal, ParseException z = 'a' | Literal(u'\u1111') z.streamline() try: z.parseString('b') except ParseException as pe: if not PY_3: self.assertEqual(pe.msg, r'''Expected {"a" | "\u1111"}''', "Invalid error message raised, got %r" % pe.msg) else: self.assertEqual(pe.msg, r'''Expected {"a" | "ᄑ"}''', "Invalid error message raised, got %r" % pe.msg) class SetNameTest(ParseTestCase): def runTest(self): from pyparsing import (oneOf,infixNotation,Word,nums,opAssoc,delimitedList,countedArray, nestedExpr,makeHTMLTags,anyOpenTag,anyCloseTag,commonHTMLEntity,replaceHTMLEntity, Forward,ZeroOrMore) a = oneOf("a b c") b = oneOf("d e f") arith_expr = infixNotation(Word(nums), [ (oneOf('* /'),2,opAssoc.LEFT), (oneOf('+ -'),2,opAssoc.LEFT), ]) arith_expr2 = infixNotation(Word(nums), [ (('?',':'),3,opAssoc.LEFT), ]) recursive = Forward() recursive <<= a + ZeroOrMore(b + recursive) tests = [ a, b, (a | b), arith_expr, arith_expr.expr, arith_expr2, arith_expr2.expr, recursive, delimitedList(Word(nums).setName("int")), countedArray(Word(nums).setName("int")), nestedExpr(), makeHTMLTags('Z'), (anyOpenTag,anyCloseTag), commonHTMLEntity, commonHTMLEntity.setParseAction(replaceHTMLEntity).transformString("lsdjkf <lsdjkf>&'"&xyzzy;"), ] expected = map(str.strip, """\ a | b | c d | e | f {a | b | c | d | e | f} Forward: + | - term + | - term Forward: ?: term ?: term Forward: {a | b | c [{d | e | f : ...}]...} int [, int]... (len) int... nested () expression (<Z>, </Z>) (<any tag>, </any tag>) common HTML entity lsdjkf <lsdjkf>&'"&xyzzy;""".splitlines()) for t,e in zip(tests, expected): tname = str(t) print_(tname) self.assertEqual(tname, e, "expression name mismatch, expected {0} got {1}".format(e, tname)) class TrimArityExceptionMaskingTest(ParseTestCase): def runTest(self): from pyparsing import Word invalid_message = [ "<lambda>() takes exactly 1 argument (0 given)", "<lambda>() missing 1 required positional argument: 't'" ][PY_3] try: Word('a').setParseAction(lambda t: t[0]+1).parseString('aaa') except Exception as e: exc_msg = str(e) self.assertNotEqual(exc_msg, invalid_message, "failed to catch TypeError thrown in _trim_arity") class TrimArityExceptionMaskingTest2(ParseTestCase): def runTest(self): # construct deep call tree def A(): import traceback traceback.print_stack(limit=2) from pyparsing import Word invalid_message = [ "<lambda>() takes exactly 1 argument (0 given)", "<lambda>() missing 1 required positional argument: 't'" ][PY_3] try: Word('a').setParseAction(lambda t: t[0]+1).parseString('aaa') except Exception as e: exc_msg = str(e) self.assertNotEqual(exc_msg, invalid_message, "failed to catch TypeError thrown in _trim_arity") def B(): A() def C(): B() def D(): C() def E(): D() def F(): E() def G(): F() def H(): G() def J(): H() def K(): J() K() class ClearParseActionsTest(ParseTestCase): def runTest(self): import pyparsing as pp ppc = pp.pyparsing_common realnum = ppc.real() self.assertEqual(realnum.parseString("3.14159")[0], 3.14159, "failed basic real number parsing") # clear parse action that converts to float realnum.setParseAction(None) self.assertEqual(realnum.parseString("3.14159")[0], "3.14159", "failed clearing parse action") # add a new parse action that tests if a '.' is prsent realnum.addParseAction(lambda t: '.' in t[0]) self.assertEqual(realnum.parseString("3.14159")[0], True, "failed setting new parse action after clearing parse action") class OneOrMoreStopTest(ParseTestCase): def runTest(self): from pyparsing import (Word, OneOrMore, alphas, Keyword, CaselessKeyword, nums, alphanums) test = "BEGIN aaa bbb ccc END" BEGIN,END = map(Keyword, "BEGIN,END".split(',')) body_word = Word(alphas).setName("word") for ender in (END, "END", CaselessKeyword("END")): expr = BEGIN + OneOrMore(body_word, stopOn=ender) + END self.assertEqual(test, expr, "Did not successfully stop on ending expression %r" % ender) if PY_3: expr = eval('BEGIN + body_word[...].stopOn(ender) + END') self.assertEqual(test, expr, "Did not successfully stop on ending expression %r" % ender) number = Word(nums+',.()').setName("number with optional commas") parser= (OneOrMore(Word(alphanums+'-/.'), stopOn=number)('id').setParseAction(' '.join) + number('data')) result = parser.parseString(' XXX Y/123 1,234.567890') self.assertEqual(result.asList(), ['XXX Y/123', '1,234.567890'], "Did not successfully stop on ending expression %r" % number) class ZeroOrMoreStopTest(ParseTestCase): def runTest(self): from pyparsing import (Word, ZeroOrMore, alphas, Keyword, CaselessKeyword) test = "BEGIN END" BEGIN,END = map(Keyword, "BEGIN,END".split(',')) body_word = Word(alphas).setName("word") for ender in (END, "END", CaselessKeyword("END")): expr = BEGIN + ZeroOrMore(body_word, stopOn=ender) + END self.assertEqual(test, expr, "Did not successfully stop on ending expression %r" % ender) if PY_3: expr = eval('BEGIN + body_word[0, ...].stopOn(ender) + END') self.assertEqual(test, expr, "Did not successfully stop on ending expression %r" % ender) class NestedAsDictTest(ParseTestCase): def runTest(self): from pyparsing import Literal, Forward, alphanums, Group, delimitedList, Dict, Word, Optional equals = Literal("=").suppress() lbracket = Literal("[").suppress() rbracket = Literal("]").suppress() lbrace = Literal("{").suppress() rbrace = Literal("}").suppress() value_dict = Forward() value_list = Forward() value_string = Word(alphanums + "@. ") value = value_list ^ value_dict ^ value_string values = Group(delimitedList(value, ",")) #~ values = delimitedList(value, ",").setParseAction(lambda toks: [toks.asList()]) value_list << lbracket + values + rbracket identifier = Word(alphanums + "_.") assignment = Group(identifier + equals + Optional(value)) assignments = Dict(delimitedList(assignment, ';')) value_dict << lbrace + assignments + rbrace response = assignments rsp = 'username=goat; errors={username=[already taken, too short]}; empty_field=' result_dict = response.parseString(rsp).asDict() print_(result_dict) self.assertEqual(result_dict['username'], 'goat', "failed to process string in ParseResults correctly") self.assertEqual(result_dict['errors']['username'], ['already taken', 'too short'], "failed to process nested ParseResults correctly") class TraceParseActionDecoratorTest(ParseTestCase): def runTest(self): from pyparsing import traceParseAction, Word, nums @traceParseAction def convert_to_int(t): return int(t[0]) class Z(object): def __call__(self, other): return other[0] * 1000 integer = Word(nums).addParseAction(convert_to_int) integer.addParseAction(traceParseAction(lambda t: t[0]*10)) integer.addParseAction(traceParseAction(Z())) integer.parseString("132") class RunTestsTest(ParseTestCase): def runTest(self): from pyparsing import Word, nums, delimitedList integer = Word(nums).setParseAction(lambda t : int(t[0])) intrange = integer("start") + '-' + integer("end") intrange.addCondition(lambda t: t.end > t.start, message="invalid range, start must be <= end", fatal=True) intrange.addParseAction(lambda t: list(range(t.start, t.end+1))) indices = delimitedList(intrange | integer) indices.addParseAction(lambda t: sorted(set(t))) tests = """\ # normal data 1-3,2-4,6,8-10,16 # lone integer 11""" results = indices.runTests(tests, printResults=False)[1] expectedResults = [ [1, 2, 3, 4, 6, 8, 9, 10, 16], [11], ] for res, expected in zip(results, expectedResults): print_(res[1].asList()) print_(expected) self.assertEqual(res[1].asList(), expected, "failed test: " + str(expected)) tests = """\ # invalid range 1-2, 3-1, 4-6, 7, 12 """ success = indices.runTests(tests, printResults=False, failureTests=True)[0] self.assertTrue(success, "failed to raise exception on improper range test") class RunTestsPostParseTest(ParseTestCase): def runTest(self): import pyparsing as pp integer = pp.pyparsing_common.integer fraction = integer('numerator') + '/' + integer('denominator') accum = [] def eval_fraction(test, result): accum.append((test, result.asList())) return "eval: {0}".format(result.numerator / result.denominator) success = fraction.runTests("""\ 1/2 1/0 """, postParse=eval_fraction)[0] print_(success) self.assertTrue(success, "failed to parse fractions in RunTestsPostParse") expected_accum = [('1/2', [1, '/', 2]), ('1/0', [1, '/', 0])] self.assertEqual(accum, expected_accum, "failed to call postParse method during runTests") class CommonExpressionsTest(ParseTestCase): def runTest(self): from pyparsing import pyparsing_common import ast success = pyparsing_common.mac_address.runTests(""" AA:BB:CC:DD:EE:FF AA.BB.CC.DD.EE.FF AA-BB-CC-DD-EE-FF """)[0] self.assertTrue(success, "error in parsing valid MAC address") success = pyparsing_common.mac_address.runTests(""" # mixed delimiters AA.BB:CC:DD:EE:FF """, failureTests=True)[0] self.assertTrue( success, "error in detecting invalid mac address") success = pyparsing_common.ipv4_address.runTests(""" 0.0.0.0 1.1.1.1 127.0.0.1 1.10.100.199 255.255.255.255 """)[0] self.assertTrue(success, "error in parsing valid IPv4 address") success = pyparsing_common.ipv4_address.runTests(""" # out of range value 256.255.255.255 """, failureTests=True)[0] self.assertTrue(success, "error in detecting invalid IPv4 address") success = pyparsing_common.ipv6_address.runTests(""" 2001:0db8:85a3:0000:0000:8a2e:0370:7334 2134::1234:4567:2468:1236:2444:2106 0:0:0:0:0:0:A00:1 1080::8:800:200C:417A ::A00:1 # loopback address ::1 # the null address :: # ipv4 compatibility form ::ffff:192.168.0.1 """)[0] self.assertTrue(success, "error in parsing valid IPv6 address") success = pyparsing_common.ipv6_address.runTests(""" # too few values 1080:0:0:0:8:800:200C # too many ::'s, only 1 allowed 2134::1234:4567::2444:2106 """, failureTests=True)[0] self.assertTrue(success, "error in detecting invalid IPv6 address") success = pyparsing_common.number.runTests(""" 100 -100 +100 3.14159 6.02e23 1e-12 """)[0] self.assertTrue(success, "error in parsing valid numerics") success = pyparsing_common.sci_real.runTests(""" 1e12 -1e12 3.14159 6.02e23 """)[0] self.assertTrue(success, "error in parsing valid scientific notation reals") # any int or real number, returned as float success = pyparsing_common.fnumber.runTests(""" 100 -100 +100 3.14159 6.02e23 1e-12 """)[0] self.assertTrue(success, "error in parsing valid numerics") success, results = pyparsing_common.iso8601_date.runTests(""" 1997 1997-07 1997-07-16 """) self.assertTrue(success, "error in parsing valid iso8601_date") expected = [ ('1997', None, None), ('1997', '07', None), ('1997', '07', '16'), ] for r,exp in zip(results, expected): self.assertTrue((r[1].year,r[1].month,r[1].day,) == exp, "failed to parse date into fields") success, results = pyparsing_common.iso8601_date().addParseAction(pyparsing_common.convertToDate()).runTests(""" 1997-07-16 """) self.assertTrue(success, "error in parsing valid iso8601_date with parse action") self.assertTrue(results[0][1][0] == datetime.date(1997, 7, 16)) success, results = pyparsing_common.iso8601_datetime.runTests(""" 1997-07-16T19:20+01:00 1997-07-16T19:20:30+01:00 1997-07-16T19:20:30.45Z 1997-07-16 19:20:30.45 """) self.assertTrue(success, "error in parsing valid iso8601_datetime") success, results = pyparsing_common.iso8601_datetime().addParseAction(pyparsing_common.convertToDatetime()).runTests(""" 1997-07-16T19:20:30.45 """) self.assertTrue(success, "error in parsing valid iso8601_datetime") self.assertTrue(results[0][1][0] == datetime.datetime(1997, 7, 16, 19, 20, 30, 450000)) success = pyparsing_common.uuid.runTests(""" 123e4567-e89b-12d3-a456-426655440000 """)[0] self.assertTrue(success, "failed to parse valid uuid") success = pyparsing_common.fraction.runTests(""" 1/2 -15/16 -3/-4 """)[0] self.assertTrue(success, "failed to parse valid fraction") success = pyparsing_common.mixed_integer.runTests(""" 1/2 -15/16 -3/-4 1 1/2 2 -15/16 0 -3/-4 12 """)[0] self.assertTrue(success, "failed to parse valid mixed integer") success, results = pyparsing_common.number.runTests(""" 100 -3 1.732 -3.14159 6.02e23""") self.assertTrue(success, "failed to parse numerics") for test,result in results: expected = ast.literal_eval(test) self.assertEqual(result[0], expected, "numeric parse failed (wrong value) (%s should be %s)" % (result[0], expected)) self.assertEqual(type(result[0]), type(expected), "numeric parse failed (wrong type) (%s should be %s)" % (type(result[0]), type(expected))) class NumericExpressionsTest(ParseTestCase): def runTest(self): import pyparsing as pp ppc = pp.pyparsing_common # disable parse actions that do type conversion so we don't accidentally trigger # conversion exceptions when what we want to check is the parsing expression real = ppc.real().setParseAction(None) sci_real = ppc.sci_real().setParseAction(None) signed_integer = ppc.signed_integer().setParseAction(None) from itertools import product def make_tests(): leading_sign = ['+', '-', ''] leading_digit = ['0', ''] dot = ['.', ''] decimal_digit = ['1', ''] e = ['e', 'E', ''] e_sign = ['+', '-', ''] e_int = ['22', ''] stray = ['9', '.', ''] seen = set() seen.add('') for parts in product(leading_sign, stray, leading_digit, dot, decimal_digit, stray, e, e_sign, e_int, stray): parts_str = ''.join(parts).strip() if parts_str in seen: continue seen.add(parts_str) yield parts_str print_(len(seen)-1, "tests produced") # collect tests into valid/invalid sets, depending on whether they evaluate to valid Python floats or ints valid_ints = set() valid_reals = set() valid_sci_reals = set() invalid_ints = set() invalid_reals = set() invalid_sci_reals = set() # check which strings parse as valid floats or ints, and store in related valid or invalid test sets for test_str in make_tests(): if '.' in test_str or 'e' in test_str.lower(): try: float(test_str) except ValueError: invalid_sci_reals.add(test_str) if 'e' not in test_str.lower(): invalid_reals.add(test_str) else: valid_sci_reals.add(test_str) if 'e' not in test_str.lower(): valid_reals.add(test_str) try: int(test_str) except ValueError: invalid_ints.add(test_str) else: valid_ints.add(test_str) # now try all the test sets against their respective expressions all_pass = True suppress_results = {'printResults': False} for expr, tests, is_fail, fn in zip([real, sci_real, signed_integer]*2, [valid_reals, valid_sci_reals, valid_ints, invalid_reals, invalid_sci_reals, invalid_ints], [False, False, False, True, True, True], [float, float, int]*2): # # success, test_results = expr.runTests(sorted(tests, key=len), failureTests=is_fail, **suppress_results) # filter_result_fn = (lambda r: isinstance(r, Exception), # lambda r: not isinstance(r, Exception))[is_fail] # print_(expr, ('FAIL', 'PASS')[success], "{1}valid tests ({0})".format(len(tests), # 'in' if is_fail else '')) # if not success: # all_pass = False # for test_string, result in test_results: # if filter_result_fn(result): # try: # test_value = fn(test_string) # except ValueError as ve: # test_value = str(ve) # print_("{0!r}: {1} {2} {3}".format(test_string, result, # expr.matches(test_string, parseAll=True), test_value)) success = True for t in tests: if expr.matches(t, parseAll=True): if is_fail: print_(t, "should fail but did not") success = False else: if not is_fail: print_(t, "should not fail but did") success = False print_(expr, ('FAIL', 'PASS')[success], "{1}valid tests ({0})".format(len(tests), 'in' if is_fail else '')) all_pass = all_pass and success self.assertTrue(all_pass, "failed one or more numeric tests") class TokenMapTest(ParseTestCase): def runTest(self): from pyparsing import tokenMap, Word, hexnums, OneOrMore parser = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) success, results = parser.runTests(""" 00 11 22 aa FF 0a 0d 1a """, printResults=False) self.assertTrue(success, "failed to parse hex integers") print_(results) self.assertEqual(results[0][-1].asList(), [0, 17, 34, 170, 255, 10, 13, 26], "tokenMap parse action failed") class ParseFileTest(ParseTestCase): def runTest(self): from pyparsing import pyparsing_common, OneOrMore s = """ 123 456 789 """ input_file = StringIO(s) integer = pyparsing_common.integer results = OneOrMore(integer).parseFile(input_file) print_(results) results = OneOrMore(integer).parseFile('test/parsefiletest_input_file.txt') print_(results) class HTMLStripperTest(ParseTestCase): def runTest(self): from pyparsing import pyparsing_common, originalTextFor, OneOrMore, Word, printables sample = """ <html> Here is some sample <i>HTML</i> text. </html> """ read_everything = originalTextFor(OneOrMore(Word(printables))) read_everything.addParseAction(pyparsing_common.stripHTMLTags) result = read_everything.parseString(sample) self.assertEqual(result[0].strip(), 'Here is some sample HTML text.') class ExprSplitterTest(ParseTestCase): def runTest(self): from pyparsing import Literal, quotedString, pythonStyleComment, Empty expr = Literal(';') + Empty() expr.ignore(quotedString) expr.ignore(pythonStyleComment) sample = """ def main(): this_semi_does_nothing(); neither_does_this_but_there_are_spaces_afterward(); a = "a;b"; return a # this is a comment; it has a semicolon! def b(): if False: z=1000;b("; in quotes"); c=200;return z return ';' class Foo(object): def bar(self): '''a docstring; with a semicolon''' a = 10; b = 11; c = 12 # this comment; has several; semicolons if self.spam: x = 12; return x # so; does; this; one x = 15;;; y += x; return y def baz(self): return self.bar """ expected = [ [' this_semi_does_nothing()', ''], [' neither_does_this_but_there_are_spaces_afterward()', ''], [' a = "a;b"', 'return a # this is a comment; it has a semicolon!'], [' z=1000', 'b("; in quotes")', 'c=200', 'return z'], [" return ';'"], [" '''a docstring; with a semicolon'''"], [' a = 10', 'b = 11', 'c = 12'], [' # this comment; has several; semicolons'], [' x = 12', 'return x # so; does; this; one'], [' x = 15', '', '', 'y += x', 'return y'], ] exp_iter = iter(expected) for line in filter(lambda ll: ';' in ll, sample.splitlines()): print_(str(list(expr.split(line)))+',') self.assertEqual(list(expr.split(line)), next(exp_iter), "invalid split on expression") print_() expected = [ [' this_semi_does_nothing()', ';', ''], [' neither_does_this_but_there_are_spaces_afterward()', ';', ''], [' a = "a;b"', ';', 'return a # this is a comment; it has a semicolon!'], [' z=1000', ';', 'b("; in quotes")', ';', 'c=200', ';', 'return z'], [" return ';'"], [" '''a docstring; with a semicolon'''"], [' a = 10', ';', 'b = 11', ';', 'c = 12'], [' # this comment; has several; semicolons'], [' x = 12', ';', 'return x # so; does; this; one'], [' x = 15', ';', '', ';', '', ';', 'y += x', ';', 'return y'], ] exp_iter = iter(expected) for line in filter(lambda ll: ';' in ll, sample.splitlines()): print_(str(list(expr.split(line, includeSeparators=True)))+',') self.assertEqual(list(expr.split(line, includeSeparators=True)), next(exp_iter), "invalid split on expression") print_() expected = [ [' this_semi_does_nothing()', ''], [' neither_does_this_but_there_are_spaces_afterward()', ''], [' a = "a;b"', 'return a # this is a comment; it has a semicolon!'], [' z=1000', 'b("; in quotes"); c=200;return z'], [' a = 10', 'b = 11; c = 12'], [' x = 12', 'return x # so; does; this; one'], [' x = 15', ';; y += x; return y'], ] exp_iter = iter(expected) for line in sample.splitlines(): pieces = list(expr.split(line, maxsplit=1)) print_(str(pieces)+',') if len(pieces) == 2: exp = next(exp_iter) self.assertEqual(pieces, exp, "invalid split on expression with maxSplits=1") elif len(pieces) == 1: self.assertEqual(len(expr.searchString(line)), 0, "invalid split with maxSplits=1 when expr not present") else: print_("\n>>> " + line) self.assertTrue(False, "invalid split on expression with maxSplits=1, corner case") class ParseFatalExceptionTest(ParseTestCase): def runTest(self): from pyparsing import Word, nums, ParseFatalException success = False try: expr = "ZZZ" - Word(nums) expr.parseString("ZZZ bad") except ParseFatalException as pfe: print_('ParseFatalException raised correctly') success = True except Exception as e: print_(type(e)) print_(e) self.assertTrue(success, "bad handling of syntax error") class InlineLiteralsUsingTest(ParseTestCase): def runTest(self): from pyparsing import ParserElement, Suppress, Literal, CaselessLiteral, Word, alphas, oneOf, CaselessKeyword, nums with AutoReset(ParserElement, "_literalStringClass"): ParserElement.inlineLiteralsUsing(Suppress) wd = Word(alphas) result = (wd + ',' + wd + oneOf("! . ?")).parseString("Hello, World!") self.assertEqual(len(result), 3, "inlineLiteralsUsing(Suppress) failed!") ParserElement.inlineLiteralsUsing(Literal) result = (wd + ',' + wd + oneOf("! . ?")).parseString("Hello, World!") self.assertEqual(len(result), 4, "inlineLiteralsUsing(Literal) failed!") ParserElement.inlineLiteralsUsing(CaselessKeyword) result = ("SELECT" + wd + "FROM" + wd).parseString("select color from colors") self.assertEqual(result.asList(), "SELECT color FROM colors".split(), "inlineLiteralsUsing(CaselessKeyword) failed!") ParserElement.inlineLiteralsUsing(CaselessLiteral) result = ("SELECT" + wd + "FROM" + wd).parseString("select color from colors") self.assertEqual(result.asList(), "SELECT color FROM colors".split(), "inlineLiteralsUsing(CaselessLiteral) failed!") integer = Word(nums) ParserElement.inlineLiteralsUsing(Literal) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") result = date_str.parseString("1999/12/31") self.assertEqual(result.asList(), ['1999', '/', '12', '/', '31'], "inlineLiteralsUsing(example 1) failed!") # change to Suppress ParserElement.inlineLiteralsUsing(Suppress) date_str = integer("year") + '/' + integer("month") + '/' + integer("day") result = date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] self.assertEqual(result.asList(), ['1999', '12', '31'], "inlineLiteralsUsing(example 2) failed!") class CloseMatchTest(ParseTestCase): def runTest(self): import pyparsing as pp searchseq = pp.CloseMatch("ATCATCGAATGGA", 2) _, results = searchseq.runTests(""" ATCATCGAATGGA XTCATCGAATGGX ATCATCGAAXGGA ATCAXXGAATGGA ATCAXXGAATGXA ATCAXXGAATGG """) expected = ( [], [0,12], [9], [4,5], None, None ) for r, exp in zip(results, expected): if exp is not None: self.assertEqual(r[1].mismatches, exp, "fail CloseMatch between %r and %r" % (searchseq.match_string, r[0])) print_(r[0], 'exc: %s' % r[1] if exp is None and isinstance(r[1], Exception) else ("no match", "match")[r[1].mismatches == exp]) class DefaultKeywordCharsTest(ParseTestCase): def runTest(self): import pyparsing as pp try: pp.Keyword("start").parseString("start1000") except pp.ParseException: pass else: self.assertTrue(False, "failed to fail on default keyword chars") try: pp.Keyword("start", identChars=pp.alphas).parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") else: pass with AutoReset(pp.Keyword, "DEFAULT_KEYWORD_CHARS"): pp.Keyword.setDefaultKeywordChars(pp.alphas) try: pp.Keyword("start").parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") else: pass try: pp.CaselessKeyword("START").parseString("start1000") except pp.ParseException: pass else: self.assertTrue(False, "failed to fail on default keyword chars") try: pp.CaselessKeyword("START", identChars=pp.alphas).parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") else: pass with AutoReset(pp.Keyword, "DEFAULT_KEYWORD_CHARS"): pp.Keyword.setDefaultKeywordChars(pp.alphas) try: pp.CaselessKeyword("START").parseString("start1000") except pp.ParseException: self.assertTrue(False, "failed to match keyword using updated keyword chars") else: pass class ColTest(ParseTestCase): def runTest(self): test = "*\n* \n* ALF\n*\n" initials = [c for i, c in enumerate(test) if pp.col(i, test) == 1] print_(initials) self.assertTrue(len(initials) == 4 and all(c=='*' for c in initials), 'fail col test') class LiteralExceptionTest(ParseTestCase): def runTest(self): import pyparsing as pp for cls in (pp.Literal, pp.CaselessLiteral, pp.Keyword, pp.CaselessKeyword, pp.Word, pp.Regex): expr = cls('xyz')#.setName('{0}_expr'.format(cls.__name__.lower())) try: expr.parseString(' ') except Exception as e: print_(cls.__name__, str(e)) self.assertTrue(isinstance(e, pp.ParseBaseException), "class {0} raised wrong exception type {1}".format(cls.__name__, type(e).__name__)) class ParseActionExceptionTest(ParseTestCase): def runTest(self): self.expect_traceback = True import pyparsing as pp import traceback number = pp.Word(pp.nums) def number_action(): raise IndexError # this is the important line! number.setParseAction(number_action) symbol = pp.Word('abcd', max=1) expr = number | symbol try: expr.parseString('1 + 2') except Exception as e: self.assertTrue(hasattr(e, '__cause__'), "no __cause__ attribute in the raised exception") self.assertTrue(e.__cause__ is not None, "__cause__ not propagated to outer exception") self.assertTrue(type(e.__cause__) == IndexError, "__cause__ references wrong exception") traceback.print_exc() else: self.assertTrue(False, "Expected ParseException not raised") class ParseActionNestingTest(ParseTestCase): # tests Issue #22 def runTest(self): vals = pp.OneOrMore(pp.pyparsing_common.integer)("int_values") def add_total(tokens): tokens['total'] = sum(tokens) return tokens vals.addParseAction(add_total) results = vals.parseString("244 23 13 2343") print_(results.dump()) self.assertEqual(results.int_values.asDict(), {}, "noop parse action changed ParseResults structure") name = pp.Word(pp.alphas)('name') score = pp.Word(pp.nums + '.')('score') nameScore = pp.Group(name + score) line1 = nameScore('Rider') result1 = line1.parseString('Mauney 46.5') print_("### before parse action is added ###") print_("result1.dump():\n" + result1.dump() + "\n") before_pa_dict = result1.asDict() line1.setParseAction(lambda t: t) result1 = line1.parseString('Mauney 46.5') after_pa_dict = result1.asDict() print_("### after parse action was added ###") print_("result1.dump():\n" + result1.dump() + "\n") self.assertEqual(before_pa_dict, after_pa_dict, "noop parse action changed ParseResults structure") class ParseResultsNameBelowUngroupedNameTest(ParseTestCase): def runTest(self): import pyparsing as pp rule_num = pp.Regex("[0-9]+")("LIT_NUM*") list_num = pp.Group(pp.Literal("[")("START_LIST") + pp.delimitedList(rule_num)("LIST_VALUES") + pp.Literal("]")("END_LIST"))("LIST") test_string = "[ 1,2,3,4,5,6 ]" list_num.runTests(test_string) U = list_num.parseString(test_string) self.assertTrue("LIT_NUM" not in U.LIST.LIST_VALUES, "results name retained as sub in ungrouped named result") class ParseResultsNamesInGroupWithDictTest(ParseTestCase): def runTest(self): import pyparsing as pp from pyparsing import pyparsing_common as ppc key = ppc.identifier() value = ppc.integer() lat = ppc.real() long = ppc.real() EQ = pp.Suppress('=') data = lat("lat") + long("long") + pp.Dict(pp.OneOrMore(pp.Group(key + EQ + value))) site = pp.QuotedString('"')("name") + pp.Group(data)("data") test_string = '"Golden Gate Bridge" 37.819722 -122.478611 height=746 span=4200' site.runTests(test_string) # U = list_num.parseString(test_string) # self.assertTrue("LIT_NUM" not in U.LIST.LIST_VALUES, "results name retained as sub in ungrouped named result") a, aEnd = pp.makeHTMLTags('a') attrs = a.parseString("<a href='blah'>") print_(attrs.dump()) self.assertEqual(attrs.startA.href, 'blah') self.assertEqual(attrs.asDict(), {'startA': {'href': 'blah', 'tag': 'a', 'empty': False}, 'href': 'blah', 'tag': 'a', 'empty': False}) class FollowedByTest(ParseTestCase): def runTest(self): import pyparsing as pp from pyparsing import pyparsing_common as ppc expr = pp.Word(pp.alphas)("item") + pp.FollowedBy(ppc.integer("qty")) result = expr.parseString("balloon 99") print_(result.dump()) self.assertTrue('qty' in result, "failed to capture results name in FollowedBy") self.assertEqual(result.asDict(), {'item': 'balloon', 'qty': 99}, "invalid results name structure from FollowedBy") class SetBreakTest(ParseTestCase): """ Test behavior of ParserElement.setBreak(), to invoke the debugger before parsing that element is attempted. Temporarily monkeypatches pdb.set_trace. """ def runTest(self): was_called = [] def mock_set_trace(): was_called.append(True) import pyparsing as pp wd = pp.Word(pp.alphas) wd.setBreak() print_("Before parsing with setBreak:", was_called) import pdb with AutoReset(pdb, "set_trace"): pdb.set_trace = mock_set_trace wd.parseString("ABC") print_("After parsing with setBreak:", was_called) self.assertTrue(bool(was_called), "set_trace wasn't called by setBreak") class UnicodeTests(ParseTestCase): def runTest(self): import pyparsing as pp ppu = pp.pyparsing_unicode ppc = pp.pyparsing_common # verify proper merging of ranges by addition kanji_printables = ppu.Japanese.Kanji.printables katakana_printables = ppu.Japanese.Katakana.printables hiragana_printables = ppu.Japanese.Hiragana.printables japanese_printables = ppu.Japanese.printables self.assertEqual(set(japanese_printables), set(kanji_printables + katakana_printables + hiragana_printables), "failed to construct ranges by merging Japanese types") # verify proper merging of ranges using multiple inheritance cjk_printables = ppu.CJK.printables self.assertEqual(len(cjk_printables), len(set(cjk_printables)), "CJK contains duplicate characters - all should be unique") chinese_printables = ppu.Chinese.printables korean_printables = ppu.Korean.printables print_(len(cjk_printables), len(set(chinese_printables + korean_printables + japanese_printables))) self.assertEqual(len(cjk_printables), len(set(chinese_printables + korean_printables + japanese_printables)), "failed to construct ranges by merging Chinese, Japanese and Korean") alphas = ppu.Greek.alphas greet = pp.Word(alphas) + ',' + pp.Word(alphas) + '!' # input string hello = u"Καλημέρα, κόσμε!" result = greet.parseString(hello) print_(result) self.assertTrue(result.asList() == [u'Καλημέρα', ',', u'κόσμε', '!'], "Failed to parse Greek 'Hello, World!' using pyparsing_unicode.Greek.alphas") # define a custom unicode range using multiple inheritance class Turkish_set(ppu.Latin1, ppu.LatinA): pass self.assertEqual(set(Turkish_set.printables), set(ppu.Latin1.printables + ppu.LatinA.printables), "failed to construct ranges by merging Latin1 and LatinA (printables)") self.assertEqual(set(Turkish_set.alphas), set(ppu.Latin1.alphas + ppu.LatinA.alphas), "failed to construct ranges by merging Latin1 and LatinA (alphas)") self.assertEqual(set(Turkish_set.nums), set(ppu.Latin1.nums + ppu.LatinA.nums), "failed to construct ranges by merging Latin1 and LatinA (nums)") key = pp.Word(Turkish_set.alphas) value = ppc.integer | pp.Word(Turkish_set.alphas, Turkish_set.alphanums) EQ = pp.Suppress('=') key_value = key + EQ + value sample = u"""\ şehir=İzmir ülke=Türkiye nüfus=4279677""" result = pp.Dict(pp.OneOrMore(pp.Group(key_value))).parseString(sample) print_(result.asDict()) self.assertEqual(result.asDict(), {u'şehir': u'İzmir', u'ülke': u'Türkiye', u'nüfus': 4279677}, "Failed to parse Turkish key-value pairs") class IndentedBlockExampleTest(ParseTestCase): # Make sure example in indentedBlock docstring actually works! def runTest(self): from textwrap import dedent from pyparsing import (Word, alphas, alphanums, indentedBlock, Optional, delimitedList, Group, Forward, nums, OneOrMore) data = dedent(''' def A(z): A1 B = 100 G = A2 A2 A3 B def BB(a,b,c): BB1 def BBA(): bba1 bba2 bba3 C D def spam(x,y): def eggs(z): pass ''') indentStack = [1] stmt = Forward() identifier = Word(alphas, alphanums) funcDecl = ("def" + identifier + Group("(" + Optional(delimitedList(identifier)) + ")") + ":") func_body = indentedBlock(stmt, indentStack) funcDef = Group(funcDecl + func_body) rvalue = Forward() funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") rvalue << (funcCall | identifier | Word(nums)) assignment = Group(identifier + "=" + rvalue) stmt << (funcDef | assignment | identifier) module_body = OneOrMore(stmt) parseTree = module_body.parseString(data) parseTree.pprint() self.assertEqual(parseTree.asList(), [['def', 'A', ['(', 'z', ')'], ':', [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], 'B', ['def', 'BB', ['(', 'a', 'b', 'c', ')'], ':', [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], 'C', 'D', ['def', 'spam', ['(', 'x', 'y', ')'], ':', [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]], "Failed indentedBlock example" ) class IndentedBlockTest(ParseTestCase): # parse pseudo-yaml indented text def runTest(self): import textwrap EQ = pp.Suppress('=') stack = [1] key = pp.pyparsing_common.identifier value = pp.Forward() key_value = key + EQ + value compound_value = pp.Dict(pp.ungroup(pp.indentedBlock(key_value, stack))) value <<= pp.pyparsing_common.integer | pp.QuotedString("'") | compound_value parser = pp.Dict(pp.OneOrMore(pp.Group(key_value))) text = """ a = 100 b = 101 c = c1 = 200 c2 = c21 = 999 c3 = 'A horse, a horse, my kingdom for a horse' d = 505 """ text = textwrap.dedent(text) print_(text) result = parser.parseString(text) print_(result.dump()) self.assertEqual(result.a, 100, "invalid indented block result") self.assertEqual(result.c.c1, 200, "invalid indented block result") self.assertEqual(result.c.c2.c21, 999, "invalid indented block result") class IndentedBlockTest2(ParseTestCase): # exercise indentedBlock with example posted in issue #87 def runTest(self): from textwrap import dedent from pyparsing import Word, alphas, alphanums, Suppress, Forward, indentedBlock, Literal, OneOrMore indent_stack = [1] key = Word(alphas, alphanums) + Suppress(":") stmt = Forward() suite = indentedBlock(stmt, indent_stack) body = key + suite pattern = (Word(alphas) + Suppress("(") + Word(alphas) + Suppress(")")) stmt << pattern def key_parse_action(toks): print_("Parsing '%s'..." % toks[0]) key.setParseAction(key_parse_action) header = Suppress("[") + Literal("test") + Suppress("]") content = (header + OneOrMore(indentedBlock(body, indent_stack, False))) contents = Forward() suites = indentedBlock(content, indent_stack) extra = Literal("extra") + Suppress(":") + suites contents << (content | extra) parser = OneOrMore(contents) sample = dedent(""" extra: [test] one0: two (three) four0: five (seven) extra: [test] one1: two (three) four1: five (seven) """) success, _ = parser.runTests([sample]) self.assertTrue(success, "Failed indentedBlock test for issue #87") class IndentedBlockScanTest(ParseTestCase): def get_parser(self): """ A valid statement is the word "block:", followed by an indent, followed by the letter A only, or another block """ stack = [1] block = pp.Forward() body = pp.indentedBlock(pp.Literal('A') ^ block, indentStack=stack, indent=True) block <<= pp.Literal('block:') + body return block def runTest(self): from textwrap import dedent # This input string is a perfect match for the parser, so a single match is found p1 = self.get_parser() r1 = list(p1.scanString(dedent("""\ block: A """))) self.assertEqual(len(r1), 1) # This input string is a perfect match for the parser, except for the letter B instead of A, so this will fail (and should) p2 = self.get_parser() r2 = list(p2.scanString(dedent("""\ block: B """))) self.assertEqual(len(r2), 0) # This input string contains both string A and string B, and it finds one match (as it should) p3 = self.get_parser() r3 = list(p3.scanString(dedent("""\ block: A block: B """))) self.assertEqual(len(r3), 1) # This input string contains both string A and string B, but in a different order. p4 = self.get_parser() r4 = list(p4.scanString(dedent("""\ block: B block: A """))) self.assertEqual(len(r4), 1) # This is the same as case 3, but with nesting p5 = self.get_parser() r5 = list(p5.scanString(dedent("""\ block: block: A block: block: B """))) self.assertEqual(len(r5), 1) # This is the same as case 4, but with nesting p6 = self.get_parser() r6 = list(p6.scanString(dedent("""\ block: block: B block: block: A """))) self.assertEqual(len(r6), 1) class ParseResultsWithNameMatchFirst(ParseTestCase): def runTest(self): import pyparsing as pp expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird') expr_b = pp.Literal('the') + pp.Literal('bird') expr = (expr_a | expr_b)('rexp') expr.runTests("""\ not the bird the bird """) self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) # test compatibility mode, restoring pre-2.3.1 behavior with AutoReset(pp.__compat__, "collect_all_And_tokens"): pp.__compat__.collect_all_And_tokens = False pp.__diag__.warn_multiple_tokens_in_named_alternation = True expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird') expr_b = pp.Literal('the') + pp.Literal('bird') if PY_3: with self.assertWarns(UserWarning, msg="failed to warn of And within alternation"): expr = (expr_a | expr_b)('rexp') else: self.expect_warning = True expr = (expr_a | expr_b)('rexp') expr.runTests(""" not the bird the bird """) self.assertEqual(expr.parseString('not the bird')['rexp'], 'not') self.assertEqual(expr.parseString('the bird')['rexp'], 'the') class ParseResultsWithNameOr(ParseTestCase): def runTest(self): import pyparsing as pp expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird') expr_b = pp.Literal('the') + pp.Literal('bird') expr = (expr_a ^ expr_b)('rexp') expr.runTests("""\ not the bird the bird """) self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) expr = (expr_a | expr_b)('rexp') expr.runTests("""\ not the bird the bird """) self.assertEqual(list(expr.parseString('not the bird')['rexp']), 'not the bird'.split()) self.assertEqual(list(expr.parseString('the bird')['rexp']), 'the bird'.split()) # test compatibility mode, restoring pre-2.3.1 behavior with AutoReset(pp.__compat__, "collect_all_And_tokens"): pp.__compat__.collect_all_And_tokens = False pp.__diag__.warn_multiple_tokens_in_named_alternation = True expr_a = pp.Literal('not') + pp.Literal('the') + pp.Literal('bird') expr_b = pp.Literal('the') + pp.Literal('bird') if PY_3: with self.assertWarns(UserWarning, msg="failed to warn of And within alternation"): expr = (expr_a ^ expr_b)('rexp') else: self.expect_warning = True expr = (expr_a ^ expr_b)('rexp') expr.runTests("""\ not the bird the bird """) self.assertEqual(expr.parseString('not the bird')['rexp'], 'not') self.assertEqual(expr.parseString('the bird')['rexp'], 'the') class EmptyDictDoesNotRaiseException(ParseTestCase): def runTest(self): import pyparsing as pp key = pp.Word(pp.alphas) value = pp.Word(pp.nums) EQ = pp.Suppress('=') key_value_dict = pp.dictOf(key, EQ + value) print_(key_value_dict.parseString("""\ a = 10 b = 20 """).dump()) try: print_(key_value_dict.parseString("").dump()) except pp.ParseException as pe: exc = pe if not hasattr(exc, '__traceback__'): # Python 2 compatibility etype, value, traceback = sys.exc_info() exc.__traceback__ = traceback print_(pp.ParseException.explain(pe)) else: self.assertTrue(False, "failed to raise exception when matching empty string") class ExplainExceptionTest(ParseTestCase): def runTest(self): import pyparsing as pp expr = pp.Word(pp.nums).setName("int") + pp.Word(pp.alphas).setName("word") try: expr.parseString("123 355") except pp.ParseException as pe: exc = pe if not hasattr(exc, '__traceback__'): # Python 2 compatibility etype, value, traceback = sys.exc_info() exc.__traceback__ = traceback print_(pp.ParseException.explain(pe, depth=0)) expr = pp.Word(pp.nums).setName("int") - pp.Word(pp.alphas).setName("word") try: expr.parseString("123 355 (test using ErrorStop)") except pp.ParseSyntaxException as pe: exc = pe if not hasattr(exc, '__traceback__'): # Python 2 compatibility etype, value, traceback = sys.exc_info() exc.__traceback__ = traceback print_(pp.ParseException.explain(pe)) integer = pp.Word(pp.nums).setName("int").addParseAction(lambda t: int(t[0])) expr = integer + integer def divide_args(t): integer.parseString("A") return t[0] / t[1] expr.addParseAction(divide_args) pp.ParserElement.enablePackrat() print_() # ~ print(expr.parseString("125 25")) try: expr.parseString("123 0") except pp.ParseException as pe: exc = pe if not hasattr(exc, '__traceback__'): # Python 2 compatibility etype, value, traceback = sys.exc_info() exc.__traceback__ = traceback print_(pp.ParseException.explain(pe)) except Exception as exc: if not hasattr(exc, '__traceback__'): # Python 2 compatibility etype, value, traceback = sys.exc_info() exc.__traceback__ = traceback print_(pp.ParseException.explain(exc)) raise class CaselessKeywordVsKeywordCaselessTest(ParseTestCase): def runTest(self): import pyparsing as pp frule = pp.Keyword('t', caseless=True) + pp.Keyword('yes', caseless=True) crule = pp.CaselessKeyword('t') + pp.CaselessKeyword('yes') flist = frule.searchString('not yes').asList() print_(flist) clist = crule.searchString('not yes').asList() print_(clist) self.assertEqual(flist, clist, "CaselessKeyword not working the same as Keyword(caseless=True)") class OneOfKeywordsTest(ParseTestCase): def runTest(self): import pyparsing as pp literal_expr = pp.oneOf("a b c") success, _ = literal_expr[...].runTests(""" # literal oneOf tests a b c a a a abc """) self.assertTrue(success, "failed literal oneOf matching") keyword_expr = pp.oneOf("a b c", asKeyword=True) success, _ = keyword_expr[...].runTests(""" # keyword oneOf tests a b c a a a """) self.assertTrue(success, "failed keyword oneOf matching") success, _ = keyword_expr[...].runTests(""" # keyword oneOf failure tests abc """, failureTests=True) self.assertTrue(success, "failed keyword oneOf failure tests") class WarnUngroupedNamedTokensTest(ParseTestCase): """ - warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results name is defined on a containing expression with ungrouped subexpressions that also have results names (default=True) """ def runTest(self): import pyparsing as pp ppc = pp.pyparsing_common pp.__diag__.warn_ungrouped_named_tokens_in_collection = True COMMA = pp.Suppress(',').setName("comma") coord = (ppc.integer('x') + COMMA + ppc.integer('y')) # this should emit a warning if PY_3: with self.assertWarns(UserWarning, msg="failed to warn with named repetition of" " ungrouped named expressions"): path = coord[...].setResultsName('path') pp.__diag__.warn_ungrouped_named_tokens_in_collection = False class WarnNameSetOnEmptyForwardTest(ParseTestCase): """ - warn_name_set_on_empty_Forward - flag to enable warnings whan a Forward is defined with a results name, but has no contents defined (default=False) """ def runTest(self): import pyparsing as pp pp.__diag__.warn_name_set_on_empty_Forward = True base = pp.Forward() if PY_3: with self.assertWarns(UserWarning, msg="failed to warn when naming an empty Forward expression"): base("x") class WarnOnMultipleStringArgsToOneOfTest(ParseTestCase): """ - warn_on_multiple_string_args_to_oneof - flag to enable warnings whan oneOf is incorrectly called with multiple str arguments (default=True) """ def runTest(self): import pyparsing as pp pp.__diag__.warn_on_multiple_string_args_to_oneof = True if PY_3: with self.assertWarns(UserWarning, msg="failed to warn when incorrectly calling oneOf(string, string)"): a = pp.oneOf('A', 'B') class EnableDebugOnNamedExpressionsTest(ParseTestCase): """ - enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent calls to ParserElement.setName() (default=False) """ def runTest(self): import pyparsing as pp import textwrap test_stdout = StringIO() with AutoReset(sys, 'stdout', 'stderr'): sys.stdout = test_stdout sys.stderr = test_stdout pp.__diag__.enable_debug_on_named_expressions = True integer = pp.Word(pp.nums).setName('integer') integer[...].parseString("1 2 3") expected_debug_output = textwrap.dedent("""\ Match integer at loc 0(1,1) Matched integer -> ['1'] Match integer at loc 1(1,2) Matched integer -> ['2'] Match integer at loc 3(1,4) Matched integer -> ['3'] Match integer at loc 5(1,6) Exception raised:Expected integer, found end of text (at char 5), (line:1, col:6) """) output = test_stdout.getvalue() print_(output) self.assertEqual(output, expected_debug_output, "failed to auto-enable debug on named expressions " "using enable_debug_on_named_expressions") class UndesirableButCommonPracticesTest(ParseTestCase): def runTest(self): import pyparsing as pp ppc = pp.pyparsing_common # While these are valid constructs, and they are not encouraged # there is apparently a lot of code out there using these # coding styles. # # Even though they are not encouraged, we shouldn't break them. # Create an And using a list of expressions instead of using '+' operator expr = pp.And([pp.Word('abc'), pp.Word('123')]) expr.runTests(""" aaa 333 b 1 ababab 32123 """) # Passing a single expression to a ParseExpression, when it really wants a sequence expr = pp.Or(pp.Or(ppc.integer)) expr.runTests(""" 123 456 abc """) class ChainedTernaryOperator(ParseTestCase): def runTest(self): import pyparsing as pp TERNARY_INFIX = pp.infixNotation( pp.pyparsing_common.integer, [ (("?", ":"), 3, pp.opAssoc.LEFT), ]) self.assertParseAndCheckList(TERNARY_INFIX, "1?1:0?1:0", [[1, '?', 1, ':', 0, '?', 1, ':', 0]]) TERNARY_INFIX = pp.infixNotation( pp.pyparsing_common.integer, [ (("?", ":"), 3, pp.opAssoc.RIGHT), ]) self.assertParseAndCheckList(TERNARY_INFIX, "1?1:0?1:0", [[1, '?', 1, ':', [0, '?', 1, ':', 0]]]) class MiscellaneousParserTests(ParseTestCase): def runTest(self): self.expect_warning = True runtests = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" if IRON_PYTHON_ENV: runtests = "ABCDEGHIJKLMNOPQRSTUVWXYZ" # test making oneOf with duplicate symbols if "A" in runtests: print_("verify oneOf handles duplicate symbols") try: test1 = pp.oneOf("a b c d a") except RuntimeError: self.assertTrue(False,"still have infinite loop in oneOf with duplicate symbols (string input)") print_("verify oneOf handles generator input") try: test1 = pp.oneOf(c for c in "a b c d a" if not c.isspace()) except RuntimeError: self.assertTrue(False,"still have infinite loop in oneOf with duplicate symbols (generator input)") print_("verify oneOf handles list input") try: test1 = pp.oneOf("a b c d a".split()) except RuntimeError: self.assertTrue(False,"still have infinite loop in oneOf with duplicate symbols (list input)") print_("verify oneOf handles set input") try: test1 = pp.oneOf(set("a b c d a")) except RuntimeError: self.assertTrue(False,"still have infinite loop in oneOf with duplicate symbols (set input)") # test MatchFirst bugfix if "B" in runtests: print_("verify MatchFirst iterates properly") results = pp.quotedString.parseString("'this is a single quoted string'") self.assertTrue(len(results) > 0, "MatchFirst error - not iterating over all choices") # verify streamline of subexpressions if "C" in runtests: print_("verify proper streamline logic") compound = pp.Literal("A") + "B" + "C" + "D" self.assertEqual(len(compound.exprs), 2,"bad test setup") print_(compound) compound.streamline() print_(compound) self.assertEqual(len(compound.exprs), 4,"streamline not working") # test for Optional with results name and no match if "D" in runtests: print_("verify Optional's do not cause match failure if have results name") testGrammar = pp.Literal("A") + pp.Optional("B")("gotB") + pp.Literal("C") try: testGrammar.parseString("ABC") testGrammar.parseString("AC") except pp.ParseException as pe: print_(pe.pstr,"->",pe) self.assertTrue(False, "error in Optional matching of string %s" % pe.pstr) # test return of furthest exception if "E" in runtests: testGrammar = ( pp.Literal("A") | ( pp.Optional("B") + pp.Literal("C") ) | pp.Literal("D") ) try: testGrammar.parseString("BC") testGrammar.parseString("BD") except pp.ParseException as pe: print_(pe.pstr,"->",pe) self.assertEqual(pe.pstr, "BD", "wrong test string failed to parse") self.assertEqual(pe.loc, 1, "error in Optional matching, pe.loc="+str(pe.loc)) # test validate if "F" in runtests: print_("verify behavior of validate()") def testValidation( grmr, gnam, isValid ): try: grmr.streamline() grmr.validate() self.assertTrue(isValid,"validate() accepted invalid grammar " + gnam) except pp.RecursiveGrammarException as e: print_(grmr) self.assertFalse(isValid, "validate() rejected valid grammar " + gnam) fwd = pp.Forward() g1 = pp.OneOrMore( ( pp.Literal("A") + "B" + "C" ) | fwd ) g2 = pp.ZeroOrMore("C" + g1) fwd << pp.Group(g2) testValidation( fwd, "fwd", isValid=True ) fwd2 = pp.Forward() fwd2 << pp.Group("A" | fwd2) testValidation( fwd2, "fwd2", isValid=False ) fwd3 = pp.Forward() fwd3 << pp.Optional("A") + fwd3 testValidation( fwd3, "fwd3", isValid=False ) # test getName if "G" in runtests: print_("verify behavior of getName()") aaa = pp.Group(pp.Word("a")("A")) bbb = pp.Group(pp.Word("b")("B")) ccc = pp.Group(":" + pp.Word("c")("C")) g1 = "XXX" + pp.ZeroOrMore( aaa | bbb | ccc ) teststring = "XXX b bb a bbb bbbb aa bbbbb :c bbbbbb aaa" names = [] print_(g1.parseString(teststring).dump()) for t in g1.parseString(teststring): print_(t, repr(t)) try: names.append( t[0].getName() ) except Exception: try: names.append( t.getName() ) except Exception: names.append( None ) print_(teststring) print_(names) self.assertEqual(names, [None, 'B', 'B', 'A', 'B', 'B', 'A', 'B', None, 'B', 'A'], "failure in getting names for tokens") from pyparsing import Keyword, Word, alphas, OneOrMore IF,AND,BUT = map(Keyword, "if and but".split()) ident = ~(IF | AND | BUT) + Word(alphas)("non-key") scanner = OneOrMore(IF | AND | BUT | ident) def getNameTester(s,l,t): print_(t, t.getName()) ident.addParseAction(getNameTester) scanner.parseString("lsjd sldkjf IF Saslkj AND lsdjf") # test ParseResults.get() method if "H" in runtests: print_("verify behavior of ParseResults.get()") # use sum() to merge separate groups into single ParseResults res = sum(g1.parseString(teststring)[1:]) print_(res.dump()) print_(res.get("A","A not found")) print_(res.get("D","!D")) self.assertEqual(res.get("A","A not found"), "aaa", "get on existing key failed") self.assertEqual(res.get("D","!D"), "!D", "get on missing key failed") if "I" in runtests: print_("verify handling of Optional's beyond the end of string") testGrammar = "A" + pp.Optional("B") + pp.Optional("C") + pp.Optional("D") testGrammar.parseString("A") testGrammar.parseString("AB") # test creating Literal with empty string if "J" in runtests: print_('verify non-fatal usage of Literal("")') e = pp.Literal("") try: e.parseString("SLJFD") except Exception as e: self.assertTrue(False, "Failed to handle empty Literal") # test line() behavior when starting at 0 and the opening line is an \n if "K" in runtests: print_('verify correct line() behavior when first line is empty string') self.assertEqual(pp.line(0, "\nabc\ndef\n"), '', "Error in line() with empty first line in text") txt = "\nabc\ndef\n" results = [ pp.line(i,txt) for i in range(len(txt)) ] self.assertEqual(results, ['', 'abc', 'abc', 'abc', 'abc', 'def', 'def', 'def', 'def'], "Error in line() with empty first line in text") txt = "abc\ndef\n" results = [ pp.line(i,txt) for i in range(len(txt)) ] self.assertEqual(results, ['abc', 'abc', 'abc', 'abc', 'def', 'def', 'def', 'def'], "Error in line() with non-empty first line in text") # test bugfix with repeated tokens when packrat parsing enabled if "L" in runtests: print_('verify behavior with repeated tokens when packrat parsing is enabled') a = pp.Literal("a") b = pp.Literal("b") c = pp.Literal("c") abb = a + b + b abc = a + b + c aba = a + b + a grammar = abb | abc | aba self.assertEqual(''.join(grammar.parseString( "aba" )), 'aba', "Packrat ABA failure!") if "M" in runtests: print_('verify behavior of setResultsName with OneOrMore and ZeroOrMore') stmt = pp.Keyword('test') print_(pp.ZeroOrMore(stmt)('tests').parseString('test test').tests) print_(pp.OneOrMore(stmt)('tests').parseString('test test').tests) print_(pp.Optional(pp.OneOrMore(stmt)('tests')).parseString('test test').tests) print_(pp.Optional(pp.OneOrMore(stmt))('tests').parseString('test test').tests) print_(pp.Optional(pp.delimitedList(stmt))('tests').parseString('test,test').tests) self.assertEqual(len(pp.ZeroOrMore(stmt)('tests').parseString('test test').tests), 2, "ZeroOrMore failure with setResultsName") self.assertEqual(len(pp.OneOrMore(stmt)('tests').parseString('test test').tests), 2, "OneOrMore failure with setResultsName") self.assertEqual(len(pp.Optional(pp.OneOrMore(stmt)('tests')).parseString('test test').tests), 2, "OneOrMore failure with setResultsName") self.assertEqual(len(pp.Optional(pp.delimitedList(stmt))('tests').parseString('test,test').tests), 2, "delimitedList failure with setResultsName") self.assertEqual(len((stmt*2)('tests').parseString('test test').tests), 2, "multiplied(1) failure with setResultsName") self.assertEqual(len((stmt*(None,2))('tests').parseString('test test').tests), 2, "multiplied(2) failure with setResultsName") self.assertEqual(len((stmt*(1,))('tests').parseString('test test').tests), 2, "multipled(3) failure with setResultsName") self.assertEqual(len((stmt*(2,))('tests').parseString('test test').tests), 2, "multipled(3) failure with setResultsName") def makeTestSuite(): import inspect suite = TestSuite() suite.addTest(PyparsingTestInit()) test_case_classes = ParseTestCase.__subclasses__() # put classes in order as they are listed in the source code test_case_classes.sort(key=lambda cls: inspect.getsourcelines(cls)[1]) test_case_classes.remove(PyparsingTestInit) # test_case_classes.remove(ParseASMLTest) test_case_classes.remove(EnablePackratParsing) if IRON_PYTHON_ENV: test_case_classes.remove(OriginalTextForTest) suite.addTests(T() for T in test_case_classes) if TEST_USING_PACKRAT: # retest using packrat parsing (disable those tests that aren't compatible) suite.addTest( EnablePackratParsing() ) unpackrattables = [ PyparsingTestInit, EnablePackratParsing, RepeaterTest, ] # add tests to test suite a second time, to run with packrat parsing # (leaving out those that we know wont work with packrat) packratTests = [t.__class__() for t in suite._tests if t.__class__ not in unpackrattables] suite.addTests( packratTests ) return suite def makeTestSuiteTemp(classes): suite = TestSuite() suite.addTest(PyparsingTestInit()) suite.addTests(cls() for cls in classes) return suite # runnable from setup.py using "python setup.py test -s unitTests.suite" suite = makeTestSuite() if __name__ == '__main__': # run specific tests by including them in this list, otherwise # all tests will be run testclasses = [ ] if not testclasses: testRunner = TextTestRunner() result = testRunner.run(suite) else: # disable chaser '.' display testRunner = TextTestRunner(verbosity=0) BUFFER_OUTPUT = False result = testRunner.run(makeTestSuiteTemp(testclasses)) sys.stdout.flush() exit(0 if result.wasSuccessful() else 1) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������