glit 0.3.0

A utility for pretty-printing git stats
Documentation
/// Benchmarks for interpreter

#[macro_use]
extern crate criterion;
extern crate glitter_lang;

use glitter_lang::ast::{Color, CompleteStyle, Delimiter, Expression, Name, Style, Tree};
use glitter_lang::git::Stats;
use glitter_lang::interpreter::Interpreter;

use criterion::{Bencher, Criterion, Fun};

fn stats() -> Stats {
    Stats {
        untracked: 1,
        added_staged: 1,
        modified: 1,
        modified_staged: 1,
        renamed: 1,
        deleted: 1,
        deleted_staged: 1,
        ahead: 1,
        behind: 1,
        conflicts: 1,
        stashes: 1,
        branch: "master".to_string(),
        remote: "origin/master".to_string(),
    }
}

fn empty_stats(c: &mut Criterion) {
    use Expression::*;
    use Name::*;

    let empty: Stats = Default::default();
    let expression = Tree(vec![Group {
        d: Delimiter::Square,
        sub: Tree(vec![
            Named {
                name: Modified,
                sub: Tree::new(),
            },
            Named {
                name: Added,
                sub: Tree::new(),
            },
            Named {
                name: Renamed,
                sub: Tree::new(),
            },
            Named {
                name: Deleted,
                sub: Tree::new(),
            },
        ]),
    }]);
    let mut interpreter = Interpreter::new(empty, true, true);

    c.bench_function("default stats \"[MARD]\"", move |b| {
        let mut out = Vec::with_capacity(128);
        b.iter(|| {
            out.clear();
            let _ = interpreter.evaluate(&expression, &mut out);
        })
    });
}

fn real_world(c: &mut Criterion) {
    use glitter_lang::parser::parse;

    let tree = parse(r"[#g*(b)#r(B(#~('..')))#w(\(#~*(+('↑')-('↓')))<#g(MARD)#r(maud)>{#m*_(h('@'))})]' '#b*('\w')'\n '").expect("failed to parse example");

    let mut i = Interpreter::new(stats(), true, true);
    c.bench_function("Real world \"$GIT_FMT\" example", move |b| {
        let mut out = Vec::with_capacity(256);
        b.iter(|| {
            out.clear();
            let _ = i.evaluate(&tree, &mut out);
        })
    });
}

fn nested_named(c: &mut Criterion) {
    use Expression::*;
    use Name::*;

    /// Recursively create tree structure for tests
    macro_rules! tree {
        ($expr:tt, $($tail:tt),*) => {{
            Tree(vec![Named {
                name: $expr,
                sub: tree![$($tail),*]
            }])
        }};
        ($expr:tt) => {{
            Tree(vec![Named {
                name: $expr,
                sub: Tree::default(),
            }])
        }};
    }

    macro_rules! depth {
        ($($tail:tt),+) => {{
            |b: &mut Bencher, s: &Stats| {
                let mut interpreter = Interpreter::new(s.clone(), true, true);
                // Use passed tokens as the Name type in each subtree
                let e = tree![$($tail),+];
                let mut out = Vec::with_capacity(128);
                b.iter(|| {
                    out.clear();
                    let _  = interpreter.evaluate(&e, &mut out);
                });
            }
        }};
    }

    c.bench_functions(
        "nested named",
        vec![
            Fun::new("depth 1", depth![Modified]),
            Fun::new("depth 2", depth![Modified, Added]),
            Fun::new("depth 3", depth![Modified, Added, Untracked]),
            Fun::new("depth 4", depth![Modified, Added, Untracked, Deleted]),
            Fun::new(
                "depth 5",
                depth![Modified, Added, Untracked, Deleted, Branch],
            ),
        ],
        stats(),
    );
}

fn tree_length(c: &mut Criterion) {
    use Expression::*;
    use Name::*;

    macro_rules! length_n {
        ($n:expr) => {
            |b: &mut Bencher, s: &Stats| {
                let e = Tree(
                    std::iter::repeat(Named {
                        name: Deleted,
                        sub: Tree::new(),
                    })
                    .take($n)
                    .collect(),
                );

                let mut i = Interpreter::new(s.clone(), true, true);
                let mut out = Vec::with_capacity(128);
                b.iter(|| {
                    out.clear();
                    let _ = i.evaluate(&e, &mut out);
                });
            }
        };
    }

    c.bench_functions(
        "tree length",
        vec![
            Fun::new("length 2", length_n!(2)),
            Fun::new("length 4", length_n!(4)),
            Fun::new("length 8", length_n!(8)),
            Fun::new("length 16", length_n!(16)),
            Fun::new("length 32", length_n!(32)),
        ],
        stats(),
    );
}

fn interpret_style(c: &mut Criterion) {
    use Color::*;
    use Expression::*;
    use Style::*;

    macro_rules! style {
        ($style:expr, $content:expr) => {
            |b: &mut Bencher, s: &Stats| {
                let styles = Tree(vec![Format {
                    style: $style,
                    sub: $content,
                }]);
                let mut i = Interpreter::new(s.clone(), true, true);
                let mut out = Vec::with_capacity(128);
                b.iter(|| {
                    out.clear();
                    let _ = i.evaluate(&styles, &mut out);
                })
            }
        };
    }

    fn test() -> Tree {
        Tree(vec![Literal("test".into())])
    }

    fn make_style(ss: &[Style]) -> CompleteStyle {
        ss.iter().collect()
    }

    c.bench_functions(
        "Interpreting Style",
        vec![
            Fun::new("Empty style", style!(Default::default(), Tree::new())),
            Fun::new("Default style", style!(Default::default(), test())),
            Fun::new("Bold", style!(make_style(&[Bold]), test())),
            Fun::new(
                "Bold, Underline text",
                style!(make_style(&[Bold, Underline]), test()),
            ),
            Fun::new(
                "Bold, Underline, Italic text",
                style!(make_style(&[Bold, Underline, Italic]), test()),
            ),
            Fun::new(
                "Colored Text",
                style!(make_style(&[Fg(Red), Bg(White)]), test()),
            ),
            Fun::new(
                "Colored Underline Text",
                style!(make_style(&[Fg(Red), Bg(White), Underline]), test()),
            ),
        ],
        stats(),
    );
}

criterion_group!(
    interpreter,
    real_world,
    empty_stats,
    nested_named,
    tree_length,
    interpret_style,
);
criterion_main!(interpreter);