faster-pest-generator 0.2.0-alpha.1

proc-macro for faster-pest
Documentation
use crate::{*, optimizer::FPestExpr};

fn to_pest(expr: &FPestExpr) -> String {
    match expr {
        FPestExpr::Str(s) => format!("{s:?}"),
        FPestExpr::CharacterCondition(c) => format!("({c})"),
        FPestExpr::Insens(s) => format!("^{s:?}"),
        FPestExpr::Ident(i) => i.to_owned(),
        FPestExpr::NegPred(e) => format!("!{}", to_pest(e)),
        FPestExpr::Seq(exprs) => format!("({})", exprs.iter().map(to_pest).collect::<Vec<_>>().join(" ~ ")),
        FPestExpr::Choice(exprs) => format!("({})", exprs.iter().map(to_pest).collect::<Vec<_>>().join(" | ")),
        FPestExpr::Opt(e) => format!("{}?", to_pest(e)),
        FPestExpr::Rep(e, true) => format!("{}*", to_pest(e)),
        FPestExpr::Rep(e, false) => format!("{}+", to_pest(e)),
    }
}

pub fn code<G: Generator>(expr: &FPestExpr, ids: &mut IdRegistry, has_whitespace: bool) -> String {
    let id = ids.id(expr);
    let mut code = match expr {
        FPestExpr::Ident(ident) => G::ident(ident),
        FPestExpr::CharacterCondition(condition) => {
            G::pattern_expr_character().replace("character_condition", condition)
        }
        FPestExpr::Choice(items) => {
            let mut code = G::pattern_expr_choice().to_owned();
            code = multi_replace(code, vec![
                ("choice_item_id", items.iter().map(|item| ids.id(item)).collect::<Vec<_>>()),
                ("choice_idents", items.iter().map(|item| {
                    match contains_idents(item, has_whitespace) {
                        true => "idents",
                        false => "",
                    }.to_string()
                }).collect::<Vec<_>>()),
            ]);
            code
        }
        FPestExpr::Str(value) => {
            G::pattern_expr_str().replace("expr_str", format!("{value:?}").as_str())
        }
        FPestExpr::Seq(items) => {
            let mut code = G::pattern_expr_seq().to_owned();
            code = multi_replace(code, vec![
                ("seq_item_id", items.iter().map(|item| ids.id(item)).collect::<Vec<_>>()),
                ("seq_idents", items.iter().map(|item| {
                    match contains_idents(item, has_whitespace) {
                        true => "idents",
                        false => "",
                    }.to_string()
                }).collect::<Vec<_>>()),
                ("seq_n", (0..items.len()).map(|i| i.to_string()).collect::<Vec<_>>()),
            ]);
            code
        }
        FPestExpr::Rep(expr, empty_accepted) => {
            if let FPestExpr::CharacterCondition(condition) = &**expr {
                let mut code = G::pattern_expr_rep_character().replace("character_condition", condition);
                if !empty_accepted {
                    code = code.replace("//NON-EMPTY", "");
                }
                code
            } else {
                let mut code = G::pattern_expr_rep().to_owned();
                code = code.replace("inner_eid", &ids.id(expr));
                code = code.replace("inner_idents", match contains_idents(expr, has_whitespace) {
                    true => "idents",
                    false => "",
                });

                if !empty_accepted {
                    code = code.replace("//NON-EMPTY", "");
                }
                code
            }
        }
        FPestExpr::Opt(expr) => {
            let code = G::pattern_expr_opt().to_owned();
            let code = code.replace("inner_eid", &ids.id(expr));
            code
        }
        FPestExpr::NegPred(expr) => {
            let code = G::pattern_expr_neg().to_owned();
            let code = code.replace("inner_id", &ids.id(expr));
            code
        }
        FPestExpr::Insens(value) => {
            let inverted_value = value.chars().map(|c| {
                if c.is_ascii_uppercase() {
                    c.to_ascii_lowercase()
                } else {
                    c.to_ascii_uppercase()
                }
            }).collect::<String>();

            let code = G::pattern_expr_insens().to_owned();
            let code = code.replace("expr_str", format!("{value:?}").as_str());
            let code = code.replace("expr_inv_str", format!("{inverted_value:?}").as_str());
            let code = code.replace("expr_len_str", &value.len().to_string());
            code
        }
    };

    code = code.replace("expr_id", &id);
    code = code.replace("expr_pest", &to_pest(expr));
    if contains_idents(expr, has_whitespace) {
        code = code.replace("//SIG-IDENTS", "");
    }
    if has_whitespace {
        code = code.replace("//WSP", "");
    }
    code
}