git-branchless 0.6.0

Branchless workflow for Git
Documentation
use std::borrow::Cow;
use super::ast::Expr;

grammar;

// Below implements a hierarchy of operator precedences. The lower-numbered
// `Expr`s bind less tightly than the higher-numbered `Expr`s.

pub Expr: Expr<'input> = {
    <lhs:Expr> "|"  <rhs:Expr2> => Expr::FunctionCall(Cow::Borrowed("union"), vec![lhs, rhs]),
    <lhs:Expr> "+"  <rhs:Expr2> => Expr::FunctionCall(Cow::Borrowed("union"), vec![lhs, rhs]),
    <lhs:Expr> "or" <rhs:Expr2> => Expr::FunctionCall(Cow::Borrowed("union"), vec![lhs, rhs]),
    <Expr2>,
}

Expr2: Expr<'input> = {
    <lhs:Expr2> "&"   <rhs:Expr3> => Expr::FunctionCall(Cow::Borrowed("intersection"), vec![lhs, rhs]),
    <lhs:Expr2> "and" <rhs:Expr3> => Expr::FunctionCall(Cow::Borrowed("intersection"), vec![lhs, rhs]),
    <lhs:Expr2> "-"   <rhs:Expr3> => Expr::FunctionCall(Cow::Borrowed("difference"),   vec![lhs, rhs]),
    <lhs:Expr2> "%"   <rhs:Expr3> => Expr::FunctionCall(Cow::Borrowed("only"),         vec![lhs, rhs]),
    <Expr3>
}

Expr3: Expr<'input> = {
    <lhs:Expr3> ":"  <rhs:Expr4> =>  Expr::FunctionCall(Cow::Borrowed("range"),       vec![lhs, rhs]),
    <lhs:Expr3> ":"              =>  Expr::FunctionCall(Cow::Borrowed("descendants"), vec![lhs,    ]),
                ":"  <rhs:Expr4> =>  Expr::FunctionCall(Cow::Borrowed("ancestors"),   vec![     rhs]),

    // For Mercurial users' familiarity.
    <lhs:Expr3> "::"  <rhs:Expr4> =>  Expr::FunctionCall(Cow::Borrowed("range"),       vec![lhs, rhs]),
    <lhs:Expr3> "::"              =>  Expr::FunctionCall(Cow::Borrowed("descendants"), vec![lhs,    ]),
                "::"  <rhs:Expr4> =>  Expr::FunctionCall(Cow::Borrowed("ancestors"),   vec![     rhs]),

    // Note that the LHS and RHS are passed in opposite order to `only`.
    <lhs:Expr3> ".." <rhs:Expr4> =>  Expr::FunctionCall(Cow::Borrowed("only"), vec![rhs, lhs]),
    <lhs:Expr3> ".."             =>  Expr::FunctionCall(Cow::Borrowed("only"), vec![Expr::Name(Cow::Borrowed(".")), lhs]),
                ".." <rhs:Expr4> =>  Expr::FunctionCall(Cow::Borrowed("only"), vec![rhs, Expr::Name(Cow::Borrowed("."))]),

    <Expr4>
}

Expr4: Expr<'input> = {
    <lhs:Expr4> "^"            => Expr::FunctionCall(Cow::Borrowed("parents.nth"), vec![lhs, Expr::Name(Cow::Borrowed("1"))]),
    <lhs:Expr4> "^" <rhs:Name> => Expr::FunctionCall(Cow::Borrowed("parents.nth"), vec![lhs, Expr::Name(rhs)]),

    <lhs:Expr4> "~"            => Expr::FunctionCall(Cow::Borrowed("ancestors.nth"), vec![lhs, Expr::Name(Cow::Borrowed("1"))]),
    <lhs:Expr4> "~" <rhs:Name> => Expr::FunctionCall(Cow::Borrowed("ancestors.nth"), vec![lhs, Expr::Name(rhs)]),

    <Expr5>
}

Expr5: Expr<'input> = {
    "(" <Expr> ")",
    <name:Name> "(" <args:FunctionArgs> ")" => Expr::FunctionCall(name, args),
    <name:Name>                             => Expr::Name(name),
}

Name: Cow<'input, str> = {
    // NOTE: `.` should be allowed in names, but not `..` (since that is an operator).
    <s:r"([a-zA-Z0-9_$@/-]|\.[a-zA-Z0-9_$@/-])+|\."> => Cow::Borrowed(&s),

    // Escaping is not supported in LALROP regex literals.
    // \x22 = double-quote.
    // \x27 = single-quote.
    // \x5c = backslash
    <s:r"\x22([^\x22]|\x5c.)*\x22|\x27([^\x27]|\x5c.)*\x27"> => {
        assert!(s.len() >= 2, "There should be at least 2 quote characters in the string literal, got: {}", s);

        let mut result = String::new();
        let mut last_char_was_backslash = false;
        for c in s.chars().skip(1) {
            if last_char_was_backslash {
                result.push(match c {
                    'n' => '\n',
                    'r' => '\r',
                    't' => '\t',
                    c => c,
                });
            } else if c == '\\' {
                last_char_was_backslash = true;
            } else {
                result.push(c);
            }
        }
        result.pop();
        Cow::Owned(result)
    },
}

FunctionArgs: Vec<Expr<'input>> = {
    <exprs:(<Expr> ",")*> <last_expr:Expr?> => {
        let mut exprs = exprs;
        if let Some(last_expr) = last_expr {
            exprs.push(last_expr);
        }
        exprs
    }
}