#parser #parser-combinator #error-message #elm #combinator

alder

Parser combinator library inspired by Elm parser error messages

9 breaking releases

0.10.0 Mar 3, 2020
0.8.0 Mar 1, 2020

#163 in Parser tooling

MIT license

49KB
1.5K SLoC

Crates.io Docs.rs MIT License Build

Alder

Warning: Library is under development and may be subject to change.

Hand written recursive descent and non-backtracking parsing "combinator" library designed with nice error in mind and lossless data.

Goals

  • Almost no backtracking
  • Lossless tree generation (for formatters and IDE approach)
  • UTF-8 support
  • Nice informative errors with contexts.
  • AST generation based on CST (some macro and trait magic)

I'm somehow inspired by this post. It's about parser generators but I prefer writing them manually.

TODO

  • Add the problem resolving in common parsers like chomp_while
  • Add more common parsers
  • Remove Arc if possible
  • Documentation (right now I have only WIP JSON example)
  • Maybe incremental parsing...

Install

Use cargo-edit:

cargo add alder

Or add it manually:

alder =  "0.10.0"

You may want to enable a derive feature as well:

alder = { version = "0.10.0" , features = ["derive"] }

Example

// Doc tests are treated as a test cases for snapshots.
// To enable them use `derive` feature and add `#[alder_test]` macro.
// It also supports /** multiline comments */
/// []
/// [true]
/// [true,false]
/// [ ]
/// [ true ]
/// [ true, false ]
/// [ true, false, ]
/// [trua, falsa]
/// [truadsadsa, falsa]
/// [true, false
/// [truad  sadsa, falsa]
/**
    [
        true,
        false,
        "foo"
    ]
*/
#[alder_test]
fn array() -> impl Parser {
    with_extra(
        extra(),
        node(Json::Array, |state| {
            state.add("[");
            match state.peek(1).as_ref() {
                "]" => (),
                _ => 'outer: loop {
                    state.add(value());
                    'inner: loop {
                        // Until we find either ']' or ','
                        match state.peek(1).as_ref() {
                            "]" => {
                                break 'outer;
                            }
                            "," => {
                                state.add(recover(","));
                                if let "]" = state.peek(1).as_ref() {
                                    // Trailing comma
                                    break 'outer;
                                }
                                break 'inner;
                            }
                            "" => { // EOF
                                state.add(raise(Problem::InvalidTokenArray, 1));
                                break 'outer;
                            }
                            _ => state.add(raise(Problem::InvalidTokenArray, 1)),
                        };
                    }
                },
            }
            state.add(recover("]"));
        }),
    )
}

Parsers should return information about what happened and where it happened:

--------------------------------- SYNTAX ERROR ---------------------------------
I was parsing Boolean when found issue:
 0 |[truadsadsa, falsa]\EOF
 ~ | ^^^^^^^^^^ I expected `true`

--------------------------------- SYNTAX ERROR ---------------------------------
I was parsing Boolean when found issue:
 0 |[truadsadsa, falsa]\EOF
 ~ |             ^^^^^ I expected `false`

Contribute

Please use git-hooks.

cp .git-hooks/* .git/hooks/

Dependencies

~1.1–1.8MB
~35K SLoC