From d11480ed95e481da8b2fd8d10021a7f9ecb43926 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Fri, 22 Apr 2022 11:34:11 -0400 Subject: [PATCH] refactoring --- .github/workflows/ci.yml | 4 +- .gitignore | 1 + Cargo.lock | 12 +++- Cargo.toml | 20 +----- build.rs | 62 +------------------ parsing/.gitignore | 2 + parsing/Cargo.toml | 20 ++++++ parsing/build.rs | 55 ++++++++++++++++ {src => parsing/src}/autocomplete_helper.rs | 0 .../mod.rs => parsing/src/lib.rs | 0 .../src}/parsing.rs | 27 +++++--- .../src}/suggestions.rs | 50 +++++++++++---- src/function_entry.rs | 6 +- src/lib.rs | 2 - src/main.rs | 2 - src/misc.rs | 42 ------------- src/widgets.rs | 2 +- 17 files changed, 156 insertions(+), 151 deletions(-) create mode 100644 parsing/.gitignore create mode 100644 parsing/Cargo.toml create mode 100644 parsing/build.rs rename {src => parsing/src}/autocomplete_helper.rs (100%) rename src/function_handling/mod.rs => parsing/src/lib.rs (100%) rename {src/function_handling => parsing/src}/parsing.rs (92%) rename {src/function_handling => parsing/src}/suggestions.rs (75%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 411ad64..fd94d3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: with: command: check - test: - name: Tests + parsing_test: + name: Parsing Crate Tests runs-on: ubuntu-latest steps: - name: Checkout sources diff --git a/.gitignore b/.gitignore index b3f0239..b115a87 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /pkg /tmp /assets.tar.zst +/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index a2605fb..47de6ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1379,6 +1379,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "parsing" +version = "0.1.0" +dependencies = [ + "exmex", + "lazy_static", + "phf", + "phf_codegen", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -2312,10 +2322,10 @@ dependencies = [ "emath", "epaint", "epi", - "exmex", "instant", "itertools", "lazy_static", + "parsing", "phf", "phf_codegen", "rayon", diff --git a/Cargo.toml b/Cargo.toml index 8a42680..dceeb38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ name = "ytbn_graphing_software" version = "0.1.0" edition = "2021" -build = "build.rs" license = "AGPL-3.0" repository = "https://github.com/Titaniumtown/YTBN-Graphing-Software" description = "Crossplatform (and web-compatible) graphing calculator" @@ -14,20 +13,9 @@ crate-type = ["cdylib"] threading = ["async-lock", "rayon"] -[profile.release] -debug = false -codegen-units = 1 -opt-level = "z" #optimize for size -lto = true -strip = true - -[profile.dev] -debug = true -opt-level = 0 -lto = true -strip = false - [dependencies] +parsing = { path = "./parsing" } + eframe = { git = "https://github.com/Titaniumtown/egui.git", default-features = false } egui = { git = "https://github.com/Titaniumtown/egui.git", default-features = false } epaint = { git = "https://github.com/Titaniumtown/egui.git", default-features = false } @@ -39,9 +27,6 @@ const_format = { version = "0.2.22", default-features = false, features = [ "fmt", ] } cfg-if = "1.0.0" -exmex = { git = "https://github.com/bertiqwerty/exmex.git", branch = "main", features = [ - "partial", -] } lazy_static = "1.4.0" tar = "0.4.38" ruzstd = { git = "https://github.com/KillingSpark/zstd-rs.git", branch = "ringbuffer" } @@ -74,6 +59,5 @@ wasm-bindgen = { version = "0.2.80", default-features = false, features = [ web-sys = "0.3.57" tracing-wasm = "0.2.1" - [package.metadata.cargo-all-features] skip_optional_dependencies = true #don't test optional dependencies, only features diff --git a/build.rs b/build.rs index 6e8b3c8..87df7ac 100644 --- a/build.rs +++ b/build.rs @@ -1,71 +1,11 @@ -use std::env; -use std::fs::File; -use std::io::{BufWriter, Write}; -use std::path::Path; - -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", -]; - fn main() { // rebuild if new commit or contents of `assets` folder changed println!("cargo:rerun-if-changed=.git/logs/HEAD"); println!("cargo:rerun-if-changed=assets/*"); - let _ = command_run::Command::with_args("./pack_assets.sh", &[""]) + let _ = command_run::Command::with_args("../pack_assets.sh", &[""]) .enable_capture() .run(); shadow_rs::new().expect("Could not initialize shadow_rs"); - - 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).expect("Could not create file")); - let max_len: usize = SUPPORTED_FUNCTIONS - .to_vec() - .iter() - .map(|func| func.len()) - .max() - .unwrap(); - - let string_hashmap = compile_hashmap( - SUPPORTED_FUNCTIONS - .to_vec() - .iter() - .map(|a| a.to_string()) - .collect(), - ); - - let mut hashmap = phf_codegen::Map::new(); - - for (key, value) in string_hashmap.iter() { - hashmap.entry(key, value); - } - - write!( - &mut file, - "static COMPLETION_HASHMAP: phf::Map<&'static str, Hint> = {};", - hashmap.build() - ) - .expect("Could not write to file"); - - writeln!(&mut file, "const MAX_COMPLETION_LEN: usize = {};", max_len) - .expect("Could not write to file"); - - write!( - &mut file, - "#[allow(dead_code)] pub const SUPPORTED_FUNCTIONS: [&str; {}] = {:?};", - SUPPORTED_FUNCTIONS.len(), - SUPPORTED_FUNCTIONS.to_vec() - ) - .expect("Could not write to file"); -} - -include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/src/autocomplete_helper.rs" -)); diff --git a/parsing/.gitignore b/parsing/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/parsing/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/parsing/Cargo.toml b/parsing/Cargo.toml new file mode 100644 index 0000000..d171775 --- /dev/null +++ b/parsing/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "parsing" +version = "0.1.0" +edition = "2021" +build = "build.rs" +license = "AGPL-3.0" +repository = "https://github.com/Titaniumtown/YTBN-Graphing-Software/tree/main/parsing" +description = "Parsing library for YTBN-Graphing-Software" + +[lib] + +[dependencies] +phf = "0.10.1" +exmex = { git = "https://github.com/bertiqwerty/exmex.git", branch = "main", features = [ + "partial", +] } +lazy_static = "1.4.0" + +[build-dependencies] +phf_codegen = "0.10.0" diff --git a/parsing/build.rs b/parsing/build.rs new file mode 100644 index 0000000..f8ea68e --- /dev/null +++ b/parsing/build.rs @@ -0,0 +1,55 @@ +use std::env; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::Path; + +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", +]; + +fn main() { + println!("cargo:rerun-if-changed=.git/logs/HEAD"); + println!("cargo:rerun-if-changed=src/*"); + + 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).expect("Could not create file")); + + let string_hashmap = compile_hashmap( + SUPPORTED_FUNCTIONS + .to_vec() + .iter() + .map(|a| a.to_string()) + .collect(), + ); + + let mut hashmap = phf_codegen::Map::new(); + + for (key, value) in string_hashmap.iter() { + hashmap.entry(key, value); + } + + write!( + &mut file, + "static COMPLETION_HASHMAP: phf::Map<&'static str, Hint> = {};", + hashmap.build() + ) + .expect("Could not write to file"); + + write!( + &mut file, + "#[allow(dead_code)] pub const SUPPORTED_FUNCTIONS: [&str; {}] = {:?};", + SUPPORTED_FUNCTIONS.len(), + SUPPORTED_FUNCTIONS.to_vec() + ) + .expect("Could not write to file"); +} + +include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/autocomplete_helper.rs" +)); diff --git a/src/autocomplete_helper.rs b/parsing/src/autocomplete_helper.rs similarity index 100% rename from src/autocomplete_helper.rs rename to parsing/src/autocomplete_helper.rs diff --git a/src/function_handling/mod.rs b/parsing/src/lib.rs similarity index 100% rename from src/function_handling/mod.rs rename to parsing/src/lib.rs diff --git a/src/function_handling/parsing.rs b/parsing/src/parsing.rs similarity index 92% rename from src/function_handling/parsing.rs rename to parsing/src/parsing.rs index 10296ad..686ceb4 100644 --- a/src/function_handling/parsing.rs +++ b/parsing/src/parsing.rs @@ -128,7 +128,7 @@ fn prettyify_function_str(func: &str) -> String { } } -const VALID_VARIABLES: [char; 5] = ['x', 'X', 'e', 'E', 'π']; +pub const VALID_VARIABLES: [char; 5] = ['x', 'X', 'e', 'E', 'π']; const LETTERS: [char; 52] = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', @@ -136,6 +136,15 @@ const LETTERS: [char; 52] = [ ]; const NUMBERS: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; +#[inline] +pub fn is_variable(c: &char) -> bool { VALID_VARIABLES.contains(&c) } + +#[inline] +pub fn is_letter(c: &char) -> bool { LETTERS.contains(&c) } + +#[inline] +pub fn is_number(c: &char) -> bool { NUMBERS.contains(&c) } + /* EXTREMELY Janky function that tries to put asterisks in the proper places to be parsed. This is so cursed. But it works, and I hopefully won't ever have to touch it again. One limitation though, variables with multiple characters like `pi` cannot be multiplied (like `pipipipi` won't result in `pi*pi*pi*pi`). But that's such a niche use case (and that same thing could be done by using exponents) that it doesn't really matter. @@ -169,11 +178,11 @@ pub fn process_func_str(function_in: &str) -> String { ' ' }; - let c_is_number = NUMBERS.contains(c); - let c_is_letter = LETTERS.contains(c); - let c_is_variable = VALID_VARIABLES.contains(c); - let prev_char_is_variable = VALID_VARIABLES.contains(&prev_char); - let prev_char_is_number = NUMBERS.contains(&prev_char); + let c_is_number = is_number(c); + let c_is_letter = is_letter(c); + let c_is_variable = is_variable(c); + let prev_char_is_variable = is_variable(&prev_char); + let prev_char_is_number = is_number(&prev_char); // makes special case for log with base of a 1-2 digit number if ((prev_prev_prev_char == 'l') @@ -187,7 +196,7 @@ pub fn process_func_str(function_in: &str) -> String { } let c_letters_var = c_is_letter | c_is_variable; - let prev_letters_var = prev_char_is_variable | LETTERS.contains(&prev_char); + let prev_letters_var = prev_char_is_variable | is_letter(&prev_char); if prev_char == ')' { // cases like `)x`, `)2`, and `)(` @@ -196,7 +205,7 @@ pub fn process_func_str(function_in: &str) -> String { } } else if *c == '(' { // cases like `x(` and `2(` - if (prev_char_is_variable | prev_char_is_number) && !LETTERS.contains(&prev_prev_char) { + if (prev_char_is_variable | prev_char_is_number) && !is_letter(&prev_prev_char) { add_asterisk = true; } } else if prev_char_is_number { @@ -235,7 +244,7 @@ pub fn process_func_str(function_in: &str) -> String { #[cfg(test)] mod tests { use super::*; - use crate::function_handling::suggestions::SUPPORTED_FUNCTIONS; + use crate::suggestions::SUPPORTED_FUNCTIONS; use std::collections::HashMap; /// returns if function with string `func_str` is valid after processing through [`process_func_str`] diff --git a/src/function_handling/suggestions.rs b/parsing/src/suggestions.rs similarity index 75% rename from src/function_handling/suggestions.rs rename to parsing/src/suggestions.rs index ead34f5..4c2f956 100644 --- a/src/function_handling/suggestions.rs +++ b/parsing/src/suggestions.rs @@ -1,8 +1,17 @@ -use crate::misc::chars_take; +use crate::parsing::is_number; pub const HINT_EMPTY: Hint = Hint::Single("x^2"); const HINT_CLOSED_PARENS: Hint = Hint::Single(")"); +/// Only enacts println if cfg(test) is enabled +macro_rules! test_print { + ($($arg:tt)*) => { + #[cfg(test)] + println!($($arg)*) + }; +} + + /// Generate a hint based on the input `input`, returns an `Option` pub fn generate_hint<'a>(input: &str) -> &'a Hint<'a> { if input.is_empty() { @@ -23,20 +32,38 @@ pub fn generate_hint<'a>(input: &str) -> &'a Hint<'a> { return &HINT_CLOSED_PARENS; } - let len = chars.len(); + // let len = chars.len(); - for key in (1..=MAX_COMPLETION_LEN) - .rev() - .filter(|i| len >= *i) - .map(|i| chars_take(&chars, i)) - .filter(|cut_string| !cut_string.is_empty()) - { - if let Some(output) = COMPLETION_HASHMAP.get(&key) { - return output; + + let mut split: Vec = Vec::new(); + + let mut buffer: Vec = Vec::new(); + for c in chars { + buffer.push(c); + if c == ')' { + split.push(buffer.iter().collect::()); + buffer.clear(); + continue; + } + + let buffer_string = buffer.iter().collect::(); + + if ((&buffer_string == "log") | (&buffer_string == "log1")) && is_number(&c) { + continue; } } - &Hint::None + if !buffer.is_empty() { + split.push(buffer.iter().collect::()); + } + + test_print!("split: {:?}", split); + + if split.is_empty() { + return COMPLETION_HASHMAP.get(input).unwrap_or(&Hint::None); + } + + COMPLETION_HASHMAP.get(& unsafe {split.last().unwrap_unchecked()}.as_str()).unwrap_or(&Hint::None) } #[derive(PartialEq)] @@ -95,6 +122,7 @@ mod tests { ("sin(", Hint::Single(")")), ("sqrt", Hint::Single("(")), ("ln(x)", Hint::None), + ("ln(x)cos", Hint::Many(&["(", "h("])), ]); for (key, value) in values { diff --git a/src/function_entry.rs b/src/function_entry.rs index 101b8cc..999b644 100644 --- a/src/function_entry.rs +++ b/src/function_entry.rs @@ -1,7 +1,5 @@ #![allow(clippy::too_many_arguments)] // Clippy, shut -use crate::function_handling::parsing::{process_func_str, BackingFunction}; -use crate::function_handling::suggestions::Hint; use crate::math_app::AppSettings; use crate::misc::*; use crate::widgets::{widgets_ontop, AutoComplete, Movement}; @@ -12,6 +10,10 @@ use egui::{ }; use emath::vec2; use epaint::Color32; +use parsing::{ + parsing::{process_func_str, BackingFunction}, + suggestions::Hint, +}; use std::fmt::{self, Debug}; use std::ops::BitXorAssign; diff --git a/src/lib.rs b/src/lib.rs index fef563e..d20ef31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,10 +4,8 @@ #[macro_use] extern crate static_assertions; -mod autocomplete_helper; mod consts; mod function_entry; -mod function_handling; mod math_app; mod misc; mod widgets; diff --git a/src/main.rs b/src/main.rs index ba954e5..3909338 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,8 @@ #[macro_use] extern crate static_assertions; -mod autocomplete_helper; mod consts; mod function_entry; -mod function_handling; mod math_app; mod misc; mod widgets; diff --git a/src/misc.rs b/src/misc.rs index 228a4a6..a5e5d2f 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -333,31 +333,6 @@ pub fn step_helper(max_i: usize, min_x: &f64, step: &f64) -> Vec { (0..max_i).map(|x| (x as f64 * step) + min_x).collect() } -/// Takes `take` number of chars from the end of `chars` and returns a string -pub fn chars_take(chars: &[char], take: usize) -> String { - let len = chars.len(); - assert!(len >= take); - - match take { - 0 => { - // return empty string if `take == 0` - String::new() - } - 1 => { - // return last character as a string if take == 1 - chars[len - 1].to_string() - } - _ if take == len => { - // return `chars` turned into a string if `take == len` - return chars.iter().collect::(); - } - _ => { - // actually do the thing - return chars.iter().rev().take(take).rev().collect::(); - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -450,21 +425,4 @@ mod tests { assert_eq!(option_vec_printer(&key), value); } } - - #[test] - fn chars_take_test() { - let values = HashMap::from([ - (("test", 2), "st"), - (("cool text", 4), "text"), - (("aaa", 0), ""), - (("aaab", 1), "b"), - ]); - - for ((in_str, i), value) in values { - assert_eq!( - chars_take(&in_str.chars().collect::>(), i), - value.to_owned() - ); - } - } } diff --git a/src/widgets.rs b/src/widgets.rs index e7615cc..ca9d415 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,6 +1,6 @@ -use crate::function_handling::suggestions::{self, generate_hint, Hint}; use egui::{text::CCursor, text_edit::CursorRange, TextEdit}; use epaint::text::cursor::{Cursor, PCursor, RCursor}; +use parsing::suggestions::{self, generate_hint, Hint}; #[derive(PartialEq, Debug)] pub enum Movement {