use crate::grammar::cfg::RX_NUM_SUFFIX;
use crate::parser::parol_grammar::ParolGrammar;
use crate::parser::parol_parser::parse;
use crate::GrammarConfig;
use anyhow::{Context, Result};
use parol_runtime::ParseTree;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::Debug;
use std::fs;
use std::hash::Hash;
use std::path::Path;
use syntree_layout::Layouter;
pub mod str_vec;
pub(crate) fn group_by<P, T, K>(data: &[T], projection: P) -> Vec<(K, Vec<T>)>
where
P: Fn(&T) -> K,
K: Eq + Hash,
T: Clone,
{
let mut grouping: HashMap<K, Vec<T>> = HashMap::new();
data.iter()
.fold(&mut grouping, |acc, t| {
let key = projection(t);
if let Some(vt) = acc.get_mut(&key) {
vt.push(t.clone());
} else {
acc.insert(key, vec![t.clone()]);
}
acc
})
.drain()
.collect()
}
pub(crate) fn generate_name<T>(exclusions: &[T], preferred_name: String) -> String
where
T: AsRef<str>,
{
fn gen_name<T>(exclusions: &[T], prefix: String, start_num: usize) -> String
where
T: AsRef<str>,
{
let mut num = start_num;
let mut new_name = format!("{}{}", prefix, num);
while exclusions.iter().any(|n| n.as_ref() == new_name) {
num += 1;
new_name = format!("{}{}", prefix, num);
}
new_name
}
if exclusions.iter().any(|n| n.as_ref() == preferred_name) {
let (suffix_number, prefix) = {
if let Some(match_) = RX_NUM_SUFFIX.find(&preferred_name) {
let num = match_.as_str().parse::<usize>().unwrap_or(1);
(num, preferred_name[0..match_.start()].to_string())
} else {
(0, preferred_name.clone())
}
};
gen_name(exclusions, prefix, suffix_number)
} else {
preferred_name
}
}
pub(crate) fn combine<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
pub(crate) fn short_cut_disjunction_combine<A, F, G>(f: F, g: G) -> impl Fn(&A) -> bool
where
F: Fn(&A) -> bool,
G: Fn(&A) -> bool,
{
move |x| {
let r = f(x);
if r {
r
} else {
g(x)
}
}
}
pub(crate) fn short_cut_conjunction_combine<A, F, G>(f: F, g: G) -> impl Fn(&A) -> bool
where
F: Fn(&A) -> bool,
G: Fn(&A) -> bool,
{
move |x| {
let r = f(x);
if !r {
r
} else {
g(x)
}
}
}
pub fn obtain_grammar_config<T>(file_name: T, verbose: bool) -> Result<GrammarConfig>
where
T: AsRef<Path> + Debug,
{
let input = fs::read_to_string(&file_name)
.with_context(|| format!("Can't read file {:?}", file_name))?;
let mut parol_grammar = ParolGrammar::new();
let _syntax_tree = parse(&input, file_name.as_ref(), &mut parol_grammar)
.with_context(|| format!("Failed parsing file {}", file_name.as_ref().display()))?;
if verbose {
println!("{}", parol_grammar);
}
GrammarConfig::try_from(parol_grammar)
}
pub fn obtain_grammar_config_from_string(input: &str, verbose: bool) -> Result<GrammarConfig> {
let mut parol_grammar = ParolGrammar::new();
let _syntax_tree = parse(input, "No file", &mut parol_grammar)
.with_context(|| format!("Failed parsing text {}", input.escape_default()))?;
if verbose {
println!("{}", parol_grammar);
}
GrammarConfig::try_from(parol_grammar)
}
pub fn generate_tree_layout<T>(syntax_tree: &ParseTree<'_>, input_file_name: T) -> Result<()>
where
T: AsRef<Path>,
{
let mut svg_full_file_name = input_file_name.as_ref().to_path_buf();
svg_full_file_name.set_extension("svg");
Layouter::new(syntax_tree)
.with_file_path(&svg_full_file_name)
.embed_with_visualize()?
.write()
.context("Failed writing layout")
}