Easy-to-use parser combinators for parsing in pure Python
—
The Parser class is the fundamental building block of parsy. It wraps parsing functions and provides methods for transformation, combination, and execution.
Creates a new parser by wrapping a parsing function that takes a stream and index, returning a Result object.
class Parser:
def __init__(self, wrapped_fn):
"""
Create a parser from a function.
Args:
wrapped_fn: Function that takes (stream, index) and returns Result
"""
def __call__(self, stream, index):
"""
Execute the parser on a stream at the given index.
Args:
stream: Input string or list of tokens to parse
index: Starting position in the stream
Returns:
Result: Result object with parsing outcome
"""Execute the parser on input streams with complete or partial parsing options.
def parse(self, stream):
"""
Parse a string completely and return the result or raise ParseError.
Args:
stream: Input string or list of tokens to parse
Returns:
Parsed result value
Raises:
ParseError: If parsing fails
"""
def parse_partial(self, stream):
"""
Parse the longest possible prefix of input.
Args:
stream: Input string or list of tokens to parse
Returns:
tuple: (result_value, remaining_stream)
Raises:
ParseError: If parsing fails
"""Transform parser results using mapping and binding operations.
def bind(self, bind_fn):
"""
Monadic bind operation for parser chaining.
Args:
bind_fn: Function that takes parse result and returns new Parser
Returns:
Parser: New parser with bound computation
"""
def map(self, map_fn):
"""
Transform the result of successful parsing.
Args:
map_fn: Function to transform the parse result
Returns:
Parser: New parser with transformed result
"""
def combine(self, combine_fn):
"""
Transform parser result by unpacking as positional arguments.
Args:
combine_fn: Function to receive *args from parse result
Returns:
Parser: New parser with combined result
"""
def combine_dict(self, combine_fn):
"""
Transform parser result by unpacking as keyword arguments.
Args:
combine_fn: Function to receive **kwargs from parse result
Returns:
Parser: New parser with combined result
"""
def concat(self):
"""
Join string results using ''.join().
Returns:
Parser: New parser with concatenated string result
"""
def result(self, res):
"""
Replace parser result with constant value.
Args:
res: Constant value to return instead of parse result
Returns:
Parser: New parser that returns constant value
"""Combine parsers in sequence with different result handling strategies.
def then(self, other):
"""
Parse this parser then other, returning other's result.
Args:
other: Parser to execute after this one
Returns:
Parser: New parser returning right-hand result
"""
def skip(self, other):
"""
Parse this parser then other, returning this parser's result.
Args:
other: Parser to execute after this one (result ignored)
Returns:
Parser: New parser returning left-hand result
"""Parse repeated occurrences of patterns with various quantifiers.
def many(self):
"""
Parse zero or more occurrences.
Returns:
Parser: New parser returning list of results
"""
def times(self, min, max=None):
"""
Parse specific number of occurrences.
Args:
min: Minimum number of occurrences
max: Maximum number of occurrences (defaults to min)
Returns:
Parser: New parser returning list of results
"""
def at_most(self, n):
"""
Parse at most n occurrences.
Args:
n: Maximum number of occurrences
Returns:
Parser: New parser returning list of results
"""
def at_least(self, n):
"""
Parse at least n occurrences.
Args:
n: Minimum number of occurrences
Returns:
Parser: New parser returning list of results
"""
def optional(self):
"""
Parse zero or one occurrence.
Returns:
Parser: New parser returning result or None
"""
def sep_by(self, sep, *, min=0, max=float('inf')):
"""
Parse separated list of items.
Args:
sep: Parser for separator between items
min: Minimum number of items (default 0)
max: Maximum number of items (default infinity)
Returns:
Parser: New parser returning list of items
"""Add descriptions and position tracking to parsers for better error messages and debugging.
def desc(self, description):
"""
Add description for error messages.
Args:
description: String description of what parser expects
Returns:
Parser: New parser with description
"""
def mark(self):
"""
Add position information to parse results.
Returns:
Parser: New parser returning (start_pos, result, end_pos)
"""
def tag(self, name):
"""
Tag result with name.
Args:
name: Tag name for the result
Returns:
Parser: New parser returning (name, result) tuple
"""Create parsers that succeed when other parsers fail.
def should_fail(self, description):
"""
Succeed only if this parser fails.
Args:
description: Description for success case
Returns:
Parser: New parser that succeeds on failure
"""Convenient operators for common parser operations.
def __add__(self, other):
"""Concatenate results using + operator."""
def __mul__(self, other):
"""Repeat parser using * operator (accepts int or range)."""
def __or__(self, other):
"""Alternative parser using | operator."""
def __rshift__(self, other):
"""Then operation using >> operator."""
def __lshift__(self, other):
"""Skip operation using << operator."""from parsy import string, regex, generate
# Basic transformation
number = regex(r'\d+').map(int)
result = number.parse('123') # Returns 123
# Sequencing with then/skip
quoted_string = (
string('"') >>
regex(r'[^"]*') <<
string('"')
)
result = quoted_string.parse('"hello"') # Returns 'hello'
# Repetition
numbers = regex(r'\d+').map(int).sep_by(string(','))
result = numbers.parse('1,2,3,4') # Returns [1, 2, 3, 4]
# Optional parsing
maybe_sign = string('-').optional()
signed_number = (maybe_sign + regex(r'\d+').map(int)).combine(
lambda sign, num: -num if sign else num
)
result = signed_number.parse('-42') # Returns -42Install with Tessl CLI
npx tessl i tessl/pypi-parsy