flatparse is a high-performance parsing library, supporting parsing for programming languages, human-readable data and machine-readable data. The “flat” in the name refers to the
ByteString parsing input, which has pinned contiguous data, and also to the library internals, which avoids indirections and heap allocations whenever possible.
flatparse is generally lower-level than
parsec-style libraries, but it is possible to build higher-level features (such as source spans, hints, indentation parsing) on top of it, without making any compromises in performance.
It is advised to build with
when using this package, since that can result in significant speedups (20-40%
from what I’ve seen). Additionally, you can enable
specifically by enabling the
llvm package flag. However, this has minor
impact, since almost all parser code will be typically inlined into modules
flatparse, and compiled there.
Features and non-features
- Excellent performance. On microbenchmarks,
flatparseis around 10 times faster than
megaparsec. On larger examples with heavier use of source positions and spans and/or indentation parsing, the performance difference grows to 20-30 times. Compile times and exectuable sizes are also significantly better with
flatparseinternals make liberal use of unboxed tuples and GHC primops. As a result, pure validators (parsers returning
flatparseare not difficult to implement with zero heap allocation.
- No incremental parsing, and only strict
ByteStringis supported as input. However, it can be still useful to convert from
Stringor other types to
ByteString, and then use
flatparsefor parsing, since
flatparseperformance usually more than makes up for the conversion costs.
- Only little-endian 64 bit systems are currently supported as the host machine. This may change in the future. Getting good performance requires architecture-specific optimizations; I’ve only considered the most common setting at this point. However,
flatparsedoes include specific big-endian parsers for primitive integer types.
- Support for fast source location handling, indentation parsing and informative error messages.
flatparseprovides a low-level interface to these. Batteries are not included, but it should be possible for users to build custom solutions, which are more sophisticated, but still as fast as possible. In my experience, the included batteries in other libraries often come with major unavoidable overheads, and often we still have to extend existing machinery in order to scale to production features.
- The backtracking model of
flatparseis different to parsec libraries, and is more close to the nom library in Rust. The idea is that parser failure is distinguished from parsing error. The former is used for control flow, and we can backtrack from it. The latter is used for unrecoverable errors, and by default it’s propagated to the top.
flatparsedoes not track whether parsers have consumed inputs. In my experience, what we really care about is the failure/error distinction, and in
megaparsecthe consumed/non-consumed separation is often muddled and discarded in larger parser implementations. By default, basic
flatparseparsers can fail but can not throw errors, with the exception of the specifically error-throwing operations. Hence,
flatparseusers have to be mindful about grammar, and explicitly insert errors where it is known that the input can’t be valid.
FlatParse.Basiconly supports the above features. If you don’t need indentation parsing, this is sufficient.
FlatParse.Statefuladditionally supports a built-in
Intworth of internal state and an additional custom reader environemnt. This can support a wide range of indentation parsing features. There is a slight overhead in performance and code size compared to
Basic. However, in small parsers and microbenchmarks the difference between
Statefulis often reduced to near zero by GHC and/or LLVM optimization.
Informative tutorials are work in progress. See
for a lexer/parser example with acceptably good error messages.
Pull requests are welcome. I’m fairly quick to add PR authors as collaborators.
Execution times below. See source code in bench. Compiled with GHC
-O2 -fllvm. Executed on Intel 1165G7 CPU at 28W power draw.
|long keyword/fpbasic||115.9 μs|
|long keyword/fpstateful||117.7 μs|
|long keyword/attoparsec||2.955 ms|
|long keyword/megaparsec||2.185 ms|
|long keyword/parsec||29.91 ms|
|numeral csv/fpbasic||549.3 μs|
|numeral csv/fpstateful||595.5 μs|
|numeral csv/attoparsec||10.82 ms|
|numeral csv/megaparsec||6.581 ms|
|numeral csv/parsec||39.33 ms|
Object file sizes for each module containing the
long keyword and
numeral csv benchmarks.
|library||object file size (bytes)|