use pest_meta::optimizer::OptimizedExpr;
use crate::*;
#[derive(Debug, Clone, PartialEq)]
pub enum FPestExpr {
Ident(String),
Str(String),
Insens(String),
CharacterCondition(String),
NegPred(Box<FPestExpr>),
Seq(Vec<FPestExpr>),
Choice(Vec<FPestExpr>),
Rep(Box<FPestExpr>, bool),
Opt(Box<FPestExpr>),
}
pub fn optimize<G: Generator>(expr: &OptimizedExpr) -> FPestExpr {
match expr {
OptimizedExpr::Str(value) => {
if value.len() == 1 {
FPestExpr::CharacterCondition(G::character(value.as_bytes()[0]))
} else {
FPestExpr::Str(value.to_owned())
}
},
OptimizedExpr::Insens(value) => {
FPestExpr::Insens(value.to_owned())
}
OptimizedExpr::Ident(ident) => {
if let Some(condition) = G::character_ident(ident) {
FPestExpr::CharacterCondition(condition.to_string())
} else {
FPestExpr::Ident(ident.to_owned())
}
},
OptimizedExpr::NegPred(expr) => {
FPestExpr::NegPred(Box::new(optimize::<G>(expr)))
}
OptimizedExpr::Seq(first, second) => {
if **second == OptimizedExpr::Rep(first.to_owned()) {
return FPestExpr::Rep(Box::new(optimize::<G>(first)), false);
}
let mut seq = Vec::new();
list_seq(expr, &mut seq);
let mut items = seq.into_iter().map(optimize::<G>).collect::<Vec<_>>();
let mut i = 0;
while i + 1 < items.len() {
if let FPestExpr::NegPred(boxed) = &items[i] {
if let FPestExpr::CharacterCondition(c) = &**boxed {
if let FPestExpr::CharacterCondition(c2) = &items[i + 1] {
items[i] = FPestExpr::CharacterCondition(format!("(!{} && {})", c, c2));
items.remove(i + 1);
continue;
} else if let FPestExpr::NegPred(boxed2) = &items[i + 1] {
if let FPestExpr::CharacterCondition(c2) = &**boxed2 {
items[i] = FPestExpr::NegPred(Box::new(FPestExpr::CharacterCondition(format!("({} || {})", c, c2))));
items.remove(i + 1);
continue;
}
}
}
}
i += 1;
}
if items.len() == 1 {
items.pop().expect("Seq")
} else {
FPestExpr::Seq(items)
}
}
OptimizedExpr::Choice(_, _) => {
let mut choices = Vec::new();
list_choices(expr, &mut choices);
let mut fp_choices = Vec::new();
let mut current_condition = String::new();
for choice in choices {
let choice = optimize::<G>(choice);
if let FPestExpr::CharacterCondition(c) = choice {
if !current_condition.is_empty() {
current_condition.push_str(" || ");
}
current_condition.push_str(&c);
} else {
if !current_condition.is_empty() {
fp_choices.push(FPestExpr::CharacterCondition(current_condition));
current_condition = String::new();
}
fp_choices.push(choice);
}
}
if !current_condition.is_empty() {
fp_choices.push(FPestExpr::CharacterCondition(current_condition));
}
if fp_choices.len() == 1 {
fp_choices.pop().expect("Choice")
} else {
FPestExpr::Choice(fp_choices)
}
},
OptimizedExpr::Opt(expr) => FPestExpr::Opt(Box::new(optimize::<G>(expr))),
OptimizedExpr::Rep(expr) => FPestExpr::Rep(Box::new(optimize::<G>(expr)), true),
OptimizedExpr::Range(a, b) => {
if a.len() == 1 && b.len() == 1 {
let a = a.chars().next().expect("Range a").to_ascii_lowercase() as u8;
let b = b.chars().next().expect("Range b").to_ascii_lowercase() as u8;
FPestExpr::CharacterCondition(G::character_range(a, b))
} else {
todo!()
}
}
OptimizedExpr::PosPred(_) => todo!(),
OptimizedExpr::Skip(_) => todo!(),
OptimizedExpr::Push(_) => todo!(),
OptimizedExpr::RestoreOnErr(_) => todo!(),
OptimizedExpr::PeekSlice(_, _) => todo!(),
}
}
pub fn optimize_second_stage(expr: &mut FPestExpr, character_set_rules: &HashMap<&str, String>) {
match expr {
FPestExpr::Ident(ident) => if let Some(condition) = character_set_rules.get(ident.as_str()) {
*expr = FPestExpr::CharacterCondition(condition.to_string());
},
FPestExpr::Str(_) => (),
FPestExpr::Insens(_) => (),
FPestExpr::CharacterCondition(_) => (),
FPestExpr::NegPred(expr) => optimize_second_stage(expr, character_set_rules),
FPestExpr::Seq(items) => {
for item in items.iter_mut() {
optimize_second_stage(item, character_set_rules);
}
let mut i = 0;
while i + 1 < items.len() {
if let FPestExpr::NegPred(boxed) = &items[i] {
if let FPestExpr::CharacterCondition(c) = &**boxed {
if let FPestExpr::CharacterCondition(c2) = &items[i + 1] {
items[i] = FPestExpr::CharacterCondition(format!("(!{} && {})", c, c2));
items.remove(i + 1);
continue;
} else if let FPestExpr::NegPred(boxed2) = &items[i + 1] {
if let FPestExpr::CharacterCondition(c2) = &**boxed2 {
items[i] = FPestExpr::NegPred(Box::new(FPestExpr::CharacterCondition(format!("({} || {})", c, c2))));
items.remove(i + 1);
continue;
}
}
}
}
i += 1;
}
if items.len() == 1 {
*expr = items.pop().expect("Seq")
}
},
FPestExpr::Choice(items) => {
let mut fp_choices = Vec::new();
let mut current_condition = String::new();
for item in items.iter_mut() {
optimize_second_stage(item, character_set_rules);
if let FPestExpr::CharacterCondition(c) = item {
if !current_condition.is_empty() {
current_condition.push_str(" || ");
}
current_condition.push_str(c);
} else {
if !current_condition.is_empty() {
fp_choices.push(FPestExpr::CharacterCondition(current_condition));
current_condition = String::new();
}
fp_choices.push(item.to_owned());
}
}
if !current_condition.is_empty() {
fp_choices.push(FPestExpr::CharacterCondition(current_condition));
}
if fp_choices.len() == 1 {
*expr = fp_choices.pop().expect("Choice")
} else {
*expr = FPestExpr::Choice(fp_choices);
}
},
FPestExpr::Rep(expr, _) => optimize_second_stage(expr, character_set_rules),
FPestExpr::Opt(expr) => optimize_second_stage(expr, character_set_rules),
}
}