Registry / serialization / rply
library0.7.8pypypi✓ verified 24d ago

RPly is a pure Python parser generator, offering a modern API and compatibility with RPython. It is a re-implementation of David Beazley's PLY library. RPly simplifies the process of building lexers (tokenizers) and parsers (syntax analyzers) for domain-specific languages or custom syntaxes. The current version is 0.7.8, and it has a relatively slow release cadence.

pip install rply
INSTALL
IMPORT
SIG · RPLY
R
rply
serializationpythonv0.7.8
Install
1.5s avg
Import
24ms
Disk
16MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
Install & Compatibility
Where this runs
tested against v0.7.8 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
py 3.103.95 runs
installs and imports cleanly · install 0.0s · import 0.024s · 18MB
glibc
py 3.103.95 runs
installs and imports cleanly · install 1.5s · import 0.024s · 18MB
16MB installed
● package 16MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

LexerGenerator
from rply import LexerGenerator
ParserGenerator
from rply import ParserGenerator
BaseBox
from rply.token import BaseBox
Required for RPython compatibility or when building AST nodes for the parser.
LexingError
from rply.lexer import LexingError
Exception raised during lexing errors.
ParsingError
from rply import ParsingError
Exception raised during parsing errors.

This quickstart demonstrates how to create a simple arithmetic expression parser using RPly. It covers defining tokens with `LexerGenerator`, creating an Abstract Syntax Tree (AST) using classes inheriting from `BaseBox`, and defining grammar rules and precedence with `ParserGenerator` to evaluate expressions.

from rply import LexerGenerator, ParserGenerator, ParsingError from rply.token import BaseBox # 1. Define the Abstract Syntax Tree (AST) nodes class Number(BaseBox): def __init__(self, value): self.value = value def eval(self): return self.value class BinaryOp(BaseBox): def __init__(self, left, right): self.left = left self.right = right class Add(BinaryOp): def eval(self): return self.left.eval() + self.right.eval() class Sub(BinaryOp): def eval(self): return self.left.eval() - self.right.eval() class Mul(BinaryOp): def eval(self): return self.left.eval() * self.right.eval() class Div(BinaryOp): def eval(self): return self.left.eval() / self.right.eval() # 2. Build the Lexer lg = LexerGenerator() lg.add('NUMBER', r'\d+') lg.add('PLUS', r'\+') lg.add('MINUS', r'-') lg.add('MUL', r'\*') lg.add('DIV', r'/') lg.add('OPEN_PAREN', r'\(') lg.add('CLOSE_PAREN', r'\)') lg.ignore(r'\s+') lexer = lg.build() # 3. Build the Parser pg = ParserGenerator( ['NUMBER', 'PLUS', 'MINUS', 'MUL', 'DIV', 'OPEN_PAREN', 'CLOSE_PAREN'], precedence=[('left', ['PLUS', 'MINUS']), ('left', ['MUL', 'DIV'])] ) @pg.production('expression : NUMBER') def expression_number(p): return Number(int(p[0].getstr())) @pg.production('expression : OPEN_PAREN expression CLOSE_PAREN') def expression_paren(p): return p[1] @pg.production('expression : expression PLUS expression') def expression_plus(p): return Add(p[0], p[2]) @pg.production('expression : expression MINUS expression') def expression_minus(p): return Sub(p[0], p[2]) @pg.production('expression : expression MUL expression') def expression_mul(p): return Mul(p[0], p[2]) @pg.production('expression : expression DIV expression') def expression_div(p): return Div(p[0], p[2]) @pg.error def error_handler(token): raise ValueError("Ran into a %s where it wasn't expected" % token.gettokentype()) parser = pg.build() # 4. Use the Lexer and Parser text = "(10 + 5) * 2 / 3 - 1" tokens = lexer.lex(text) try: result = parser.parse(tokens).eval() print(f"Result of '{text}': {result}") except ParsingError as e: print(f"Parsing error at position {e.getsourcepos()}: {e}") except ValueError as e: print(f"Error: {e}")
Debug
Known issues
gotchaWhen targeting RPython, AST nodes passed between parser productions *must* inherit from `rply.token.BaseBox`. This ensures type compatibility in the RPython type inference system. For pure Python usage, this inheritance is not strictly necessary but is good practice if RPython compatibility might be a future goal.
fix
Ensure all classes used as AST nodes in parser productions inherit from `rply.token.BaseBox`.
affects: All versions
gotchaCustom error handlers provided to `ParserGenerator` or `LexerGenerator` must raise an exception to correctly signal a parsing or lexing error. If an error handler merely returns, the parser/lexer will attempt to continue, potentially leading to incorrect results or infinite loops.
fix
Always raise an exception (e.g., `ValueError`, `ParsingError`, `LexingError`) within custom error handler functions.
affects: All versions
gotchaOmitting or incorrectly defining precedence rules in `ParserGenerator` can lead to ambiguous grammars and unexpected parse trees, especially with operators like multiplication/division and addition/subtraction.
fix
Carefully define precedence rules using `precedence=[('associativity', ['TOKEN1', 'TOKEN2'])]` in the `ParserGenerator` constructor. Operators with higher precedence should appear later in the list.
affects: All versions
Errors
Common errors & fixes
LexingError: No token defined for
The lexer encountered characters in the input string for which no corresponding token rule has been defined in the LexerGenerator.
fix
Add or modify a token definition using `@lg.add('TOKEN_NAME', r'regex_pattern')` to cover the unhandled characters, or define an `@lg.ignore(r'\s+')` rule for whitespace and other ignorable patterns.
ParsingError: Syntax error at
The sequence of tokens generated by the lexer does not conform to any of the grammar rules defined in the ParserGenerator.
fix
Review the grammar rules defined with `@pg.production('rule : SYMBOL_1 SYMBOL_2')` to ensure they correctly describe the desired language syntax, and verify that the input token stream matches these rules.
rply.errors.RPlyError: Parser has no start rule
The ParserGenerator was built without explicitly defining a starting non-terminal symbol for the grammar, which is required for parsing to begin.
fix
Provide a start rule by passing the `start` argument to the `ParserGenerator` constructor, for example: `pg = ParserGenerator(tokens_list, precedence=precedence_list, start='my_start_rule')`.
rply.errors.RPlyError: Rule 'some_rule : SOME_TOKEN' must have a token named 'SOME_TOKEN'
A production rule defined in the ParserGenerator references a token name ('SOME_TOKEN') that was not declared in the list of tokens passed to the ParserGenerator or defined in the LexerGenerator.
fix
Ensure all token names used in parser production rules are correctly listed in the `tokens` argument when initializing `ParserGenerator` and are also defined as tokens in the `LexerGenerator`.
ModuleNotFoundError: No module named 'rply'
The `rply` library is either not installed, or an incorrect submodule path (like `rply.lexer` or `rply.parser`) is being used for import.
fix
Ensure `rply` is installed (`pip install rply`), and use direct imports like `from rply import LexerGenerator, ParserGenerator`.
Upgrade
Version history
0.7.8latest on PyPI · released Jan 27, 2021
Audit
Dependencies

No dependency data recorded yet.

Agent activity
12 hits · last 30 days
node
10
Resources