{ "info": { "author": "R\u00e9my Sanchez", "author_email": "remy.sanchez@hyperthese.net", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3 :: Only", "Topic :: Software Development :: Libraries" ], "description": "Non-String Regular Expressions\n==============================\n\nRegular expressions are used to match strings of characters, however the\nconcept can be applied to anything else. This engine allows you to match\nany list of any type of objects using the same kind of constructs that\nregular expressions allow.\n\nThe algorithm is (far away but) based on `this\narticle `__ by Russ Cox, aka\nuses the Thomson NFA algorithm (because it's apparently more efficient\nbut mostly because it's the first explanation of a RE engine that I\nunderstood).\n\nHowever the package doesn't support (yet?) the regular expression syntax\nthat everybody is used to (because it allows to do different things).\n\n **Note** \u2014 The current implementation is a pile of crap because I\n have no idea what I'm doing\n\nInstallation\n------------\n\n::\n\n pip install nsre\n\nThen from your project you can\n\n.. code:: python\n\n from nsre import *\n\nConcept demo\n------------\n\nBy example, suppose that you have a list of dictionaries with a ``type``\nkey which indicates the type of content. You want to check certain\npatterns in that list. Suppose you want a series if ``image`` that might\nhave an attached ``caption`` then all of that is followed by a series of\n``text``.\n\nConceptually, it would like like this\n\n::\n\n (image caption?)* text+\n\nWhich once translated into NSRE looks like this\n\n.. code:: python\n\n from nsre import *\n\n re = AnyNumber(\n Symbol(KeyHasValue(\"type\", \"image\")) + Maybe(KeyHasValue(\"type\", \"caption\"))\n ) + Range(KeyHasValue(\"type\", \"text\"), min=1)\n\n assert re.match(\n [\n {\"type\": \"image\", \"url\": \"https://img1.jpg\"},\n {\"type\": \"image\", \"url\": \"https://img2.jpg\"},\n {\"type\": \"image\", \"url\": \"https://img3.jpg\"},\n {\"type\": \"caption\", \"text\": \"Image 3\"},\n {\"type\": \"image\", \"url\": \"https://img4.jpg\"},\n {\"type\": \"caption\", \"text\": \"Image 4\"},\n {\"type\": \"image\", \"url\": \"https://img5.jpg\"},\n {\"type\": \"text\", \"text\": \"Hello\"},\n {\"type\": \"text\", \"text\": \"Foo\"},\n {\"type\": \"text\", \"text\": \"Bar\"},\n ]\n )\n\nExamples\n--------\n\nBecause all of that is very abstract, let's have a look at more\nexamples:\n\nString RE\n~~~~~~~~~\n\nSuppose that you want to `match a\nstring `__,\nthe traditional RE would be:\n\n::\n\n \"([^\"\\\\]|\\\\.)*\"\n\nYou can translate this into:\n\n.. code:: python\n\n re = (\n Symbol('\"')\n + AnyNumber(Symbol(Neg(InSet('\"\\\\'))) | Chain([\"\\\\\", All()]))\n + Symbol('\"')\n )\n\nEmail validation\n~~~~~~~~~~~~~~~~\n\nThere is a notable debate around how to validate email addresses,\nhowever let's consider for the exercise the following expression:\n\n::\n\n [a-z]+([\\.-][a-z]+)*@[a-z]+([\\.\\-][a-z]+)*\\.[a-z]+\n\nNow let's see how that translates in NSRE:\n\n.. code:: python\n\n letter = ChrRanges((\"a\", \"z\"))\n join = ChrRanges((\".\", \".\"), (\"-\", \"-\"))\n\n re = (\n Range(letter, min=1)\n + AnyNumber(Symbol(join) + Range(letter, min=1))\n + Symbol(\"@\")\n + Range(letter, min=1)\n + AnyNumber(Symbol(join) + Range(letter, min=1))\n + Symbol(\".\")\n + Range(letter, min=1)\n )\n\n assert re.match(\"remy.sanchez@with-madrid.com\")\n\nReference\n---------\n\nThere is two kind of objects provided:\n\n- Comparators \u2014 It's mostly the equivalent of writing ``a`` or\n ``[abc]`` in a regular expression. It will do a test (equal, is\n instance of, etc) on the considered value to see if it's a match.\n- FSM \u2014 Builds the finite-state machine that will be used to test the\n regular expression. Those objects match the constructs like ``*`` or\n ``?``.\n\nComparators\n~~~~~~~~~~~\n\n``InSet``\n^^^^^^^^^\n\nChecks if the compared value is found within a set (faster than finding\nit in a list but it means that the base type T has to be hashable).\n\n.. code:: python\n\n assert Symbol(InSet([1, 2, 3])).match([1])\n\n``InList``\n^^^^^^^^^^\n\nChecks if the compared value is found within a list. It means that the\nbase type T has to be comparable with ``__eq__()``.\n\n.. code:: python\n\n assert Symbol(InList([1, 2, 3])).match([1])\n\n``IsInstance``\n^^^^^^^^^^^^^^\n\nTests if the provided value is an instance of any of the classes passed\nto the constructor.\n\n.. code:: python\n\n assert Symbol(IsInstance(A, B, C)).match([A()])\n\n``AttributeHasValue``\n^^^^^^^^^^^^^^^^^^^^^\n\nTests the compared value to see if their ``attribute`` has the right\n``value``.\n\n.. code:: python\n\n class Foo:\n foo = 'bar'\n\n assert Symbol(AttributeHasValue('foo', 'bar')).match([Foo()])\n\n``KeyHasValue``\n^^^^^^^^^^^^^^^\n\nTests the compared Dict to see if their ``key`` has the right ``value``.\n\n.. code:: python\n\n assert Symbol(KeyHasValue('foo', 'bar')).match([{'foo': 'bar'}])\n\n``Neg``\n^^^^^^^\n\nNegates the output of any comparator or raw value\n\n.. code:: python\n\n assert Symbol(Neg('a')).match('b')\n assert not Symbol(Neg('a')).match('a')\n\n``All``\n^^^^^^^\n\nMatches anything.\n\n.. code:: python\n\n assert Symbol(All()).match('a')\n assert Symbol(All()).match([1])\n\n``ChrRanges``\n^^^^^^^^^^^^^\n\nFor a given character, checks that it is within a given set of ranges.\n\n.. code:: python\n\n assert Symbol(ChrRanges(('a', 'z'), ('A', 'Z'))).match('X')\n\nFSM\n~~~\n\nSymbol\n^^^^^^\n\nMatches exactly 1 symbol\n\n.. code:: python\n\n re = Symbol(\"a\")\n\n assert re.match(\"a\")\n assert not re.match(\"b\")\n assert not re.match(\"aa\")\n\nChain\n^^^^^\n\nMatches exactly a chain of symbols\n\n.. code:: python\n\n re = Chain('hello')\n\n assert re.match('hello')\n\n``+``\n^^^^^\n\nPuts two rules next to each other\n\n.. code:: python\n\n re = Symbol('h') + Symbol('e') + Symbol('l') + Symbol('l') + Symbol('o')\n\n assert re.match('hello')\n\n``|``\n^^^^^\n\nBranches different options\n\n.. code:: python\n\n re = Chain('My name is ') + (Chain('Foo') | Chain('Bar'))\n\n assert re.match('My name is Foo')\n\nMaybe (equivalent to ``?``)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nMatches 0 or 1 occurrences\n\n.. code:: python\n\n re = Chain('Call me') + Maybe(Chain(' maybe'))\n\n assert re.match('Call me')\n assert re.match('Call me maybe')\n\nAnyNumber (equivalent to ``*``)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nMatches 0 or more occurrences\n\n.. code:: python\n\n re = AnyNumber(Chain(\"na \")) + Chain(\"Batman!\")\n\n assert re.match(\"na na na na Batman!\")\n\nRange (equivalent to ``{min,max}`` and ``+``)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nMatches from min to max number of occurrences. By default ``min`` is 0\nand ``max`` is infinity.\n\n.. code:: python\n\n re = Maybe(Chain('Cam')) + Range('a', min=3) + Chain('rgh')\n\n assert re.match('Camaaaaaaaargh')\n\nPerformance\n-----------\n\nIt's terrible\n\nDevelopment\n-----------\n\nThere is no dependencies per se for NSRE, however there is several\npackages to help on the development in ``requirements.txt``.\n\nInstall the dev dependencies\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n pip install -r requirements.txt\n\nUpdate dependencies\n~~~~~~~~~~~~~~~~~~~\n\nEdit ``requirements.txt`` then run\n\n::\n\n make venv\n\nRun tests\n~~~~~~~~~\n\nUnit tests run with ``pytest``.\n\n::\n\n make test\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Xowap/nsre", "keywords": "", "license": "WTFPL", "maintainer": "", "maintainer_email": "", "name": "nsre", "package_url": "https://pypi.org/project/nsre/", "platform": "", "project_url": "https://pypi.org/project/nsre/", "project_urls": { "Homepage": "https://github.com/Xowap/nsre" }, "release_url": "https://pypi.org/project/nsre/0.1.0/", "requires_dist": null, "requires_python": "", "summary": "Non-String Regular Expressions, making RegExps more abstract", "version": "0.1.0" }, "last_serial": 5589094, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "296e4f685b7f831770877dda953e3969", "sha256": "9d275bf795af44cf990a5a2042533253bbe37cfe8c22e4e4f8338ae9bf26f125" }, "downloads": -1, "filename": "nsre-0.1.0.tar.gz", "has_sig": false, "md5_digest": "296e4f685b7f831770877dda953e3969", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10057, "upload_time": "2019-07-26T13:39:13", "url": "https://files.pythonhosted.org/packages/3f/02/04533a99fc78e2a703c995e56c1730225ddfa4d36f1e74c2fceb9d09b92e/nsre-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "296e4f685b7f831770877dda953e3969", "sha256": "9d275bf795af44cf990a5a2042533253bbe37cfe8c22e4e4f8338ae9bf26f125" }, "downloads": -1, "filename": "nsre-0.1.0.tar.gz", "has_sig": false, "md5_digest": "296e4f685b7f831770877dda953e3969", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10057, "upload_time": "2019-07-26T13:39:13", "url": "https://files.pythonhosted.org/packages/3f/02/04533a99fc78e2a703c995e56c1730225ddfa4d36f1e74c2fceb9d09b92e/nsre-0.1.0.tar.gz" } ] }