Skip to content

Conversion parsers

parser > function: conversion parser

Conversion parsers don't change how the text is parsed—they change the value returned. Every parser returns a value when it succeeds. The function supplied must take a single argument (the successful value) and returns a new value. This is how text is converted to other objects and simpler objects built into larger ones. In accordance with Python's operator precedence, > is the operator in Parsita with the loosest binding.

from parsita import *

class IntegerParsers(TextParsers):
    integer = reg(r'[-+]?[0-9]+') > int

assert IntegerParsers.integer.parse('-128') == Success(-128)

parser >= function: transformation parser

Transformation parsers pass the parsed output to a function which returns a new parser. This new parser is then immediately applied to the remaining input. There are two main uses for this parser.

Fallible conversion

The first use is as a fallible conversion parser. The conversion parser cannot tolerate failure. From the conversion parser's perspective, by the time the parsed output gets to the conversion function, parsing has already succeeded, just the output value is being changed. If the parsing needs to fail, then the transformation parser can be used. The transformer function can return a SuccessParser with success or a FailureParser with failure to declare success or failure based on its calculation.

The TransformationParser is basically equivalent to a PredicateParser followed by a ConversionParser. Where you would otherwise write pred(parser, is_valid_object) > create_object, you can write parser >= maybe_create_object, instead.

from dataclasses import dataclass

from parsita import *

@dataclass
class Percent:
    number: int

def to_percent(number: int) -> Parser[str, Percent]:
    if not 0 <= number <= 100:
        return failure("a number between 0 and 100")
    else:
        return success(Percent(number))

class PercentParsers(TextParsers):
    percent = (reg(r"[0-9]+") > int) >= to_percent

assert PercentParsers.percent.parse('50') == Success(Percent(50))
assert isinstance(PercentParsers.percent.parse('150'), Failure)

In the current version of Parsita, the error messages that come from this leave something to be desired. Right now in the core of Parsita, there is no way to separately specify where in the input parsing failed or what the actual token was. The location of the failure is always the farther point that input was consumed and the actual token is always the next token from that point. That means that the error message will always mark the token after what was successfully consumed and passed to >= before it was converted into a failure. Future versions of Parsita will address this issue.

Parsers parameterized by previous input

The second use is to parse later text based on previous text. This is incredibly powerful, but in my experience, not that useful. When viewing parser combinators as a monad, this is the bind operation, so functional programming enthusiasts are really into it. It is only included in Parsita because it came free with the fallible conversion parser use case above.

from parsita import *

def select_parser(type: str):
    if type == 'int':
        return reg(r"[0-9]+") > int
    elif type == 'decimal':
        return reg(r"[0-9]+\.[0-9]+") > float

class NumberParsers(TextParsers):
    type = lit('int', 'decimal')
    number = type >= select_parser

assert NumberParsers.number.parse('int 5') == Success(5)
assert isinstance(NumberParsers.number.parse('int 2.0'), Failure)