alder 0.3.0

Parser combinator library inspired by Elm parser error messages
Documentation

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

  • Nice informative errors with contexts.
  • Almost no backtracking
  • Lossless tree generation (for formatters and IDE approach)
  • 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

  • UTF-8 support
  • 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.2.0"

You may want to enable a derive feature as well:

alder = { version = "0.2.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 injects whitespace parser before and after every token.
    // Unless you explicitly told parser not to do it (for example in strings).
    with_extra(ws(), node(Json::Array, |state| {
        state.add("[");
        match state.input.peek() {
            Some(']') => (),
            _ => 'outer: loop {
                state.add(value());
                'inner: loop { // Until we find either ']' or ','
                    match state.input.peek() {
                        Some(']') => {
                            break 'outer;
                        }
                        Some(',') => {
                            // If there was a problem and we find `,` we try to process rest of the array normally.
                            state.add(recover(","));

                            // Trailing comma
                            if let Some(']') = state.input.peek() { 
                                break 'outer;
                            }
                            break 'inner;
                        },
                        // EOF
                        None => { 
                            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`