panfix 0.6.2

Panfix parsing: linear time parsing of multifix operators.
Documentation
mod refn {
    use panfix::refn_impl::{parse, Grammar, GrammarBuilder, ParseTree};
    use panfix::rule;

    fn write_sexpr(out: &mut String, tree: &ParseTree) {
        if tree.arity() == 0 {
            if tree.name() == "$MissingAtom" {
                out.push('?');
            } else {
                out.push_str(tree.text())
            }
        } else {
            out.push('(');
            if tree.name() == "$Juxtapose" {
                out.push('J');
            } else {
                out.push_str(tree.name());
            }
            for child in tree.children() {
                out.push(' ');
                write_sexpr(out, child);
            }
            out.push(')');
        }
    }

    fn run_parser(g: &Grammar, source: &str) -> String {
        match parse(g, source) {
            Err(err) => format!("{}", err),
            Ok(tree) => {
                let mut string = String::new();
                write_sexpr(&mut string, &tree);
                string
            }
        }
    }

    #[test]
    fn test_basics() {
        let g = GrammarBuilder::new()
            .subgrammar("main", |builder| {
                builder
                    .regex("Var", "[a-zA-Z]+")
                    .ops_r(vec![rule!(Dot: _ "." _)])
                    .ops_l(vec![rule!(Plus: _ "+" _), rule!(Minus: _ "-" _)])
            })
            .build();

        assert_eq!(run_parser(&g, "xyz"), "xyz");
        assert_eq!(run_parser(&g, "x+ y"), "(Plus x y)");
        assert_eq!(run_parser(&g, "x y z"), "(J x (J y z))");
        assert_eq!(run_parser(&g, "x.y.z"), "(Dot x (Dot y z))");
        assert_eq!(run_parser(&g, "x-y+y-z"), "(Minus (Plus (Minus x y) y) z)");
        assert_eq!(run_parser(&g, "p.x+p.y"), "(Plus (Dot p x) (Dot p y))");
    }
}