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
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.024s · 18MB
glibcpy 3.10–3.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}")
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.
fixAdd 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.
fixReview 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.
fixProvide 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.
fixEnsure 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.
fixEnsure `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.