use phf (generate hashmap at compiletime)
This commit is contained in:
parent
3c09919627
commit
d9c6c8143e
@ -38,10 +38,12 @@ serde_json = "1.0.79"
|
|||||||
tracing = "0.1.32"
|
tracing = "0.1.32"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
|
phf = "0.10.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = "0.11.0"
|
shadow-rs = "0.11.0"
|
||||||
command-run = "1.1.1"
|
command-run = "1.1.1"
|
||||||
|
phf_codegen = "0.10.0"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
instant = { version = "0.1.12" }
|
instant = { version = "0.1.12" }
|
||||||
|
|||||||
118
build.rs
118
build.rs
@ -1,3 +1,9 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// rebuild if new commit or contents of `assets` folder changed
|
// rebuild if new commit or contents of `assets` folder changed
|
||||||
println!("cargo:rerun-if-changed=.git/logs/HEAD");
|
println!("cargo:rerun-if-changed=.git/logs/HEAD");
|
||||||
@ -7,4 +13,116 @@ fn main() {
|
|||||||
.enable_capture()
|
.enable_capture()
|
||||||
.run();
|
.run();
|
||||||
shadow_rs::new().unwrap();
|
shadow_rs::new().unwrap();
|
||||||
|
|
||||||
|
generate_hashmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_hashmap() {
|
||||||
|
let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs");
|
||||||
|
let mut file = BufWriter::new(File::create(&path).unwrap());
|
||||||
|
|
||||||
|
write!(
|
||||||
|
&mut file,
|
||||||
|
"static COMPLETION_HASHMAP: phf::Map<&'static str, &'static str> = {}",
|
||||||
|
compile_hashmap().build()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
write!(&mut file, ";\n").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of supported functions from exmex
|
||||||
|
const SUPPORTED_FUNCTIONS: [&str; 22] = [
|
||||||
|
"abs", "signum", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "floor",
|
||||||
|
"round", "ceil", "trunc", "fract", "exp", "sqrt", "cbrt", "ln", "log2", "log10",
|
||||||
|
];
|
||||||
|
|
||||||
|
const QUOTE: char = '"';
|
||||||
|
fn compile_hashmap() -> phf_codegen::Map<String> {
|
||||||
|
let mut tuple_list: Vec<(String, String)> = Vec::new();
|
||||||
|
|
||||||
|
for entry in SUPPORTED_FUNCTIONS
|
||||||
|
.iter()
|
||||||
|
.map(|entry| format!("{}(", entry))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
{
|
||||||
|
for i in 1..entry.len() {
|
||||||
|
let (first, last) = entry.split_at(i);
|
||||||
|
tuple_list.push((first.to_string(), last.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output: phf_codegen::Map<String> = phf_codegen::Map::new();
|
||||||
|
|
||||||
|
let key_list: Vec<String> = tuple_list.iter().map(|(key, _)| key.clone()).collect();
|
||||||
|
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
for (key, value) in tuple_list.clone() {
|
||||||
|
if seen.contains(&key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
seen.insert(key.clone());
|
||||||
|
|
||||||
|
let duplicate_num = key_list.iter().filter(|ele| **ele == key).count();
|
||||||
|
if 1 == duplicate_num {
|
||||||
|
output.entry(key, &(QUOTE.to_string() + &value + "E.to_string()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let same_keys_merged: Vec<String> = tuple_list
|
||||||
|
.iter()
|
||||||
|
.filter(|(a, _)| **a == key)
|
||||||
|
.map(|(a, b)| a.clone() + b)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let merged_key_value = key.clone() + &value;
|
||||||
|
|
||||||
|
let mut common_substr: Option<String> = None;
|
||||||
|
for same_key in same_keys_merged {
|
||||||
|
if let Some(common_substr_unwrapped) = common_substr {
|
||||||
|
common_substr = common_substring(&common_substr_unwrapped, &same_key);
|
||||||
|
} else {
|
||||||
|
common_substr = common_substring(&same_key, &merged_key_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if common_substr.is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(common_substr_unwrapped) = common_substr {
|
||||||
|
if !common_substr_unwrapped.is_empty() {
|
||||||
|
output.entry(
|
||||||
|
key.clone(),
|
||||||
|
&(QUOTE.to_string()
|
||||||
|
+ &common_substr_unwrapped.replace(&key, "")
|
||||||
|
+ "E.to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn common_substring<'a>(a: &'a str, b: &'a str) -> Option<String> {
|
||||||
|
let a_chars: Vec<char> = a.chars().collect();
|
||||||
|
let b_chars: Vec<char> = b.chars().collect();
|
||||||
|
if a_chars[0] != b_chars[0] {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut last_value: String = a_chars[0].to_string();
|
||||||
|
let max_common_i = std::cmp::min(a.len(), b.len()) - 1;
|
||||||
|
for i in 1..=max_common_i {
|
||||||
|
let a_i = a_chars[i];
|
||||||
|
let b_i = b_chars[i];
|
||||||
|
if a_i == b_i {
|
||||||
|
last_value += &a_i.to_string()
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(last_value)
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/misc.rs
37
src/misc.rs
@ -313,28 +313,6 @@ pub fn step_helper(max_i: usize, min_x: f64, step: f64) -> Vec<f64> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn common_substring<'a>(a: &'a str, b: &'a str) -> Option<String> {
|
|
||||||
let a_chars: Vec<char> = a.chars().collect();
|
|
||||||
let b_chars: Vec<char> = b.chars().collect();
|
|
||||||
if a_chars[0] != b_chars[0] {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut last_value: String = a_chars[0].to_string();
|
|
||||||
let max_common_i = std::cmp::min(a.len(), b.len()) - 1;
|
|
||||||
for i in 1..=max_common_i {
|
|
||||||
let a_i = a_chars[i];
|
|
||||||
let b_i = b_chars[i];
|
|
||||||
if a_i == b_i {
|
|
||||||
last_value += &a_i.to_string()
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(last_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chars_take(chars: &[char], i: usize) -> String {
|
pub fn chars_take(chars: &[char], i: usize) -> String {
|
||||||
if i > chars.len() {
|
if i > chars.len() {
|
||||||
panic!("chars_take: i is larget than chars.len()");
|
panic!("chars_take: i is larget than chars.len()");
|
||||||
@ -436,21 +414,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests [`common_substring`]
|
|
||||||
#[test]
|
|
||||||
fn common_substring_test() {
|
|
||||||
let values = HashMap::from([
|
|
||||||
(("test", "text"), Some("te")),
|
|
||||||
(("lol", "text"), None),
|
|
||||||
(("sin(", "sinh("), Some("sin")),
|
|
||||||
(("aaa", "bbb"), None),
|
|
||||||
]);
|
|
||||||
|
|
||||||
for ((key_a, key_b), value) in values {
|
|
||||||
assert_eq!(common_substring(key_a, key_b), value.map(|e| e.to_owned()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chars_take_test() {
|
fn chars_take_test() {
|
||||||
let values = HashMap::from([(("test", 2), "st"), (("cool text", 4), "text")]);
|
let values = HashMap::from([(("test", 2), "st"), (("cool text", 4), "text")]);
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::misc::{chars_take, common_substring};
|
use crate::misc::chars_take;
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
/// Generate a hint based on the input `input`, returns an `Option<String>`
|
/// Generate a hint based on the input `input`, returns an `Option<String>`
|
||||||
pub fn generate_hint(input: &str) -> Option<String> {
|
pub fn generate_hint(input: &str) -> Option<String> {
|
||||||
@ -54,75 +53,7 @@ pub fn generate_hint(input: &str) -> Option<String> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates hashmap used for function name completion
|
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
|
||||||
fn gen_completion_hashmap(input: Vec<String>) -> HashMap<String, String> {
|
|
||||||
let mut tuple_list: Vec<(String, String)> = Vec::new();
|
|
||||||
|
|
||||||
for entry in input {
|
|
||||||
for i in 1..entry.len() {
|
|
||||||
let (first, last) = entry.split_at(i);
|
|
||||||
tuple_list.push((first.to_string(), last.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut output: HashMap<String, String> = HashMap::new();
|
|
||||||
|
|
||||||
let key_list: Vec<String> = tuple_list.iter().map(|(key, _)| key.clone()).collect();
|
|
||||||
|
|
||||||
let mut seen = HashSet::new();
|
|
||||||
for (key, value) in tuple_list.clone() {
|
|
||||||
if seen.contains(&key) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
seen.insert(key.clone());
|
|
||||||
|
|
||||||
let duplicate_num = key_list.iter().filter(|ele| **ele == key).count();
|
|
||||||
if 1 == duplicate_num {
|
|
||||||
output.insert(key, value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let same_keys_merged: Vec<String> = tuple_list
|
|
||||||
.iter()
|
|
||||||
.filter(|(a, _)| **a == key)
|
|
||||||
.map(|(a, b)| a.clone() + b)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let merged_key_value = key.clone() + &value;
|
|
||||||
|
|
||||||
let mut common_substr: Option<String> = None;
|
|
||||||
for same_key in same_keys_merged {
|
|
||||||
if let Some(common_substr_unwrapped) = common_substr {
|
|
||||||
common_substr = common_substring(&common_substr_unwrapped, &same_key);
|
|
||||||
} else {
|
|
||||||
common_substr = common_substring(&same_key, &merged_key_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if common_substr.is_none() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(common_substr_unwrapped) = common_substr {
|
|
||||||
if !common_substr_unwrapped.is_empty() {
|
|
||||||
output.insert(key.clone(), common_substr_unwrapped.replace(&key, ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List of supported functions from exmex
|
|
||||||
const SUPPORTED_FUNCTIONS: [&str; 22] = [
|
|
||||||
"abs", "signum", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "floor",
|
|
||||||
"round", "ceil", "trunc", "fract", "exp", "sqrt", "cbrt", "ln", "log2", "log10",
|
|
||||||
];
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
static ref COMPLETION_HASHMAP: HashMap<String, String> = gen_completion_hashmap(SUPPORTED_FUNCTIONS.to_vec().iter().map(|ele| ele.to_string() + "(").collect());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets completion from `COMPLETION_HASHMAP`
|
/// Gets completion from `COMPLETION_HASHMAP`
|
||||||
pub fn get_completion(key: String) -> Option<String> {
|
pub fn get_completion(key: String) -> Option<String> {
|
||||||
@ -135,7 +66,7 @@ pub fn get_completion(key: String) -> Option<String> {
|
|||||||
if data_x.is_empty() {
|
if data_x.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(data_x.clone())
|
Some(data_x.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
@ -144,6 +75,8 @@ pub fn get_completion(key: String) -> Option<String> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Tests to make sure hints are properly outputed based on input
|
/// Tests to make sure hints are properly outputed based on input
|
||||||
@ -193,10 +126,14 @@ mod tests {
|
|||||||
fn hashmap_test_gen() -> HashMap<String, Option<String>> {
|
fn hashmap_test_gen() -> HashMap<String, Option<String>> {
|
||||||
let mut values: HashMap<String, Option<String>> = HashMap::new();
|
let mut values: HashMap<String, Option<String>> = HashMap::new();
|
||||||
|
|
||||||
let processed_func: Vec<String> = SUPPORTED_FUNCTIONS
|
let processed_func: Vec<String> = [
|
||||||
.iter()
|
"abs", "signum", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh",
|
||||||
.map(|ele| ele.to_string() + "(")
|
"floor", "round", "ceil", "trunc", "fract", "exp", "sqrt", "cbrt", "ln", "log2",
|
||||||
.collect();
|
"log10",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|ele| ele.to_string() + "(")
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut data_tuple: Vec<(String, Option<String>)> = Vec::new();
|
let mut data_tuple: Vec<(String, Option<String>)> = Vec::new();
|
||||||
for func in processed_func.iter() {
|
for func in processed_func.iter() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user