13 releases (8 breaking)

Uses old Rust 2015

0.8.0 Feb 1, 2017
0.6.0 Jan 29, 2017
0.4.0 Dec 31, 2016

#41 in #packets


Used in peel-ip

MIT license

50KB
418 lines

peel

Build Status Build status Coverage Status master doc peel License MIT Crates.io doc.rs

Dynamic parsing within trees 🌲 🌳 🌴

Target of this library is to provide a flexible approach in parsing data. This will mainly be done within arena based parser trees which can be modified during runtime. Every parser is using the nom framework for the actual parsing work. A complete source code example can be found within the src/example directory of the crate.

Architecture and usage

Every Peel instance can be seen as a parsing graph structure which has different states and transitions. In the example within the crate the structure looks like this:

Example parser diagram

Independently of what these parser do, the creation of this structure is done within the peel_example function:

pub fn peel_example() -> Peel<()> {
    // Create a tree
    let mut p = Peel::new();

    // Create some parsers
    let parser_1 = p.new_parser(Parser1);
    let parser_2 = p.new_parser(Parser2);
    let parser_3 = p.new_parser(Parser3);
    let parser_4 = p.new_parser(Parser4);

    // Link the parsers together
    p.link_nodes(&[(parser_1, parser_2),
                   (parser_1, parser_3),
                   (parser_2, parser_3),
                   (parser_3, parser_3),
                   (parser_3, parser_4)]);

    p
}

The first created parser will automatically be the root parser and the entry point for the tree traversal. Every succeeding parser returns a certain result, which will be pushed into a vector as a Box<Any> which can be downcasted to a certain other type.

This means that the traversal method of Peel will try to find the deepest possible valid path within the tree structure. After the creation of the structure the traversal can begin:

let mut peel = peel_example();
peel.set_log_level(LogLevel::Trace);
let result = peel.traverse(b"1234", vec![]).unwrap();

assert_eq!(result.len(), 4);
println!("{:?}", result);

With the help of the log crate it will output:

[peel] [DEBUG] Parser 1 parsing succeed, left input length: 3
[peel] [DEBUG] Failed parser: Parser 3
[peel] [DEBUG] Parser 2 parsing succeed, left input length: 2
[peel] [DEBUG] Parser 3 parsing succeed, left input length: 1
[peel] [DEBUG] Parser 4 parsing succeed, left input length: 0

A minimal parser has to implement the Parser trait which could look like this:

use example::prelude::*;

/// The first example parser
pub struct Parser1;

#[derive(Debug, PartialEq)]
/// The result of the first example parser
pub struct Parser1Result;

impl Parsable<()> for Parser1 {
    /// The actual parsing entry point
    fn parse<'a>(&mut self,
                 input: &'a [u8],                    // The input for the parser
                 result: Option<&ParserResultVec>,   // The current parsing result
                 data: Option<&mut ()>)              // Additional data which will
                                                     // be shared accross parsers
                 -> IResult<&'a [u8], ParserResult> {

        do_parse!(input, tag!("1") >> (Box::new(Parser1Result)))
    }
}

impl fmt::Display for Parser1 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Parser 1")
    }
}

It is possible to access the current parsing result for a more advanced behavior like dependency checks during the parsing. Furthermore, additional data data can be used to share data between parsers.

Contributing

You want to contribute to this project? Wow, thanks! So please just fork it and send me a pull request.

Dependencies

~6–12MB
~134K SLoC