Skip to main content

dart_parsing 0.1.2 copy "dart_parsing: ^0.1.2" to clipboard
dart_parsing: ^0.1.2 copied to clipboard

Composable, reusable, and extensible parsers for Dart.

dart-parsing #

Composable, reusable, and extensible parsers for Dart.

dart-parsing is a small parsing library built around a minimal core abstraction. It lets you build expressive parser pipelines through composition, instead of relying on large regular expressions or complex, stateful parsing code.

The main goal is simple: write parsers that look like the grammar they implement.

Why dart-parsing? #

Common parsing approaches usually fall into one of these categories:

  • Regular expressions — compact, but hard to read, debug, and extend
  • Hand-written parsers — flexible, but often verbose and error-prone
  • Parser generators — powerful, but heavyweight and not always idiomatic in Dart

dart-parsing focuses on small building blocks that can be composed into larger parsers in a way that stays readable and maintainable.

Key ideas:

  • Parsers are easy to combine
  • Small parsers can be reused across different grammars
  • New behavior comes from composition, not modifying the core
  • The resulting code should describe what is being parsed, not how to move through characters

Quick example #

Here is a small parser that reads a latitude like:

15.832373° S
final northSouthSign = OneOf([
  StringLiteral("N").map((_) => 1),
  StringLiteral("S").map((_) => -1),
]);

final lat = DoubleParser()
    .skip(StringLiteral("°"))
    .skip(StringLiteral(" "))
    .take(northSouthSign)
    .map((value, sign) => value * sign);

Parsers can then be combined to form larger structures in a way that mirrors the input grammar.

A full example parsing multiple cities and coordinates can be found in EXAMPLE.md.

Performance #

Although the library focuses on readability and composability, performance is a first-class concern in the implementation.

Parsers operate directly on the input string using codeUnits comparisons rather than creating intermediate substrings. This keeps parsing close to a simple indexed scan over a String, which is both predictable and efficient in Dart.

Some of the key performance characteristics:

  • Character-level parsing
    Parsers compare integer code units instead of allocating temporary String objects.

  • Minimal heap allocations
    Most combinators pass indices and lightweight results rather than copying data.

  • No regex engine overhead
    Parsing relies on direct control flow instead of a general-purpose regex engine.

  • Composability without abstraction penalty
    Even though parsers are built from small units, execution remains close to a linear scan.

Benchmark #

A small benchmark was created to compare different strategies for parsing the same coordinate format:

15.832373° S, 47.987751° W

Each strategy parsed a large input string containing many concatenated coordinates.

  • Input length: 269,999,999 characters
  • Approx. size: 539,999,998 bytes (UTF-16 code units)
  • Each benchmark ran using the default configuration from the benchmark_harness package.

Results #

Approach Total time (µs)
Code unit–based parser 3,439,030 µs
String slice-based parser 3,524,248.5 µs
Regex-based parser 3,812,673 µs
PetitParser 3,778,925.5 µs

That corresponds to roughly 3.4–3.8 seconds to parse ~540 MB of UTF-16 input.

  • All approaches complete within the same general performance band, showing that Dart can handle large-scale text parsing efficiently.
  • The code unit–based parser is the fastest in this benchmark, while still using fully composable parser building blocks.
  • The string slices approach shows similar performance, demonstrating that alternative string-view strategies can also scale well.
  • Both the regex engine and PetitParser perform respectably, but come with additional abstraction or engine overhead compared to the direct code unit approach.
  • These results reinforce that a composable, declarative parser design can remain competitive even at very large input sizes.

Benchmark results can vary depending on:

  • Dart VM warm-up and JIT optimizations
  • Allocation patterns and GC timing
  • Input characteristics (length, structure, failure cases)

The key takeaway is not just which approach is fastest, but that structured, combinator-style parsers can scale to very large inputs while remaining readable and maintainable.

When to use this #

dart-parsing works well when:

  • You want readable parsing logic written in pure Dart
  • You are parsing structured text formats (configs, coordinates, small DSLs, logs, etc.)
  • Regular expressions are becoming difficult to maintain
  • You want a strongly-typed parsing pipeline

License #

MIT License

0
likes
130
points
29
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Composable, reusable, and extensible parsers for Dart.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

collection, unorm_dart

More

Packages that depend on dart_parsing