A Python parser combinator library inspired by Haskell's Parsec.
Project description
PyParsec
PyParsec is an industrial-strength Parser Combinator library for Python.
It is a faithful port of Haskell's legendary Parsec library, adapted for Python's idioms while maintaining mathematical correctness. Unlike many Python parser libraries, PyParsec is stack-safe, type-safe, and supports generic inputs (strings, bytes, or token lists).
Key Features
- 🛡️ Robust Error Handling: Distinguishes between "Empty" and "Consumed" failures, allowing precise control over backtracking using
try_parse. - 🚀 Stack-Safe Combinators:
many,choice, and expression chains are implemented iteratively. You won't hitRecursionErroron large inputs. - 🧩 Generic Input: Parse
str,bytes, orList[T](e.g., tokens from a lexer). TheSourcePoslogic is decoupled from the input type. - 🔋 Batteries Included: Includes the full suite of Parsec modules:
Token: Automatically generate lexers (whitespace, comments, identifiers) from a language definition.Expr: Built-in operator precedence parsing (Infix, Prefix, Postfix).
- 🐍 Pythonic Operators:
>>is polymorphic: behaves like Bind (>>=) when passed a function, and Sequence (*>) when passed a parser.|is Choice (<|>).
Installation
This project is managed with uv and hatchling. You can install it directly from the repository:
pip install pyarsec
import pyparsec,import parsecpy,import parsec, andimport pyarsecall work.
For development:
git clone https://github.com/khengari77/PyParsec.git
cd PyParsec
uv sync
Quick Start
1. Basic Parsing
Parsing a sequence of digits into an integer.
from pyparsec import many1, digit, run_parser, pure
# Logic: Parse one or more digits -> join them -> convert to int
integer = many1(digit()) >> (lambda ds: pure(int("".join(ds))))
result, err = run_parser(integer, "12345")
print(result) # 12345
2. Using the Token Module (Easy Lexing)
Don't write manual regexes. Use TokenParser to handle whitespace, comments, and data types automatically.
from pyparsec import TokenParser, python_style, run_parser
# Create a lexer based on Python syntax rules (handling # comments, etc.)
lexer = TokenParser(python_style)
# These parsers automatically skip trailing whitespace/comments!
integer = lexer.integer
parens = lexer.parens
identifier = lexer.identifier
# Parse: ( count )
parser = parens(identifier)
print(run_parser(parser, "( my_variable ) # comments are ignored"))
# Output: ('my_variable', None)
3. Operator Precedence (Expr Module)
Parsing mathematical expressions with correct order of operations (PEMDAS) is often difficult. PyParsec makes it declarative.
from pyparsec import build_expression_parser, Operator, Infix, Assoc, run_parser
from pyparsec import TokenParser, empty_def
# Setup a simple lexer
lexer = TokenParser(empty_def)
integer = lexer.integer
# Define functions
def add(x, y): return x + y
def mul(x, y): return x * y
# Define Operator Table
# Higher precedence comes first!
table = [
[Infix(lexer.reserved_op("*") >> (lambda _: mul), Assoc.LEFT)],
[Infix(lexer.reserved_op("+") >> (lambda _: add), Assoc.LEFT)]
]
expr = build_expression_parser(table, integer)
print(run_parser(expr, "2 + 3 * 4"))
# Output: 14 (not 20!)
Advanced: Generic Input (Parsing Tokens)
PyParsec isn't limited to strings. You can parse a list of objects produced by a separate lexing stage.
from dataclasses import dataclass
from pyparsec import token, run_parser, SourcePos
@dataclass
class Tok:
kind: str
value: str
# Define a primitive that consumes a Token object
def match_kind(kind):
return token(
show_tok=lambda t: f"Token({kind})",
test_tok=lambda t: t.value if t.kind == kind else None,
# Update position based on token logic (optional)
next_pos=lambda pos, t: SourcePos(pos.line, pos.column + 1, pos.name)
)
stream = [Tok("ID", "x"), Tok("EQ", "="), Tok("NUM", "10")]
# Grammar: ID = NUM
parser = match_kind("ID") >> match_kind("EQ") >> match_kind("NUM")
print(run_parser(parser, stream))
# Output: '10'
Module Overview
pyparsec.Prim: Core primitives (pure,bind,fail,try_parse,many).pyparsec.Char: String-specific parsers (char,string,alpha_num,one_of).pyparsec.Combinators: Logic flow (choice,optional,sep_by,between).pyparsec.Token: Automated lexer generation (TokenParser,LanguageDef).pyparsec.Expr: Operator precedence builder (build_expression_parser).pyparsec.Language: Pre-defined language styles (java_style,python_style,haskell_style).
Contributing
- Clone the repo.
- Install
uv. - Run tests:
uv run pytest tests/(We use Hypothesis for property-based testing).
Star History
License
MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyarsec-0.1.0.tar.gz.
File metadata
- Download URL: pyarsec-0.1.0.tar.gz
- Upload date:
- Size: 72.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28797562b836ee3e48928bacdc3d9bc245868d1862ec0b22f2e852772a5cdf00
|
|
| MD5 |
3d500c4062eacc5ffc62ef21629e56b3
|
|
| BLAKE2b-256 |
4a7f74260444f9a1712a1c6dac02ba9d9a8ac92f0a1aec670dd7b1fb1c8f0356
|
Provenance
The following attestation bundles were made for pyarsec-0.1.0.tar.gz:
Publisher:
publish.yml on khengari77/PyParsec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyarsec-0.1.0.tar.gz -
Subject digest:
28797562b836ee3e48928bacdc3d9bc245868d1862ec0b22f2e852772a5cdf00 - Sigstore transparency entry: 1039302678
- Sigstore integration time:
-
Permalink:
khengari77/PyParsec@b94192b2373d47fb5fc26edfbbfa3d26941ea651 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/khengari77
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b94192b2373d47fb5fc26edfbbfa3d26941ea651 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyarsec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyarsec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8eb84c5e3f0215415a7cad1a7fcc3a9bdc95cccd05e3feda5cbec93c64cf15aa
|
|
| MD5 |
2d56be10d7037fac0184fef4758cc29f
|
|
| BLAKE2b-256 |
22a20f5c230855ae5af1fdc454aef1bd5c83d7a87895d45778264fc52e73a3cc
|
Provenance
The following attestation bundles were made for pyarsec-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on khengari77/PyParsec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyarsec-0.1.0-py3-none-any.whl -
Subject digest:
8eb84c5e3f0215415a7cad1a7fcc3a9bdc95cccd05e3feda5cbec93c64cf15aa - Sigstore transparency entry: 1039302759
- Sigstore integration time:
-
Permalink:
khengari77/PyParsec@b94192b2373d47fb5fc26edfbbfa3d26941ea651 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/khengari77
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b94192b2373d47fb5fc26edfbbfa3d26941ea651 -
Trigger Event:
release
-
Statement type: