{"id":13595688,"url":"https://github.com/alex/rply","last_synced_at":"2025-04-14T23:27:31.653Z","repository":{"id":48632618,"uuid":"5308421","full_name":"alex/rply","owner":"alex","description":"An attempt to port David Beazley's PLY to RPython, and give it a cooler API.","archived":false,"fork":false,"pushed_at":"2023-01-21T19:10:13.000Z","size":196,"stargazers_count":378,"open_issues_count":34,"forks_count":60,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-05-17T14:41:54.168Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alex.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-08-06T00:56:54.000Z","updated_at":"2024-04-14T11:21:18.000Z","dependencies_parsed_at":"2023-02-12T12:16:21.404Z","dependency_job_id":null,"html_url":"https://github.com/alex/rply","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex%2Frply","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex%2Frply/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex%2Frply/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex%2Frply/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alex","download_url":"https://codeload.github.com/alex/rply/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248977064,"owners_count":21192518,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T16:01:55.576Z","updated_at":"2025-04-14T23:27:31.627Z","avatar_url":"https://github.com/alex.png","language":"Python","funding_links":[],"categories":["Python","Tools and Frameworks"],"sub_categories":["Python"],"readme":"RPLY\n====\n\n.. image:: https://secure.travis-ci.org/alex/rply.png\n    :target: https://travis-ci.org/alex/rply\n\nWelcome to RPLY! A pure Python parser generator, that also works with RPython.\nIt is a more-or-less direct port of David Beazley's awesome PLY, with a new\npublic API, and RPython support.\n\nYou can find the documentation `online`_.\n\nBasic API:\n\n.. code:: python\n\n    from rply import ParserGenerator, LexerGenerator\n    from rply.token import BaseBox\n\n    lg = LexerGenerator()\n    # Add takes a rule name, and a regular expression that defines the rule.\n    lg.add(\"PLUS\", r\"\\+\")\n    lg.add(\"MINUS\", r\"-\")\n    lg.add(\"NUMBER\", r\"\\d+\")\n\n    lg.ignore(r\"\\s+\")\n\n    # This is a list of the token names. precedence is an optional list of\n    # tuples which specifies order of operation for avoiding ambiguity.\n    # precedence must be one of \"left\", \"right\", \"nonassoc\".\n    # cache_id is an optional string which specifies an ID to use for\n    # caching. It should *always* be safe to use caching,\n    # RPly will automatically detect when your grammar is\n    # changed and refresh the cache for you.\n    pg = ParserGenerator([\"NUMBER\", \"PLUS\", \"MINUS\"],\n            precedence=[(\"left\", ['PLUS', 'MINUS'])], cache_id=\"myparser\")\n\n    @pg.production(\"main : expr\")\n    def main(p):\n        # p is a list, of each of the pieces on the right hand side of the\n        # grammar rule\n        return p[0]\n\n    @pg.production(\"expr : expr PLUS expr\")\n    @pg.production(\"expr : expr MINUS expr\")\n    def expr_op(p):\n        lhs = p[0].getint()\n        rhs = p[2].getint()\n        if p[1].gettokentype() == \"PLUS\":\n            return BoxInt(lhs + rhs)\n        elif p[1].gettokentype() == \"MINUS\":\n            return BoxInt(lhs - rhs)\n        else:\n            raise AssertionError(\"This is impossible, abort the time machine!\")\n\n    @pg.production(\"expr : NUMBER\")\n    def expr_num(p):\n        return BoxInt(int(p[0].getstr()))\n\n    lexer = lg.build()\n    parser = pg.build()\n\n    class BoxInt(BaseBox):\n        def __init__(self, value):\n            self.value = value\n\n        def getint(self):\n            return self.value\n\nThen you can do:\n\n.. code:: python\n\n    parser.parse(lexer.lex(\"1 + 3 - 2+12-32\"))\n\nYou can also substitute your own lexer. A lexer is an object with a ``next()``\nmethod that returns either the next token in sequence, or ``None`` if the token\nstream has been exhausted.\n\nWhy do we have the boxes?\n-------------------------\n\nIn RPython, like other statically typed languages, a variable must have a\nspecific type, we take advantage of polymorphism to keep values in a box so\nthat everything is statically typed. You can write whatever boxes you need for\nyour project.\n\nIf you don't intend to use your parser from RPython, and just want a cool pure\nPython parser you can ignore all the box stuff and just return whatever you\nlike from each production method.\n\nError handling\n--------------\n\nBy default, when a parsing error is encountered, an ``rply.ParsingError`` is\nraised, it has a method ``getsourcepos()``, which returns an\n``rply.token.SourcePosition`` object.\n\nYou may also provide an error handler, which, at the moment, must raise an\nexception. It receives the ``Token`` object that the parser errored on.\n\n.. code:: python\n\n    pg = ParserGenerator(...)\n\n    @pg.error\n    def error_handler(token):\n        raise ValueError(\"Ran into a %s where it wasn't expected\" % token.gettokentype())\n\nPython compatibility\n--------------------\n\nRPly is tested and known to work under Python 2.7, 3.4+, and PyPy. It is\nalso valid RPython for PyPy checkouts from ``6c642ae7a0ea`` onwards.\n\nLinks\n-----\n\n* `Source code and issue tracker \u003chttps://github.com/alex/rply/\u003e`_\n* `PyPI releases \u003chttps://pypi.python.org/pypi/rply\u003e`_\n* `Talk at PyCon US 2013: So you want to write an interpreter? \u003chttp://pyvideo.org/video/1694/so-you-want-to-write-an-interpreter\u003e`_\n\n.. _`online`: https://rply.readthedocs.io/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex%2Frply","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falex%2Frply","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex%2Frply/lists"}