{ "info": { "author": "Mike Biggs", "author_email": "nfomon@users.noreply.github.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "# Parsik\n\nA simplistic PEG (parsing expression grammar) parser.\n\nYou provide a grammar for your language, and this module gives you a Parser\nthat you can use to recognize if documents (strings) match the grammar. You\ncan attach callback functions for when various parts of the grammar match, and\nto help construct some \"output\" of the parse (e.g. a parse tree).\n\nThe library is simple and unoptimized in time and space. And a grammar with\nleft-recursion could put the parser into an infinite-loop. But the\nimplementation is small and straightforward, and there is extensive logging to\nhelp analyze the parser's behaviour.\n\nIf you're looking for something even slightly more complex or elegant, I\nrecommend parsy (https://github.com/python-parsy/parsy).\n\n\n## Example\n\n```python\nfrom parsik import Parser, ParseFailure,\\\n Any, Char, EOF, Fail, OneOrMore, Optional, R,\\\n Regex, Sequence, Times, ZeroOrMore, silent\n\n# A grammar for 7- or 10-digit phone numbers.\ngrammar = {\n 'PHONENUM': Sequence(\n Optional('AREACODE'),\n 'THREE', silent(Char('-')), 'FOUR'\n ),\n 'AREACODE': Sequence(\n silent(Char('(')), 'THREE', silent(Regex(r'\\) ')),\n on_match=lambda x: x[0]\n ),\n 'THREE': Regex(r'\\d{3}'), # three digits\n 'FOUR': Regex(r'\\d{4}'), # four digits\n}\nparser = Parser(grammar, \"Phone numbers\")\n\noutput = parser.parse(\"PHONENUM\", \"123-4567\")\nassert output == [ '123', '4567']\n\noutput = parser.parse(\"PHONENUM\", \"(555) 123-4567\")\nassert output == [ '555', '123', '4567']\n\ntry:\n parser.parse(\"PHONENUM\", \"123-456\")\nexcept ParseFailure:\n pass\nelse:\n assert False\n```\n\n\n## Matchers\n\nParsik comes with several \"Matchers\" that you can use to express your grammar:\ncomponents like Sequence, Optional, ZeroOrMore, Regex, etc. These Matchers can\nbe composed and can refer to other rules in the grammar; even recursively, so\nlong as they aren't \"left-recursive\" which could cause an infinite-loop.\n\nThe included Matchers are:\n- Char: matches a single character.\n- Regex: matches a regular expression.\n- EOF: matches the end of the input string.\n- R: matches a rule that is defined elsewhere in the grammar.\n - but usually you can just use a string to refer to a rule by name.\n- Optional: optionally matches an item.\n- Any: matches any one of a number of possible items.\n- Sequence: matches a sequence of items in a specific order.\n- Times: matches an item a specific number of times, or range of times.\n- ZeroOrMore: matches an item zero or more times.\n- OneOrMore: matches an item one or more times.\n- Fail: if an item matches, then abort the parse.\n\nYou can also make your own types of Matchers.\n\nThere isn't any special syntax or operator-overloading for expressing the\ngrammar. It's just a dictionary where the keys are rule-names, and the values\nare Matchers. Some Matchers, like Sequence, are composed of other matchers or\nrules. They just take the list of child matchers in their constructor. You\ncan also refer to other grammar rules by name, even if it forms a cycle.\n\n\n## Logging\n\nParsik uses python's standard logging. If you enable it at DEBUG level:\n\n```\nlogging.basicConfig(level=logging.DEBUG, format='%(message)s')\n```\n\nthen you'll get an elaborate analysis of any parse attempt. For example:\n\n```python\ngrammar = {\n 'MAIN': Sequence('FIRST', 'SECOND', 'THIRD'),\n 'FIRST': ZeroOrMore(Regex(r'ab')),\n 'SECOND': Any(Char('x'), Char('y')),\n 'THIRD': 'FIRST',\n}\np = Parser(grammar, \"Quick example\")\np.parse(\"MAIN\", \"ababy\")\n```\n\nThis will log:\n\n```\nParsing Quick example {MAIN} against input string 'ababy'.\n\npos input output \u2713|\u2715 rule rule details\n------- ---------- ------------------- --- --------------- -------------------------\n0 'a' MAIN [[{FIRST},{SECOND},{THIRD}]]\n0 'a' {FIRST}\n0 'a' FIRST *(r'ab')\n0 'a' r'ab'\n0-2 'ab' 'ab' \u2713 r'ab'\n2 'a' r'ab'\n2-4 'ab' 'ab' \u2713 r'ab'\n4 'y' r'ab'\n4 '' \u2715 r'ab'\n0-4 'abab' '[ab, ab]' \u2713 FIRST *(r'ab')\n0-4 'abab' '[ab, ab]' \u2713 {FIRST}\n4 'y' {SECOND}\n4 'y' SECOND any(['x', 'y'])\n4 'y' 'x'\n4 '' \u2715 'x'\n4 'y' 'y'\n4-$ 'y' 'y' \u2713 'y'\n4-$ 'y' 'y' \u2713 SECOND any(['x', 'y'])\n4-$ 'y' 'y' \u2713 {SECOND}\n5 '$' {THIRD}\n5 '$' THIRD {FIRST}\n5 '$' FIRST *(r'ab')\n5 '$' r'ab'\n5-$ '$' \u2715 r'ab'\n5-$ '$' '[]' \u2713 FIRST *(r'ab')\n5-$ '$' '[]' \u2713 THIRD {FIRST}\n5-$ '$' '[]' \u2713 {THIRD}\n0-$ 'a' '[[ab, ab], y, []]' \u2713 MAIN [[{FIRST},{SECOND},{THIRD}]]\nParse success: '[[ab, ab], y, []]'.\n```\n\nEach row shows a step in the recursive-descent parse attempt. A Matcher will\nlog a line immediately before and immediately after it attempts to match. So,\none line where the \u2713|\u2715 column is blank, and another where it has the result.\n\nThe columns are:\n- pos: The position(s) (character #s) in the input document under view.\n - `$` means it has reached the end.\n- input: The text of the input document at those positions.\n- output: If a matcher was successful, this is its output.\n- \u2713|\u2715:\n - blank means that the matcher is going to attempt a match.\n - \u2713 means that the match was successful.\n - \u2715 means that the match failed.\n- rule: The name of the rule, if the matcher is a named rule in the grammar.\n- rule details: A concise representation of what the matcher will attempt to\n recognize. They display as:\n - Char: ``'x'``\n - Regex: ``r'pattern'``\n - EOF: ``EOF``\n - R: ``{rule name}``\n - Optional: ``?(...)``\n - Any: ``any(...)``\n - Sequence: ``[...]``\n - Times: ``X{min,max}(...)``\n - ZeroOrMore: ``*(...)``\n - OneOrMore: ``+(...)``\n - Fail: ``Fail(...)``\n\n\n## License\n\nThis package is licensed under the GNU GPL v3.\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/nfomon/parsik", "keywords": "parser,parsing-library,peg", "license": "", "maintainer": "", "maintainer_email": "", "name": "parsik", "package_url": "https://pypi.org/project/parsik/", "platform": "", "project_url": "https://pypi.org/project/parsik/", "project_urls": { "Homepage": "https://github.com/nfomon/parsik" }, "release_url": "https://pypi.org/project/parsik/0.9.3/", "requires_dist": null, "requires_python": "", "summary": "A minimalistic PEG parser", "version": "0.9.3" }, "last_serial": 5201605, "releases": { "0.9.1": [ { "comment_text": "", "digests": { "md5": "648bd63130cd83fa91c5bb75eb65a703", "sha256": "d0dfb1c1f42e3fbb00db5b41995b6c4d5b9aef8ffb7df475dce27932843a547d" }, "downloads": -1, "filename": "parsik-0.9.1-py3-none-any.whl", "has_sig": false, "md5_digest": "648bd63130cd83fa91c5bb75eb65a703", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 23766, "upload_time": "2019-04-28T09:56:39", "url": "https://files.pythonhosted.org/packages/b8/88/95d928008d610e6c92a65947d5259d9bb0683f04d5fb5f337bfc51cd2850/parsik-0.9.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "03cc01c55638fa1ab5adfcf931b026f2", "sha256": "10452b13c957b1243a3eb8e346aadcc3993443125adce6309d8abd2e798ae6e2" }, "downloads": -1, "filename": "parsik-0.9.1.tar.gz", "has_sig": false, "md5_digest": "03cc01c55638fa1ab5adfcf931b026f2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14345, "upload_time": "2019-04-28T09:56:49", "url": "https://files.pythonhosted.org/packages/ec/3b/84539025a2520da05892de9a9661fe41ab347bf69265d0ddd145f935342c/parsik-0.9.1.tar.gz" } ], "0.9.2": [ { "comment_text": "", "digests": { "md5": "0310622d8128dec73458a58c7703b080", "sha256": "063dcc17c14b11999f83039b0bab7747783db81e4e37b82eeb628847cc2b29f3" }, "downloads": -1, "filename": "parsik-0.9.2-py3-none-any.whl", "has_sig": false, "md5_digest": "0310622d8128dec73458a58c7703b080", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 30858, "upload_time": "2019-04-29T00:31:36", "url": "https://files.pythonhosted.org/packages/14/b8/1e74cfb408a78b20565771dfb2e6971cd71c926d3b58fbd57957beb698dc/parsik-0.9.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1a585a6c62061b9311d4e93565176b4e", "sha256": "bc1e31f9ca4b484c8907fecf356df8ede3e4d2a33d953da51bbe6b60c92cafde" }, "downloads": -1, "filename": "parsik-0.9.2.tar.gz", "has_sig": false, "md5_digest": "1a585a6c62061b9311d4e93565176b4e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14559, "upload_time": "2019-04-29T00:31:40", "url": "https://files.pythonhosted.org/packages/eb/07/96ed785eeef1f9a80a61e8119b60785fa7405353916db574830b20a8814a/parsik-0.9.2.tar.gz" } ], "0.9.3": [ { "comment_text": "", "digests": { "md5": "a604ec0bf958b56fc0065fa36207a227", "sha256": "a93ab56fd2b7944bb53326ac5451d6672ed5084f9600f0053d61d68f5dfd3755" }, "downloads": -1, "filename": "parsik-0.9.3-py3-none-any.whl", "has_sig": false, "md5_digest": "a604ec0bf958b56fc0065fa36207a227", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 30928, "upload_time": "2019-04-29T04:51:12", "url": "https://files.pythonhosted.org/packages/c8/89/eb4a15777cfcced325297a1999beff8def4f87987ad01bff6b774a70de97/parsik-0.9.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8b1a211098000be218361e1c3dabf70b", "sha256": "c43829bd01bacd1f74d57dca3e01cc8ffe7f05e2e5712b24cc9f1afbead562b7" }, "downloads": -1, "filename": "parsik-0.9.3.tar.gz", "has_sig": false, "md5_digest": "8b1a211098000be218361e1c3dabf70b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14604, "upload_time": "2019-04-29T04:51:15", "url": "https://files.pythonhosted.org/packages/2d/16/95324b36249cd946dd33e23130a6e6ba4c95e495e22e38baa2ee7c635b0f/parsik-0.9.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a604ec0bf958b56fc0065fa36207a227", "sha256": "a93ab56fd2b7944bb53326ac5451d6672ed5084f9600f0053d61d68f5dfd3755" }, "downloads": -1, "filename": "parsik-0.9.3-py3-none-any.whl", "has_sig": false, "md5_digest": "a604ec0bf958b56fc0065fa36207a227", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 30928, "upload_time": "2019-04-29T04:51:12", "url": "https://files.pythonhosted.org/packages/c8/89/eb4a15777cfcced325297a1999beff8def4f87987ad01bff6b774a70de97/parsik-0.9.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8b1a211098000be218361e1c3dabf70b", "sha256": "c43829bd01bacd1f74d57dca3e01cc8ffe7f05e2e5712b24cc9f1afbead562b7" }, "downloads": -1, "filename": "parsik-0.9.3.tar.gz", "has_sig": false, "md5_digest": "8b1a211098000be218361e1c3dabf70b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14604, "upload_time": "2019-04-29T04:51:15", "url": "https://files.pythonhosted.org/packages/2d/16/95324b36249cd946dd33e23130a6e6ba4c95e495e22e38baa2ee7c635b0f/parsik-0.9.3.tar.gz" } ] }