Skip to main content

A type-safe applicative parsing library

Project description

functional_parsing_library

A small production non-ready Python library implementing basic applicative parsers. Roughly speaking, these are functions with signature str -> T | CouldNotParse transforming strings into structured data. For example, you might have a function integer which will transform "1" and "-1919" to the integers 1 and -1919, and the string "boink" to CouldNotParse().

What makes these functions useful is that they can be combined with so-called parser combinators. This way, complicated parsers can be gradually built up from smaller, simpler parsers. For example, if we already have parsers nonnegative_integer and negative_integer, the integer parser from earlier could be written as integer = nonnegative_integer | negative_integer, where | should be read as "or". This library implements various such combinators, such as many, some, ignore_left, many_till, and so on.

Another piece of structure that makes these functions useful is that they're functorial: If I have a parser p of type Parser[T] (that is, a function which parses strings to objects of type T), and a function f: T -> S, then f * p will be a parser for objects of type S. For example, take len * many(word('borf')), and try to parse "borfborfborf". Here word('borf') will parse "borf" to the string "borf" (and any other string to CouldNotParse), so the parser many(word('borf') will try and match as many "borf"s as possible and parse our string to the list ['borf', 'borf', 'borf']. The length of this list is 3, so len * many(word('borf')) parses our string to the integer 3.

This works with multi-argument functions as well. If f is a function of type [T, S] -> U, and we have parsers p and q for objects of type T and S, then f * p & q will first try to match p, and if this succeeds it will try and match q, and finally it will apply f.

Another feature of this library is its type safety. Running mypy on

from functional_parsing_library.strings import word


def add_strings(one: str, two: str, three: str) -> int:
    return len(one + two + three)


reveal_type(add_strings * word('hi'))
reveal_type(add_strings * word('hi') & word('hi'))
reveal_type(add_strings * word('hi') & word('hi') & word('di'))

will show that the first parser has type MappedParser[int, str, str], the second MappedParser[int, str], and the third Parser[int]. Expressions like

add_strings * word('hi') & word('hi') & integer

or

add_strings * word('hi') & word('hi') & word('hi') & word('hi')

will raise a TypeError, and mypy will catch this.

Documentation

To see some documentation, clone this repo, run

make serve-documentation

and in your browser you can peruse this library's docstrings at port 8000.

TODO list

  • Backport to earlier Python versions, say 3.9 and up.
  • Ambiguity in parsing, for example char('a') | word('ab') should parse "ab" as both "a" with remainder "b" and as "ab" with no remainder.
  • Syntactic sugar for monadic structure of Parser. Probably context managers can be used to craft some makeshift Haskell-like do notation.
  • The failing tests in the todos module.
  • if p is a parser for some unbound typevar U, and f a function from U to Any, mypy does not deal well with the expression f * p.

Variable number of arguments

As it stands, mapping over parsers cannot be done with functions accepting a variable number of arguments. For example,

from functional_parsing_library.strings import any_char

def f(*x: str) -> str:
    return ''

f * any_char & any_char

results in an internal mypy error for mypy==1.10.1 and below. From mypy==1.11.0, the internal error is gone, and the snippet results in the following type error:

Unsupported operand types for & ("MappedParser[str, Never, *tuple[Never, ...]]" and "Parser[str]")  [operator]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

functional_parsing_library-0.0.25.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

functional_parsing_library-0.0.25-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file functional_parsing_library-0.0.25.tar.gz.

File metadata

File hashes

Hashes for functional_parsing_library-0.0.25.tar.gz
Algorithm Hash digest
SHA256 e3c2965f758b6827dca8b27085fa3bf2e4707eb4378469158d84ad1b3256de0a
MD5 56a2edc546d414c1389882a7842ade4d
BLAKE2b-256 742c8f866c3c401ec7ac554e0b57865191c515d02e6bb55388890498c2339933

See more details on using hashes here.

Provenance

The following attestation bundles were made for functional_parsing_library-0.0.25.tar.gz:

Publisher: ci.yml on wpbindt/functional_parsing_library

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file functional_parsing_library-0.0.25-py3-none-any.whl.

File metadata

File hashes

Hashes for functional_parsing_library-0.0.25-py3-none-any.whl
Algorithm Hash digest
SHA256 23be747378fc3135f483a40ccf3b79c8603e83dbc0f6cde210c4c9e741e151e7
MD5 a026f22d096b328f7afbb5509ee2b824
BLAKE2b-256 642e2840fd13c968d4324b86d2f9a6ee3c8d04e8aeb84df42fc6e48b995ad738

See more details on using hashes here.

Provenance

The following attestation bundles were made for functional_parsing_library-0.0.25-py3-none-any.whl:

Publisher: ci.yml on wpbindt/functional_parsing_library

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page